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..d539750 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,15 +13,24 @@ 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'] +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 @@ -31,12 +40,6 @@ 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 +60,51 @@ 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.hatch.version] +source = "vcs" -[tool.setuptools.package-data] -"*" = ["py.typed"] +[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://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", +] -# 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" +[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"] +"src/app_model/types/_keys/*" = ["E501"] +"setup.py" = ["F821"] # https://docs.pytest.org/en/6.2.x/customize.html [tool.pytest.ini_options] @@ -149,19 +138,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 +155,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" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..4fb190a --- /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( + name="app-model", + install_requires=[ + "psygnal>=0.3.4", + "pydantic>=1.8", + "in-n-out>=0.1.5", + "typing_extensions", + ], +) 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..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,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: