Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run mypy for full repo & fix all mypy issues #227

Merged
merged 74 commits into from
Mar 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
675b245
add __init__.py files to test folders
fohrloop Mar 14, 2024
1cdd2ec
ignore venv with mypy
fohrloop Mar 14, 2024
2359143
always return a list in get_method
fohrloop Mar 14, 2024
11dca4e
add None check for p.stdout (for mypy)
fohrloop Mar 14, 2024
6596b61
fix a mypy warning in conftest.py about DbusService = None
fohrloop Mar 14, 2024
7257d57
fix broken test for get_methods (was returning None, now [])
fohrloop Mar 14, 2024
066ba5c
fix mypy import warning
fohrloop Mar 14, 2024
434ceea
fix equality checks between StrEnum and str (mypy)
fohrloop Mar 14, 2024
7c529da
assert equality holds both ways with StrEnum and str
fohrloop Mar 14, 2024
7658bb4
define the __eq__ and __hash__ for the EnumMemberString
fohrloop Mar 14, 2024
f5180c4
mypy SomeConst.FOO == 'somestr' fix
fohrloop Mar 14, 2024
7b14ddb
remove unused EnumMemberString methods
fohrloop Mar 17, 2024
9333186
add return type to _create_class in testmethods.py
fohrloop Mar 17, 2024
6cbe354
ignore the DbusService and start_dbus_service types in conftest.py
fohrloop Mar 17, 2024
a65a799
DBusMethod.of(): add return type
fohrloop Mar 17, 2024
e7f820c
define unnamed Methods with special constant instead of using None
fohrloop Mar 17, 2024
18cf8cd
drop support for get_methods(None)
fohrloop Mar 17, 2024
1902ad8
fix 'unreachable code' warning in get_methods
fohrloop Mar 17, 2024
949f8a9
improve type formatting in registry.py
fohrloop Mar 17, 2024
e1b54c4
add overloads to get_methods
fohrloop Mar 17, 2024
c22b302
ignore purposeful wrong types in tests
fohrloop Mar 17, 2024
67ea4c8
import overload to fix errors in tests
fohrloop Mar 17, 2024
c5ac4e8
ignore @overload in coverage
fohrloop Mar 17, 2024
acd7190
simplify the StrEnum
fohrloop Mar 18, 2024
ab40f0c
rename self to cls in ConstantEnumMeta method
fohrloop Mar 18, 2024
96da81e
rename: StrEnumMeta
fohrloop Mar 18, 2024
9677bb0
update StrEnumMeta docstring
fohrloop Mar 18, 2024
c5d686d
fix mypy errors related to StageName
fohrloop Mar 18, 2024
a853e0d
format
fohrloop Mar 18, 2024
8f1921a
fix mypy: StrEnumMeta._check_uniqueness()
fohrloop Mar 18, 2024
793e9d5
ignore mypy error in purposeful ValueError
fohrloop Mar 18, 2024
08ac3a6
MethodsPriorityOrder: List -> Sequence
fohrloop Mar 18, 2024
8734543
mypy fix: importing PlatformName
fohrloop Mar 18, 2024
323ec8b
fix imports for mypy
fohrloop Mar 18, 2024
b402eff
refactor: dbus_method fixture to conftest
fohrloop Mar 18, 2024
600be89
fix a type annotation in test_process_dbus_call
fohrloop Mar 18, 2024
87fae12
ignore mypy type errors when testing that
fohrloop Mar 18, 2024
0d24d13
add type annotations to Method.has_enter, .has_exit and .has_hearbeat
fohrloop Mar 18, 2024
d48c510
mypy: ignore the purposeful re-defines of SomeMethod in test_method.py
fohrloop Mar 18, 2024
69b5a35
ignore the wrong value in on_fail in tests
fohrloop Mar 18, 2024
08b63bb
ignore the redefs of MyConst in test_strenum.py
fohrloop Mar 18, 2024
5337c59
require types-colorame
fohrloop Mar 18, 2024
9073288
fix mypy complaining about from invoke import task
fohrloop Mar 18, 2024
c830ffb
remove duplicated tests
fohrloop Mar 18, 2024
bb13e0f
split a test in three
fohrloop Mar 18, 2024
a41ce65
rename: test_strenum_*
fohrloop Mar 18, 2024
b2ddfd1
remove unnecessary type: ignores
fohrloop Mar 18, 2024
bec4c4f
mypy: add missing self in methods in test_method.py
fohrloop Mar 18, 2024
5d634b2
Don't inheric Mode from ABC
fohrloop Mar 18, 2024
9657b2a
mypy ignore unreachable
fohrloop Mar 18, 2024
ed86b3d
fix few mypy unreachable false positives
fohrloop Mar 18, 2024
90bd275
add missing __future__.annotations import
fohrloop Mar 23, 2024
74bbcdb
refactor get_new_classname
fohrloop Mar 23, 2024
cfe6eb6
add type annotations for Queues
fohrloop Mar 23, 2024
1102530
add type hints for should_stop Callable
fohrloop Mar 23, 2024
cd3f15d
add type hint for Queue
fohrloop Mar 23, 2024
876c2ce
fix type annotation of Tuple returned in DbusService (tests)
fohrloop Mar 23, 2024
8718d98
mypy fix: Tuple in DbusService.handle_method
fohrloop Mar 23, 2024
0c5e366
mypy fix: DbusService start_service dbus address
fohrloop Mar 23, 2024
780f41c
mypy: fix errors in tasks.py
fohrloop Mar 23, 2024
66e94da
mypy fix: a list in conf.py
fohrloop Mar 23, 2024
2b74c96
make corresponding *Value for each StrEnum
fohrloop Mar 23, 2024
bf818be
mypy fix: get_method mode arg can be any str
fohrloop Mar 23, 2024
a0460eb
add do_assert()
fohrloop Mar 23, 2024
13cd116
fix mypy unreachable issues
fohrloop Mar 23, 2024
568d7e3
make Mode.activation_result always ActivationResult
fohrloop Mar 23, 2024
c953994
run mypy for whole repo, not just ./wakepy
fohrloop Mar 23, 2024
bd7d5d7
add test for StrEnum.name
fohrloop Mar 23, 2024
b58461a
add pytest to tox check requirements
fohrloop Mar 23, 2024
6dc29d0
add required packages for 'tox check' step
fohrloop Mar 23, 2024
67f6702
add Python 3.7 typing support (Literal, get_args)
fohrloop Mar 23, 2024
d36e2c4
refactor: assert_strenum_values
fohrloop Mar 23, 2024
cf7c129
restore 100% test coverage
fohrloop Mar 23, 2024
0b5315d
add mention about installing typing-extensions
fohrloop Mar 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Wakepy has two main modes:
- Permissive MIT licence
- Low amount of python dependencies
- For using the D-Bus methods on Linux: [jeepney](https://jeepney.readthedocs.io/)
- On Python 3.7: [typing-extensions](https://pypi.org/project/typing-extensions/).
- Otherwise: None


Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
exclude_patterns: list[str] = []

html_static_path = ["_static"]

Expand Down
2 changes: 2 additions & 0 deletions docs/source/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pip install wakepy

```{note}
On Linux will install also **[`jeepney`](https://jeepney.readthedocs.io/)** for DBus communication (if not installed). On other systems there are no python requirements.

On Python 3.7 installs [typing-extensions](https://pypi.org/project/typing-extensions/).
```

## Basic Usage
Expand Down
18 changes: 16 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ authors = [
]
dependencies = [
# For using the D-Bus based methods
"jeepney >= 0.7.1;sys_platform=='linux'"
"jeepney >= 0.7.1;sys_platform=='linux'",
# The typing.Literal was introduced in Python 3.8. Need to install
# typing-extensions for Python 3.7 support.
'typing-extensions; python_version < "3.8.0"',
]

# Python 3.7 introduces from __future__ import annotations
Expand Down Expand Up @@ -38,7 +41,8 @@ dev = [
"IPython",
"invoke==2.2.0",
# Colorama is used with the tasks.py (invoke commands)
"colorama==0.4.6"
"colorama==0.4.6",

]
# For building documentation
doc = [
Expand Down Expand Up @@ -67,10 +71,16 @@ test =[
]
# For linters, code analysis and formatting tools
check = [
"invoke==2.2.0",
"black==24.2.0",
"mypy==1.9.0",
"isort==5.13.2",
"ruff==0.3.2",
# some version of pytest required for checking types. Perhaps any recent
# version of pytest will do.
"pytest",
"types-colorama", # for mypy
"time-machine==2.14.0" # for mypy
]


Expand All @@ -87,6 +97,7 @@ requires = ["flit_core >=3.2,<4"]
build-backend = "flit_core.buildapi"

[tool.mypy]
exclude = ['venv', '.venv']
check_untyped_defs = true
disallow_any_generics = true
no_implicit_optional = true
Expand Down Expand Up @@ -119,6 +130,7 @@ exclude_lines = [
"pragma: no cover",
"if typing.TYPE_CHECKING:",
"@(abc\\.)?abstractmethod",
"@(typing\\.)?overload",
]

[tool.coverage.coverage_conditional_plugin.omit]
Expand All @@ -127,6 +139,8 @@ exclude_lines = [

[tool.coverage.coverage_conditional_plugin.rules]
no-cover-if-no-dbus = "platform_system != 'Linux'"
no-cover-if-py-gte-38 = "sys_version_info >= (3, 8)"
no-cover-if-py-lt-38 = "sys_version_info < (3, 8)"

[tool.isort]
profile = "black"
Expand Down
15 changes: 8 additions & 7 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import typing

from colorama import Fore
from invoke import task
from invoke import task # type: ignore

if typing.TYPE_CHECKING:
from invoke.runners import Result
Expand All @@ -34,30 +34,31 @@
def get_run_with_print(c):
def run_with_print(cmd: str, ignore_errors: bool = False) -> Result:
print("Running:", Fore.YELLOW, cmd, Fore.RESET)
return c.run(cmd, pty=platform.system() == "Linux", warn=ignore_errors)
res: Result = c.run(cmd, pty=platform.system() == "Linux", warn=ignore_errors)
return res

return run_with_print


@task
def format(c):
def format(c) -> None:
run = get_run_with_print(c)
run("python -m isort .")
run("python -m black .")
run("python -m ruff check --fix .")


@task
def check(c) -> int:
def check(c) -> None:
run = get_run_with_print(c)
run("python -m isort --check .")
run("python -m black --check .")
run("python -m ruff check --no-fix .")
run("python -m mypy ./wakepy")
run("python -m .")


@task
def docs(c):
def docs(c) -> None:
"""Starts sphinx build with live-reload on browser."""

run = get_run_with_print(c)
Expand All @@ -68,7 +69,7 @@ def docs(c):


@task
def test(c, pdb: bool = False):
def test(c, pdb: bool = False) -> None:
run = get_run_with_print(c)
pdb_flag = " --pdb " if pdb else ""
res = run(
Expand Down
Empty file added tests/__init__.py
Empty file.
51 changes: 51 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import sys
from typing import Optional

import pytest

from wakepy.core.strenum import StrEnum

if sys.version_info < (3, 8): # pragma: no-cover-if-py-gte-38
import typing_extensions as typing
else: # pragma: no-cover-if-py-lt-38
import typing


@pytest.fixture
def do_assert():
"""Function to be used instead of assert statement."""

# Fixes issue with mypy: https://github.com/python/mypy/issues/11969
# In short, when testing and needing to assert that a variable has certain
# value, and then mutating the value and asserting the value again (
# against new assumed value), mypy does not handle that case but you'll get
# [unreachable] errors. Using `do_assert(...)` instead of `assert ...` in
# tests fixes this.

def _do_assert(
expression: bool,
message: Optional[str] = None,
) -> None:
"""Original idea: Nikita Sobolev (safe-assert)[1]. Fixed the return
type to make this usable[2]

[1] https://github.com/wemake-services/safe-assert/blob/e3ebfe72a910915e227a9f5447a0f7f56d5219e6/safe_assert/__init__.py
[2] https://github.com/wemake-services/safe-assert/pull/131
"""
if not expression:
if message:
raise AssertionError(message)
raise AssertionError

return _do_assert


@pytest.fixture
def assert_strenum_values():

def _assert_strenum_values(strenum_cls: typing.Type[StrEnum], values: typing.Any):
"""Note: `values` is a typing.Literal. Could not find a type annotation
for that"""
assert set(typing.get_args(values)) == {member.value for member in strenum_cls}

return _assert_strenum_values
Empty file added tests/integration/__init__.py
Empty file.
12 changes: 9 additions & 3 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@
import pytest

if sys.platform.lower().startswith("linux"):
from dbus_service import DBusService, start_dbus_service
from tests.integration.dbus_service import DBusService, start_dbus_service
else:
DBusService = None
start_dbus_service = None

class DBusService: ... # type: ignore

def start_dbus_service(): ... # type: ignore


from wakepy.core import DBusAddress, DBusMethod

Expand Down Expand Up @@ -56,6 +59,9 @@ def private_bus():
env={"DBUS_VERBOSE": "1"},
)

if p.stdout is None:
raise RuntimeError("Error when starting private bus")

bus_address = p.stdout.readline().decode("utf-8").strip()

logger.info("Initiated private bus: %s", bus_address)
Expand Down
20 changes: 13 additions & 7 deletions tests/integration/dbus_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import queue
import threading
import time
from typing import Callable, Optional, Tuple, Type
from typing import Any, Callable, Optional, Tuple, Type

from jeepney import HeaderFields, MessageType, new_error, new_method_return
from jeepney.bus_messages import message_bus
Expand Down Expand Up @@ -41,7 +41,9 @@ class DBusService:

addr: DBusAddress

def __init__(self, bus_address: str, queue_: queue.Queue, stop: Callable):
def __init__(
self, bus_address: str, queue_: queue.Queue[str], stop: Callable[[], bool]
):
"""
Parameters
----------
Expand Down Expand Up @@ -139,7 +141,9 @@ def _get_error_message(self, msg, method=".Error.NoMethod"):
"""Create an error message for replying to a message"""
return new_error(msg, self.bus_name + method)

def handle_method(self, method: str, args: Tuple) -> Optional[Tuple[str, Tuple]]:
def handle_method(
self, method: str, args: Tuple[Any, ...]
) -> Optional[Tuple[str, Tuple[Any, ...]]]:
"""Should return either None (when method does not exist), or tuple of
output signature (like "ii" or "sus", etc.), and output values which
are of the type defined by the output signature
Expand All @@ -158,15 +162,17 @@ def start_dbus_service(
--print-address. If not given, uses the service_cls.addr.bus.
"""

queue_ = queue.Queue()
queue_: queue.Queue[str] = queue.Queue()
should_stop = False

def start_service(
service: Type[DBusService], queue_: queue.Queue, should_stop: Callable
service: Type[DBusService],
queue_: queue.Queue[str],
should_stop: Callable[[], bool],
):
logger.info(f"Launching dbus service: {service.addr.service}")

service_ = service(bus_address or service.addr.bus, queue_, stop=should_stop)
addr = bus_address or service.addr.bus or "SESSION"
service_ = service(addr, queue_, stop=should_stop)
service_.start(
server_name=service.addr.service,
object_path=service.addr.path,
Expand Down
Empty file added tests/unit/__init__.py
Empty file.
Empty file.
14 changes: 13 additions & 1 deletion tests/unit/test_core/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from wakepy.core.method import Method, PlatformName
from wakepy.core import DBusAddress, DBusMethod, Method, PlatformName

# B, D, E
FIRST_MODE = "first_mode"
Expand Down Expand Up @@ -76,3 +76,15 @@ class MethodE(TestMethod):
class MethodF(TestMethod):
name = "F"
mode = SECOND_MODE


@pytest.fixture
def service():
return DBusAddress(path="/foo", service="wakepy.foo", interface="/foo")


@pytest.fixture
def dbus_method(service: DBusAddress):
return DBusMethod(
name="test-method", signature="isi", params=("first", "second", "third")
).of(service)
Empty file.
18 changes: 13 additions & 5 deletions tests/unit/test_core/test_activation/test_activation.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,25 @@

import pytest
import time_machine
from testmethods import (

from tests.unit.test_core.testmethods import (
FAILURE_REASON,
METHOD_MISSING,
METHOD_OPTIONS,
WakepyMethodTestError,
get_test_method_class,
iterate_test_methods,
)

from wakepy.core import DBusAdapter, MethodActivationResult, get_methods
from wakepy.core import (
DBusAdapter,
Method,
MethodActivationResult,
PlatformName,
get_methods,
)
from wakepy.core.activation import (
StageName,
StageNameValue,
WakepyFakeSuccess,
activate_method,
activate_mode,
Expand All @@ -32,7 +39,7 @@
try_enter_and_heartbeat,
)
from wakepy.core.heartbeat import Heartbeat
from wakepy.core.method import Method, MethodError, PlatformName
from wakepy.core.method import MethodError


def test_activate_without_methods(monkeypatch):
Expand Down Expand Up @@ -592,10 +599,11 @@ def test_deactivate_fail_heartbeat_not_stopping():
deactivate_method(method, heartbeat)


def test_stagename():
def test_stagename(assert_strenum_values):
assert StageName.PLATFORM_SUPPORT == "PLATFORM_SUPPORT"
assert StageName.ACTIVATION == "ACTIVATION"
assert StageName.REQUIREMENTS == "REQUIREMENTS"
assert_strenum_values(StageName, StageNameValue)


# These are the only "falsy" values for WAKEPY_FAKE_SUCCESS
Expand Down
7 changes: 5 additions & 2 deletions tests/unit/test_core/test_activation/test_prioritization.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import pytest

from wakepy.core import PlatformName
from wakepy.core.activation import (
check_methods_priority,
get_prioritized_methods,
get_prioritized_methods_groups,
sort_methods_by_priority,
)
from wakepy.core.method import PlatformName
from wakepy.core.registry import get_methods


Expand Down Expand Up @@ -70,7 +70,10 @@ def test_check_methods_priority():
TypeError,
match=re.escape("methods_priority must be a list[str | set[str]]!"),
):
check_methods_priority(methods_priority=[MethodA], methods=methods)
check_methods_priority(
methods_priority=[MethodA], # type: ignore
methods=methods,
)


@pytest.mark.usefixtures("provide_methods_a_f")
Expand Down
Loading
Loading