From 875b7454428522c2b716e1b41045452ef9b804c8 Mon Sep 17 00:00:00 2001 From: "Kenneth J. Pronovici" Date: Mon, 25 Aug 2025 11:34:25 -0500 Subject: [PATCH 1/3] Replace Pylint with Ruff linter --- .pylintrc | 502 ------------------------------ .run/commands/pylint.sh | 38 --- .run/commands/ruffautofix.sh | 9 + .run/commands/ruffformat.sh | 2 +- .run/commands/rufflint.sh | 25 ++ .run/tasks/checks.sh | 4 +- .run/tasks/format.sh | 1 + .run/tasks/{pylint.sh => lint.sh} | 6 +- Changelog | 2 +- DEVELOPER.md | 36 +-- docs/conf.py | 3 - poetry.lock | 93 +----- pyproject.toml | 130 +++++++- src/tests/uciparse/test_uci.py | 2 - src/uciparse/cli.py | 1 - src/uciparse/uci.py | 10 +- 16 files changed, 197 insertions(+), 667 deletions(-) delete mode 100644 .pylintrc delete mode 100644 .run/commands/pylint.sh create mode 100644 .run/commands/ruffautofix.sh create mode 100644 .run/commands/rufflint.sh rename .run/tasks/{pylint.sh => lint.sh} (66%) diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index bcd825e..0000000 --- a/.pylintrc +++ /dev/null @@ -1,502 +0,0 @@ -# Pylint configuration file, originally generated from pylint --generate-rcfile -# vim: set ft=conf: - -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. -extension-pkg-whitelist= - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Add files or directories matching the regex patterns to the blacklist. The -# regex matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the -# number of processors available to use. -jobs=1 - -# Control the amount of potential inferred values when inferring a single -# object. This can help the performance when dealing with large functions or -# complex, nested conditions. -limit-inference-results=100 - -# List of plugins (as comma separated values of python module names) to load, -# usually to register additional checkers. -load-plugins= - -# Pickle collected data for later comparisons. -persistent=yes - -# Specify a configuration file. -#rcfile= - -# When enabled, pylint would attempt to guess common misconfiguration and emit -# user-friendly hints instead of false-positive error messages. -suggestion-mode=yes - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once). You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use "--disable=all --enable=classes -# --disable=W". -disable=fixme, - c-extension-no-member, - missing-class-docstring, - missing-module-docstring, - missing-function-docstring, - unused-wildcard-import, - trailing-whitespace, - too-few-public-methods, - no-else-return, - no-else-raise, - duplicate-code, - consider-using-f-string - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable= - - -[REPORTS] - -# Python expression which should return a score less than or equal to 10. You -# have access to the variables 'error', 'warning', 'refactor', and 'convention' -# which contain the number of messages in each category, as well as 'statement' -# which is the total number of statements analyzed. This score is used by the -# global evaluation report (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details. -# KJP: this has been customized to make paths clickable in IntelliJ -msg-template={abspath}:{line}:{column} - {C} - ({symbol}) - {msg} - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio). You can also give a reporter class, e.g. -# mypackage.mymodule.MyReporterClass. -output-format=colorized - -# Tells whether to display a full report or only the messages. -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - -# Complete name of functions that never returns. When checking for -# inconsistent-return-statements if a never returning function is called then -# it will be considered as an explicit return statement and no message will be -# printed. -never-returning-functions=sys.exit - - -[LOGGING] - -# Format style used to check logging format string. `old` means using % -# formatting, `new` is for `{}` formatting,and `fstr` is for f-strings. -logging-format-style=old - -# Logging modules to check that the string format arguments are in logging -# function parameter format. -logging-modules=logging - - -[SPELLING] - -# Limits count of emitted suggestions for spelling mistakes. -max-spelling-suggestions=4 - -# Spelling dictionary name. Available dictionaries: none. To make it work, -# install the python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains the private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to the private dictionary (see the -# --spelling-private-dict-file option) instead of raising a message. -spelling-store-unknown-words=no - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME, - XXX, - TODO - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# Tells whether to warn about missing members when the owner of the attribute -# is inferred to be None. -ignore-none=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis). It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - -# List of decorators that change the signature of a decorated function. -signature-mutators= - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid defining new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_, - _cb - -# A regular expression matching the name of dummy variables (i.e. expected to -# not be used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored|^unused - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore. -ignored-argument-names=_.*|^ignored|^unused - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=132 - -# Maximum number of lines in a module. -max-module-lines=1000 - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=yes - - -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - -# Minimum lines number of a similarity. -min-similarity-lines=4 - - -[BASIC] - -# Naming style matching correct argument names. -argument-naming-style=snake_case - -# Regular expression matching correct argument names. Overrides argument- -# naming-style. -#argument-rgx= - -# Naming style matching correct attribute names. -attr-naming-style=snake_case - -# Regular expression matching correct attribute names. Overrides attr-naming- -# style. -#attr-rgx= - -# Bad variable names which should always be refused, separated by a comma. -bad-names= - -# Naming style matching correct class attribute names. -class-attribute-naming-style=snake_case - -# Regular expression matching correct class attribute names. Overrides class- -# attribute-naming-style. -#class-attribute-rgx= - -# Naming style matching correct class names. -class-naming-style=PascalCase - -# Regular expression matching correct class names. Overrides class-naming- -# style. -#class-rgx= - -# Naming style matching correct constant names. -const-naming-style=UPPER_CASE - -# Regular expression matching correct constant names. Overrides const-naming- -# style. -#const-rgx= - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Naming style matching correct function names. -function-naming-style=snake_case - -# Regular expression matching correct function names. Overrides function- -# naming-style. -#function-rgx= - -# Good variable names which should always be accepted, separated by a comma. -good-names=e, - f, - i, - j, - k, - p, - r, - id, - _ - -# Include a hint for the correct naming format with invalid-name. -include-naming-hint=no - -# Naming style matching correct inline iteration names. -inlinevar-naming-style=any - -# Regular expression matching correct inline iteration names. Overrides -# inlinevar-naming-style. -#inlinevar-rgx= - -# Naming style matching correct method names. -method-naming-style=snake_case - -# Regular expression matching correct method names. Overrides method-naming- -# style. -#method-rgx= - -# Naming style matching correct module names. -module-naming-style=snake_case - -# Regular expression matching correct module names. Overrides module-naming- -# style. -#module-rgx= - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -# These decorators are taken in consideration only for invalid-name. -property-classes=abc.abstractproperty - -# Naming style matching correct variable names. -variable-naming-style=snake_case - -# Regular expression matching correct variable names. Overrides variable- -# naming-style. -#variable-rgx= - - -[STRING] - -# This flag controls whether the implicit-str-concat-in-sequence should -# generate a warning on implicit string concatenation in sequences defined over -# several lines. -check-str-concat-over-line-jumps=no - - -[IMPORTS] - -# List of modules that can be imported at any level, not just the top level -# one. -allow-any-import-level= - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma. -deprecated-modules=optparse,tkinter.tix - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled). -ext-import-graph= - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled). -import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled). -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - -# Couples of modules and preferred modules, separated by a comma. -preferred-modules= - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__ - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected= - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=cls - - -[DESIGN] - -# Maximum number of arguments for function / method. -max-args=10 - -# Maximum number of attributes for a class (see R0902). -max-attributes=10 - -# Maximum number of boolean expressions in an if statement (see R0916). -max-bool-expr=5 - -# Maximum number of branch for function / method body. -max-branches=12 - -# Maximum number of locals for function / method body. -max-locals=15 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body. -max-returns=10 - -# Maximum number of statements in function / method body. -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "BaseException, Exception". -overgeneral-exceptions=builtins.BaseException, - builtins.Exception diff --git a/.run/commands/pylint.sh b/.run/commands/pylint.sh deleted file mode 100644 index 57a9d13..0000000 --- a/.run/commands/pylint.sh +++ /dev/null @@ -1,38 +0,0 @@ -# vim: set ft=bash ts=3 sw=3 expandtab: -# Run the Pylint code checker - -# We generate relative paths in the output to make integration with Pycharm work better - -command_pylint() { - local OPTIND OPTARG option tests template - - echo "Running pylint checks..." - - template="{path}:{line}:{column} - {C} - ({symbol}) - {msg}" - - if [ -f "tests/__init__.py" ]; then - tests="tests" - elif [ -f "src/tests/__init__.py" ]; then - tests="src/tests" - else - tests="" - fi - - while getopts "t" option; do - case $option in - t) - echo "Tests will be ignored" - tests="" # -t means to ignore the tests - ;; - ?) - echo "invalid option -$OPTARG" - exit 1 - ;; - esac - done - - shift $((OPTIND -1)) # pop off the options consumed by getopts - - poetry_run pylint --msg-template="$template" -j 0 "$@" $(ls -d src/*) $tests - echo "done" -} diff --git a/.run/commands/ruffautofix.sh b/.run/commands/ruffautofix.sh new file mode 100644 index 0000000..b16ef84 --- /dev/null +++ b/.run/commands/ruffautofix.sh @@ -0,0 +1,9 @@ +# vim: set ft=bash ts=3 sw=3 expandtab: +# Run the Ruff linter, applying automatic fixes only + +command_ruffautofix() { + echo "Applying Ruff automatic fixes..." + CLICOLOR_FORCE=1 poetry_run ruff check --fix --fix-only + echo "done" +} + diff --git a/.run/commands/ruffformat.sh b/.run/commands/ruffformat.sh index 05e9e07..b6100fd 100644 --- a/.run/commands/ruffformat.sh +++ b/.run/commands/ruffformat.sh @@ -3,7 +3,7 @@ command_ruffformat() { echo "Running Ruff formatter..." - poetry_run ruff format "$@" + CLICOLOR_FORCE=1 poetry_run ruff format "$@" echo "done" } diff --git a/.run/commands/rufflint.sh b/.run/commands/rufflint.sh new file mode 100644 index 0000000..72e0f4e --- /dev/null +++ b/.run/commands/rufflint.sh @@ -0,0 +1,25 @@ +# vim: set ft=bash ts=3 sw=3 expandtab: +# Run the Ruff linter with no automatic fixes + +# The command line below is a bit of a hack. The goal is to generate output +# that's compatible with the PyCharm output filter, which expects this: +# +# $FILE_PATH$:$LINE$ +# +# However, right now ruff generates some extra stuff at the front of the line, +# (" --> "), which needs to be stripped so PyCharm will recognize the pattern. +# In theory it seems like it should be possible to do this entirely within the +# PyCharm output filter, without needing sed, but I haven't been able to make +# it work. +# +# Note that the extra .* in the regex below is needed to handle the ANSI color +# escape sequences, which aren't immediately obvious. +# +# See: https://github.com/astral-sh/ruff/issues/19983 + +command_rufflint() { + echo "Running Ruff linter..." + CLICOLOR_FORCE=1 poetry_run ruff check --no-fix | sed 's/ .*-->.* //' + echo "done" +} + diff --git a/.run/tasks/checks.sh b/.run/tasks/checks.sh index ce741a9..9afa60b 100644 --- a/.run/tasks/checks.sh +++ b/.run/tasks/checks.sh @@ -10,8 +10,8 @@ task_checks() { echo "" run_command ruffformat --check echo "" - run_command mypy + run_command rufflint echo "" - run_command pylint + run_command mypy } diff --git a/.run/tasks/format.sh b/.run/tasks/format.sh index 5cc55a7..a35d929 100644 --- a/.run/tasks/format.sh +++ b/.run/tasks/format.sh @@ -6,5 +6,6 @@ help_format() { task_format() { run_command ruffformat + run_command ruffautofix } diff --git a/.run/tasks/pylint.sh b/.run/tasks/lint.sh similarity index 66% rename from .run/tasks/pylint.sh rename to .run/tasks/lint.sh index af36d1f..6d9a94c 100644 --- a/.run/tasks/pylint.sh +++ b/.run/tasks/lint.sh @@ -1,11 +1,11 @@ # vim: set ft=bash sw=3 ts=3 expandtab: -help_pylint() { +help_lint() { # No help - exists for PyCharm integration echo -n "" } -task_pylint() { - run_command pylint +task_lint() { + run_command rufflint } diff --git a/Changelog b/Changelog index dfd8fef..957462d 100644 --- a/Changelog +++ b/Changelog @@ -7,7 +7,7 @@ Version 0.1.27 unreleased * Add a new `run clean` target to clean up generated data. * Move unit tests from `tests` into `src/tests/uciparse`. * Update the MyPy configuration so we're using latest rules. - * Replace black and isort tools with the Ruff formatter. + * Replace black, isort, and pylint with the Ruff formatter and linter. * Update the jinja2 transitive dependency to address CVE-2025-27516. * Update the requests transitive dependency to address CVE-2024-47081. * Update the urllib3 transitive dependency to address CVE-2025-50181. diff --git a/DEVELOPER.md b/DEVELOPER.md index 8a2dcc6..bf9cf42 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -8,7 +8,7 @@ This code should work equivalently on MacOS, Linux, and Windows. This project uses [Poetry v2](https://python-poetry.org/) to manage Python packaging and dependencies. Most day-to-day tasks (such as running unit tests from the command line) are orchestrated through Poetry. -A coding standard is enforced using [Ruff](https://docs.astral.sh/ruff/) and [Pylint](https://pypi.org/project/pylint/). Python 3 type hinting is validated using [MyPy](https://pypi.org/project/mypy/). +A coding standard is enforced using [Ruff](https://docs.astral.sh/ruff/). Python 3 type hinting is validated using [MyPy](https://pypi.org/project/mypy/). ## Pre-Commit Hooks @@ -187,7 +187,7 @@ Go to the PyCharm settings and find the `uci-parse` project. Under folder. In the **Exclude Files** box, enter the following: ``` -LICENSE;NOTICE;PyPI.md;build;dist;docs/_build;out;poetry.lock;poetry.toml;run;.coverage;.coverage.lcov;.coveragerc;.gitattributes;.github;.gitignore;.htmlcov;.idea;.mypy_cache;.poetry;.pre-commit-config.yaml;.pylintrc;.pytest_cache;.python-version;.readthedocs.yml;.run;.tabignore;.venv +LICENSE;NOTICE;PyPI.md;build;dist;docs/_build;out;poetry.lock;poetry.toml;run;.coverage;.coverage.lcov;.coveragerc;.gitattributes;.github;.gitignore;.htmlcov;.idea;.mypy_cache;.poetry;.pre-commit-config.yaml;.pytest_cache;.python-version;.readthedocs.yml;.ruff_cache;.run;.tabignore;.venv ``` When you're done, click **Ok**. Then, go to the gear icon in the project panel @@ -217,11 +217,11 @@ run configuration before PyCharm will find the right test suite. ### External Tools Optionally, you might want to set up external tools for some of common -developer tasks: code reformatting and the PyLint and MyPy checks. One nice +developer tasks: code reformatting and the Ruff and MyPy checks. One nice advantage of doing this is that you can configure an output filter, which makes -the Pylint and MyPy errors clickable. To set up external tools, go to PyCharm -settings and find **Tools > External Tools**. Add the tools as described -below. +the Ruff linter and MyPy errors clickable. To set up external tools, go to +PyCharm settings and find **Tools > External Tools**. Add the tools as +described below. #### Linux or MacOS @@ -272,23 +272,23 @@ source ~/.bash_profile |Open console for tool outout|_Checked_| |Make console active on message in stdout|_Checked_| |Make console active on message in stderr|_Checked_| -|Output filters|`$FILE_PATH$:$LINE$:$COLUMN$:.*`| +|Output filters|`$FILE_PATH$:$LINE$`| -##### Run Pylint Checks +##### Run Ruff Linter |Field|Value| |-----|-----| -|Name|`Run Pylint Checks`| -|Description|`Run the Pylint code checks`| +|Name|`Run Ruff Linter`| +|Description|`Run the Ruff linter code checks`| |Group|`Developer Tools`| |Program|`$ProjectFileDir$/run`| -|Arguments|`pylint`| +|Arguments|`lint`| |Working directory|`$ProjectFileDir$`| |Synchronize files after execution|_Unchecked_| |Open console for tool outout|_Checked_| |Make console active on message in stdout|_Checked_| |Make console active on message in stderr|_Checked_| -|Output filters|`$FILE_PATH$:$LINE$:$COLUMN.*`| +|Output filters|`$FILE_PATH$:$LINE$`| #### Windows @@ -328,23 +328,23 @@ change the path for `bash.exe`. |Open console for tool outout|_Checked_| |Make console active on message in stdout|_Checked_| |Make console active on message in stderr|_Checked_| -|Output filters|`$FILE_PATH$:$LINE$:$COLUMN$:.*`| +|Output filters|`$FILE_PATH$:$LINE$`| -##### Run Pylint Checks +##### Run Ruff Linter |Field|Value| |-----|-----| -|Name|`Run Pylint Checks`| -|Description|`Run the Pylint code checks`| +|Name|`Run Ruff Linter`| +|Description|`Run the Ruff linter code checks`| |Group|`Developer Tools`| |Program|`powershell.exe`| -|Arguments|`& 'C:\Program Files\Git\bin\bash.exe' -l "./run" pylint | Out-String`| +|Arguments|`& 'C:\Program Files\Git\bin\bash.exe' -l "./run" lint | Out-String`| |Working directory|`$ProjectFileDir$`| |Synchronize files after execution|_Unchecked_| |Open console for tool outout|_Checked_| |Make console active on message in stdout|_Checked_| |Make console active on message in stderr|_Checked_| -|Output filters|`$FILE_PATH$:$LINE$:$COLUMN.*`| +|Output filters|`$FILE_PATH$:$LINE$`| ## Release Process diff --git a/docs/conf.py b/docs/conf.py index 27e6bc3..9fd882e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -# pylint: skip-file -# # UCI Parse documentation build configuration file, based on existing # documentation for Requests (https://github.com/psf/requests). # diff --git a/poetry.lock b/poetry.lock index abf75c7..9543257 100644 --- a/poetry.lock +++ b/poetry.lock @@ -17,14 +17,14 @@ files = [ name = "astroid" version = "3.3.8" description = "An abstract syntax tree for Python with inference support." -optional = false +optional = true python-versions = ">=3.9.0" -groups = ["main", "dev"] +groups = ["main"] +markers = "extra == \"docs\"" files = [ {file = "astroid-3.3.8-py3-none-any.whl", hash = "sha256:187ccc0c248bfbba564826c26f070494f7bc964fd286b6d9fff4420e55de828c"}, {file = "astroid-3.3.8.tar.gz", hash = "sha256:a88c7994f914a4ea8572fac479459f4955eeccc877be3f2d959a33273b0cf40b"}, ] -markers = {main = "extra == \"docs\""} [package.dependencies] typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} @@ -260,22 +260,6 @@ files = [ [package.extras] toml = ["tomli"] -[[package]] -name = "dill" -version = "0.3.9" -description = "serialize all of Python" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, - {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, -] - -[package.extras] -graph = ["objgraph (>=1.7.2)"] -profile = ["gprof2dot (>=2022.7.29)"] - [[package]] name = "distlib" version = "0.3.9" @@ -415,21 +399,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -groups = ["dev"] -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - [[package]] name = "jinja2" version = "3.1.6" @@ -521,18 +490,6 @@ files = [ {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - [[package]] name = "mypy" version = "1.14.1" @@ -698,36 +655,6 @@ files = [ [package.extras] windows-terminal = ["colorama (>=0.4.6)"] -[[package]] -name = "pylint" -version = "3.3.3" -description = "python code static checker" -optional = false -python-versions = ">=3.9.0" -groups = ["dev"] -files = [ - {file = "pylint-3.3.3-py3-none-any.whl", hash = "sha256:26e271a2bc8bce0fc23833805a9076dd9b4d5194e2a02164942cb3cdc37b4183"}, - {file = "pylint-3.3.3.tar.gz", hash = "sha256:07c607523b17e6d16e2ae0d7ef59602e332caa762af64203c24b41c27139f36a"}, -] - -[package.dependencies] -astroid = ">=3.3.8,<=3.4.0-dev0" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = [ - {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, -] -isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" -mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -tomlkit = ">=0.10.1" - -[package.extras] -spelling = ["pyenchant (>=3.2,<4.0)"] -testutils = ["gitpython (>3)"] - [[package]] name = "pytest" version = "8.3.4" @@ -1103,18 +1030,6 @@ files = [ ] markers = {main = "extra == \"docs\" and python_version < \"3.11\"", dev = "python_version < \"3.11\""} -[[package]] -name = "tomlkit" -version = "0.13.2" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, - {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, -] - [[package]] name = "typing-extensions" version = "4.12.2" @@ -1195,4 +1110,4 @@ docs = ["importlib-metadata", "sphinx", "sphinx-autoapi"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4" -content-hash = "c5acbb8b26737b4dffcb0312cea63228c5dc265a8d1c4a96d10027e79c0b8ecb" +content-hash = "160c5eda719b41724a5eb2bea683d2c5a145e1f3813252aa798f47729530aa73" diff --git a/pyproject.toml b/pyproject.toml index dd82194..22ed2e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,7 +59,6 @@ docs = [ pytest = ">=8.0.2,<9.0.0" pytest-testdox = ">=3.1.0,<4.0.0" coverage = ">=7.4.4,<8.0.0" -pylint = ">=3.0.1,<4.0.0" pre-commit = ">=4.0.1,<5.0.0" mypy = ">=1.6.0,<2.0.0" colorama = ">=0.4.6,<1.0.0" @@ -97,6 +96,135 @@ line-ending = "lf" docstring-code-format = true docstring-code-line-length = 80 +[tool.ruff.lint.flake8-tidy-imports] +ban-relative-imports = "all" + +[tool.ruff.lint.flake8-pytest-style] +fixture-parentheses = false +mark-parentheses = false +parametrize-names-type = "csv" +parametrize-values-type = "list" +parametrize-values-row-type = "list" + +# Note: requires TID251 to be selected below +[tool.ruff.lint.flake8-tidy-imports.banned-api] +"conftest".msg = "use 'tests.conftest' instead" +"unittest.TestCase".msg = "use pytest, not unittest" + +[tool.ruff.lint] +select = [ + # Start with all Ruff linter rules, and exclude some specific rules and categories (see below) + "ALL" +] + +ignore = [ + # Ruff-recommended exclusions, not needed because we're using the Ruff formatter + "COM812", # missing-trailing-comma + "COM819", # prohibited-trailing-comma + "D206", # docstring-tab-indentation + "D300", # triple-single-quotes + "E111", # indentation-with-invalid-multiple + "E114", # indentation-with-invalid-multiple-comment + "E117", # over-indented + "E501", # line-too-long + "Q000", # bad-quotes-inline-string + "Q001", # bad-quotes-multiline-string + "Q002", # bad-quotes-docstring + "Q003", # avoidable-escaped-quote + "W191", # tab-indentation + + # Exclusions of entire rule categories, which we don't think are worth enforcing + "C90", # mccabe cyclomatic complexity + "CPY", # flake8-copyright + "D", # pydocstyle + "DJ", # flake8-django + "DOC", # pydoclint + "ERA", # eradicate + "EXE", # flake8-executable + "FIX", # flake8-fixme + "PTH", # flake8-use-pathlib + "TD", # flake8-todo + + # Exclusions of specific rules, which we don't think are worth enforcing + "ANN401", # allow dynamically typed expressions using `typing.Any` + "EM101", # allow raw string messages in exceptions; having to pull out a variable makes code harder to read + "EM102", # allow f-string messages in exceptions; having to pull out a variable makes code harder to read + "FURB140", # don't automatically convert generator expressions to use `itertools.starmap()` + "PGH004", # allow `# noqa` with no error code; sometimes this is needed to prevent unwanted magical auto-fixes + "PLR2004", # allow magic numbers; using a constant doesn't necessarily make a magic number any more legible + "PT011", # allow `pytest.raises` for any exception type; this is often a false-positive that has little benefit + "PT018", # allow `assert` with multiple conditions; ruff suggestions often make the code less legible + "PT019", # allow unused PyTest fixtures; this check has a lot of false positives for `@unittest.mock.patch()` + "S101", # allow use of `assert`; we sometimes use assert as a type hint for MyPy, and do not run with -O + "S404", # allow use of `subprocess`; it's a common way to run shell commands, and this check is overly paranoid + "SIM102", # allow nested `if` clauses; ruff suggestions often make the code less legible + "SIM117", # allow nested `with` clauses; ruff suggestions often make the code less legible + "TRY003", # allow long messages in exceptions; this is often a false-positive that has little benefit + + # Exclusions of specific rules that we want to work toward being compliant with + "ANN001", + "ANN202", + "ANN204", + "ARG001", + "ARG002", + "BLE001", + "C414", + "C420", + "E303", + "E713", + "F841", + "FBT001", + "FBT002", + "FLY002", + "FURB101", + "FURB110", + "FURB118", + "FURB136", + "PERF401", + "PGH003", + "PIE808", + "PLR0911", + "PLR0912", + "PLR0913", + "PLR0917", + "PLR1702", + "PLR5501", + "PLR6201", + "PLR6301", + "PLW1514", + "PT012", + "RET505", + "RET506", + "S311", + "SIM110", + "SIM113", + "T201", + "TC001", + "TC003", + "TID252", + "TRY004", + "TRY201", + "TRY300", + "UP006", + "UP009", + "UP015", + "UP031", + "UP035", + "UP045", + "W291", +] + +[tool.ruff.lint.per-file-ignores] +"src/tests/**/*" = [ + # Exclusions that apply to unit tests only + "ANN", # don't require type annotations in tests + "FBT", # allow use of boolean positional arguments + "PLC1901", # allow comparison to empty string + "PLC2701", # allow imports of private names + "PLR", # don't warn about too many arguments, methods, etc. + "SLF001", # allow access to private members +] + [tool.mypy] files = [ "src" ] pretty = true diff --git a/src/tests/uciparse/test_uci.py b/src/tests/uciparse/test_uci.py index 0e758d1..2148ff1 100644 --- a/src/tests/uciparse/test_uci.py +++ b/src/tests/uciparse/test_uci.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: -# pylint: disable=redefined-outer-name import os from typing import Dict, List @@ -164,7 +163,6 @@ def test_normalized(self): assert UciCommentLine(comment="# comment", indented=True).normalized() == " # comment\n" -# pylint: disable=too-many-public-methods class TestUciFile: """Unit tests for UciFile.""" diff --git a/src/uciparse/cli.py b/src/uciparse/cli.py index 312b807..e6a92e5 100644 --- a/src/uciparse/cli.py +++ b/src/uciparse/cli.py @@ -32,7 +32,6 @@ def parse() -> None: raise SystemExit from e -# pylint: disable=invalid-name def diff() -> None: """Run the ucidiff command.""" parser = argparse.ArgumentParser( diff --git a/src/uciparse/uci.py b/src/uciparse/uci.py index 2a87fcc..250407d 100644 --- a/src/uciparse/uci.py +++ b/src/uciparse/uci.py @@ -188,7 +188,6 @@ _PACKAGE_REGEX = re.compile(r"(^)((([\"'])([a-zA-Z0-9_-]+)(?:\4))|([a-zA-Z0-9_-]+))((\s*)(#.*))?($)") # Matches the remainder of a config line -# pylint: disable=line-too-long _CONFIG_REGEX = re.compile( r"(^)((([\"'])([a-zA-Z0-9_-]+)(?:\4))|([a-zA-Z0-9_-]+))((\s+)((([\"'])([a-zA-Z0-9_-]+)(?:\11))|([a-zA-Z0-9_-]+)))?((\s*)(#.*))?($)" ) @@ -205,7 +204,7 @@ def _contains_single(string: str) -> bool: return match is not None -def _parse_line(lineno: int, line: str) -> Optional[UciLine]: # pylint: disable=unsubscriptable-object +def _parse_line(lineno: int, line: str) -> Optional[UciLine]: """Parse a line, raising UciParseError if it is not valid.""" match = _LINE_REGEX.match(line) if not match: @@ -282,7 +281,7 @@ def _parse_comment(_lineno: int, prefix: str, remainder: str) -> UciCommentLine: return UciCommentLine(comment=comment, indented=indented) -def _serialize_identifier(prefix: str, identifier: Optional[str]) -> str: # pylint: disable=unsubscriptable-object +def _serialize_identifier(prefix: str, identifier: Optional[str]) -> str: """Serialize an identifier, which is never quoted.""" return "%s%s" % (prefix, identifier) if identifier else "" @@ -293,7 +292,7 @@ def _serialize_value(prefix: str, value: str) -> str: return "%s%s%s%s" % (prefix, quote, value, quote) -def _serialize_comment(prefix: str, comment: Optional[str]) -> str: # pylint: disable=unsubscriptable-object +def _serialize_comment(prefix: str, comment: Optional[str]) -> str: """Serialize a comment, with an optional prefix.""" return "%s%s" % (prefix, comment) if comment else "" @@ -399,11 +398,10 @@ def normalized(self) -> List[str]: # We join the lines first and then re-split so we don't end up with lines that have an embedded newline return "".join([line.normalized() for line in self.lines]).splitlines(keepends=True) - # pylint: disable=invalid-name @staticmethod def from_file(path: str) -> UciFile: """Generate a UciFile from a file on disk.""" - with open(path, "r") as fp: # pylint: disable=unspecified-encoding + with open(path, "r") as fp: return UciFile.from_fp(fp) @staticmethod From 149398b681a379c09c447b97c193cc3b75a3ade7 Mon Sep 17 00:00:00 2001 From: "Kenneth J. Pronovici" Date: Mon, 25 Aug 2025 15:50:01 -0500 Subject: [PATCH 2/3] Fix rufflint error handling --- .run/commands/rufflint.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.run/commands/rufflint.sh b/.run/commands/rufflint.sh index 72e0f4e..3b9deaa 100644 --- a/.run/commands/rufflint.sh +++ b/.run/commands/rufflint.sh @@ -19,7 +19,14 @@ command_rufflint() { echo "Running Ruff linter..." - CLICOLOR_FORCE=1 poetry_run ruff check --no-fix | sed 's/ .*-->.* //' + + # normally we would just run the command, but the $() subshell messes with error handling + OUTPUT=$(CLICOLOR_FORCE=1 poetry_run ruff check --no-fix 2>&1) + if [ $? != 0 ]; then + echo "$OUTPUT" | sed 's/ *.*-->.* //' + exit 1 + fi + echo "done" } From ce444e47ea3754d806ea3ef7eb9525cb7a25a703 Mon Sep 17 00:00:00 2001 From: "Kenneth J. Pronovici" Date: Mon, 25 Aug 2025 16:21:13 -0500 Subject: [PATCH 3/3] Adjust rufflint output --- .run/commands/rufflint.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.run/commands/rufflint.sh b/.run/commands/rufflint.sh index 3b9deaa..853d4c9 100644 --- a/.run/commands/rufflint.sh +++ b/.run/commands/rufflint.sh @@ -27,6 +27,7 @@ command_rufflint() { exit 1 fi + echo "$OUTPUT" echo "done" }