diff --git a/.gitignore b/.gitignore index 6d9fd0bf3e..06d5dcf6c3 100644 --- a/.gitignore +++ b/.gitignore @@ -124,5 +124,8 @@ junit.xml .DS_Store Thumbs.db -# Potential venv -.venv \ No newline at end of file +# python stuff +.pdm-python +.pyprojectx +**/__pycache__ +/.venv/ \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 3c4ccadcac..3f338fbaf3 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,16 @@ { "recommendations": [ "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode" + "esbenp.prettier-vscode", + "ms-python.python", + "ms-python.vscode-pylance", + "ms-python.pylint", + "ms-python.black-formatter", + "ms-python.mypy-type-checker", + "charliermarsh.ruff", + "shd101wyy.markdown-preview-enhanced", + "mhutchie.git-graph", + "tamasfe.even-better-toml", + "redhat.vscode-yaml" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 59f927cf6c..1619e42745 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,9 +8,54 @@ "editor.formatOnSave": true }, "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit", - "source.organizeImports": "explicit" + "source.fixAll.eslint": "always", + "source.organizeImports": "always" }, "typescript.tsdk": "node_modules/typescript/lib", "python.languageServer": "None", + "editor.formatOnSave": true, + "git.allowForcePush": true, + "git.autofetch": "all", + "git.autoStash": true, + "git.pruneOnFetch": true, + "git.rebaseWhenSync": true, + "git.suggestSmartCommit": false, + "git.supportCancellation": true, + "git.useEditorAsCommitInput": false, + "diffEditor.ignoreTrimWhitespace": false, + "terminal.integrated.persistentSessionReviveProcess": "never", + "mypy-type-checker.args": ["--ide", "--python-version", "3.8"], + "mypy-type-checker.importStrategy": "fromEnvironment", + "files.autoSave": "onFocusChange", + "search.useIgnoreFiles": true, + "git.useCommitInputAsStashMessage": true, + "files.eol": "\n", + "python.testing.pytestEnabled": true, + "testing.automaticallyOpenPeekView": "never", + "python.analysis.typeCheckingMode": "off", + "python.analysis.autoFormatStrings": true, + "python.analysis.gotoDefinitionInStringLiteral": true, + "python.analysis.typeshedPaths": ["./.venv/lib/site-packages/mypy/typeshed"], + "pylint.importStrategy": "fromEnvironment", + "pylint.severity": { + "convention": "Warning", + "error": "Error", + "fatal": "Error", + "refactor": "Warning", + "warning": "Warning", + "info": "Warning" + }, + "black-formatter.importStrategy": "fromEnvironment", + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + }, + "yaml.schemas": { + "https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/github-workflow.json": ".github/workflows/*.yaml" + }, + "explorer.compactFolders": false, + "search.exclude": { + "pw": true + }, + "mypy-type-checker.ignorePatterns": ["pw"], + "python.terminal.activateEnvInCurrentTerminal": true } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 1f88582c6b..0218f736ec 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -40,6 +40,86 @@ } } } + }, + { + "label": "pdm install requirements", + "type": "shell", + "command": "./pw", + "args": ["install"], + "presentation": { + "clear": true + }, + "problemMatcher": [] + }, + { + "label": "pdm update lockfile", + "type": "shell", + "command": "./pw", + "args": ["pdm", "update"], + "presentation": { + "clear": true + }, + "problemMatcher": [] + }, + { + "label": "pdm refresh lockfile (no update)", + "type": "shell", + "command": "./pw", + "args": ["pdm", "lock", "--refresh"], + "presentation": { + "clear": true + }, + "problemMatcher": [] + }, + { + "label": "basedmypy - all files", + "type": "shell", + "command": "./pw", + "args": ["pdm", "run", "mypy_all"], + "presentation": { + "clear": true + }, + "problemMatcher": [] + }, + { + "label": "ruff - all files", + "type": "shell", + "command": "${command:python.interpreterPath}", + "args": ["-m", "ruff", "."], + "presentation": { + "clear": true + }, + "problemMatcher": [] + }, + { + "label": "ruff fix - all files", + "type": "shell", + "command": "${command:python.interpreterPath}", + "args": ["-m", "ruff", "--fix", "."], + "presentation": { + "clear": true + }, + "problemMatcher": [] + }, + { + "label": "pylint - all files", + "type": "shell", + "command": "${command:python.interpreterPath}", + "args": ["-m", "pylint", "basedpyright", "tests"], + "presentation": { + "clear": true + }, + "problemMatcher": [] + }, + { + "label": "black - all files", + "type": "shell", + "command": "${command:python.interpreterPath}", + "args": ["-m", "black", "--color", "."], + "presentation": { + "clear": true + }, + "problemMatcher": [] } ] } diff --git a/basedpyright/__init__.py b/basedpyright/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/basedpyright/langserver.py b/basedpyright/langserver.py new file mode 100644 index 0000000000..1a6700bea5 --- /dev/null +++ b/basedpyright/langserver.py @@ -0,0 +1,7 @@ +from __future__ import annotations + +from basedpyright.run_node import run + + +def main(): + run("pyright-langserver") diff --git a/basedpyright/pyright.py b/basedpyright/pyright.py new file mode 100644 index 0000000000..ff2adc1660 --- /dev/null +++ b/basedpyright/pyright.py @@ -0,0 +1,7 @@ +from __future__ import annotations + +from basedpyright.run_node import run + + +def main(): + run("pyright") diff --git a/basedpyright/run_node.py b/basedpyright/run_node.py new file mode 100644 index 0000000000..b1241f9c3c --- /dev/null +++ b/basedpyright/run_node.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +import sys +from pathlib import Path + +from nodejs import node + + +def run(script_name: str): + node.run([Path(__file__).parent / f"dist/{script_name}.js", *sys.argv[1:]]) diff --git a/pdm.lock b/pdm.lock new file mode 100644 index 0000000000..93951b1a8b --- /dev/null +++ b/pdm.lock @@ -0,0 +1,24 @@ +# This file is @generated by PDM. +# It is not intended for manual editing. + +[metadata] +groups = ["default"] +strategy = ["cross_platform", "inherit_metadata"] +lock_version = "4.4.1" +content_hash = "sha256:b15d5f94f9ae3c567c12f1b4e97f1002b452d978d777cb753000fed1f850df87" + +[[package]] +name = "nodejs-bin" +version = "18.4.0a4" +requires_python = "~=3.5" +summary = " Node.js is an open-source, cross-platform, back-end JavaScript\nruntime environment that runs on the V8 engine and executes JavaScript code\noutside a web browser." +groups = ["default"] +files = [ + {file = "nodejs_bin-18.4.0a4-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:16cb1abf7fe8c11c574e1e474d9f934a0df49a480290eae6e733d8bb09512e22"}, + {file = "nodejs_bin-18.4.0a4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:068ca987ed83ea1123775fafe5dc22d8f2ff920d7d31571e1bfe6fb1093833eb"}, + {file = "nodejs_bin-18.4.0a4-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:06cfeaa4d26eec94d8edb9927525ce94eb96dadc81f7d1daed42d1a7d003a4c9"}, + {file = "nodejs_bin-18.4.0a4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:431ee3529f4fb226ddcfd4f14cb37e7df31238c42dfd051f4bf8f0c21029b133"}, + {file = "nodejs_bin-18.4.0a4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:21f1f77ddc8fe05353bb6d6ee8e5a62edb3a8dcdb2740a5f9307fd8d9eef6691"}, + {file = "nodejs_bin-18.4.0a4-py3-none-win32.whl", hash = "sha256:59671fdc563dabb8be8a0b6dae4169d780482b3c9e0fba3f9aa2b7ee8d2261ac"}, + {file = "nodejs_bin-18.4.0a4-py3-none-win_amd64.whl", hash = "sha256:cbd509218b4b17f75ee7841f9c21d5cacc1626d3b823a652a6627dbad18228ec"}, +] diff --git a/pdm_build.py b/pdm_build.py new file mode 100644 index 0000000000..0197917d0d --- /dev/null +++ b/pdm_build.py @@ -0,0 +1,14 @@ +from __future__ import annotations + +from pathlib import Path +from shutil import copytree + +from nodejs import npm + +if not Path("node_modules").exists(): + npm.run(["ci"], check=True) +npm.run(["run", "build:cli:dev"], check=True) + +pyright_npm_package_dir = Path("packages/pyright") +pyright_pypi_package_dir = Path("basedpyright") +copytree("packages/pyright/dist", "basedpyright/dist", dirs_exist_ok=True) diff --git a/pw b/pw new file mode 100644 index 0000000000..526809430d --- /dev/null +++ b/pw @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 + +################################################################################## +# Pyprojectx wrapper script # +# https://github.com/pyprojectx/pyprojectx # +# # +# Copyright (c) 2021 Ivo Houbrechts # +# # +# Licensed under the MIT license # +################################################################################## +import argparse +import os +import subprocess +import sys +from pathlib import Path +from venv import EnvBuilder + +VERSION = "2.0.2" + +PYPROJECTX_INSTALL_DIR_ENV_VAR = "PYPROJECTX_INSTALL_DIR" +PYPROJECTX_PACKAGE_ENV_VAR = "PYPROJECTX_PACKAGE" +PYPROJECT_TOML = "pyproject.toml" +DEFAULT_INSTALL_DIR = ".pyprojectx" + +CYAN = "\033[96m" +BLUE = "\033[94m" +RED = "\033[91m" +RESET = "\033[0m" +if sys.platform.startswith("win"): + os.system("color") + + +def run(args): + try: + options = get_options(args) + pyprojectx_script = ensure_pyprojectx(options) + explicit_options = [] + if not options.toml: + explicit_options += ["--toml", str(options.toml_path)] + if not options.install_dir: + explicit_options += ["--install-dir", str(options.install_path)] + + subprocess.run([str(pyprojectx_script), *explicit_options, *args], check=True) + except subprocess.CalledProcessError as e: + raise SystemExit(e.returncode) from e + + +def get_options(args): + options = arg_parser().parse_args(args) + options.install_path = Path( + options.install_dir + or os.environ.get(PYPROJECTX_INSTALL_DIR_ENV_VAR, Path(__file__).with_name(DEFAULT_INSTALL_DIR)) + ) + options.toml_path = Path(options.toml) if options.toml else Path(__file__).with_name(PYPROJECT_TOML) + if os.environ.get(PYPROJECTX_PACKAGE_ENV_VAR): + options.version = "development" + options.pyprojectx_package = os.environ.get(PYPROJECTX_PACKAGE_ENV_VAR) + else: + options.version = VERSION + options.pyprojectx_package = f"pyprojectx~={VERSION}" + options.verbosity = 0 if options.quiet or not options.verbosity else options.verbosity + return options + + +def arg_parser(): + parser = argparse.ArgumentParser( + description="Execute commands or aliases defined in the [tool.pyprojectx] section of pyproject.toml. " + "Use the -i or --info option to see available tools and aliases.", + allow_abbrev=False, + ) + parser.add_argument("--version", action="version", version=VERSION) + parser.add_argument( + "--toml", + "-t", + action="store", + help="The toml config file. Defaults to 'pyproject.toml' in the same directory as the pw script.", + ) + parser.add_argument( + "--install-dir", + action="store", + help=f"The directory where all tools (including pyprojectx) are installed; defaults to the " + f"{PYPROJECTX_INSTALL_DIR_ENV_VAR} environment value if set, else '.pyprojectx' " + f"in the same directory as the invoked pw script", + ) + parser.add_argument( + "--force-install", + "-f", + action="store_true", + help="Force clean installation of the virtual environment used to run cmd, if any", + ) + parser.add_argument( + "--install-context", + action="store", + metavar="tool-context", + help="Install a tool context without actually running any command.", + ) + parser.add_argument( + "--verbose", + "-v", + action="count", + dest="verbosity", + help="Give more output. This option is additive and can be used up to 2 times.", + ) + parser.add_argument( + "--quiet", + "-q", + action="store_true", + help="Suppress output", + ) + parser.add_argument( + "--info", + "-i", + action="store_true", + help="Show the configuration details of a command instead of running it. " + "If no command is specified, a list with all available tools and aliases is shown.", + ) + parser.add_argument( + "--add", + action="store", + metavar="[context:],...", + help="Add one or more packages to a tool context. " + "If no context is specified, the packages are added to the main context. " + "Packages can be specified as in 'pip install', except that a ',' can't be used in the version specification.", + ) + parser.add_argument( + "--lock", + action="store_true", + help="Write all dependencies of all tool contexts to 'pw.lock' to guarantee reproducible outcomes.", + ) + parser.add_argument( + "--install-px", action="store_true", help="Install the px and pxg scripts in your home directory." + ) + parser.add_argument( + "--upgrade", + action="store_true", + help="Download the latest pyprojectx wrapper scripts.", + ) + parser.add_argument( + "command", nargs=argparse.REMAINDER, help="The command/alias with optional arguments to execute." + ) + return parser + + +def ensure_pyprojectx(options): + env_builder = EnvBuilder(with_pip=True) + venv_dir = options.install_path.joinpath( + "pyprojectx", f"{options.version}-py{sys.version_info.major}.{sys.version_info.minor}" + ) + env_context = env_builder.ensure_directories(venv_dir) + pyprojectx_script = Path(env_context.bin_path, "pyprojectx") + pyprojectx_exe = Path(env_context.bin_path, "pyprojectx.exe") + pip_cmd = [env_context.env_exe, "-m", "pip", "install"] + + if options.quiet: + out = subprocess.DEVNULL + pip_cmd.append("--quiet") + else: + out = sys.stderr + + if not pyprojectx_script.is_file() and not pyprojectx_exe.is_file(): + if not options.quiet: + print(f"{CYAN}creating pyprojectx venv in {BLUE}{venv_dir}{RESET}", file=sys.stderr) + env_builder.create(venv_dir) + subprocess.run( + [*pip_cmd, "--upgrade", "pip"], + stdout=out, + check=True, + ) + + if not options.quiet: + print( + f"{CYAN}installing pyprojectx {BLUE}{options.version}: {options.pyprojectx_package} {RESET}", + file=sys.stderr, + ) + if options.version == "development": + if not options.quiet: + print( + f"{RED}WARNING: {options.pyprojectx_package} is installed in editable mode{RESET}", + file=sys.stderr, + ) + pip_cmd.append("-e") + subprocess.run([*pip_cmd, options.pyprojectx_package], stdout=out, check=True) + return pyprojectx_script + + +if __name__ == "__main__": + run(sys.argv[1:]) diff --git a/pw.bat b/pw.bat new file mode 100644 index 0000000000..5a81b0841d --- /dev/null +++ b/pw.bat @@ -0,0 +1,2 @@ +@echo off +python "%~dp0pw" %* diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..8d32289f44 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,318 @@ +[tool.pyprojectx] +main = ["pdm"] + +[tool.pdm] +package-type = "library" + +[project] +name = "basedpyright" +version = "0.1.0" +description = "Default template for PDM package" +authors = [ + { name = "detachhead", email = "detachhead@users.noreply.github.com" }, +] +dependencies = ["nodejs-bin>=18.4.0a4"] +requires-python = ">=3.8" +readme = "README.md" +license = { text = "MIT" } + +[project.urls] +repository = "https://github.com/detachhead/basedpyright" + +[project.scripts] +pyright = 'basedpyright.pyright:main' +pyright-langserver = 'basedpyright.langserver:main' + +[build-system] +requires = ["pdm-backend", "nodejs-bin>=18.4.0a4"] +build-backend = "pdm.backend" + +[tool.pdm.dev-dependencies] +lint = ["black>=23", "basedmypy>=2.1", "pylint>=3.0.0a7", "ruff>=0.0.290"] + +[tool.black] +target-version = ["py38"] +skip-magic-trailing-comma = true +preview = true +force-exclude = "pw" + +[tool.pylint.MASTER] +fail-on = "I" +bad-names = ["foo", "bar", "baz", "retval"] +load-plugins = [ + # we don't use all of these but enabling them all for completeness since there's no error if a rule is not found + "pylint.extensions.bad_builtin", + "pylint.extensions.broad_try_caluse", + "pylint.extensions.check_elif", + "pylint.extensions.code_style", + "pylint.extensions.comparetozero", + "pylint.extensions.comparison_placement", + "pylint.extensions.confusing_elif", + "pylint.extensions.consider_refactoring_into_while_condition", + "pylint.extensions.consider_ternary_expression", + "pylint.extensions.dict_init_mutate", + "pylint.extensions.docparams", + "pylint.extensions.docstyle", + "pylint.extensions.dunder", + "pylint.extensions.empty_comment", + "pylint.extensions.emptystring", + "pylint.extensions.eq_without_hash", + "pylint.extensions.for_any_all", + "pylint.extensions.magic_value", + "pylint.extensions.no_self_use", + "pylint.extensions.overlapping_exceptions", + "pylint.extensions.private_import", + "pylint.extensions.redefined_loop_name", + "pylint.extensions.redefined_variable_type", + "pylint.extensions.set_membership", + "pylint.extensions.typing", + "pylint.extensions.while_used", +] + +[tool.pylint."MESSAGE CONTROL"] +disable = ["R", "C", "E", "F", "W", "I"] +enable = [ + "useless-suppression", + # rules that have not yet been implemented in ruff. once all of these are implemented in ruff, we should remove pylint + # (see https://github.com/astral-sh/ruff/issues/970): + "possibly-unused-variable", + "access-member-before-definition", + "assigning-non-slot", + "assignment-from-no-return", + "assignment-from-none", + "bad-except-order", + "bad-exception-cause", + "bad-reversed-sequence", + "bad-super-call", + "catching-non-exception", + "class-variable-slots-conflict", + "dict-iter-missing-items", + "inconsistent-mro", + "init-is-generator", + "invalid-bool-returned", + "invalid-bytes-returned", + "invalid-character-carriage-return", + "invalid-class-object", + "invalid-enum-extension", + "invalid-envvar-value", + "invalid-format-returned", + "invalid-getnewargs-ex-returned", + "invalid-getnewargs-returned", + "invalid-index-returned", + "invalid-length-hint-returned", + "invalid-length-returned", + "invalid-metaclass", + "invalid-repr-returned", + "invalid-sequence-index", + "invalid-slice-index", + "invalid-slice-step", + "invalid-slots", + "invalid-slots-object", + "invalid-star-assignment-target", + "invalid-str-returned", + "invalid-unary-operand-type", + "invalid-unicode-codec", + "logging-format-truncated", + "logging-unsupported-format", + "method-hidden", + "misplaced-format-function", + "missing-kwoa", + "modified-iterating-dict", + "modified-iterating-set", + "non-iterator-returned", + "nonlocal-and-global", + "not-a-mapping", + "not-async-context-manager", + "not-callable", + "not-context-manager", + "potential-index-error", + "raising-non-exception", + "redundant-keyword-arg", + "relative-beyond-top-level", + "singledispatch-method", + "singledispatchmethod-function", + "star-needs-assignment-target", + "unhashable-member", + "unpacking-non-sequence", + "unsupported-assignment-operation", + "unsupported-binary-operation", + "unsupported-delete-operation", + "abstract-method", + "anomalous-unicode-escape-in-string", + "arguments-out-of-order", + "arguments-renamed", + "attribute-defined-outside-init", + "bad-builtin", + "bad-indentation", + "bad-staticmethod-argument", + "bad-thread-instantiation", + "comparison-with-callable", + "confusing-with-statement", + "cyclic-import", + "deprecated-argument", + "deprecated-class", + "deprecated-decorator", + "deprecated-method", + "deprecated-module", + "differing-param-doc", + "differing-type-doc", + "global-variable-undefined", + "invalid-format-index", + "invalid-overridden-method", + "method-cache-max-size-none", + "missing-any-param-doc", + "missing-format-attribute", + "missing-param-doc", + "missing-parentheses-for-call-in-test", + "missing-raises-doc", + "missing-return-doc", + "missing-return-type-doc", + "missing-timeout", + "missing-yield-doc", + "missing-yield-type-doc", + "modified-iterating-list", + "multiple-constructor-doc", + "nan-comparison", + "non-ascii-file-name", + "non-parent-init-called", + "non-str-assignment-to-dunder-name", + "overlapping-except", + "overridden-final-method", + "preferred-module", + "raising-format-tuple", + "redeclared-assigned-name", + "redefined-outer-name", + "redefined-slots-in-subclass", + "redundant-returns-doc", + "redundant-unittest-assert", + "redundant-yields-doc", + "self-cls-assignment", + "shallow-copy-environ", + "signature-differs", + "super-init-not-called", + "unbalanced-dict-unpacking", + "unbalanced-tuple-unpacking", + "undefined-loop-variable", + "unnecessary-ellipsis", + "unreachable", + "unused-private-member", + "unused-wildcard-import", + "useless-param-doc", + "useless-parent-delegation", + "useless-type-doc", + "using-constant-test", + "using-final-decorator-in-unsupported-version", + "while-used", + "wrong-exception-operation", + "bad-file-encoding", + "bad-mcs-classmethod-argument", + "bad-mcs-method-argument", + "dict-init-mutate", + "invalid-characters-in-docstring", + "mixed-line-endings", + "superfluous-parens", + "unexpected-line-ending-format", + "use-implicit-booleaness-not-comparison", + "chained-comparison", + "condition-evals-to-constant", + "confusing-consecutive-elif", + "consider-swap-variables", + "consider-using-augmented-assign", + "consider-using-join", + "consider-using-max-builtin", + "consider-using-min-builtin", + "consider-using-namedtuple-or-dataclass", + "consider-using-tuple", + "simplifiable-condition", + "simplify-boolean-expression", + "stop-iteration-return", + "use-set-for-membership", + "useless-return", +] + +[tool.pylint.REPORTS] +reports = "no" +output-format = "colorized" +score = "no" + +[tool.pylint.FORMAT] +max-line-length = 200 + +[tool.mypy] +allow_redefinition = false +enable_error_code = "helpful-string" +default_return = false # enabled for our own code only as 3rd party code is not coompatible +cache_dir = 'nul' # disable cache because it sucks + +[tool.pyright] +# we dont use pyright for type checking, this is just for vscode import suggestions and reachability checks +pythonVersion = "3.8" +pythonPlatform = "All" + +[tool.ruff] +unsafe-fixes = true +extend-select = ["ALL"] +ignore = [ + "ANN", # flake8-annotations (covered by mypy) + "COM", # flake8-commas (covered by black) + "EM", # flake8-errmsg + "FIX", # flake8-fixme + "PLR0913", # Too many arguments to function call + "PLR0912", # Too many branches + "PLR0915", # Too many statements + "PLR2004", # Magic value used in comparison + "PLR1722", # Use `sys.exit()` instead of `exit` + "PLW2901", # `for` loop variable overwritten by assignment target + "PLE0605", # Invalid format for `__all__`, must be `tuple` or `list` (covered by mypy) + "PLR0911", # Too many return statements + "PLW0603", # Using the global statement is discouraged + "PLC0105", # `TypeVar` name does not reflect its covariance + "PLC0414", # Import alias does not rename original package (used by mypy for explicit re-export) + "RUF013", # PEP 484 prohibits implicit Optional (covered by mypy) + "RUF016", # Slice in indexed access to type (covered by mypy) + "TRY002", # Create your own exception + "TRY003", # Avoid specifying long messages outside the exception class + "D10", # Missing docstring + "D203", # 1 blank line required before class docstring + "D204", # 1 blank line required after class docstring (handled by black) + "D205", # 1 blank line required between summary line and description + "D209", # Multi-line docstring closing quotes should be on a separate line + "D210", # No whitespaces allowed surrounding docstring text (conflicts with black when docstring ends with quote (eg. """this is a "quote" """)) + "D212", # Multi-line docstring summary should start at the first line + "D213", # Multi-line docstring summary should start at the second line + "D400", # First line should end with a period + "D401", # First line should be in imperative mood + "D403", # First word of the first line should be properly capitalized + "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 + "D415", # First line should end with a period, question mark, or exclamation point + "D418", # Function/Method decorated with @overload shouldn't contain a docstring (vscode supports it) + "PT013", # Found incorrect import of pytest, use simple import pytest instead (only for bad linters that can't check the qualname) + "TD002", # Missing author in TODO + "PGH003", # Use specific rule codes when ignoring type issues (covered by mypy) + "E701", # Multiple statements on one line (sometimes conflicts with black) + "CPY001", # missing-copyright-notice + "C901", # max-complexity +] +target-version = "py38" +respect-gitignore = true +line-length = 100 +preview = true +extend-exclude = ["pw"] + +[tool.ruff.pycodestyle] +ignore-overlong-task-comments = true + +[tool.ruff.flake8-pytest-style] +# not including the parenthesis causes Any expression in mypy +fixture-parentheses = true + +[tool.ruff.per-file-ignores] +"*.pyi" = ["A001", "A002", "N"] # we don't control names in 3rd party modules +"tests/**/*.py" = [ + "S101", # Use of assert detected (pytest uses assert statements) +] +[tool.ruff.isort] +combine-as-imports = true +required-imports = ["from __future__ import annotations"]