pax_global_header00006660000000000000000000000064146537617220014527gustar00rootroot0000000000000052 comment=5cff01b3a8ea81961483b7ff885df66db6b44e4e PyCQA-pycodestyle-0dd4a37/000077500000000000000000000000001465376172200154165ustar00rootroot00000000000000PyCQA-pycodestyle-0dd4a37/.gitattributes000066400000000000000000000000321465376172200203040ustar00rootroot00000000000000testing/data/E90.py -text PyCQA-pycodestyle-0dd4a37/.github/000077500000000000000000000000001465376172200167565ustar00rootroot00000000000000PyCQA-pycodestyle-0dd4a37/.github/workflows/000077500000000000000000000000001465376172200210135ustar00rootroot00000000000000PyCQA-pycodestyle-0dd4a37/.github/workflows/main.yml000066400000000000000000000021161465376172200224620ustar00rootroot00000000000000name: main on: pull_request: push: branches: [master, main, 'test-me-*'] tags: ['*'] jobs: main: strategy: matrix: include: - os: windows-latest py: 3.9 toxenv: py - os: ubuntu-latest py: pypy3.10 toxenv: py - os: ubuntu-latest py: 3.8 toxenv: py - os: ubuntu-latest py: 3.9 toxenv: py - os: ubuntu-latest py: '3.10' toxenv: py - os: ubuntu-latest py: '3.11' toxenv: py - os: ubuntu-latest py: '3.12-dev' toxenv: py - os: ubuntu-latest py: 3.9 toxenv: flake8 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: ${{ matrix.py }} if: matrix.py != '3.12-dev' - uses: deadsnakes/action@v3.0.1 with: python-version: ${{ matrix.py }} if: matrix.py == '3.12-dev' - run: pip install tox - run: tox -e ${{ matrix.toxenv }} PyCQA-pycodestyle-0dd4a37/.gitignore000066400000000000000000000001131465376172200174010ustar00rootroot00000000000000*.egg *.egg-info *.pyc /.coverage* /.tox /build/ /dist /venv*/ docs/_build PyCQA-pycodestyle-0dd4a37/.pre-commit-config.yaml000066400000000000000000000012731465376172200217020ustar00rootroot00000000000000exclude: ^testing/data/ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 hooks: - id: check-yaml - id: debug-statements - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/asottile/reorder-python-imports rev: v3.13.0 hooks: - id: reorder-python-imports args: [--py38-plus] - repo: https://github.com/asottile/pyupgrade rev: v3.17.0 hooks: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/asottile/setup-cfg-fmt rev: v2.5.0 hooks: - id: setup-cfg-fmt - repo: https://github.com/pycqa/flake8 rev: 7.1.0 hooks: - id: flake8 PyCQA-pycodestyle-0dd4a37/.readthedocs.yaml000066400000000000000000000001021465376172200206360ustar00rootroot00000000000000version: 2 build: os: ubuntu-22.04 tools: python: "3.11" PyCQA-pycodestyle-0dd4a37/CHANGES.txt000066400000000000000000000667641465376172200172520ustar00rootroot00000000000000Changelog ========= 2.12.1 (2024-08-04) ------------------- Changes: * Properly preserve escaped `{` and `}` in fstrings in logical lines in 3.12+. PR #1252. 2.12.0 (2024-06-15) ------------------- Changes: * E721: Fix false positive of the form `x.type(...) ==`. PR #1228. * E502: Fix false-negative with a backslash escape in a comment. PR #1234. * E204: New lint forbidding whitespace after decorator `@`. PR #1247. 2.11.1 (2023-10-12) ------------------- Changes: * E275: fix false positive with fstrings containing keyword parts in python 3.12 2.11.0 (2023-07-29) ------------------- Changes: * Drop EOL python 3.6 / 3.7. PR #1129, #1160. * Add support for python 3.12. PR #1147, #1148, #1152, #1153, #1154, #1163, #1164, #1165, #1166, #1176, #1177, #1182. * E721: adjust handling of type comparison. Allowed forms are now ``isinstance(x, t)`` or ``type(x) is t``. PR #1086, #1167. * Remove handling of python 2 ``<>`` operator. PR #1161. * W606: removed. ``async`` / ``await`` are always keywords. PR #1162. * Internal: move tests to pytest. PR #1168, #1169, #1171, #1173, #1174, #1175. * Remove handling of python 2 ``ur''`` strings. PR #1181. 2.10.0 (2022-11-23) ------------------- Changes: * E231: allow trailing comma inside 1-tuples in ``[]``. PR #1108. * W601, W602, W603, W604: removed (no longer relevant in python 3). PR #1111. * E741: also apply to lambdas. PR #1106. * E741: fix false positive for comparison operators. PR #1118. 2.9.1 (2022-08-03) ------------------ Changes: * E275: fix false positive for yield expressions. PR #1091. 2.9.0 (2022-07-30) ------------------ Changes: * E221, E222, E223, E224: add support for ``:=`` operator. PR #1032. * Drop python 2.7 / 3.5. * E262: consider non-breaking spaces (``\xa0``) as whitespace. PR #1035. * Improve performance of ``_is_binary_operator``. PR #1052. * E275: requires whitespace around keywords. PR #1063. * Add support for python 3.11. PR #1070. 2.8.0 (2021-10-10) ------------------ Changes: * Drop python 3.4. PR #982. * E712: fix false negative with multiple comparisons. PR #987. * E211: fix false positives with ``match``. PR #989. * E772: improve performance of bare except check. PR #992. * Backport tokenize performance improvement from python 3.10. PR #993. * E225: fix for lambdas containing positional-only args. PR #1012. * Remove ``indent_size_str`` "setting". PR #995. * E402: allow ``__all__`` to be typed. PR #1019. * E225: fix false positives for ``*`` in ``case``. PR #1003. * E201: detect tabs as whitespace. PR #1015. 2.7.0 (2021-03-14) ------------------ Changes: * Fix physical checks (such as W191) at end of file. PR #961. * Add ``--indent-size`` option (defaulting to ``4``). PR #970. * W605: fix escaped crlf false positive on windows. PR #976. 2.6.0 (2020-05-11) ------------------ Announcements: * Anthony Sottile (@asottile) joined the team as a core developer. :tada: Changes: * E306: fix detection inside ``async def``. PR #929. * E301: fix regression disallowing decorated one-liners. PR #927. * E714: fix false positive with chained ``is not``. PR #931. 2.6.0a1 (2020-04-23) -------------------- New checks: * E225: require whitespace around ``and`` ``in`` ``is`` and ``or``. PR #847. Changes: * E117: fix indentation using tabs by treating as 8-space indents. PR #837. * E721: fix false positive with names containg ``istype``. PR #850. * E741: allow ``l`` as a named argument in a function call. PR #853. * E302: fix false-negative with decorated functions. PR #859. * W504: ellipsis (``...``) is no longer treated as a binary operator. PR #875. * E402: allow ``with``, ``if``, ``elif``, ``else`` to guard imports. PR #834. * Add support for assignment expressions ``:=`` (PEP 572). PR #879. * Add support for positional-only arguments ``/`` (PEP 570). PR #872, #918. * Add support for python 3.8. * Add support for matrix multiplication operator ``@`` (PEP 465). PR #897. * Support visual indent for continuation lines for ``with`` / ``assert`` / ``raise``. PR #912. * E302: allow two blank lines after a block of one-liners. PR #913. * E302: allow two-and-fewer newlines at the top of the file. PR #919. 2.5.0 (2019-01-29) ------------------ New checks: * E117: Over-indented code blocks * W505: Maximum doc-string length only when configured with --max-doc-length Changes: * Remove support for EOL Python 2.6 and 3.3. PR #720. * Add E117 error for over-indented code blocks. * Allow W605 to be silenced by `# noqa` and fix the position reported by W605 * Allow users to omit blank lines around one-liner definitions of classes and functions * Include the function return annotation (``->``) as requiring surrounding whitespace only on Python 3 * Verify that only names can follow ``await``. Previously we allowed numbers and strings. * Add support for Python 3.7 * Fix detection of annotated argument defaults for E252 * Correct the position reported by W504 2.4.0 (2018-04-10) ------------------ New checks: * Add W504 warning for checking that a break doesn't happen after a binary operator. This check is ignored by default. PR #502. * Add W605 warning for invalid escape sequences in string literals. PR #676. * Add W606 warning for 'async' and 'await' reserved keywords being introduced in Python 3.7. PR #684. * Add E252 error for missing whitespace around equal sign in type annotated function arguments with defaults values. PR #717. Changes: * An internal bisect search has replaced a linear search in order to improve efficiency. PR #648. * pycodestyle now uses PyPI trove classifiers in order to document supported python versions on PyPI. PR #654. * 'setup.cfg' '[wheel]' section has been renamed to '[bdist_wheel]', as the former is legacy. PR #653. * pycodestyle now handles very long lines much more efficiently for python 3.2+. Fixes #643. PR #644. * You can now write 'pycodestyle.StyleGuide(verbose=True)' instead of 'pycodestyle.StyleGuide(verbose=True, paths=['-v'])' in order to achieve verbosity. PR #663. * The distribution of pycodestyle now includes the license text in order to comply with open source licenses which require this. PR #694. * 'maximum_line_length' now ignores shebang ('#!') lines. PR #736. * Add configuration option for the allowed number of blank lines. It is implemented as a top level dictionary which can be easily overwritten. Fixes #732. PR #733. Bugs: * Prevent a 'DeprecationWarning', and a 'SyntaxError' in future python, caused by an invalid escape sequence. PR #625. * Correctly report E501 when the first line of a docstring is too long. Resolves #622. PR #630. * Support variable annotation when variable start by a keyword, such as class variable type annotations in python 3.6. PR #640. * pycodestyle internals have been changed in order to allow 'python3 -m cProfile' to report correct metrics. PR #647. * Fix a spelling mistake in the description of E722. PR #697. * 'pycodestyle --diff' now does not break if your 'gitconfig' enables 'mnemonicprefix'. PR #706. 2.3.1 (2017-01-31) ------------------ Bugs: * Fix regression in detection of E302 and E306; #618, #620 2.3.0 (2017-01-30) ------------------ New Checks: * Add E722 warning for bare ``except`` clauses * Report E704 for async function definitions (``async def``) Bugs: * Fix another E305 false positive for variables beginning with "class" or "def" * Fix detection of multiple spaces between ``async`` and ``def`` * Fix handling of variable annotations. Stop reporting E701 on Python 3.6 for variable annotations. 2.2.0 (2016-11-14) ------------------ Announcements: * Added Make target to obtain proper tarball file permissions; #599 Bugs: * Fixed E305 regression caused by #400; #593 2.1.0 (2016-11-04) ------------------ Announcements: * Change all references to the pep8 project to say pycodestyle; #530 Changes: * Report E302 for blank lines before an "async def"; #556 * Update our list of tested and supported Python versions which are 2.6, 2.7, 3.2, 3.3, 3.4 and 3.5 as well as the nightly Python build and PyPy. * Report E742 and E743 for functions and classes badly named 'l', 'O', or 'I'. * Report E741 on 'global' and 'nonlocal' statements, as well as prohibited single-letter variables. * Deprecated use of `[pep8]` section name in favor of `[pycodestyle]`; #591 * Report E722 when bare except clause is used; #579 Bugs: * Fix opt_type AssertionError when using Flake8 2.6.2 and pycodestyle; #561 * Require two blank lines after toplevel def, class; #536 * Remove accidentally quadratic computation based on the number of colons. This will make pycodestyle faster in some cases; #314 2.0.0 (2016-05-31) ------------------ Announcements: * Repository renamed to `pycodestyle`; Issue #466 / #481. * Added joint Code of Conduct as member of PyCQA; #483 Changes: * Added tox test support for Python 3.5 and pypy3 * Added check E275 for whitespace on `from ... import ...` lines; #489 / #491 * Added W503 to the list of codes ignored by default ignore list; #498 * Removed use of project level `.pep8` configuration file; #364 Bugs: * Fixed bug with treating `~` operator as binary; #383 / #384 * Identify binary operators as unary; #484 / #485 1.7.0 (2016-01-12) ------------------ Announcements: * Repository moved to PyCQA Organization on GitHub: https://github.com/pycqa/pep8 Changes: * Reverted the fix in #368, "options passed on command line are only ones accepted" feature. This has many unintended consequences in pep8 and flake8 and needs to be reworked when I have more time. * Added support for Python 3.5. (Issue #420 & #459) * Added support for multi-line config_file option parsing. (Issue #429) * Improved parameter parsing. (Issues #420 & #456) Bugs: * Fixed BytesWarning on Python 3. (Issue #459) 1.6.2 (2015-02-15) ------------------ Changes: * Added check for breaking around a binary operator. (Issue #197, Pull #305) Bugs: * Restored config_file parameter in process_options(). (Issue #380) 1.6.1 (2015-02-08) ------------------ Changes: * Assign variables before referenced. (Issue #287) Bugs: * Exception thrown due to unassigned ``local_dir`` variable. (Issue #377) 1.6.0 (2015-02-06) ------------------ News: * Ian Lee joined the project as a maintainer. Changes: * Report E731 for lambda assignment. (Issue #277) * Report E704 for one-liner def instead of E701. Do not report this error in the default configuration. (Issue #277) * Replace codes E111, E112 and E113 with codes E114, E115 and E116 for bad indentation of comments. (Issue #274) * Report E266 instead of E265 when the block comment starts with multiple ``#``. (Issue #270) * Report E402 for import statements not at the top of the file. (Issue #264) * Do not enforce whitespaces around ``**`` operator. (Issue #292) * Strip whitespace from around paths during normalization. (Issue #339 / #343) * Update ``--format`` documentation. (Issue #198 / Pull Request #310) * Add ``.tox/`` to default excludes. (Issue #335) * Do not report E121 or E126 in the default configuration. (Issues #256 / #316) * Allow spaces around the equals sign in an annotated function. (Issue #357) * Allow trailing backslash if in an inline comment. (Issue #374) * If ``--config`` is used, only that configuration is processed. Otherwise, merge the user and local configurations are merged. (Issue #368 / #369) Bug fixes: * Don't crash if Checker.build_tokens_line() returns None. (Issue #306) * Don't crash if os.path.expanduser() throws an ImportError. (Issue #297) * Missing space around keyword parameter equal not always reported, E251. (Issue #323) * Fix false positive E711/E712/E713. (Issues #330 and #336) * Do not skip physical checks if the newline is escaped. (Issue #319) * Flush sys.stdout to avoid race conditions with printing. See flake8 bug: https://gitlab.com/pycqa/flake8/issues/17 for more details. (Issue #363) 1.5.7 (2014-05-29) ------------------ Bug fixes: * Skip the traceback on "Broken pipe" signal. (Issue #275) * Do not exit when an option in ``setup.cfg`` or ``tox.ini`` is not recognized. * Check the last line even if it does not end with a newline. (Issue #286) * Always open files in universal newlines mode in Python 2. (Issue #288) 1.5.6 (2014-04-14) ------------------ Bug fixes: * Check the last line even if it has no end-of-line. (Issue #273) 1.5.5 (2014-04-10) ------------------ Bug fixes: * Fix regression with E22 checks and inline comments. (Issue #271) 1.5.4 (2014-04-07) ------------------ Bug fixes: * Fix negative offset with E303 before a multi-line docstring. (Issue #269) 1.5.3 (2014-04-04) ------------------ Bug fixes: * Fix wrong offset computation when error is on the last char of a physical line. (Issue #268) 1.5.2 (2014-04-04) ------------------ Changes: * Distribute a universal wheel file. Bug fixes: * Report correct line number for E303 with comments. (Issue #60) * Do not allow newline after parameter equal. (Issue #252) * Fix line number reported for multi-line strings. (Issue #220) * Fix false positive E121/E126 with multi-line strings. (Issue #265) * Fix E501 not detected in comments with Python 2.5. * Fix caret position with ``--show-source`` when line contains tabs. 1.5.1 (2014-03-27) ------------------ Bug fixes: * Fix a crash with E125 on multi-line strings. (Issue #263) 1.5 (2014-03-26) ---------------- Changes: * Report E129 instead of E125 for visually indented line with same indent as next logical line. (Issue #126) * Report E265 for space before block comment. (Issue #190) * Report E713 and E714 when operators ``not in`` and ``is not`` are recommended. (Issue #236) * Allow long lines in multiline strings and comments if they cannot be wrapped. (Issue #224). * Optionally disable physical line checks inside multiline strings, using ``# noqa``. (Issue #242) * Change text for E121 to report "continuation line under-indented for hanging indent" instead of indentation not being a multiple of 4. * Report E131 instead of E121 / E126 if the hanging indent is not consistent within the same continuation block. It helps when error E121 or E126 is in the ``ignore`` list. * Report E126 instead of E121 when the continuation line is hanging with extra indentation, even if indentation is not a multiple of 4. Bug fixes: * Allow the checkers to report errors on empty files. (Issue #240) * Fix ignoring too many checks when ``--select`` is used with codes declared in a flake8 extension. (Issue #216) * Fix regression with multiple brackets. (Issue #214) * Fix ``StyleGuide`` to parse the local configuration if the keyword argument ``paths`` is specified. (Issue #246) * Fix a false positive E124 for hanging indent. (Issue #254) * Fix a false positive E126 with embedded colon. (Issue #144) * Fix a false positive E126 when indenting with tabs. (Issue #204) * Fix behaviour when ``exclude`` is in the configuration file and the current directory is not the project directory. (Issue #247) * The logical checks can return ``None`` instead of an empty iterator. (Issue #250) * Do not report multiple E101 if only the first indentation starts with a tab. (Issue #237) * Fix a rare false positive W602. (Issue #34) 1.4.6 (2013-07-02) ------------------ Changes: * Honor ``# noqa`` for errors E711 and E712. (Issue #180) * When both a ``tox.ini`` and a ``setup.cfg`` are present in the project directory, merge their contents. The ``tox.ini`` file takes precedence (same as before). (Issue #182) * Give priority to ``--select`` over ``--ignore``. (Issue #188) * Compare full path when excluding a file. (Issue #186) * New option ``--hang-closing`` to switch to the alternative style of closing bracket indentation for hanging indent. Add error E133 for closing bracket which is missing indentation. (Issue #103) * Accept both styles of closing bracket indentation for hanging indent. Do not report error E123 in the default configuration. (Issue #103) Bug fixes: * Do not crash when running AST checks and the document contains null bytes. (Issue #184) * Correctly report other E12 errors when E123 is ignored. (Issue #103) * Fix false positive E261/E262 when the file contains a BOM. (Issue #193) * Fix E701, E702 and E703 not detected sometimes. (Issue #196) * Fix E122 not detected in some cases. (Issue #201 and #208) * Fix false positive E121 with multiple brackets. (Issue #203) 1.4.5 (2013-03-06) ------------------ * When no path is specified, do not try to read from stdin. The feature was added in 1.4.3, but it is not supported on Windows. Use ``-`` filename argument to read from stdin. This usage is supported since 1.3.4. (Issue #170) * Do not require ``setuptools`` in setup.py. It works around an issue with ``pip`` and Python 3. (Issue #172) * Add ``__pycache__`` to the ignore list. * Change misleading message for E251. (Issue #171) * Do not report false E302 when the source file has a coding cookie or a comment on the first line. (Issue #174) * Reorganize the tests and add tests for the API and for the command line usage and options. (Issues #161 and #162) * Ignore all checks which are not explicitly selected when ``select`` is passed to the ``StyleGuide`` constructor. 1.4.4 (2013-02-24) ------------------ * Report E227 or E228 instead of E225 for whitespace around bitwise, shift or modulo operators. (Issue #166) * Change the message for E226 to make clear that it is about arithmetic operators. * Fix a false positive E128 for continuation line indentation with tabs. * Fix regression with the ``--diff`` option. (Issue #169) * Fix the ``TestReport`` class to print the unexpected warnings and errors. 1.4.3 (2013-02-22) ------------------ * Hide the ``--doctest`` and ``--testsuite`` options when installed. * Fix crash with AST checkers when the syntax is invalid. (Issue #160) * Read from standard input if no path is specified. * Initiate a graceful shutdown on ``Control+C``. * Allow changing the ``checker_class`` for the ``StyleGuide``. 1.4.2 (2013-02-10) ------------------ * Support AST checkers provided by third-party applications. * Register new checkers with ``register_check(func_or_cls, codes)``. * Allow constructing a ``StyleGuide`` with a custom parser. * Accept visual indentation without parenthesis after the ``if`` statement. (Issue #151) * Fix UnboundLocalError when using ``# noqa`` with continued lines. (Issue #158) * Re-order the lines for the ``StandardReport``. * Expand tabs when checking E12 continuation lines. (Issue #155) * Refactor the testing class ``TestReport`` and the specific test functions into a separate test module. 1.4.1 (2013-01-18) ------------------ * Allow sphinx.ext.autodoc syntax for comments. (Issue #110) * Report E703 instead of E702 for the trailing semicolon. (Issue #117) * Honor ``# noqa`` in addition to ``# nopep8``. (Issue #149) * Expose the ``OptionParser`` factory for better extensibility. 1.4 (2012-12-22) ---------------- * Report E226 instead of E225 for optional whitespace around common operators (``*``, ``**``, ``/``, ``+`` and ``-``). This new error code is ignored in the default configuration because PEP 8 recommends to "use your own judgement". (Issue #96) * Lines with a ``# nopep8`` at the end will not issue errors on line length E501 or continuation line indentation E12*. (Issue #27) * Fix AssertionError when the source file contains an invalid line ending ``"\r\r\n"``. (Issue #119) * Read the ``[pep8]`` section of ``tox.ini`` or ``setup.cfg`` if present. (Issue #93 and #141) * Add the Sphinx-based documentation, and publish it on https://pycodestyle.readthedocs.io/. (Issue #105) 1.3.4 (2012-12-18) ------------------ * Fix false positive E124 and E128 with comments. (Issue #100) * Fix error on stdin when running with bpython. (Issue #101) * Fix false positive E401. (Issue #104) * Report E231 for nested dictionary in list. (Issue #142) * Catch E271 at the beginning of the line. (Issue #133) * Fix false positive E126 for multi-line comments. (Issue #138) * Fix false positive E221 when operator is preceded by a comma. (Issue #135) * Fix ``--diff`` failing on one-line hunk. (Issue #137) * Fix the ``--exclude`` switch for directory paths. (Issue #111) * Use ``-`` filename to read from standard input. (Issue #128) 1.3.3 (2012-06-27) ------------------ * Fix regression with continuation line checker. (Issue #98) 1.3.2 (2012-06-26) ------------------ * Revert to the previous behaviour for ``--show-pep8``: do not imply ``--first``. (Issue #89) * Add E902 for IO errors. (Issue #87) * Fix false positive for E121, and missed E124. (Issue #92) * Set a sensible default path for config file on Windows. (Issue #95) * Allow ``verbose`` in the configuration file. (Issue #91) * Show the enforced ``max-line-length`` in the error message. (Issue #86) 1.3.1 (2012-06-18) ------------------ * Explain which configuration options are expected. Accept and recommend the options names with hyphen instead of underscore. (Issue #82) * Do not read the user configuration when used as a module (except if ``config_file=True`` is passed to the ``StyleGuide`` constructor). * Fix wrong or missing cases for the E12 series. * Fix cases where E122 was missed. (Issue #81) 1.3 (2012-06-15) ---------------- .. warning:: The internal API is backwards incompatible. * Remove global configuration and refactor the library around a ``StyleGuide`` class; add the ability to configure various reporters. (Issue #35 and #66) * Read user configuration from ``~/.config/pep8`` and local configuration from ``./.pep8``. (Issue #22) * Fix E502 for backslash embedded in multi-line string. (Issue #68) * Fix E225 for Python 3 iterable unpacking (PEP 3132). (Issue #72) * Enable the new checkers from the E12 series in the default configuration. * Suggest less error-prone alternatives for E712 errors. * Rewrite checkers to run faster (E22, E251, E27). * Fixed a crash when parsed code is invalid (too many closing brackets). * Fix E127 and E128 for continuation line indentation. (Issue #74) * New option ``--format`` to customize the error format. (Issue #23) * New option ``--diff`` to check only modified code. The unified diff is read from STDIN. Example: ``hg diff | pep8 --diff`` (Issue #39) * Correctly report the count of failures and set the exit code to 1 when the ``--doctest`` or the ``--testsuite`` fails. * Correctly detect the encoding in Python 3. (Issue #69) * Drop support for Python 2.3, 2.4 and 3.0. (Issue #78) 1.2 (2012-06-01) ---------------- * Add E121 through E128 for continuation line indentation. These checks are disabled by default. If you want to force all checks, use switch ``--select=E,W``. Patch by Sam Vilain. (Issue #64) * Add E721 for direct type comparisons. (Issue #47) * Add E711 and E712 for comparisons to singletons. (Issue #46) * Fix spurious E225 and E701 for function annotations. (Issue #29) * Add E502 for explicit line join between brackets. * Fix E901 when printing source with ``--show-source``. * Report all errors for each checker, instead of reporting only the first occurrence for each line. * Option ``--show-pep8`` implies ``--first``. 1.1 (2012-05-24) ---------------- * Add E901 for syntax errors. (Issues #63 and #30) * Add E271, E272, E273 and E274 for extraneous whitespace around keywords. (Issue #57) * Add ``tox.ini`` configuration file for tests. (Issue #61) * Add ``.travis.yml`` configuration file for continuous integration. (Issue #62) 1.0.1 (2012-04-06) ------------------ * Fix inconsistent version numbers. 1.0 (2012-04-04) ---------------- * Fix W602 ``raise`` to handle multi-char names. (Issue #53) 0.7.0 (2012-03-26) ------------------ * Now ``--first`` prints only the first occurrence of each error. The ``--repeat`` flag becomes obsolete because it is the default behaviour. (Issue #6) * Allow specifying ``--max-line-length``. (Issue #36) * Make the shebang more flexible. (Issue #26) * Add testsuite to the bundle. (Issue #25) * Fixes for Jython. (Issue #49) * Add PyPI classifiers. (Issue #43) * Fix the ``--exclude`` option. (Issue #48) * Fix W602, accept ``raise`` with 3 arguments. (Issue #34) * Correctly select all tests if ``DEFAULT_IGNORE == ''``. 0.6.1 (2010-10-03) ------------------ * Fix inconsistent version numbers. (Issue #21) 0.6.0 (2010-09-19) ------------------ * Test suite reorganized and enhanced in order to check more failures with fewer test files. Read the ``run_tests`` docstring for details about the syntax. * Fix E225: accept ``print >>sys.stderr, "..."`` syntax. * Fix E501 for lines containing multibyte encoded characters. (Issue #7) * Fix E221, E222, E223, E224 not detected in some cases. (Issue #16) * Fix E211 to reject ``v = dic['a'] ['b']``. (Issue #17) * Exit code is always 1 if any error or warning is found. (Issue #10) * ``--ignore`` checks are now really ignored, especially in conjunction with ``--count``. (Issue #8) * Blank lines with spaces yield W293 instead of W291: some developers want to ignore this warning and indent the blank lines to paste their code easily in the Python interpreter. * Fix E301: do not require a blank line before an indented block. (Issue #14) * Fix E203 to accept NumPy slice notation ``a[0, :]``. (Issue #13) * Performance improvements. * Fix decoding and checking non-UTF8 files in Python 3. * Fix E225: reject ``True+False`` when running on Python 3. * Fix an exception when the line starts with an operator. * Allow a new line before closing ``)``, ``}`` or ``]``. (Issue #5) 0.5.0 (2010-02-17) ------------------ * Changed the ``--count`` switch to print to sys.stderr and set exit code to 1 if any error or warning is found. * E241 and E242 are removed from the standard checks. If you want to include these checks, use switch ``--select=E,W``. (Issue #4) * Blank line is not mandatory before the first class method or nested function definition, even if there's a docstring. (Issue #1) * Add the switch ``--version``. * Fix decoding errors with Python 3. (Issue #13 [1]_) * Add ``--select`` option which is mirror of ``--ignore``. * Add checks E261 and E262 for spaces before inline comments. * New check W604 warns about deprecated usage of backticks. * New check W603 warns about the deprecated operator ``<>``. * Performance improvement, due to rewriting of E225. * E225 now accepts: - no whitespace after unary operator or similar. (Issue #9 [1]_) - lambda function with argument unpacking or keyword defaults. * Reserve "2 blank lines" for module-level logical blocks. (E303) * Allow multi-line comments. (E302, issue #10 [1]_) 0.4.2 (2009-10-22) ------------------ * Decorators on classes and class methods are OK now. 0.4 (2009-10-20) ---------------- * Support for all versions of Python from 2.3 to 3.1. * New and greatly expanded self tests. * Added ``--count`` option to print the total number of errors and warnings. * Further improvements to the handling of comments and blank lines. (Issue #1 [1]_ and others changes.) * Check all py files in directory when passed a directory (Issue #2 [1]_). This also prevents an exception when traversing directories with non ``*.py`` files. * E231 should allow commas to be followed by ``)``. (Issue #3 [1]_) * Spaces are no longer required around the equals sign for keyword arguments or default parameter values. .. [1] These issues refer to the `previous issue tracker`__. .. __: http://github.com/cburroughs/pep8.py/issues 0.3.1 (2009-09-14) ------------------ * Fixes for comments: do not count them when checking for blank lines between items. * Added setup.py for pypi upload and easy_installability. 0.2 (2007-10-16) ---------------- * Loads of fixes and improvements. 0.1 (2006-10-01) ---------------- * First release. PyCQA-pycodestyle-0dd4a37/CONTRIBUTING.rst000066400000000000000000000054601465376172200200640ustar00rootroot00000000000000Contributing to pycodestyle =========================== When contributing to pycodestyle, please observe our `Code of Conduct`_. Step 1: Forking pycodestyle for editing --------------------------------------- Fork the pycodestyle repository on GitHub. This will add pycodestyle to your GitHub account. You will push your changes to your fork and then make pull requests into the official pycodestyle repository. GitHub has an excellent `guide`_ that has screenshots on how to do this. Next, clone your fork of the pycodestyle repository to your system for editing:: $ git clone https://www.github.com//pycodestyle Now you have a copy of the pycodestyle codebase that is almost ready for edits. Next we will setup `virtualenv`_ which will help create an isolated environment to manage dependencies. Step 2: Use virtualenv when developing -------------------------------------- `virtualenv`_ is a tool to create isolated python environments. First, install virtualenv with:: $ pip install virtualenv Next, ``cd`` to the pycodestyle repository that you cloned earlier and create, then activate a virtualenv:: $ cd pycodestyle $ virtualenv venv-pycodestyle $ source venv-pycodestyle/bin/activate Note that ``venv*/`` is ignored via ``.gitignore``. Now you can install the pycodestyle requirements:: $ pip install -r dev-requirements.txt To deactivate the virtualenv you can type:: $ deactivate For more information see `virtualenv`_'s documentation. Step 3: Run tests ----------------- Before creating a pull request you should run the tests to make sure that the changes that have been made haven't caused any regressions in functionality. To run the tests, the core developer team and GitHub Actions use `tox`_:: $ pip install -r dev-requirements.txt $ tox All the tests should pass for all available interpreters, with the summary of:: congratulations :) At this point you can create a pull request back to the official pycodestyle repository for review! For more information on how to make a pull request, GitHub has an excellent `guide`_. The current tests are written in 2 styles: * pytest tests * functional test using a custom framework Running tests ~~~~~~~~~~~~~ The tests are written using ``pytest``, the existing tests include unit, integration and functional tests. To run the tests:: $ pytest tests Running functional ~~~~~~~~~~~~~~~~~~ $ pip install -e . $ # Run all tests. $ pytest tests/test_data.py $ # Run a subset of the tests. $ pytest tests/tests_data.py -k testing/data/E30.py .. _virtualenv: http://docs.python-guide.org/en/latest/dev/virtualenvs/ .. _guide: https://guides.github.com/activities/forking/ .. _tox: https://tox.readthedocs.io/en/latest/ .. _Code of Conduct: http://meta.pycqa.org/en/latest/code-of-conduct.html PyCQA-pycodestyle-0dd4a37/LICENSE000066400000000000000000000023461465376172200164300ustar00rootroot00000000000000Copyright © 2006-2009 Johann C. Rocholl Copyright © 2009-2014 Florent Xicluna Copyright © 2014-2020 Ian Lee Licensed under the terms of the Expat License 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 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PyCQA-pycodestyle-0dd4a37/Makefile000066400000000000000000000001571465376172200170610ustar00rootroot00000000000000release: umask 022 && chmod -R a+rX . && python setup.py sdist bdist_wheel # twine upload dist/* test: tox PyCQA-pycodestyle-0dd4a37/README.rst000066400000000000000000000066061465376172200171150ustar00rootroot00000000000000pycodestyle (formerly called pep8) - Python style guide checker =============================================================== .. image:: https://github.com/PyCQA/pycodestyle/actions/workflows/main.yml/badge.svg :target: https://github.com/PyCQA/pycodestyle/actions/workflows/main.yml :alt: Build status .. image:: https://readthedocs.org/projects/pycodestyle/badge/?version=latest :target: https://pycodestyle.pycqa.org :alt: Documentation Status .. image:: https://img.shields.io/pypi/wheel/pycodestyle.svg :target: https://pypi.org/project/pycodestyle/ :alt: Wheel Status .. image:: https://badges.gitter.im/PyCQA/pycodestyle.svg :alt: Join the chat at https://gitter.im/PyCQA/pycodestyle :target: https://gitter.im/PyCQA/pycodestyle?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge pycodestyle is a tool to check your Python code against some of the style conventions in `PEP 8`_. .. _PEP 8: http://www.python.org/dev/peps/pep-0008/ .. note:: This package used to be called ``pep8`` but was renamed to ``pycodestyle`` to reduce confusion. Further discussion can be found `in the issue where Guido requested this change `_, or in the lightning talk at PyCon 2016 by @IanLee1521: `slides `_ `video `_. Features -------- * Plugin architecture: Adding new checks is easy. * Parseable output: Jump to error location in your editor. * Small: Just one Python file, requires only stdlib. You can use just the ``pycodestyle.py`` file for this purpose. * Comes with a comprehensive test suite. Installation ------------ You can install, upgrade, and uninstall ``pycodestyle.py`` with these commands:: $ pip install pycodestyle $ pip install --upgrade pycodestyle $ pip uninstall pycodestyle There's also a package for Debian/Ubuntu, but it's not always the latest version. Example usage and output ------------------------ :: $ pycodestyle --first optparse.py optparse.py:69:11: E401 multiple imports on one line optparse.py:77:1: E302 expected 2 blank lines, found 1 optparse.py:88:5: E301 expected 1 blank line, found 0 optparse.py:347:31: E211 whitespace before '(' optparse.py:357:17: E201 whitespace after '{' optparse.py:472:29: E221 multiple spaces before operator You can also make ``pycodestyle.py`` show the source code for each error, and even the relevant text from PEP 8:: $ pycodestyle --show-source --show-pep8 testing/data/E40.py testing/data/E40.py:2:10: E401 multiple imports on one line import os, sys ^ Imports should usually be on separate lines. Okay: import os\nimport sys E401: import sys, os Or you can display how often each error was found:: $ pycodestyle --statistics -qq Python-2.5/Lib 232 E201 whitespace after '[' 599 E202 whitespace before ')' 631 E203 whitespace before ',' 842 E211 whitespace before '(' 2531 E221 multiple spaces before operator 4473 E301 expected 1 blank line, found 0 4006 E302 expected 2 blank lines, found 1 165 E303 too many blank lines (4) 325 E401 multiple imports on one line 3615 E501 line too long (82 characters) Links ----- * `Read the documentation `_ * `Fork me on GitHub `_ PyCQA-pycodestyle-0dd4a37/dev-requirements.txt000066400000000000000000000000041465376172200214500ustar00rootroot00000000000000tox PyCQA-pycodestyle-0dd4a37/docs/000077500000000000000000000000001465376172200163465ustar00rootroot00000000000000PyCQA-pycodestyle-0dd4a37/docs/Makefile000066400000000000000000000127201465376172200200100ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pycodestyle.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pycodestyle.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/pycodestyle" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pycodestyle" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." PyCQA-pycodestyle-0dd4a37/docs/advanced.rst000066400000000000000000000052441465376172200206520ustar00rootroot00000000000000.. currentmodule:: pycodestyle ============== Advanced usage ============== Automated tests --------------- You can also execute ``pycodestyle`` tests from Python code. For example, this can be highly useful for automated testing of coding style conformance in your project:: import unittest import pycodestyle class TestCodeFormat(unittest.TestCase): def test_conformance(self): """Test that we conform to PEP-8.""" style = pycodestyle.StyleGuide(quiet=True) result = style.check_files(['file1.py', 'file2.py']) self.assertEqual(result.total_errors, 0, "Found code style errors (and warnings).") There's also a shortcut for checking a single file:: import pycodestyle fchecker = pycodestyle.Checker('testing/data/E27.py', show_source=True) file_errors = fchecker.check_all() print("Found %s errors (and warnings)" % file_errors) Configuring tests ----------------- You can configure automated ``pycodestyle`` tests in a variety of ways. For example, you can pass in a path to a configuration file that ``pycodestyle`` should use:: import pycodestyle style = pycodestyle.StyleGuide(config_file='/path/to/tox.ini') You can also set specific options explicitly:: style = pycodestyle.StyleGuide(ignore=['E501']) Skip file header ---------------- Another example is related to the `feature request #143 `_: skip a number of lines at the beginning and the end of a file. This use case is easy to implement through a custom wrapper for the PEP 8 library:: #!python import pycodestyle LINES_SLICE = slice(14, -20) class StyleGuide(pycodestyle.StyleGuide): """This subclass of pycodestyle.StyleGuide will skip the first and last lines of each file.""" def input_file(self, filename, lines=None, expected=None, line_offset=0): if lines is None: assert line_offset == 0 line_offset = LINES_SLICE.start or 0 lines = pycodestyle.readlines(filename)[LINES_SLICE] return super(StyleGuide, self).input_file( filename, lines=lines, expected=expected, line_offset=line_offset) if __name__ == '__main__': style = StyleGuide(parse_argv=True, config_file=True) report = style.check_files() if report.total_errors: raise SystemExit(1) This module declares a lines' window which skips 14 lines at the beginning and 20 lines at the end. If there's no line to skip at the end, it could be changed with ``LINES_SLICE = slice(14, None)`` for example. You can save it in a file and use it with the same options as the original ``pycodestyle``. PyCQA-pycodestyle-0dd4a37/docs/api.rst000066400000000000000000000050261465376172200176540ustar00rootroot00000000000000=============== pycodestyle API =============== .. module:: pycodestyle The library provides classes which are usable by third party tools. .. contents:: :local: .. _main_classes: Checker Classes --------------- The :class:`StyleGuide` class is used to configure a style guide checker instance to check multiple files. The :class:`Checker` class can be used to check a single file. .. autoclass:: StyleGuide(parse_argv=False, config_file=None, parser=None, paths=None, report=None, **kwargs) .. automethod:: init_report(reporter=None) .. automethod:: check_files(paths=None) .. automethod:: input_file(filename, lines=None, expected=None, line_offset=0) .. automethod:: input_dir(dirname) .. automethod:: excluded(filename, parent=None) .. automethod:: ignore_code(code) .. automethod:: get_checks(argument_name) .. autoclass:: Checker(filename=None, lines=None, report=None, **kwargs) .. automethod:: readline .. automethod:: run_check(check, argument_names) .. automethod:: check_physical(line) .. automethod:: build_tokens_line .. automethod:: check_logical .. automethod:: check_ast .. automethod:: generate_tokens .. automethod:: check_all(expected=None, line_offset=0) .. _report_classes: Report Classes -------------- .. autoclass:: BaseReport(options) .. automethod:: start .. automethod:: stop .. automethod:: init_file(filename, lines, expected, line_offset) .. automethod:: increment_logical_line .. automethod:: error(line_number, offset, text, check) .. automethod:: get_file_results .. automethod:: get_count(prefix='') .. automethod:: get_statistics(prefix='') .. automethod:: print_statistics(prefix='') .. automethod:: print_benchmark .. autoclass:: FileReport .. autoclass:: StandardReport .. autoclass:: DiffReport Utilities --------- .. autofunction:: expand_indent(line) .. autofunction:: mute_string(text) .. autofunction:: read_config(options, args, arglist, parser) .. autofunction:: process_options(arglist=None, parse_argv=False, config_file=None) .. autofunction:: register_check(func_or_cls, codes=None) .. These ones are used internally, but they don't need advertising .. autofunction:: readlines(filename) .. autofunction:: isidentifier(word) .. autofunction:: stdin_get_value() .. autofunction:: parse_udiff(diff, patterns=None, parent='.') .. autofunction:: filename_match(filename, patterns, default=True) .. autofunction:: get_parser(prog='pycodestyle', version=pycodestyle.__version__) .. autofunction:: init_checks_registry() PyCQA-pycodestyle-0dd4a37/docs/conf.py000066400000000000000000000202231465376172200176440ustar00rootroot00000000000000# # pycodestyle documentation build configuration file, created by # sphinx-quickstart on Tue Aug 21 09:47:49 2012. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import os import sys # 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('.')) sys.path.insert(0, os.path.abspath('..')) # -- General configuration ---------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'pycodestyle' authors = 'Johann C. Rocholl, Florent Xicluna, Ian Lee' copyright = '2006-2016, %s' % (authors) # 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. # pkg_version = __import__('pycodestyle').__version__.split('.') # The short X.Y version. version = '.'.join(pkg_version[:2]) # The full version, including alpha/beta/rc tags. release = '.'.join(pkg_version) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for # all documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'pycodestyledoc' # -- Options for LaTeX output ------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto/manual]). latex_documents = [ ('index', 'pycodestyle.tex', 'pycodestyle documentation', authors, 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output ------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'pycodestyle', 'pycodestyle documentation', [authors], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ----------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'pycodestyle', 'pycodestyle documentation', authors, 'pycodestyle', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' PyCQA-pycodestyle-0dd4a37/docs/developer.rst000066400000000000000000000100121465376172200210570ustar00rootroot00000000000000.. currentmodule:: pycodestyle ================= Developer's notes ================= Source code ~~~~~~~~~~~ The source code is currently `available on GitHub`_ under the terms and conditions of the :ref:`Expat license `. Fork away! * `Source code `_ and `issue tracker `_ on GitHub. * `Continuous tests `_ against Python 2.7 and 3.5+ as well as the nightly Python build and PyPy, on GitHub Actions. .. _available on GitHub: https://github.com/pycqa/pycodestyle Direction ~~~~~~~~~ Some high-level aims and directions to bear in mind for contributions: * ``pycodestyle`` is intended to be as fast as possible. Using the ``ast`` module defeats that purpose. The `pep8-naming `_ plugin exists for this sort of functionality. * If you want to provide extensibility / plugins, please see `flake8 `_ - ``pycodestyle`` doesn't want or need a plugin architecture. * ``pycodestyle`` aims to have no external dependencies. Contribute ~~~~~~~~~~ You can add checks to this program by writing plugins. Each plugin is a simple function that is called for each line of source code, either physical or logical. Physical line: * Raw line of text from the input file. Logical line: * Multi-line statements converted to a single line. * Stripped left and right. * Contents of strings replaced with ``"xxx"`` of same length. * Comments removed. The check function requests physical or logical lines by the name of the first argument:: def maximum_line_length(physical_line) def extraneous_whitespace(logical_line) def blank_lines(logical_line, blank_lines, indent_level, line_number) The last example above demonstrates how check plugins can request additional information with extra arguments. All attributes of the :class:`Checker` object are available. Some examples: * ``lines``: a list of the raw lines from the input file * ``tokens``: the tokens that contribute to this logical line * ``line_number``: line number in the input file * ``total_lines``: number of lines in the input file * ``blank_lines``: blank lines before this one * ``indent_char``: indentation character in this file (``" "`` or ``"\t"``) * ``indent_level``: indentation (with tabs expanded to multiples of 8) * ``previous_indent_level``: indentation on previous line * ``previous_logical``: previous logical line Check plugins can also maintain per-file state. If you need this, declare a parameter named ``checker_state``. You will be passed a dict, which will be the same one for all lines in the same file but a different one for different files. Each check plugin gets its own dict, so you don't need to worry about clobbering the state of other plugins. The docstring of each check function shall be the relevant part of text from `PEP 8`_. It is printed if the user enables ``--show-pep8``. Several docstrings contain examples directly from the `PEP 8`_ document. :: Okay: spam(ham[1], {eggs: 2}) E201: spam( ham[1], {eggs: 2}) These examples are verified automatically by ``test_self_doctest.py``. You can add examples for your own check functions. The format is simple: ``"Okay"`` or error/warning code followed by colon and space, the rest of the line is example source code. If you put ``'r'`` before the docstring, you can use ``\n`` for newline and ``\t`` for tab. Then be sure to pass the tests:: $ pytest tests $ python pycodestyle.py --verbose pycodestyle.py When contributing to pycodestyle, please observe our `Code of Conduct`_. To run the tests, the core developer team and GitHub Actions use tox:: $ pip install -r dev-requirements.txt $ tox All the tests should pass for all available interpreters, with the summary of:: congratulations :) .. _PEP 8: http://www.python.org/dev/peps/pep-0008/ .. _Code of Conduct: http://meta.pycqa.org/en/latest/code-of-conduct.html Changes ~~~~~~~ .. include:: ../CHANGES.txt :start-line: 3 PyCQA-pycodestyle-0dd4a37/docs/index.rst000066400000000000000000000035621465376172200202150ustar00rootroot00000000000000.. pycodestyle documentation master file pycodestyle's documentation =========================== *Python style guide checker* pycodestyle (formerly pep8) is a tool to check your Python code against some of the style conventions in `PEP 8`_. .. _PEP 8: http://www.python.org/dev/peps/pep-0008/ Contents: .. toctree:: :maxdepth: 2 intro advanced API developer * Online documentation: https://pycodestyle.pycqa.org/ * Source code and issue tracker: https://github.com/pycqa/pycodestyle Indices and tables ================== * :ref:`genindex` * :ref:`search` Credits ======= Created by Johann C. Rocholl. Maintained by Florent Xicluna and Ian Lee. .. _license: License ======= The ``pycodestyle`` library is provided under the terms and conditions of the Expat license:: # 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 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 AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. PyCQA-pycodestyle-0dd4a37/docs/intro.rst000066400000000000000000000634311465376172200202420ustar00rootroot00000000000000.. currentmodule:: pycodestyle Introduction ============ pycodestyle is a tool to check your Python code against some of the style conventions in `PEP 8`_. .. contents:: :local: Features -------- * Plugin architecture: Adding new checks is easy. * Parseable output: Jump to error location in your editor. * Small: Just one Python file, requires only stdlib. You can use just the ``pycodestyle.py`` file for this purpose. * Comes with a comprehensive test suite. Disclaimer ---------- This utility does not enforce every single rule of PEP 8. It helps to verify that some coding conventions are applied but it does not intend to be exhaustive. Some rules cannot be expressed with a simple algorithm, and other rules are only guidelines which you could circumvent when you need to. Always remember this statement from `PEP 8`_: *A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is most important.* Among other things, these features are currently not in the scope of the ``pycodestyle`` library: * **naming conventions**: this kind of feature is supported through plugins. Install `flake8 `_ and the `pep8-naming extension `_ to use this feature. * **docstring conventions**: they are not in the scope of this library; see the `pydocstyle project `_. * **automatic fixing**: see the section *PEP8 Fixers* in the :ref:`related tools ` page. Installation ------------ You can install, upgrade, uninstall ``pycodestyle.py`` with these commands:: $ pip install pycodestyle $ pip install --upgrade pycodestyle $ pip uninstall pycodestyle Example usage and output ------------------------ :: $ pycodestyle --first optparse.py optparse.py:69:11: E401 multiple imports on one line optparse.py:77:1: E302 expected 2 blank lines, found 1 optparse.py:88:5: E301 expected 1 blank line, found 0 optparse.py:347:31: E211 whitespace before '(' optparse.py:357:17: E201 whitespace after '{' optparse.py:472:29: E221 multiple spaces before operator You can also make ``pycodestyle.py`` show the source code for each error, and even the relevant text from PEP 8:: $ pycodestyle --show-source --show-pep8 testing/data/E40.py testing/data/E40.py:2:10: E401 multiple imports on one line import os, sys ^ Imports should usually be on separate lines. Okay: import os\nimport sys E401: import sys, os Or you can display how often each error was found:: $ pycodestyle --statistics -qq Python-2.5/Lib 232 E201 whitespace after '[' 599 E202 whitespace before ')' 631 E203 whitespace before ',' 842 E211 whitespace before '(' 2531 E221 multiple spaces before operator 4473 E301 expected 1 blank line, found 0 4006 E302 expected 2 blank lines, found 1 165 E303 too many blank lines (4) 325 E401 multiple imports on one line 3615 E501 line too long (82 characters) You can also make ``pycodestyle.py`` show the error text in different formats by using ``--format`` having options default/pylint/custom:: $ pycodestyle testing/data/E40.py --format=default testing/data/E40.py:2:10: E401 multiple imports on one line $ pycodestyle testing/data/E40.py --format=pylint testing/data/E40.py:2: [E401] multiple imports on one line $ pycodestyle testing/data/E40.py --format='%(path)s|%(row)d|%(col)d| %(code)s %(text)s' testing/data/E40.py|2|10| E401 multiple imports on one line Variables in the ``custom`` format option +----------------+------------------+ | Variable | Significance | +================+==================+ | ``path`` | File name | +----------------+------------------+ | ``row`` | Row number | +----------------+------------------+ | ``col`` | Column number | +----------------+------------------+ | ``code`` | Error code | +----------------+------------------+ | ``text`` | Error text | +----------------+------------------+ Quick help is available on the command line:: $ pycodestyle -h Usage: pycodestyle [options] input ... Options: --version show program's version number and exit -h, --help show this help message and exit -v, --verbose print status messages, or debug with -vv -q, --quiet report only file names, or nothing with -qq --first show first occurrence of each error --exclude=patterns exclude files or directories which match these comma separated patterns (default: .svn,CVS,.bzr,.hg,.git) --filename=patterns when parsing directories, only check filenames matching these comma separated patterns (default: *.py) --select=errors select errors and warnings (e.g. E,W6) --ignore=errors skip errors and warnings (e.g. E4,W) --show-source show source code for each error --show-pep8 show text of PEP 8 for each error (implies --first) --statistics count errors and warnings --count print total number of errors and warnings to standard error and set exit code to 1 if total is not null --max-line-length=n set maximum allowed line length (default: 79) --max-doc-length=n set maximum allowed doc line length and perform these checks (unchecked if not set) --indent-size=n set how many spaces make up an indent (default: 4) --hang-closing hang closing bracket instead of matching indentation of opening bracket's line --format=format set the error format [default|pylint|] --diff report only lines changed according to the unified diff received on STDIN Testing Options: --benchmark measure processing speed Configuration: The project options are read from the [pycodestyle] section of the tox.ini file or the setup.cfg file located in any parent folder of the path(s) being processed. Allowed options are: exclude, filename, select, ignore, max-line-length, max-doc-length, hang-closing, count, format, quiet, show-pep8, show-source, statistics, verbose. --config=path user config file location (default: ~/.config/pycodestyle) Configuration ------------- The behaviour may be configured at two levels, the user and project levels. At the user level, settings are read from the following locations: If on Windows: ``~\.pycodestyle`` Otherwise, if the :envvar:`XDG_CONFIG_HOME` environment variable is defined: ``XDG_CONFIG_HOME/pycodestyle`` Else if :envvar:`XDG_CONFIG_HOME` is not defined: ``~/.config/pycodestyle`` Example:: [pycodestyle] count = False ignore = E226,E302,E71 max-line-length = 160 statistics = True At the project level, a ``setup.cfg`` file or a ``tox.ini`` file is read if present. If none of these files have a ``[pycodestyle]`` section, no project specific configuration is loaded. Error codes ----------- This is the current list of error and warning codes: +------------+----------------------------------------------------------------------+ | code | sample message | +============+======================================================================+ | **E1** | *Indentation* | +------------+----------------------------------------------------------------------+ | E101 | indentation contains mixed spaces and tabs | +------------+----------------------------------------------------------------------+ | E111 | indentation is not a multiple of four | +------------+----------------------------------------------------------------------+ | E112 | expected an indented block | +------------+----------------------------------------------------------------------+ | E113 | unexpected indentation | +------------+----------------------------------------------------------------------+ | E114 | indentation is not a multiple of four (comment) | +------------+----------------------------------------------------------------------+ | E115 | expected an indented block (comment) | +------------+----------------------------------------------------------------------+ | E116 | unexpected indentation (comment) | +------------+----------------------------------------------------------------------+ | E117 | over-indented | +------------+----------------------------------------------------------------------+ | E121 (\*^) | continuation line under-indented for hanging indent | +------------+----------------------------------------------------------------------+ | E122 (^) | continuation line missing indentation or outdented | +------------+----------------------------------------------------------------------+ | E123 (*) | closing bracket does not match indentation of opening bracket's line | +------------+----------------------------------------------------------------------+ | E124 (^) | closing bracket does not match visual indentation | +------------+----------------------------------------------------------------------+ | E125 (^) | continuation line with same indent as next logical line | +------------+----------------------------------------------------------------------+ | E126 (\*^) | continuation line over-indented for hanging indent | +------------+----------------------------------------------------------------------+ | E127 (^) | continuation line over-indented for visual indent | +------------+----------------------------------------------------------------------+ | E128 (^) | continuation line under-indented for visual indent | +------------+----------------------------------------------------------------------+ | E129 (^) | visually indented line with same indent as next logical line | +------------+----------------------------------------------------------------------+ | E131 (^) | continuation line unaligned for hanging indent | +------------+----------------------------------------------------------------------+ | E133 (*) | closing bracket is missing indentation | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | **E2** | *Whitespace* | +------------+----------------------------------------------------------------------+ | E201 | whitespace after '(' | +------------+----------------------------------------------------------------------+ | E202 | whitespace before ')' | +------------+----------------------------------------------------------------------+ | E203 | whitespace before ',', ';', or ':' | +------------+----------------------------------------------------------------------+ | E204 | whitespace after decorator '@' | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | E211 | whitespace before '(' | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | E221 | multiple spaces before operator | +------------+----------------------------------------------------------------------+ | E222 | multiple spaces after operator | +------------+----------------------------------------------------------------------+ | E223 | tab before operator | +------------+----------------------------------------------------------------------+ | E224 | tab after operator | +------------+----------------------------------------------------------------------+ | E225 | missing whitespace around operator | +------------+----------------------------------------------------------------------+ | E226 (*) | missing whitespace around arithmetic operator | +------------+----------------------------------------------------------------------+ | E227 | missing whitespace around bitwise or shift operator | +------------+----------------------------------------------------------------------+ | E228 | missing whitespace around modulo operator | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | E231 | missing whitespace after ',', ';', or ':' | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | E241 (*) | multiple spaces after ',' | +------------+----------------------------------------------------------------------+ | E242 (*) | tab after ',' | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | E251 | unexpected spaces around keyword / parameter equals | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | E261 | at least two spaces before inline comment | +------------+----------------------------------------------------------------------+ | E262 | inline comment should start with '# ' | +------------+----------------------------------------------------------------------+ | E265 | block comment should start with '# ' | +------------+----------------------------------------------------------------------+ | E266 | too many leading '#' for block comment | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | E271 | multiple spaces after keyword | +------------+----------------------------------------------------------------------+ | E272 | multiple spaces before keyword | +------------+----------------------------------------------------------------------+ | E273 | tab after keyword | +------------+----------------------------------------------------------------------+ | E274 | tab before keyword | +------------+----------------------------------------------------------------------+ | E275 | missing whitespace after keyword | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | **E3** | *Blank line* | +------------+----------------------------------------------------------------------+ | E301 | expected 1 blank line, found 0 | +------------+----------------------------------------------------------------------+ | E302 | expected 2 blank lines, found 0 | +------------+----------------------------------------------------------------------+ | E303 | too many blank lines (3) | +------------+----------------------------------------------------------------------+ | E304 | blank lines found after function decorator | +------------+----------------------------------------------------------------------+ | E305 | expected 2 blank lines after end of function or class | +------------+----------------------------------------------------------------------+ | E306 | expected 1 blank line before a nested definition | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | **E4** | *Import* | +------------+----------------------------------------------------------------------+ | E401 | multiple imports on one line | +------------+----------------------------------------------------------------------+ | E402 | module level import not at top of file | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | **E5** | *Line length* | +------------+----------------------------------------------------------------------+ | E501 (^) | line too long (82 > 79 characters) | +------------+----------------------------------------------------------------------+ | E502 | the backslash is redundant between brackets | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | **E7** | *Statement* | +------------+----------------------------------------------------------------------+ | E701 | multiple statements on one line (colon) | +------------+----------------------------------------------------------------------+ | E702 | multiple statements on one line (semicolon) | +------------+----------------------------------------------------------------------+ | E703 | statement ends with a semicolon | +------------+----------------------------------------------------------------------+ | E704 (*) | multiple statements on one line (def) | +------------+----------------------------------------------------------------------+ | E711 (^) | comparison to None should be 'if cond is None:' | +------------+----------------------------------------------------------------------+ | E712 (^) | comparison to True should be 'if cond is True:' or 'if cond:' | +------------+----------------------------------------------------------------------+ | E713 | test for membership should be 'not in' | +------------+----------------------------------------------------------------------+ | E714 | test for object identity should be 'is not' | +------------+----------------------------------------------------------------------+ | E721 (^) | do not compare types, use 'isinstance()' | +------------+----------------------------------------------------------------------+ | E722 | do not use bare except, specify exception instead | +------------+----------------------------------------------------------------------+ | E731 | do not assign a lambda expression, use a def | +------------+----------------------------------------------------------------------+ | E741 | do not use variables named 'l', 'O', or 'I' | +------------+----------------------------------------------------------------------+ | E742 | do not define classes named 'l', 'O', or 'I' | +------------+----------------------------------------------------------------------+ | E743 | do not define functions named 'l', 'O', or 'I' | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | **E9** | *Runtime* | +------------+----------------------------------------------------------------------+ | E901 | SyntaxError or IndentationError | +------------+----------------------------------------------------------------------+ | E902 | IOError | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | **W1** | *Indentation warning* | +------------+----------------------------------------------------------------------+ | W191 | indentation contains tabs | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | **W2** | *Whitespace warning* | +------------+----------------------------------------------------------------------+ | W291 | trailing whitespace | +------------+----------------------------------------------------------------------+ | W292 | no newline at end of file | +------------+----------------------------------------------------------------------+ | W293 | blank line contains whitespace | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | **W3** | *Blank line warning* | +------------+----------------------------------------------------------------------+ | W391 | blank line at end of file | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | **W5** | *Line break warning* | +------------+----------------------------------------------------------------------+ | W503 (*) | line break before binary operator | +------------+----------------------------------------------------------------------+ | W504 (*) | line break after binary operator | +------------+----------------------------------------------------------------------+ | W505 (\*^) | doc line too long (82 > 79 characters) | +------------+----------------------------------------------------------------------+ +------------+----------------------------------------------------------------------+ | **W6** | *Deprecation warning* | +------------+----------------------------------------------------------------------+ | W605 | invalid escape sequence '\x' | +------------+----------------------------------------------------------------------+ **(*)** In the default configuration, the checks **E121**, **E123**, **E126**, **E133**, **E226**, **E241**, **E242**, **E704**, **W503**, **W504** and **W505** are ignored because they are not rules unanimously accepted, and `PEP 8`_ does not enforce them. Please note that if the option ``--ignore=errors`` is used, the default configuration will be overridden and ignore only the check(s) you skip. The check **W503** is mutually exclusive with check **W504**. The check **E133** is mutually exclusive with check **E123**. Use switch ``--hang-closing`` to report **E133** instead of **E123**. Use switch ``--max-doc-length=n`` to report **W505**. **(^)** These checks can be disabled at the line level using the ``# noqa`` special comment. This possibility should be reserved for special cases. *Special cases aren't special enough to break the rules.* Note: most errors can be listed with such one-liner:: $ python pycodestyle.py --first --select E,W testing/data --format '%(code)s: %(text)s' .. _related-tools: Related tools ------------- The `flake8 checker `_ is a wrapper around ``pycodestyle`` and similar tools. It supports plugins. Other tools which use ``pycodestyle`` are referenced in the Wiki: `list of related tools `_. .. _PEP 8: http://www.python.org/dev/peps/pep-0008/ PyCQA-pycodestyle-0dd4a37/docs/make.bat000066400000000000000000000117621465376172200177620ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pycodestyle.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pycodestyle.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end PyCQA-pycodestyle-0dd4a37/pycodestyle.py000077500000000000000000003036301465376172200203440ustar00rootroot00000000000000#!/usr/bin/env python # pycodestyle.py - Check Python source code formatting, according to # PEP 8 # # Copyright (C) 2006-2009 Johann C. Rocholl # Copyright (C) 2009-2014 Florent Xicluna # Copyright (C) 2014-2016 Ian Lee # # 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 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 AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. r""" Check Python source code formatting, according to PEP 8. For usage and a list of options, try this: $ python pycodestyle.py -h This program and its regression test suite live here: https://github.com/pycqa/pycodestyle Groups of errors and warnings: E errors W warnings 100 indentation 200 whitespace 300 blank lines 400 imports 500 line length 600 deprecation 700 statements 900 syntax error """ import bisect import configparser import inspect import io import keyword import os import re import sys import time import tokenize import warnings from fnmatch import fnmatch from functools import lru_cache from optparse import OptionParser # this is a performance hack. see https://bugs.python.org/issue43014 if ( sys.version_info < (3, 10) and callable(getattr(tokenize, '_compile', None)) ): # pragma: no cover (>', '**', '*', '+', '-']) ARITHMETIC_OP = frozenset(['**', '*', '/', '//', '+', '-', '@']) WS_OPTIONAL_OPERATORS = ARITHMETIC_OP.union(['^', '&', '|', '<<', '>>', '%']) WS_NEEDED_OPERATORS = frozenset([ '**=', '*=', '/=', '//=', '+=', '-=', '!=', '<', '>', '%=', '^=', '&=', '|=', '==', '<=', '>=', '<<=', '>>=', '=', 'and', 'in', 'is', 'or', '->', ':=']) WHITESPACE = frozenset(' \t\xa0') NEWLINE = frozenset([tokenize.NL, tokenize.NEWLINE]) SKIP_TOKENS = NEWLINE.union([tokenize.INDENT, tokenize.DEDENT]) # ERRORTOKEN is triggered by backticks in Python 3 SKIP_COMMENTS = SKIP_TOKENS.union([tokenize.COMMENT, tokenize.ERRORTOKEN]) BENCHMARK_KEYS = ['directories', 'files', 'logical lines', 'physical lines'] INDENT_REGEX = re.compile(r'([ \t]*)') ERRORCODE_REGEX = re.compile(r'\b[A-Z]\d{3}\b') DOCSTRING_REGEX = re.compile(r'u?r?["\']') EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[\[({][ \t]|[ \t][\]}),;:](?!=)') WHITESPACE_AFTER_DECORATOR_REGEX = re.compile(r'@\s') WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)') COMPARE_SINGLETON_REGEX = re.compile(r'(\bNone|\bFalse|\bTrue)?\s*([=!]=)' r'\s*(?(1)|(None|False|True))\b') COMPARE_NEGATIVE_REGEX = re.compile(r'\b(?%&^]+|:=)(\s*)') LAMBDA_REGEX = re.compile(r'\blambda\b') HUNK_REGEX = re.compile(r'^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$') STARTSWITH_DEF_REGEX = re.compile(r'^(async\s+def|def)\b') STARTSWITH_TOP_LEVEL_REGEX = re.compile(r'^(async\s+def\s+|def\s+|class\s+|@)') STARTSWITH_INDENT_STATEMENT_REGEX = re.compile( r'^\s*({})\b'.format('|'.join(s.replace(' ', r'\s+') for s in ( 'def', 'async def', 'for', 'async for', 'if', 'elif', 'else', 'try', 'except', 'finally', 'with', 'async with', 'class', 'while', ))) ) DUNDER_REGEX = re.compile(r"^__([^\s]+)__(?::\s*[a-zA-Z.0-9_\[\]\"]+)? = ") BLANK_EXCEPT_REGEX = re.compile(r"except\s*:") if sys.version_info >= (3, 12): # pragma: >=3.12 cover FSTRING_START = tokenize.FSTRING_START FSTRING_MIDDLE = tokenize.FSTRING_MIDDLE FSTRING_END = tokenize.FSTRING_END else: # pragma: <3.12 cover FSTRING_START = FSTRING_MIDDLE = FSTRING_END = -1 _checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}} def _get_parameters(function): return [parameter.name for parameter in inspect.signature(function).parameters.values() if parameter.kind == parameter.POSITIONAL_OR_KEYWORD] def register_check(check, codes=None): """Register a new check object.""" def _add_check(check, kind, codes, args): if check in _checks[kind]: _checks[kind][check][0].extend(codes or []) else: _checks[kind][check] = (codes or [''], args) if inspect.isfunction(check): args = _get_parameters(check) if args and args[0] in ('physical_line', 'logical_line'): if codes is None: codes = ERRORCODE_REGEX.findall(check.__doc__ or '') _add_check(check, args[0], codes, args) elif inspect.isclass(check): if _get_parameters(check.__init__)[:2] == ['self', 'tree']: _add_check(check, 'tree', codes, None) return check ######################################################################## # Plugins (check functions) for physical lines ######################################################################## @register_check def tabs_or_spaces(physical_line, indent_char): r"""Never mix tabs and spaces. The most popular way of indenting Python is with spaces only. The second-most popular way is with tabs only. Code indented with a mixture of tabs and spaces should be converted to using spaces exclusively. When invoking the Python command line interpreter with the -t option, it issues warnings about code that illegally mixes tabs and spaces. When using -tt these warnings become errors. These options are highly recommended! Okay: if a == 0:\n a = 1\n b = 1 """ indent = INDENT_REGEX.match(physical_line).group(1) for offset, char in enumerate(indent): if char != indent_char: return offset, "E101 indentation contains mixed spaces and tabs" @register_check def tabs_obsolete(physical_line): r"""On new projects, spaces-only are strongly recommended over tabs. Okay: if True:\n return W191: if True:\n\treturn """ indent = INDENT_REGEX.match(physical_line).group(1) if '\t' in indent: return indent.index('\t'), "W191 indentation contains tabs" @register_check def trailing_whitespace(physical_line): r"""Trailing whitespace is superfluous. The warning returned varies on whether the line itself is blank, for easier filtering for those who want to indent their blank lines. Okay: spam(1)\n# W291: spam(1) \n# W293: class Foo(object):\n \n bang = 12 """ physical_line = physical_line.rstrip('\n') # chr(10), newline physical_line = physical_line.rstrip('\r') # chr(13), carriage return physical_line = physical_line.rstrip('\x0c') # chr(12), form feed, ^L stripped = physical_line.rstrip(' \t\v') if physical_line != stripped: if stripped: return len(stripped), "W291 trailing whitespace" else: return 0, "W293 blank line contains whitespace" @register_check def trailing_blank_lines(physical_line, lines, line_number, total_lines): r"""Trailing blank lines are superfluous. Okay: spam(1) W391: spam(1)\n However the last line should end with a new line (warning W292). """ if line_number == total_lines: stripped_last_line = physical_line.rstrip('\r\n') if physical_line and not stripped_last_line: return 0, "W391 blank line at end of file" if stripped_last_line == physical_line: return len(lines[-1]), "W292 no newline at end of file" @register_check def maximum_line_length(physical_line, max_line_length, multiline, line_number, noqa): r"""Limit all lines to a maximum of 79 characters. There are still many devices around that are limited to 80 character lines; plus, limiting windows to 80 characters makes it possible to have several windows side-by-side. The default wrapping on such devices looks ugly. Therefore, please limit all lines to a maximum of 79 characters. For flowing long blocks of text (docstrings or comments), limiting the length to 72 characters is recommended. Reports error E501. """ line = physical_line.rstrip() length = len(line) if length > max_line_length and not noqa: # Special case: ignore long shebang lines. if line_number == 1 and line.startswith('#!'): return # Special case for long URLs in multi-line docstrings or # comments, but still report the error when the 72 first chars # are whitespaces. chunks = line.split() if ((len(chunks) == 1 and multiline) or (len(chunks) == 2 and chunks[0] == '#')) and \ len(line) - len(chunks[-1]) < max_line_length - 7: return if length > max_line_length: return (max_line_length, "E501 line too long " "(%d > %d characters)" % (length, max_line_length)) ######################################################################## # Plugins (check functions) for logical lines ######################################################################## def _is_one_liner(logical_line, indent_level, lines, line_number): if not STARTSWITH_TOP_LEVEL_REGEX.match(logical_line): return False line_idx = line_number - 1 if line_idx < 1: prev_indent = 0 else: prev_indent = expand_indent(lines[line_idx - 1]) if prev_indent > indent_level: return False while line_idx < len(lines): line = lines[line_idx].strip() if not line.startswith('@') and STARTSWITH_TOP_LEVEL_REGEX.match(line): break else: line_idx += 1 else: return False # invalid syntax: EOF while searching for def/class next_idx = line_idx + 1 while next_idx < len(lines): if lines[next_idx].strip(): break else: next_idx += 1 else: return True # line is last in the file return expand_indent(lines[next_idx]) <= indent_level @register_check def blank_lines(logical_line, blank_lines, indent_level, line_number, blank_before, previous_logical, previous_unindented_logical_line, previous_indent_level, lines): r"""Separate top-level function and class definitions with two blank lines. Method definitions inside a class are separated by a single blank line. Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations). Use blank lines in functions, sparingly, to indicate logical sections. Okay: def a():\n pass\n\n\ndef b():\n pass Okay: def a():\n pass\n\n\nasync def b():\n pass Okay: def a():\n pass\n\n\n# Foo\n# Bar\n\ndef b():\n pass Okay: default = 1\nfoo = 1 Okay: classify = 1\nfoo = 1 E301: class Foo:\n b = 0\n def bar():\n pass E302: def a():\n pass\n\ndef b(n):\n pass E302: def a():\n pass\n\nasync def b(n):\n pass E303: def a():\n pass\n\n\n\ndef b(n):\n pass E303: def a():\n\n\n\n pass E304: @decorator\n\ndef a():\n pass E305: def a():\n pass\na() E306: def a():\n def b():\n pass\n def c():\n pass """ # noqa top_level_lines = BLANK_LINES_CONFIG['top_level'] method_lines = BLANK_LINES_CONFIG['method'] if not previous_logical and blank_before < top_level_lines: return # Don't expect blank lines before the first line if previous_logical.startswith('@'): if blank_lines: yield 0, "E304 blank lines found after function decorator" elif (blank_lines > top_level_lines or (indent_level and blank_lines == method_lines + 1) ): yield 0, "E303 too many blank lines (%d)" % blank_lines elif STARTSWITH_TOP_LEVEL_REGEX.match(logical_line): # allow a group of one-liners if ( _is_one_liner(logical_line, indent_level, lines, line_number) and blank_before == 0 ): return if indent_level: if not (blank_before == method_lines or previous_indent_level < indent_level or DOCSTRING_REGEX.match(previous_logical) ): ancestor_level = indent_level nested = False # Search backwards for a def ancestor or tree root # (top level). for line in lines[line_number - top_level_lines::-1]: if line.strip() and expand_indent(line) < ancestor_level: ancestor_level = expand_indent(line) nested = STARTSWITH_DEF_REGEX.match(line.lstrip()) if nested or ancestor_level == 0: break if nested: yield 0, "E306 expected %s blank line before a " \ "nested definition, found 0" % (method_lines,) else: yield 0, "E301 expected {} blank line, found 0".format( method_lines) elif blank_before != top_level_lines: yield 0, "E302 expected %s blank lines, found %d" % ( top_level_lines, blank_before) elif (logical_line and not indent_level and blank_before != top_level_lines and previous_unindented_logical_line.startswith(('def ', 'class ')) ): yield 0, "E305 expected %s blank lines after " \ "class or function definition, found %d" % ( top_level_lines, blank_before) @register_check def extraneous_whitespace(logical_line): r"""Avoid extraneous whitespace. Avoid extraneous whitespace in these situations: - Immediately inside parentheses, brackets or braces. - Immediately before a comma, semicolon, or colon. Okay: spam(ham[1], {eggs: 2}) E201: spam( ham[1], {eggs: 2}) E201: spam(ham[ 1], {eggs: 2}) E201: spam(ham[1], { eggs: 2}) E202: spam(ham[1], {eggs: 2} ) E202: spam(ham[1 ], {eggs: 2}) E202: spam(ham[1], {eggs: 2 }) E203: if x == 4: print x, y; x, y = y , x E203: if x == 4: print x, y ; x, y = y, x E203: if x == 4 : print x, y; x, y = y, x Okay: @decorator E204: @ decorator """ line = logical_line for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line): text = match.group() char = text.strip() found = match.start() if text[-1].isspace(): # assert char in '([{' yield found + 1, "E201 whitespace after '%s'" % char elif line[found - 1] != ',': code = ('E202' if char in '}])' else 'E203') # if char in ',;:' yield found, f"{code} whitespace before '{char}'" if WHITESPACE_AFTER_DECORATOR_REGEX.match(logical_line): yield 1, "E204 whitespace after decorator '@'" @register_check def whitespace_around_keywords(logical_line): r"""Avoid extraneous whitespace around keywords. Okay: True and False E271: True and False E272: True and False E273: True and\tFalse E274: True\tand False """ for match in KEYWORD_REGEX.finditer(logical_line): before, after = match.groups() if '\t' in before: yield match.start(1), "E274 tab before keyword" elif len(before) > 1: yield match.start(1), "E272 multiple spaces before keyword" if '\t' in after: yield match.start(2), "E273 tab after keyword" elif len(after) > 1: yield match.start(2), "E271 multiple spaces after keyword" @register_check def missing_whitespace_after_keyword(logical_line, tokens): r"""Keywords should be followed by whitespace. Okay: from foo import (bar, baz) E275: from foo import(bar, baz) E275: from importable.module import(bar, baz) E275: if(foo): bar """ for tok0, tok1 in zip(tokens, tokens[1:]): # This must exclude the True/False/None singletons, which can # appear e.g. as "if x is None:", and async/await, which were # valid identifier names in old Python versions. if (tok0.end == tok1.start and tok0.type == tokenize.NAME and keyword.iskeyword(tok0.string) and tok0.string not in SINGLETONS and not (tok0.string == 'except' and tok1.string == '*') and not (tok0.string == 'yield' and tok1.string == ')') and tok1.string not in ':\n'): yield tok0.end, "E275 missing whitespace after keyword" @register_check def indentation(logical_line, previous_logical, indent_char, indent_level, previous_indent_level, indent_size): r"""Use indent_size (PEP8 says 4) spaces per indentation level. For really old code that you don't want to mess up, you can continue to use 8-space tabs. Okay: a = 1 Okay: if a == 0:\n a = 1 E111: a = 1 E114: # a = 1 Okay: for item in items:\n pass E112: for item in items:\npass E115: for item in items:\n# Hi\n pass Okay: a = 1\nb = 2 E113: a = 1\n b = 2 E116: a = 1\n # b = 2 """ c = 0 if logical_line else 3 tmpl = "E11%d %s" if logical_line else "E11%d %s (comment)" if indent_level % indent_size: yield 0, tmpl % ( 1 + c, "indentation is not a multiple of " + str(indent_size), ) indent_expect = previous_logical.endswith(':') if indent_expect and indent_level <= previous_indent_level: yield 0, tmpl % (2 + c, "expected an indented block") elif not indent_expect and indent_level > previous_indent_level: yield 0, tmpl % (3 + c, "unexpected indentation") if indent_expect: expected_indent_amount = 8 if indent_char == '\t' else 4 expected_indent_level = previous_indent_level + expected_indent_amount if indent_level > expected_indent_level: yield 0, tmpl % (7, 'over-indented') @register_check def continued_indentation(logical_line, tokens, indent_level, hang_closing, indent_char, indent_size, noqa, verbose): r"""Continuation lines indentation. Continuation lines should align wrapped elements either vertically using Python's implicit line joining inside parentheses, brackets and braces, or using a hanging indent. When using a hanging indent these considerations should be applied: - there should be no arguments on the first line, and - further indentation should be used to clearly distinguish itself as a continuation line. Okay: a = (\n) E123: a = (\n ) Okay: a = (\n 42) E121: a = (\n 42) E122: a = (\n42) E123: a = (\n 42\n ) E124: a = (24,\n 42\n) E125: if (\n b):\n pass E126: a = (\n 42) E127: a = (24,\n 42) E128: a = (24,\n 42) E129: if (a or\n b):\n pass E131: a = (\n 42\n 24) """ first_row = tokens[0][2][0] nrows = 1 + tokens[-1][2][0] - first_row if noqa or nrows == 1: return # indent_next tells us whether the next block is indented; assuming # that it is indented by 4 spaces, then we should not allow 4-space # indents on the final continuation line; in turn, some other # indents are allowed to have an extra 4 spaces. indent_next = logical_line.endswith(':') row = depth = 0 valid_hangs = (indent_size,) if indent_char != '\t' \ else (indent_size, indent_size * 2) # remember how many brackets were opened on each line parens = [0] * nrows # relative indents of physical lines rel_indent = [0] * nrows # for each depth, collect a list of opening rows open_rows = [[0]] # for each depth, memorize the hanging indentation hangs = [None] # visual indents indent_chances = {} last_indent = tokens[0][2] visual_indent = None last_token_multiline = False # for each depth, memorize the visual indent column indent = [last_indent[1]] if verbose >= 3: print(">>> " + tokens[0][4].rstrip()) for token_type, text, start, end, line in tokens: newline = row < start[0] - first_row if newline: row = start[0] - first_row newline = not last_token_multiline and token_type not in NEWLINE if newline: # this is the beginning of a continuation line. last_indent = start if verbose >= 3: print("... " + line.rstrip()) # record the initial indent. rel_indent[row] = expand_indent(line) - indent_level # identify closing bracket close_bracket = (token_type == tokenize.OP and text in ']})') # is the indent relative to an opening bracket line? for open_row in reversed(open_rows[depth]): hang = rel_indent[row] - rel_indent[open_row] hanging_indent = hang in valid_hangs if hanging_indent: break if hangs[depth]: hanging_indent = (hang == hangs[depth]) # is there any chance of visual indent? visual_indent = (not close_bracket and hang > 0 and indent_chances.get(start[1])) if close_bracket and indent[depth]: # closing bracket for visual indent if start[1] != indent[depth]: yield (start, "E124 closing bracket does not match " "visual indentation") elif close_bracket and not hang: # closing bracket matches indentation of opening # bracket's line if hang_closing: yield start, "E133 closing bracket is missing indentation" elif indent[depth] and start[1] < indent[depth]: if visual_indent is not True: # visual indent is broken yield (start, "E128 continuation line " "under-indented for visual indent") elif hanging_indent or (indent_next and rel_indent[row] == 2 * indent_size): # hanging indent is verified if close_bracket and not hang_closing: yield (start, "E123 closing bracket does not match " "indentation of opening bracket's line") hangs[depth] = hang elif visual_indent is True: # visual indent is verified indent[depth] = start[1] elif visual_indent in (text, str): # ignore token lined up with matching one from a # previous line pass else: # indent is broken if hang <= 0: error = "E122", "missing indentation or outdented" elif indent[depth]: error = "E127", "over-indented for visual indent" elif not close_bracket and hangs[depth]: error = "E131", "unaligned for hanging indent" else: hangs[depth] = hang if hang > indent_size: error = "E126", "over-indented for hanging indent" else: error = "E121", "under-indented for hanging indent" yield start, "%s continuation line %s" % error # look for visual indenting if (parens[row] and token_type not in (tokenize.NL, tokenize.COMMENT) and not indent[depth]): indent[depth] = start[1] indent_chances[start[1]] = True if verbose >= 4: print(f"bracket depth {depth} indent to {start[1]}") # deal with implicit string concatenation elif token_type in (tokenize.STRING, tokenize.COMMENT, FSTRING_START): indent_chances[start[1]] = str # visual indent after assert/raise/with elif not row and not depth and text in ["assert", "raise", "with"]: indent_chances[end[1] + 1] = True # special case for the "if" statement because len("if (") == 4 elif not indent_chances and not row and not depth and text == 'if': indent_chances[end[1] + 1] = True elif text == ':' and line[end[1]:].isspace(): open_rows[depth].append(row) # keep track of bracket depth if token_type == tokenize.OP: if text in '([{': depth += 1 indent.append(0) hangs.append(None) if len(open_rows) == depth: open_rows.append([]) open_rows[depth].append(row) parens[row] += 1 if verbose >= 4: print("bracket depth %s seen, col %s, visual min = %s" % (depth, start[1], indent[depth])) elif text in ')]}' and depth > 0: # parent indents should not be more than this one prev_indent = indent.pop() or last_indent[1] hangs.pop() for d in range(depth): if indent[d] > prev_indent: indent[d] = 0 for ind in list(indent_chances): if ind >= prev_indent: del indent_chances[ind] del open_rows[depth + 1:] depth -= 1 if depth: indent_chances[indent[depth]] = True for idx in range(row, -1, -1): if parens[idx]: parens[idx] -= 1 break assert len(indent) == depth + 1 if start[1] not in indent_chances: # allow lining up tokens indent_chances[start[1]] = text last_token_multiline = (start[0] != end[0]) if last_token_multiline: rel_indent[end[0] - first_row] = rel_indent[row] if indent_next and expand_indent(line) == indent_level + indent_size: pos = (start[0], indent[0] + indent_size) if visual_indent: code = "E129 visually indented line" else: code = "E125 continuation line" yield pos, "%s with same indent as next logical line" % code @register_check def whitespace_before_parameters(logical_line, tokens): r"""Avoid extraneous whitespace. Avoid extraneous whitespace in the following situations: - before the open parenthesis that starts the argument list of a function call. - before the open parenthesis that starts an indexing or slicing. Okay: spam(1) E211: spam (1) Okay: dict['key'] = list[index] E211: dict ['key'] = list[index] E211: dict['key'] = list [index] """ prev_type, prev_text, __, prev_end, __ = tokens[0] for index in range(1, len(tokens)): token_type, text, start, end, __ = tokens[index] if ( token_type == tokenize.OP and text in '([' and start != prev_end and (prev_type == tokenize.NAME or prev_text in '}])') and # Syntax "class A (B):" is allowed, but avoid it (index < 2 or tokens[index - 2][1] != 'class') and # Allow "return (a.foo for a in range(5))" not keyword.iskeyword(prev_text) and ( sys.version_info < (3, 9) or # 3.12+: type is a soft keyword but no braces after prev_text == 'type' or not keyword.issoftkeyword(prev_text) ) ): yield prev_end, "E211 whitespace before '%s'" % text prev_type = token_type prev_text = text prev_end = end @register_check def whitespace_around_operator(logical_line): r"""Avoid extraneous whitespace around an operator. Okay: a = 12 + 3 E221: a = 4 + 5 E222: a = 4 + 5 E223: a = 4\t+ 5 E224: a = 4 +\t5 """ for match in OPERATOR_REGEX.finditer(logical_line): before, after = match.groups() if '\t' in before: yield match.start(1), "E223 tab before operator" elif len(before) > 1: yield match.start(1), "E221 multiple spaces before operator" if '\t' in after: yield match.start(2), "E224 tab after operator" elif len(after) > 1: yield match.start(2), "E222 multiple spaces after operator" @register_check def missing_whitespace(logical_line, tokens): r"""Surround operators with the correct amount of whitespace. - Always surround these binary operators with a single space on either side: assignment (=), augmented assignment (+=, -= etc.), comparisons (==, <, >, !=, <=, >=, in, not in, is, is not), Booleans (and, or, not). - Each comma, semicolon or colon should be followed by whitespace. - If operators with different priorities are used, consider adding whitespace around the operators with the lowest priorities. Okay: i = i + 1 Okay: submitted += 1 Okay: x = x * 2 - 1 Okay: hypot2 = x * x + y * y Okay: c = (a + b) * (a - b) Okay: foo(bar, key='word', *args, **kwargs) Okay: alpha[:-i] Okay: [a, b] Okay: (3,) Okay: a[3,] = 1 Okay: a[1:4] Okay: a[:4] Okay: a[1:] Okay: a[1:4:2] E225: i=i+1 E225: submitted +=1 E225: x = x /2 - 1 E225: z = x **y E225: z = 1and 1 E226: c = (a+b) * (a-b) E226: hypot2 = x*x + y*y E227: c = a|b E228: msg = fmt%(errno, errmsg) E231: ['a','b'] E231: foo(bar,baz) E231: [{'a':'b'}] """ need_space = False prev_type = tokenize.OP prev_text = prev_end = None operator_types = (tokenize.OP, tokenize.NAME) brace_stack = [] for token_type, text, start, end, line in tokens: if token_type == tokenize.OP and text in {'[', '(', '{'}: brace_stack.append(text) elif token_type == FSTRING_START: # pragma: >=3.12 cover brace_stack.append('f') elif token_type == tokenize.NAME and text == 'lambda': brace_stack.append('l') elif brace_stack: if token_type == tokenize.OP and text in {']', ')', '}'}: brace_stack.pop() elif token_type == FSTRING_END: # pragma: >=3.12 cover brace_stack.pop() elif ( brace_stack[-1] == 'l' and token_type == tokenize.OP and text == ':' ): brace_stack.pop() if token_type in SKIP_COMMENTS: continue if token_type == tokenize.OP and text in {',', ';', ':'}: next_char = line[end[1]:end[1] + 1] if next_char not in WHITESPACE and next_char not in '\r\n': # slice if text == ':' and brace_stack[-1:] == ['[']: pass # 3.12+ fstring format specifier elif text == ':' and brace_stack[-2:] == ['f', '{']: # pragma: >=3.12 cover # noqa: E501 pass # tuple (and list for some reason?) elif text == ',' and next_char in ')]': pass else: yield start, f'E231 missing whitespace after {text!r}' if need_space: if start != prev_end: # Found a (probably) needed space if need_space is not True and not need_space[1]: yield (need_space[0], "E225 missing whitespace around operator") need_space = False elif ( # def f(a, /, b): # ^ # def f(a, b, /): # ^ # f = lambda a, /: # ^ prev_text == '/' and text in {',', ')', ':'} or # def f(a, b, /): # ^ prev_text == ')' and text == ':' ): # Tolerate the "/" operator in function definition # For more info see PEP570 pass else: if need_space is True or need_space[1]: # A needed trailing space was not found yield prev_end, "E225 missing whitespace around operator" elif prev_text != '**': code, optype = 'E226', 'arithmetic' if prev_text == '%': code, optype = 'E228', 'modulo' elif prev_text not in ARITHMETIC_OP: code, optype = 'E227', 'bitwise or shift' yield (need_space[0], "%s missing whitespace " "around %s operator" % (code, optype)) need_space = False elif token_type in operator_types and prev_end is not None: if ( text == '=' and ( # allow lambda default args: lambda x=None: None brace_stack[-1:] == ['l'] or # allow keyword args or defaults: foo(bar=None). brace_stack[-1:] == ['('] or # allow python 3.8 fstring repr specifier brace_stack[-2:] == ['f', '{'] ) ): pass elif text in WS_NEEDED_OPERATORS: need_space = True elif text in UNARY_OPERATORS: # Check if the operator is used as a binary operator # Allow unary operators: -123, -x, +1. # Allow argument unpacking: foo(*args, **kwargs). if prev_type == tokenize.OP and prev_text in '}])' or ( prev_type != tokenize.OP and prev_text not in KEYWORDS and ( sys.version_info < (3, 9) or not keyword.issoftkeyword(prev_text) ) ): need_space = None elif text in WS_OPTIONAL_OPERATORS: need_space = None if need_space is None: # Surrounding space is optional, but ensure that # trailing space matches opening space need_space = (prev_end, start != prev_end) elif need_space and start == prev_end: # A needed opening space was not found yield prev_end, "E225 missing whitespace around operator" need_space = False prev_type = token_type prev_text = text prev_end = end @register_check def whitespace_around_comma(logical_line): r"""Avoid extraneous whitespace after a comma or a colon. Note: these checks are disabled by default Okay: a = (1, 2) E241: a = (1, 2) E242: a = (1,\t2) """ line = logical_line for m in WHITESPACE_AFTER_COMMA_REGEX.finditer(line): found = m.start() + 1 if '\t' in m.group(): yield found, "E242 tab after '%s'" % m.group()[0] else: yield found, "E241 multiple spaces after '%s'" % m.group()[0] @register_check def whitespace_around_named_parameter_equals(logical_line, tokens): r"""Don't use spaces around the '=' sign in function arguments. Don't use spaces around the '=' sign when used to indicate a keyword argument or a default parameter value, except when using a type annotation. Okay: def complex(real, imag=0.0): Okay: return magic(r=real, i=imag) Okay: boolean(a == b) Okay: boolean(a != b) Okay: boolean(a <= b) Okay: boolean(a >= b) Okay: def foo(arg: int = 42): Okay: async def foo(arg: int = 42): E251: def complex(real, imag = 0.0): E251: return magic(r = real, i = imag) E252: def complex(real, image: float=0.0): """ parens = 0 no_space = False require_space = False prev_end = None annotated_func_arg = False in_def = bool(STARTSWITH_DEF_REGEX.match(logical_line)) message = "E251 unexpected spaces around keyword / parameter equals" missing_message = "E252 missing whitespace around parameter equals" for token_type, text, start, end, line in tokens: if token_type == tokenize.NL: continue if no_space: no_space = False if start != prev_end: yield (prev_end, message) if require_space: require_space = False if start == prev_end: yield (prev_end, missing_message) if token_type == tokenize.OP: if text in '([': parens += 1 elif text in ')]': parens -= 1 elif in_def and text == ':' and parens == 1: annotated_func_arg = True elif parens == 1 and text == ',': annotated_func_arg = False elif parens and text == '=': if annotated_func_arg and parens == 1: require_space = True if start == prev_end: yield (prev_end, missing_message) else: no_space = True if start != prev_end: yield (prev_end, message) if not parens: annotated_func_arg = False prev_end = end @register_check def whitespace_before_comment(logical_line, tokens): """Separate inline comments by at least two spaces. An inline comment is a comment on the same line as a statement. Inline comments should be separated by at least two spaces from the statement. They should start with a # and a single space. Each line of a block comment starts with a # and one or multiple spaces as there can be indented text inside the comment. Okay: x = x + 1 # Increment x Okay: x = x + 1 # Increment x Okay: # Block comments: Okay: # - Block comment list Okay: # \xa0- Block comment list E261: x = x + 1 # Increment x E262: x = x + 1 #Increment x E262: x = x + 1 # Increment x E262: x = x + 1 # \xa0Increment x E265: #Block comment E266: ### Block comment """ prev_end = (0, 0) for token_type, text, start, end, line in tokens: if token_type == tokenize.COMMENT: inline_comment = line[:start[1]].strip() if inline_comment: if prev_end[0] == start[0] and start[1] < prev_end[1] + 2: yield (prev_end, "E261 at least two spaces before inline comment") symbol, sp, comment = text.partition(' ') bad_prefix = symbol not in '#:' and (symbol.lstrip('#')[:1] or '#') if inline_comment: if bad_prefix or comment[:1] in WHITESPACE: yield start, "E262 inline comment should start with '# '" elif bad_prefix and (bad_prefix != '!' or start[0] > 1): if bad_prefix != '#': yield start, "E265 block comment should start with '# '" elif comment: yield start, "E266 too many leading '#' for block comment" elif token_type != tokenize.NL: prev_end = end @register_check def imports_on_separate_lines(logical_line): r"""Place imports on separate lines. Okay: import os\nimport sys E401: import sys, os Okay: from subprocess import Popen, PIPE Okay: from myclas import MyClass Okay: from foo.bar.yourclass import YourClass Okay: import myclass Okay: import foo.bar.yourclass """ line = logical_line if line.startswith('import '): found = line.find(',') if -1 < found and ';' not in line[:found]: yield found, "E401 multiple imports on one line" @register_check def module_imports_on_top_of_file( logical_line, indent_level, checker_state, noqa): r"""Place imports at the top of the file. Always put imports at the top of the file, just after any module comments and docstrings, and before module globals and constants. Okay: import os Okay: # this is a comment\nimport os Okay: '''this is a module docstring'''\nimport os Okay: r'''this is a module docstring'''\nimport os E402: a=1\nimport os E402: 'One string'\n"Two string"\nimport os E402: a=1\nfrom sys import x Okay: if x:\n import os """ # noqa def is_string_literal(line): if line[0] in 'uUbB': line = line[1:] if line and line[0] in 'rR': line = line[1:] return line and (line[0] == '"' or line[0] == "'") allowed_keywords = ( 'try', 'except', 'else', 'finally', 'with', 'if', 'elif') if indent_level: # Allow imports in conditional statement/function return if not logical_line: # Allow empty lines or comments return if noqa: return line = logical_line if line.startswith('import ') or line.startswith('from '): if checker_state.get('seen_non_imports', False): yield 0, "E402 module level import not at top of file" elif re.match(DUNDER_REGEX, line): return elif any(line.startswith(kw) for kw in allowed_keywords): # Allow certain keywords intermixed with imports in order to # support conditional or filtered importing return elif is_string_literal(line): # The first literal is a docstring, allow it. Otherwise, report # error. if checker_state.get('seen_docstring', False): checker_state['seen_non_imports'] = True else: checker_state['seen_docstring'] = True else: checker_state['seen_non_imports'] = True @register_check def compound_statements(logical_line): r"""Compound statements (on the same line) are generally discouraged. While sometimes it's okay to put an if/for/while with a small body on the same line, never do this for multi-clause statements. Also avoid folding such long lines! Always use a def statement instead of an assignment statement that binds a lambda expression directly to a name. Okay: if foo == 'blah':\n do_blah_thing() Okay: do_one() Okay: do_two() Okay: do_three() E701: if foo == 'blah': do_blah_thing() E701: for x in lst: total += x E701: while t < 10: t = delay() E701: if foo == 'blah': do_blah_thing() E701: else: do_non_blah_thing() E701: try: something() E701: finally: cleanup() E701: if foo == 'blah': one(); two(); three() E702: do_one(); do_two(); do_three() E703: do_four(); # useless semicolon E704: def f(x): return 2*x E731: f = lambda x: 2*x """ line = logical_line last_char = len(line) - 1 found = line.find(':') prev_found = 0 counts = {char: 0 for char in '{}[]()'} while -1 < found < last_char: update_counts(line[prev_found:found], counts) if ( counts['{'] <= counts['}'] and # {'a': 1} (dict) counts['['] <= counts[']'] and # [1:2] (slice) counts['('] <= counts[')'] and # (annotation) line[found + 1] != '=' # assignment expression ): lambda_kw = LAMBDA_REGEX.search(line, 0, found) if lambda_kw: before = line[:lambda_kw.start()].rstrip() if before[-1:] == '=' and before[:-1].strip().isidentifier(): yield 0, ("E731 do not assign a lambda expression, use a " "def") break if STARTSWITH_DEF_REGEX.match(line): yield 0, "E704 multiple statements on one line (def)" elif STARTSWITH_INDENT_STATEMENT_REGEX.match(line): yield found, "E701 multiple statements on one line (colon)" prev_found = found found = line.find(':', found + 1) found = line.find(';') while -1 < found: if found < last_char: yield found, "E702 multiple statements on one line (semicolon)" else: yield found, "E703 statement ends with a semicolon" found = line.find(';', found + 1) @register_check def explicit_line_join(logical_line, tokens): r"""Avoid explicit line join between brackets. The preferred way of wrapping long lines is by using Python's implied line continuation inside parentheses, brackets and braces. Long lines can be broken over multiple lines by wrapping expressions in parentheses. These should be used in preference to using a backslash for line continuation. E502: aaa = [123, \\n 123] E502: aaa = ("bbb " \\n "ccc") Okay: aaa = [123,\n 123] Okay: aaa = ("bbb "\n "ccc") Okay: aaa = "bbb " \\n "ccc" Okay: aaa = 123 # \\ """ prev_start = prev_end = parens = 0 comment = False backslash = None for token_type, text, start, end, line in tokens: if token_type == tokenize.COMMENT: comment = True if start[0] != prev_start and parens and backslash and not comment: yield backslash, "E502 the backslash is redundant between brackets" if start[0] != prev_start: comment = False # Reset comment flag on newline if end[0] != prev_end: if line.rstrip('\r\n').endswith('\\'): backslash = (end[0], len(line.splitlines()[-1]) - 1) else: backslash = None prev_start = prev_end = end[0] else: prev_start = start[0] if token_type == tokenize.OP: if text in '([{': parens += 1 elif text in ')]}': parens -= 1 # The % character is strictly speaking a binary operator, but the # common usage seems to be to put it next to the format parameters, # after a line break. _SYMBOLIC_OPS = frozenset("()[]{},:.;@=%~") | frozenset(("...",)) def _is_binary_operator(token_type, text): return ( token_type == tokenize.OP or text in {'and', 'or'} ) and ( text not in _SYMBOLIC_OPS ) def _break_around_binary_operators(tokens): """Private function to reduce duplication. This factors out the shared details between :func:`break_before_binary_operator` and :func:`break_after_binary_operator`. """ line_break = False unary_context = True # Previous non-newline token types and text previous_token_type = None previous_text = None for token_type, text, start, end, line in tokens: if token_type == tokenize.COMMENT: continue if ('\n' in text or '\r' in text) and token_type != tokenize.STRING: line_break = True else: yield (token_type, text, previous_token_type, previous_text, line_break, unary_context, start) unary_context = text in '([{,;' line_break = False previous_token_type = token_type previous_text = text @register_check def break_before_binary_operator(logical_line, tokens): r""" Avoid breaks before binary operators. The preferred place to break around a binary operator is after the operator, not before it. W503: (width == 0\n + height == 0) W503: (width == 0\n and height == 0) W503: var = (1\n & ~2) W503: var = (1\n / -2) W503: var = (1\n + -1\n + -2) Okay: foo(\n -x) Okay: foo(x\n []) Okay: x = '''\n''' + '' Okay: foo(x,\n -y) Okay: foo(x, # comment\n -y) """ for context in _break_around_binary_operators(tokens): (token_type, text, previous_token_type, previous_text, line_break, unary_context, start) = context if (_is_binary_operator(token_type, text) and line_break and not unary_context and not _is_binary_operator(previous_token_type, previous_text)): yield start, "W503 line break before binary operator" @register_check def break_after_binary_operator(logical_line, tokens): r""" Avoid breaks after binary operators. The preferred place to break around a binary operator is before the operator, not after it. W504: (width == 0 +\n height == 0) W504: (width == 0 and\n height == 0) W504: var = (1 &\n ~2) Okay: foo(\n -x) Okay: foo(x\n []) Okay: x = '''\n''' + '' Okay: x = '' + '''\n''' Okay: foo(x,\n -y) Okay: foo(x, # comment\n -y) The following should be W504 but unary_context is tricky with these Okay: var = (1 /\n -2) Okay: var = (1 +\n -1 +\n -2) """ prev_start = None for context in _break_around_binary_operators(tokens): (token_type, text, previous_token_type, previous_text, line_break, unary_context, start) = context if (_is_binary_operator(previous_token_type, previous_text) and line_break and not unary_context and not _is_binary_operator(token_type, text)): yield prev_start, "W504 line break after binary operator" prev_start = start @register_check def comparison_to_singleton(logical_line, noqa): r"""Comparison to singletons should use "is" or "is not". Comparisons to singletons like None should always be done with "is" or "is not", never the equality operators. Okay: if arg is not None: E711: if arg != None: E711: if None == arg: E712: if arg == True: E712: if False == arg: Also, beware of writing if x when you really mean if x is not None -- e.g. when testing whether a variable or argument that defaults to None was set to some other value. The other value might have a type (such as a container) that could be false in a boolean context! """ if noqa: return for match in COMPARE_SINGLETON_REGEX.finditer(logical_line): singleton = match.group(1) or match.group(3) same = (match.group(2) == '==') msg = "'if cond is %s:'" % (('' if same else 'not ') + singleton) if singleton in ('None',): code = 'E711' else: code = 'E712' nonzero = ((singleton == 'True' and same) or (singleton == 'False' and not same)) msg += " or 'if %scond:'" % ('' if nonzero else 'not ') yield match.start(2), ("%s comparison to %s should be %s" % (code, singleton, msg)) @register_check def comparison_negative(logical_line): r"""Negative comparison should be done using "not in" and "is not". Okay: if x not in y:\n pass Okay: assert (X in Y or X is Z) Okay: if not (X in Y):\n pass Okay: zz = x is not y E713: Z = not X in Y E713: if not X.B in Y:\n pass E714: if not X is Y:\n pass E714: Z = not X.B is Y """ match = COMPARE_NEGATIVE_REGEX.search(logical_line) if match: pos = match.start(1) if match.group(2) == 'in': yield pos, "E713 test for membership should be 'not in'" else: yield pos, "E714 test for object identity should be 'is not'" @register_check def comparison_type(logical_line, noqa): r"""Object type comparisons should `is` / `is not` / `isinstance()`. Do not compare types directly. Okay: if isinstance(obj, int): Okay: if type(obj) is int: E721: if type(obj) == type(1): """ match = COMPARE_TYPE_REGEX.search(logical_line) if match and not noqa: inst = match.group(1) if inst and inst.isidentifier() and inst not in SINGLETONS: return # Allow comparison for types which are not obvious yield ( match.start(), "E721 do not compare types, for exact checks use `is` / `is not`, " "for instance checks use `isinstance()`", ) @register_check def bare_except(logical_line, noqa): r"""When catching exceptions, mention specific exceptions when possible. Okay: except Exception: Okay: except BaseException: E722: except: """ if noqa: return match = BLANK_EXCEPT_REGEX.match(logical_line) if match: yield match.start(), "E722 do not use bare 'except'" @register_check def ambiguous_identifier(logical_line, tokens): r"""Never use the characters 'l', 'O', or 'I' as variable names. In some fonts, these characters are indistinguishable from the numerals one and zero. When tempted to use 'l', use 'L' instead. Okay: L = 0 Okay: o = 123 Okay: i = 42 E741: l = 0 E741: O = 123 E741: I = 42 Variables can be bound in several other contexts, including class and function definitions, lambda functions, 'global' and 'nonlocal' statements, exception handlers, and 'with' and 'for' statements. In addition, we have a special handling for function parameters. Okay: except AttributeError as o: Okay: with lock as L: Okay: foo(l=12) Okay: foo(l=I) Okay: for a in foo(l=12): Okay: lambda arg: arg * l Okay: lambda a=l[I:5]: None Okay: lambda x=a.I: None Okay: if l >= 12: E741: except AttributeError as O: E741: with lock as l: E741: global I E741: nonlocal l E741: def foo(l): E741: def foo(l=12): E741: l = foo(l=12) E741: for l in range(10): E741: [l for l in lines if l] E741: lambda l: None E741: lambda a=x[1:5], l: None E741: lambda **l: E741: def f(**l): E742: class I(object): E743: def l(x): """ func_depth = None # set to brace depth if 'def' or 'lambda' is found seen_colon = False # set to true if we're done with function parameters brace_depth = 0 idents_to_avoid = ('l', 'O', 'I') prev_type, prev_text, prev_start, prev_end, __ = tokens[0] for index in range(1, len(tokens)): token_type, text, start, end, line = tokens[index] ident = pos = None # find function definitions if prev_text in {'def', 'lambda'}: func_depth = brace_depth seen_colon = False elif ( func_depth is not None and text == ':' and brace_depth == func_depth ): seen_colon = True # update parameter parentheses level if text in '([{': brace_depth += 1 elif text in ')]}': brace_depth -= 1 # identifiers on the lhs of an assignment operator if text == ':=' or (text == '=' and brace_depth == 0): if prev_text in idents_to_avoid: ident = prev_text pos = prev_start # identifiers bound to values with 'as', 'for', # 'global', or 'nonlocal' if prev_text in ('as', 'for', 'global', 'nonlocal'): if text in idents_to_avoid: ident = text pos = start # function / lambda parameter definitions if ( func_depth is not None and not seen_colon and index < len(tokens) - 1 and tokens[index + 1][1] in ':,=)' and prev_text in {'lambda', ',', '*', '**', '('} and text in idents_to_avoid ): ident = text pos = start if prev_text == 'class': if text in idents_to_avoid: yield start, "E742 ambiguous class definition '%s'" % text if prev_text == 'def': if text in idents_to_avoid: yield start, "E743 ambiguous function definition '%s'" % text if ident: yield pos, "E741 ambiguous variable name '%s'" % ident prev_text = text prev_start = start @register_check def python_3000_invalid_escape_sequence(logical_line, tokens, noqa): r"""Invalid escape sequences are deprecated in Python 3.6. Okay: regex = r'\.png$' W605: regex = '\.png$' """ if noqa: return # https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals valid = [ '\n', '\\', '\'', '"', 'a', 'b', 'f', 'n', 'r', 't', 'v', '0', '1', '2', '3', '4', '5', '6', '7', 'x', # Escape sequences only recognized in string literals 'N', 'u', 'U', ] prefixes = [] for token_type, text, start, _, _ in tokens: if token_type in {tokenize.STRING, FSTRING_START}: # Extract string modifiers (e.g. u or r) prefixes.append(text[:text.index(text[-1])].lower()) if token_type in {tokenize.STRING, FSTRING_MIDDLE}: if 'r' not in prefixes[-1]: start_line, start_col = start pos = text.find('\\') while pos >= 0: pos += 1 if text[pos] not in valid: line = start_line + text.count('\n', 0, pos) if line == start_line: col = start_col + pos else: col = pos - text.rfind('\n', 0, pos) - 1 yield ( (line, col - 1), f"W605 invalid escape sequence '\\{text[pos]}'" ) pos = text.find('\\', pos + 1) if token_type in {tokenize.STRING, FSTRING_END}: prefixes.pop() ######################################################################## @register_check def maximum_doc_length(logical_line, max_doc_length, noqa, tokens): r"""Limit all doc lines to a maximum of 72 characters. For flowing long blocks of text (docstrings or comments), limiting the length to 72 characters is recommended. Reports warning W505 """ if max_doc_length is None or noqa: return prev_token = None skip_lines = set() # Skip lines that for token_type, text, start, end, line in tokens: if token_type not in SKIP_COMMENTS.union([tokenize.STRING]): skip_lines.add(line) for token_type, text, start, end, line in tokens: # Skip lines that aren't pure strings if token_type == tokenize.STRING and skip_lines: continue if token_type in (tokenize.STRING, tokenize.COMMENT): # Only check comment-only lines if prev_token is None or prev_token in SKIP_TOKENS: lines = line.splitlines() for line_num, physical_line in enumerate(lines): if start[0] + line_num == 1 and line.startswith('#!'): return length = len(physical_line) chunks = physical_line.split() if token_type == tokenize.COMMENT: if (len(chunks) == 2 and length - len(chunks[-1]) < MAX_DOC_LENGTH): continue if len(chunks) == 1 and line_num + 1 < len(lines): if (len(chunks) == 1 and length - len(chunks[-1]) < MAX_DOC_LENGTH): continue if length > max_doc_length: doc_error = (start[0] + line_num, max_doc_length) yield (doc_error, "W505 doc line too long " "(%d > %d characters)" % (length, max_doc_length)) prev_token = token_type ######################################################################## # Helper functions ######################################################################## def readlines(filename): """Read the source code.""" try: with tokenize.open(filename) as f: return f.readlines() except (LookupError, SyntaxError, UnicodeError): # Fall back if file encoding is improperly declared with open(filename, encoding='latin-1') as f: return f.readlines() def stdin_get_value(): """Read the value from stdin.""" return io.TextIOWrapper(sys.stdin.buffer, errors='ignore').read() noqa = lru_cache(512)(re.compile(r'# no(?:qa|pep8)\b', re.I).search) def expand_indent(line): r"""Return the amount of indentation. Tabs are expanded to the next multiple of 8. """ line = line.rstrip('\n\r') if '\t' not in line: return len(line) - len(line.lstrip()) result = 0 for char in line: if char == '\t': result = result // 8 * 8 + 8 elif char == ' ': result += 1 else: break return result def mute_string(text): """Replace contents with 'xxx' to prevent syntax matching.""" # String modifiers (e.g. u or r) start = text.index(text[-1]) + 1 end = len(text) - 1 # Triple quotes if text[-3:] in ('"""', "'''"): start += 2 end -= 2 return text[:start] + 'x' * (end - start) + text[end:] def parse_udiff(diff, patterns=None, parent='.'): """Return a dictionary of matching lines.""" # For each file of the diff, the entry key is the filename, # and the value is a set of row numbers to consider. rv = {} path = nrows = None for line in diff.splitlines(): if nrows: if line[:1] != '-': nrows -= 1 continue if line[:3] == '@@ ': hunk_match = HUNK_REGEX.match(line) (row, nrows) = (int(g or '1') for g in hunk_match.groups()) rv[path].update(range(row, row + nrows)) elif line[:3] == '+++': path = line[4:].split('\t', 1)[0] # Git diff will use (i)ndex, (w)ork tree, (c)ommit and # (o)bject instead of a/b/c/d as prefixes for patches if path[:2] in ('b/', 'w/', 'i/'): path = path[2:] rv[path] = set() return { os.path.join(parent, filepath): rows for (filepath, rows) in rv.items() if rows and filename_match(filepath, patterns) } def normalize_paths(value, parent=os.curdir): """Parse a comma-separated list of paths. Return a list of absolute paths. """ if not value: return [] if isinstance(value, list): return value paths = [] for path in value.split(','): path = path.strip() if '/' in path: path = os.path.abspath(os.path.join(parent, path)) paths.append(path.rstrip('/')) return paths def filename_match(filename, patterns, default=True): """Check if patterns contains a pattern that matches filename. If patterns is unspecified, this always returns True. """ if not patterns: return default return any(fnmatch(filename, pattern) for pattern in patterns) def update_counts(s, counts): r"""Adds one to the counts of each appearance of characters in s, for characters in counts""" for char in s: if char in counts: counts[char] += 1 def _is_eol_token(token): return token[0] in NEWLINE or token[4][token[3][1]:].lstrip() == '\\\n' ######################################################################## # Framework to run all checks ######################################################################## class Checker: """Load a Python source file, tokenize it, check coding style.""" def __init__(self, filename=None, lines=None, options=None, report=None, **kwargs): if options is None: options = StyleGuide(kwargs).options else: assert not kwargs self._io_error = None self._physical_checks = options.physical_checks self._logical_checks = options.logical_checks self._ast_checks = options.ast_checks self.max_line_length = options.max_line_length self.max_doc_length = options.max_doc_length self.indent_size = options.indent_size self.fstring_start = 0 self.multiline = False # in a multiline string? self.hang_closing = options.hang_closing self.indent_size = options.indent_size self.verbose = options.verbose self.filename = filename # Dictionary where a checker can store its custom state. self._checker_states = {} if filename is None: self.filename = 'stdin' self.lines = lines or [] elif filename == '-': self.filename = 'stdin' self.lines = stdin_get_value().splitlines(True) elif lines is None: try: self.lines = readlines(filename) except OSError: (exc_type, exc) = sys.exc_info()[:2] self._io_error = f'{exc_type.__name__}: {exc}' self.lines = [] else: self.lines = lines if self.lines: ord0 = ord(self.lines[0][0]) if ord0 in (0xef, 0xfeff): # Strip the UTF-8 BOM if ord0 == 0xfeff: self.lines[0] = self.lines[0][1:] elif self.lines[0][:3] == '\xef\xbb\xbf': self.lines[0] = self.lines[0][3:] self.report = report or options.report self.report_error = self.report.error self.noqa = False def report_invalid_syntax(self): """Check if the syntax is valid.""" (exc_type, exc) = sys.exc_info()[:2] if len(exc.args) > 1: offset = exc.args[1] if len(offset) > 2: offset = offset[1:3] else: offset = (1, 0) self.report_error(offset[0], offset[1] or 0, f'E901 {exc_type.__name__}: {exc.args[0]}', self.report_invalid_syntax) def readline(self): """Get the next line from the input buffer.""" if self.line_number >= self.total_lines: return '' line = self.lines[self.line_number] self.line_number += 1 if self.indent_char is None and line[:1] in WHITESPACE: self.indent_char = line[0] return line def run_check(self, check, argument_names): """Run a check plugin.""" arguments = [] for name in argument_names: arguments.append(getattr(self, name)) return check(*arguments) def init_checker_state(self, name, argument_names): """Prepare custom state for the specific checker plugin.""" if 'checker_state' in argument_names: self.checker_state = self._checker_states.setdefault(name, {}) def check_physical(self, line): """Run all physical checks on a raw input line.""" self.physical_line = line for name, check, argument_names in self._physical_checks: self.init_checker_state(name, argument_names) result = self.run_check(check, argument_names) if result is not None: (offset, text) = result self.report_error(self.line_number, offset, text, check) if text[:4] == 'E101': self.indent_char = line[0] def build_tokens_line(self): """Build a logical line from tokens.""" logical = [] comments = [] length = 0 prev_row = prev_col = mapping = None for token_type, text, start, end, line in self.tokens: if token_type in SKIP_TOKENS: continue if not mapping: mapping = [(0, start)] if token_type == tokenize.COMMENT: comments.append(text) continue if token_type == tokenize.STRING: text = mute_string(text) elif token_type == FSTRING_MIDDLE: # pragma: >=3.12 cover # fstring tokens are "unescaped" braces -- re-escape! brace_count = text.count('{') + text.count('}') text = 'x' * (len(text) + brace_count) end = (end[0], end[1] + brace_count) if prev_row: (start_row, start_col) = start if prev_row != start_row: # different row prev_text = self.lines[prev_row - 1][prev_col - 1] if prev_text == ',' or (prev_text not in '{[(' and text not in '}])'): text = ' ' + text elif prev_col != start_col: # different column text = line[prev_col:start_col] + text logical.append(text) length += len(text) mapping.append((length, end)) (prev_row, prev_col) = end self.logical_line = ''.join(logical) self.noqa = comments and noqa(''.join(comments)) return mapping def check_logical(self): """Build a line from tokens and run all logical checks on it.""" self.report.increment_logical_line() mapping = self.build_tokens_line() if not mapping: return mapping_offsets = [offset for offset, _ in mapping] (start_row, start_col) = mapping[0][1] start_line = self.lines[start_row - 1] self.indent_level = expand_indent(start_line[:start_col]) if self.blank_before < self.blank_lines: self.blank_before = self.blank_lines if self.verbose >= 2: print(self.logical_line[:80].rstrip()) for name, check, argument_names in self._logical_checks: if self.verbose >= 4: print(' ' + name) self.init_checker_state(name, argument_names) for offset, text in self.run_check(check, argument_names) or (): if not isinstance(offset, tuple): # As mappings are ordered, bisecting is a fast way # to find a given offset in them. token_offset, pos = mapping[bisect.bisect_left( mapping_offsets, offset)] offset = (pos[0], pos[1] + offset - token_offset) self.report_error(offset[0], offset[1], text, check) if self.logical_line: self.previous_indent_level = self.indent_level self.previous_logical = self.logical_line if not self.indent_level: self.previous_unindented_logical_line = self.logical_line self.blank_lines = 0 self.tokens = [] def check_ast(self): """Build the file's AST and run all AST checks.""" try: tree = compile(''.join(self.lines), '', 'exec', PyCF_ONLY_AST) except (ValueError, SyntaxError, TypeError): return self.report_invalid_syntax() for name, cls, __ in self._ast_checks: checker = cls(tree, self.filename) for lineno, offset, text, check in checker.run(): if not self.lines or not noqa(self.lines[lineno - 1]): self.report_error(lineno, offset, text, check) def generate_tokens(self): """Tokenize file, run physical line checks and yield tokens.""" if self._io_error: self.report_error(1, 0, 'E902 %s' % self._io_error, readlines) tokengen = tokenize.generate_tokens(self.readline) try: prev_physical = '' for token in tokengen: if token[2][0] > self.total_lines: return self.noqa = token[4] and noqa(token[4]) self.maybe_check_physical(token, prev_physical) yield token prev_physical = token[4] except (SyntaxError, tokenize.TokenError): self.report_invalid_syntax() def maybe_check_physical(self, token, prev_physical): """If appropriate for token, check current physical line(s).""" # Called after every token, but act only on end of line. if token.type == FSTRING_START: # pragma: >=3.12 cover self.fstring_start = token.start[0] # a newline token ends a single physical line. elif _is_eol_token(token): # if the file does not end with a newline, the NEWLINE # token is inserted by the parser, but it does not contain # the previous physical line in `token[4]` if token.line == '': self.check_physical(prev_physical) else: self.check_physical(token.line) elif ( token.type == tokenize.STRING and '\n' in token.string or token.type == FSTRING_END ): # Less obviously, a string that contains newlines is a # multiline string, either triple-quoted or with internal # newlines backslash-escaped. Check every physical line in # the string *except* for the last one: its newline is # outside of the multiline string, so we consider it a # regular physical line, and will check it like any other # physical line. # # Subtleties: # - we don't *completely* ignore the last line; if it # contains the magical "# noqa" comment, we disable all # physical checks for the entire multiline string # - have to wind self.line_number back because initially it # points to the last line of the string, and we want # check_physical() to give accurate feedback if noqa(token.line): return if token.type == FSTRING_END: # pragma: >=3.12 cover start = self.fstring_start else: start = token.start[0] end = token.end[0] self.multiline = True self.line_number = start for line_number in range(start, end): self.check_physical(self.lines[line_number - 1] + '\n') self.line_number += 1 self.multiline = False def check_all(self, expected=None, line_offset=0): """Run all checks on the input file.""" self.report.init_file(self.filename, self.lines, expected, line_offset) self.total_lines = len(self.lines) if self._ast_checks: self.check_ast() self.line_number = 0 self.indent_char = None self.indent_level = self.previous_indent_level = 0 self.previous_logical = '' self.previous_unindented_logical_line = '' self.tokens = [] self.blank_lines = self.blank_before = 0 parens = 0 for token in self.generate_tokens(): self.tokens.append(token) token_type, text = token[0:2] if self.verbose >= 3: if token[2][0] == token[3][0]: pos = '[{}:{}]'.format(token[2][1] or '', token[3][1]) else: pos = 'l.%s' % token[3][0] print('l.%s\t%s\t%s\t%r' % (token[2][0], pos, tokenize.tok_name[token[0]], text)) if token_type == tokenize.OP: if text in '([{': parens += 1 elif text in '}])': parens -= 1 elif not parens: if token_type in NEWLINE: if token_type == tokenize.NEWLINE: self.check_logical() self.blank_before = 0 elif len(self.tokens) == 1: # The physical line contains only this token. self.blank_lines += 1 del self.tokens[0] else: self.check_logical() if self.tokens: self.check_physical(self.lines[-1]) self.check_logical() return self.report.get_file_results() class BaseReport: """Collect the results of the checks.""" print_filename = False def __init__(self, options): self._benchmark_keys = options.benchmark_keys self._ignore_code = options.ignore_code # Results self.elapsed = 0 self.total_errors = 0 self.counters = dict.fromkeys(self._benchmark_keys, 0) self.messages = {} def start(self): """Start the timer.""" self._start_time = time.time() def stop(self): """Stop the timer.""" self.elapsed = time.time() - self._start_time def init_file(self, filename, lines, expected, line_offset): """Signal a new file.""" self.filename = filename self.lines = lines self.expected = expected or () self.line_offset = line_offset self.file_errors = 0 self.counters['files'] += 1 self.counters['physical lines'] += len(lines) def increment_logical_line(self): """Signal a new logical line.""" self.counters['logical lines'] += 1 def error(self, line_number, offset, text, check): """Report an error, according to options.""" code = text[:4] if self._ignore_code(code): return if code in self.counters: self.counters[code] += 1 else: self.counters[code] = 1 self.messages[code] = text[5:] # Don't care about expected errors or warnings if code in self.expected: return if self.print_filename and not self.file_errors: print(self.filename) self.file_errors += 1 self.total_errors += 1 return code def get_file_results(self): """Return the count of errors and warnings for this file.""" return self.file_errors def get_count(self, prefix=''): """Return the total count of errors and warnings.""" return sum(self.counters[key] for key in self.messages if key.startswith(prefix)) def get_statistics(self, prefix=''): """Get statistics for message codes that start with the prefix. prefix='' matches all errors and warnings prefix='E' matches all errors prefix='W' matches all warnings prefix='E4' matches all errors that have to do with imports """ return ['%-7s %s %s' % (self.counters[key], key, self.messages[key]) for key in sorted(self.messages) if key.startswith(prefix)] def print_statistics(self, prefix=''): """Print overall statistics (number of errors and warnings).""" for line in self.get_statistics(prefix): print(line) def print_benchmark(self): """Print benchmark numbers.""" print('{:<7.2f} {}'.format(self.elapsed, 'seconds elapsed')) if self.elapsed: for key in self._benchmark_keys: print('%-7d %s per second (%d total)' % (self.counters[key] / self.elapsed, key, self.counters[key])) class FileReport(BaseReport): """Collect the results of the checks and print the filenames.""" print_filename = True class StandardReport(BaseReport): """Collect and print the results of the checks.""" def __init__(self, options): super().__init__(options) self._fmt = REPORT_FORMAT.get(options.format.lower(), options.format) self._repeat = options.repeat self._show_source = options.show_source self._show_pep8 = options.show_pep8 def init_file(self, filename, lines, expected, line_offset): """Signal a new file.""" self._deferred_print = [] return super().init_file( filename, lines, expected, line_offset) def error(self, line_number, offset, text, check): """Report an error, according to options.""" code = super().error(line_number, offset, text, check) if code and (self.counters[code] == 1 or self._repeat): self._deferred_print.append( (line_number, offset, code, text[5:], check.__doc__)) return code def get_file_results(self): """Print results and return the overall count for this file.""" self._deferred_print.sort() for line_number, offset, code, text, doc in self._deferred_print: print(self._fmt % { 'path': self.filename, 'row': self.line_offset + line_number, 'col': offset + 1, 'code': code, 'text': text, }) if self._show_source: if line_number > len(self.lines): line = '' else: line = self.lines[line_number - 1] print(line.rstrip()) print(re.sub(r'\S', ' ', line[:offset]) + '^') if self._show_pep8 and doc: print(' ' + doc.strip()) # stdout is block buffered when not stdout.isatty(). # line can be broken where buffer boundary since other # processes write to same file. # flush() after print() to avoid buffer boundary. # Typical buffer size is 8192. line written safely when # len(line) < 8192. sys.stdout.flush() return self.file_errors class DiffReport(StandardReport): """Collect and print the results for the changed lines only.""" def __init__(self, options): super().__init__(options) self._selected = options.selected_lines def error(self, line_number, offset, text, check): if line_number not in self._selected[self.filename]: return return super().error(line_number, offset, text, check) class StyleGuide: """Initialize a PEP-8 instance with few options.""" def __init__(self, *args, **kwargs): # build options from the command line self.checker_class = kwargs.pop('checker_class', Checker) parse_argv = kwargs.pop('parse_argv', False) config_file = kwargs.pop('config_file', False) parser = kwargs.pop('parser', None) # build options from dict options_dict = dict(*args, **kwargs) arglist = None if parse_argv else options_dict.get('paths', None) verbose = options_dict.get('verbose', None) options, self.paths = process_options( arglist, parse_argv, config_file, parser, verbose) if options_dict: options.__dict__.update(options_dict) if 'paths' in options_dict: self.paths = options_dict['paths'] self.runner = self.input_file self.options = options if not options.reporter: options.reporter = BaseReport if options.quiet else StandardReport options.select = tuple(options.select or ()) if not (options.select or options.ignore) and DEFAULT_IGNORE: # The default choice: ignore controversial checks options.ignore = tuple(DEFAULT_IGNORE.split(',')) else: # Ignore all checks which are not explicitly selected options.ignore = ('',) if options.select else tuple(options.ignore) options.benchmark_keys = BENCHMARK_KEYS[:] options.ignore_code = self.ignore_code options.physical_checks = self.get_checks('physical_line') options.logical_checks = self.get_checks('logical_line') options.ast_checks = self.get_checks('tree') self.init_report() def init_report(self, reporter=None): """Initialize the report instance.""" self.options.report = (reporter or self.options.reporter)(self.options) return self.options.report def check_files(self, paths=None): """Run all checks on the paths.""" if paths is None: paths = self.paths report = self.options.report runner = self.runner report.start() try: for path in paths: if os.path.isdir(path): self.input_dir(path) elif not self.excluded(path): runner(path) except KeyboardInterrupt: print('... stopped') report.stop() return report def input_file(self, filename, lines=None, expected=None, line_offset=0): """Run all checks on a Python source file.""" if self.options.verbose: print('checking %s' % filename) fchecker = self.checker_class( filename, lines=lines, options=self.options) return fchecker.check_all(expected=expected, line_offset=line_offset) def input_dir(self, dirname): """Check all files in this directory and all subdirectories.""" dirname = dirname.rstrip('/') if self.excluded(dirname): return 0 counters = self.options.report.counters verbose = self.options.verbose filepatterns = self.options.filename runner = self.runner for root, dirs, files in os.walk(dirname): if verbose: print('directory ' + root) counters['directories'] += 1 for subdir in sorted(dirs): if self.excluded(subdir, root): dirs.remove(subdir) for filename in sorted(files): # contain a pattern that matches? if ( filename_match(filename, filepatterns) and not self.excluded(filename, root) ): runner(os.path.join(root, filename)) def excluded(self, filename, parent=None): """Check if the file should be excluded. Check if 'options.exclude' contains a pattern matching filename. """ if not self.options.exclude: return False basename = os.path.basename(filename) if filename_match(basename, self.options.exclude): return True if parent: filename = os.path.join(parent, filename) filename = os.path.abspath(filename) return filename_match(filename, self.options.exclude) def ignore_code(self, code): """Check if the error code should be ignored. If 'options.select' contains a prefix of the error code, return False. Else, if 'options.ignore' contains a prefix of the error code, return True. """ if len(code) < 4 and any(s.startswith(code) for s in self.options.select): return False return (code.startswith(self.options.ignore) and not code.startswith(self.options.select)) def get_checks(self, argument_name): """Get all the checks for this category. Find all globally visible functions where the first argument name starts with argument_name and which contain selected tests. """ checks = [] for check, attrs in _checks[argument_name].items(): (codes, args) = attrs if any(not (code and self.ignore_code(code)) for code in codes): checks.append((check.__name__, check, args)) return sorted(checks) def get_parser(prog='pycodestyle', version=__version__): """Create the parser for the program.""" parser = OptionParser(prog=prog, version=version, usage="%prog [options] input ...") parser.config_options = [ 'exclude', 'filename', 'select', 'ignore', 'max-line-length', 'max-doc-length', 'indent-size', 'hang-closing', 'count', 'format', 'quiet', 'show-pep8', 'show-source', 'statistics', 'verbose'] parser.add_option('-v', '--verbose', default=0, action='count', help="print status messages, or debug with -vv") parser.add_option('-q', '--quiet', default=0, action='count', help="report only file names, or nothing with -qq") parser.add_option('-r', '--repeat', default=True, action='store_true', help="(obsolete) show all occurrences of the same error") parser.add_option('--first', action='store_false', dest='repeat', help="show first occurrence of each error") parser.add_option('--exclude', metavar='patterns', default=DEFAULT_EXCLUDE, help="exclude files or directories which match these " "comma separated patterns (default: %default)") parser.add_option('--filename', metavar='patterns', default='*.py', help="when parsing directories, only check filenames " "matching these comma separated patterns " "(default: %default)") parser.add_option('--select', metavar='errors', default='', help="select errors and warnings (e.g. E,W6)") parser.add_option('--ignore', metavar='errors', default='', help="skip errors and warnings (e.g. E4,W) " "(default: %s)" % DEFAULT_IGNORE) parser.add_option('--show-source', action='store_true', help="show source code for each error") parser.add_option('--show-pep8', action='store_true', help="show text of PEP 8 for each error " "(implies --first)") parser.add_option('--statistics', action='store_true', help="count errors and warnings") parser.add_option('--count', action='store_true', help="print total number of errors and warnings " "to standard error and set exit code to 1 if " "total is not null") parser.add_option('--max-line-length', type='int', metavar='n', default=MAX_LINE_LENGTH, help="set maximum allowed line length " "(default: %default)") parser.add_option('--max-doc-length', type='int', metavar='n', default=None, help="set maximum allowed doc line length and perform " "these checks (unchecked if not set)") parser.add_option('--indent-size', type='int', metavar='n', default=INDENT_SIZE, help="set how many spaces make up an indent " "(default: %default)") parser.add_option('--hang-closing', action='store_true', help="hang closing bracket instead of matching " "indentation of opening bracket's line") parser.add_option('--format', metavar='format', default='default', help="set the error format [default|pylint|]") parser.add_option('--diff', action='store_true', help="report changes only within line number ranges in " "the unified diff received on STDIN") group = parser.add_option_group("Testing Options") group.add_option('--benchmark', action='store_true', help="measure processing speed") return parser def read_config(options, args, arglist, parser): """Read and parse configurations. If a config file is specified on the command line with the "--config" option, then only it is used for configuration. Otherwise, the user configuration (~/.config/pycodestyle) and any local configurations in the current directory or above will be merged together (in that order) using the read method of ConfigParser. """ config = configparser.RawConfigParser() cli_conf = options.config local_dir = os.curdir if USER_CONFIG and os.path.isfile(USER_CONFIG): if options.verbose: print('user configuration: %s' % USER_CONFIG) config.read(USER_CONFIG) parent = tail = args and os.path.abspath(os.path.commonprefix(args)) while tail: if config.read(os.path.join(parent, fn) for fn in PROJECT_CONFIG): local_dir = parent if options.verbose: print('local configuration: in %s' % parent) break (parent, tail) = os.path.split(parent) if cli_conf and os.path.isfile(cli_conf): if options.verbose: print('cli configuration: %s' % cli_conf) config.read(cli_conf) pycodestyle_section = None if config.has_section(parser.prog): pycodestyle_section = parser.prog elif config.has_section('pep8'): pycodestyle_section = 'pep8' # Deprecated warnings.warn('[pep8] section is deprecated. Use [pycodestyle].') if pycodestyle_section: option_list = {o.dest: o.type or o.action for o in parser.option_list} # First, read the default values (new_options, __) = parser.parse_args([]) # Second, parse the configuration for opt in config.options(pycodestyle_section): if opt.replace('_', '-') not in parser.config_options: print(" unknown option '%s' ignored" % opt) continue if options.verbose > 1: print(" {} = {}".format(opt, config.get(pycodestyle_section, opt))) normalized_opt = opt.replace('-', '_') opt_type = option_list[normalized_opt] if opt_type in ('int', 'count'): value = config.getint(pycodestyle_section, opt) elif opt_type in ('store_true', 'store_false'): value = config.getboolean(pycodestyle_section, opt) else: value = config.get(pycodestyle_section, opt) if normalized_opt == 'exclude': value = normalize_paths(value, local_dir) setattr(new_options, normalized_opt, value) # Third, overwrite with the command-line options (options, __) = parser.parse_args(arglist, values=new_options) return options def process_options(arglist=None, parse_argv=False, config_file=None, parser=None, verbose=None): """Process options passed either via arglist or command line args. Passing in the ``config_file`` parameter allows other tools, such as flake8 to specify their own options to be processed in pycodestyle. """ if not parser: parser = get_parser() if not parser.has_option('--config'): group = parser.add_option_group("Configuration", description=( "The project options are read from the [%s] section of the " "tox.ini file or the setup.cfg file located in any parent folder " "of the path(s) being processed. Allowed options are: %s." % (parser.prog, ', '.join(parser.config_options)))) group.add_option('--config', metavar='path', default=config_file, help="user config file location") # Don't read the command line if the module is used as a library. if not arglist and not parse_argv: arglist = [] # If parse_argv is True and arglist is None, arguments are # parsed from the command line (sys.argv) (options, args) = parser.parse_args(arglist) options.reporter = None # If explicitly specified verbosity, override any `-v` CLI flag if verbose is not None: options.verbose = verbose if parse_argv and not args: if options.diff or any(os.path.exists(name) for name in PROJECT_CONFIG): args = ['.'] else: parser.error('input not specified') options = read_config(options, args, arglist, parser) options.reporter = parse_argv and options.quiet == 1 and FileReport options.filename = _parse_multi_options(options.filename) options.exclude = normalize_paths(options.exclude) options.select = _parse_multi_options(options.select) options.ignore = _parse_multi_options(options.ignore) if options.diff: options.reporter = DiffReport stdin = stdin_get_value() options.selected_lines = parse_udiff(stdin, options.filename, args[0]) args = sorted(options.selected_lines) return options, args def _parse_multi_options(options, split_token=','): r"""Split and strip and discard empties. Turns the following: A, B, into ["A", "B"] """ if options: return [o.strip() for o in options.split(split_token) if o.strip()] else: return options def _main(): """Parse options and run checks on Python source.""" import signal # Handle "Broken pipe" gracefully try: signal.signal(signal.SIGPIPE, lambda signum, frame: sys.exit(1)) except AttributeError: pass # not supported on Windows style_guide = StyleGuide(parse_argv=True) options = style_guide.options report = style_guide.check_files() if options.statistics: report.print_statistics() if options.benchmark: report.print_benchmark() if report.total_errors: if options.count: sys.stderr.write(str(report.total_errors) + '\n') sys.exit(1) if __name__ == '__main__': _main() PyCQA-pycodestyle-0dd4a37/setup.cfg000066400000000000000000000026261465376172200172450ustar00rootroot00000000000000[metadata] name = pycodestyle version = attr: pycodestyle.__version__ description = Python style guide checker long_description = file: README.rst long_description_content_type = text/x-rst url = https://pycodestyle.pycqa.org/ author = Johann C. Rocholl author_email = johann@rocholl.net maintainer = Ian Lee maintainer_email = IanLee1521@gmail.com license = MIT license_files = LICENSE classifiers = Development Status :: 5 - Production/Stable Environment :: Console Intended Audience :: Developers License :: OSI Approved :: MIT License Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: PyPy Topic :: Software Development :: Libraries :: Python Modules keywords = pycodestyle, pep8, PEP 8, PEP-8, PEP8 project_urls = Changes=https://pycodestyle.pycqa.org/en/latest/developer.html#changes [options] py_modules = pycodestyle python_requires = >=3.8 include_package_data = True zip_safe = False [options.entry_points] console_scripts = pycodestyle = pycodestyle:_main [bdist_wheel] universal = 1 [pycodestyle] ignore = E226,E24,W504 max_line_length = 79 max_doc_length = 72 [coverage:run] plugins = covdefaults omit = testing/data [coverage:report] fail_under = 93 PyCQA-pycodestyle-0dd4a37/setup.py000066400000000000000000000000451465376172200171270ustar00rootroot00000000000000from setuptools import setup setup() PyCQA-pycodestyle-0dd4a37/testing/000077500000000000000000000000001465376172200170735ustar00rootroot00000000000000PyCQA-pycodestyle-0dd4a37/testing/__init__.py000066400000000000000000000000001465376172200211720ustar00rootroot00000000000000PyCQA-pycodestyle-0dd4a37/testing/data/000077500000000000000000000000001465376172200200045ustar00rootroot00000000000000PyCQA-pycodestyle-0dd4a37/testing/data/E10.py000066400000000000000000000011511465376172200207010ustar00rootroot00000000000000#: E101 E122 W191 W191 if True: pass change_2_log = \ """Change 2 by slamb@testclient on 2006/04/13 21:46:23 creation """ p4change = { 2: change_2_log, } class TestP4Poller(unittest.TestCase): def setUp(self): self.setUpGetProcessOutput() return self.setUpChangeSource() def tearDown(self): pass # #: E101 W191 W191 if True: foo(1, 2) #: E101 E101 W191 W191 def test_keys(self): """areas.json - All regions are accounted for.""" expected = set([ u'Norrbotten', u'V\xe4sterbotten', ]) #: E101 W191 if True: print(""" tab at start of this line """) PyCQA-pycodestyle-0dd4a37/testing/data/E11.py000066400000000000000000000013411465376172200207030ustar00rootroot00000000000000#: E111 if x > 2: print x #: E111 E117 if True: print #: E112 if False: print #: E113 print print #: E114 E116 mimetype = 'application/x-directory' # 'httpd/unix-directory' create_date = False #: E116 E116 E116 def start(self): if True: self.master.start() # try: # self.master.start() # except MasterExit: # self.shutdown() # finally: # sys.exit() #: E115 E115 E115 E115 E115 E115 def start(self): if True: # try: # self.master.start() # except MasterExit: # self.shutdown() # finally: # sys.exit() self.master.start() #: E117 def start(): print #: E117 W191 def start(): print PyCQA-pycodestyle-0dd4a37/testing/data/E12.py000066400000000000000000000163531465376172200207150ustar00rootroot00000000000000#: E121 print "E121", ( "dent") #: E122 print "E122", ( "dent") #: E123 my_list = [ 1, 2, 3, 4, 5, 6, ] #: E124 print "E124", ("visual", "indent_two" ) #: E124 print "E124", ("visual", "indent_five" ) #: E124 a = (123, ) #: E129 W503 if (row < 0 or self.moduleCount <= row or col < 0 or self.moduleCount <= col): raise Exception("%s,%s - %s" % (row, col, self.moduleCount)) #: E126 print "E126", ( "dent") #: E126 print "E126", ( "dent") #: E127 print "E127", ("over-", "over-indent") #: E128 print "E128", ("visual", "hanging") #: E128 print "E128", ("under-", "under-indent") #: #: E126 my_list = [ 1, 2, 3, 4, 5, 6, ] #: E121 result = { 'key1': 'value', 'key2': 'value', } #: E126 E126 rv.update(dict.fromkeys(( 'qualif_nr', 'reasonComment_en', 'reasonComment_fr', 'reasonComment_de', 'reasonComment_it'), '?'), "foo") #: E126 abricot = 3 + \ 4 + \ 5 + 6 #: E131 print "hello", ( "there", # "john", "dude") #: E126 part = set_mimetype(( a.get('mime_type', 'text')), 'default') #: #: E122 if True: result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', ) #: E122 if some_very_very_very_long_variable_name or var \ or another_very_long_variable_name: raise Exception() #: E122 if some_very_very_very_long_variable_name or var[0] \ or another_very_long_variable_name: raise Exception() #: E122 if True: if some_very_very_very_long_variable_name or var \ or another_very_long_variable_name: raise Exception() #: E122 if True: if some_very_very_very_long_variable_name or var[0] \ or another_very_long_variable_name: raise Exception() #: E122 dictionary = [ "is": { "nested": yes(), }, ] #: E122 setup('', scripts=[''], classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: Developers', ]) #: #: E123 W291 print "E123", ( "bad", "hanging", "close" ) # #: E123 E123 E123 result = { 'foo': [ 'bar', { 'baz': 'frop', } ] } #: E123 result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', ) #: E124 my_list = [1, 2, 3, 4, 5, 6, ] #: E124 my_list = [1, 2, 3, 4, 5, 6, ] #: E124 result = some_function_that_takes_arguments('a', 'b', 'c', 'd', 'e', 'f', ) #: E124 fooff(aaaa, cca( vvv, dadd ), fff, ) #: E124 fooff(aaaa, ccaaa( vvv, dadd ), fff, ) #: E124 d = dict('foo', help="exclude files or directories which match these " "comma separated patterns (default: %s)" % DEFAULT_EXCLUDE ) #: E124 E128 E128 if line_removed: self.event(cr, uid, name="Removing the option for contract", description="contract line has been removed", ) #: #: E125 if foo is None and bar is "frop" and \ blah == 'yeah': blah = 'yeahnah' #: E125 # Further indentation required as indentation is not distinguishable def long_function_name( var_one, var_two, var_three, var_four): print(var_one) # #: E125 def qualify_by_address( self, cr, uid, ids, context=None, params_to_check=frozenset(QUALIF_BY_ADDRESS_PARAM)): """ This gets called by the web server """ #: E129 W503 if (a == 2 or b == "abc def ghi" "jkl mno"): return True #: #: E126 my_list = [ 1, 2, 3, 4, 5, 6, ] #: E126 abris = 3 + \ 4 + \ 5 + 6 #: E126 fixed = re.sub(r'\t+', ' ', target[c::-1], 1)[::-1] + \ target[c + 1:] #: E126 E126 rv.update(dict.fromkeys(( 'qualif_nr', 'reasonComment_en', 'reasonComment_fr', 'reasonComment_de', 'reasonComment_it'), '?'), "foo") #: E126 eat_a_dict_a_day({ "foo": "bar", }) #: E126 W503 if ( x == ( 3 ) or y == 4): pass #: E126 W503 W503 if ( x == ( 3 ) or x == ( 3) or y == 4): pass #: E131 troublesome_hash = { "hash": "value", "long": "the quick brown fox jumps over the lazy dog before doing a " "somersault", } #: #: E128 # Arguments on first line forbidden when not using vertical alignment foo = long_function_name(var_one, var_two, var_three, var_four) # #: E128 print('l.%s\t%s\t%s\t%r' % (token[2][0], pos, tokenize.tok_name[token[0]], token[1])) #: E128 def qualify_by_address(self, cr, uid, ids, context=None, params_to_check=frozenset(QUALIF_BY_ADDRESS_PARAM)): """ This gets called by the web server """ #: #: E128 foo(1, 2, 3, 4, 5, 6) #: E128 foo(1, 2, 3, 4, 5, 6) #: E128 foo(1, 2, 3, 4, 5, 6) #: E128 foo(1, 2, 3, 4, 5, 6) #: E127 foo(1, 2, 3, 4, 5, 6) #: E127 foo(1, 2, 3, 4, 5, 6) #: E127 foo(1, 2, 3, 4, 5, 6) #: E127 foo(1, 2, 3, 4, 5, 6) #: E127 foo(1, 2, 3, 4, 5, 6) #: E127 foo(1, 2, 3, 4, 5, 6) #: E127 foo(1, 2, 3, 4, 5, 6) #: E127 foo(1, 2, 3, 4, 5, 6) #: E127 foo(1, 2, 3, 4, 5, 6) #: E128 E128 if line_removed: self.event(cr, uid, name="Removing the option for contract", description="contract line has been removed", ) #: E124 E127 E127 if line_removed: self.event(cr, uid, name="Removing the option for contract", description="contract line has been removed", ) #: E127 rv.update(d=('a', 'b', 'c'), e=42) # #: E127 W503 rv.update(d=('a' + 'b', 'c'), e=42, f=42 + 42) #: E127 W503 input1 = {'a': {'calc': 1 + 2}, 'b': 1 + 42} #: E128 W503 rv.update(d=('a' + 'b', 'c'), e=42, f=(42 + 42)) #: E123 if True: def example_issue254(): return [node.copy( ( replacement # First, look at all the node's current children. for child in node.children # Replace them. for replacement in replace(child) ), dict(name=token.undefined) )] #: E125:2:5 E125:8:5 if (""" """): pass for foo in """ abc 123 """.strip().split(): print(foo) #: E122:6:5 E122:7:5 E122:8:1 print dedent( ''' mkdir -p ./{build}/ mv ./build/ ./{build}/%(revision)s/ '''.format( build='build', # more stuff ) ) #: E701:1:8 E231:1:8 E122:2:1 E203:4:8 E128:5:1 if True:\ print(True) print(a , end=' ') #: E127:4:12 def foo(): pass raise 123 + \ 123 #: E127:4:13 class Eggs: pass assert 123456 == \ 123456 #: E127:4:11 def f1(): print('foo') with open('/path/to/some/file/you/want/to/read') as file_1, \ open('/path/to/some/file/being/written', 'w') as file_2: file_2.write(file_1.read()) #: E127:5:11 def f1(): print('foo') with open('/path/to/some/file/you/want/to/read') as file_1, \ open('/path/to/some/file/being/written', 'w') as file_2, \ open('later-misindent'): file_2.write(file_1.read()) PyCQA-pycodestyle-0dd4a37/testing/data/E12not.py000066400000000000000000000321001465376172200214220ustar00rootroot00000000000000if ( x == ( 3 ) or y == 4): pass y = x == 2 \ or x == 3 if x == 2 \ or y > 1 \ or x == 3: pass if x == 2 \ or y > 1 \ or x == 3: pass #: W503 if (foo == bar and baz == frop): pass #: W503 if ( foo == bar and baz == frop ): pass a = ( ) a = (123, ) # if start[1] > end_col and not ( over_indent == 4 and indent_next): return (0, "E121 continuation line over-" "indented for visual indent") print "OK", ("visual", "indent") print "Okay", ("visual", "indent_three" ) print "a-ok", ( "there", "dude", ) print "hello", ( "there", "dude") print "hello", ( "there", # "john", "dude") print "hello", ( "there", "dude") print "hello", ( "there", "dude", ) # Aligned with opening delimiter foo = long_function_name(var_one, var_two, var_three, var_four) # # Extra indentation is not necessary. foo = long_function_name( var_one, var_two, var_three, var_four) arm = 'AAA' \ 'BBB' \ 'CCC' bbb = 'AAA' \ 'BBB' \ 'CCC' cc = ('AAA' 'BBB' 'CCC') cc = {'text': 'AAA' 'BBB' 'CCC'} cc = dict(text='AAA' 'BBB') sat = 'AAA' \ 'BBB' \ 'iii' \ 'CCC' #: W504 W504 abricot = (3 + 4 + 5 + 6) abricot = 3 + \ 4 + \ 5 + 6 part = [-1, 2, 3, 4, 5, 6] part = [-1, (2, 3, 4, 5, 6), 7, 8, 9, 0] fnct(1, 2, 3, 4, 5, 6) fnct(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) def long_function_name( var_one, var_two, var_three, var_four): print(var_one) #: W504 if ((row < 0 or self.moduleCount <= row or col < 0 or self.moduleCount <= col)): raise Exception("%s,%s - %s" % (row, col, self.moduleCount)) result = { 'foo': [ 'bar', { 'baz': 'frop', } ] } foo = my.func({ "foo": "bar", }, "baz") # fooff(aaaa, cca( vvv, dadd ), fff, ggg) fooff(aaaa, abbb, cca( vvv, aaa, dadd), "visual indentation is not a multiple of four",) # if bar: return ( start, 'E121 lines starting with a ' 'closing bracket should be indented ' "to match that of the opening " "bracket's line" ) #: W504 # you want vertical alignment, so use a parens if ((foo.bar("baz") and foo.bar("frop") )): print "yes" #: W504 # also ok, but starting to look like LISP if ((foo.bar("baz") and foo.bar("frop"))): print "yes" #: W504 if (a == 2 or b == "abc def ghi" "jkl mno"): return True #: W504 if (a == 2 or b == """abc def ghi jkl mno"""): return True if length > options.max_line_length: return options.max_line_length, \ "E501 line too long (%d characters)" % length # print 'l.{line}\t{pos}\t{name}\t{text}'.format( line=token[2][0], pos=pos, name=tokenize.tok_name[token[0]], text=repr(token[1]), ) print('%-7d %s per second (%d total)' % ( options.counters[key] / elapsed, key, options.counters[key])) #: W504 if os.path.exists(os.path.join(path, PEP8_BIN)): cmd = ([os.path.join(path, PEP8_BIN)] + self._pep8_options(targetfile)) #: W504 fixed = (re.sub(r'\t+', ' ', target[c::-1], 1)[::-1] + target[c + 1:]) #: W504 fixed = ( re.sub(r'\t+', ' ', target[c::-1], 1)[::-1] + target[c + 1:] ) #: W504 if foo is None and bar is "frop" and \ blah == 'yeah': blah = 'yeahnah' """This is a multi-line docstring.""" if blah: # is this actually readable? :) multiline_literal = """ while True: if True: 1 """.lstrip() multiline_literal = ( """ while True: if True: 1 """.lstrip() ) multiline_literal = ( """ while True: if True: 1 """ .lstrip() ) if blah: multiline_visual = (""" while True: if True: 1 """ .lstrip()) rv = {'aaa': 42} rv.update(dict.fromkeys(( 'qualif_nr', 'reasonComment_en', 'reasonComment_fr', 'reasonComment_de', 'reasonComment_it'), '?')) rv.update(dict.fromkeys(('qualif_nr', 'reasonComment_en', 'reasonComment_fr', 'reasonComment_de', 'reasonComment_it'), '?')) rv.update(dict.fromkeys(('qualif_nr', 'reasonComment_en', 'reasonComment_fr', 'reasonComment_de', 'reasonComment_it'), '?')) rv.update(dict.fromkeys( ('qualif_nr', 'reasonComment_en', 'reasonComment_fr', 'reasonComment_de', 'reasonComment_it'), '?' ), "foo", context={ 'alpha': 4, 'beta': 53242234, 'gamma': 17, }) rv.update( dict.fromkeys(( 'qualif_nr', 'reasonComment_en', 'reasonComment_fr', 'reasonComment_de', 'reasonComment_it'), '?'), "foo", context={ 'alpha': 4, 'beta': 53242234, 'gamma': 17, }, ) # event_obj.write(cursor, user_id, { 'user': user, 'summary': text, 'data': data, }) event_obj.write(cursor, user_id, { 'user': user, 'summary': text, 'data': {'aaa': 1, 'bbb': 2}, }) event_obj.write(cursor, user_id, { 'user': user, 'summary': text, 'data': { 'aaa': 1, 'bbb': 2}, }) event_obj.write(cursor, user_id, { 'user': user, 'summary': text, 'data': {'timestamp': now, 'content': { 'aaa': 1, 'bbb': 2 }}, }) def qualify_by_address( self, cr, uid, ids, context=None, params_to_check=frozenset(QUALIF_BY_ADDRESS_PARAM)): """ This gets called by the web server """ def qualify_by_address(self, cr, uid, ids, context=None, params_to_check=frozenset(QUALIF_BY_ADDRESS_PARAM)): """ This gets called by the web server """ _ipv4_re = re.compile(r'^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.' r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.' r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.' r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$') fct(""" AAA """ + status_2_string) if context: msg = """\ action: GET-CONFIG payload: ip_address: "%(ip)s" username: "%(username)s" """ % context if context: msg = """\ action: \ GET-CONFIG """ % context if context: msg = """\ action: """\ """GET-CONFIG """ % context def unicode2html(s): """Convert the characters &<>'" in string s to HTML-safe sequences. Convert newline to
too.""" return unicode((s or '').replace('&', '&') .replace('>', '>') .replace('<', '<') .replace("'", ''') .replace('"', '"') .replace('\n', '
\n')) # parser.add_option('--count', action='store_true', help="print total number of errors and warnings " "to standard error and set exit code to 1 if " "total is not null") parser.add_option('--exclude', metavar='patterns', default=DEFAULT_EXCLUDE, help="exclude files or directories which match these " "comma separated patterns (default: %s)" % DEFAULT_EXCLUDE) add_option('--count', help="print total number of errors " "to standard error total is not null") add_option('--count', help="print total number of errors " "to standard error " "total is not null") # help = ("print total number of errors " + "to standard error") help = "print total number of errors " \ "to standard error" help = u"print total number of errors " \ u"to standard error" help = b"print total number of errors " \ b"to standard error" help = br"print total number of errors " \ br"to standard error" help = f"print total number of errors " \ f"to standard error" d = dict('foo', help="exclude files or directories which match these " "comma separated patterns (default: %s)" % DEFAULT_EXCLUDE) d = dict('foo', help=u"exclude files or directories which match these " u"comma separated patterns (default: %s)" % DEFAULT_EXCLUDE) d = dict('foo', help=b"exclude files or directories which match these " b"comma separated patterns (default: %s)" % DEFAULT_EXCLUDE) d = dict('foo', help=br"exclude files or directories which match these " br"comma separated patterns (default: %s)" % DEFAULT_EXCLUDE) d = dict('foo', help="exclude files or directories which match these " "comma separated patterns (default: %s)" % DEFAULT_EXCLUDE) d = dict('foo', help="exclude files or directories which match these " "comma separated patterns (default: %s, %s)" % (DEFAULT_EXCLUDE, DEFAULT_IGNORE) ) d = dict('foo', help="exclude files or directories which match these " "comma separated patterns (default: %s, %s)" % # who knows what might happen here? (DEFAULT_EXCLUDE, DEFAULT_IGNORE) ) # parens used to allow the indenting. troublefree_hash = { "hash": "value", "long": ("the quick brown fox jumps over the lazy dog before doing a " "somersault"), "long key that tends to happen more when you're indented": ( "stringwithalongtoken you don't want to break" ), } # another accepted form troublefree_hash = { "hash": "value", "long": "the quick brown fox jumps over the lazy dog before doing " "a somersault", ("long key that tends to happen more " "when you're indented"): "stringwithalongtoken you don't want to break", } # confusing but accepted... don't do that troublesome_hash = { "hash": "value", "long": "the quick brown fox jumps over the lazy dog before doing a " "somersault", "long key that tends to happen more " "when you're indented": "stringwithalongtoken you don't want to break", } # d = dict('foo', help="exclude files or directories which match these " "comma separated patterns (default: %s)" % DEFAULT_EXCLUDE ) d = dict('foo', help="exclude files or directories which match these " "comma separated patterns (default: %s)" % DEFAULT_EXCLUDE, foobar="this clearly should work, because it is at " "the right indent level", ) rv.update(dict.fromkeys( ('qualif_nr', 'reasonComment_en', 'reasonComment_fr', 'reasonComment_de', 'reasonComment_it'), '?'), "foo", context={'alpha': 4, 'beta': 53242234, 'gamma': 17}) def f(): try: if not Debug: print(''' If you would like to see debugging output, try: %s -d5 ''' % sys.argv[0]) d = { # comment 1: 2 } # issue 138 [ 12, # this is a multi-line inline # comment ] # issue 151 if a > b and \ c > d: moo_like_a_cow() # my_list = [ 1, 2, 3, 4, 5, 6, ] my_list = [1, 2, 3, 4, 5, 6, ] result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', ) result = some_function_that_takes_arguments('a', 'b', 'c', 'd', 'e', 'f', ) # issue 203 dica = { ('abc' 'def'): ( 'abc'), } (abcdef[0] [1]) = ( 'abc') ('abc' 'def') == ( 'abc') # issue 214 bar( 1).zap( 2) bar( 1).zap( 2) # if True: def example_issue254(): return [node.copy( ( replacement # First, look at all the node's current children. for child in node.children # Replace them. for replacement in replace(child) ), dict(name=token.undefined) )] def valid_example(): return [node.copy(properties=dict( (key, val if val is not None else token.undefined) for key, val in node.items() ))] def other_example(): return [node.copy(properties=dict( (key, val if val is not None else token.undefined) for key, val in node.items() ))] foo([ 'bug' ]) # issue 144, finally! some_hash = { "long key that tends to happen more when you're indented": "stringwithalongtoken you don't want to break", } { 1: 999999 if True else 0, } print dedent( ''' mkdir -p ./{build}/ mv ./build/ ./{build}/%(revision)s/ '''.format( build='build', # more stuff ) ) def foo(): pass raise 123 + \ 123 class Eggs: pass assert 123456 == \ 123456 def f1(): print('foo') with open('/path/to/some/file/you/want/to/read') as file_1, \ open('/path/to/some/file/being/written', 'w') as file_2, \ open('just-making-sure-more-continuations-also-work'): file_2.write(file_1.read()) PyCQA-pycodestyle-0dd4a37/testing/data/E20.py000066400000000000000000000025121465376172200207040ustar00rootroot00000000000000#: E201:1:6 spam( ham[1], {eggs: 2}) #: E201:1:10 spam(ham[ 1], {eggs: 2}) #: E201:1:15 spam(ham[1], { eggs: 2}) #: E201:1:6 spam( ham[1], {eggs: 2}) #: E201:1:10 spam(ham[ 1], {eggs: 2}) #: E201:1:15 spam(ham[1], { eggs: 2}) #: Okay spam(ham[1], {eggs: 2}) #: #: E202:1:23 spam(ham[1], {eggs: 2} ) #: E202:1:22 spam(ham[1], {eggs: 2 }) #: E202:1:11 spam(ham[1 ], {eggs: 2}) #: E202:1:23 spam(ham[1], {eggs: 2} ) #: E202:1:22 spam(ham[1], {eggs: 2 }) #: E202:1:11 spam(ham[1 ], {eggs: 2}) #: Okay spam(ham[1], {eggs: 2}) result = func( arg1='some value', arg2='another value', ) result = func( arg1='some value', arg2='another value' ) result = [ item for item in items if item > 5 ] #: #: E203:1:10 if x == 4 : print x, y x, y = y, x #: E203:1:10 if x == 4 : print x, y x, y = y, x #: E203:2:15 E702:2:16 if x == 4: print x, y ; x, y = y, x #: E203:2:15 E702:2:16 if x == 4: print x, y ; x, y = y, x #: E203:3:13 if x == 4: print x, y x, y = y , x #: E203:3:13 if x == 4: print x, y x, y = y , x #: Okay if x == 4: print x, y x, y = y, x a[b1, :] == a[b1, ...] b = a[:, b1] #: E204:1:2 @ decorator def f(): pass #: E204:1:2 @ decorator def f(): pass #: E204:1:2 @ decorator def f(): pass #: E204:2:6 if True: @ decorator def f(): pass #: PyCQA-pycodestyle-0dd4a37/testing/data/E21.py000066400000000000000000000003421465376172200207040ustar00rootroot00000000000000#: E211 spam (1) #: E211 E211 dict ['key'] = list [index] #: E211 dict['key'] ['subkey'] = list[index] #: Okay spam(1) dict['key'] = list[index] # This is not prohibited by PEP8, but avoid it. class Foo (Bar, Baz): pass PyCQA-pycodestyle-0dd4a37/testing/data/E22.py000066400000000000000000000041161465376172200207100ustar00rootroot00000000000000#: E221 a = 12 + 3 b = 4 + 5 #: E221 E221 x = 1 y = 2 long_variable = 3 #: E221 E221 x[0] = 1 x[1] = 2 long_variable = 3 #: E221 E221 x = f(x) + 1 y = long_variable + 2 z = x[0] + 3 #: E221:3:14 text = """ bar foo %s""" % rofl #: Okay x = 1 y = 2 long_variable = 3 #: #: E222 a = a + 1 b = b + 10 #: E222 E222 x = -1 y = -2 long_variable = 3 #: E222 E222 x[0] = 1 x[1] = 2 long_variable = 3 #: #: E223 foobart = 4 a = 3 # aligned with tab #: #: E224 a += 1 b += 1000 #: #: E225 submitted +=1 #: E225 submitted+= 1 #: E225 c =-1 #: E225 x = x /2 - 1 #: E225 c = alpha -4 #: E225 c = alpha- 4 #: E225 z = x **y #: E225 z = (x + 1) **y #: E225 z = (x + 1)** y #: E225 _1kB = _1MB >>10 #: E225 _1kB = _1MB>> 10 #: E225 E225 i=i+ 1 #: E225 E225 i=i +1 #: E225 i = 1and 1 #: E225 i = 1or 0 #: E225 1is 1 #: E225 1in [] #: E225 i = 1 @2 #: E225 i = 1@ 2 #: E225 E226 i=i+1 #: E225 E226 i =i+1 #: E225 E226 i= i+1 #: E225 E226 c = (a +b)*(a - b) #: E225 E226 c = (a+ b)*(a - b) #: E225 x[lambda: None]=1 #: #: E226 z = 2//30 #: E226 E226 c = (a+b) * (a-b) #: E226 norman = True+False #: E226 x = x*2 - 1 #: E226 x = x/2 - 1 #: E226 E226 hypot2 = x*x + y*y #: E226 c = (a + b)*(a - b) #: E226 def halves(n): return (i//2 for i in range(n)) #: E227 _1kB = _1MB>>10 #: E227 _1MB = _1kB<<10 #: E227 a = b|c #: E227 b = c&a #: E227 c = b^a #: E228 a = b%c #: E228 msg = fmt%(errno, errmsg) #: E228 msg = "Error %d occurred"%errno #: #: Okay i = i + 1 submitted += 1 x = x * 2 - 1 hypot2 = x * x + y * y c = (a + b) * (a - b) _1MiB = 2 ** 20 _1TiB = 2**30 foo(bar, key='word', *args, **kwargs) baz(**kwargs) negative = -1 spam(-1) -negative func1(lambda *args, **kw: (args, kw)) func2(lambda a, b=h[:], c=0: (a, b, c)) if not -5 < x < +5: print >>sys.stderr, "x is out of range." print >> sys.stdout, "x is an integer." x = x / 2 - 1 x = 1 @ 2 if alpha[:-i]: *a, b = (1, 2, 3) def squares(n): return (i**2 for i in range(n)) ENG_PREFIXES = { -6: "\u03bc", # Greek letter mu -3: "m", } #: PyCQA-pycodestyle-0dd4a37/testing/data/E23.py000066400000000000000000000002431465376172200207060ustar00rootroot00000000000000#: E231 a = (1,2) #: E231 a[b1,:] #: E231 a = [{'a':''}] #: Okay a = (4,) b = (5, ) c = {'text': text[5:]} result = { 'key1': 'value', 'key2': 'value', } PyCQA-pycodestyle-0dd4a37/testing/data/E24.py000066400000000000000000000003301465376172200207040ustar00rootroot00000000000000#: E241 a = (1, 2) #: Okay b = (1, 20) #: E242 a = (1, 2) # tab before 2 #: Okay b = (1, 20) # space before 20 #: E241 E241 E241 # issue 135 more_spaces = [a, b, ef, +h, c, -d] PyCQA-pycodestyle-0dd4a37/testing/data/E25.py000066400000000000000000000022761465376172200207200ustar00rootroot00000000000000#: E251 E251 def foo(bar = False): '''Test function with an error in declaration''' pass #: E251 foo(bar= True) #: E251 foo(bar =True) #: E251 E251 foo(bar = True) #: E251 y = bar(root= "sdasd") #: E251:2:29 parser.add_argument('--long-option', default= "/rather/long/filesystem/path/here/blah/blah/blah") #: E251:1:45 parser.add_argument('--long-option', default ="/rather/long/filesystem/path/here/blah/blah/blah") #: E251:3:8 E251:3:10 foo(True, baz=(1, 2), biz = 'foo' ) #: Okay foo(bar=(1 == 1)) foo(bar=(1 != 1)) foo(bar=(1 >= 1)) foo(bar=(1 <= 1)) (options, args) = parser.parse_args() d[type(None)] = _deepcopy_atomic # Annotated Function Definitions #: Okay def munge(input: AnyStr, sep: AnyStr = None, limit=1000, extra: Union[str, dict] = None) -> AnyStr: pass #: Okay async def add(a: int = 0, b: int = 0) -> int: return a + b # Previously E251 four times #: E271:1:6 async def add(a: int = 0, b: int = 0) -> int: return a + b #: E252:1:15 E252:1:16 E252:1:27 E252:1:36 def add(a: int=0, b: int =0, c: int= 0) -> int: return a + b + c #: Okay def add(a: int = _default(name='f')): return a PyCQA-pycodestyle-0dd4a37/testing/data/E26.py000066400000000000000000000025321465376172200207140ustar00rootroot00000000000000#: E261:1:5 pass # an inline comment #: E262:1:12 x = x + 1 #Increment x #: E262:1:12 x = x + 1 # Increment x #: E262:1:12 x = y + 1 #: Increment x #: E265:1:1 #Block comment a = 1 #: E265:2:1 m = 42 #! This is important mx = 42 - 42 #: E266:3:5 E266:6:5 def how_it_feel(r): ### This is a variable ### a = 42 ### Of course it is unused return #: E265:1:1 E266:2:1 ##if DEBUG: ## logging.error() #: W291:1:42 ######################################### #: #: Okay #!/usr/bin/env python pass # an inline comment x = x + 1 # Increment x y = y + 1 #: Increment x # Block comment a = 1 # Block comment1 # Block comment2 aaa = 1 # example of docstring (not parsed) def oof(): """ #foo not parsed """ #################################################################### # A SEPARATOR # #################################################################### # ################################################################ # # ####################### another separator ###################### # # ################################################################ # #: E262:3:9 # -*- coding: utf8 -*- #  (One space one NBSP) Ok for block comment a = 42 #  (One space one NBSP) #: E262:2:9 # (Two spaces) Ok for block comment a = 42 # (Two spaces) PyCQA-pycodestyle-0dd4a37/testing/data/E27.py000066400000000000000000000013351465376172200207150ustar00rootroot00000000000000#: Okay True and False #: E271 True and False #: E272 True and False #: E271 if 1: #: E273 True and False #: E273 E274 True and False #: E271 a and b #: E271 1 and b #: E271 a and 2 #: E271 E272 1 and b #: E271 E272 a and 2 #: E272 this and False #: E273 a and b #: E274 a and b #: E273 E274 this and False #: Okay from u import (a, b) from v import c, d #: E271 from w import (e, f) #: E275 from w import(e, f) #: E275 from importable.module import(e, f) #: E275 try: from importable.module import(e, f) except ImportError: pass #: E275 if(foo): pass else: pass #: Okay matched = {"true": True, "false": False} #: E275:2:11 if True: assert(1) #: Okay def f(): print((yield)) x = (yield) PyCQA-pycodestyle-0dd4a37/testing/data/E30.py000066400000000000000000000041141465376172200207050ustar00rootroot00000000000000#: E301:5:5 class X: def a(): pass def b(): pass #: E301:6:5 class X: def a(): pass # comment def b(): pass #: #: E302:2:1 """Main module.""" def _main(): pass #: E302:2:1 import sys def get_sys_path(): return sys.path #: E302:4:1 def a(): pass def b(): pass #: E302:6:1 def a(): pass # comment def b(): pass #: #: E302:4:1 def a(): pass async def b(): pass #: #: E303:5:1 print print #: E303:5:1 print # comment print #: E303:5:5 E303:8:5 def a(): print # comment # another comment print #: #: E303:6:5 class xyz: def a(self): pass def b(self): pass #: E303:5:5 if True: a = 1 a = 2 #: E304:3:1 @decorator def function(): pass #: E303:5:1 #!python """This class docstring comes on line 5. It gives error E303: too many blank lines (3) """ #: #: E305:7:1 def a(): print # comment # another comment a() #: E305:8:1 def a(): print # comment # another comment try: a() except Exception: pass #: E305:5:1 def a(): print # Two spaces before comments, too. if a(): a() #: #: E306:3:5 def a(): x = 1 def b(): pass #: E306:3:5 async def a(): x = 1 def b(): pass #: E306:3:5 E306:5:9 def a(): x = 2 def b(): x = 1 def c(): pass #: E306:3:5 E306:6:5 def a(): x = 1 class C: pass x = 2 def b(): pass #: #: E305:8:1 # Example from https://github.com/PyCQA/pycodestyle/issues/400 import stuff def main(): blah, blah if __name__ == '__main__': main() # Previously just E272:1:6 E272:4:6 #: E302:4:1 E271:1:6 E271:4:6 async def x(): pass async def x(y: int = 1): pass #: E704:3:1 E302:3:1 def bar(): pass def baz(): pass #: E704:1:1 E302:2:1 def bar(): pass def baz(): pass #: E704:4:5 E306:4:5 def foo(): def bar(): pass def baz(): pass #: E704:2:5 E306:3:5 def foo(): def bar(): pass def baz(): pass #: E302:5:1 def f(): pass # wat @hi def g(): pass PyCQA-pycodestyle-0dd4a37/testing/data/E30not.py000066400000000000000000000053001465376172200214240ustar00rootroot00000000000000#: Okay class X: pass #: Okay def foo(): pass #: Okay # -*- coding: utf-8 -*- class X: pass #: Okay # -*- coding: utf-8 -*- def foo(): pass #: Okay class X: def a(): pass # comment def b(): pass # This is a # ... multi-line comment def c(): pass # This is a # ... multi-line comment @some_decorator class Y: def a(): pass # comment def b(): pass @property def c(): pass try: from nonexistent import Bar except ImportError: class Bar(object): """This is a Bar replacement""" def with_feature(f): """Some decorator""" wrapper = f if has_this_feature(f): def wrapper(*args): call_feature(args[0]) return f(*args) return wrapper try: next except NameError: def next(iterator, default): for item in iterator: return item return default def a(): pass class Foo(): """Class Foo""" def b(): pass # comment def c(): pass # comment def d(): pass # This is a # ... multi-line comment # And this one is # ... a second paragraph # ... which spans on 3 lines # Function `e` is below # NOTE: Hey this is a testcase def e(): pass def a(): print # comment print print # Comment 1 # Comment 2 # Comment 3 def b(): pass #: Okay def foo(): pass def bar(): pass class Foo(object): pass class Bar(object): pass if __name__ == '__main__': foo() #: Okay classification_errors = None #: Okay defined_properly = True #: Okay defaults = {} defaults.update({}) #: Okay def foo(x): classification = x definitely = not classification #: E704:3:1 E704:4:1 # This emits the (ignored-by-default) E704, but here we're testing # for no E30x being emitted. def bar(): pass def baz(): pass def main(): pass #: E704:4:5 E704:5:5 def foo(): # This emits the (ignored-by-default) E704, but here we're testing # for no E30x being emitted. def bar(): pass def baz(): pass #: E704:8:1 E704:10:1 from typing import overload from typing import Union # This emits the (ignored-by-default) E704, but here we're testing # for no E30x being emitted. @overload def f(x: int) -> int: ... @overload def f(x: str) -> str: ... def f(x: Union[int, str]) -> Union[int, str]: return x #: E704:8:5 E704:10:5 from typing import Protocol class C(Protocol): # This emits the (ignored-by-default) E704, but here we're testing # for no E30x being emitted. @property def f(self) -> int: ... @property def g(self) -> str: ... #: Okay #!python # -*- coding: utf-8 -*- def a(): pass #: Okay def f( a, ): pass PyCQA-pycodestyle-0dd4a37/testing/data/E40.py000066400000000000000000000014101465376172200207020ustar00rootroot00000000000000#: E401 import os, sys #: Okay import os import sys from subprocess import Popen, PIPE from myclass import MyClass from foo.bar.yourclass import YourClass import myclass import foo.bar.yourclass #: Okay __all__ = ['abc'] import foo #: Okay __version__ = "42" import foo #: Okay __author__ = "Simon Gomizelj" import foo #: Okay try: import foo except ImportError: pass else: print('imported foo') finally: print('made attempt to import foo') import bar #: Okay with warnings.catch_warnings(): warnings.filterwarnings("ignore", DeprecationWarning) import foo import bar #: Okay if False: import foo elif not True: import bar else: import mwahaha import bar #: E402 VERSION = '1.2.3' import foo #: E402 import foo a = 1 import bar PyCQA-pycodestyle-0dd4a37/testing/data/E50.py000066400000000000000000000065401465376172200207140ustar00rootroot00000000000000#: E501 a = '12345678901234567890123456789012345678901234567890123456789012345678901234567890' #: E501 a = '1234567890123456789012345678901234567890123456789012345678901234567890' or \ 6 #: E501 a = 7 or \ '1234567890123456789012345678901234567890123456789012345678901234567890' or \ 6 #: E501 E501 a = 7 or \ '1234567890123456789012345678901234567890123456789012345678901234567890' or \ '1234567890123456789012345678901234567890123456789012345678901234567890' or \ 6 #: E501 a = '1234567890123456789012345678901234567890123456789012345678901234567890' # \ #: E502 a = ('123456789012345678901234567890123456789012345678901234567890123456789' \ '01234567890') #: E502 a = ('AAA \ BBB' \ 'CCC') #: E502 if (foo is None and bar is "e000" and \ blah == 'yeah'): blah = 'yeahnah' #: E502 W503 W503 y = ( 2 + 2 # \ + 3 # \ + 4 \ + 3 ) # #: Okay a = ('AAA' 'BBB') a = ('AAA \ BBB' 'CCC') a = 'AAA' \ 'BBB' \ 'CCC' a = ('AAA\ BBBBBBBBB\ CCCCCCCCC\ DDDDDDDDD') # #: Okay if aaa: pass elif bbb or \ ccc: pass ddd = \ ccc ('\ ' + ' \ ') (''' ''' + ' \ ') #: E501 E225 E226 very_long_identifiers=and_terrible_whitespace_habits(are_no_excuse+for_long_lines) # #: E501 W505 '''multiline string with a long long long long long long long long long long long long long long long long line ''' #: E501 W505 '''same thing, but this time without a terminal newline in the string long long long long long long long long long long long long long long long long line''' #: E501 if True: x = f""" covdefaults>=1.2; python_version == '2.7' or python_version == '{py_ver}' """ # # issue 224 (unavoidable long lines in docstrings) #: Okay """ I'm some great documentation. Because I'm some great documentation, I'm going to give you a reference to some valuable information about some API that I'm calling: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx """ #: E501 W505 """ longnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaces""" #: E501 W505 # Regression test for #622 def foo(): """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis pulvinar vitae """ #: E501 loooooong = 'looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong' f"""\ """ #: Okay """ This almost_empty_line """ #: E501 W505 """ This almost_empty_line """ #: E501 W505 # A basic comment # with a long long long long long long long long long long long long long long long long line # #: Okay # I'm some great comment. Because I'm so great, I'm going to give you a # reference to some valuable information about some API that I'm # calling: # # http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx import this # longnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaceslongnospaces # #: Okay # This # almost_empty_line # #: E501 W505 # This # almost_empty_line # #: Okay #!/reallylongpath/toexecutable --maybe --with --some ARGUMENTS TO DO WITH WHAT EXECUTABLE TO RUN PyCQA-pycodestyle-0dd4a37/testing/data/E70.py000066400000000000000000000007661465376172200207220ustar00rootroot00000000000000#: E701:1:5 if a: a = False #: E701:1:40 if not header or header[:6] != 'bytes=': return #: E702:1:10 a = False; b = True #: E702:1:17 import bdist_egg; bdist_egg.write_safety_flag(cmd.egg_info, safe) #: E703:1:13 import shlex; #: E702:1:9 E703:1:23 del a[:]; a.append(42); #: E704:1:1 def f(x): return 2 #: E704:1:1 async def f(x): return 2 #: E704:1:1 E271:1:6 async def f(x): return 2 #: E704:1:1 E226:1:19 def f(x): return 2*x #: E704:2:5 E226:2:23 while all is round: def f(x): return 2*x #: PyCQA-pycodestyle-0dd4a37/testing/data/E71.py000066400000000000000000000023351465376172200207150ustar00rootroot00000000000000#: E711 if res == None: pass #: E711 if res != None: pass #: E711 if None == res: pass #: E711 if None != res: pass #: E711 if res[1] == None: pass #: E711 if res[1] != None: pass #: E711 if None != res[1]: pass #: E711 if None == res[1]: pass # #: E712 if res == True: pass #: E712 if res != False: pass #: E712 if True != res: pass #: E712 if False == res: pass #: E712 if res[1] == True: pass #: E712 if res[1] != False: pass #: E712 E712 var = 1 if cond == True else -1 if cond == False else cond # #: E713 if not X in Y: pass #: E713 if not X.B in Y: pass #: E713 if not X in Y and Z == "zero": pass #: E713 if X == "zero" or not Y in Z: pass # #: E714 if not X is Y: pass #: E714 if not X.B is Y: pass #: E714 if not X is Y is not Z: pass #: E714 if not X is not Y: pass # #: Okay if x not in y: pass if not (X in Y or X is Z): pass if not (X in Y): pass if x is not y: pass if X is not Y is not Z: pass if TrueElement.get_element(True) == TrueElement.get_element(False): pass if (True) == TrueElement or x == TrueElement: pass assert (not foo) in bar assert {'x': not foo} in bar assert [42, not foo] in bar #: PyCQA-pycodestyle-0dd4a37/testing/data/E72.py000066400000000000000000000025101465376172200207110ustar00rootroot00000000000000#: E721 if type(res) == type(42): pass #: E721 if type(res) != type(""): pass #: Okay res.type("") == "" #: Okay import types if res == types.IntType: pass #: Okay import types if type(res) is not types.ListType: pass #: E721 assert type(res) == type(False) or type(res) == type(None) #: E721 assert type(res) == type([]) #: E721 assert type(res) == type(()) #: E721 assert type(res) == type((0,)) #: E721 assert type(res) == type((0)) #: E721 assert type(res) != type((1, )) #: Okay assert type(res) is type((1, )) #: Okay assert type(res) is not type((1, )) #: E211 E721 assert type(res) == type ([2, ]) #: E201 E201 E202 E721 assert type(res) == type( ( ) ) #: E201 E202 E721 assert type(res) == type( (0, ) ) #: #: Okay import types if isinstance(res, int): pass if isinstance(res, str): pass if isinstance(res, types.MethodType): pass #: Okay def func_histype(a, b, c): pass #: E722 try: pass except: pass #: E722 try: pass except Exception: pass except: pass #: E722 E203 E271 try: pass except : pass #: Okay fake_code = """" try: do_something() except: pass """ try: pass except Exception: pass #: Okay from . import custom_types as types red = types.ColorTypeRED red is types.ColorType.RED #: Okay from . import compute_type if compute_type(foo) == 5: pass PyCQA-pycodestyle-0dd4a37/testing/data/E73.py000066400000000000000000000004061465376172200207140ustar00rootroot00000000000000#: E731:1:1 f = lambda x: 2 * x #: E731:1:1 E226:1:16 f = lambda x: 2*x #: E731:2:5 while False: this = lambda y, z: 2 * x #: Okay f = object() f.method = lambda: 'Method' f = {} f['a'] = lambda x: x ** 2 f = [] f.append(lambda x: x ** 2) lambda: 'no-op' PyCQA-pycodestyle-0dd4a37/testing/data/E74.py000066400000000000000000000004251465376172200207160ustar00rootroot00000000000000#: E741:1:8 lambda l: dict(zip(l, range(len(l)))) #: E741:1:7 E704:1:1 def f(l): print(l, l, l) #: E741:2:12 x = ( lambda l: dict(zip(l, range(len(l)))), ) #: E741:2:12 E741:3:12 x = ( lambda l: dict(zip(l, range(len(l)))), lambda l: dict(zip(l, range(len(l)))), ) PyCQA-pycodestyle-0dd4a37/testing/data/E90.py000066400000000000000000000003471465376172200207170ustar00rootroot00000000000000#: E901 = [x #: E901 E101 W191 while True: try: pass except: print 'Whoops' #: Okay # Issue #119 # Do not crash with Python2 if the line endswith '\r\r\n' EMPTY_SET = set() SET_TYPE = type(EMPTY_SET) toto = 0 + 0 #: PyCQA-pycodestyle-0dd4a37/testing/data/W19.py000066400000000000000000000052051465376172200207400ustar00rootroot00000000000000#: W191 if False: print # indented with 1 tab #: #: W191 y = x == 2 \ or x == 3 #: E101 W191 W504 if ( x == ( 3 ) or y == 4): pass #: E101 W191 if x == 2 \ or y > 1 \ or x == 3: pass #: E101 W191 if x == 2 \ or y > 1 \ or x == 3: pass #: #: E101 W191 W504 if (foo == bar and baz == frop): pass #: E101 W191 W504 if ( foo == bar and baz == frop ): pass #: #: E101 E101 W191 W191 if start[1] > end_col and not ( over_indent == 4 and indent_next): return (0, "E121 continuation line over-" "indented for visual indent") #: #: E101 W191 def long_function_name( var_one, var_two, var_three, var_four): print(var_one) #: E101 W191 W504 if ((row < 0 or self.moduleCount <= row or col < 0 or self.moduleCount <= col)): raise Exception("%s,%s - %s" % (row, col, self.moduleCount)) #: E101 E101 E101 E101 W191 W191 W191 W191 W191 W191 if bar: return ( start, 'E121 lines starting with a ' 'closing bracket should be indented ' "to match that of the opening " "bracket's line" ) # #: E101 W191 W504 # you want vertical alignment, so use a parens if ((foo.bar("baz") and foo.bar("frop") )): print "yes" #: E101 W191 W504 # also ok, but starting to look like LISP if ((foo.bar("baz") and foo.bar("frop"))): print "yes" #: E101 W191 W504 if (a == 2 or b == "abc def ghi" "jkl mno"): return True #: E101 W191 W504 if (a == 2 or b == """abc def ghi jkl mno"""): return True #: W191:2:1 W191:3:1 E101:3:2 if length > options.max_line_length: return options.max_line_length, \ "E501 line too long (%d characters)" % length # #: E101 W191 W191 W504 if os.path.exists(os.path.join(path, PEP8_BIN)): cmd = ([os.path.join(path, PEP8_BIN)] + self._pep8_options(targetfile)) #: W191 ''' multiline string with tab in it''' #: E101 W191 '''multiline string with tabs and spaces ''' #: Okay '''sometimes, you just need to go nuts in a multiline string and allow all sorts of crap like mixed tabs and spaces or trailing whitespace or long long long long long long long long long long long long long long long long long lines ''' # nopep8 #: Okay '''this one will get no warning even though the noqa comment is not immediately after the string ''' + foo # noqa # #: E101 W191 if foo is None and bar is "frop" and \ blah == 'yeah': blah = 'yeahnah' # #: W191 W191 W191 if True: foo( 1, 2) #: W191 W191 W191 W191 W191 def test_keys(self): """areas.json - All regions are accounted for.""" expected = set([ u'Norrbotten', u'V\xe4sterbotten', ]) #: W191 x = [ 'abc' ] #: PyCQA-pycodestyle-0dd4a37/testing/data/W29.py000066400000000000000000000006251465376172200207420ustar00rootroot00000000000000#: Okay # 情 #: W291:1:6 print #: W293:2:1 class Foo(object): bang = 12 #: W291:2:35 '''multiline string with trailing whitespace''' #: W291 W292 noeol x = 1 #: W191 W292 noeol if False: pass # indented with tabs #: W292:1:5 E225:1:2 noeol 1+ 1 #: W292:1:27 E261:1:12 noeol import this # no line feed #: W292:3:22 noeol class Test(object): def __repr__(self): return 'test' PyCQA-pycodestyle-0dd4a37/testing/data/W39.py000066400000000000000000000004151465376172200207400ustar00rootroot00000000000000#: W391:2:1 # The next line is blank #: W391:3:1 # Two additional empty lines #: W292:4:5 W293:3:1 W293:4:1 noeol # The last lines contain space #: Okay '''there is nothing wrong with a multiline string at EOF that happens to have a blank line in it ''' PyCQA-pycodestyle-0dd4a37/testing/data/W60.py000066400000000000000000000005121465376172200207300ustar00rootroot00000000000000#: W605:1:10 regex = '\.png$' #: W605:2:1 regex = ''' \.png$ ''' #: W605:2:6 f( '\_' ) #: W605:4:6 """ multi-line literal with \_ somewhere in the middle """ #: W605:1:3 f"\d" #: Okay regex = r'\.png$' regex = '\\.png$' regex = r''' \.png$ ''' regex = r''' \\.png$ ''' s = '\\' regex = '\w' # noqa regex = ''' \w ''' # noqa PyCQA-pycodestyle-0dd4a37/testing/data/crlf.py000066400000000000000000000000211465376172200212750ustar00rootroot00000000000000'''\ test ''' PyCQA-pycodestyle-0dd4a37/testing/data/latin-1.py000066400000000000000000000002301465376172200216160ustar00rootroot00000000000000# -*- coding: latin-1 -*- # Test non-UTF8 encoding latin1 = ('' '') c = ("w") PyCQA-pycodestyle-0dd4a37/testing/data/noqa.py000066400000000000000000000010351465376172200213130ustar00rootroot00000000000000#: Okay # silence E501 url = 'https://api.github.com/repos/sigmavirus24/Todo.txt-python/branches/master?client_id=xxxxxxxxxxxxxxxxxxxxxxxxxxxx&?client_secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' # noqa # silence E128 from functools import (partial, reduce, wraps, # noqa cmp_to_key) from functools import (partial, reduce, wraps, cmp_to_key) # noqa a = 1 if a == None: # noqa pass # should silence E501 s = f''' loong {y} looooooooooooooong loooooooooooooong looooooooong loooooooong looooooooong {x} ''' # noqa #: PyCQA-pycodestyle-0dd4a37/testing/data/python3.py000066400000000000000000000016351465376172200217670ustar00rootroot00000000000000#!/usr/bin/env python3 from typing import ClassVar, List # Annotated function (Issue #29) def foo(x: int) -> int: return x + 1 # Annotated variables #575 CONST: int = 42 match: int = 42 case: int = 42 class Class: # Camel-caes cls_var: ClassVar[str] for_var: ClassVar[str] while_var: ClassVar[str] def_var: ClassVar[str] if_var: ClassVar[str] elif_var: ClassVar[str] else_var: ClassVar[str] try_var: ClassVar[str] except_var: ClassVar[str] finally_var: ClassVar[str] with_var: ClassVar[str] forVar: ClassVar[str] whileVar: ClassVar[str] defVar: ClassVar[str] ifVar: ClassVar[str] elifVar: ClassVar[str] elseVar: ClassVar[str] tryVar: ClassVar[str] exceptVar: ClassVar[str] finallyVar: ClassVar[str] withVar: ClassVar[str] def m(self): xs: List[int] = [] # Used to trigger W504 def f( x: str = ... ): ... PyCQA-pycodestyle-0dd4a37/testing/data/python310.py000066400000000000000000000005641465376172200221300ustar00rootroot00000000000000#: Okay var, var2 = 1, 2 match (var, var2): case [2, 3]: pass case (1, 2): pass case _: print("Default") #: Okay var = 0, 1, 2 match var: case *_, 1, 2: pass case 0, *_, 2: pass case 0, 1, *_: pass case (*_, 1, 2): pass case (0, *_, 2): pass case (0, 1, *_): pass PyCQA-pycodestyle-0dd4a37/testing/data/python311.py000066400000000000000000000004121465376172200221210ustar00rootroot00000000000000#: Okay try: ... except* OSError as e: pass #: Okay from typing import Generic from typing import TypeVarTuple Ts = TypeVarTuple('Ts') class Shape(Generic[*Ts]): pass def f(*args: *Ts) -> None: ... def g(x: Shape[*Ts]) -> Shape[*Ts]: ... PyCQA-pycodestyle-0dd4a37/testing/data/python312.py000066400000000000000000000007011465376172200221230ustar00rootroot00000000000000#: Okay # https://github.com/python/cpython/issues/90432: fixed in 3.12 def foo(): pas \ def bar(): pass #: Okay # new type aliases type X = int | str type Y[T] = list[T] type Z[T: str] = list[T] #: Okay # new generics def f[T](x: T) -> T: pass def g[T: str, U: int](x: T, y: U) -> dict[T, U]: pass #: Okay # new nested f-strings f'{ thing } {f'{other} {thing}'}' #: E201:1:4 E202:1:17 f'{ an_error_now }' #: Okay f'{x:02x}' PyCQA-pycodestyle-0dd4a37/testing/data/python35.py000066400000000000000000000001141465376172200220430ustar00rootroot00000000000000#: E225 def bar(a, b)->int: pass #: Okay def baz(a, b) -> int: pass PyCQA-pycodestyle-0dd4a37/testing/data/python36.py000066400000000000000000000000441465376172200220460ustar00rootroot00000000000000#: Okay f'{hello}:{world}' f'in{x}' PyCQA-pycodestyle-0dd4a37/testing/data/python38.py000066400000000000000000000013661465376172200220600ustar00rootroot00000000000000#: Okay def f1(a, /, b): pass def f2(a, b, /): pass def f3( a, /, b, ): pass lambda a, /: None #: Okay if x := 1: print(x) if m and (token := m.group(1)): pass stuff = [[y := f(x), x / y] for x in range(5)] #: E225:1:5 if x:= 1: pass #: E225:1:18 if False or (x :=1): pass #: Okay import typing as t __all__: t.List[str] = [] import logging logging.getLogger(__name__) #: E402 import typing as t all_the_things: t.List[str] = [] import logging #: E221:1:5 E222:1:9 E221:3:6 if x := 1: pass if (x := 2): pass #: E223:1:5 E224:1:8 if x := 2: pass #: E221:1:6 E221:1:19 if (x := 1) == (y := 2): pass #: E741 while l := 1: pass #: E741 if (l := 1): pass #: Okay f'{x=}' PyCQA-pycodestyle-0dd4a37/testing/data/python39.py000066400000000000000000000001311465376172200220460ustar00rootroot00000000000000#: W292:1:70 noeol # This line doesn't have a linefeed (in 3.8 this is reported thrice!) PyCQA-pycodestyle-0dd4a37/testing/data/utf-8-bom.py000066400000000000000000000001231465376172200220700ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- hello = 'こんにちわ' # EOF PyCQA-pycodestyle-0dd4a37/testing/data/utf-8.py000066400000000000000000000034271465376172200213270ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Some random text with multi-byte characters (utf-8 encoded) # # Εδώ μάτσο κειμένων τη, τρόπο πιθανό διευθυντές ώρα μη. Νέων απλό π ροή # κι, το επί δεδομένη καθορίζουν. Πάντως ζητήσεις περιβάλλοντος ένα με, # ξέχασε αρπάζεις φαινόμενο όλη. Τρέξει εσφαλμένη χρησιμοποίησέ νέα τι. # πετάνε φακέλους, άρα με διακοπής λαμβάνουν εφαμοργής. Λες κι μειώσει # καθυστερεί. # 79 narrow chars # 01 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3[79] # 78 narrow chars (Na) + 1 wide char (W) # 01 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 情 # 3 narrow chars (Na) + 40 wide chars (W) # 情 情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情 # 3 narrow chars (Na) + 69 wide chars (W) # 情 情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情 # #: E501 W505 # 80 narrow chars (Na) # 01 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 [80] # #: E501 W505 # 78 narrow chars (Na) + 2 wide char (W) # 01 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8情情 # #: E501 W505 # 3 narrow chars (Na) + 77 wide chars (W) # 情 情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情情 # PyCQA-pycodestyle-0dd4a37/testing/support.py000066400000000000000000000017451465376172200211700ustar00rootroot00000000000000from __future__ import annotations import os.path from pycodestyle import BaseReport from pycodestyle import StyleGuide ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) class InMemoryReport(BaseReport): """ Collect the results in memory, without printing anything. """ def __init__(self, options): super().__init__(options) self.in_memory_errors = [] def error(self, line_number, offset, text, check): """ Report an error, according to options. """ code = text[:4] self.in_memory_errors.append(f'{code}:{line_number}:{offset + 1}') return super().error(line_number, offset, text, check) def errors_from_src(src: str) -> list[str]: guide = StyleGuide(select=('E', 'W'), max_doc_length=72) reporter = guide.init_report(InMemoryReport) guide.input_file( filename='in-memory-test-file.py', lines=src.splitlines(True), ) return reporter.in_memory_errors PyCQA-pycodestyle-0dd4a37/tests/000077500000000000000000000000001465376172200165605ustar00rootroot00000000000000PyCQA-pycodestyle-0dd4a37/tests/__init__.py000066400000000000000000000000001465376172200206570ustar00rootroot00000000000000PyCQA-pycodestyle-0dd4a37/tests/test_E101.py000066400000000000000000000010761465376172200206030ustar00rootroot00000000000000"""moved from data files due to 3.12 making this a TokenError""" import sys import unittest from testing.support import errors_from_src class E101Test(unittest.TestCase): def test_E101(self): errors = errors_from_src( 'if True:\n' '\tprint(1) # tabs\n' ' print(2) # spaces\n' ) if sys.version_info >= (3, 12): # pragma: >=3.12 cover self.assertEqual(errors, ['W191:2:1', 'E901:3:28']) else: # pragma: <3.12 cover self.assertEqual(errors, ['W191:2:1', 'E101:3:1']) PyCQA-pycodestyle-0dd4a37/tests/test_E901.py000066400000000000000000000017051465376172200206120ustar00rootroot00000000000000"""moved from data files due to 3.12 changing syntax errors""" import sys import unittest from testing.support import errors_from_src class E901Test(unittest.TestCase): def test_closing_brace(self): errors = errors_from_src('}\n') if sys.version_info < (3, 12): # pragma: <3.12 cover self.assertEqual(errors, ['E901:2:1']) else: # pragma: >=3.12 cover self.assertEqual(errors, []) def test_unclosed_brace(self): src = '''\ if msg: errmsg = msg % progress.get(cr_dbname)) def lasting(self, duration=300): progress = self._progress.setdefault('foo', {} ''' errors = errors_from_src(src) if sys.version_info < (3, 12): # pragma: <3.12 cover expected = ['E122:4:1', 'E251:5:13', 'E251:5:15'] else: # pragma: >=3.12 cover expected = ['E122:4:1', 'E251:5:13', 'E251:5:15', 'E901:5:1'] # noqa: E501 self.assertEqual(errors, expected) PyCQA-pycodestyle-0dd4a37/tests/test_all.py000066400000000000000000000006271465376172200207460ustar00rootroot00000000000000import os.path import pycodestyle from testing.support import ROOT def test_own_dog_food(): style = pycodestyle.StyleGuide(select='E,W', quiet=True) files = [pycodestyle.__file__, __file__, os.path.join(ROOT, 'setup.py')] report = style.init_report(pycodestyle.StandardReport) report = style.check_files(files) assert list(report.messages) == ['W504'], f'Failures: {report.messages}' PyCQA-pycodestyle-0dd4a37/tests/test_api.py000066400000000000000000000412171465376172200207470ustar00rootroot00000000000000import io import os.path import shlex import sys import unittest import pycodestyle from testing.support import ROOT E11 = os.path.join(ROOT, 'testing', 'data', 'E11.py') class DummyChecker: def __init__(self, tree, filename): pass def run(self): if False: yield class APITestCase(unittest.TestCase): """Test the public methods.""" def setUp(self): self._saved_stdout = sys.stdout self._saved_stderr = sys.stderr self._saved_checks = pycodestyle._checks sys.stdout = io.StringIO() sys.stderr = io.StringIO() pycodestyle._checks = { k: {f: (vals[0][:], vals[1]) for (f, vals) in v.items()} for k, v in self._saved_checks.items() } def tearDown(self): sys.stdout = self._saved_stdout sys.stderr = self._saved_stderr pycodestyle._checks = self._saved_checks def reset(self): sys.stdout.seek(0) sys.stdout.truncate() sys.stderr.seek(0) sys.stderr.truncate() def test_register_physical_check(self): def check_dummy(physical_line, line_number): raise NotImplementedError pycodestyle.register_check(check_dummy, ['Z001']) self.assertTrue(check_dummy in pycodestyle._checks['physical_line']) codes, args = pycodestyle._checks['physical_line'][check_dummy] self.assertTrue('Z001' in codes) self.assertEqual(args, ['physical_line', 'line_number']) options = pycodestyle.StyleGuide().options functions = [func for _, func, _ in options.physical_checks] self.assertIn(check_dummy, functions) def test_register_logical_check(self): def check_dummy(logical_line, tokens): raise NotImplementedError pycodestyle.register_check(check_dummy, ['Z401']) self.assertTrue(check_dummy in pycodestyle._checks['logical_line']) codes, args = pycodestyle._checks['logical_line'][check_dummy] self.assertTrue('Z401' in codes) self.assertEqual(args, ['logical_line', 'tokens']) pycodestyle.register_check(check_dummy, []) pycodestyle.register_check(check_dummy, ['Z402', 'Z403']) codes, args = pycodestyle._checks['logical_line'][check_dummy] self.assertEqual(codes, ['Z401', 'Z402', 'Z403']) self.assertEqual(args, ['logical_line', 'tokens']) options = pycodestyle.StyleGuide().options functions = [func for _, func, _ in options.logical_checks] self.assertIn(check_dummy, functions) def test_register_ast_check(self): pycodestyle.register_check(DummyChecker, ['Z701']) self.assertTrue(DummyChecker in pycodestyle._checks['tree']) codes, args = pycodestyle._checks['tree'][DummyChecker] self.assertTrue('Z701' in codes) self.assertTrue(args is None) options = pycodestyle.StyleGuide().options classes = [cls for _, cls, _ in options.ast_checks] self.assertIn(DummyChecker, classes) def test_register_invalid_check(self): class InvalidChecker(DummyChecker): def __init__(self, filename): raise NotImplementedError def check_dummy(logical, tokens): raise NotImplementedError pycodestyle.register_check(InvalidChecker, ['Z741']) pycodestyle.register_check(check_dummy, ['Z441']) for checkers in pycodestyle._checks.values(): self.assertTrue(DummyChecker not in checkers) self.assertTrue(check_dummy not in checkers) self.assertRaises(TypeError, pycodestyle.register_check) def test_styleguide(self): report = pycodestyle.StyleGuide().check_files() self.assertEqual(report.total_errors, 0) self.assertFalse(sys.stdout.getvalue()) self.assertFalse(sys.stderr.getvalue()) self.reset() report = pycodestyle.StyleGuide().check_files(['missing-file']) stdout = sys.stdout.getvalue().splitlines() self.assertEqual(len(stdout), report.total_errors) self.assertEqual(report.total_errors, 1) # < 3.3 returns IOError; >= 3.3 returns FileNotFoundError assert stdout[0].startswith("missing-file:1:1: E902 ") self.assertFalse(sys.stderr.getvalue()) self.reset() report = pycodestyle.StyleGuide().check_files([E11]) stdout = sys.stdout.getvalue().splitlines() self.assertEqual(len(stdout), report.total_errors) self.assertEqual(report.total_errors, 24) self.assertFalse(sys.stderr.getvalue()) self.reset() # Passing the paths in the constructor gives same result report = pycodestyle.StyleGuide(paths=[E11]).check_files() stdout = sys.stdout.getvalue().splitlines() self.assertEqual(len(stdout), report.total_errors) self.assertEqual(report.total_errors, 24) self.assertFalse(sys.stderr.getvalue()) self.reset() def test_styleguide_options(self): # Instantiate a simple checker pep8style = pycodestyle.StyleGuide(paths=[E11]) # Check style's attributes self.assertEqual(pep8style.checker_class, pycodestyle.Checker) self.assertEqual(pep8style.paths, [E11]) self.assertEqual(pep8style.runner, pep8style.input_file) self.assertEqual(pep8style.options.ignore_code, pep8style.ignore_code) self.assertEqual(pep8style.options.paths, pep8style.paths) # Check unset options for o in ('benchmark', 'config', 'count', 'diff', 'quiet', 'show_pep8', 'show_source', 'statistics', 'verbose'): oval = getattr(pep8style.options, o) self.assertTrue(oval in (None, False), msg=f'{o} = {oval!r}') # Check default options self.assertTrue(pep8style.options.repeat) self.assertEqual(pep8style.options.benchmark_keys, ['directories', 'files', 'logical lines', 'physical lines']) self.assertEqual(pep8style.options.exclude, ['.svn', 'CVS', '.bzr', '.hg', '.git', '__pycache__', '.tox']) self.assertEqual(pep8style.options.filename, ['*.py']) self.assertEqual(pep8style.options.format, 'default') self.assertEqual(pep8style.options.select, ()) self.assertEqual(pep8style.options.ignore, ('E226', 'E24', 'W504')) self.assertEqual(pep8style.options.max_line_length, 79) def test_styleguide_ignore_code(self): def parse_argv(argstring): _saved_argv = sys.argv sys.argv = shlex.split('pycodestyle %s /dev/null' % argstring) try: return pycodestyle.StyleGuide(parse_argv=True) finally: sys.argv = _saved_argv options = parse_argv('').options self.assertEqual(options.select, ()) self.assertEqual( options.ignore, ('E121', 'E123', 'E126', 'E226', 'E24', 'E704', 'W503', 'W504') ) options = parse_argv('--ignore E,W').options self.assertEqual(options.select, ()) self.assertEqual(options.ignore, ('E', 'W')) options = parse_argv('--select E,W').options self.assertEqual(options.select, ('E', 'W')) self.assertEqual(options.ignore, ('',)) options = parse_argv('--select E --ignore E24').options self.assertEqual(options.select, ('E',)) self.assertEqual(options.ignore, ('',)) options = parse_argv('--ignore E --select E24').options self.assertEqual(options.select, ('E24',)) self.assertEqual(options.ignore, ('',)) options = parse_argv('--ignore W --select E24').options self.assertEqual(options.select, ('E24',)) self.assertEqual(options.ignore, ('',)) options = parse_argv('--max-doc-length=72').options self.assertEqual(options.max_doc_length, 72) options = parse_argv('').options self.assertEqual(options.max_doc_length, None) pep8style = pycodestyle.StyleGuide(paths=[E11]) self.assertFalse(pep8style.ignore_code('E112')) self.assertFalse(pep8style.ignore_code('W191')) self.assertTrue(pep8style.ignore_code('E241')) pep8style = pycodestyle.StyleGuide(select='E', paths=[E11]) self.assertFalse(pep8style.ignore_code('E112')) self.assertTrue(pep8style.ignore_code('W191')) self.assertFalse(pep8style.ignore_code('E241')) pep8style = pycodestyle.StyleGuide(select='W', paths=[E11]) self.assertTrue(pep8style.ignore_code('E112')) self.assertFalse(pep8style.ignore_code('W191')) self.assertTrue(pep8style.ignore_code('E241')) pep8style = pycodestyle.StyleGuide(select=('F401',), paths=[E11]) self.assertEqual(pep8style.options.select, ('F401',)) self.assertEqual(pep8style.options.ignore, ('',)) self.assertFalse(pep8style.ignore_code('F')) self.assertFalse(pep8style.ignore_code('F401')) self.assertTrue(pep8style.ignore_code('F402')) def test_styleguide_excluded(self): pep8style = pycodestyle.StyleGuide(paths=[E11]) self.assertFalse(pep8style.excluded('./foo/bar')) self.assertFalse(pep8style.excluded('./foo/bar/main.py')) self.assertTrue(pep8style.excluded('./CVS')) self.assertTrue(pep8style.excluded('./.tox')) self.assertTrue(pep8style.excluded('./subdir/CVS')) self.assertTrue(pep8style.excluded('__pycache__')) self.assertTrue(pep8style.excluded('./__pycache__')) self.assertTrue(pep8style.excluded('subdir/__pycache__')) self.assertFalse(pep8style.excluded('draftCVS')) self.assertFalse(pep8style.excluded('./CVSoup')) self.assertFalse(pep8style.excluded('./CVS/subdir')) def test_styleguide_checks(self): pep8style = pycodestyle.StyleGuide(paths=[E11]) # Default lists of checkers self.assertTrue(len(pep8style.options.physical_checks) > 4) self.assertTrue(len(pep8style.options.logical_checks) > 10) self.assertEqual(len(pep8style.options.ast_checks), 0) # Sanity check for name, check, args in pep8style.options.physical_checks: self.assertEqual(check.__name__, name) self.assertEqual(args[0], 'physical_line') for name, check, args in pep8style.options.logical_checks: self.assertEqual(check.__name__, name) self.assertEqual(args[0], 'logical_line') # Do run E11 checks options = pycodestyle.StyleGuide().options functions = [func for _, func, _ in options.logical_checks] self.assertIn(pycodestyle.indentation, functions) options = pycodestyle.StyleGuide(select=['E']).options functions = [func for _, func, _ in options.logical_checks] self.assertIn(pycodestyle.indentation, functions) options = pycodestyle.StyleGuide(ignore=['W']).options functions = [func for _, func, _ in options.logical_checks] self.assertIn(pycodestyle.indentation, functions) options = pycodestyle.StyleGuide(ignore=['E12']).options functions = [func for _, func, _ in options.logical_checks] self.assertIn(pycodestyle.indentation, functions) # Do not run E11 checks options = pycodestyle.StyleGuide(select=['W']).options functions = [func for _, func, _ in options.logical_checks] self.assertNotIn(pycodestyle.indentation, functions) options = pycodestyle.StyleGuide(ignore=['E']).options functions = [func for _, func, _ in options.logical_checks] self.assertNotIn(pycodestyle.indentation, functions) options = pycodestyle.StyleGuide(ignore=['E11']).options functions = [func for _, func, _ in options.logical_checks] self.assertNotIn(pycodestyle.indentation, functions) def test_styleguide_init_report(self): style = pycodestyle.StyleGuide(paths=[E11]) standard_report = pycodestyle.StandardReport self.assertEqual(style.options.reporter, standard_report) self.assertEqual(type(style.options.report), standard_report) class MinorityReport(pycodestyle.BaseReport): pass report = style.init_report(MinorityReport) self.assertEqual(style.options.report, report) self.assertEqual(type(report), MinorityReport) style = pycodestyle.StyleGuide(paths=[E11], reporter=MinorityReport) self.assertEqual(type(style.options.report), MinorityReport) self.assertEqual(style.options.reporter, MinorityReport) def test_styleguide_check_files(self): pep8style = pycodestyle.StyleGuide(paths=[E11]) report = pep8style.check_files() self.assertTrue(report.total_errors) self.assertRaises(TypeError, pep8style.check_files, 42) # < 3.3 raises TypeError; >= 3.3 raises AttributeError self.assertRaises(Exception, pep8style.check_files, [42]) def test_check_nullbytes(self): pycodestyle.register_check(DummyChecker, ['Z701']) pep8style = pycodestyle.StyleGuide() count_errors = pep8style.input_file('stdin', lines=['\x00\n']) stdout = sys.stdout.getvalue() if sys.version_info < (3, 11, 4): # pragma: <3.11 cover expected = ["stdin:1:1: E901 ValueError: source code string cannot contain null bytes"] # noqa: E501 elif sys.version_info < (3, 12): # pragma: <3.12 cover # pragma: >=3.11 cover # noqa: E501 expected = ["stdin:1:1: E901 SyntaxError: source code string cannot contain null bytes"] # noqa: E501 else: # pragma: >=3.12 cover expected = [ "stdin:1:1: E901 SyntaxError: source code string cannot contain null bytes", # noqa: E501 "stdin:1:1: E901 TokenError: source code cannot contain null bytes", # noqa: E501 ] self.assertEqual(stdout.splitlines(), expected) self.assertFalse(sys.stderr.getvalue()) self.assertEqual(count_errors, len(expected)) def test_styleguide_unmatched_triple_quotes(self): pycodestyle.register_check(DummyChecker, ['Z701']) lines = [ 'def foo():\n', ' """test docstring""\'\n', ] pep8style = pycodestyle.StyleGuide() pep8style.input_file('stdin', lines=lines) stdout = sys.stdout.getvalue() if sys.version_info < (3, 10): # pragma: <3.10 cover expected = [ 'stdin:2:5: E901 TokenError: EOF in multi-line string', 'stdin:2:26: E901 SyntaxError: EOF while scanning triple-quoted string literal', # noqa: E501 ] elif sys.version_info < (3, 12): # pragma: >=3.10 cover # pragma: <3.12 cover # noqa: E501 expected = [ 'stdin:2:5: E901 TokenError: EOF in multi-line string', 'stdin:2:6: E901 SyntaxError: unterminated triple-quoted string literal (detected at line 2)', # noqa: E501 ] else: # pragma: >=3.12 cover expected = [ 'stdin:2:6: E901 SyntaxError: unterminated triple-quoted string literal (detected at line 2)', # noqa: E501 'stdin:2:6: E901 TokenError: EOF in multi-line string', ] self.assertEqual(stdout.splitlines(), expected) def test_styleguides_other_indent_size(self): pycodestyle.register_check(DummyChecker, ['Z701']) lines = [ 'def foo():\n', ' pass\n', '\n', '\n', 'def foo_correct():\n', ' pass\n', '\n', '\n', 'def bar():\n', ' [1, 2, 3,\n', ' 4, 5, 6,\n', ' ]\n', '\n', '\n', 'if (1 in [1, 2, 3]\n', ' and bool(0) is False\n', ' and bool(1) is True):\n', ' pass\n' ] pep8style = pycodestyle.StyleGuide() pep8style.options.indent_size = 3 count_errors = pep8style.input_file('stdin', lines=lines) stdout = sys.stdout.getvalue() self.assertEqual(count_errors, 4) expected = ( 'stdin:2:5: ' 'E111 indentation is not a multiple of 3' ) self.assertTrue(expected in stdout) expected = ( 'stdin:11:6: ' 'E127 continuation line over-indented for visual indent' ) self.assertTrue(expected in stdout) expected = ( 'stdin:12:6: ' 'E124 closing bracket does not match visual indentation' ) self.assertTrue(expected in stdout) expected = ( 'stdin:17:6: ' 'E127 continuation line over-indented for visual indent' ) self.assertTrue(expected in stdout) PyCQA-pycodestyle-0dd4a37/tests/test_blank_lines.py000066400000000000000000000252131465376172200224550ustar00rootroot00000000000000""" Tests for the blank_lines checker. It uses dedicated assertions which work with TestReport. """ import unittest import pycodestyle from testing.support import errors_from_src class BlankLinesTestCase(unittest.TestCase): """ Common code for running blank_lines tests. """ def assertNoErrors(self, actual): """ Check that the actual result from the checker has no errors. """ self.assertEqual([], actual) class TestBlankLinesDefault(BlankLinesTestCase): """ Tests for default blank with 2 blank lines for top level and 1 blank line for methods. """ def test_initial_no_blank(self): """ It will accept no blank lines at the start of the file. """ result = errors_from_src("""def some_function(): pass """) self.assertNoErrors(result) def test_initial_lines_one_blank(self): """ It will accept 1 blank lines before the first line of actual code, even if in other places it asks for 2 """ result = errors_from_src(""" def some_function(): pass """) self.assertNoErrors(result) def test_initial_lines_two_blanks(self): """ It will accept 2 blank lines before the first line of actual code, as normal. """ result = errors_from_src(""" def some_function(): pass """) self.assertNoErrors(result) def test_method_less_blank_lines(self): """ It will trigger an error when less than 1 blank lin is found before method definitions. """ result = errors_from_src("""# First comment line. class X: def a(): pass def b(): pass """) self.assertEqual([ 'E301:6:5', # b() call ], result) def test_method_less_blank_lines_comment(self): """ It will trigger an error when less than 1 blank lin is found before method definition, ignoring comments. """ result = errors_from_src("""# First comment line. class X: def a(): pass # A comment will not make it better. def b(): pass """) self.assertEqual([ 'E301:7:5', # b() call ], result) def test_top_level_fewer_blank_lines(self): """ It will trigger an error when less 2 blank lines are found before top level definitions. """ result = errors_from_src("""# First comment line. # Second line of comment. def some_function(): pass async def another_function(): pass def this_one_is_good(): pass class SomeCloseClass(object): pass async def this_async_is_good(): pass class AFarEnoughClass(object): pass """) self.assertEqual([ 'E302:7:1', # another_function 'E302:14:1', # SomeCloseClass ], result) def test_top_level_more_blank_lines(self): """ It will trigger an error when more 2 blank lines are found before top level definitions. """ result = errors_from_src("""# First comment line. # Second line of comment. def some_function(): pass def this_one_is_good(): pass class SomeFarClass(object): pass class AFarEnoughClass(object): pass """) self.assertEqual([ 'E303:6:1', # some_function 'E303:15:1', # SomeFarClass ], result) def test_method_more_blank_lines(self): """ It will trigger an error when more than 1 blank line is found before method definition """ result = errors_from_src("""# First comment line. class SomeCloseClass(object): def oneMethod(self): pass def anotherMethod(self): pass def methodOK(self): pass def veryFar(self): pass """) self.assertEqual([ 'E303:7:5', # oneMethod 'E303:11:5', # anotherMethod 'E303:19:5', # veryFar ], result) def test_initial_lines_more_blank(self): """ It will trigger an error for more than 2 blank lines before the first line of actual code. """ result = errors_from_src(""" def some_function(): pass """) self.assertEqual(['E303:4:1'], result) def test_blank_line_between_decorator(self): """ It will trigger an error when the decorator is followed by a blank line. """ result = errors_from_src("""# First line. @some_decorator def some_function(): pass class SomeClass(object): @method_decorator def some_method(self): pass """) self.assertEqual(['E304:6:1', 'E304:14:5'], result) def test_blank_line_decorator(self): """ It will accept the decorators which are adjacent to the function and method definition. """ result = errors_from_src("""# First line. @another_decorator @some_decorator def some_function(): pass class SomeClass(object): @method_decorator def some_method(self): pass """) self.assertNoErrors(result) def test_top_level_fewer_follow_lines(self): """ It will trigger an error when less than 2 blank lines are found between a top level definitions and other top level code. """ result = errors_from_src(""" def a(): print('Something') a() """) self.assertEqual([ 'E305:5:1', # a call ], result) def test_top_level_fewer_follow_lines_comments(self): """ It will trigger an error when less than 2 blank lines are found between a top level definitions and other top level code, even if we have comments before """ result = errors_from_src(""" def a(): print('Something') # comment # another comment # With comment still needs 2 spaces before, # as comments are ignored. a() """) self.assertEqual([ 'E305:11:1', # a call ], result) def test_top_level_good_follow_lines(self): """ It not trigger an error when 2 blank lines are found between a top level definitions and other top level code. """ result = errors_from_src(""" def a(): print('Something') # Some comments in other parts. # More comments. # With the right spaces, # It will work, even when we have comments. a() """) self.assertNoErrors(result) def test_method_fewer_follow_lines(self): """ It will trigger an error when less than 1 blank line is found between a method and previous definitions. """ result = errors_from_src(""" def a(): x = 1 def b(): pass """) self.assertEqual([ 'E306:4:5', # b() call ], result) def test_method_nested_fewer_follow_lines(self): """ It will trigger an error when less than 1 blank line is found between a method and previous definitions, even when nested. """ result = errors_from_src(""" def a(): x = 2 def b(): x = 1 def c(): pass """) self.assertEqual([ 'E306:7:9', # c() call ], result) def test_method_nested_less_class(self): """ It will trigger an error when less than 1 blank line is found between a method and previous definitions, even when used to define a class. """ result = errors_from_src(""" def a(): x = 1 class C: pass """) self.assertEqual([ 'E306:4:5', # class C definition. ], result) def test_method_nested_ok(self): """ Will not trigger an error when 1 blank line is found found between a method and previous definitions, even when nested. """ result = errors_from_src(""" def a(): x = 2 def b(): x = 1 def c(): pass class C: pass """) self.assertNoErrors(result) class TestBlankLinesTwisted(BlankLinesTestCase): """ Tests for blank_lines with 3 blank lines for top level and 2 blank line for methods as used by the Twisted coding style. """ def setUp(self): self._original_lines_config = pycodestyle.BLANK_LINES_CONFIG.copy() pycodestyle.BLANK_LINES_CONFIG['top_level'] = 3 pycodestyle.BLANK_LINES_CONFIG['method'] = 2 def tearDown(self): pycodestyle.BLANK_LINES_CONFIG = self._original_lines_config def test_initial_lines_one_blanks(self): """ It will accept less than 3 blank lines before the first line of actual code. """ result = errors_from_src(""" def some_function(): pass """) self.assertNoErrors(result) def test_initial_lines_tree_blanks(self): """ It will accept 3 blank lines before the first line of actual code, as normal. """ result = errors_from_src(""" def some_function(): pass """) self.assertNoErrors(result) def test_top_level_fewer_blank_lines(self): """ It will trigger an error when less 3 blank lines are found before top level definitions. """ result = errors_from_src("""# First comment line. # Second line of comment. def some_function(): pass async def another_function(): pass def this_one_is_good(): pass class SomeCloseClass(object): pass async def this_async_is_good(): pass class AFarEnoughClass(object): pass """) self.assertEqual([ 'E302:9:1', # another_function 'E302:17:1', # SomeCloseClass ], result) def test_top_level_more_blank_lines(self): """ It will trigger an error when more 2 blank lines are found before top level definitions. """ result = errors_from_src("""# First comment line. # Second line of comment. def some_function(): pass def this_one_is_good(): pass class SomeVeryFarClass(object): pass class AFarEnoughClass(object): pass """) self.assertEqual([ 'E303:7:1', # some_function 'E303:18:1', # SomeVeryFarClass ], result) def test_the_right_blanks(self): """ It will accept 3 blank for top level and 2 for nested. """ result = errors_from_src(""" def some_function(): pass # With comments. some_other = code_here class SomeClass: ''' Docstring here. ''' def some_method(): pass def another_method(): pass # More methods. def another_method_with_comment(): pass @decorator def another_method_with_comment(): pass """) self.assertNoErrors(result) PyCQA-pycodestyle-0dd4a37/tests/test_data.py000066400000000000000000000052311465376172200211030ustar00rootroot00000000000000from __future__ import annotations import collections import os.path import re import sys import pytest import pycodestyle from testing.support import errors_from_src from testing.support import ROOT PY_RE = re.compile(r'^python(\d)(\d*)\.py$') CASE_RE = re.compile('^(#:.*\n)', re.MULTILINE) def _nsort(items: list[str]) -> list[str]: return sorted( items, key=lambda s: [ int(part) if part.isdigit() else part.lower() for part in re.split(r'(\d+)', s) ], ) def get_tests(): ret = [] for fname in _nsort(os.listdir(os.path.join(ROOT, 'testing', 'data'))): match = PY_RE.match(fname) if match is not None: major, minor = int(match[1]), int(match[2] or '0') mark = pytest.mark.skipif( sys.version_info < (major, minor), reason=f'requires Python {major}.{minor}', ) else: mark = () fname = os.path.join('testing', 'data', fname) fname_full = os.path.join(ROOT, fname) src = ''.join(pycodestyle.readlines(fname_full)) line = 1 parts_it = iter(CASE_RE.split(src)) # the first case will not have a comment for it s = next(parts_it) if s.strip(): id_s = f'{fname}:{line}' ret.append(pytest.param('#: Okay', s, id=id_s, marks=mark)) line += s.count('\n') for comment, s in zip(parts_it, parts_it): if s.strip(): id_s = f'{fname}:{line}' ret.append(pytest.param(comment, s, id=id_s, marks=mark)) line += s.count('\n') + 1 assert ret return ret @pytest.mark.parametrize(('case', 's'), get_tests()) def test(case, s): codes = collections.Counter() exact = collections.Counter() assert case.startswith('#:') for code in case[2:].strip().split(): if code == 'Okay': continue elif code == 'noeol': s = s.rstrip('\n') elif ':' in code: exact[code] += 1 else: codes[code] += 1 unexpected = collections.Counter() for code in errors_from_src(s): if exact[code]: exact[code] -= 1 elif codes[code[:4]]: codes[code[:4]] -= 1 else: # pragma: no cover unexpected[code] += 1 messages = ( *(f'-{k}\n' for k, v in codes.items() for _ in range(v)), *(f'-{k}\n' for k, v in exact.items() for _ in range(v)), *(f'+{k}\n' for k, v in unexpected.items() for _ in range(v)), ) if messages: # pragma: no cover raise AssertionError(f'unexpected codes!\n{"".join(messages)}') PyCQA-pycodestyle-0dd4a37/tests/test_parser.py000066400000000000000000000023421465376172200214660ustar00rootroot00000000000000import os import tempfile import unittest import pycodestyle def _process_file(contents): with tempfile.NamedTemporaryFile(delete=False) as f: f.write(contents) options, args = pycodestyle.process_options(config_file=f.name) os.remove(f.name) return options, args class ParserTestCase(unittest.TestCase): def test_vanilla_ignore_parsing(self): contents = b""" [pycodestyle] ignore = E226,E24 """ options, args = _process_file(contents) self.assertEqual(options.ignore, ["E226", "E24"]) def test_multiline_ignore_parsing(self): contents = b""" [pycodestyle] ignore = E226, E24 """ options, args = _process_file(contents) self.assertEqual(options.ignore, ["E226", "E24"]) def test_trailing_comma_ignore_parsing(self): contents = b""" [pycodestyle] ignore = E226, """ options, args = _process_file(contents) self.assertEqual(options.ignore, ["E226"]) def test_multiline_trailing_comma_ignore_parsing(self): contents = b""" [pycodestyle] ignore = E226, E24, """ options, args = _process_file(contents) self.assertEqual(options.ignore, ["E226", "E24"]) PyCQA-pycodestyle-0dd4a37/tests/test_pycodestyle.py000066400000000000000000000020671465376172200225420ustar00rootroot00000000000000import io import sys import tokenize import pytest from pycodestyle import Checker from pycodestyle import expand_indent from pycodestyle import mute_string @pytest.mark.parametrize( ('s', 'expected'), ( (' ', 4), ('\t', 8), (' \t', 8), (' \t', 16), ), ) def test_expand_indent(s, expected): assert expand_indent(s) == expected @pytest.mark.parametrize( ('s', 'expected'), ( ('"abc"', '"xxx"'), ("'''abc'''", "'''xxx'''"), ("r'abc'", "r'xxx'"), ), ) def test_mute_string(s, expected): assert mute_string(s) == expected def test_fstring_logical_line(): src = '''\ f'hello {{ {thing} }} world' ''' checker = Checker(lines=src.splitlines()) checker.tokens = list(tokenize.generate_tokens(io.StringIO(src).readline)) checker.build_tokens_line() if sys.version_info >= (3, 12): # pragma: >3.12 cover assert checker.logical_line == "f'xxxxxxxxx{thing}xxxxxxxxx'" else: assert checker.logical_line == "f'xxxxxxxxxxxxxxxxxxxxxxxxx'" PyCQA-pycodestyle-0dd4a37/tests/test_self_doctests.py000066400000000000000000000017171465376172200230400ustar00rootroot00000000000000import re import pytest import pycodestyle from testing.support import errors_from_src SELFTEST_REGEX = re.compile(r'\b(Okay|[EW]\d{3}): (.*)') def get_tests(): ret = [ pytest.param( match[1], match[2], id=f'pycodestyle.py:{f.__code__.co_firstlineno}:{f.__name__}@{i}', ) for group in pycodestyle._checks.values() for f in group if f.__doc__ is not None for i, match in enumerate(SELFTEST_REGEX.finditer(f.__doc__)) ] assert ret return tuple(ret) @pytest.mark.parametrize(('expected', 's'), get_tests()) def test(expected, s): s = '\n'.join((*s.replace(r'\t', '\t').split(r'\n'), '')) errors = errors_from_src(s) if expected == 'Okay': assert errors == [] else: for error in errors: if error.startswith(f'{expected}:'): break else: raise AssertionError(f'expected {expected} from {s!r}') PyCQA-pycodestyle-0dd4a37/tests/test_shell.py000066400000000000000000000173101465376172200213020ustar00rootroot00000000000000import configparser import io import os.path import sys import unittest import pycodestyle from testing.support import ROOT class ShellTestCase(unittest.TestCase): """Test the usual CLI options and output.""" def setUp(self): self._saved_argv = sys.argv self._saved_stdout = sys.stdout self._saved_stderr = sys.stderr self._saved_pconfig = pycodestyle.PROJECT_CONFIG self._saved_cpread = configparser.RawConfigParser._read self._saved_stdin_get_value = pycodestyle.stdin_get_value self._config_filenames = [] self.stdin = '' sys.argv = ['pycodestyle'] sys.stdout = io.StringIO() sys.stderr = io.StringIO() def fake_config_parser_read(cp, fp, filename): self._config_filenames.append(filename) configparser.RawConfigParser._read = fake_config_parser_read pycodestyle.stdin_get_value = self.stdin_get_value def tearDown(self): sys.argv = self._saved_argv sys.stdout = self._saved_stdout sys.stderr = self._saved_stderr pycodestyle.PROJECT_CONFIG = self._saved_pconfig configparser.RawConfigParser._read = self._saved_cpread pycodestyle.stdin_get_value = self._saved_stdin_get_value def stdin_get_value(self): return self.stdin def pycodestyle(self, *args): sys.stdout.seek(0) sys.stdout.truncate() sys.stderr.seek(0) sys.stderr.truncate() sys.argv[1:] = args try: pycodestyle._main() errorcode = None except SystemExit: errorcode = sys.exc_info()[1].code return sys.stdout.getvalue(), sys.stderr.getvalue(), errorcode def test_print_usage(self): stdout, stderr, errcode = self.pycodestyle('--help') self.assertFalse(errcode) self.assertFalse(stderr) self.assertTrue(stdout.startswith( "Usage: pycodestyle [options] input" )) stdout, stderr, errcode = self.pycodestyle('--version') self.assertFalse(errcode) self.assertFalse(stderr) self.assertEqual(stdout.count('\n'), 1) stdout, stderr, errcode = self.pycodestyle('--obfuscated') self.assertEqual(errcode, 2) self.assertEqual(stderr.splitlines(), ["Usage: pycodestyle [options] input ...", "", "pycodestyle: error: no such option: --obfuscated"]) self.assertFalse(stdout) self.assertFalse(self._config_filenames) def test_check_simple(self): E11 = os.path.join(ROOT, 'testing', 'data', 'E11.py') stdout, stderr, errcode = self.pycodestyle(E11) stdout = stdout.splitlines() self.assertEqual(errcode, 1) self.assertFalse(stderr) self.assertEqual(len(stdout), 24) for line, num, col in zip(stdout, (3, 6, 6, 9, 12), (3, 6, 6, 1, 5)): path, x, y, msg = line.rsplit(':', 3) self.assertTrue(path.endswith(E11)) self.assertEqual(x, str(num)) self.assertEqual(y, str(col)) self.assertTrue(msg.startswith(' E11')) # Config file read from the pycodestyle's setup.cfg config_filenames = [os.path.basename(p) for p in self._config_filenames] self.assertTrue('setup.cfg' in config_filenames) def test_check_stdin(self): pycodestyle.PROJECT_CONFIG = () stdout, stderr, errcode = self.pycodestyle('-') self.assertFalse(errcode) self.assertFalse(stderr) self.assertFalse(stdout) self.stdin = 'import os, sys\n' stdout, stderr, errcode = self.pycodestyle('-') stdout = stdout.splitlines() self.assertEqual(errcode, 1) self.assertFalse(stderr) self.assertEqual(stdout, ['stdin:1:10: E401 multiple imports on one line']) def test_check_non_existent(self): self.stdin = 'import os, sys\n' stdout, stderr, errcode = self.pycodestyle('fictitious.py') self.assertEqual(errcode, 1) self.assertFalse(stderr) self.assertTrue(stdout.startswith('fictitious.py:1:1: E902 ')) def test_check_noarg(self): # issue #170: do not read stdin by default pycodestyle.PROJECT_CONFIG = () stdout, stderr, errcode = self.pycodestyle() self.assertEqual(errcode, 2) self.assertEqual(stderr.splitlines(), ["Usage: pycodestyle [options] input ...", "", "pycodestyle: error: input not specified"]) self.assertFalse(self._config_filenames) def test_check_diff(self): pycodestyle.PROJECT_CONFIG = () diff_lines = [ "--- testing/data/E11.py 2006-06-01 08:49:50 +0500", "+++ testing/data/E11.py 2008-04-06 17:36:29 +0500", "@@ -2,4 +2,7 @@", " if x > 2:", " print x", "+#: E111", "+if True:", "+ print", " #: E112", " if False:", "", ] self.stdin = '\n'.join(diff_lines) stdout, stderr, errcode = self.pycodestyle('--diff') stdout = stdout.splitlines() self.assertEqual(errcode, 1) self.assertFalse(stderr) for line, num, col in zip(stdout, (3, 6), (3, 6)): path, x, y, msg = line.split(':') self.assertEqual(x, str(num)) self.assertEqual(y, str(col)) self.assertTrue(msg.startswith(' E11')) diff_lines[:2] = [ "--- a/testing/data/E11.py 2006-06-01 08:49 +0400", "+++ b/testing/data/E11.py 2008-04-06 17:36 +0400", ] self.stdin = '\n'.join(diff_lines) stdout, stderr, errcode = self.pycodestyle('--diff') stdout = stdout.splitlines() self.assertEqual(errcode, 1) self.assertFalse(stderr) for line, num, col in zip(stdout, (3, 6), (3, 6)): path, x, y, msg = line.split(':') self.assertEqual(x, str(num)) self.assertEqual(y, str(col)) self.assertTrue(msg.startswith(' E11')) # issue #127, #137: one-line chunks diff_lines[:-1] = [ "diff --git a/testing/data/E11.py b/testing/data/E11.py", "index 8735e25..2ecb529 100644", "--- a/testing/data/E11.py", "+++ b/testing/data/E11.py", "@@ -5,0 +6 @@ if True:", "+ print", ] self.stdin = '\n'.join(diff_lines) stdout, stderr, errcode = self.pycodestyle('--diff') stdout = stdout.splitlines() self.assertEqual(errcode, 1) self.assertFalse(stderr) self.assertTrue('testing/data/E11.py:6:6: E111 ' in stdout[0]) self.assertTrue('testing/data/E11.py:6:6: E117 ' in stdout[1]) # missing '--diff' self.stdin = '\n'.join(diff_lines) stdout, stderr, errcode = self.pycodestyle() self.assertEqual(errcode, 2) self.assertFalse(stdout) self.assertTrue(stderr.startswith( 'Usage: pycodestyle [options] input ...' )) # no matching file in the diff diff_lines[3] = "+++ b/testing/lost/E11.py" self.stdin = '\n'.join(diff_lines) stdout, stderr, errcode = self.pycodestyle('--diff') self.assertFalse(errcode) self.assertFalse(stdout) self.assertFalse(stderr) for index, diff_line in enumerate(diff_lines, 0): diff_line = diff_line.replace('a/', 'i/') diff_lines[index] = diff_line.replace('b/', 'w/') self.stdin = '\n'.join(diff_lines) stdout, stderr, errcode = self.pycodestyle('--diff') self.assertFalse(errcode) self.assertFalse(stdout) self.assertFalse(stderr) PyCQA-pycodestyle-0dd4a37/tests/test_util.py000066400000000000000000000015201465376172200211440ustar00rootroot00000000000000import os import unittest from pycodestyle import normalize_paths class UtilTestCase(unittest.TestCase): def test_normalize_paths(self): self.assertEqual(normalize_paths(''), []) self.assertEqual(normalize_paths([]), []) self.assertEqual(normalize_paths(None), []) self.assertEqual(normalize_paths(['foo']), ['foo']) self.assertEqual(normalize_paths('foo'), ['foo']) self.assertEqual(normalize_paths('foo,bar'), ['foo', 'bar']) self.assertEqual(normalize_paths('foo, bar '), ['foo', 'bar']) self.assertEqual( normalize_paths('/foo/bar,baz/../bat'), [os.path.realpath('/foo/bar'), os.path.abspath('bat')], ) self.assertEqual( normalize_paths(".pyc,\n build/*"), ['.pyc', os.path.abspath('build/*')], ) PyCQA-pycodestyle-0dd4a37/tox.ini000066400000000000000000000011051465376172200167260ustar00rootroot00000000000000# Tox (https://tox.readthedocs.io/en/latest/) is a tool for running tests # in multiple virtualenvs. This configuration file will run the # test suite on all supported python versions. To use it, "pip install tox" # and then run "tox" from this directory. [tox] envlist = py, pypy3 skip_missing_interpreters = True [testenv] deps = covdefaults coverage pytest commands = python -m pycodestyle --statistics pycodestyle.py coverage run -m pytest tests coverage report [testenv:flake8] skip_install = true deps = flake8 commands = flake8 pycodestyle.py