diff --git a/plugins/ext_test/pyproject.toml b/plugins/ext_test/pyproject.toml new file mode 100644 index 00000000..715301a9 --- /dev/null +++ b/plugins/ext_test/pyproject.toml @@ -0,0 +1,194 @@ +[build-system] +requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"] + +[tool.mypy] +disallow_incomplete_defs = true +disallow_untyped_calls = true +disallow_untyped_defs = true +exclude = [ + "^examples/", # examples directory + "^noxfile\\.py$", # nox config file + "setup\\.py$", # any files named setup.py + "^tasks\\.py$", # tasks.py invoke config file + "^tests/", # tests directory +] +show_column_numbers = true +show_error_codes = true +show_error_context = true +strict = true +warn_redundant_casts = true +warn_return_any = true +warn_unreachable = true +warn_unused_ignores = false + +[tool.ruff] +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "site-packages", + "venv", +] + +# Same as Black. +line-length = 127 +indent-width = 4 + +# Assume Python 3.13 +target-version = "py313" +output-format = "full" + +[tool.ruff.lint] +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. +# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or +# McCabe complexity (`C901`) by default. +select = [ + # https://beta.ruff.rs/docs/rules + # "A", # flake8-builtins + # "ANN", # flake8-annotations + # "ARG", # flake8-unused-arguments + "ASYNC", # flake8-async + # "B", # flake8-bugbear + # "BLE", # flake8-blind-except + # "C4", # flake8-comprehensions + "C90", # McCabe cyclomatic complexity + # "COM", # flake8-commas + # "D", # pydocstyle + "DJ", # flake8-django + # "DTZ", # flake8-datetimez + "E", # pycodestyle + # "EM", # flake8-errmsg + # "ERA", # eradicate + # "EXE", # flake8-executable + "F", # Pyflakes + "FA", # flake8-future-annotations + # "FBT", # flake8-boolean-trap + "G", # flake8-logging-format + # "I", # isort + "ICN", # flake8-import-conventions + # "INP", # flake8-no-pep420 + "INT", # flake8-gettext + # "ISC", # flake8-implicit-str-concat + # "N", # pep8-naming + "NPY", # NumPy-specific rules + "PD", # pandas-vet + # "PGH", # pygrep-hooks + # "PIE", # flake8-pie + # "PL", # Pylint + # "PT", # flake8-pytest-style + # "PTH", # flake8-use-pathlib + # "PYI", # flake8-pyi + # "RET", # flake8-return + "RSE", # flake8-raise + # "Q", # flake8-quotes + # "RUF", # Ruff-specific rules + # "S", # flake8-bandit + # "SIM", # flake8-simplify + # "SLF", # flake8-self + # "T10", # flake8-debugger + # "T20", # flake8-print + # "TCH", # flake8-type-checking + # "TD", # flake8-todos + # "TID", # flake8-tidy-imports + # "TRY", # tryceratops + # "UP", # pyupgrade + # "W", # pycodestyle + # "YTT", # flake8-2020 +] +ignore = [ + # `ruff rule S101` for a description of that rule + "B904", # Within an `except` clause, raise exceptions with `raise ... from err` -- FIX ME + "B905", # `zip()` without an explicit `strict=` parameter -- FIX ME + "E501", # Line too long + "EM101", # Exception must not use a string literal, assign to variable first + "EXE001", # Shebang is present but file is not executable -- DO NOT FIX + "G004", # Logging statement uses f-string + "PLC1901", # `{}` can be simplified to `{}` as an empty string is falsey + "PLW060", # Using global for `{name}` but no assignment is done -- DO NOT FIX + "PLW2901", # PLW2901: Redefined loop variable -- FIX ME + "PT011", # `pytest.raises(Exception)` is too broad, set the `match` parameter or use a more specific exception + "PT018", # Assertion should be broken down into multiple parts + "S101", # Use of `assert` detected -- DO NOT FIX + "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes -- FIX ME + "SLF001", # Private member accessed: `_Iterator` -- FIX ME + "UP038", # Use `X | Y` in `{}` call instead of `(X, Y)` -- DO NOT FIX +] + +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +mccabe.max-complexity = 49 + +per-file-ignores."cmd2/__init__.py" = [ + "E402", # Module level import not at top of file + "F401", # Unused import +] + +per-file-ignores."docs/conf.py" = [ + "F401", # Unused import +] + +per-file-ignores."examples/override_parser.py" = [ + "E402", # Module level import not at top of file +] + +per-file-ignores."examples/scripts/*.py" = [ + "F821", # Undefined name `app` +] + +per-file-ignores."tests/pyscript/*.py" = [ + "F821", # Undefined name `app` +] + +[tool.ruff.format] +# Like Black, use double quotes for strings. +quote-style = "preserve" + +# Like Black, indent with spaces, rather than tabs. +indent-style = "space" + +# Like Black, respect magic trailing commas. +skip-magic-trailing-comma = false + +# Like Black, automatically detect the appropriate line ending. +line-ending = "auto" + +# Enable auto-formatting of code examples in docstrings. Markdown, +# reStructuredText code/literal blocks and doctests are all supported. +# +# This is currently disabled by default, but it is planned for this +# to be opt-out in the future. +docstring-code-format = false + +# Set the line length limit used when formatting code snippets in +# docstrings. +# +# This only has an effect when the `docstring-code-format` setting is +# enabled. +docstring-code-line-length = "dynamic" diff --git a/plugins/ext_test/tasks.py b/plugins/ext_test/tasks.py index 64011786..f51b14bf 100644 --- a/plugins/ext_test/tasks.py +++ b/plugins/ext_test/tasks.py @@ -82,8 +82,7 @@ def pytest_clean(context): def mypy(context): """Run mypy optional static type checker""" with context.cd(TASK_ROOT_STR): - context.run("mypy cmd2_ext_test") - namespace.add_task(mypy) + context.run("mypy .") namespace.add_task(mypy) @@ -194,15 +193,22 @@ def pypi_test(context): namespace.add_task(pypi_test) -# Flake8 - linter and tool for style guide enforcement and linting +# ruff fast linter @invoke.task -def flake8(context): - """Run flake8 linter and tool for style guide enforcement""" +def lint(context): + """Run ruff fast linter""" with context.cd(TASK_ROOT_STR): - context.run( - "flake8 --ignore=E252,W503 --max-complexity=26 --max-line-length=127 --show-source --statistics " - "--exclude=.git,__pycache__,.tox,.nox,.eggs,*.egg,.venv,.idea,.pytest_cache,.vscode,build,dist,htmlcov" - ) + context.run("ruff check") -namespace.add_task(flake8) +namespace.add_task(lint) + + +@invoke.task +def format(context): + """Run ruff format --check""" + with context.cd(TASK_ROOT_STR): + context.run("ruff format --check") + + +namespace.add_task(format) diff --git a/plugins/tasks.py b/plugins/tasks.py index 4ef83255..2717ecf4 100644 --- a/plugins/tasks.py +++ b/plugins/tasks.py @@ -9,6 +9,8 @@ - setuptools >= 39.1.0 """ +import pathlib + import invoke from plugins.ext_test import ( @@ -32,6 +34,9 @@ # ##### +TASK_ROOT = pathlib.Path(__file__).resolve().parent +TASK_ROOT_STR = str(TASK_ROOT) + @invoke.task(pre=[ext_test_tasks.pytest]) @invoke.task() @@ -130,11 +135,22 @@ def wheel(_): namespace.add_task(wheel) -# Flake8 - linter and tool for style guide enforcement and linting -@invoke.task(pre=[ext_test_tasks.flake8]) -def flake8(_): - """Run flake8 linter and tool for style guide enforcement""" - pass +# ruff linter +@invoke.task(pre=[ext_test_tasks.lint]) +def lint(context): + with context.cd(TASK_ROOT_STR): + context.run("ruff check") + + +namespace.add_task(lint) + + +# ruff formatter +@invoke.task(pre=[ext_test_tasks.format]) +def format(context): + """Run formatter""" + with context.cd(TASK_ROOT_STR): + context.run("ruff format --check") -namespace.add_task(flake8) +namespace.add_task(format) diff --git a/pyproject.toml b/pyproject.toml index 0acf8697..189d5292 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,29 @@ ignore-path = [ max-line-length = 120 verbose = 0 +[tool.mypy] +disallow_incomplete_defs = true +disallow_untyped_calls = true +disallow_untyped_defs = true +exclude = [ + "^docs/", # docs directory + "^examples/", # examples directory + "^plugins/*", # plugins directory + "^noxfile\\.py$", # nox config file + "setup\\.py$", # any files named setup.py + "^tasks\\.py$", # tasks.py invoke config file + "^tests/", # tests directory + "^tests_isolated/" # tests_isolated directory +] +show_column_numbers = true +show_error_codes = true +show_error_context = true +strict = true +warn_redundant_casts = true +warn_return_any = true +warn_unreachable = true +warn_unused_ignores = false + [tool.ruff] # Exclude a variety of commonly ignored directories. exclude = [ diff --git a/setup.cfg b/setup.cfg index c0458996..8e539d0c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,16 +6,3 @@ addopts = --cov-append --cov-report=term --cov-report=html - -[mypy] -disallow_incomplete_defs = True -disallow_untyped_defs = True -disallow_untyped_calls = True -warn_redundant_casts = True -warn_unused_ignores = False -warn_return_any = True -warn_unreachable = True -strict = True -show_error_context = True -show_column_numbers = True -show_error_codes = True diff --git a/tasks.py b/tasks.py index 8c84ce4f..a7c1dc5a 100644 --- a/tasks.py +++ b/tasks.py @@ -95,19 +95,17 @@ def pytest_clean(context): namespace_clean.add_task(pytest_clean, 'pytest') -@invoke.task(post=[plugin_tasks.mypy]) +@invoke.task() def mypy(context): """Run mypy optional static type checker""" with context.cd(TASK_ROOT_STR): - context.run("mypy cmd2") - with context.cd(str(TASK_ROOT / 'examples')): - context.run("mypy decorator_example.py") + context.run("mypy .") namespace.add_task(mypy) -@invoke.task(post=[plugin_tasks.mypy_clean]) +@invoke.task() def mypy_clean(context): """Remove mypy cache directory""" # pylint: disable=unused-argument @@ -348,7 +346,7 @@ def pypi_test(context): # ruff fast linter -@invoke.task(post=[plugin_tasks.flake8]) +@invoke.task() def lint(context): """Run ruff fast linter""" with context.cd(TASK_ROOT_STR): @@ -361,7 +359,7 @@ def lint(context): # ruff fast formatter @invoke.task() def format(context): - """Run ruff format --checkt""" + """Run ruff format --check""" with context.cd(TASK_ROOT_STR): context.run("ruff format --check")