diff options
| author | Neil Muller <drnlmuller+debian@gmail.com> | 2024-04-16 10:39:41 +0200 |
|---|---|---|
| committer | git-ubuntu importer <ubuntu-devel-discuss@lists.ubuntu.com> | 2024-04-17 22:39:38 +0000 |
| commit | 125730c0db93991d5d67239fbb592c7c35a557a0 (patch) | |
| tree | 8afad13513ac68bec80162dd459dde3b088eabf0 | |
| parent | 49b70975ca3484ec3e8c642fea829c781b68530b (diff) | |
2.1.0-1 (patches unapplied)import/2.1.0-1
Imported using git-ubuntu import.
Notes
Notes:
* New upstream version (Closes: #1066751).
* Add pybuild-plugin-pyproject build dependency since upstream has moved
away from a pure setuptools build.
* Refresh patches and drop no longer relevant patches.
- Dropped 0004-Drop-setuptools_scm_git_archive-requirement-from-set.patch
and 0005-Remove-LICENSE.txt-from-the-list-of-installed-files.patch .
- Renamed 0002-disable_pkg_resources_in_tests.patch to
0002-Remove-checks-for-installed-formencode-from-the-test.patch to
better reflect its function.
* Fixed incorrect license shortnames in debian/copyright.
* Remove PYBUILD_BEFORE_TEST workaround, as it's no longer needed.
| -rw-r--r-- | .gitattributes | 21 | ||||
| -rw-r--r-- | .github/workflows/run-tests.yml | 35 | ||||
| -rw-r--r-- | .readthedocs.yaml | 22 | ||||
| -rw-r--r-- | LICENSE.txt | 6 | ||||
| -rw-r--r-- | MANIFEST.in | 25 | ||||
| -rw-r--r-- | Makefile | 20 | ||||
| -rw-r--r-- | README.rst | 8 | ||||
| -rw-r--r-- | debian/changelog | 16 | ||||
| -rw-r--r-- | debian/control | 1 | ||||
| -rw-r--r-- | debian/copyright | 53 | ||||
| -rw-r--r-- | debian/patches/0001-remove-dns-tests.diff | 14 | ||||
| -rw-r--r-- | debian/patches/0002-Remove-checks-for-installed-formencode-from-the-test.patch | 34 | ||||
| -rw-r--r-- | debian/patches/0002-disable_pkg_resources_in_tests.patch | 22 | ||||
| -rw-r--r-- | debian/patches/0003-Remove-tests-from-test_email-that-require-dns.patch | 12 | ||||
| -rw-r--r-- | debian/patches/0004-Drop-setuptools_scm_git_archive-requirement-from-set.patch | 37 | ||||
| -rw-r--r-- | debian/patches/0005-Remove-LICENSE.txt-from-the-list-of-installed-files.patch | 25 | ||||
| -rw-r--r-- | debian/patches/series | 4 | ||||
| -rwxr-xr-x | debian/rules | 14 | ||||
| -rw-r--r-- | docs/Validator.txt | 291 | ||||
| -rw-r--r-- | docs/_static/formencode.png | bin | 0 -> 23643 bytes | |||
| -rw-r--r-- | docs/conf.py | 34 | ||||
| -rw-r--r-- | docs/modules/htmlfill.txt | 1 | ||||
| -rw-r--r-- | docs/modules/schema.txt | 2 | ||||
| -rw-r--r-- | docs/whatsnew-0-to-1.2.4.txt | 4 | ||||
| -rw-r--r-- | docs/whatsnew-1.2.5.txt | 2 | ||||
| -rw-r--r-- | docs/whatsnew-2.0.txt | 37 | ||||
| -rw-r--r-- | examples/WebwareExamples/index.py | 7 | ||||
| -rw-r--r-- | formencode/__init__.py | 19 | ||||
| -rw-r--r-- | formencode/i18n/sv/LC_MESSAGES/FormEncode.mo | bin | 2145 -> 0 bytes | |||
| -rw-r--r-- | formencode/tests/__init__.py | 17 | ||||
| -rw-r--r-- | pyproject.toml | 5 | ||||
| -rw-r--r-- | requirements-dev.txt | 12 | ||||
| -rw-r--r-- | requirements-docs.txt | 2 | ||||
| -rw-r--r-- | requirements-test.txt | 5 | ||||
| -rw-r--r-- | setup.cfg | 59 | ||||
| -rwxr-xr-x | setup.py | 58 | ||||
| -rw-r--r-- | src/formencode/__init__.py | 32 | ||||
| -rw-r--r-- | src/formencode/api.py (renamed from formencode/api.py) | 142 | ||||
| -rw-r--r-- | src/formencode/compound.py (renamed from formencode/compound.py) | 33 | ||||
| -rw-r--r-- | src/formencode/context.py (renamed from formencode/context.py) | 26 | ||||
| -rw-r--r-- | src/formencode/declarative.py (renamed from formencode/declarative.py) | 16 | ||||
| -rw-r--r-- | src/formencode/doctest_xml_compare.py (renamed from formencode/doctest_xml_compare.py) | 16 | ||||
| -rw-r--r-- | src/formencode/exc.py (renamed from formencode/exc.py) | 2 | ||||
| -rw-r--r-- | src/formencode/fieldstorage.py (renamed from formencode/fieldstorage.py) | 5 | ||||
| -rw-r--r-- | src/formencode/foreach.py (renamed from formencode/foreach.py) | 16 | ||||
| -rw-r--r-- | src/formencode/htmlfill.py (renamed from formencode/htmlfill.py) | 33 | ||||
| -rw-r--r-- | src/formencode/htmlfill_schemabuilder.py (renamed from formencode/htmlfill_schemabuilder.py) | 11 | ||||
| -rw-r--r-- | src/formencode/htmlgen.py (renamed from formencode/htmlgen.py) | 57 | ||||
| -rw-r--r-- | src/formencode/htmlrename.py (renamed from formencode/htmlrename.py) | 2 | ||||
| -rw-r--r-- | src/formencode/i18n/FormEncode.pot (renamed from formencode/i18n/FormEncode.pot) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/cs/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/cs/LC_MESSAGES/FormEncode.mo) | bin | 7752 -> 7752 bytes | |||
| -rw-r--r-- | src/formencode/i18n/cs/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/cs/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/da/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/da/LC_MESSAGES/FormEncode.mo) | bin | 11940 -> 11940 bytes | |||
| -rw-r--r-- | src/formencode/i18n/da/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/da/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/de/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/de/LC_MESSAGES/FormEncode.mo) | bin | 12358 -> 12358 bytes | |||
| -rw-r--r-- | src/formencode/i18n/de/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/de/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/el/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/el/LC_MESSAGES/FormEncode.mo) | bin | 14196 -> 14196 bytes | |||
| -rw-r--r-- | src/formencode/i18n/el/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/el/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/es/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/es/LC_MESSAGES/FormEncode.mo) | bin | 9307 -> 9307 bytes | |||
| -rw-r--r-- | src/formencode/i18n/es/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/es/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/et/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/et/LC_MESSAGES/FormEncode.mo) | bin | 7850 -> 7850 bytes | |||
| -rw-r--r-- | src/formencode/i18n/et/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/et/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/fi/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/fi/LC_MESSAGES/FormEncode.mo) | bin | 7598 -> 7598 bytes | |||
| -rw-r--r-- | src/formencode/i18n/fi/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/fi/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/fr/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/fr/LC_MESSAGES/FormEncode.mo) | bin | 11865 -> 11865 bytes | |||
| -rw-r--r-- | src/formencode/i18n/fr/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/fr/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/it/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/it/LC_MESSAGES/FormEncode.mo) | bin | 9083 -> 9083 bytes | |||
| -rw-r--r-- | src/formencode/i18n/it/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/it/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/ja/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/ja/LC_MESSAGES/FormEncode.mo) | bin | 9183 -> 9183 bytes | |||
| -rw-r--r-- | src/formencode/i18n/ja/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/ja/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/ko/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/ko/LC_MESSAGES/FormEncode.mo) | bin | 12591 -> 12591 bytes | |||
| -rw-r--r-- | src/formencode/i18n/ko/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/ko/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/lt/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/lt/LC_MESSAGES/FormEncode.mo) | bin | 7999 -> 7999 bytes | |||
| -rw-r--r-- | src/formencode/i18n/lt/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/lt/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/mk/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/mk/LC_MESSAGES/FormEncode.mo) | bin | 13895 -> 13895 bytes | |||
| -rw-r--r-- | src/formencode/i18n/mk/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/mk/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.mo) | bin | 8602 -> 8602 bytes | |||
| -rw-r--r-- | src/formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/nl/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/nl/LC_MESSAGES/FormEncode.mo) | bin | 11923 -> 11923 bytes | |||
| -rw-r--r-- | src/formencode/i18n/nl/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/nl/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/pl/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/pl/LC_MESSAGES/FormEncode.mo) | bin | 11985 -> 11985 bytes | |||
| -rw-r--r-- | src/formencode/i18n/pl/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/pl/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.mo) | bin | 8451 -> 8451 bytes | |||
| -rw-r--r-- | src/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.mo) | bin | 8900 -> 8900 bytes | |||
| -rw-r--r-- | src/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/ru/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/ru/LC_MESSAGES/FormEncode.mo) | bin | 10947 -> 10947 bytes | |||
| -rw-r--r-- | src/formencode/i18n/ru/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/ru/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/sk/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/sk/LC_MESSAGES/FormEncode.mo) | bin | 7838 -> 7838 bytes | |||
| -rw-r--r-- | src/formencode/i18n/sk/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/sk/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/sl/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/sl/LC_MESSAGES/FormEncode.mo) | bin | 10112 -> 10112 bytes | |||
| -rw-r--r-- | src/formencode/i18n/sl/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/sl/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/sr/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/sr/LC_MESSAGES/FormEncode.mo) | bin | 13271 -> 13271 bytes | |||
| -rw-r--r-- | src/formencode/i18n/sr/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/sr/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/sr@latin/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/sr@latin/LC_MESSAGES/FormEncode.mo) | bin | 10866 -> 10866 bytes | |||
| -rw-r--r-- | src/formencode/i18n/sr@latin/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/sr@latin/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/sv/LC_MESSAGES/FormEncode.mo | bin | 0 -> 2605 bytes | |||
| -rw-r--r-- | src/formencode/i18n/sv/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/sv/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/tr/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/tr/LC_MESSAGES/FormEncode.mo) | bin | 9612 -> 9612 bytes | |||
| -rw-r--r-- | src/formencode/i18n/tr/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/tr/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.mo) | bin | 8180 -> 8180 bytes | |||
| -rw-r--r-- | src/formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.mo (renamed from formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.mo) | bin | 11161 -> 11161 bytes | |||
| -rw-r--r-- | src/formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.po (renamed from formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.po) | 0 | ||||
| -rw-r--r-- | src/formencode/interfaces.py (renamed from formencode/interfaces.py) | 4 | ||||
| -rw-r--r-- | src/formencode/javascript/ordering.js (renamed from formencode/javascript/ordering.js) | 0 | ||||
| -rw-r--r-- | src/formencode/national.py (renamed from formencode/national.py) | 49 | ||||
| -rw-r--r-- | src/formencode/rewritingparser.py (renamed from formencode/rewritingparser.py) | 48 | ||||
| -rw-r--r-- | src/formencode/schema.py (renamed from formencode/schema.py) | 72 | ||||
| -rw-r--r-- | src/formencode/validators.py (renamed from formencode/validators.py) | 244 | ||||
| -rw-r--r-- | src/formencode/variabledecode.py (renamed from formencode/variabledecode.py) | 13 | ||||
| -rw-r--r-- | tests/__init__.py | 26 | ||||
| -rw-r--r-- | tests/htmlfill_data/data-error1.txt (renamed from formencode/tests/htmlfill_data/data-error1.txt) | 0 | ||||
| -rw-r--r-- | tests/htmlfill_data/data-fill1.txt (renamed from formencode/tests/htmlfill_data/data-fill1.txt) | 0 | ||||
| -rw-r--r-- | tests/htmlfill_data/data-fill2.txt (renamed from formencode/tests/htmlfill_data/data-fill2.txt) | 0 | ||||
| -rw-r--r-- | tests/htmlfill_data/data-fill3.txt (renamed from formencode/tests/htmlfill_data/data-fill3.txt) | 0 | ||||
| -rw-r--r-- | tests/htmlfill_data/data-fill4.txt (renamed from formencode/tests/htmlfill_data/data-fill4.txt) | 0 | ||||
| -rw-r--r-- | tests/htmlfill_data/data-form-last-element.txt (renamed from formencode/tests/htmlfill_data/data-form-last-element.txt) | 0 | ||||
| -rw-r--r-- | tests/htmlfill_data/data-schema1.txt (renamed from formencode/tests/htmlfill_data/data-schema1.txt) | 0 | ||||
| -rw-r--r-- | tests/non_empty.txt (renamed from formencode/tests/non_empty.txt) | 2 | ||||
| -rw-r--r-- | tests/test_cc.py (renamed from formencode/tests/test_cc.py) | 7 | ||||
| -rw-r--r-- | tests/test_compound.py (renamed from formencode/tests/test_compound.py) | 15 | ||||
| -rw-r--r-- | tests/test_context.py (renamed from formencode/tests/test_context.py) | 14 | ||||
| -rw-r--r-- | tests/test_declarative.py (renamed from formencode/tests/test_declarative.py) | 6 | ||||
| -rw-r--r-- | tests/test_doctest_xml_compare.py (renamed from formencode/tests/test_doctest_xml_compare.py) | 1 | ||||
| -rw-r--r-- | tests/test_doctests.py (renamed from formencode/tests/test_doctests.py) | 73 | ||||
| -rw-r--r-- | tests/test_email.py (renamed from formencode/tests/test_email.py) | 14 | ||||
| -rw-r--r-- | tests/test_htmlfill.py (renamed from formencode/tests/test_htmlfill.py) | 21 | ||||
| -rw-r--r-- | tests/test_htmlfill_control.py (renamed from formencode/tests/test_htmlfill_control.py) | 3 | ||||
| -rw-r--r-- | tests/test_htmlgen.py (renamed from formencode/tests/test_htmlgen.py) | 15 | ||||
| -rw-r--r-- | tests/test_htmlrename.py (renamed from formencode/tests/test_htmlrename.py) | 1 | ||||
| -rw-r--r-- | tests/test_i18n.py (renamed from formencode/tests/test_i18n.py) | 15 | ||||
| -rw-r--r-- | tests/test_schema.py (renamed from formencode/tests/test_schema.py) | 39 | ||||
| -rw-r--r-- | tests/test_subclassing.py (renamed from formencode/tests/test_subclassing.py) | 36 | ||||
| -rw-r--r-- | tests/test_subclassing_old.py (renamed from formencode/tests/test_subclassing_old.py) | 48 | ||||
| -rw-r--r-- | tests/test_validators.py (renamed from formencode/tests/test_validators.py) | 172 | ||||
| -rw-r--r-- | tests/test_variabledecode.py (renamed from formencode/tests/test_variabledecode.py) | 1 | ||||
| -rw-r--r-- | tox.ini | 25 |
138 files changed, 970 insertions, 1309 deletions
diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 4aa54ca..0000000 --- a/.gitattributes +++ /dev/null @@ -1,21 +0,0 @@ -# Set default behaviour, in case users don't have core.autocrlf set. -* text=auto - -*.bat text eol=crlf -*.cfg text -*.conf text -*.css text -*.html text -*.in text -*.ini text -*.py text -*.rst text -*.sh text eol=lf -*.txt text -*.po text -*.pot text - -*.mo binary -*.png binary - -.git_archival.txt export-subst diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml deleted file mode 100644 index c1e6213..0000000 --- a/.github/workflows/run-tests.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Tests - -on: - push: - branches: - - main - pull_request: {} - -jobs: - build: - runs-on: ${{ matrix.os }} - timeout-minutes: 10 - strategy: - fail-fast: false - matrix: - python-version: [2.7, 3.6, 3.7, 3.8, 3.9, 3.10.0] - os: [ubuntu-18.04, macOS-latest, windows-latest] - include: - # pypy3 on Windows currently fails trying to - # run dnspython tests. Moving pypy3 to only test linux. - - python-version: pypy3 - os: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - make - - name: Run tests - run: | - make ci diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..3dd84c5 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,22 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +# We recommend specifying your dependencies to enable reproducible builds: +# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + - requirements: requirements-docs.txt diff --git a/LICENSE.txt b/LICENSE.txt index a611276..20bd89b 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,7 +1,5 @@ Copyright (c) 2015 Ian Bicking and FormEncode Contributors - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights @@ -9,13 +7,9 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/MANIFEST.in b/MANIFEST.in index 44df65a..549bdeb 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,26 @@ exclude .gitignore exclude .gitattributes -exclude .travis.yml +exclude .readthedocs.yaml + +include MANIFEST.in + +include LICENSE.txt +include README.rst + +include tox.ini + +graft src/formencode +graft src/formencode/javascript +include src/formencode/i18n/*.pot +recursive-include src/formencode/i18n *.po *.mo + +graft examples +recursive-include examples *.py + +graft tests +graft tests/htmlfill_data +recursive-include docs *.rst conf.py Makefile make.bat +prune docs/_build + +global-exclude */__pycache__/* +global-exclude *.py[cod] @@ -1,21 +1,25 @@ -.PHONY: docs init: pip install -e . - pip install -r requirements-dev.txt -ci: - pytest formencode + pip install -r requirements-test.txt + +.PHONY: tests + +tests: + pytest tests flake8: - flake8 --ignore=E501,F401,E128,E402,E731,F821 formencode + flake8 src tests coverage: pytest --cov-config .coveragerc --verbose --cov-report term --cov-report xml --cov=formencode formencode publish: - pip install 'twine>=1.5.0' - python setup.py sdist bdist_wheel + pip install "twine>=4" build + python -m build twine upload dist/* - rm -fr build dist .egg FormEncode.egg-info + rm -fr build dist .egg src/FormEncode.egg-info + +.PHONY: docs docs: cd docs && make html @@ -1,6 +1,14 @@ FormEncode ========== +.. |PyPI| image:: https://img.shields.io/pypi/v/formencode + :target: https://pypi.org/project/formencode + :alt: PyPI + +.. |Python| image:: https://img.shields.io/pypi/pyversions/formencode + :target: https://pypi.org/project/formencode + :alt: PyPI - Python Version + .. image:: https://github.com/formencode/formencode/actions/workflows/run-tests.yml/badge.svg :target: https://github.com/formencode/formencode/actions :alt: Test Status diff --git a/debian/changelog b/debian/changelog index 3300f28..23dbb49 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,19 @@ +python-formencode (2.1.0-1) unstable; urgency=low + + * New upstream version (Closes: #1066751). + * Add pybuild-plugin-pyproject build dependency since upstream has moved + away from a pure setuptools build. + * Refresh patches and drop no longer relevant patches. + - Dropped 0004-Drop-setuptools_scm_git_archive-requirement-from-set.patch + and 0005-Remove-LICENSE.txt-from-the-list-of-installed-files.patch . + - Renamed 0002-disable_pkg_resources_in_tests.patch to + 0002-Remove-checks-for-installed-formencode-from-the-test.patch to + better reflect its function. + * Fixed incorrect license shortnames in debian/copyright. + * Remove PYBUILD_BEFORE_TEST workaround, as it's no longer needed. + + -- Neil Muller <drnlmuller+debian@gmail.com> Tue, 16 Apr 2024 10:39:41 +0200 + python-formencode (2.0.1-2) unstable; urgency=medium * Team upload. diff --git a/debian/control b/debian/control index 14530b1..87b20d4 100644 --- a/debian/control +++ b/debian/control @@ -9,6 +9,7 @@ Build-Depends: python3-pycountry <!nocheck>, python3-pytest <!nocheck>, python3-six, + pybuild-plugin-pyproject, python3-setuptools, python3-setuptools-scm, Maintainer: Fabio Tranchitella <kobold@debian.org> diff --git a/debian/copyright b/debian/copyright index 2759494..6678249 100644 --- a/debian/copyright +++ b/debian/copyright @@ -2,59 +2,62 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: FormEncode Upstream-Contact: Christopher Lambacher Source: http://www.formencode.org/ +Files-Excluded: .gitattributes .github +Comment: .gitattributes messes up CI and local checkouts. + .github's license is not clear and it is not needed. Files: * Copyright: 2001-2014 Ian Bicking <ianb@colorstudy.com> - 2015 Ian Bicking and FormEncode Contributors -License: MIT + 2008-2023 Ian Bicking and FormEncode Contributors +License: Expat -Files: formencode/i18n/da/LC_MESSAGES/FormEncode.po +Files: src/formencode/i18n/da/LC_MESSAGES/FormEncode.po Copyright: 2013 SurveyMonkey <chrisg@surveymonkey.com> -License: LGPL 2.0 +License: LGPL-2.0 -Files: formencode/i18n/de/LC_MESSAGES/FormEncode.po +Files: src/formencode/i18n/de/LC_MESSAGES/FormEncode.po Copyright: 2006 Gregor Horvath <gh@gregor-horvath.com> 2009 Christoph Zwerschke <cito@online.de> -License: LGPL 2.0 +License: LGPL-2.0 -Files: formencode/i18n/es/LC_MESSAGES/FormEncode.po +Files: src/formencode/i18n/es/LC_MESSAGES/FormEncode.po Copyright: 2006 Alberto Valverde González <alberto@toscat.net> -License: LGPL 2.0 +License: LGPL-2.0 -Files: formencode/i18n/fi/LC_MESSAGES/FormEncode.po +Files: src/formencode/i18n/fi/LC_MESSAGES/FormEncode.po Copyright: 2007 Marko Manninen <mmstud@gmail.com> -License: LGPL 2.0 +License: LGPL-2.0 -Files: formencode/i18n/it/LC_MESSAGES/FormEncode.po +Files: src/formencode/i18n/it/LC_MESSAGES/FormEncode.po Copyright: 2006-2007 Jose Soares Da Silva <jose.soares@sferacarta.com> -License: LGPL 2.0 +License: LGPL-2.0 -Files: formencode/i18n/pl/LC_MESSAGES/FormEncode.po +Files: src/formencode/i18n/pl/LC_MESSAGES/FormEncode.po Copyright: 2007 Robert Wojciechowicz <rw@smsnet.pl> -License: LGPL 2.0 +License: LGPL-2.0 -Files: formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.po +Files: src/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.po Copyright: 2006 Jorge Godoy <godoy@g2ctech.com> -License: LGPL 2.0 +License: LGPL-2.0 -Files: formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.po +Files: src/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.po Copyright: 2006 Pedro Algarvio <ufs@ufsoft.org> -License: LGPL 2.0 +License: LGPL-2.0 Files: docs/_themes/old/static/old.css -Copyright: Public domain +Copyright: David Goodger, Ian Bicking, Christoph Zwerschke License: public-domain Comment: - A modified version of the default docutils stylesheet, so released - in the public domain. + A modification of the default cascading style sheet (v.1.3) for the + HTML output of Docutils. Files: debian/* Copyright: 2006-2008 Tranchitella - 2008-2015 Debian Python Modules Team + 2008-2015 Debian Python Team 2016 Neil Muller -License: MIT +License: Expat -License: MIT +License: Expat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, @@ -77,7 +80,7 @@ License: MIT License: public-domain This stylesheet has been placed in the public domain. -License: LGPL 2.0 +License: LGPL-2.0 The code is licensed under the GNU Lesser General Public License v2.0. You can find the full text of the LGPL in /usr/share/common-licenses/LGPL-2.0 in your Debian system. diff --git a/debian/patches/0001-remove-dns-tests.diff b/debian/patches/0001-remove-dns-tests.diff index 9c19e09..b63408a 100644 --- a/debian/patches/0001-remove-dns-tests.diff +++ b/debian/patches/0001-remove-dns-tests.diff @@ -6,14 +6,14 @@ Tests that require network (DNS) access Patch-Name: 0001-remove-dns-tests.diff Forwarded: not-needed --- - formencode/validators.py | 30 ------------------------------ + src/formencode/validators.py | 30 ------------------------------ 1 file changed, 30 deletions(-) -diff --git a/formencode/validators.py b/formencode/validators.py -index 1122287..7fec31e 100644 ---- a/formencode/validators.py -+++ b/formencode/validators.py -@@ -1292,20 +1292,6 @@ class Email(FancyValidator): +diff --git a/src/formencode/validators.py b/src/formencode/validators.py +index f2011b4..b470c58 100644 +--- a/src/formencode/validators.py ++++ b/src/formencode/validators.py +@@ -1247,20 +1247,6 @@ class Email(FancyValidator): 'nobody@xn--m7r7ml7t24h.com' >>> e.to_python('o*reilly@test.com') 'o*reilly@test.com' @@ -34,7 +34,7 @@ index 1122287..7fec31e 100644 >>> e = Email(not_empty=False) >>> e.to_python('') -@@ -1433,22 +1419,6 @@ class URL(FancyValidator): +@@ -1387,22 +1373,6 @@ class URL(FancyValidator): Traceback (most recent call last): ... Invalid: That is not a valid URL diff --git a/debian/patches/0002-Remove-checks-for-installed-formencode-from-the-test.patch b/debian/patches/0002-Remove-checks-for-installed-formencode-from-the-test.patch new file mode 100644 index 0000000..e95966d --- /dev/null +++ b/debian/patches/0002-Remove-checks-for-installed-formencode-from-the-test.patch @@ -0,0 +1,34 @@ +From: Neil Muller <drnlmuller+debian@gmail.com> +Date: Mon, 25 Mar 2024 11:40:58 +0200 +Subject: Remove checks for installed formencode from the tests. + +We remove the various checks for an installed formencode, +as that is not needed for the running the tests during the build + +Name: 0002-Remove-checks-for-installed-formencode-from-the-test.patch +Forwarded: not-needed +--- + tests/__init__.py | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/tests/__init__.py b/tests/__init__.py +index 922f3c1..c3918e2 100644 +--- a/tests/__init__.py ++++ b/tests/__init__.py +@@ -11,16 +11,3 @@ os.environ['LANGUAGE'] = 'C' + + # Enable deprecation warnings (which are disabled by default) + warnings.simplefilter('default') +- +-try: +- try: +- import importlib.metadata as metadata +- try: +- metadata.distribution("FormEncode") +- except metadata.PackageNotFoundError as error: +- raise ImportError from error +- except ImportError: # Python < 3.8 +- import pkg_resources +- pkg_resources.require('FormEncode') +-except ImportError as error: +- raise ImportError("Install FormEncode before running the tests") from error diff --git a/debian/patches/0002-disable_pkg_resources_in_tests.patch b/debian/patches/0002-disable_pkg_resources_in_tests.patch deleted file mode 100644 index 236f452..0000000 --- a/debian/patches/0002-disable_pkg_resources_in_tests.patch +++ /dev/null @@ -1,22 +0,0 @@ -From: Stefano Rivera <stefanor@debian.org> -Date: Sun, 11 Oct 2015 22:20:18 +0200 -Subject: disable_pkg_resources_in_tests - -Patch-Name: 0002-disable_pkg_resources_in_tests.patch -Forwarded: not-needed ---- - formencode/tests/__init__.py | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/formencode/tests/__init__.py b/formencode/tests/__init__.py -index 24f3397..9aab8db 100644 ---- a/formencode/tests/__init__.py -+++ b/formencode/tests/__init__.py -@@ -13,5 +13,5 @@ os.environ['LANGUAGE'] = 'C' - import warnings - warnings.simplefilter('default') - --import pkg_resources --pkg_resources.require('FormEncode') -+#import pkg_resources -+#pkg_resources.require('FormEncode') diff --git a/debian/patches/0003-Remove-tests-from-test_email-that-require-dns.patch b/debian/patches/0003-Remove-tests-from-test_email-that-require-dns.patch index 30a6098..68f4691 100644 --- a/debian/patches/0003-Remove-tests-from-test_email-that-require-dns.patch +++ b/debian/patches/0003-Remove-tests-from-test_email-that-require-dns.patch @@ -8,14 +8,14 @@ removes them so the build does not require network access. Patch-Name: 0003-Remove-tests-from-test_email-that-require-dns.patch Forwarded: not-needed --- - formencode/tests/test_email.py | 16 ---------------- + tests/test_email.py | 16 ---------------- 1 file changed, 16 deletions(-) -diff --git a/formencode/tests/test_email.py b/formencode/tests/test_email.py -index f79aea8..31cce5e 100644 ---- a/formencode/tests/test_email.py -+++ b/formencode/tests/test_email.py -@@ -68,19 +68,3 @@ class TestEmail(unittest.TestCase): +diff --git a/tests/test_email.py b/tests/test_email.py +index ac40b7a..00bfb3d 100644 +--- a/tests/test_email.py ++++ b/tests/test_email.py +@@ -64,19 +64,3 @@ class TestEmail(unittest.TestCase): for email, expected in valid_email_addresses: self.assertEqual(self.validate(email), expected) diff --git a/debian/patches/0004-Drop-setuptools_scm_git_archive-requirement-from-set.patch b/debian/patches/0004-Drop-setuptools_scm_git_archive-requirement-from-set.patch deleted file mode 100644 index 8aaf216..0000000 --- a/debian/patches/0004-Drop-setuptools_scm_git_archive-requirement-from-set.patch +++ /dev/null @@ -1,37 +0,0 @@ -From: Neil Muller <drnlmuller+debian@gmail.com> -Date: Wed, 16 Dec 2020 10:27:57 +0200 -Subject: Drop 'setuptools_scm_git_archive' requirement from setup.py - -The system setuptools we require in debian/control is good enough, -and this just breaks the build. - -Patch-Name: 0004-Drop-setuptools_scm_git_archive-requirement-from-set.patch -Forwarded: not-needed ---- - setup.py | 7 ------- - 1 file changed, 7 deletions(-) - -diff --git a/setup.py b/setup.py -index 99ae469..3709faf 100755 ---- a/setup.py -+++ b/setup.py -@@ -19,11 +19,6 @@ tests_require = [ - 'dnspython==1.16.0' if sys.version_info[:2] < (3,0) else 'dnspython>=2.0.0', - 'pycountry<19' if sys.version_info < (3,0) else 'pycountry'] - --setup_requires = [ -- 'setuptools_scm<6.0' if sys.version_info[:2] < (3,0) else 'setuptools_scm', -- 'setuptools_scm_git_archive', --] -- - doctests = ['docs/htmlfill.txt', 'docs/Validator.txt', - 'formencode/tests/non_empty.txt'] - -@@ -58,7 +53,5 @@ setup(name='FormEncode', - install_requires=['six'], - tests_require=tests_require, - use_scm_version=True, -- setup_requires=setup_requires, -- - extras_require={'testing': tests_require}, - ) diff --git a/debian/patches/0005-Remove-LICENSE.txt-from-the-list-of-installed-files.patch b/debian/patches/0005-Remove-LICENSE.txt-from-the-list-of-installed-files.patch deleted file mode 100644 index 07abff9..0000000 --- a/debian/patches/0005-Remove-LICENSE.txt-from-the-list-of-installed-files.patch +++ /dev/null @@ -1,25 +0,0 @@ -From: Neil Muller <drnlmuller+debian@gmail.com> -Date: Wed, 16 Dec 2020 12:26:59 +0200 -Subject: Remove LICENSE.txt from the list of installed files - -Upstream added this to include the license in generated artifacts, -but we have debian/copyright. - -Patch-Name: 0005-Remove-LICENSE-install -Forwarded: not-needed ---- - setup.py | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/setup.py b/setup.py -index 3709faf..fe029f5 100755 ---- a/setup.py -+++ b/setup.py -@@ -44,7 +44,6 @@ setup(name='FormEncode', - author_email='ianb@colorstudy.com', - url='http://formencode.org', - license='MIT', -- data_files = [("", ["LICENSE.txt"])], - zip_safe=False, - packages=find_packages(), - include_package_data=True, diff --git a/debian/patches/series b/debian/patches/series index 3e3b59e..8d840fd 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,5 +1,3 @@ 0001-remove-dns-tests.diff -0002-disable_pkg_resources_in_tests.patch +0002-Remove-checks-for-installed-formencode-from-the-test.patch 0003-Remove-tests-from-test_email-that-require-dns.patch -0004-Drop-setuptools_scm_git_archive-requirement-from-set.patch -0005-Remove-LICENSE.txt-from-the-list-of-installed-files.patch diff --git a/debian/rules b/debian/rules index a840dda..4aeb325 100755 --- a/debian/rules +++ b/debian/rules @@ -1,24 +1,14 @@ #! /usr/bin/make -f export PYBUILD_NAME=formencode export DH_ALWAYS_EXCLUDE=tests - -# We need to have extra data files and the i18n files in place -# for the tests, but pybuild.testfiles doesn't preserve the directory -# structure we need - https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=947800 -# So we kludge around it here -define PYBUILD_BEFORE_TEST - cp {dir}/formencode/tests/non_empty.txt {build_dir}/formencode/tests/ - cp -r {dir}/formencode/tests/htmlfill_data {build_dir}/formencode/tests/ - cp -r {dir}/formencode/i18n {build_dir}/formencode/ -endef -export PYBUILD_BEFORE_TEST +export PYBUILD_TEST_ARGS="{dir}" %: dh $@ --with python3 --buildsystem=pybuild override_dh_auto_install: dh_auto_install --buildsystem=pybuild - dh_install -X.po -X.py -ppython3-formencode formencode/i18n/* /usr/share/locale/ + dh_install -X.po -X.py -ppython3-formencode src/formencode/i18n/* /usr/share/locale/ find debian/python3-formencode/ -type d \( -name i18n -or -name docs \) -print0 | xargs --null --no-run-if-empty rm -rf override_dh_auto_test: diff --git a/docs/Validator.txt b/docs/Validator.txt index 22c0984..7dc671c 100644 --- a/docs/Validator.txt +++ b/docs/Validator.txt @@ -35,9 +35,9 @@ The basic metaphor for validation is **to_python** and -- the trusted application, your own Python objects. The "other" may be a web form, an external database, an XML-RPC request, or any data source that is not completely trusted or does not map directly to -Python's object model. :meth:`to_python` is the process of taking -external data and preparing it for internal use, :meth:`from_python` -generally reverses this process (:meth:`from_python` is usually the less +Python's object model. ``to_python()`` is the process of taking +external data and preparing it for internal use, ``from_python()`` +generally reverses this process (``from_python()`` is usually the less interesting of the pair, but provides some important features). The core of this validation process is two methods and an exception:: @@ -52,29 +52,26 @@ The core of this validation process is two methods and an exception:: ... Invalid: Please enter an integer value -``"ten"`` isn't a valid integer, so we get a :class:`formencode.Invalid` -exception. Typically we'd catch that exception, and use it for some +``"ten"`` isn't a valid integer, so we get a :class:`~formencode.api.Invalid` +exception. Typically, we'd catch that exception, and use it for some sort of feedback. Like: -.. comment (fake raw_input): +.. comment (fake input): - >>> raw_input_input = [] - >>> def raw_input(prompt): - ... value = raw_input_input.pop(0) + >>> input_values = [] + >>> def input(prompt): + ... value = input_values.pop(0) ... print ('%s%s' % (prompt, value)) ... return value - >>> raw_input_input.extend(['ten', '10']) - >>> raw_input_input.extend(['bob', 'bob@nowhere.com']) - >>> import six - >>> if not six.PY2: # Python 3 - ... input = raw_input + >>> input_values.extend(['ten', '10']) + >>> input_values.extend(['bob', 'bob@nowhere.com']) :: >>> def get_integer(): ... while 1: ... try: - ... value = raw_input('Enter a number: ') + ... value = input('Enter a number: ') ... return validator.to_python(value) ... except formencode.Invalid as e: ... print (e) @@ -90,17 +87,17 @@ We can also generalize this kind of function:: >>> def valid_input(prompt, validator): ... while 1: ... try: - ... value = raw_input(prompt) + ... value = input(prompt) ... return validator.to_python(value) ... except formencode.Invalid as e: - ... print (e) + ... print(e) >>> valid_input('Enter your email: ', validators.Email()) Enter your email: bob An email address must contain a single @ Enter your email: bob@nowhere.com 'bob@nowhere.com' -:class:`Invalid` exceptions generally give a good, user-readable error +``Invalid`` exceptions generally give a good, user-readable error message about the problem with the input. Using the exception gets more complicated when you use compound data structures (dictionaries and lists), which we'll talk about later__. @@ -151,24 +148,26 @@ they'll be specific to your application:: ... return value .. note:: - The class :class:`formencode.FancyValidator` is the superclass + The class `formencode.FancyValidator` is the superclass for most validators in FormEncode, and it provides a number of useful features that most validators can use -- for instance, you can pass ``strip=True`` into any of these validators, and they'll strip whitespace from the incoming value before any other validation. -This overrides the internal :meth:`_convert_to_python()` method: -:class:`formencode.FancyValidator` adds a number of extra features, and then -calls the internal :meth:`_convert_to_python` method, which is the method you'll -typically write. Contrary to the external method :meth:`to_python`, its -only concern is the conversion part, not the validation part. If further -validation is necessary, this can be done in two other internal methods, -either :meth:`_validate_python()` or :meth:`_validate_other()`. We will give -an example for that later. When a validator finds an error, it raises an -exception (:class:`formencode.Invalid`), with the error message and the -value and "state" objects. We'll talk about state_ later. Here's the -other custom validator, that checks passwords against words in the -standard Unix word file:: +This overrides the internal ``_convert_to_python()`` method: +:class:`formencode.api.FancyValidator` adds a number of extra features, +and then calls the internal ``_convert_to_python`` method, +which is the method you'll typically write. +Contrary to the external method ``to_python()``, its only concern is +the conversion part, not the validation part. +If further validation is necessary, +this can be done in two other internal methods, +either ``_validate_python()`` or ``_validate_other()``. +We will give an example for that later. When a validator finds an error, +it raises an exception (:class:`formencode.api.Invalid`), with the error +message and the value and "state" objects. We'll talk about state_ later. +Here's the other custom validator, that checks passwords against words +in the standard Unix word file:: >>> class SecurePassword(formencode.FancyValidator): ... words_filename = '/usr/share/dict/words' @@ -195,8 +194,8 @@ And here's a schema:: ... chained_validators = [validators.FieldsMatch( ... 'password', 'password_confirm')] -Like any other validator, a :class:`Registration` instance will have the -:meth:`to_python` and :meth:`from_python` methods. The input should be a +Like any other validator, a ``Registration`` instance will have the +``to_python`` and ``from_python`` methods. The input should be a dictionary (or a Paste MultiDict), with keys like ``"first_name"``, ``"password"``, etc. The validators you give as attributes will be applied to each of the values of the dictionary. *All* the values @@ -204,26 +203,27 @@ will be validated, so if there are multiple invalid fields you will get information about all of them. Most validators (anything that subclasses -:class:`formencode.FancyValidator`) will take a certain standard -set of constructor keyword arguments. See -:class:`formencode.api.FancyValidator` for more -- here we use -``not_empty=True``. +:class:`formencode.api.FancyValidator`) will take a certain standard +set of constructor keyword arguments. +See :class:`formencode.api.FancyValidator` for more +-- here we use ``not_empty=True``. -Another notable validator is :class:`formencode.compound.All` -- this +Another notable validator is :class:`~formencode.compound.All` -- this is a *compound validator* -- that is, it's a validator that takes -validators as input. Schemas are one example; in this case :class:`All` -takes a list of validators and applies each of them in turn. -:class:`formencode.compound.Any` is its compliment, that uses the +validators as input. Schemas are one example; in this case +:class:`~formencode.compound.All` takes a list of validators +and applies each of them in turn. +:class:`~formencode.compound.Any` is its compliment, that uses the first passing validator in its list. .. _pre_validators: .. _chained_validators: -:attr:`chained_validators` are validators that are run on the entire -dictionary after other validation is done (:attr:`pre_validators` are -applied before the schema validation). chained_validators will also +``chained_validators`` are validators that are run on the entire +dictionary after other validation is done (``pre_validators`` are +applied before the schema validation). ``chained_validators`` will also allow for multiple validators to fail and report to the error_dict -so, for example, if you have an email_confirm and a password_confirm +so, for example, if you have an ``email_confirm`` and a ``password_confirm`` fields and use FieldsMatch on both of them as follows: >>> chained_validators = [ @@ -258,16 +258,17 @@ function that you write. For example:: Invalid: state: You must enter a state -The :func:`validate_state` function (or any validation function) returns +The ``validate_state()`` function (or any validation function) returns any errors in the form (or it may raise Invalid directly). It can -also modify the :obj:`value_dict` dictionary directly. When it returns +also modify the ``value_dict`` dictionary directly. When it returns None this indicates that everything is valid. You can use this with a -:class:`Schema` by putting :class:`ValidateState` in :attr:`pre_validators` -(all validation will be done before the schema's validation, and if there's -an error the schema won't be run). Or you can put it in -:attr:`chained_validators` and it will be run *after* the schema. If the -schema fails (the values are invalid) then :class:`ValidateState` will not -be run, unless you set :attr:`validate_partial_form` to True (like +:class:`formencode.schema.Schema` by putting ``ValidateState`` in +``pre_validators`` (all validation will be done before the schema's +validation, and if there's an error the schema won't be run). +Or you can put it in ``chained_validators`` and it will be run +*after* the schema. If the schema fails (the values are invalid) +then ``ValidateState`` will not be run, unless you set +``validate_partial_form`` to True (like ``ValidateState = SimpleFormValidator(validate_state, validate_partial_form=True)``. If you validate a partial form you should be careful that you handle missing keys and other @@ -286,12 +287,12 @@ is for:: ... title = validators.ByteString(not_empty=True) >>> validator = formencode.ForEach(BookSchema()) -The :obj:`validator` we've created will take a list of dictionaries as +The ``validator`` we've created will take a list of dictionaries as input (like ``[{"id": "1", "title": "War & Peace"}, {"id": "2", -"title": "Brave New World"}, ...]``). It applies the :class:`BookSchema` -to each entry, and collects any errors and reraises them. Of course, -when you are validating input from an HTML form you won't get well -structured data like this (we'll talk about that later__). +"title": "Brave New World"}, ...]``). It applies the ``BookSchema`` +to each entry, and collects any errors and re-raises them. +Of course, when you are validating input from an HTML form you won't +get well-structured data like this (we'll talk about that later__). .. __: `HTTP/HTML Form Input`_ @@ -299,9 +300,8 @@ Writing Your Own Validator -------------------------- We gave a brief introduction to creating a validator earlier -(:class:`UniqueUsername` and :class:`SecurePassword`). We'll discuss -that a little more. Here's a more complete implementation of -:class:`SecurePassword`:: +(``UniqueUsername`` and ``SecurePassword``). We'll discuss that a little +more. Here's a more complete implementation of ``SecurePassword``:: >>> import re >>> class SecurePassword(validators.FancyValidator): @@ -335,27 +335,27 @@ that a little more. Here's a more complete implementation of ... value, state) With all validators, any arguments you pass to the constructor will be -used to set instance variables. So :class:`SecureValidator(min=5)` will +used to set instance variables. So ``SecureValidator(min=5)`` will be a minimum-five-character validator. This makes it easy to also subclass other validators, giving different default values. Unlike the previous implementation we use the already mentioned -:meth:`_validate_python` method, which is another internal method -:class:`FancyValidator` allows us to override. :meth:`_validate_python` -doesn't have any return value, it simply raises an exception if it -needs to. It validates the value *after* it has been converted -(by :meth:`_convert_to_python`). :meth:`_validate_other` validates before +``_validate_python`` method, which is another internal method +:class:`~formencode.api.FancyValidator` allows us to override. +``_validate_python`` doesn't have any return value, it simply raises an +exception if it needs to. It validates the value *after* it has been +converted (by ``_convert_to_python``). ``_validate_other`` validates before conversion, but that's usually not that useful. The external method -:meth:`to_python` cares about the extra features such as the -:attr:`if_empty` parameter, and uses the internal methods to do the -actual conversion and validation; first it calls :meth:`_validate_other`, -then :meth:`_convert_to_python` and at last :meth:`_validate_python`. +``to_python`` cares about the extra features such as the ``if_empty`` +parameter, and uses the internal methods to do the actual conversion +and validation; first it calls ``_validate_other``, then +``_convert_to_python`` and at last ``_validate_python``. The use of ``self.message(...)`` is meant to make the messages easy to -format for different environments, and replacable (with translations, +format for different environments, and replaceable (with translations, or simply with different text). Each message should have an identifier (``"min"`` and ``"non_letter"`` in this example). The -keyword arguments to :meth:`message` are used for message substitution. +keyword arguments to ``message`` are used for message substitution. See Messages_ for more. Other Validator Usage @@ -371,10 +371,9 @@ to set these. These are (effectively) equivalent:: ... regex = '^[a-zA-Z]+$' >>> plain = Plain() -You can actually use classes most places where you could use an -instance; :meth:`.to_python()` and :meth:`.from_python()` will create -instances as necessary, and many other methods are available on both -the instance and the class level. +You can actually use classes most places where you could use an instance; +``to_python()`` and ``from_python()`` will create instances as necessary, +and many other methods are available on both the instance and the class level. When dealing with nested validators this class syntax is often easier to work with, and better displays the structure. @@ -382,41 +381,39 @@ to work with, and better displays the structure. .. _FancyValidator: There are several options that most validators support (including your -own validators, if you subclass from :class:`formencode.FancyValidator`): - -:attr:`if_empty`: - If set, then this value will be returned if the input evaluates - to false (empty list, empty string, None, etc), but not the 0 or - False objects. This only applies to ``.to_python()``. - -:attr:`not_empty`: - If true, then if an empty value is given raise an error. - (Both with ``.to_python()`` and also ``.from_python()`` - if ``.validate_python`` is true). - -:attr:`strip`: - If true and the input is a string, strip it (occurs before empty - tests). - -:attr:`if_invalid`: - If set, then when this validator would raise Invalid during - ``.to_python()``, instead return this value. - -:attr:`if_invalid_python`: - If set, when the Python value (converted with - ``.from_python()``) is invalid, this value will be returned. - -:attr:`accept_python`: - If True (the default), then ``._validate_python()`` and - ``._validate_other()`` will not be called when - ``.from_python()`` is used. - -:attr:`if_missing`: - Typically when a field is missing the schema will raise an - error. In that case no validation is run -- so things like - ``if_invalid`` won't be triggered. This special attribute (if - set) will be used when the field is missing, and no error will - occur. (``None`` or ``()`` are common values) +own validators, if you subclass from :class:`formencode.api.FancyValidator`): + +``if_empty``: + If set, then this value will be returned if the input evaluates + to false (empty list, empty string, None, etc), but not the 0 or + False objects. This only applies to ``.to_python()``. + +``not_empty``: + If true, then if an empty value is given raise an error. + (Both with ``.to_python()`` and also ``.from_python()`` + if ``.validate_python`` is true). + +``strip``: + If true and the input is a string, strip it (occurs before empty tests). + +``if_invalid``: + If set, then when this validator would raise Invalid during + ``.to_python()``, instead return this value. + +``if_invalid_python``: + If set, when the Python value (converted with + ``.from_python()``) is invalid, this value will be returned. + +``accept_python``: + If True (the default), then ``._validate_python()`` and + ``._validate_other()`` will not be called when ``.from_python()`` is used. + +``if_missing``: + Typically when a field is missing the schema will raise an + error. In that case no validation is run -- so things like + ``if_invalid`` won't be triggered. This special attribute (if + set) will be used when the field is missing, and no error will + occur. (``None`` or ``()`` are common values) State ----- @@ -434,47 +431,49 @@ validator know the locale? State! Whatever else you need to pass in, just put it in the state object as an attribute, then look for that attribute in your validator. -Also, during compound validation (a :class:`formencode.schema.Schema` -or :class:`formencode.foreach.ForEach`) the state (if not None) will -have more instance variables added to it. During a :class:`Schema` -(dictionary) validation the instance variable ``key`` and -``full_dict`` will be added -- ``key`` is the current key (i.e., -validator name), and ``full_dict`` is the rest of the values being -validated. During a :class:`ForEeach` (list) validation, ``index`` and -``full_list`` will be set. +Also, during compound validation (a :class:`~formencode.schema.Schema` +or :class:`~formencode.foreach.ForEach`) the state (if not None) will +have more instance variables added to it. +During a :class:`~formencode.schema.Schema` (dictionary) validation +the instance variable ``key`` and ``full_dict`` will be added -- ``key`` +is the current key (i.e., validator name), and ``full_dict`` is the rest +of the values being validated. +During a :class:`~formencode.foreach.ForEach` (list) validation, +``index`` and ``full_list`` will be set. Invalid Exceptions ------------------ -Besides the string error message, :class:`formencode.Invalid` +Besides the string error message, :class:`~formencode.api.Invalid` exceptions have a few other instance variables: -:attr:`value`: +``value``: The input to the validator that failed. -:attr:`state`: +``state``: The associated state_. -:attr:`msg`: +``msg``: The error message (``str(exc)`` returns this) -:attr:`error_list`: - If the exception happened in a ``ForEach`` (list) validator, then - this will contain a list of ``Invalid`` exceptions. Each item - from the list will have an entry, either None for no error, or an - exception. +``error_list``: + If the exception happened in a :class:`~formencode.foreach.ForEach` + (list) validator, then this will contain a list of + :class:`~formencode.api.Invalid` exceptions. Each item from the list + will have an entry, either None for no error, or an exception. -:attr:`error_dict`: - If the exception happened in a :class:`Schema` (dictionary) validator, - then this will contain :class:`Invalid` exceptions for each failing - field. Passing fields not be included in this dictionary. +``error_dict``: + If the exception happened in a :class:`~formencode.schema.Schema` + (dictionary) validator, then this will contain + :class:`~formencode.api.Invalid` exceptions for each failing field. + Passing fields not be included in this dictionary. -:meth:`.unpack_errors()`: +``.unpack_errors()``: This method returns a set of lists and dictionaries containing - strings, for each error. It's an unpacking of :attr:`error_list`, - :attr:`error_dict` and :attr:`msg`. If you get an Invalid exception - from a :class:`Schema`, you probably want to call this method on the - exception object. + strings, for each error. It's an unpacking of ``error_list``, + ``error_dict`` and ``msg``. If you get an Invalid exception + from a :class:`~formencode.schema.Schema`, you probably want to call + this method on the exception object. .. _Messages: @@ -495,7 +494,7 @@ for each substitution. This way you can reorder or even ignore placeholders in your new message. When you are creating a validator, for maximum flexibility you should -use the :meth:`message` method, like:: +use the ``message`` method, like:: messages = { 'key': 'my message (with a %(substitution)s)', @@ -518,13 +517,13 @@ the found gettext function. To serve a standard translation mechanism and to enable custom translations it looks in the following order to find a gettext (``_``) function: -1. method of the :obj:`state` object +1. method of the ``state`` object -2. function :func:`__builtin__._`. This function is only used when:: +2. function ``builtin._``. This function is only used when:: - Validator.use_builtin_gettext == True #True is default + Validator.use_builtin_gettext == True # True is default -3. formencode builtin :func:`_stdtrans` function +3. formencode builtin ``_stdtrans`` function for standalone use of FormEncode. The language to use is determined out of the locale system (see gettext documentation). Optionally you @@ -537,7 +536,7 @@ find a gettext (``_``) function: messages in the directory ``localedir/language/LC_MESSAGES/FormEncode.mo`` -4. Custom gettext function and addtional parameters +4. Custom gettext function and additional parameters If you use a custom gettext function and you want FormEncode to call your function with additional parameters you can set the @@ -573,12 +572,12 @@ Optionally you can also add a test of your language to ne = formencode.validators.NotEmpty() [...] def test_de(): - _test_lang("de", u"Bitte einen Wert eingeben") + _test_lang("de", "Bitte einen Wert eingeben") And the test for your language:: def test_<lang>(): - _test_lang("<lang>", u"<translation of Not Empty Text in the language <lang>") + _test_lang("<lang>", "<translation of Not Empty Text in the language <lang>") HTTP/HTML Form Input -------------------- diff --git a/docs/_static/formencode.png b/docs/_static/formencode.png Binary files differnew file mode 100644 index 0000000..6595c52 --- /dev/null +++ b/docs/_static/formencode.png diff --git a/docs/conf.py b/docs/conf.py index 9fde57c..ac4a06d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,3 @@ -from __future__ import absolute_import -# -*- coding: utf-8 -*- -# # FormEncode documentation build configuration file, created by # sphinx-quickstart on Tue Aug 30 2011. # @@ -12,12 +9,10 @@ from __future__ import absolute_import # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('..')) +# import os +# import sys +# sys.path.insert(0, os.path.join(os.path.dirname( +# os.path.dirname(os.path.abspath(__file__))), 'src')) # -- General configuration ----------------------------------------------------- @@ -34,30 +29,23 @@ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage'] # The suffix of source filenames. source_suffix = '.txt' -# The encoding of source files. -#source_encoding = 'utf-8-sig' - # The master toctree document. master_doc = 'index' # General information about the project. -project = u'FormEncode' -copyright = u'2008-2012, Ian Bicking and Contributors' +project = 'FormEncode' +copyright = '2008-2023, Ian Bicking and Contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. -# -# The short X.Y version. -version = '2.0.0' from pkg_resources import get_distribution release = get_distribution('formencode').version -# for example take major/minor version = '.'.join(release.split('.')[:2]) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +language = 'en' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: @@ -182,8 +170,8 @@ htmlhelp_basename = 'FormEncode_doc' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'FormEncode.tex', u'FormEncode Documentation', - u'Ian Bicking', 'manual'), + ('index', 'FormEncode.tex', 'FormEncode Documentation', + 'Ian Bicking', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -215,6 +203,6 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'formencode', u'FormEncode Documentation', - [u'Ian Bicking'], 1) + ('index', 'formencode', 'FormEncode Documentation', + ['Ian Bicking'], 1) ] diff --git a/docs/modules/htmlfill.txt b/docs/modules/htmlfill.txt index ad13455..0cff1dd 100644 --- a/docs/modules/htmlfill.txt +++ b/docs/modules/htmlfill.txt @@ -13,5 +13,4 @@ Module Contents .. autofunction:: escape_formatter .. autofunction:: escapenl_formatter .. autofunction:: ignore_formatter -.. autofunction:: ignore_formatter .. autoclass:: FillingParser diff --git a/docs/modules/schema.txt b/docs/modules/schema.txt index f877abc..c78d985 100644 --- a/docs/modules/schema.txt +++ b/docs/modules/schema.txt @@ -8,3 +8,5 @@ Module Contents .. autoclass:: Schema +.. autoclass:: SimpleFormValidator + diff --git a/docs/whatsnew-0-to-1.2.4.txt b/docs/whatsnew-0-to-1.2.4.txt index b5e5ca4..94a4d6d 100644 --- a/docs/whatsnew-0-to-1.2.4.txt +++ b/docs/whatsnew-0-to-1.2.4.txt @@ -60,7 +60,7 @@ What's New In FormEncode 0 to 1.2.4 * Added :class:`formencode.validators.IPAddress`, validating IP addresses, from Leandro Lucarella. -* Added :meth:`formencode.api.Invalid.__unicode__` +* Added method ``Invalid.__unicode__`` * In :mod:`formencode.htmlfill` use a default encoding of utf8 when handling mixed ``str``/``unicode`` content. Also do not modify @@ -125,7 +125,7 @@ What's New In FormEncode 0 to 1.2.4 ``<form:error name="field_name" formatter="ignore" />`` -- this will cause the error to be swallowed, not shown to the user. -* Added :class:`formencode.validators.XRI` for validation i-names, +* Added ``formencode.validators.XRI`` for validation i-names, i-numbers, URLs, etc (as used in OpenID). * Look in ``/usr/share/locale`` for locale files, in addition to the diff --git a/docs/whatsnew-1.2.5.txt b/docs/whatsnew-1.2.5.txt index c4ea4d3..9b4a810 100644 --- a/docs/whatsnew-1.2.5.txt +++ b/docs/whatsnew-1.2.5.txt @@ -33,5 +33,5 @@ Backwards Incompatibilities Documentation Enhancements -------------------------- -- Superceded news with whatsnew documents that will be archived for each +- Superseded news with whatsnew documents that will be archived for each version. Archived all news prior to 1.2.5 in :doc:`/whatsnew-0-to-1.2.4`. diff --git a/docs/whatsnew-2.0.txt b/docs/whatsnew-2.0.txt index 7b5adcc..370f805 100644 --- a/docs/whatsnew-2.0.txt +++ b/docs/whatsnew-2.0.txt @@ -1,28 +1,35 @@ What's New In FormEncode 2.0 ============================ -This article explains the latest changes in `FormEncode` version 2.0.0 -compared to its predecessor, `FormEncode` 1.3.0 +This article explains the latest changes in `FormEncode` version 2.0 +compared to its predecessor, `FormEncode` version 1.3. +2.1.0 +----- + + - Add support for Python 3.7 to 3.12, end support for older Python versions + - Context.set() now works as a context manager + - Fix binary of swedish translation + - Some internal code cleanup and modernization 2.0.1 ----- - - Add support for 3.10 - - use Pytest instead of Nose and Github Actions instead of Travis for tests + + - Add support for Python 3.10 + - use Pytest instead of Nose and GitHub Actions instead of Travis for tests - Documentation updates - - Note this will be the last version to support Python 2.7. The next version - will be 2.1 to signal this change. If you want to keep support for Python - 2.7 update your dependencies spec to be below 2.1 + - Note this will be the last version to support Python 2. + The next version will be 2.1.0 to signal this change. + For compatibility with older Python versions, please use versions < 2.1. 2.0.0 ----- - - `FormEncode` can now run on Python 3.6 and higher without needing to run 2to3 first. - - `FormEncode` 2.0 is no longer compatible with Python 2.6 and 3.2 to 3.5. - If you need Python 2.6 or 3.2 to 3.5 compatibility please use `FormEncode` 1.3. - You might also try `FormEncode` 2.0.0a1 which supports Python 2.6 and Python 3.3-3.5. - - This will be the last major version to support Python 2.7 - - Add strict flag to USPostalCode to raise error on postal codes that has too + - `FormEncode` can now run on Python 3.6 and higher without needing to run 2to3 first. + - `FormEncode` 2.0 is no longer compatible with Python 2.6 and 3.2 to 3.5. + For compatibility with older Python versions, please use versions < 1.3. + - This will be the last major version to support Python 2. + - Add strict flag to ``USPostalCode`` to raise error on postal codes that has too many digits instead of just truncating - Various Python 3 fixes - Serbian latin translation @@ -30,6 +37,6 @@ compared to its predecessor, `FormEncode` 1.3.0 - Dutch, UK, Greek and South Korean postal code format fixes - Add postal code formats for Switzerland, Cyprus, Faroe Islands, San Marino, Ukraine and Vatican City. - Add ISODateTimeConverter validator - - Add ability to target htmlfill to particular form or ignore a form + - Add ability to target ``htmlfill`` to particular form or ignore a form - Fix format errors in some translations - - The version of the library can be checked using formencode.__version__ + - The version of the library can be checked using ``formencode.__version__`` diff --git a/examples/WebwareExamples/index.py b/examples/WebwareExamples/index.py index c8b1a1e..4a3fdd7 100644 --- a/examples/WebwareExamples/index.py +++ b/examples/WebwareExamples/index.py @@ -1,9 +1,6 @@ -from __future__ import absolute_import -from __future__ import print_function from formencode import Invalid, htmlfill, Schema, validators from WebKit.Page import Page -import six page_style = ''' @@ -76,8 +73,8 @@ class index(Page): try: fields = FormSchema.to_python(fields, self) except Invalid as e: - errors = dict((k, v.encode('utf-8')) - for k, v in six.iteritems(e.unpack_errors())) + errors = {k: v.encode('utf-8') + for k, v in e.unpack_errors().items()} print("Errors:", errors) self.rendered_form = htmlfill.render(form_template, defaults=fields, errors=errors) diff --git a/formencode/__init__.py b/formencode/__init__.py deleted file mode 100644 index b4d46e7..0000000 --- a/formencode/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import absolute_import -# formencode package -from pkg_resources import get_distribution, DistributionNotFound - -from formencode.api import ( - NoDefault, Invalid, Validator, Identity, - FancyValidator, is_empty, is_validator) -from formencode.schema import Schema -from formencode.compound import CompoundValidator, Any, All, Pipe -from formencode.foreach import ForEach -from formencode import validators -from formencode import national -from formencode.variabledecode import NestedVariables - -try: - __version__ = get_distribution(__name__).version -except DistributionNotFound: - # package is not installed - __version__ = 'local-test' diff --git a/formencode/i18n/sv/LC_MESSAGES/FormEncode.mo b/formencode/i18n/sv/LC_MESSAGES/FormEncode.mo Binary files differdeleted file mode 100644 index aa132e3..0000000 --- a/formencode/i18n/sv/LC_MESSAGES/FormEncode.mo +++ /dev/null diff --git a/formencode/tests/__init__.py b/formencode/tests/__init__.py deleted file mode 100644 index 24f3397..0000000 --- a/formencode/tests/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import absolute_import -import sys -import os - -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) - -# Make sure messages are not translated when running the tests -# (setting the environment variable here may be too late already, -# in this case you must set it manually before running the tests). -os.environ['LANGUAGE'] = 'C' - -# Enable deprecation warnings (disabled by default in Python > 2.6). -import warnings -warnings.simplefilter('default') - -import pkg_resources -pkg_resources.require('FormEncode') diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8a0985f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,5 @@ +[build-system] +requires = ["setuptools", "wheel", "setuptools_scm>=7"] +build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] diff --git a/requirements-dev.txt b/requirements-dev.txt index cfd0c5e..7a52c58 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,9 +1,5 @@ -pytest<4.7; python_version == '2.7' -pytest-cov<2.13; python_version == '2.7' -pytest; python_version > '2.7' -pytest-cov; python_version > '2.7' +build wheel -pycountry<19; python_version == '2.7' -dnspython<2.0.0; python_version == '2.7' -pycountry; python_version > '2.7' -dnspython; python_version > '2.7' +flake8 +sphinx +-r requirements-test.txt diff --git a/requirements-docs.txt b/requirements-docs.txt new file mode 100644 index 0000000..ac1ea17 --- /dev/null +++ b/requirements-docs.txt @@ -0,0 +1,2 @@ +sphinx >=7, <8 +-e . diff --git a/requirements-test.txt b/requirements-test.txt new file mode 100644 index 0000000..4582cad --- /dev/null +++ b/requirements-test.txt @@ -0,0 +1,5 @@ +dnspython >= 2 +pycountry +pytest +pytest-cov +wheel @@ -1,3 +1,51 @@ +[metadata] +name = FormEncode +description = "HTML form validation, generation, and conversion package" +long_description = file: README.rst +long_description_content_type = text/x-rst +author = Ian Bicking +author_email = ianb@colorstudy.com +maintainer = Chris Lambacher +maintainer_email = chris@kateandchris.net +url = https://www.formencode.org/ +license = MIT +license_files = LICENSE.txt +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Developers + License :: OSI Approved :: MIT License + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 + Programming Language :: Python :: Implementation :: CPython + Programming Language :: Python :: Implementation :: PyPy + Topic :: Software Development :: Libraries :: Python Modules + +[options] +package_dir = + = src +packages = + formencode +python_requires = >=3.7 +include_package_data = False + +[options.package_data] +formencode = + javascript/*.js + i18n/**/*.mo + +[options.extras_require] +testing = + pytest + dnspython >= 2 + pycountry + # Babel configuration [compile_catalog] domain = FormEncode @@ -22,14 +70,9 @@ input_file = formencode/i18n/FormEncode.pot output_dir = formencode/i18n previous = true -[wheel] -universal = 1 - [pep8] -max_line_length=100 +max_line_length=88 [flake8] -max_line_length=100 - -[metadata] -license_files = LICENSE.txt +max-line-length = 88 +extend-ignore = E128,E402,E501,E731 @@ -5,60 +5,6 @@ and decoupled processes for filling and generating forms. The official repo is at GitHub: https://github.com/formencode/formencode """ -from __future__ import absolute_import +from setuptools import setup -import sys -from setuptools import setup, find_packages -import platform - -if not (2,7) <= sys.version_info[:2] < (3,0) and not (3,6) <= sys.version_info[:2]: - raise ImportError('Python version not supported') - -tests_require = [ - 'pytest<4.7' if sys.version_info[:2] < (3,0) else 'pytest', - 'dnspython==1.16.0' if sys.version_info[:2] < (3,0) else 'dnspython>=2.0.0', - 'pycountry<19' if sys.version_info < (3,0) else 'pycountry'] - -setup_requires = [ - 'setuptools_scm<6.0' if sys.version_info[:2] < (3,0) else 'setuptools_scm', - 'setuptools_scm_git_archive', -] - -doctests = ['docs/htmlfill.txt', 'docs/Validator.txt', - 'formencode/tests/non_empty.txt'] - -setup(name='FormEncode', - # requires_python='>=2.7,!=3.0,!=3.1,!=3.2,!=3.3,!=3.4,!=3.5' # PEP345 - description="HTML form validation, generation, and conversion package", - long_description=__doc__, - classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Topic :: Software Development :: Libraries :: Python Modules", - ], - author='Ian Bicking', - author_email='ianb@colorstudy.com', - url='http://formencode.org', - license='MIT', - data_files = [("", ["LICENSE.txt"])], - zip_safe=False, - packages=find_packages(), - include_package_data=True, - package_data={'formencode': ['../docs/*.txt']}, - test_suite='formencode.tests', - install_requires=['six'], - tests_require=tests_require, - use_scm_version=True, - setup_requires=setup_requires, - - extras_require={'testing': tests_require}, - ) +setup() diff --git a/src/formencode/__init__.py b/src/formencode/__init__.py new file mode 100644 index 0000000..fde4099 --- /dev/null +++ b/src/formencode/__init__.py @@ -0,0 +1,32 @@ +"""The formencode package""" + +try: + from importlib.metadata import version, PackageNotFoundError +except ImportError: # Python < 3.8 + from pkg_resources import get_distribution + from pkg_resources import DistributionNotFound as PackageNotFoundError + + def version(distribution_name): + return get_distribution(distribution_name).version + +from formencode.api import ( + NoDefault, Invalid, Validator, Identity, + FancyValidator, is_empty, is_validator) +from formencode.schema import Schema +from formencode.compound import CompoundValidator, Any, All, Pipe +from formencode.foreach import ForEach +from formencode import validators +from formencode import national +from formencode.variabledecode import NestedVariables + +__all__ = [ + 'NoDefault', 'Invalid', 'Validator', 'Identity', 'FancyValidator', + 'is_empty', 'is_validator', + 'Schema', 'CompoundValidator', 'Any', 'All', 'Pipe', + 'ForEach', 'validators', 'national', 'NestedVariables', + '__version__'] + +try: + __version__ = version(__name__) +except PackageNotFoundError: # package is not installed + __version__ = 'local-test' diff --git a/formencode/api.py b/src/formencode/api.py index da321fa..88c62b3 100644 --- a/formencode/api.py +++ b/src/formencode/api.py @@ -1,55 +1,44 @@ """ Core classes for validation. """ -from __future__ import absolute_import -from . import declarative import gettext import os import re import textwrap import warnings -import six -try: - from pkg_resources import resource_filename -except ImportError: - resource_filename = None +from . import declarative __all__ = ['NoDefault', 'Invalid', 'Validator', 'Identity', 'FancyValidator', 'is_empty', 'is_validator'] def get_localedir(): - """Retrieve the location of locales. - - If we're built as an egg, we need to find the resource within the egg. - Otherwise, we need to look for the locales on the filesystem or in the - system message catalog. + """Retrieve the location of locales.""" + file_dir = os.path.join(os.path.dirname(__file__), 'i18n') + if not hasattr(os, 'access'): + # this happens on Google App Engine + return file_dir - """ - locale_dir = '' - # Check the egg first - if resource_filename is not None: - try: - locale_dir = resource_filename(__name__, "i18n") - except NotImplementedError: - # resource_filename doesn't work with non-egg zip files + import importlib.resources as resources + # try to get it via the resource + pkg_name = __name__.split('.', 1)[0] + try: + resource_dir = resources.files(pkg_name) / 'i18n' + except AttributeError: # Python < 3.11 + with resources.path(pkg_name, 'i18n') as resource_dir: pass - if not hasattr(os, 'access'): - # This happens on Google App Engine - return os.path.join(os.path.dirname(__file__), 'i18n') - if os.access(locale_dir, os.R_OK | os.X_OK): - # If the resource is present in the egg, use it - return locale_dir + if os.access(resource_dir, os.R_OK | os.X_OK): + # if the resource is present, use it + return resource_dir - # Otherwise, search the filesystem - locale_dir = os.path.join(os.path.dirname(__file__), 'i18n') - if not os.access(locale_dir, os.R_OK | os.X_OK): - # Fallback on the system catalog - locale_dir = os.path.normpath('/usr/share/locale') + # otherwise, search the filesystem + if os.access(file_dir, os.R_OK | os.X_OK): + return file_dir - return locale_dir + # fallback on the system catalog + return os.path.normpath('/usr/share/locale') def set_stdtranslation(domain="FormEncode", languages=None, @@ -59,18 +48,17 @@ def set_stdtranslation(domain="FormEncode", languages=None, languages=languages, localedir=localedir, fallback=True) global _stdtrans - try: - _stdtrans = t.ugettext - except AttributeError: # Python 3 - _stdtrans = t.gettext + _stdtrans = t.gettext + set_stdtranslation() # Dummy i18n translation function, nothing is translated here. -# Instead this is actually done in api.Validator.message. +# Instead, this is actually done in api.Validator.message. # The surrounding _('string') of the strings is only for extracting # the strings automatically. -# If you run pygettext with this source comment this function out temporarily. +# If you run pygettext with this source, comment this function out temporarily. + _ = lambda s: s @@ -92,7 +80,7 @@ def deprecated(old=None, new=None): return outer -class NoDefault(object): +class NoDefault: """A dummy value used for parameters with no default.""" @@ -149,18 +137,7 @@ class Invalid(Exception): % (self, self.error_list, self.error_dict)) def __str__(self): - val = self.msg - return val - - if six.text_type is not str: # Python 2 - - def __unicode__(self): - if isinstance(self.msg, six.text_type): - return self.msg - elif isinstance(self.msg, str): - return self.msg.decode('utf8') - else: - return six.text_type(self.msg) + return self.msg def unpack_errors(self, encode_variables=False, dict_char='.', list_char='-'): @@ -179,9 +156,9 @@ class Invalid(Exception): for item in self.error_list] if self.error_dict: result = {} - for name, item in six.iteritems(self.error_dict): + for name, item in self.error_dict.items(): result[name] = item if isinstance( - item, six.string_types) else item.unpack_errors() + item, str) else item.unpack_errors() if encode_variables: from . import variabledecode result = variabledecode.variable_encode( @@ -196,9 +173,7 @@ class Invalid(Exception): return self.msg -############################################################ -## Base Classes -############################################################ +# Base Classes class Validator(declarative.Declarative): @@ -240,31 +215,6 @@ class Validator(declarative.Declarative): def from_python(self, value, state=None): return value - _message_vars_decode = None - if six.text_type is not str: - def _message_vars_decode(self, message_vars): - """ - Under python2, a form value in web frameworks may be encoded as - UTF8 unicode. The standard error templates use a ``%(value)r`` - string formatting, which will render the error as ``u"Foo"`` instead - of just ``"Foo"``. This decoder, which can be overridden, will - encode the value back to a python string and render ``"Foo"``. This - decoder will only update the error dictionary if the unicode and - string values are equal by comparison. - - v = message_vars.items()[0][1] - """ - if six.text_type is not str: - for (k, v) in message_vars.items(): - if isinstance(v, six.text_type): - try: - v2 = v.encode('utf-8') - except Exception as e: - v2 = v - if v == v2: - message_vars[k] = v2 - return message_vars - def message(self, msgName, state, **kw): # determine translation function try: @@ -272,8 +222,8 @@ class Validator(declarative.Declarative): except AttributeError: try: if self.use_builtins_gettext: - import six.moves.builtins - trans = six.moves.builtins._ + import builtins + trans = builtins._ else: trans = _stdtrans except AttributeError: @@ -282,12 +232,7 @@ class Validator(declarative.Declarative): if not callable(trans): trans = _stdtrans - msg = self._messages[msgName] - msg = trans(msg, **self.gettextargs) - - if self._message_vars_decode: - # handle custom decoding for message vars - kw = self._message_vars_decode(kw) + msg = trans(self._messages[msgName], **self.gettextargs) try: return msg % kw @@ -340,11 +285,10 @@ class Validator(declarative.Declarative): This changes the class's docstring to include information about all the messages this validator uses. """ - doc = cls.__doc__ or '' - doc = [textwrap.dedent(doc).rstrip()] - messages = sorted(six.iteritems(cls._messages)) - doc.append('\n\n**Messages**\n\n') - for name, default in messages: + doc = [ + textwrap.dedent(cls.__doc__ or '').rstrip(), + '\n\n**Messages**\n\n'] + for name, default in sorted(cls._messages.items()): default = re.sub(r'(%\(.*?\)[rsifcx])', r'``\1``', default) doc.append('``' + name + '``:\n') doc.append(' ' + default + '\n\n') @@ -356,6 +300,7 @@ class _Identity(Validator): def __repr__(self): return 'validators.Identity' + Identity = _Identity() @@ -482,12 +427,11 @@ class FancyValidator(Validator): stacklevel=cls._inheritance_level + 2) setattr(cls, new, new_attrs[old]) elif new in new_attrs: - setattr(cls, old, deprecated(old=old, new=new)( - new_attrs[new])) + setattr(cls, old, deprecated(old=old, new=new)(new_attrs[new])) def to_python(self, value, state=None): try: - if self.strip and isinstance(value, six.string_types): + if self.strip and isinstance(value, str): value = value.strip() elif hasattr(value, 'mixed'): # Support Paste's MultiDict @@ -515,7 +459,7 @@ class FancyValidator(Validator): def from_python(self, value, state=None): try: - if self.strip and isinstance(value, six.string_types): + if self.strip and isinstance(value, str): value = value.strip() if not self.accept_python: if self.is_empty(value): @@ -551,7 +495,7 @@ class FancyValidator(Validator): return None def assert_string(self, value, state): - if not isinstance(value, six.string_types): + if not isinstance(value, str): raise Invalid(self.message('badType', state, type=type(value), value=value), value, state) diff --git a/formencode/compound.py b/src/formencode/compound.py index 6005f1a..5caf950 100644 --- a/formencode/compound.py +++ b/src/formencode/compound.py @@ -1,18 +1,13 @@ """ Validators for applying validations in sequence. """ -from __future__ import absolute_import - -from .api import (FancyValidator, Identity, Invalid, NoDefault, Validator, - is_validator) -import six +from .api import ( + FancyValidator, Identity, Invalid, NoDefault, Validator, is_validator) __all__ = ['CompoundValidator', 'Any', 'All', 'Pipe'] -############################################################ -## Compound Validators -############################################################ +# Compound Validators def to_python(validator, value, state): return validator.to_python(value, state) @@ -40,14 +35,14 @@ class CompoundValidator(FancyValidator): @staticmethod def __classinit__(cls, new_attrs): FancyValidator.__classinit__(cls, new_attrs) - toAdd = [] - for name, value in six.iteritems(new_attrs): + to_add = [] + for name, value in new_attrs.items(): if is_validator(value) and value is not Identity: - toAdd.append((name, value)) + to_add.append((name, value)) # @@: Should we really delete too? delattr(cls, name) - toAdd.sort() - cls.validators.extend([value for _name, value in toAdd]) + to_add.sort() + cls.validators.extend([value for _name, value in to_add]) def __init__(self, *args, **kw): Validator.__init__(self, *args, **kw) @@ -135,12 +130,12 @@ class Any(CompoundValidator): class All(CompoundValidator): - """Check if all of the specified validators are valid. + """Check that specified validators are valid. - This class is like an 'and' operator for validators. All - validators must work, and the results are passed in turn through - all validators for conversion in the order of evaluation. All - is the same as `Pipe` but operates in the reverse order. + This class is like an 'and' operator for validators. + All validators must work, and the results are passed in turn through + all validators for conversion in the order of evaluation. + ``All`` is the same as ``Pipe`` but operates in the reverse order. The order of evaluation differs depending on if you are validating to Python or from Python as follows: @@ -149,7 +144,7 @@ class All(CompoundValidator): The validators are evaluated left to right when validating from Python. - `Pipe` is more intuitive when predominantly validating to Python. + ``Pipe`` is more intuitive when predominantly validating to Python. Examples:: diff --git a/formencode/context.py b/src/formencode/context.py index 128c912..f1c0b68 100644 --- a/formencode/context.py +++ b/src/formencode/context.py @@ -25,7 +25,7 @@ Note that all values are thread local; this means you cannot use a context object to pass information to another thread. In a single-thread environment it doesn't really matter. -Typically you will create ``Context`` instances for your application, +Typically, you will create ``Context`` instances for your application, environment, etc. These should be global module-level variables, that may be imported by any interested module; each instance is a namespace of its own. @@ -41,29 +41,27 @@ First, when instantiating a ``Context`` object, you can give it a value. ``None`` is a typical value for that. Another is ``context.set_default(**vars)``, which will set only those -variables to default values. This will not effect the stack of -scopes, but will only add defaults. +variables to default values. This will not affect the stack of scopes, +but will only add defaults. -When Python 2.5 comes out, this syntax would certainly be useful:: +A context can be also used like this:: - with context(page='view'): + with context.set(page='view'): do stuff... And ``page`` will be set to ``'view'`` only inside that ``with`` block. """ -from __future__ import absolute_import import threading from itertools import count -from six.moves import range __all__ = ['Context', 'ContextRestoreError'] _restore_ids = count() -class NoDefault(object): +class NoDefault: """A dummy value used for parameters with no default.""" @@ -71,7 +69,7 @@ class ContextRestoreError(Exception): """Raised when something is restored out-of-order.""" -class Context(object): +class Context: def __init__(self, default=NoDefault): self.__dict__['_local'] = threading.local() @@ -95,7 +93,7 @@ class Context(object): def __setattr__(self, attr, value): raise AttributeError( - "You can only write attribute on context object with the .set() method") + "You can only set attributes on context objects with the .set() method") def set(self, **kw): state_id = next(_restore_ids) @@ -153,13 +151,19 @@ class Context(object): self.__class__.__name__, myid, ' '.join(varlist)) -class RestoreState(object): +class RestoreState: def __init__(self, context, state_id): self.state_id = state_id self.context = context self.restored = False + def __enter__(self): + return self.context + + def __exit__(self, _exc_type, _exc_value, _exc_tb): + self.restore() + def restore(self): if self.restored: # @@: Should this really be allowed? diff --git a/formencode/declarative.py b/src/formencode/declarative.py index 2375b3b..550a270 100644 --- a/formencode/declarative.py +++ b/src/formencode/declarative.py @@ -17,18 +17,14 @@ in a variable by that name. Also, you can define a __classinit__(cls, new_attrs) method, which will be called when the class is created (including subclasses). """ -from __future__ import absolute_import import copy import types from itertools import count -import six -from six.moves import map -from six.moves import zip -class classinstancemethod(object): +class classinstancemethod: """ Acts like a class method when called from a class, like an instance method when called by an instance. The method should @@ -43,7 +39,7 @@ class classinstancemethod(object): return _methodwrapper(self.func, obj=obj, cls=cls) -class _methodwrapper(object): +class _methodwrapper: def __init__(self, func, obj, cls): self.func = func @@ -85,7 +81,7 @@ class DeclarativeMeta(type): return cls -class singletonmethod(object): +class singletonmethod: """ For Declarative subclasses, this decorator will call the method on the cls.singleton() object if called as a class method (or @@ -101,7 +97,7 @@ class singletonmethod(object): return types.MethodType(self.func, obj) -class Declarative(six.with_metaclass(DeclarativeMeta, object)): +class Declarative(metaclass=DeclarativeMeta): __unpackargs__ = () @@ -145,7 +141,7 @@ class Declarative(six.with_metaclass(DeclarativeMeta, object)): for name in self.__mutableattributes__: if name not in kw: setattr(self, name, copy.copy(getattr(self, name))) - for name, value in six.iteritems(kw): + for name, value in kw.items(): setattr(self, name, value) if 'declarative_count' not in kw: self.declarative_count = next(self.counter) @@ -190,7 +186,7 @@ class Declarative(six.with_metaclass(DeclarativeMeta, object)): else: break args.extend('%s=%s' % (name, source.makeRepr(value)) - for (name, value) in six.iteritems(vals)) + for (name, value) in vals.items()) return '%s(%s)' % (self.__class__.__name__, ', '.join(args)) diff --git a/formencode/doctest_xml_compare.py b/src/formencode/doctest_xml_compare.py index ebac496..395d697 100644 --- a/formencode/doctest_xml_compare.py +++ b/src/formencode/doctest_xml_compare.py @@ -1,15 +1,7 @@ -from __future__ import absolute_import -from __future__ import print_function - import doctest import xml.etree.ElementTree as ET -import six -from six.moves import map -from six.moves import zip -try: - XMLParseError = ET.ParseError -except AttributeError: # Python < 2.7 - from xml.parsers.expat import ExpatError as XMLParseError + +XMLParseError = ET.ParseError RealOutputChecker = doctest.OutputChecker @@ -74,7 +66,7 @@ def xml_compare(x1, x2, reporter=None): if reporter: reporter('Tags do not match: %s and %s' % (x1.tag, x2.tag)) return False - for name, value in six.iteritems(x1.attrib): + for name, value in x1.attrib.items(): if x2.attrib.get(name) != value: if reporter: reporter('Attributes do not match: %s=%r, %s=%r' @@ -125,7 +117,7 @@ def make_xml(s): def make_string(xml): - if isinstance(xml, six.string_types): + if isinstance(xml, str): xml = make_xml(xml) s = ET.tostring(xml) if s == '<xml />': diff --git a/formencode/exc.py b/src/formencode/exc.py index 8bc7529..3abdaf4 100644 --- a/formencode/exc.py +++ b/src/formencode/exc.py @@ -2,7 +2,7 @@ Custom exceptions and warnings. """ -__all__ = ['FERunTimeWarning'] +__all__ = ['FERuntimeWarning'] class FERuntimeWarning(RuntimeWarning): diff --git a/formencode/fieldstorage.py b/src/formencode/fieldstorage.py index 40a4d1b..93b2ccd 100644 --- a/formencode/fieldstorage.py +++ b/src/formencode/fieldstorage.py @@ -1,11 +1,6 @@ -## FormEncode, a Form processor -## Copyright (C) 2003, Ian Bicking <ianb@colorstudy.com> """ Wrapper class for use with cgi.FieldStorage types for file uploads """ -from __future__ import absolute_import - -import cgi def convert_fieldstorage(fs): diff --git a/formencode/foreach.py b/src/formencode/foreach.py index 70abfc2..96c064a 100644 --- a/formencode/foreach.py +++ b/src/formencode/foreach.py @@ -1,11 +1,9 @@ """ Validator for repeating items. """ -from __future__ import absolute_import from .api import NoDefault, Invalid from .compound import CompoundValidator, from_python -import six __all__ = ['ForEach'] @@ -85,7 +83,7 @@ class ForEach(CompoundValidator): new_list = set(new_list) return new_list else: - raise Invalid('Errors:\n%s' % '\n'.join(six.text_type(e) + raise Invalid('Errors:\n%s' % '\n'.join(str(e) for e in errors if e), value, state, error_list=errors) finally: if state is not None: @@ -107,15 +105,14 @@ class ForEach(CompoundValidator): def empty_value(self, value): return [] - class _IfMissing(object): + class _IfMissing: def __get__(self, obj, cls=None): if obj is None: return [] - elif obj._if_missing is ForEach._if_missing: + if obj._if_missing is ForEach._if_missing: return [] - else: - return obj._if_missing + return obj._if_missing def __set__(self, obj, value): obj._if_missing = value @@ -127,7 +124,7 @@ class ForEach(CompoundValidator): del _IfMissing def _convert_to_list(self, value): - if isinstance(value, six.string_types): + if isinstance(value, str): return [value] elif value is None: return [] @@ -137,6 +134,5 @@ class ForEach(CompoundValidator): for _n in value: break return value - ## @@: Should this catch any other errors?: - except TypeError: + except TypeError: # @@: Should this catch any other errors? return [value] diff --git a/formencode/htmlfill.py b/src/formencode/htmlfill.py index 90f4bed..a9fd12d 100644 --- a/formencode/htmlfill.py +++ b/src/formencode/htmlfill.py @@ -1,12 +1,10 @@ """ Parser for HTML forms, that fills in defaults and errors. See ``render``. """ -from __future__ import absolute_import import re from formencode.rewritingparser import RewritingParser, html_quote -import six __all__ = ['render', 'htmlliteral', 'default_formatter', 'none_formatter', 'escape_formatter', @@ -70,8 +68,8 @@ def render(form, defaults=None, errors=None, use_all_keys=False, ``listener`` can be an object that watches fields pass; the only one currently is in ``htmlfill_schemabuilder.SchemaBuilder`` - ``encoding`` specifies an encoding to assume when mixing str and - unicode text in the template. + ``encoding`` specifies an encoding to assume when mixing bytes and + str text in the template. ``prefix_error`` specifies if the HTML created by auto_error_formatter is put before the input control (default) or after the control. @@ -126,7 +124,7 @@ def render(form, defaults=None, errors=None, use_all_keys=False, return p.text() -class htmlliteral(object): +class htmlliteral: def __init__(self, html, text=None): if text is None: @@ -247,7 +245,7 @@ class FillingParser(RewritingParser): self.in_select = None self.skip_next = False self.errors = errors or {} - if isinstance(self.errors, six.string_types): + if isinstance(self.errors, str): self.errors = {None: self.errors} self.in_error = None self.skip_error = False @@ -276,14 +274,11 @@ class FillingParser(RewritingParser): Compare the two objects as strings (coercing to strings if necessary). Also uses encoding to compare the strings. """ - if not isinstance(str1, six.string_types): - if hasattr(str1, '__unicode__'): - str1 = six.text_type(str1) - else: - str1 = str(str1) - if type(str1) == type(str2): + if not isinstance(str1, str): + str1 = str(str1) + if type(str1) is type(str2): return str1 == str2 - if isinstance(str1, six.text_type): + if isinstance(str1, str): str1 = str1.encode(self.encoding or self.default_encoding) else: str2 = str2.encode(self.encoding or self.default_encoding) @@ -297,7 +292,7 @@ class FillingParser(RewritingParser): if key in unused_errors: del unused_errors[key] if self.auto_error_formatter: - for key, value in six.iteritems(unused_errors): + for key, value in unused_errors.items(): error_message = self.auto_error_formatter(value) error_message = '<!-- for: %s -->\n%s' % (key, error_message) self.insert_at_marker( @@ -320,9 +315,6 @@ class FillingParser(RewritingParser): if self.encoding is not None: new_content = [] for item in self._content: - if (six.text_type is not str # Python 2 - and isinstance(item, str)): - item = item.decode(self.encoding) new_content.append(item) self._content = new_content self._text = self._get_text() @@ -422,11 +414,8 @@ class FillingParser(RewritingParser): if self.prefix_error: self.write_marker(name) value = self.defaults.get(name) - if (six.text_type is not str # Python 2 - and isinstance(name, six.text_type) and isinstance(value, str)): - value = value.decode(self.encoding or self.default_encoding) if name in self.add_attributes: - for attr_name, attr_value in six.iteritems(self.add_attributes[name]): + for attr_name, attr_value in self.add_attributes[name].items(): if attr_name.startswith('+'): attr_name = attr_name[1:] self.set_attr(attrs, attr_name, @@ -572,7 +561,7 @@ class FillingParser(RewritingParser): """ if obj is None: return False - if isinstance(obj, six.string_types): + if isinstance(obj, str): return obj == value if hasattr(obj, '__contains__'): if value in obj: diff --git a/formencode/htmlfill_schemabuilder.py b/src/formencode/htmlfill_schemabuilder.py index 2059ff7..5519a50 100644 --- a/formencode/htmlfill_schemabuilder.py +++ b/src/formencode/htmlfill_schemabuilder.py @@ -6,8 +6,6 @@ You can either pass ``SchemaBuilder`` to ``htmlfill.render`` (the ``listen`` argument), or call ``parse_schema`` to just parse out a ``Schema`` object. """ -from __future__ import absolute_import - from . import validators from . import schema from . import compound @@ -29,9 +27,8 @@ def parse_schema(form): return listener.schema() -default_validators = dict( - [(name.lower(), getattr(validators, name)) - for name in dir(validators)]) +default_validators = { + name.lower(): getattr(validators, name) for name in dir(validators)} def get_messages(cls, message): @@ -63,7 +60,7 @@ def force_list(v): return [v] -class SchemaBuilder(object): +class SchemaBuilder: def __init__(self, validators=default_validators): self.validators = validators @@ -97,7 +94,7 @@ class SchemaBuilder(object): if required: v.validators.append( validators.NotEmpty( - messages=get_messages(validators.NotEmpty, message))) + messages=get_messages(validators.NotEmpty, message))) else: v.validators[0].if_missing = False if add_to_end: diff --git a/formencode/htmlgen.py b/src/formencode/htmlgen.py index 6ab6173..c41d526 100644 --- a/formencode/htmlgen.py +++ b/src/formencode/htmlgen.py @@ -55,17 +55,10 @@ Examples:: '<a href="#top">return to top</a>' """ -from __future__ import absolute_import import xml.etree.ElementTree as ET -import six -from six.moves import map - -try: - from html import escape -except ImportError: # Python < 3.2 - from cgi import escape +from html import escape __all__ = ['html'] @@ -93,26 +86,19 @@ class _HTML: def quote(self, arg): if arg is None: return '' - if six.text_type is not str: # Python 2 - arg = six.text_type(arg).encode(default_encoding) return escape(arg, True) def str(self, arg, encoding=None): - if isinstance(arg, six.string_types): - if not isinstance(arg, str): - arg = arg.encode(default_encoding) + if isinstance(arg, str): return arg - elif arg is None: + if isinstance(str, bytes): + return arg.encode(default_encoding) + if arg is None: return '' - elif isinstance(arg, (list, tuple)): + if isinstance(arg, (list, tuple)): return ''.join(map(self.str, arg)) - elif isinstance(arg, Element): - return str(arg) - else: - arg = six.text_type(arg) - if not isinstance(arg, str): # Python 2 - arg = arg.encode(default_encoding) - return arg + return str(arg) + html = _HTML() @@ -134,7 +120,7 @@ class Element(ET.Element if value is None: del kw[name] continue - kw[name] = six.text_type(value) + kw[name] = str(value) if name.endswith('_'): kw[name[:-1]] = value del kw[name] @@ -154,33 +140,22 @@ class Element(ET.Element if not ET.iselement(arg): if last is None: if el.text is None: - el.text = six.text_type(arg) + el.text = str(arg) else: - el.text += six.text_type(arg) + el.text += str(arg) else: if last.tail is None: - last.tail = six.text_type(arg) + last.tail = str(arg) else: - last.tail += six.text_type(arg) + last.tail += str(arg) else: last = arg el.append(last) return el - if six.text_type is str: # Python 3 - - def __str__(self): - return ET.tostring( - self, default_encoding).decode(default_encoding) - - else: - - def __str__(self): - return ET.tostring(self, default_encoding) - - def __unicode__(self): - # This is lame! - return str(self).decode(default_encoding) + def __str__(self): + return ET.tostring( + self, default_encoding).decode(default_encoding) def __repr__(self): content = str(self) diff --git a/formencode/htmlrename.py b/src/formencode/htmlrename.py index e8a5c38..d884848 100644 --- a/formencode/htmlrename.py +++ b/src/formencode/htmlrename.py @@ -1,8 +1,6 @@ """ Module to rename form fields """ -from __future__ import absolute_import - from formencode.rewritingparser import RewritingParser __all__ = ['rename', 'add_prefix'] diff --git a/formencode/i18n/FormEncode.pot b/src/formencode/i18n/FormEncode.pot index dde4513..dde4513 100644 --- a/formencode/i18n/FormEncode.pot +++ b/src/formencode/i18n/FormEncode.pot diff --git a/formencode/i18n/cs/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/cs/LC_MESSAGES/FormEncode.mo Binary files differindex 9414290..9414290 100644 --- a/formencode/i18n/cs/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/cs/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/cs/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/cs/LC_MESSAGES/FormEncode.po index 48e6bb4..48e6bb4 100644 --- a/formencode/i18n/cs/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/cs/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/da/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/da/LC_MESSAGES/FormEncode.mo Binary files differindex dee1e87..dee1e87 100644 --- a/formencode/i18n/da/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/da/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/da/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/da/LC_MESSAGES/FormEncode.po index 6d0813d..6d0813d 100644 --- a/formencode/i18n/da/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/da/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/de/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/de/LC_MESSAGES/FormEncode.mo Binary files differindex ffd8999..ffd8999 100644 --- a/formencode/i18n/de/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/de/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/de/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/de/LC_MESSAGES/FormEncode.po index b71cbd1..b71cbd1 100644 --- a/formencode/i18n/de/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/de/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/el/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/el/LC_MESSAGES/FormEncode.mo Binary files differindex a096549..a096549 100644 --- a/formencode/i18n/el/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/el/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/el/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/el/LC_MESSAGES/FormEncode.po index e5baecc..e5baecc 100644 --- a/formencode/i18n/el/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/el/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/es/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/es/LC_MESSAGES/FormEncode.mo Binary files differindex c3d7860..c3d7860 100644 --- a/formencode/i18n/es/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/es/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/es/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/es/LC_MESSAGES/FormEncode.po index 1b2a8ef..1b2a8ef 100644 --- a/formencode/i18n/es/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/es/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/et/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/et/LC_MESSAGES/FormEncode.mo Binary files differindex f833094..f833094 100644 --- a/formencode/i18n/et/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/et/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/et/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/et/LC_MESSAGES/FormEncode.po index 091b81a..091b81a 100644 --- a/formencode/i18n/et/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/et/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/fi/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/fi/LC_MESSAGES/FormEncode.mo Binary files differindex 286aa4d..286aa4d 100644 --- a/formencode/i18n/fi/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/fi/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/fi/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/fi/LC_MESSAGES/FormEncode.po index b794252..b794252 100644 --- a/formencode/i18n/fi/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/fi/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/fr/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/fr/LC_MESSAGES/FormEncode.mo Binary files differindex f51196a..f51196a 100644 --- a/formencode/i18n/fr/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/fr/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/fr/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/fr/LC_MESSAGES/FormEncode.po index 5eb418e..5eb418e 100644 --- a/formencode/i18n/fr/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/fr/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/it/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/it/LC_MESSAGES/FormEncode.mo Binary files differindex 97a1487..97a1487 100644 --- a/formencode/i18n/it/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/it/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/it/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/it/LC_MESSAGES/FormEncode.po index 5290851..5290851 100644 --- a/formencode/i18n/it/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/it/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/ja/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/ja/LC_MESSAGES/FormEncode.mo Binary files differindex f3f38a9..f3f38a9 100644 --- a/formencode/i18n/ja/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/ja/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/ja/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/ja/LC_MESSAGES/FormEncode.po index 3d1bdc8..3d1bdc8 100644 --- a/formencode/i18n/ja/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/ja/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/ko/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/ko/LC_MESSAGES/FormEncode.mo Binary files differindex b51c431..b51c431 100644 --- a/formencode/i18n/ko/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/ko/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/ko/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/ko/LC_MESSAGES/FormEncode.po index e249992..e249992 100644 --- a/formencode/i18n/ko/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/ko/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/lt/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/lt/LC_MESSAGES/FormEncode.mo Binary files differindex f23e1d8..f23e1d8 100644 --- a/formencode/i18n/lt/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/lt/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/lt/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/lt/LC_MESSAGES/FormEncode.po index afb3aaa..afb3aaa 100644 --- a/formencode/i18n/lt/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/lt/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/mk/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/mk/LC_MESSAGES/FormEncode.mo Binary files differindex 7fed059..7fed059 100644 --- a/formencode/i18n/mk/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/mk/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/mk/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/mk/LC_MESSAGES/FormEncode.po index c73c542..c73c542 100644 --- a/formencode/i18n/mk/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/mk/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.mo Binary files differindex 84512b7..84512b7 100644 --- a/formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.po index 573d77a..573d77a 100644 --- a/formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/nl/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/nl/LC_MESSAGES/FormEncode.mo Binary files differindex 0213758..0213758 100644 --- a/formencode/i18n/nl/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/nl/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/nl/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/nl/LC_MESSAGES/FormEncode.po index 1fd58f2..1fd58f2 100644 --- a/formencode/i18n/nl/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/nl/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/pl/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/pl/LC_MESSAGES/FormEncode.mo Binary files differindex df3a45b..df3a45b 100644 --- a/formencode/i18n/pl/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/pl/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/pl/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/pl/LC_MESSAGES/FormEncode.po index 5960a09..5960a09 100644 --- a/formencode/i18n/pl/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/pl/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.mo Binary files differindex 3807b3a..3807b3a 100644 --- a/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.po index 94b6bcf..94b6bcf 100644 --- a/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.mo Binary files differindex 305a236..305a236 100644 --- a/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.po index ef5ffd8..ef5ffd8 100644 --- a/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/ru/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/ru/LC_MESSAGES/FormEncode.mo Binary files differindex 5582117..5582117 100644 --- a/formencode/i18n/ru/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/ru/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/ru/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/ru/LC_MESSAGES/FormEncode.po index b67e4ef..b67e4ef 100644 --- a/formencode/i18n/ru/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/ru/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/sk/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/sk/LC_MESSAGES/FormEncode.mo Binary files differindex 899b0e2..899b0e2 100644 --- a/formencode/i18n/sk/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/sk/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/sk/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/sk/LC_MESSAGES/FormEncode.po index 589a3ab..589a3ab 100644 --- a/formencode/i18n/sk/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/sk/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/sl/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/sl/LC_MESSAGES/FormEncode.mo Binary files differindex 8e4a8df..8e4a8df 100644 --- a/formencode/i18n/sl/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/sl/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/sl/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/sl/LC_MESSAGES/FormEncode.po index 56702f5..56702f5 100644 --- a/formencode/i18n/sl/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/sl/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/sr/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/sr/LC_MESSAGES/FormEncode.mo Binary files differindex 47c90bc..47c90bc 100644 --- a/formencode/i18n/sr/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/sr/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/sr/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/sr/LC_MESSAGES/FormEncode.po index 6fc7c16..6fc7c16 100644 --- a/formencode/i18n/sr/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/sr/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/sr@latin/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/sr@latin/LC_MESSAGES/FormEncode.mo Binary files differindex 2c89c4b..2c89c4b 100644 --- a/formencode/i18n/sr@latin/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/sr@latin/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/sr@latin/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/sr@latin/LC_MESSAGES/FormEncode.po index 6e43148..6e43148 100644 --- a/formencode/i18n/sr@latin/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/sr@latin/LC_MESSAGES/FormEncode.po diff --git a/src/formencode/i18n/sv/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/sv/LC_MESSAGES/FormEncode.mo Binary files differnew file mode 100644 index 0000000..5f3abb7 --- /dev/null +++ b/src/formencode/i18n/sv/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/sv/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/sv/LC_MESSAGES/FormEncode.po index aa71a92..aa71a92 100644 --- a/formencode/i18n/sv/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/sv/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/tr/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/tr/LC_MESSAGES/FormEncode.mo Binary files differindex ea96dd1..ea96dd1 100644 --- a/formencode/i18n/tr/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/tr/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/tr/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/tr/LC_MESSAGES/FormEncode.po index 558e459..558e459 100644 --- a/formencode/i18n/tr/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/tr/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.mo Binary files differindex 7f72346..7f72346 100644 --- a/formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.po index 0409aec..0409aec 100644 --- a/formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.po diff --git a/formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.mo b/src/formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.mo Binary files differindex fea058d..fea058d 100644 --- a/formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.mo +++ b/src/formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.mo diff --git a/formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.po b/src/formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.po index de05a76..de05a76 100644 --- a/formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.po +++ b/src/formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.po diff --git a/formencode/interfaces.py b/src/formencode/interfaces.py index 2d8cfae..d110c97 100644 --- a/formencode/interfaces.py +++ b/src/formencode/interfaces.py @@ -3,14 +3,14 @@ Interfaces for FormEncode (for documentation purposes only) """ -class Attribute(object): +class Attribute: def __init__(self, description, name=None): self.description = description self.name = name -class Interface(object): +class Interface: pass diff --git a/formencode/javascript/ordering.js b/src/formencode/javascript/ordering.js index 9985e43..9985e43 100644 --- a/formencode/javascript/ordering.js +++ b/src/formencode/javascript/ordering.js diff --git a/formencode/national.py b/src/formencode/national.py index a7a76ed..b78256f 100644 --- a/formencode/national.py +++ b/src/formencode/national.py @@ -1,13 +1,11 @@ """ Country specific validators for use with FormEncode. """ -from __future__ import absolute_import import re from .api import FancyValidator from .compound import Any from .validators import Regex, Invalid, _ -import six try: import pycountry @@ -25,9 +23,7 @@ else: no_country = ('Please easy_install pycountry or validators handling' ' country names and/or languages will not work.') -############################################################ -## country lists and functions -############################################################ +# country lists and functions country_additions = [ ('BY', _('Belarus')), @@ -142,9 +138,7 @@ elif pycountry: return _l(pycountry.languages.get(alpha_2=code).name) -############################################################ -## country, state and postal code validators -############################################################ +# country, state and postal code validators class DelimitedDigitsPostalCode(Regex): """ @@ -194,8 +188,7 @@ class DelimitedDigitsPostalCode(Regex): def assembly_formatstring(self, partition_lengths, delimiter): if len(partition_lengths) == 1: return _('%d digits') % partition_lengths[0] - else: - return delimiter.join('n' * l for l in partition_lengths) + return delimiter.join('n' * n for n in partition_lengths) def assembly_grouping(self, partition_lengths, delimiter): digit_groups = ['%s' * length for length in partition_lengths] @@ -213,7 +206,7 @@ class DelimitedDigitsPostalCode(Regex): def __init__(self, partition_lengths, delimiter=None, strict=False, *args, **kw): - if isinstance(partition_lengths, six.integer_types): + if isinstance(partition_lengths, int): partition_lengths = [partition_lengths] if not delimiter: delimiter = '' @@ -405,8 +398,8 @@ class UKPostalCode(Regex): value, state) value = match.group(1).upper() if not value.startswith('BFPO'): - value = value.replace(' ', '') - value = '%s %s' % (value[:-3], value[-3:]) + value = ''.join(value.split()) + value = '%s %s' % (value[:-3], value[-3:]) return value @@ -421,19 +414,19 @@ class CountryValidator(FancyValidator): :: >>> CountryValidator.to_python('Germany') - u'DE' + 'DE' >>> CountryValidator.to_python('Finland') - u'FI' + 'FI' >>> CountryValidator.to_python('UNITED STATES') - u'US' + 'US' >>> CountryValidator.to_python('Krakovia') Traceback (most recent call last): ... Invalid: That country is not listed in ISO 3166 >>> CountryValidator.from_python('DE') - u'Germany' + 'Germany' >>> CountryValidator.from_python('FI') - u'Finland' + 'Finland' """ key_ok = True @@ -617,9 +610,7 @@ class USStateProvince(FancyValidator): return str(value).strip().upper() -############################################################ -## phone number validators -############################################################ +# phone number validators class USPhoneNumber(FancyValidator): """ @@ -779,8 +770,7 @@ class InternationalPhoneNumber(FancyValidator): value = value.encode('ascii', 'strict') except UnicodeEncodeError: raise Invalid(self.message('phoneFormat', state), value, state) - if six.text_type is str: # Python 3 - value = value.decode('ascii') + value = value.decode('ascii') value = self._mark_chars_re.sub('-', value) for f, t in [(' ', ' '), ('--', '-'), (' - ', '-'), ('- ', '-'), (' -', '-')]: @@ -802,9 +792,7 @@ class InternationalPhoneNumber(FancyValidator): return value -############################################################ -## language validators -############################################################ +# language validators class LanguageValidator(FancyValidator): """ @@ -823,17 +811,17 @@ class LanguageValidator(FancyValidator): >>> l = LanguageValidator() >>> l.to_python('German') - u'de' + 'de' >>> l.to_python('Chinese') - u'zh' + 'zh' >>> l.to_python('Klingonian') Traceback (most recent call last): ... Invalid: That language is not listed in ISO 639 >>> l.from_python('de') - u'German' + 'German' >>> l.from_python('zh') - u'Chinese' + 'Chinese' """ key_ok = True @@ -872,4 +860,5 @@ def validators(): return [name for name, value in list(globals().items()) if isinstance(value, type) and issubclass(value, FancyValidator)] + __all__ = ['Invalid'] + validators() diff --git a/formencode/rewritingparser.py b/src/formencode/rewritingparser.py index da85200..9d29b79 100644 --- a/formencode/rewritingparser.py +++ b/src/formencode/rewritingparser.py @@ -1,16 +1,8 @@ -from __future__ import absolute_import - -from six.moves import html_parser import re -import six -from six.moves import range - -try: - from html import escape -except ImportError: # Python < 3.2 - from cgi import escape -from six.moves.html_entities import name2codepoint +from html import escape +from html.entities import name2codepoint +from html.parser import HTMLParser def html_quote(v): @@ -18,26 +10,19 @@ def html_quote(v): return '' if hasattr(v, '__html__'): return v.__html__() - if isinstance(v, six.string_types): + if isinstance(v, str): return escape(v, True) - if hasattr(v, '__unicode__'): - v = six.text_type(v) - else: - v = str(v) - return escape(v, True) + return escape(str(v), True) -class RewritingParser(html_parser.HTMLParser): +class RewritingParser(HTMLParser): listener = None skip_next = False def __init__(self): self._content = [] - try: - html_parser.HTMLParser.__init__(self, convert_charrefs=False) - except TypeError: # Python < 3.4 - html_parser.HTMLParser.__init__(self) + HTMLParser.__init__(self, convert_charrefs=False) def feed(self, data): self.data_is_str = isinstance(data, str) @@ -46,7 +31,7 @@ class RewritingParser(html_parser.HTMLParser): self.source_pos = 1, 0 if self.listener: self.listener.reset() - html_parser.HTMLParser.feed(self, data) + HTMLParser.feed(self, data) _entityref_re = re.compile(r'&([a-zA-Z][-.a-zA-Z\d]*);') _charref_re = re.compile(r'&#(\d+|[xX][a-fA-F\d]+);') @@ -62,7 +47,7 @@ class RewritingParser(html_parser.HTMLParser): # If we don't recognize it, pass it through as though it # wasn't an entity ref at all return match.group(0) - return six.unichr(name2codepoint[name]) + return chr(name2codepoint[name]) def _sub_charref(self, match): num = match.group(1) @@ -70,7 +55,7 @@ class RewritingParser(html_parser.HTMLParser): num = int(num[1:], 16) else: num = int(num) - return six.unichr(num) + return chr(num) def handle_misc(self, whatever): self.write_pos() @@ -163,13 +148,12 @@ class RewritingParser(html_parser.HTMLParser): except UnicodeDecodeError as e: if self.data_is_str: e.reason += ( - " the form was passed in as an encoded string, but" - " some data or error messages were unicode strings;" - " the form should be passed in as a unicode string") + " the form was passed in as a byte string," + " but some data or error messages were normal strings;" + " the form should be passed in as a string") else: e.reason += ( - " the form was passed in as an unicode string, but" - " some data or error message was an encoded string;" - " the data and error messages should be passed in as" - " unicode strings") + " the form was passed in as a string, but some data" + " or error message was encoded; the data and error" + " messages should be passed in as strings") raise diff --git a/formencode/schema.py b/src/formencode/schema.py index f758497..7a14494 100644 --- a/formencode/schema.py +++ b/src/formencode/schema.py @@ -1,14 +1,10 @@ -from __future__ import absolute_import import warnings from .api import _, is_validator, FancyValidator, Invalid, NoDefault from . import declarative from .exc import FERuntimeWarning -import six -from six.moves import map -from six.moves import zip -__all__ = ['Schema'] +__all__ = ['Schema', 'SimpleFormValidator'] class Schema(FancyValidator): @@ -85,7 +81,7 @@ class Schema(FancyValidator): # Scan through the class variables we've defined *just* # for this subclass, looking for validators (both classes # and instances): - for key, value in six.iteritems(new_attrs): + for key, value in new_attrs.items(): if key in ('pre_validators', 'chained_validators'): if is_validator(value): msg = "Any validator with the name %s will be ignored." % \ @@ -100,12 +96,12 @@ class Schema(FancyValidator): elif key in cls.fields: del cls.fields[key] - for name, value in six.iteritems(cls.fields): + for name, value in cls.fields.items(): cls.add_field(name, value) def __initargs__(self, new_attrs): self.fields = self.fields.copy() - for key, value in six.iteritems(new_attrs): + for key, value in new_attrs.items(): if key in ('pre_validators', 'chained_validators'): if is_validator(value): msg = "Any validator with the name %s will be ignored." % \ @@ -249,9 +245,7 @@ class Schema(FancyValidator): previous_full_dict = getattr(state, 'full_dict', None) state.full_dict = value_dict try: - __traceback_info__ = None - for name, value in six.iteritems(value_dict): - __traceback_info__ = 'for_python in %s' % name + for name, value in value_dict.items(): try: unused.remove(name) except ValueError: @@ -268,8 +262,6 @@ class Schema(FancyValidator): except Invalid as e: errors[name] = e - del __traceback_info__ - for name in unused: validator = self.fields[name] if state is not None: @@ -290,7 +282,6 @@ class Schema(FancyValidator): state.key = previous_key for validator in pre: - __traceback_info__ = 'for_python pre_validator %s' % validator new = validator.from_python(new, state) return new @@ -331,53 +322,44 @@ class Schema(FancyValidator): result = [] result.extend(self.pre_validators) result.extend(self.chained_validators) - result.extend(six.itervalues(self.fields)) + result.extend(self.fields.values()) return result def is_empty(self, value): - ## Generally nothing is empty for us + # generally nothing is empty for us return False def empty_value(self, value): return {} def _value_is_iterator(self, value): - if isinstance(value, six.string_types + (six.binary_type, )): + if isinstance(value, (bytes, str)): return False - elif isinstance(value, (list, tuple)): + if isinstance(value, (list, tuple)): return True try: for _v in value: break return True - ## @@: Should this catch any other errors?: - except TypeError: + except TypeError: # @@: should this catch any other errors? return False def format_compound_error(v, indent=0): if isinstance(v, Exception): - try: - return str(v) - except (UnicodeDecodeError, UnicodeEncodeError): - # There doesn't seem to be a better way to get a str() - # version if possible, and unicode() if necessary, because - # testing for the presence of a __unicode__ method isn't - # enough - return six.text_type(v) - elif isinstance(v, dict): + return str(v) + if isinstance(v, dict): return ('%s\n' % (' ' * indent)).join( '%s: %s' % (k, format_compound_error(value, indent=len(k) + 2)) - for k, value in sorted(six.iteritems(v)) if value is not None) - elif isinstance(v, list): + for k, value in sorted(v.items()) if value is not None) + if isinstance(v, list): return ('%s\n' % (' ' * indent)).join( '%s' % (format_compound_error(value, indent=indent)) for value in v if value is not None) - elif isinstance(v, six.string_types): + if isinstance(v, str): return v - else: - assert False, "I didn't expect something like %s" % repr(v) + raise TypeError("I didn't expect something like %r" % v) def merge_dicts(d1, d2): @@ -387,15 +369,14 @@ def merge_dicts(d1, d2): def merge_values(v1, v2): - if isinstance(v1, six.string_types) and isinstance(v2, six.string_types): + if isinstance(v1, str) and isinstance(v2, str): return v1 + '\n' + v2 - elif isinstance(v1, (list, tuple)) and isinstance(v2, (list, tuple)): + if isinstance(v1, (list, tuple)) and isinstance(v2, (list, tuple)): return merge_lists(v1, v2) - elif isinstance(v1, dict) and isinstance(v2, dict): + if isinstance(v1, dict) and isinstance(v2, dict): return merge_dicts(v1, v2) - else: - # @@: Should we just ignore errors? Seems we do... - return v1 + # @@: Should we just ignore errors? Seems we do... + return v1 def merge_lists(l1, l2): @@ -478,18 +459,15 @@ class SimpleFormValidator(FancyValidator): errors = self.func(value_dict, state, self) if not errors: return value_dict - if isinstance(errors, six.string_types): + if isinstance(errors, str): raise Invalid(errors, value_dict, state) - elif isinstance(errors, dict): + if isinstance(errors, dict): raise Invalid( format_compound_error(errors), value_dict, state, error_dict=errors) - elif isinstance(errors, Invalid): + if isinstance(errors, Invalid): raise errors - else: - raise TypeError( - "Invalid error value: %r" % errors) - return value_dict + raise TypeError("Invalid error value: %r" % errors) validate_partial = to_python diff --git a/formencode/validators.py b/src/formencode/validators.py index 1122287..f2011b4 100644 --- a/formencode/validators.py +++ b/src/formencode/validators.py @@ -1,19 +1,14 @@ -## FormEncode, a Form processor -## Copyright (C) 2003, Ian Bicking <ianb@colorstudy.com> +# FormEncode, a Form processor +# Copyright (C) 2003, Ian Bicking <ianb@colorstudy.com> """ Validator/Converters for use with FormEncode. """ -from __future__ import absolute_import import cgi -import locale import re import warnings from encodings import idna -import six -from six.moves import map -from six.moves import range try: # import dnspython import dns.resolver @@ -37,18 +32,16 @@ from .api import (FancyValidator, Identity, Invalid, NoDefault, Validator, assert Identity and Invalid and NoDefault # silence unused import warnings # Dummy i18n translation function, nothing is translated here. -# Instead this is actually done in api.message. +# Instead, this is actually done in api.message. # The surrounding _('string') of the strings is only for extracting # the strings automatically. # If you run pygettext with this source comment this function out temporarily. _ = lambda s: s -############################################################ -## Utility methods -############################################################ - +# Utility methods # These all deal with accepting both datetime and mxDateTime modules and types + datetime_module = None mxDateTime_module = None @@ -99,9 +92,7 @@ def datetime_isotime(module): return module.ISO.Time -############################################################ -## Wrapper Validators -############################################################ +# Wrapper Validators class ConfirmType(FancyValidator): """ @@ -314,9 +305,7 @@ class Constant(FancyValidator): _convert_from_python = _convert_to_python -############################################################ -## Normal validators -############################################################ +# Normal validators class MaxLength(FancyValidator): """ @@ -517,13 +506,13 @@ class Regex(FancyValidator): def __init__(self, *args, **kw): FancyValidator.__init__(self, *args, **kw) - if isinstance(self.regex, six.string_types): + if isinstance(self.regex, str): ops = 0 - assert not isinstance(self.regexOps, six.string_types), ( + assert not isinstance(self.regexOps, str), ( "regexOps should be a list of options from the re module " "(names, or actual values)") for op in self.regexOps: - if isinstance(op, six.string_types): + if isinstance(op, str): ops |= getattr(re, op) else: ops |= op @@ -531,13 +520,13 @@ class Regex(FancyValidator): def _validate_python(self, value, state): self.assert_string(value, state) - if self.strip and isinstance(value, six.string_types): + if self.strip and isinstance(value, str): value = value.strip() if not self.regex.search(value): raise Invalid(self.message('invalid', state), value, state) def _convert_to_python(self, value, state): - if self.strip and isinstance(value, six.string_types): + if self.strip and isinstance(value, str): return value.strip() return value @@ -619,7 +608,7 @@ class OneOf(FancyValidator): try: items = '; '.join(map(str, self.list)) except UnicodeError: - items = '; '.join(map(six.text_type, self.list)) + items = '; '.join(map(str, self.list)) raise Invalid( self.message('notIn', state, items=items, value=value), value, state) @@ -694,16 +683,15 @@ class DictConverter(FancyValidator): state, items=items), value, state) def _convert_from_python(self, value, state): - for k, v in six.iteritems(self.dict): + for k, v in self.dict.items(): if value == v: return k if self.hideDict: raise Invalid(self.message('valueNotFound', state), value, state) - else: - items = '; '.join(map(repr, six.itervalues(self.dict))) - raise Invalid( - self.message('chooseValue', state, - value=repr(value), items=items), value, state) + items = '; '.join(map(repr, self.dict.values())) + raise Invalid( + self.message('chooseValue', state, + value=repr(value), items=items), value, state) class IndexListConverter(FancyValidator): @@ -816,13 +804,6 @@ class DateValidator(FancyValidator): def _validate_python(self, value, state): date_format = self.message('date_format', state) - if (str is not six.text_type # Python 2 - and isinstance(date_format, six.text_type)): - # strftime uses the locale encoding, not Unicode - encoding = locale.getlocale(locale.LC_TIME)[1] or 'utf-8' - date_format = date_format.encode(encoding) - else: - encoding = None if self.earliest_date: if callable(self.earliest_date): earliest_date = self.earliest_date() @@ -830,8 +811,6 @@ class DateValidator(FancyValidator): earliest_date = self.earliest_date if value < earliest_date: date_formatted = earliest_date.strftime(date_format) - if encoding: - date_formatted = date_formatted.decode(encoding) raise Invalid( self.message('after', state, date=date_formatted), value, state) @@ -842,8 +821,6 @@ class DateValidator(FancyValidator): latest_date = self.latest_date if value > latest_date: date_formatted = latest_date.strftime(date_format) - if encoding: - date_formatted = date_formatted.decode(encoding) raise Invalid( self.message('before', state, date=date_formatted), value, state) @@ -852,8 +829,6 @@ class DateValidator(FancyValidator): now = datetime_now(dt_mod) if value < now: date_formatted = now.strftime(date_format) - if encoding: - date_formatted = date_formatted.decode(encoding) raise Invalid( self.message('future', state, date=date_formatted), value, state) @@ -866,8 +841,6 @@ class DateValidator(FancyValidator): dt_mod, value.year, value.month, value.day) if value_as_date < today: date_formatted = now.strftime(date_format) - if encoding: - date_formatted = date_formatted.decode(encoding) raise Invalid( self.message('future', state, date=date_formatted), value, state) @@ -1012,8 +985,6 @@ class Number(RangeValidator): class ByteString(FancyValidator): """Convert to byte string, treating empty things as the empty string. - Under Python 2.x you can also use the alias `String` for this validator. - Also takes a `max` and `min` argument, and the string length must fall in that range. @@ -1065,27 +1036,27 @@ class ByteString(FancyValidator): def _convert_to_python(self, value, state): if value is None: value = '' - elif not isinstance(value, six.string_types): + elif not isinstance(value, str): try: value = bytes(value) except UnicodeEncodeError: - value = six.text_type(value) - if self.encoding is not None and isinstance(value, six.text_type): + value = str(value) + if self.encoding is not None and isinstance(value, str): value = value.encode(self.encoding) return value def _convert_from_python(self, value, state): if value is None: value = '' - elif not isinstance(value, six.string_types): + elif not isinstance(value, str): if isinstance(value, (list, tuple)): value = self.list_joiner.join( self._convert_from_python(v, state) for v in value) try: value = str(value) except UnicodeEncodeError: - value = six.text_type(value) - if self.encoding is not None and isinstance(value, six.text_type): + value = str(value) + if self.encoding is not None and isinstance(value, str): value = value.encode(self.encoding) if self.strip: value = value.strip() @@ -1096,11 +1067,8 @@ class ByteString(FancyValidator): return if value is None: value = '' - elif not isinstance(value, six.string_types): - try: - value = str(value) - except UnicodeEncodeError: - value = six.text_type(value) + elif not isinstance(value, str): + value = str(value) if self.max is not None and len(value) > self.max: raise Invalid( self.message('tooLong', state, max=self.max), value, state) @@ -1117,10 +1085,10 @@ class UnicodeString(ByteString): This is implemented as a specialization of the ByteString class. - Under Python 3.x you can also use the alias `String` for this validator. + You can also use the alias `String` for this validator. In addition to the String arguments, an encoding argument is also - accepted. By default the encoding will be utf-8. You can overwrite + accepted. By default, the encoding will be utf-8. You can overwrite this using the encoding parameter. You can also set inputEncoding and outputEncoding differently. An inputEncoding of None means "do not decode", an outputEncoding of None means "do not encode". @@ -1129,11 +1097,11 @@ class UnicodeString(ByteString): :: - >>> UnicodeString().to_python(None) == u'' + >>> UnicodeString().to_python(None) == '' True - >>> UnicodeString().to_python([]) == u'' + >>> UnicodeString().to_python([]) == '' True - >>> UnicodeString(encoding='utf-7').to_python('Ni Ni Ni') == u'Ni Ni Ni' + >>> UnicodeString(encoding='utf-7').to_python('Ni Ni Ni') == 'Ni Ni Ni' True """ @@ -1152,44 +1120,31 @@ class UnicodeString(ByteString): def _convert_to_python(self, value, state): if not value: - return u'' - if isinstance(value, six.text_type): + return '' + if isinstance(value, str): return value - if not isinstance(value, six.text_type): - if hasattr(value, '__unicode__'): - value = six.text_type(value) - return value - if not (six.text_type is str # Python 3 - and isinstance(value, bytes) and self.inputEncoding): - value = str(value) - if self.inputEncoding and not isinstance(value, six.text_type): + if isinstance(value, bytes): try: - value = six.text_type(value, self.inputEncoding) + return value.decode( + encoding=self.inputEncoding or self.encoding) except UnicodeDecodeError: raise Invalid(self.message('badEncoding', state), value, state) - except TypeError: - raise Invalid( - self.message('badType', state, - type=type(value), value=value), value, state) - return value + return str(value) def _convert_from_python(self, value, state): - if not isinstance(value, six.text_type): - if hasattr(value, '__unicode__'): - value = six.text_type(value) - else: - value = str(value) - if self.outputEncoding and isinstance(value, six.text_type): + if not isinstance(value, str): + value = str(value) + if self.outputEncoding and isinstance(value, str): value = value.encode(self.outputEncoding) return value def empty_value(self, value): - return u'' + return '' # Provide proper alias for native strings -String = UnicodeString if str is six.text_type else ByteString +String = UnicodeString class Set(FancyValidator): @@ -1213,11 +1168,11 @@ class Set(FancyValidator): ['this', 'that'] >>> s = Set(use_set=True) >>> s.to_python(None) - set([]) + set() >>> s.to_python('this') - set(['this']) + {'this'} >>> s.to_python(('this',)) - set(['this']) + {'this'} """ use_set = False @@ -1357,8 +1312,7 @@ class Email(FancyValidator): value, state) try: idna_domain = [idna.ToASCII(p) for p in domain.split('.')] - if six.text_type is str: # Python 3 - idna_domain = [p.decode('ascii') for p in idna_domain] + idna_domain = [p.decode('ascii') for p in idna_domain] idna_domain = '.'.join(idna_domain) except UnicodeError: # UnicodeError: label empty or too long @@ -1378,10 +1332,10 @@ class Email(FancyValidator): import socket try: try: - dns.resolver.query(domain, 'MX') + dns.resolver.resolve(domain, 'MX') except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): try: - dns.resolver.query(domain, 'A') + dns.resolver.resolve(domain, 'A') except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): raise Invalid( self.message('domainDoesNotExist', @@ -1460,13 +1414,13 @@ class URL(FancyValidator): You may set allow_idna to False to change this behavior:: >>> URL(allow_idna=True).to_python( - ... u'http://\u0433\u0443\u0433\u043b.\u0440\u0444') + ... 'http://\u0433\u0443\u0433\u043b.\u0440\u0444') 'http://xn--c1aay4a.xn--p1ai' >>> URL(allow_idna=True, add_http=True).to_python( - ... u'\u0433\u0443\u0433\u043b.\u0440\u0444') + ... '\u0433\u0443\u0433\u043b.\u0440\u0444') 'http://xn--c1aay4a.xn--p1ai' >>> URL(allow_idna=False).to_python( - ... u'http://\u0433\u0443\u0433\u043b.\u0440\u0444') + ... 'http://\u0433\u0443\u0433\u043b.\u0440\u0444') Traceback (most recent call last): ... Invalid: That is not a valid URL @@ -1530,7 +1484,7 @@ class URL(FancyValidator): def _encode_idna(self, url): global urlparse if urlparse is None: - from six.moves.urllib import parse as urlparse + from urllib import parse as urlparse try: scheme, netloc, path, params, query, fragment = urlparse.urlparse( url) @@ -1538,8 +1492,7 @@ class URL(FancyValidator): return url try: netloc = netloc.encode('idna') - if six.text_type is str: # Python 3 - netloc = netloc.decode('ascii') + netloc = netloc.decode('ascii') return str(urlparse.urlunparse((scheme, netloc, path, params, query, fragment))) except UnicodeError: @@ -1548,9 +1501,9 @@ class URL(FancyValidator): def _check_url_exists(self, url, state): global http_client, urlparse, socket if http_client is None: - from six.moves import http_client + from http import client as http_client if urlparse is None: - from six.moves.urllib import parse as urlparse + import parse as urlparse if socket is None: import socket scheme, netloc, path, params, query, fragment = urlparse.urlparse( @@ -1569,28 +1522,13 @@ class URL(FancyValidator): conn.close() except http_client.HTTPException as e: e = str(e) - if str is not six.text_type: # Python 2 - try: - e = e.decode('utf-8') - except UnicodeDecodeError: - try: - e = e.decode('latin-1') - except UnicodeDecodeError: - e = e.decode('ascii', 'replace') raise Invalid( - self.message('httpError', state, error=e), state, url) + self.message('httpError', state, error=str(e)), + state, url) except socket.error as e: - e = str(e) - if str is not six.text_type: # Python 2 - try: - e = e.decode('utf-8') - except UnicodeDecodeError: - try: - e = e.decode('latin-1') - except UnicodeDecodeError: - e = e.decode('ascii', 'replace') raise Invalid( - self.message('socketError', state, error=e), state, url) + self.message('socketError', state, error=str(e)), + state, url) else: if res.status == 404: raise Invalid( @@ -1710,7 +1648,7 @@ class XRI(FancyValidator): is not valid. """ - if not isinstance(value, six.string_types): + if not isinstance(value, str): raise Invalid( self.message('badType', state, type=str(type(value)), value=value), value, state) @@ -1890,7 +1828,7 @@ class FileUploadKeeper(FancyValidator): if isinstance(upload, cgi.FieldStorage): filename = upload.filename content = upload.value - elif isinstance(upload, six.string_types) and upload: + elif isinstance(upload, str) and upload: filename = None # @@: Should this encode upload if it is unicode? content = upload @@ -1998,19 +1936,19 @@ class DateConverter(FancyValidator): _date_re = dict( dmy=re.compile( r'^\s*(\d\d?)[\-\./\\](\d\d?|%s)[\-\./\\](\d\d\d?\d?)\s*$' - % '|'.join(_month_names), re.I), + % '|'.join(_month_names), re.I), mdy=re.compile( r'^\s*(\d\d?|%s)[\-\./\\](\d\d?)[\-\./\\](\d\d\d?\d?)\s*$' - % '|'.join(_month_names), re.I), + % '|'.join(_month_names), re.I), ymd=re.compile( r'^\s*(\d\d\d?\d?)[\-\./\\](\d\d?|%s)[\-\./\\](\d\d?)\s*$' - % '|'.join(_month_names), re.I), + % '|'.join(_month_names), re.I), my=re.compile( r'^\s*(\d\d?|%s)[\-\./\\](\d\d\d?\d?)\s*$' - % '|'.join(_month_names), re.I), + % '|'.join(_month_names), re.I), ym=re.compile( r'^\s*(\d\d\d?\d?)[\-\./\\](\d\d?|%s)\s*$' - % '|'.join(_month_names), re.I)) + % '|'.join(_month_names), re.I)) _formats = dict(d='%d', m='%m', y='%Y') @@ -2307,7 +2245,7 @@ class TimeConverter(FancyValidator): return hour, minute, second def _convert_from_python(self, value, state): - if isinstance(value, six.string_types): + if isinstance(value, str): return value if hasattr(value, 'hour'): hour, minute = value.hour, value.minute @@ -2334,8 +2272,6 @@ class TimeConverter(FancyValidator): return '%i:%02i%s' % (hour, minute, ampm) - - class ISODateTimeConverter(FancyValidator): """ Converts fields which contain both date and time, in the @@ -2376,7 +2312,6 @@ class ISODateTimeConverter(FancyValidator): return value.isoformat("T") - def PostalCode(*kw, **kwargs): deprecation_warning("please use formencode.national.USPostalCode") from formencode.national import USPostalCode @@ -2448,7 +2383,7 @@ class StringBool(FancyValidator): # originally from TurboGears 1 string=_('Value should be %(true)r or %(false)r')) def _convert_to_python(self, value, state): - if isinstance(value, six.string_types): + if isinstance(value, str): value = value.strip().lower() if value in self.true_values: return True @@ -2463,6 +2398,7 @@ class StringBool(FancyValidator): # originally from TurboGears 1 def _convert_from_python(self, value, state): return (self.true_values if value else self.false_values)[0] + # Should deprecate: StringBoolean = StringBool @@ -2765,11 +2701,13 @@ class RequireIfMissing(FormValidator): raise Invalid( _('You must give a value for %s') % self.required, value_dict, state, - error_dict={self.required: - Invalid(self.message('empty', state), + error_dict={ + self.required: Invalid( + self.message('empty', state), value_dict.get(self.required), state)}) return value_dict + RequireIfPresent = RequireIfMissing @@ -2878,9 +2816,9 @@ class FieldsMatch(FormValidator): else: errors[name] = self.message('invalidNoMatch', state) if errors: - error_list = sorted(six.iteritems(errors)) error_message = '<br>\n'.join( - '%s: %s' % (name, value) for name, value in error_list) + '%s: %s' % (name, value) + for name, value in sorted(errors.items())) raise Invalid(error_message, field_dict, state, error_dict=errors) @@ -2937,10 +2875,9 @@ class CreditCardValidator(FormValidator): def _validate_python(self, field_dict, state): errors = self._validateReturn(field_dict, state) if errors: - error_list = sorted(six.iteritems(errors)) raise Invalid( '<br>\n'.join('%s: %s' % (name, value) - for name, value in error_list), + for name, value in sorted(errors.items())), field_dict, state, error_dict=errors) def _validateReturn(self, field_dict, state): @@ -3052,10 +2989,9 @@ class CreditCardExpires(FormValidator): def _validate_python(self, field_dict, state): errors = self._validateReturn(field_dict, state) if errors: - error_list = sorted(six.iteritems(errors)) raise Invalid( '<br>\n'.join('%s: %s' % (name, value) - for name, value in error_list), + for name, value in sorted(errors.items())), field_dict, state, error_dict=errors) def _validateReturn(self, field_dict, state): @@ -3076,15 +3012,17 @@ class CreditCardExpires(FormValidator): dt_mod, next_month_year, next_month, 1) assert expires_date > today except ValueError: - return {self.cc_expires_month_field: - self.message('notANumber', state), - self.cc_expires_year_field: - self.message('notANumber', state)} + return { + self.cc_expires_month_field: + self.message('notANumber', state), + self.cc_expires_year_field: + self.message('notANumber', state)} except AssertionError: - return {self.cc_expires_month_field: - self.message('invalidNumber', state), - self.cc_expires_year_field: - self.message('invalidNumber', state)} + return { + self.cc_expires_month_field: + self.message('invalidNumber', state), + self.cc_expires_year_field: + self.message('invalidNumber', state)} class CreditCardSecurityCode(FormValidator): @@ -3126,11 +3064,10 @@ class CreditCardSecurityCode(FormValidator): def _validate_python(self, field_dict, state): errors = self._validateReturn(field_dict, state) if errors: - error_list = sorted(six.iteritems(errors)) raise Invalid( '<br>\n'.join( '%s: %s' % (name, value) - for name, value in error_list), + for name, value in errors.items()), field_dict, state, error_dict=errors) def _validateReturn(self, field_dict, state): @@ -3150,7 +3087,8 @@ class CreditCardSecurityCode(FormValidator): def validators(): """Return the names of all validators in this module.""" - return [name for name, value in six.iteritems(globals()) - if isinstance(value, type) and issubclass(value, Validator)] + return [name for name, value in globals().items() + if isinstance(value, type) and issubclass(value, Validator)] + __all__ = ['Invalid'] + validators() diff --git a/formencode/variabledecode.py b/src/formencode/variabledecode.py index 7f93416..4fe9a1c 100644 --- a/formencode/variabledecode.py +++ b/src/formencode/variabledecode.py @@ -19,11 +19,8 @@ a dict or list, both variable_decode and variable_encode take dict_char and list_char keyword args. For example, to have the GET/POST variables, ``a_1=something`` as a list, you would use a ``list_char='_'``. """ -from __future__ import absolute_import from .api import FancyValidator -import six -from six.moves import range __all__ = ['variable_decode', 'variable_encode', 'NestedVariables'] @@ -31,7 +28,7 @@ __all__ = ['variable_decode', 'variable_encode', 'NestedVariables'] def _sort_key(item): """Robust sort key that sorts items with invalid keys last. - This is used to make sorting behave the same across Python 2 and 3. + This is used to make sorting work with keys that have difference types. """ key = item[0] return not isinstance(key, int), key @@ -42,7 +39,7 @@ def variable_decode(d, dict_char='.', list_char='-'): result = {} dicts_to_sort = set() known_lengths = {} - for key, value in six.iteritems(d): + for key, value in d.items(): keys = key.split(dict_char) new_keys = [] was_repetition_count = False @@ -104,10 +101,10 @@ def variable_decode(d, dict_char='.', list_char='-'): to_sort = to_sort[sub_key] if None in to_sort: none_values = [(0, x) for x in to_sort.pop(None)] - none_values.extend(six.iteritems(to_sort)) + none_values.extend(to_sort.items()) to_sort = none_values else: - to_sort = six.iteritems(to_sort) + to_sort = to_sort.items() to_sort = [x[1] for x in sorted(to_sort, key=_sort_key)] if key in known_lengths: if len(to_sort) < known_lengths[key]: @@ -123,7 +120,7 @@ def variable_encode(d, prepend='', result=None, add_repetitions=True, if result is None: result = {} if isinstance(d, dict): - for key, value in six.iteritems(d): + for key, value in d.items(): if key is None: name = prepend elif not prepend: diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..922f3c1 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,26 @@ +import os +import sys +import warnings + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) + +# Make sure messages are not translated when running the tests +# (setting the environment variable here may be too late already, +# in this case you must set it manually before running the tests). +os.environ['LANGUAGE'] = 'C' + +# Enable deprecation warnings (which are disabled by default) +warnings.simplefilter('default') + +try: + try: + import importlib.metadata as metadata + try: + metadata.distribution("FormEncode") + except metadata.PackageNotFoundError as error: + raise ImportError from error + except ImportError: # Python < 3.8 + import pkg_resources + pkg_resources.require('FormEncode') +except ImportError as error: + raise ImportError("Install FormEncode before running the tests") from error diff --git a/formencode/tests/htmlfill_data/data-error1.txt b/tests/htmlfill_data/data-error1.txt index e663cee..e663cee 100644 --- a/formencode/tests/htmlfill_data/data-error1.txt +++ b/tests/htmlfill_data/data-error1.txt diff --git a/formencode/tests/htmlfill_data/data-fill1.txt b/tests/htmlfill_data/data-fill1.txt index ed6b5ec..ed6b5ec 100644 --- a/formencode/tests/htmlfill_data/data-fill1.txt +++ b/tests/htmlfill_data/data-fill1.txt diff --git a/formencode/tests/htmlfill_data/data-fill2.txt b/tests/htmlfill_data/data-fill2.txt index d5e40be..d5e40be 100644 --- a/formencode/tests/htmlfill_data/data-fill2.txt +++ b/tests/htmlfill_data/data-fill2.txt diff --git a/formencode/tests/htmlfill_data/data-fill3.txt b/tests/htmlfill_data/data-fill3.txt index db96d2e..db96d2e 100644 --- a/formencode/tests/htmlfill_data/data-fill3.txt +++ b/tests/htmlfill_data/data-fill3.txt diff --git a/formencode/tests/htmlfill_data/data-fill4.txt b/tests/htmlfill_data/data-fill4.txt index 4811b40..4811b40 100644 --- a/formencode/tests/htmlfill_data/data-fill4.txt +++ b/tests/htmlfill_data/data-fill4.txt diff --git a/formencode/tests/htmlfill_data/data-form-last-element.txt b/tests/htmlfill_data/data-form-last-element.txt index eabbaae..eabbaae 100644 --- a/formencode/tests/htmlfill_data/data-form-last-element.txt +++ b/tests/htmlfill_data/data-form-last-element.txt diff --git a/formencode/tests/htmlfill_data/data-schema1.txt b/tests/htmlfill_data/data-schema1.txt index 7ab2f49..7ab2f49 100644 --- a/formencode/tests/htmlfill_data/data-schema1.txt +++ b/tests/htmlfill_data/data-schema1.txt diff --git a/formencode/tests/non_empty.txt b/tests/non_empty.txt index 8db8ec1..bbed33e 100644 --- a/formencode/tests/non_empty.txt +++ b/tests/non_empty.txt @@ -1,6 +1,4 @@ michele@ionic:~/Progetti/TurboGears/svn/thirdparty/formencode/formencode$ python -Python 2.4.3 (#2, Mar 30 2006, 14:45:01) -[GCC 4.0.3 (Ubuntu 4.0.3-1ubuntu3)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> from formencode import validators >>> int = validators.Int() diff --git a/formencode/tests/test_cc.py b/tests/test_cc.py index c0e5319..429ba11 100644 --- a/formencode/tests/test_cc.py +++ b/tests/test_cc.py @@ -1,6 +1,3 @@ -from __future__ import absolute_import -# -*- coding: utf-8 -*- - import unittest from formencode import Invalid @@ -24,7 +21,7 @@ class TestCreditCardValidator(unittest.TestCase): def test_validate(self): validate, message = self.validate, self.message - self.assertTrue(validate('visa', '4' + '1' * 15) is None) + self.assertIsNone(validate('visa', '4' + '1' * 15)) self.assertEqual(validate('visa', '5' + '1' * 12), message('invalidNumber')) self.assertEqual(validate('visa', '4' + '1' * 11 + '2'), @@ -52,7 +49,7 @@ class TestCreditCardExpires(unittest.TestCase): def test_validate(self): validate, message = self.validate, self.message - self.assertTrue(validate('11', '2250') is None) + self.assertIsNone(validate('11', '2250')) self.assertEqual(validate('11', 'test'), message('notANumber')) self.assertEqual(validate('test', '2250'), message('notANumber')) self.assertEqual(validate('10', '2005'), message('invalidNumber')) diff --git a/formencode/tests/test_compound.py b/tests/test_compound.py index d74f8d6..c8f838e 100644 --- a/formencode/tests/test_compound.py +++ b/tests/test_compound.py @@ -1,6 +1,3 @@ -from __future__ import absolute_import -# -*- coding: utf-8 -*- - import unittest from formencode import compound, Invalid @@ -14,8 +11,8 @@ class TestCompoundValidator(unittest.TestCase): def test_repr(self): r = repr(self.validator) - self.assertFalse('validatorArgs' in r) - self.assertTrue('validators=[]' in r) + self.assertNotIn('validatorArgs', r) + self.assertIn('validators=[]', r) def test_to_python(self): self.assertRaises(NotImplementedError, @@ -38,7 +35,7 @@ class TestAllCompoundValidator(unittest.TestCase): def test_repr(self): r = repr(self.validator) - self.assertFalse('validatorArgs' in r) + self.assertNotIn('validatorArgs', r) self.assertEqual(r.count('DictConverter'), 2) def test_to_python(self): @@ -61,7 +58,7 @@ class TestAnyCompoundValidator(unittest.TestCase): def test_repr(self): r = repr(self.validator) - self.assertFalse('validatorArgs' in r) + self.assertNotIn('validatorArgs', r) self.assertEqual(r.count('DictConverter'), 3) def test_to_python(self): @@ -76,7 +73,7 @@ class TestAnyCompoundValidator(unittest.TestCase): try: self.validator.to_python(3) except Invalid as e: - self.assertTrue('Enter a value from: 2' in str(e)) + self.assertIn('Enter a value from: 2', str(e)) else: self.fail('Invalid should be raised when no validator succeeds.') @@ -93,7 +90,7 @@ class TestPipeCompoundValidator(unittest.TestCase): def test_repr(self): r = repr(self.validator) - self.assertFalse('validatorArgs' in r) + self.assertNotIn('validatorArgs', r) self.assertEqual(r.count('DictConverter'), 2) def test_to_python(self): diff --git a/formencode/tests/test_context.py b/tests/test_context.py index 4c2a28c..34da8fa 100644 --- a/formencode/tests/test_context.py +++ b/tests/test_context.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import import pytest from formencode.context import Context, ContextRestoreError @@ -54,3 +53,16 @@ def test_default(): assert con.a == 2 res.restore() assert con.a == 4 + + +def test_context_manager(): + with c1.set(foo=1): + assert_is(c1, 'foo', 1) + assert_is(c1, 'foo', None) + with c1.set(foo=2), c2.set(foo='test'): + assert_is(c1, 'foo', 2) + assert_is(c2, 'foo', 'test') + change_state(c1, assert_is, c1, 'foo', 3, foo=3) + assert_is(c1, 'foo', 2) + assert_is(c1, 'foo', None) + assert not hasattr(c2, 'foo') diff --git a/formencode/tests/test_declarative.py b/tests/test_declarative.py index ffce388..1217621 100644 --- a/formencode/tests/test_declarative.py +++ b/tests/test_declarative.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import -# -*- coding: utf-8 -*- import unittest import re @@ -24,8 +22,8 @@ class TestDeclarative(unittest.TestCase): D = declarative.Declarative obj_bar = D(foo='bar', woo='par') obj_baz = obj_bar(foo='baz') - self.assertTrue(type(obj_bar) is type(obj_baz)) - self.assertTrue(obj_bar is not obj_baz) + self.assertIs(type(obj_bar), type(obj_baz)) + self.assertIsNot(obj_bar, obj_baz) self.assertEqual(obj_baz.foo, 'baz') self.assertEqual(obj_baz.woo, 'par') diff --git a/formencode/tests/test_doctest_xml_compare.py b/tests/test_doctest_xml_compare.py index 316abac..b6fa5e5 100644 --- a/formencode/tests/test_doctest_xml_compare.py +++ b/tests/test_doctest_xml_compare.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import import sys import formencode.doctest_xml_compare as dxml diff --git a/formencode/tests/test_doctests.py b/tests/test_doctests.py index a3288ae..b02d22d 100644 --- a/formencode/tests/test_doctests.py +++ b/tests/test_doctests.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import import os import sys import doctest @@ -9,7 +8,6 @@ from formencode import htmlgen from formencode import national from formencode import schema from formencode import validators -import six import pytest @@ -20,48 +18,34 @@ modules = [compound, htmlfill, htmlgen, national, schema, validators] """Text files that will have their doctests tested.""" text_files = [ - 'docs/htmlfill.txt', - 'docs/Validator.txt', - 'formencode/tests/non_empty.txt', - ] + "docs/htmlfill.txt", + "docs/Validator.txt", + "tests/non_empty.txt", +] """Used to resolve text files to absolute paths.""" -base = os.path.dirname(os.path.dirname(os.path.dirname( - os.path.abspath(__file__)))) - - -if six.text_type is str: # Python 3 - - OutputChecker = doctest.OutputChecker - - class OutputChecker3(OutputChecker): - - def check_output(self, want, got, optionflags): - if want.startswith("u'"): - want = want[1:] - elif want.startswith('set(['): - want = want[3:].replace( - '([', '{').replace('])', '}').replace('{}', 'set()') - return OutputChecker.check_output(self, want, got, optionflags) - - doctest.OutputChecker = OutputChecker3 +base = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) def doctest_file(document, verbose, raise_error): - failure_count, test_count = doctest.testfile(document, - module_relative=False, - optionflags=doctest.ELLIPSIS | doctest.IGNORE_EXCEPTION_DETAIL, - verbose=verbose) + failure_count, test_count = doctest.testfile( + document, + module_relative=False, + optionflags=doctest.ELLIPSIS | doctest.IGNORE_EXCEPTION_DETAIL, + verbose=verbose, + ) if raise_error: assert test_count > 0 assert failure_count == 0 def doctest_module(document, verbose, raise_error): - failure_count, test_count = doctest.testmod(document, - optionflags=doctest.ELLIPSIS | doctest.IGNORE_EXCEPTION_DETAIL, - verbose=verbose) + failure_count, test_count = doctest.testmod( + document, + optionflags=doctest.ELLIPSIS | doctest.IGNORE_EXCEPTION_DETAIL, + verbose=verbose, + ) if raise_error: assert test_count > 0 assert failure_count == 0 @@ -69,11 +53,14 @@ def doctest_module(document, verbose, raise_error): def set_func_description(fn, description): """Wrap function and set description attr for pytest to display.""" + def _wrapper(*a_test_args): fn(*a_test_args) + _wrapper.description = description return _wrapper + def collect_functions(): verbose = False raise_error = True @@ -82,26 +69,30 @@ def collect_functions(): name = "Doctests for %s" % (document,) if not document.startswith(os.sep): document = os.path.join(base, document) - yield set_func_description(doctest_file, name), document, \ - verbose, raise_error + yield set_func_description( + doctest_file, name + ), document, verbose, raise_error else: name = "Doctests for %s" % (document.__name__,) - yield set_func_description(doctest_module, name), document, \ - verbose, raise_error + yield set_func_description( + doctest_module, name + ), document, verbose, raise_error -@pytest.mark.parametrize("testfn,document,verbose,raise_error", list(collect_functions())) -def test_doctests(testfn,document,verbose,raise_error): +@pytest.mark.parametrize( + "testfn,document,verbose,raise_error", list(collect_functions()) +) +def test_doctests(testfn, document, verbose, raise_error): """Generate each doctest.""" testfn(document, verbose, raise_error) -if __name__ == '__main__': +if __name__ == "__main__": # Call this file directly if you want to test doctests. args = sys.argv[1:] verbose = False - if '-v' in args: - args.remove('-v') + if "-v" in args: + args.remove("-v") verbose = True if not args: args = text_files + modules diff --git a/formencode/tests/test_email.py b/tests/test_email.py index f79aea8..ac40b7a 100644 --- a/formencode/tests/test_email.py +++ b/tests/test_email.py @@ -1,12 +1,8 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import unicode_literals import unittest from formencode import Invalid from formencode.validators import Email -import six class TestEmail(unittest.TestCase): @@ -18,7 +14,7 @@ class TestEmail(unittest.TestCase): try: return self.validator.to_python(*args) except Invalid as e: - return six.text_type(e) + return str(e) def message(self, message_name, username, domain): email = '@'.join((username, domain)) @@ -56,10 +52,10 @@ class TestEmail(unittest.TestCase): ('foo{bar}@example.com', 'foo{bar}@example.com'), # examples from RFC 3696 # punting on the difficult and extremely uncommon ones - #('"Abc\@def"@example.com', '"Abc\@def"@example.com'), - #('"Fred Bloggs"@example.com', '"Fred Bloggs"@example.com'), - #('"Joe\\Blow"@example.com', '"Joe\\Blow"@example.com'), - #('"Abc@def"@example.com', '"Abc@def"@example.com'), + # ('"Abc\@def"@example.com', '"Abc\@def"@example.com'), + # ('"Fred Bloggs"@example.com', '"Fred Bloggs"@example.com'), + # ('"Joe\\Blow"@example.com', '"Joe\\Blow"@example.com'), + # ('"Abc@def"@example.com', '"Abc@def"@example.com'), ('customer/department=shipping@example.com', 'customer/department=shipping@example.com'), ('$A12345@example.com', '$A12345@example.com'), diff --git a/formencode/tests/test_htmlfill.py b/tests/test_htmlfill.py index ecade0a..5e57551 100644 --- a/formencode/tests/test_htmlfill.py +++ b/tests/test_htmlfill.py @@ -1,20 +1,12 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import print_function -from __future__ import unicode_literals - import os import re import sys +from html.entities import name2codepoint + import xml.etree.ElementTree as ET -import six -try: - XMLParseError = ET.ParseError -except AttributeError: # Python < 2.7 - from xml.parsers.expat import ExpatError as XMLParseError -from six.moves.html_entities import name2codepoint +XMLParseError = ET.ParseError base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname( os.path.abspath(__file__))))) @@ -34,6 +26,7 @@ def collect_values(): fn = os.path.join(data_dir, fn) yield fn + @pytest.mark.parametrize('filename', list(collect_values())) def test_runfile(filename): f = open(filename) @@ -50,7 +43,7 @@ def test_runfile(filename): data_content = '' namespace = {} if data_content: - six.exec_(data_content, namespace) + exec(data_content, namespace) data = namespace.copy() data['defaults'] = data.get('defaults', {}) if 'check' in data: @@ -93,7 +86,7 @@ def test_no_trailing_newline(): def test_escape_defaults(): - rarr = six.unichr(name2codepoint['rarr']) + rarr = chr(name2codepoint['rarr']) assert (htmlfill.render('<input type="submit" value="next>→">', {}, {}) == '<input type="submit" value="next>%s">' % rarr) assert (htmlfill.render('<input type="submit" value="1&2">', {}, {}) @@ -461,7 +454,7 @@ def test_error_class_textarea(): def test_mix_str_and_unicode(): html = '<input type="text" name="cheese">' - uhtml = six.text_type(html) + uhtml = str(html) cheese = dict(cheese='Käse') ucheese = dict(cheese='Käse') expected = '<input type="text" name="cheese" value="Käse">' diff --git a/formencode/tests/test_htmlfill_control.py b/tests/test_htmlfill_control.py index 4ee4fa6..e695959 100644 --- a/formencode/tests/test_htmlfill_control.py +++ b/tests/test_htmlfill_control.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import from formencode import htmlfill @@ -95,6 +94,7 @@ def test_error_attr_ignore(): rendered_html = htmlfill.render(html, errors={"foo": "bang"}, force_defaults=True, data_formencode_form="b",) + assert expected_html == rendered_html def test_error_attr_form(): @@ -175,4 +175,3 @@ if __name__ == '__main__': test_error_attr_ignore() test_error_attr_form() test_error_attr_form_alt() -
\ No newline at end of file diff --git a/formencode/tests/test_htmlgen.py b/tests/test_htmlgen.py index 9dccf5b..0af2421 100644 --- a/formencode/tests/test_htmlgen.py +++ b/tests/test_htmlgen.py @@ -1,14 +1,9 @@ -from __future__ import absolute_import -from __future__ import print_function - import doctest from formencode.htmlgen import html -import six # A test value that can't be encoded as ascii: -uni_value = u'\xff' -str_value = uni_value if str is six.text_type else uni_value.encode('utf-8') +uni_value = '\xff' def test_basic(): @@ -40,12 +35,12 @@ def test_unicode(): assert False, ( "We need something that can't be ASCII-encoded: %r (%r)" % (uni_value, uni_value.encode('ascii'))) - assert six.text_type(html.b(uni_value)) == u'<b>%s</b>' % uni_value + assert str(html.b(uni_value)) == '<b>%s</b>' % uni_value def test_quote(): assert html.quote('<hey>!') == '<hey>!' - assert html.quote(uni_value) == str_value + assert html.quote(uni_value) == uni_value assert html.quote(None) == '' assert html.str(None) == '' assert str(html.b('<hey>')) == '<b><hey></b>' @@ -61,7 +56,7 @@ def test_comment(): return s assert strip(html.comment('test')) == '<!--test-->' - assert strip(html.comment(uni_value)) == '<!--%s-->' % str_value + assert strip(html.comment(uni_value)) == '<!--%s-->' % uni_value assert strip(html.comment('test')('this')) == '<!--testthis-->' @@ -79,7 +74,7 @@ def test_namespace(): if __name__ == '__main__': # It's like a super-mini py.test... - for name, value in six.iteritems(globals()): + for name, value in globals().items(): if name.startswith('test'): print(name) value() diff --git a/formencode/tests/test_htmlrename.py b/tests/test_htmlrename.py index 5f93c4a..86a6f92 100644 --- a/formencode/tests/test_htmlrename.py +++ b/tests/test_htmlrename.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import from formencode.htmlrename import rename, add_prefix diff --git a/formencode/tests/test_i18n.py b/tests/test_i18n.py index 5998f96..77ad988 100644 --- a/formencode/tests/test_i18n.py +++ b/tests/test_i18n.py @@ -1,9 +1,4 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import unicode_literals - import formencode -import six ne = formencode.validators.NotEmpty() @@ -11,15 +6,15 @@ ne = formencode.validators.NotEmpty() def _test_builtins(func): def dummy(s): return "builtins dummy" - import six.moves.builtins - six.moves.builtins._ = dummy + import builtins + builtins._ = dummy try: ne.to_python("") except formencode.api.Invalid as e: func(e) - del six.moves.builtins._ + del builtins._ def test_builtins(): @@ -38,7 +33,7 @@ def test_bultins_disabled(): def test_state(): - class st(object): + class st: def _(self, s): return "state dummy" @@ -55,7 +50,7 @@ def _test_lang(language, notemptytext): try: ne.to_python("") except formencode.api.Invalid as e: - assert six.text_type(e) == notemptytext + assert str(e) == notemptytext formencode.api.set_stdtranslation() # set back to defaults diff --git a/formencode/tests/test_schema.py b/tests/test_schema.py index 2ed349a..4f57bb3 100644 --- a/formencode/tests/test_schema.py +++ b/tests/test_schema.py @@ -1,8 +1,6 @@ -from __future__ import absolute_import -from __future__ import print_function import unittest -from six.moves.urllib.parse import parse_qsl +from urllib.parse import parse_qsl from formencode import Invalid, Validator, compound, foreach, validators from formencode.schema import Schema, merge_dicts, SimpleFormValidator @@ -18,14 +16,14 @@ def _notranslation(s): def setup_module(module): """Disable i18n translation""" - import six.moves.builtins - six.moves.builtins._ = _notranslation + import builtins + builtins._ = _notranslation def teardown_module(module): """Remove translation function""" - import six.moves.builtins - del six.moves.builtins._ + import builtins + del builtins._ def d(**kw): @@ -46,16 +44,15 @@ def cgi_parse(qs): return d -class DecodeCase(object): +class DecodeCase: error_expected = False def __init__(self, schema, input, **output): - self.raw_input = input - self.schema = schema if isinstance(input, str): input = cgi_parse(input) self.input = input + self.schema = schema self.output = output all_cases.append(self) @@ -76,7 +73,7 @@ class BadCase(DecodeCase): self.output = self.output['text'] def test(self): - print(repr(self.raw_input)) + print(repr(self.input)) try: print(repr(self.schema.to_python(self.input))) except Invalid as e: @@ -178,8 +175,9 @@ class ChainedTest(Schema): b = validators.String() b_confirm = validators.String() - chained_validators = [validators.FieldsMatch('a', 'a_confirm'), - validators.FieldsMatch('b', 'b_confirm')] + chained_validators = [ + validators.FieldsMatch('a', 'a_confirm'), + validators.FieldsMatch('b', 'b_confirm')] def test_multiple_chained_validators_errors(): @@ -212,7 +210,7 @@ def test_SimpleFormValidator_doc(): assert f.__doc__ == g.__doc__, "Docstrings don't match!" -class State(object): +class State: pass @@ -262,16 +260,15 @@ def test_state_manipulation(): assert state.key == old_key, "key not restored" -class TestAtLeastOneCheckboxIsChecked(object): +class TestAtLeastOneCheckboxIsChecked(unittest.TestCase): """Tests to address SourceForge bug #1777245 The reporter is trying to enforce agreement to a Terms of Service agreement, with failure to check the 'I agree' checkbox handled as a validation failure. The tests below illustrate a working approach. - """ - def setup(self): + def setUp(self): self.not_empty_messages = {'missing': 'a missing value message'} class CheckForCheckboxSchema(Schema): @@ -282,7 +279,7 @@ class TestAtLeastOneCheckboxIsChecked(object): def test_Schema_with_input_present(self): # <input type="checkbox" name="agree" value="yes" checked="checked"> result = self.schema.to_python({'agree': 'yes'}) - assert result['agree'] is True + self.assertTrue(result['agree']) def test_Schema_with_input_missing(self): # <input type="checkbox" name="agree" value="yes"> @@ -290,10 +287,10 @@ class TestAtLeastOneCheckboxIsChecked(object): self.schema.to_python({}) except Invalid as exc: error_message = exc.error_dict['agree'].msg - assert self.not_empty_messages['missing'] == error_message, \ - error_message + self.assertEqual(self.not_empty_messages['missing'], error_message, + error_message) else: - assert False, 'missing input not detected' + self.fail('missing input not detected') class TestStrictSchemaWithMultipleEqualInputFields(unittest.TestCase): diff --git a/formencode/tests/test_subclassing.py b/tests/test_subclassing.py index 3f62079..ec6c2cb 100644 --- a/formencode/tests/test_subclassing.py +++ b/tests/test_subclassing.py @@ -1,6 +1,3 @@ -from __future__ import absolute_import -# -*- coding: utf-8 -*- - import unittest from formencode.api import is_validator, FancyValidator, Invalid @@ -50,23 +47,20 @@ class TestCustomValidator(unittest.TestCase): try: cv.to_python('1') except Invalid as e: - self.assertTrue( - 'one is invalid' in str(e), e) + self.assertIn('one is invalid', str(e), e) else: self.fail("one should be invalid") self.assertEqual(cv.to_python('2'), 2) try: cv.to_python('3') except Invalid as e: - self.assertTrue( - 'three is invalid' in str(e), e) + self.assertIn('three is invalid', str(e), e) else: self.fail("three should be invalid") try: cv.to_python('4') except Invalid as e: - self.assertTrue( - 'four is invalid' in str(e), e) + self.assertIn('four is invalid', str(e), e) else: self.fail("four should be invalid") self.assertEqual(cv.to_python('5'), 5) @@ -78,8 +72,7 @@ class TestCustomValidator(unittest.TestCase): try: cv.from_python(2) except Invalid as e: - self.assertTrue( - 'two is invalid' in str(e), e) + self.assertIn('two is invalid', str(e), e) else: self.fail("two should be invalid") self.assertEqual(cv.from_python(3), '3') @@ -93,22 +86,19 @@ class TestCustomValidator(unittest.TestCase): try: cv.from_python(2) except Invalid as e: - self.assertTrue( - 'two is invalid' in str(e), e) + self.assertIn('two is invalid', str(e), e) else: self.fail("two should be invalid") try: cv.from_python(3) except Invalid as e: - self.assertTrue( - 'three is invalid' in str(e), e) + self.assertIn('three is invalid', str(e), e) else: self.fail("three should be invalid") try: cv.from_python(4) except Invalid as e: - self.assertTrue( - 'four is invalid' in str(e), e) + self.assertIn('four is invalid', str(e), e) else: self.fail("four should be invalid") self.assertEqual(cv.from_python(5), '5') @@ -144,8 +134,7 @@ class TestNotOneValidator(unittest.TestCase): try: nov.to_python('1') except Invalid as e: - self.assertTrue( - 'must not be 1' in str(e), e) + self.assertIn('must not be 1', str(e), e) else: self.fail("1 should be invalid") self.assertEqual(nov.to_python('2'), 2) @@ -159,8 +148,7 @@ class TestNotOneValidator(unittest.TestCase): try: nov.to_python('42') except Invalid as e: - self.assertTrue( - 'must not be 42' in str(e), e) + self.assertIn('must not be 42', str(e), e) else: self.fail("42 should be invalid") @@ -175,8 +163,7 @@ class TestNotOneValidator(unittest.TestCase): try: nov.to_python('42') except Invalid as e: - self.assertTrue( - 'must not be 42' in str(e), e) + self.assertIn('must not be 42', str(e), e) else: self.fail("42 should be invalid") self.assertEqual(nov.to_python('43'), 43) @@ -244,8 +231,7 @@ class TestAllAndNotOneValidator(unittest.TestCase): try: cav.to_python('4') except Invalid as e: - self.assertTrue( - 'must not be 4' in str(e), e) + self.assertIn('must not be 4', str(e), e) else: self.fail("4 should be invalid") self.assertEqual(cav.to_python('5'), 5) diff --git a/formencode/tests/test_subclassing_old.py b/tests/test_subclassing_old.py index 23b62df..f47bd0d 100644 --- a/formencode/tests/test_subclassing_old.py +++ b/tests/test_subclassing_old.py @@ -1,14 +1,9 @@ -from __future__ import absolute_import -# -*- coding: utf-8 -*- - import unittest import warnings from formencode.api import is_validator, FancyValidator, Invalid from formencode.compound import CompoundValidator, All from formencode.validators import Int -from six.moves import map - with warnings.catch_warnings(record=True) as custom_warnings: warnings.simplefilter('default') @@ -54,7 +49,7 @@ class TestDeprecatedCustomValidator(unittest.TestCase): output = '\n'.join(map(str, custom_warnings)) for old, new in deprecated: msg = '%s is deprecated; use %s instead' % (old, new) - self.assertTrue(msg in output, output or 'no warnings') + self.assertIn(msg, output, output or 'no warnings') def test_is_validator(self): self.assertTrue(is_validator(DeprecatedCustomValidator)) @@ -66,23 +61,20 @@ class TestDeprecatedCustomValidator(unittest.TestCase): try: cv.to_python('1') except Invalid as e: - self.assertTrue( - 'one is invalid' in str(e), e) + self.assertIn('one is invalid', str(e), e) else: self.fail("one should be invalid") self.assertEqual(cv.to_python('2'), 2) try: cv.to_python('3') except Invalid as e: - self.assertTrue( - 'three is invalid' in str(e), e) + self.assertIn('three is invalid', str(e), e) else: self.fail("three should be invalid") try: cv.to_python('4') except Invalid as e: - self.assertTrue( - 'four is invalid' in str(e), e) + self.assertIn('four is invalid', str(e), e) else: self.fail("four should be invalid") self.assertEqual(cv.to_python('5'), 5) @@ -94,8 +86,7 @@ class TestDeprecatedCustomValidator(unittest.TestCase): try: cv.from_python(2) except Invalid as e: - self.assertTrue( - 'two is invalid' in str(e), e) + self.assertIn('two is invalid', str(e), e) else: self.fail("two should be invalid") self.assertEqual(cv.from_python(3), '3') @@ -109,22 +100,19 @@ class TestDeprecatedCustomValidator(unittest.TestCase): try: cv.from_python(2) except Invalid as e: - self.assertTrue( - 'two is invalid' in str(e), e) + self.assertIn('two is invalid', str(e), e) else: self.fail("two should be invalid") try: cv.from_python(3) except Invalid as e: - self.assertTrue( - 'three is invalid' in str(e), e) + self.assertIn('three is invalid', str(e), e) else: self.fail("three should be invalid") try: cv.from_python(4) except Invalid as e: - self.assertTrue( - 'four is invalid' in str(e), e) + self.assertIn('four is invalid', str(e), e) else: self.fail("four should be invalid") self.assertEqual(cv.from_python(5), '5') @@ -160,7 +148,7 @@ class TestDeprecatedNotOneValidator(unittest.TestCase): for output in runtime_warnings, not_one_warnings: output = '\n'.join(map(str, output)) msg = '_to_python is deprecated; use _convert_to_python instead' - self.assertTrue(msg in output, output or 'no warnings') + self.assertIn(msg, output, output or 'no warnings') def test_is_validator(self): self.assertTrue(is_validator(DeprecatedNotOneValidator)) @@ -174,8 +162,7 @@ class TestDeprecatedNotOneValidator(unittest.TestCase): try: nov.to_python('1') except Invalid as e: - self.assertTrue( - 'must not be 1' in str(e), e) + self.assertIn('must not be 1', str(e), e) else: self.fail("1 should be invalid") self.assertEqual(nov.to_python('2'), 2) @@ -190,8 +177,7 @@ class TestDeprecatedNotOneValidator(unittest.TestCase): try: nov.to_python('42') except Invalid as e: - self.assertTrue( - 'must not be 42' in str(e), e) + self.assertIn('must not be 42', str(e), e) else: self.fail("42 should be invalid") @@ -207,8 +193,7 @@ class TestDeprecatedNotOneValidator(unittest.TestCase): try: nov.to_python('42') except Invalid as e: - self.assertTrue( - 'must not be 42' in str(e), e) + self.assertIn('must not be 42', str(e), e) else: self.fail("42 should be invalid") self.assertEqual(nov.to_python('43'), 43) @@ -235,14 +220,14 @@ class TestDeprecatedCustomCompoundValidator(unittest.TestCase): def test_1_warnings(self): output = '\n'.join(map(str, custom_compound_warnings)) msg = 'attempt_convert is deprecated; use _attempt_convert instead' - self.assertTrue(msg in output, output or 'no warnings') + self.assertIn(msg, output, output or 'no warnings') def test_is_validator(self): self.assertTrue(is_validator(DeprecatedCustomCompoundValidator)) self.assertTrue(is_validator(self.validator)) def test_to_python(self): - with warnings.catch_warnings(record=True) as _ignore: + with warnings.catch_warnings(record=True): ccv = self.validator self.assertEqual(ccv.to_python('2'), 2) self.assertEqual(ccv.to_python('4'), 4) @@ -283,7 +268,7 @@ class TestDeprecatedAllAndNotOneValidator(unittest.TestCase): for output in runtime_warnings, all_and_not_one_warnings: output = '\n'.join(map(str, output)) msg = 'attempt_convert is deprecated; use _attempt_convert instead' - self.assertTrue(msg in output, output or 'no warnings') + self.assertIn(msg, output, output or 'no warnings') def test_is_validator(self): self.assertTrue(is_validator(DeprecatedAllAndNotOneValidator)) @@ -298,8 +283,7 @@ class TestDeprecatedAllAndNotOneValidator(unittest.TestCase): try: cav.to_python('4') except Invalid as e: - self.assertTrue( - 'must not be 4' in str(e), e) + self.assertIn('must not be 4', str(e), e) else: self.fail("4 should be invalid") self.assertEqual(cav.to_python('5'), 5) diff --git a/formencode/tests/test_validators.py b/tests/test_validators.py index fd7d186..5db4e08 100644 --- a/formencode/tests/test_validators.py +++ b/tests/test_validators.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import unicode_literals - import datetime import unittest import pytest @@ -12,24 +8,18 @@ from formencode.variabledecode import NestedVariables from formencode.schema import Schema from formencode.foreach import ForEach from formencode.api import NoDefault -import six - -if not six.PY2: - unicode = str def validate(validator, value): try: return validator.to_python(value) - return None except Invalid as e: return e.unpack_errors() def validate_from(validator, value): try: - validator.from_python(value) - return None + return validator.from_python(value) except Invalid as e: return e.unpack_errors() @@ -39,13 +29,13 @@ class TestValidators(unittest.TestCase): def testHelp(self): from pydoc import text, plain s = plain(text.document(validators)) - self.assertTrue('Validator/Converters for use with FormEncode.' in s) - self.assertTrue('class Bool' in s) - self.assertTrue('Always Valid, returns True or False' in s) - self.assertTrue('class Email' in s) - self.assertTrue('Validate an email address.' in s) - self.assertTrue('class FieldsMatch' in s) - self.assertTrue('Tests that the given fields match' in s) + self.assertIn('Validator/Converters for use with FormEncode.', s) + self.assertIn('class Bool', s) + self.assertIn('Always Valid, returns True or False', s) + self.assertIn('class Email', s) + self.assertIn('Validate an email address.', s) + self.assertIn('class FieldsMatch', s) + self.assertIn('Tests that the given fields match', s) class TestByteStringValidator(unittest.TestCase): @@ -55,13 +45,12 @@ class TestByteStringValidator(unittest.TestCase): self.messages = self.validator.message def test_alias(self): - if str is not six.text_type: # Python 2 - self.assertTrue(self.validator is validators.String) + self.assertIsNot(self.validator, validators.String) def test_docstring(self): doc = self.validator.__doc__ - self.assertTrue( - 'Enter a value not more than ``%(max)i`` characters long' in doc) + self.assertIn( + 'Enter a value not more than ``%(max)i`` characters long', doc) def test_sv_min(self): sv = self.validator(min=2, accept_python=False) @@ -93,56 +82,55 @@ class TestUnicodeStringValidator(unittest.TestCase): self.validator = validators.UnicodeString def test_alias(self): - if str is six.text_type: # Python 3 - self.assertTrue(self.validator is validators.String) + self.assertIs(self.validator, validators.String) def test_docstring(self): doc = self.validator.__doc__ - self.assertTrue('Invalid data or incorrect encoding' in doc) + self.assertIn('Invalid data or incorrect encoding', doc) def test_unicode(self): un = self.validator() self.assertEqual(un.to_python(12), '12') - self.assertTrue(type(un.to_python(12)) is six.text_type) + self.assertIs(type(un.to_python(12)), str) self.assertEqual(un.from_python(12), '12'.encode('ascii')) - self.assertTrue(type(un.from_python(12)) is bytes) + self.assertIs(type(un.from_python(12)), bytes) def test_unicode_encoding(self): uv = self.validator() us = 'käse' u7s, u8s = us.encode('utf-7'), us.encode('utf-8') self.assertEqual(uv.to_python(u8s), us) - self.assertTrue(type(uv.to_python(u8s)) is six.text_type) + self.assertIs(type(uv.to_python(u8s)), str) self.assertEqual(uv.from_python(us), u8s) - self.assertTrue(type(uv.from_python(us)) is bytes) + self.assertIs(type(uv.from_python(us)), bytes) uv = self.validator(encoding='utf-7') self.assertEqual(uv.to_python(u7s), us) - self.assertTrue(type(uv.to_python(u7s)) is six.text_type) + self.assertIs(type(uv.to_python(u7s)), str) self.assertEqual(uv.from_python(us), u7s) - self.assertTrue(type(uv.from_python(us)) is bytes) + self.assertIs(type(uv.from_python(us)), bytes) uv = self.validator(inputEncoding='utf-7') self.assertEqual(uv.to_python(u7s), us) - self.assertTrue(type(uv.to_python(u7s)) is six.text_type) + self.assertIs(type(uv.to_python(u7s)), str) uv = self.validator(outputEncoding='utf-7') self.assertEqual(uv.from_python(us), u7s) - self.assertTrue(type(uv.from_python(us)) is bytes) + self.assertIs(type(uv.from_python(us)), bytes) uv = self.validator(inputEncoding=None) self.assertEqual(uv.to_python(us), us) - self.assertTrue(type(uv.to_python(us)) is six.text_type) + self.assertIs(type(uv.to_python(us)), str) self.assertEqual(uv.from_python(us), u8s) - self.assertTrue(type(uv.from_python(us)) is bytes) + self.assertIs(type(uv.from_python(us)), bytes) uv = self.validator(outputEncoding=None) self.assertEqual(uv.to_python(u8s), us) - self.assertTrue(type(uv.to_python(u8s)) is six.text_type) + self.assertIs(type(uv.to_python(u8s)), str) self.assertEqual(uv.from_python(us), us) - self.assertTrue(type(uv.from_python(us)) is six.text_type) + self.assertIs(type(uv.from_python(us)), str) def test_unicode_empty(self): iv = self.validator() for value in [None, b"", ""]: result = iv.to_python(value) self.assertEqual(result, "") - self.assertTrue(isinstance(result, six.text_type)) + self.assertIsInstance(result, str) class TestIntValidator(unittest.TestCase): @@ -153,7 +141,7 @@ class TestIntValidator(unittest.TestCase): def test_docstring(self): doc = self.validator.__doc__ - self.assertTrue('Please enter an integer value' in doc) + self.assertIn('Please enter an integer value', doc) def test_int_min(self): iv = self.validator(min=5) @@ -169,8 +157,8 @@ class TestIntValidator(unittest.TestCase): def test_int_minmax_optional(self): iv = self.validator(min=5, max=10, if_empty=None) - self.assertTrue(iv.to_python("") is None) - self.assertTrue(iv.to_python(None) is None) + self.assertIsNone(iv.to_python("")) + self.assertIsNone(iv.to_python(None)) self.assertEqual(iv.to_python('7'), 7) self.assertEqual(validate(iv, "1"), self.messages('tooLow', None, min=5)) @@ -212,15 +200,15 @@ class TestDateConverterValidator(unittest.TestCase): try: dc.to_python('20/12/150') except Invalid as e: - self.assertTrue( - 'Please enter a four-digit year after 1899' in unicode(e)) + self.assertIn( + 'Please enter a four-digit year after 1899', str(e)) else: self.fail('Date should be invalid') try: dc.to_python('oh/happy/day') except Invalid as e: - self.assertTrue( - 'Please enter the date in the form DD/MM/YYYY' in unicode(e)) + self.assertIn( + 'Please enter the date in the form DD/MM/YYYY', str(e)) else: self.fail('Date should be invalid') @@ -237,17 +225,16 @@ class TestDateConverterValidator(unittest.TestCase): dc = self.validator(month_style=style) self.assertEqual(dc.month_style, 'ymd') try: - dc = self.validator(month_style='Klingon') + self.validator(month_style='Klingon') except TypeError as e: - self.assertTrue( - unicode(e).replace("u'", "'") == "Bad month_style: 'klingon'") + self.assertEqual(str(e), "Bad month_style: 'klingon'") else: self.fail('month_style should be invalid') try: - dc = self.validator(month_style='ydm') + self.validator(month_style='ydm') except TypeError as e: - self.assertTrue( - unicode(e).replace("u'", "'") == "Bad month_style: 'ydm'") + self.assertEqual( + str(e), "Bad month_style: 'ydm'") else: self.fail('month_style should be invalid') @@ -261,14 +248,14 @@ class TestDateConverterValidator(unittest.TestCase): try: self.assertEqual(dc.to_python('20/12/2007'), d) except Invalid as e: - self.assertTrue('Please enter a month from 1 to 12' in unicode(e)) + self.assertIn('Please enter a month from 1 to 12', str(e)) else: self.fail('Date should be invalid') try: self.assertEqual(dc.to_python('12/Dec/2007'), d) except Invalid as e: - self.assertTrue( - 'Please enter the date in the form MM/DD/YYYY' in unicode(e)) + self.assertIn( + 'Please enter the date in the form MM/DD/YYYY', str(e)) else: self.fail('Date should be invalid') @@ -282,14 +269,14 @@ class TestDateConverterValidator(unittest.TestCase): try: self.assertEqual(dc.to_python('12/20/2007'), d) except Invalid as e: - self.assertTrue('Please enter a month from 1 to 12' in unicode(e)) + self.assertIn('Please enter a month from 1 to 12', str(e)) else: self.fail('Date should be invalid') try: self.assertEqual(dc.to_python('Dec/12/2007'), d) except Invalid as e: - self.assertTrue( - 'Please enter the date in the form DD/MM/YYYY' in unicode(e)) + self.assertIn( + 'Please enter the date in the form DD/MM/YYYY', str(e)) else: self.fail('Date should be invalid') @@ -303,14 +290,14 @@ class TestDateConverterValidator(unittest.TestCase): try: self.assertEqual(dc.to_python('2013/30/06'), d) except Invalid as e: - self.assertTrue('Please enter a month from 1 to 12' in unicode(e)) + self.assertIn('Please enter a month from 1 to 12', str(e)) else: self.fail('Date should be invalid') try: self.assertEqual(dc.to_python('2013/06/Jun'), d) except Invalid as e: - self.assertTrue( - 'Please enter the date in the form YYYY/MM/DD' in unicode(e)) + self.assertIn( + 'Please enter the date in the form YYYY/MM/DD', str(e)) else: self.fail('Date should be invalid') @@ -324,21 +311,21 @@ class TestDateConverterValidator(unittest.TestCase): try: self.assertEqual(dc.to_python('20/2007'), d) except Invalid as e: - self.assertTrue('Please enter a month from 1 to 12' in unicode(e)) + self.assertIn('Please enter a month from 1 to 12', str(e)) else: self.fail('Date should be invalid') try: self.assertEqual(dc.to_python('12/20/2007'), d) except Invalid as e: - self.assertTrue( - 'Please enter the date in the form MM/YYYY' in unicode(e)) + self.assertIn( + 'Please enter the date in the form MM/YYYY', str(e)) else: self.fail('Date should be invalid') try: self.assertEqual(dc.to_python('2007/Dec'), d) except Invalid as e: - self.assertTrue( - 'Please enter the date in the form MM/YYYY' in unicode(e)) + self.assertIn( + 'Please enter the date in the form MM/YYYY', str(e)) else: self.fail('Date should be invalid') @@ -388,30 +375,30 @@ class TestTimeConverterValidator(unittest.TestCase): try: tc.to_python('25:30:15') except Invalid as e: - self.assertTrue( - 'You must enter an hour in the range 0-23' in unicode(e)) + self.assertIn( + 'You must enter an hour in the range 0-23', str(e)) else: self.fail('Time should be invalid') try: tc.to_python('20:75:15') except Invalid as e: - self.assertTrue( - 'You must enter a minute in the range 0-59' in unicode(e)) + self.assertIn( + 'You must enter a minute in the range 0-59', str(e)) else: self.fail('Time should be invalid') try: tc.to_python('20:30:75') except Invalid as e: - self.assertTrue( - 'You must enter a second in the range 0-59' in unicode(e)) + self.assertIn( + 'You must enter a second in the range 0-59', str(e)) else: self.fail('Time should be invalid') try: tc.to_python('20:30:zx') except Invalid as e: - self.assertTrue( - 'The second value you gave is not a number' in unicode(e)) - self.assertTrue('zx' in unicode(e)) + self.assertIn( + 'The second value you gave is not a number', str(e)) + self.assertIn('zx', str(e)) else: self.fail('Time should be invalid') @@ -621,8 +608,8 @@ class TestOneOfValidator(unittest.TestCase): def test_docstring(self): doc = validators.OneOf.__doc__ - self.assertTrue( - 'Value must be one of: ``%(items)s`` (not ``%(value)r``)' in doc) + self.assertIn( + 'Value must be one of: ``%(items)s`` (not ``%(value)r``)', doc) def test_unicode_list(self): o = validators.OneOf(['ö', 'a']) @@ -643,34 +630,27 @@ class TestOneOfValidator(unittest.TestCase): self.assertEqual(expected, value) def test_mixed_list(self): - """before `_message_vars_decode`, the errors were: - Value must be one of: a; b; ö; ™; 1; 2; 3 (not u'c') - Value must be one of: a; b; ö; ™; 1; 2; 3 (not u'\xa7') - """ o = validators.OneOf(['a', 'b', 'ö', '™', 1, 2, 3]) try: o.to_python('c') raise ValueError("exception expected") except Invalid as e: - self.assertTrue("Value must be one of: a; b; ö; ™; 1; 2; 3 (not 'c')" in unicode(e)) + self.assertIn("Value must be one of: a; b; ö; ™; 1; 2; 3 (not 'c')", str(e)) try: o.to_python(9) raise ValueError("exception expected") except Invalid as e: - self.assertTrue("Value must be one of: a; b; ö; ™; 1; 2; 3 (not 9)" in unicode(e)) + self.assertIn("Value must be one of: a; b; ö; ™; 1; 2; 3 (not 9)", str(e)) try: - o.to_python(u'd') + o.to_python('d') raise ValueError("exception expected") except Invalid as e: - self.assertTrue("Value must be one of: a; b; ö; ™; 1; 2; 3 (not 'd')" in unicode(e)) + self.assertIn("Value must be one of: a; b; ö; ™; 1; 2; 3 (not 'd')", str(e)) try: o.to_python('§') raise ValueError("exception expected") except Invalid as e: - if six.PY2: - self.assertTrue(r"Value must be one of: a; b; ö; ™; 1; 2; 3 (not u'\xa7')" in unicode(e)) - else: - self.assertTrue(r"Value must be one of: a; b; ö; ™; 1; 2; 3 (not '§')" in unicode(e)) + self.assertIn("Value must be one of: a; b; ö; ™; 1; 2; 3 (not '§')", str(e)) class TestIPAddressValidator(unittest.TestCase): @@ -695,13 +675,13 @@ class TestIPAddressValidator(unittest.TestCase): try: validate('1.2.3.037') except Invalid as e: - self.assertTrue('The octets must not have leading zeros' in unicode(e)) + self.assertIn('The octets must not have leading zeros', str(e)) else: self.fail('IP address octets with leading zeros should be invalid') try: validate('1.2.3.0377') except Invalid as e: - self.assertTrue('The octets must not have leading zeros' in unicode(e)) + self.assertIn('The octets must not have leading zeros', str(e)) else: self.fail('IP octets with leading zeros should be invalid') @@ -709,13 +689,14 @@ class TestIPAddressValidator(unittest.TestCase): validate = self.validator(leading_zeros=True).to_python try: validate('1.2.3.037') - except Invalid as e: + except Invalid: self.fail('IP address octets with leading zeros should be valid') try: validate('1.2.3.0377') except Invalid as e: - self.assertTrue("The octets must be within the range of 0-255" - " (not '377')" in unicode(e).replace("u'", "'")) + self.assertIn( + "The octets must be within the range of 0-255 (not '377')", + str(e)) else: self.fail( 'IP address octets should not be interpreted as octal numbers') @@ -727,8 +708,9 @@ class TestURLValidator(unittest.TestCase): self.validator = validators.URL() def test_cojp(self): - self.assertEqual(self.validator.to_python('http://domain.co.jp'), - 'http://domain.co.jp') + self.assertEqual( + self.validator.to_python('http://domain.co.jp'), + 'http://domain.co.jp') def test_1char_thirdlevel(self): self.assertEqual(self.validator.to_python( diff --git a/formencode/tests/test_variabledecode.py b/tests/test_variabledecode.py index d7b6583..6fe53f5 100644 --- a/formencode/tests/test_variabledecode.py +++ b/tests/test_variabledecode.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import import unittest from formencode.variabledecode import variable_decode, variable_encode @@ -1,13 +1,26 @@ [tox] -envlist=py27,pypy,py36,py37,py38,py39,py310,pypy3 +envlist=py3{7,8,9,10,11,12},pypy3,flake8,docs + +[testenv:docs] +basepython = python3.11 +deps = + pycountry + sphinx +commands = + sphinx-build -b html -nEW docs docs/_build/html + +[testenv:flake8] +basepython = python3.11 +deps = + flake8 +commands = + flake8 src tests [testenv] deps= pytest wheel - py27,pypy: pycountry < 19 - py27,pypy: dnspython < 2.0.0 - py{36,37,38,39,310},pypy3: pycountry dnspython + pycountry + dnspython >= 2 commands= - pip install -e . - pytest formencode + pytest tests {posargs} |
