diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..94b9b30 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,44 @@ +--- +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-merge-conflict + - id: check-toml # For pyproject.toml + - id: check-yaml # For workflows, and this file + - id: end-of-file-fixer + - id: trailing-whitespace + args: [--markdown-linebreak-ext=md] + - id: mixed-line-ending + args: [--fix=lf] + + - repo: local + hooks: + - id: ruff + name: ruff + description: Run ruff linter + entry: rye run ruff check --force-exclude + language: system + types_or: [python, pyi] + require_serial: true + args: [--fix, --exit-non-zero-on-fix] + + - repo: local + hooks: + - id: ruff-format + name: ruff-format + description: Run ruff formatter + entry: rye run ruff format + language: system + types_or: [python, pyi] + require_serial: true + + - repo: local + hooks: + - id: basedpyright + name: Based Pyright + description: Run basedpyright type checker + entry: rye run basedpyright --warnings + language: system + types: [python] + pass_filenames: false # pyright runs for the entire project, it can't run for single files diff --git a/pyproject.toml b/pyproject.toml index 50bdb6e..7c0d7f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,10 +14,135 @@ build-backend = "hatchling.build" [tool.rye] managed = true -dev-dependencies = [] +dev-dependencies = ["ruff>=0.8.0", "pre-commit>=4.0.1", "basedpyright>=1.22.0"] [tool.hatch.metadata] allow-direct-references = true [tool.hatch.build.targets.wheel] packages = ["src"] + +[tool.ruff] +target-version = "py312" +line-length = 119 + +[tool.ruff.lint] +select = ["ALL"] + +ignore = [ + "C90", # mccabe + "FBT", # flake8-boolean-trap + "CPY", # flake8-copyright + "EM", # flake8-errmsg + "SLF", # flake8-self + "TCH", # flake8-type-checking + "ARG", # flake8-unused-arguments + "TD", # flake8-todos + "FIX", # flake8-fixme + + "D100", # Missing docstring in public module + "D104", # Missing docstring in public package + "D105", # Missing docstring in magic method + "D106", # Missing docstring in public nested class + "D107", # Missing docstring in __init__ + "D203", # Blank line required before class docstring + "D213", # Multi-line summary should start at the second line (incompatible with D213) + "D301", # Use r""" if any backslashes in a docstring + "D401", # First line of docstring should be in imperative mood + "D404", # First word of the docstring should not be "This" + "D405", # Section name should be properly capitalized + "D406", # Section name should end with a newline + "D407", # Missing dashed underline after section + "D408", # Section underline should be in the line following the section's name + "D409", # Section underline should match the length of its name + "D410", # Missing blank line after section + "D411", # Missing blank line before section + "D412", # No blank lines allowed between a section header and its content + "D413", # Missing blank line after last section + "D414", # Section has no content + "D416", # Section name should end with a colon + "D417", # Missing argument descrition in the docstring + + "ANN002", # Missing type annotation for *args + "ANN003", # Missing type annotation for **kwargs + "ANN204", # Missing return type annotation for special method + "ANN401", # Dynamically typed expressions (typing.Any) disallowed + + "SIM102", # use a single if statement instead of nested if statements + "SIM108", # Use ternary operator {contents} instead of if-else-block + + "B904", # Raise without `from` within an `except` clause + "G004", # Logging statement uses f-strings + "PLR2004", # Using unnamed numerical constants + "PGH003", # Using specific rule codes in type ignores + "E731", # Don't asign a lambda expression, use a def + "S311", # Use `secrets` for random number generation, not `random` + "TRY003", # Avoid specifying long messages outside the exception class + "T201", # Use of print() function + + # Redundant rules with ruff-format: + "E111", # Indentation of a non-multiple of 4 spaces + "E114", # Comment with indentation of a non-multiple of 4 spaces + "E117", # Cheks for over-indented code + "D206", # Checks for docstrings indented with tabs + "D300", # Checks for docstring that use ''' instead of """ + "Q000", # Checks of inline strings that use wrong quotes (' instead of ") + "Q001", # Multiline string that use wrong quotes (''' instead of """) + "Q002", # Checks for docstrings that use wrong quotes (''' instead of """) + "Q003", # Checks for avoidable escaped quotes ("\"" -> '"') + "COM812", # Missing trailing comma (in multi-line lists/tuples/...) + "COM819", # Prohibited trailing comma (in single-line lists/tuples/...) + "ISC001", # Single line implicit string concatenation ("hi" "hey" -> "hihey") + "ISC002", # Multi line implicit string concatenation +] + +[tool.ruff.lint.isort] +order-by-type = false +case-sensitive = true +combine-as-imports = true + +# Redundant rules with ruff-format +force-single-line = false # forces all imports to appear on their own line +force-wrap-aliases = false # Split imports with multiple members and at least one alias +lines-after-imports = -1 # The number of blank lines to place after imports +lines-between-types = 0 # Number of lines to place between "direct" and import from imports +split-on-trailing-comma = false # if last member of multiline import has a comma, don't fold it to single line + +[tool.ruff.lint.pylint] +max-args = 20 +max-branches = 20 +max-returns = 20 +max-statements = 250 + +[tool.ruff.format] +line-ending = "lf" + +[tool.basedpyright] +pythonPlatform = "Linux" +pythonVersion = "3.12" +typeCheckingMode = "all" + +# Diagnostic behavior settings +strictListInference = false +strictDictionaryInference = false +strictSetInference = false +analyzeUnannotatedFunctions = true +strictParameterNoneValue = true +enableTypeIgnoreComments = true +deprecateTypingAliases = true # only applies up to pythonVersion +enableExperimentalFeatures = false +disableBytesTypePromotions = true + +# Diagnostic rules +reportAny = false +reportExplicitAny = false +reportImplicitStringConcatenation = false +reportUnreachable = "information" +reportUnknownArgumentType = false +reportUnknownVariableType = false +reportUnknownMemberType = false +reportUnknownParameterType = false +reportUnknownLambdaType = false +reportMissingTypeStubs = "information" +reportUninitializedInstanceVariable = false # until https://github.com/DetachHead/basedpyright/issues/491 +reportMissingParameterType = false # ruff's flake8-annotations (ANN) already covers this + gives us more control diff --git a/requirements-dev.lock b/requirements-dev.lock index 505fd45..e414dc4 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -10,3 +10,24 @@ # universal: false -e file:. +basedpyright==1.22.0 +cfgv==3.4.0 + # via pre-commit +distlib==0.3.9 + # via virtualenv +filelock==3.16.1 + # via virtualenv +identify==2.6.3 + # via pre-commit +nodeenv==1.9.1 + # via pre-commit +nodejs-wheel-binaries==22.11.0 + # via basedpyright +platformdirs==4.3.6 + # via virtualenv +pre-commit==4.0.1 +pyyaml==6.0.2 + # via pre-commit +ruff==0.8.0 +virtualenv==20.28.0 + # via pre-commit