Skip to content

Commit 3cf4bf8

Browse files
committed
Enforce best practices concerning annotations, typing and symbol import/export
1 parent d9ea006 commit 3cf4bf8

12 files changed

Lines changed: 381 additions & 244 deletions

File tree

.github/workflows/ci.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ on:
1010
- "docs/**"
1111
- ".readthedocs.yaml"
1212
- "pyproject.toml"
13-
- "setup.cfg"
13+
- "mypy.ini"
1414
- "**/*.py"
1515
pull_request:
1616
paths:
@@ -28,9 +28,16 @@ concurrency:
2828
cancel-in-progress: true
2929

3030
jobs:
31+
ruff:
32+
runs-on: ubuntu-latest
33+
timeout-minutes: &timeout-minutes 5
34+
steps:
35+
- uses: actions/checkout@v6
36+
- uses: astral-sh/ruff-action@v4.0.0
37+
3138
mypy:
3239
runs-on: ${{ matrix.os }}
33-
timeout-minutes: &timeout-minutes 5
40+
timeout-minutes: *timeout-minutes
3441
strategy:
3542
# mypy is os and python-version sensitive. Test on all supported combinations
3643
matrix:

mypy.ini

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
[mypy]
22
mypy_path = src/, typings/
3-
exclude = build
3+
exclude = build/, dist/
44
strict = True
55

66
# Leverage type inference for function return type
77
disallow_untyped_calls = False
88
disallow_incomplete_defs = False
99
disallow_untyped_defs = False
1010

11-
# https://github.com/python/mypy/issues/8234 (post assert "type: ignore")
12-
# https://github.com/python/mypy/issues/8823 (version specific "type: ignore")
13-
warn_unused_ignores = False
14-
1511
disable_error_code =
1612
# https://github.com/python/mypy/issues/6232 (redefinition with correct type)
1713
attr-defined, assignment,

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ dev = [
8282
{ include-group = "docs" },
8383
"ewmhlib",
8484
"mypy>=0.990,<2",
85+
"ruff>=0.15.16",
8586
"types-python-xlib>=0.32",
8687
"types-pywin32>=305.0.0.3",
8788
]

ruff.toml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
[lint]
2+
future-annotations = true
3+
# https://docs.astral.sh/ruff/rules/
4+
extend-select = [
5+
"ANN2", # flake8-annotations: missing-return-type
6+
"PYI", # flake8-pyi
7+
"FA", # flake8-future-annotations
8+
"ICN", # flake8-import-conventions
9+
"F401", # unused-import
10+
"YTT", # flake8-2020
11+
"TC", # flake8-type-checking
12+
"TID", # flake8-tidy-imports
13+
"UP", # pyupgrade
14+
"RUF", # Ruff-specific rules
15+
"F404", # late-future-import
16+
"PGH", # pygrep-hooks (blanket-* rules)
17+
]
18+
ignore = [
19+
# Only enforce return types on public functions. Where otherwise mypy infers as Any
20+
# Still worth running `ruff check --fix --select=202` once in a while for autofixes
21+
"ANN202", # missing-return-type-private-function
22+
# Explicit is preferred
23+
"UP015", # redundant-open-modes,
24+
# Autofixes print-f style formatting to f-strings,
25+
# which is sometimes simpler, but looses template code reading semantics
26+
"UP032", # f-string
27+
# TC helps prevent circular imports, reduce runtime cost of typing symbols,
28+
# and prevent leaking implementations details into modules
29+
# However stdlib is not at risk of circular import, is clearly not public API,
30+
# and assume it's gonna be included in the import chain at some point anyway
31+
"TC003", # typing-only-standard-library-import
32+
# Typeshed doesn't want complex or non-literal defaults for maintenance and testing reasons.
33+
# This doesn't affect us, let's have more complete stubs.
34+
"PYI011", # typed-argument-default-in-stub
35+
"PYI014", # argument-default-in-stub
36+
"PYI053", # string-or-bytes-too-long
37+
38+
# TODO: Consider later
39+
"UP031", # printf-string-formatting
40+
"RUF059", # unused-unpacked-variable
41+
"E722", # bare-except
42+
]
43+
# F401 would remove imports not marked as explicit re-exports, which may break API boundaries
44+
extend-unsafe-fixes = ["F401"]
45+
46+
[lint.per-file-ignores]
47+
"**/typings/**/*.pyi" = [
48+
"PGH003", # TODO: Blanket ignores until using pyobj type stubs
49+
"F811", # Re-exports false positives
50+
# The following can't be controlled for external libraries:
51+
"A", # Shadowing builtin names
52+
"E741", # ambiguous variable name
53+
"F403", # `from . import *` used; unable to detect undefined names
54+
"FBT", # flake8-boolean-trap
55+
"ICN001", # unconventional-import-alias
56+
"N8", # Naming conventions
57+
"PLC2701", # Private name import
58+
"PLE0302", # The special method expects a given signature
59+
"PLR0904", # Too many public methods
60+
"PLR0913", # Argument count
61+
"PLR0917", # Too many positional arguments
62+
"PLW3201", # misspelled dunder method name
63+
"SLOT", # flake8-slots
64+
# Stubs can sometimes re-export entire modules.
65+
# Issues with using a star-imported name will be caught by type-checkers.
66+
"F405", # may be undefined, or defined from star imports
67+
# It's normal to be missing annotations for local stubs.
68+
# If they were complete, we'd upload them to typeshed!
69+
"ANN0",
70+
"ANN2",
71+
]
72+
73+
# https://docs.astral.sh/ruff/settings/#lintflake8-type-checking
74+
[lint.flake8-type-checking]
75+
quote-annotations = true

src/pywinctl/__init__.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
#!/usr/bin/python
2-
# -*- coding: utf-8 -*-
2+
from ._main import (Re, Window, checkPermissions, getActiveWindow,
3+
getActiveWindowTitle, getAllAppsNames, getAllAppsWindowsTitles,
4+
getAllTitles, getAllWindows, getAppsWithName, getWindowsWithTitle,
5+
getAllWindowsDict, getTopWindowAt, getWindowsAt, displayWindowsUnderMouse,
6+
getAllScreens, getScreenSize, getWorkArea, getMousePos
7+
)
38

4-
__all__ = [
9+
__all__ = [ # noqa: RUF022
510
"version", "Re",
611
# OS Specifics
712
"Window", "checkPermissions", "getActiveWindow", "getActiveWindowTitle", "getWindowsWithTitle",
@@ -18,9 +23,3 @@ def version(numberOnly: bool = True) -> str:
1823
return ("" if numberOnly else "PyWinCtl-")+__version__
1924

2025

21-
from ._main import (Re, Window, checkPermissions, getActiveWindow,
22-
getActiveWindowTitle, getAllAppsNames, getAllAppsWindowsTitles,
23-
getAllTitles, getAllWindows, getAppsWithName, getWindowsWithTitle,
24-
getAllWindowsDict, getTopWindowAt, getWindowsAt, displayWindowsUnderMouse,
25-
getAllScreens, getScreenSize, getWorkArea, getMousePos
26-
)

0 commit comments

Comments
 (0)