From 832c5dfd84948575e909f46796bf28485bd54117 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Wed, 16 Nov 2022 07:53:06 -0500 Subject: [PATCH 1/8] build: use hatch and ruff --- .pre-commit-config.yaml | 18 ++---- pyproject.toml | 119 ++++++++++++++++++---------------------- 2 files changed, 56 insertions(+), 81 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a7cddec..cd65fd1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,12 +19,6 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - - repo: https://github.com/PyCQA/autoflake - rev: v1.7.7 - hooks: - - id: autoflake - args: ["--in-place", "--remove-all-unused-imports"] - - repo: https://github.com/PyCQA/isort rev: 5.10.1 hooks: @@ -41,15 +35,11 @@ repos: hooks: - id: black - - repo: https://github.com/PyCQA/flake8 - rev: 5.0.4 + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.0.118 hooks: - - id: flake8 - additional_dependencies: - - flake8-pyprojecttoml @ git+https://github.com/tlambert03/flake8-pyprojecttoml.git@main - - flake8-bugbear - - flake8-docstrings - - flake8-typing-imports + - id: ruff + args: ["--fix"] - repo: https://github.com/pre-commit/mirrors-mypy rev: v0.982 diff --git a/pyproject.toml b/pyproject.toml index 1756f21..2a78b0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ # https://peps.python.org/pep-0517/ [build-system] -requires = ["setuptools>=45", "wheel", "setuptools-scm>=6.2"] -build-backend = "setuptools.build_meta" +requires = ["hatchling", "hatch-vcs"] +build-backend = "hatchling.build" # https://peps.python.org/pep-0621/ [project] @@ -13,12 +13,16 @@ license = { text = "BSD 3-Clause License" } authors = [{ email = "talley.lambert@gmail.com" }, { name = "Talley Lambert" }] classifiers = [ "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Topic :: Desktop Environment", + "Topic :: Software Development", + "Topic :: Software Development :: User Interfaces", ] dynamic = ["version"] dependencies = ['psygnal', 'pydantic', 'in-n-out>=0.1.5', 'typing_extensions'] @@ -26,17 +30,13 @@ dependencies = ['psygnal', 'pydantic', 'in-n-out>=0.1.5', 'typing_extensions'] # extras # https://peps.python.org/pep-0621/#dependencies-optional-dependencies [project.optional-dependencies] +min-req = ['psygnal', 'pydantic', 'in-n-out>=0.1.5', 'typing_extensions'] + test = ["pytest>=6.0", "pytest-cov"] test-qt = ["pytest-qt", "fonticon-fontawesome6"] qt = ["qtpy", "superqt"] dev = [ "black", - "cruft", - "flake8-bugbear", - "flake8-docstrings", - "flake8-pyprojecttoml", - "flake8-typing-imports", - "flake8", "ipython", "isort", "mypy", @@ -57,65 +57,62 @@ docs = [ "mkdocs-macros-plugin==0.7.0", "typing_extensions>=4.0", ] + [project.urls] homepage = "https://github.com/pyapp-kit/app-model" repository = "https://github.com/pyapp-kit/app-model" -# same as console_scripts entry point -# [project.scripts] -# spam-cli = "spam:main_cli" - -# Entry points -# https://peps.python.org/pep-0621/#entry-points -# [project.entry-points."spam.magical"] -# tomatoes = "spam:main_tomatoes" - -# https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html -[tool.setuptools] -zip-safe = false -include-package-data = true -packages = { find = { where = ["src"], exclude = [] } } - -[tool.setuptools.package-data] -"*" = ["py.typed"] +[tool.hatch.version] +source = "vcs" + +[tool.hatch.envs.test] +features = ["testing"] +[tool.hatch.envs.test.scripts] +run = "pytest -v --color=yes --cov-config=pyproject.toml --cov=magicgui --cov-report=xml --cov-report=term-missing" +[[tool.hatch.envs.test.matrix]] +backend = ["pyqt5", "pyside2", "pyqt6", "pyside6"] +[tool.hatch.envs.test.overrides] +# matrix.deps.features = [ +# { value = "min-req", if = ["min-req"] }, +# ] +matrix.backend.features = [ + { value = "pyqt5", if = ["pyqt5"] }, + { value = "pyside2", if = ["pyside2"] }, + { value = "pyqt6", if = ["pyqt6"] }, + { value = "pyside6", if = ["pyside6"] }, +] -# https://github.com/pypa/setuptools_scm/#pyprojecttoml-usage -[tool.setuptools_scm] # https://pycqa.github.io/isort/docs/configuration/options.html [tool.isort] profile = "black" src_paths = ["src/app_model", "tests"] -# https://flake8.pycqa.org/en/latest/user/options.html -# https://gitlab.com/durko/flake8-pyprojecttoml -[tool.flake8] -exclude = "docs,.eggs,examples,_version.py" -max-line-length = 88 -ignore = "E203" -min-python-version = "3.8.0" -docstring-convention = "all" # use numpy convention, while allowing D417 -extend-ignore = """ -E203 # whitespace before ':' -D107,D203,D212,D213,D402,D413,D415,D416 # numpy -D100 # missing docstring in public module -D105 # missing docstring in magic method -D401 # imperative mood -W503 # line break before binary operator -B010 -""" -per-file-ignores = [ - "tests/*,demo/*,docs/*: D", - "src/app_model/_registries.py,src/app_model/context/_expressions.py: D10", + +# https://github.com/charliermarsh/ruff +[tool.ruff] +target-version = "py38" +line-length = 88 +extend-select = ["D", "M001"] +extend-ignore = [ + "D100", + "D107", + "D203", + "D212", + "D213", + "D402", + "D413", + "D415", + "D416", ] +[tool.ruff.per-file-ignores] +"tests/*.py" = ["D", "E501"] +"demo/*" = ["D"] +"docs/*" = ["D"] +"src/app_model/_registries.py" = ["D10"] +"src/app_model/context/_expressions.py" = ["D10"] -# http://www.pydocstyle.org/en/stable/usage.html -[tool.pydocstyle] -match_dir = "src/app_model" -convention = "numpy" -add_select = "D402,D415,D417" -ignore = "D100,D213,D401,D413,D107" # https://docs.pytest.org/en/6.2.x/customize.html [tool.pytest.ini_options] @@ -149,19 +146,14 @@ exclude_lines = [ "if TYPE_CHECKING:", "@overload", "except ImportError", + "pass" ] skip_covered = true show_missing = true -# https://github.com/cruft/cruft -[tool.cruft] -skip = ["tests"] - # https://github.com/mgedmin/check-manifest#configuration [tool.check-manifest] ignore = [ - ".cruft.json", - ".flake8", ".github_changelog_generator", ".pre-commit-config.yaml", "tests/**/*", @@ -171,12 +163,5 @@ ignore = [ ".readthedocs.yaml", "mkdocs.yml", "CHANGELOG.md", + ".ruff_cache/**/*", ] - -# https://python-semantic-release.readthedocs.io/en/latest/configuration.html -[tool.semantic_release] -version_source = "tag_only" -branch = "main" -changelog_sections = "feature,fix,breaking,documentation,performance,chore,:boom:,:sparkles:,:children_crossing:,:lipstick:,:iphone:,:egg:,:chart_with_upwards_trend:,:ambulance:,:lock:,:bug:,:zap:,:goal_net:,:alien:,:wheelchair:,:speech_balloon:,:mag:,:apple:,:penguin:,:checkered_flag:,:robot:,:green_apple:,Other" -# commit_parser=semantic_release.history.angular_parser -build_command = "pip install build && python -m build" From f2d35b50c853c13af6927663aa9069aa93a2e6cd Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Wed, 16 Nov 2022 07:56:00 -0500 Subject: [PATCH 2/8] fix: fix linting --- pyproject.toml | 1 + src/app_model/backends/qt/_qmenu.py | 8 ++++---- src/app_model/expressions/_expressions.py | 4 ++-- src/app_model/registries/_commands_reg.py | 4 ++++ src/app_model/types/_keys/_key_codes.py | 14 ++++++++++---- tests/conftest.py | 2 +- 6 files changed, 22 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2a78b0d..e9914e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -112,6 +112,7 @@ extend-ignore = [ "docs/*" = ["D"] "src/app_model/_registries.py" = ["D10"] "src/app_model/context/_expressions.py" = ["D10"] +"src/app_model/types/_keys/*" = ["E501"] # https://docs.pytest.org/en/6.2.x/customize.html diff --git a/src/app_model/backends/qt/_qmenu.py b/src/app_model/backends/qt/_qmenu.py index b4033e1..73924c9 100644 --- a/src/app_model/backends/qt/_qmenu.py +++ b/src/app_model/backends/qt/_qmenu.py @@ -32,10 +32,10 @@ # fmt: off class _AcceptsMenus(Protocol): _app: Application - def clear(self) -> None: ... # noqa: E704 - def addMenu(self, menu: QMenu) -> None: ... # noqa: E704 - def addAction(self, menu: QAction) -> None: ... # noqa: E704 - def addSeparator(self) -> None: ... # noqa: E704 + def clear(self) -> None: ... + def addMenu(self, menu: QMenu) -> None: ... + def addAction(self, menu: QAction) -> None: ... + def addSeparator(self) -> None: ... # fmt: on diff --git a/src/app_model/expressions/_expressions.py b/src/app_model/expressions/_expressions.py index 627f2f1..1762ca2 100644 --- a/src/app_model/expressions/_expressions.py +++ b/src/app_model/expressions/_expressions.py @@ -468,9 +468,9 @@ class ExprTranformer(ast.NodeTransformer): # fmt: off @overload - def visit(self, node: ast.expr) -> Expr: ... # noqa + def visit(self, node: ast.expr) -> Expr: ... @overload - def visit(self, node: PassedType) -> PassedType: ... # noqa + def visit(self, node: PassedType) -> PassedType: ... # fmt: on def visit(self, node: ast.AST) -> Optional[ast.AST]: diff --git a/src/app_model/registries/_commands_reg.py b/src/app_model/registries/_commands_reg.py index 65c564a..42abec9 100644 --- a/src/app_model/registries/_commands_reg.py +++ b/src/app_model/registries/_commands_reg.py @@ -165,6 +165,8 @@ def execute_command( ---------- id : CommandId ID of the command to execute + args: Any + Positional arguments to pass to the command execute_asychronously : bool Whether to execute the command asynchronously in a thread, by default `False`. Note that *regardless* of this setting, @@ -172,6 +174,8 @@ def execute_command( to call `result()` on the returned object. Eventually, this will default to True, but we need to solve `ensure_main_thread` Qt threading issues first + kwargs: Any + Keyword arguments to pass to the command Returns ------- diff --git a/src/app_model/types/_keys/_key_codes.py b/src/app_model/types/_keys/_key_codes.py index 51c5048..29e0d9a 100644 --- a/src/app_model/types/_keys/_key_codes.py +++ b/src/app_model/types/_keys/_key_codes.py @@ -192,6 +192,7 @@ class ScanCode(IntEnum): implementations to support special keyboards (such as multimedia or legacy keyboards). """ + UNIDENTIFIED = 0 # This value code should be used when no other value given in this specification is appropriate. # ----------------------- Writing System Keys ----------------------- @@ -651,8 +652,10 @@ def __or__( class KeyCombo(int): - """KeyCombo is an integer combination of one or more - [`KeyMod`][app_model.types.KeyMod] and [`KeyCode`][app_model.types.KeyCode].""" + """KeyCombo is an integer combination of one or more. + + [`KeyMod`][app_model.types.KeyMod] and [`KeyCode`][app_model.types.KeyCode]. + """ def __new__( cls: Type["KeyCombo"], modifiers: KeyMod, key: KeyCode = KeyCode.UNKNOWN @@ -670,8 +673,11 @@ def __repr__(self) -> str: class KeyChord(int): - """KeyChord is an integer combination of two [`KeyCombo`][app_model.types.KeyCombo], - [`KeyCode`][app_model.types.KeyCode], or [int][].""" + """KeyChord is an integer combination of two key combos. + + It could be two [`KeyCombo`][app_model.types.KeyCombo] + [`KeyCode`][app_model.types.KeyCode], or [int][]. + """ def __new__(cls: Type["KeyChord"], first_part: int, second_part: int) -> "KeyChord": # shift the second part 16 bits to the left diff --git a/tests/conftest.py b/tests/conftest.py index 3ab2245..61506c5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -66,7 +66,7 @@ def redo(self) -> Mock: fixtures directory must be added to sys path during the test (as we do below) """ try: - from fake_module import GLOBAL_MOCK # noqa + from fake_module import GLOBAL_MOCK return GLOBAL_MOCK except ImportError as e: From c0bfa3c6c1170c6e2c453e7463d6b15fa73c46e9 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Wed, 16 Nov 2022 08:12:08 -0500 Subject: [PATCH 3/8] test: remove envs --- pyproject.toml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e9914e4..053de78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,23 +65,6 @@ repository = "https://github.com/pyapp-kit/app-model" [tool.hatch.version] source = "vcs" -[tool.hatch.envs.test] -features = ["testing"] -[tool.hatch.envs.test.scripts] -run = "pytest -v --color=yes --cov-config=pyproject.toml --cov=magicgui --cov-report=xml --cov-report=term-missing" -[[tool.hatch.envs.test.matrix]] -backend = ["pyqt5", "pyside2", "pyqt6", "pyside6"] -[tool.hatch.envs.test.overrides] -# matrix.deps.features = [ -# { value = "min-req", if = ["min-req"] }, -# ] -matrix.backend.features = [ - { value = "pyqt5", if = ["pyqt5"] }, - { value = "pyside2", if = ["pyside2"] }, - { value = "pyqt6", if = ["pyqt6"] }, - { value = "pyside6", if = ["pyside6"] }, -] - # https://pycqa.github.io/isort/docs/configuration/options.html [tool.isort] From 4a55b334364e2ec244bc177f007119f2e4dc6a0e Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Wed, 16 Nov 2022 08:12:33 -0500 Subject: [PATCH 4/8] chore: cleanup --- pyproject.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 053de78..36f8c71 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,13 +65,11 @@ repository = "https://github.com/pyapp-kit/app-model" [tool.hatch.version] source = "vcs" - # https://pycqa.github.io/isort/docs/configuration/options.html [tool.isort] profile = "black" src_paths = ["src/app_model", "tests"] - # https://github.com/charliermarsh/ruff [tool.ruff] target-version = "py38" @@ -130,7 +128,7 @@ exclude_lines = [ "if TYPE_CHECKING:", "@overload", "except ImportError", - "pass" + "pass", ] skip_covered = true show_missing = true From 3b1da8f49e556d01bf346607fb9358f80b32324e Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Wed, 16 Nov 2022 08:12:46 -0500 Subject: [PATCH 5/8] chore: remove min-req --- pyproject.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 36f8c71..f9ac0bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,8 +30,6 @@ dependencies = ['psygnal', 'pydantic', 'in-n-out>=0.1.5', 'typing_extensions'] # extras # https://peps.python.org/pep-0621/#dependencies-optional-dependencies [project.optional-dependencies] -min-req = ['psygnal', 'pydantic', 'in-n-out>=0.1.5', 'typing_extensions'] - test = ["pytest>=6.0", "pytest-cov"] test-qt = ["pytest-qt", "fonticon-fontawesome6"] qt = ["qtpy", "superqt"] From 9f52ccc620ceb98b2f831429dcd53e82614821f7 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Wed, 16 Nov 2022 08:18:19 -0500 Subject: [PATCH 6/8] chore: add setup --- pyproject.toml | 14 +++++++++++++- setup.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 setup.py diff --git a/pyproject.toml b/pyproject.toml index f9ac0bd..5c1f2b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,12 @@ classifiers = [ "Topic :: Software Development :: User Interfaces", ] dynamic = ["version"] -dependencies = ['psygnal', 'pydantic', 'in-n-out>=0.1.5', 'typing_extensions'] +dependencies = [ + "psygnal>=0.3.4", + "pydantic>=1.8", + "in-n-out>=0.1.5", + "typing_extensions", +] # extras # https://peps.python.org/pep-0621/#dependencies-optional-dependencies @@ -63,11 +68,18 @@ repository = "https://github.com/pyapp-kit/app-model" [tool.hatch.version] source = "vcs" +[tool.hatch.envs.test] +features = ["test"] +[tool.hatch.envs.test.scripts] +run = "pytest -v --color=yes --cov-config=pyproject.toml -W i --cov=app_model --cov-report=xml --cov-report=term-missing" + + # https://pycqa.github.io/isort/docs/configuration/options.html [tool.isort] profile = "black" src_paths = ["src/app_model", "tests"] + # https://github.com/charliermarsh/ruff [tool.ruff] target-version = "py38" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..19599bc --- /dev/null +++ b/setup.py @@ -0,0 +1,29 @@ +import sys + +sys.stderr.write( + """ +=============================== +Unsupported installation method +=============================== +app-model does not support installation with `python setup.py install`. +Please use `python -m pip install .` instead. +""" +) +sys.exit(1) + + +# The below code will never execute, however GitHub is particularly +# picky about where it finds Python packaging metadata. +# See: https://github.com/github/feedback/discussions/6456 +# +# To be removed once GitHub catches up. + +setup( # noqa: F821 + name="app-model", + install_requires=[ + "psygnal>=0.3.4", + "pydantic>=1.8", + "in-n-out>=0.1.5", + "typing_extensions", + ], +) From 7ddfae546a76c8dd75ed090cbd67477f9fd8ffae Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Wed, 16 Nov 2022 08:18:49 -0500 Subject: [PATCH 7/8] chore: fix setuppy --- pyproject.toml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5c1f2b2..d539750 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -104,7 +104,7 @@ extend-ignore = [ "src/app_model/_registries.py" = ["D10"] "src/app_model/context/_expressions.py" = ["D10"] "src/app_model/types/_keys/*" = ["E501"] - +"setup.py" = ["F821"] # https://docs.pytest.org/en/6.2.x/customize.html [tool.pytest.ini_options] diff --git a/setup.py b/setup.py index 19599bc..4fb190a 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ # # To be removed once GitHub catches up. -setup( # noqa: F821 +setup( name="app-model", install_requires=[ "psygnal>=0.3.4", From 7fb9f576b642d56760bf3f24380c1e084d12e736 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Wed, 16 Nov 2022 08:23:29 -0500 Subject: [PATCH 8/8] fix: fix docs build --- src/app_model/registries/_commands_reg.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app_model/registries/_commands_reg.py b/src/app_model/registries/_commands_reg.py index 42abec9..048812c 100644 --- a/src/app_model/registries/_commands_reg.py +++ b/src/app_model/registries/_commands_reg.py @@ -152,7 +152,7 @@ def __getitem__(self, id: str) -> _RegisteredCommand: raise KeyError(f"Command {id!r} not registered") return self._commands[id] - def execute_command( + def execute_command( # noqa: D417 self, id: str, *args: Any, @@ -165,7 +165,7 @@ def execute_command( ---------- id : CommandId ID of the command to execute - args: Any + *args: Any Positional arguments to pass to the command execute_asychronously : bool Whether to execute the command asynchronously in a thread, @@ -174,7 +174,7 @@ def execute_command( to call `result()` on the returned object. Eventually, this will default to True, but we need to solve `ensure_main_thread` Qt threading issues first - kwargs: Any + **kwargs: Any Keyword arguments to pass to the command Returns