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

Mypy: Set disallow_untyped_defs = true #242

Merged
merged 14 commits into from
Apr 12, 2024
Merged
7 changes: 6 additions & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@
#
# -- Project information -----------------------------------------------------

import typing

from wakepy import __version__

if typing.TYPE_CHECKING:
from sphinx.application import Sphinx

project = "wakepy"
copyright = "2023–2024, Niko Föhr"
author = "Niko Föhr"
Expand Down Expand Up @@ -98,5 +103,5 @@
numpydoc_class_members_toctree = False


def setup(app):
def setup(app: Sphinx) -> None:
app.add_js_file("wakepy.js", loading_method="defer")
13 changes: 9 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ build-backend = "setuptools.build_meta"
version_file = "src/wakepy/_version.py"

[tool.mypy]
exclude = ['venv', '.venv']
exclude = ['venv', '.venv', 'docs']
check_untyped_defs = true
disallow_any_generics = true
no_implicit_optional = true
Expand All @@ -63,14 +63,19 @@ warn_return_any = true
warn_unreachable = true
warn_unused_configs = true
no_implicit_reexport = true
disallow_untyped_defs = true

[[tool.mypy.overrides]]
module = 'jeepney.*'
module = [
'jeepney.*',
'tox.*'
]
ignore_missing_imports = true

[[tool.mypy.overrides]]
module = 'tox.*'
ignore_missing_imports = true
module = ['tests.*', 'tasks']
disallow_untyped_defs = false



[tool.pytest.ini_options]
Expand Down
6 changes: 3 additions & 3 deletions src/wakepy/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"""


def main():
def main() -> None:
modename = parse_arguments(sys.argv[1:])
mode = create_mode(modename=modename, on_fail=handle_activation_error)
print(get_startup_text(mode=modename))
Expand All @@ -56,7 +56,7 @@ def main():
print("\n\nExited.")


def handle_activation_error(result: ActivationResult):
def handle_activation_error(result: ActivationResult) -> None:
from wakepy import __version__

error_text = f"""
Expand Down Expand Up @@ -152,7 +152,7 @@ def get_startup_text(mode: ModeName) -> str:
return "\n".join((wakepy_text, options_txt)) + "\n"


def wait_until_keyboardinterrupt():
def wait_until_keyboardinterrupt() -> None:
spinning_chars = ["|", "/", "-", "\\"]
try:
for char in itertools.cycle(spinning_chars): # pragma: no branch
Expand Down
18 changes: 13 additions & 5 deletions src/wakepy/core/activation.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ class MethodActivationResult:

failure_reason: str = ""

def __repr__(self):
def __repr__(self) -> str:
error_at = " @" + self.failure_stage if self.failure_stage else ""
failure_reason = f', "{self.failure_reason}"' if self.success is False else ""
success_str = (
Expand Down Expand Up @@ -617,7 +617,11 @@ def deactivate_method(method: Method, heartbeat: Optional[Heartbeat] = None) ->
f"a bug report and rebooting for clearing the mode. "
)
try:
retval = method.exit_mode()
# The Method.exit_mode() *should* always return None. However, it
# is not possible to control user created Methods implementation,
# so this is a safety net for users not having strict static type
# checking.
retval = method.exit_mode() # type: ignore[func-returns-value]
if retval is not None:
raise ValueError("exit_mode returned a value other than None!")
except Exception as e:
Expand Down Expand Up @@ -807,7 +811,7 @@ def _try_method_call(method: Method, mthdname: str) -> Tuple[MethodOutcome, str]
return outcome, err_message


def _rollback_with_exit(method):
def _rollback_with_exit(method: Method) -> None:
"""Roll back entering a mode by exiting it.

Raises
Expand All @@ -824,7 +828,11 @@ def _rollback_with_exit(method):
return

try:
exit_outcome = method.exit_mode()
# The Method.exit_mode() *should* always return None. However, it
# is not possible to control user created Methods implementation,
# so this is a safety net for users not having strict static type
# checking.
exit_outcome = method.exit_mode() # type: ignore[func-returns-value]
if exit_outcome is not None:
raise ValueError("exit_method did not return None")
except Exception as exc:
Expand Down Expand Up @@ -853,7 +861,7 @@ class WakepyFakeSuccess(Method):

supported_platforms = (CURRENT_PLATFORM,)

def enter_mode(self):
def enter_mode(self) -> None:
"""Function which says if fake success should be enabled

Fake success is controlled via WAKEPY_FAKE_SUCCESS environment
Expand Down
4 changes: 2 additions & 2 deletions src/wakepy/core/dbus.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ def _args_as_tuple(
assert isinstance(args, dict), "args may only be tuple, list or dict"
return self.__dict_args_as_tuple(args, method)

def __check_args_length(self, args: Tuple[Any, ...], method: DBusMethod):
def __check_args_length(self, args: Tuple[Any, ...], method: DBusMethod) -> None:
if method.params is None:
# not possible to check.
return
Expand Down Expand Up @@ -277,7 +277,7 @@ class DBusAdapter:
Exception will be omitted if using the high-level API of wakepy.
"""

def process(self, call: DBusMethodCall):
def process(self, call: DBusMethodCall) -> object:
"""Processes a :class:`~wakepy.core.DBusMethodCall`.

Parameters
Expand Down
14 changes: 7 additions & 7 deletions src/wakepy/core/method.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def __init__(self, dbus_adapter: Optional[DBusAdapter] = None):
# only on methods using D-Bus.
self._dbus_adapter = dbus_adapter

def __init_subclass__(cls, **kwargs) -> None:
def __init_subclass__(cls, **kwargs: object) -> None:
"""These are automatically added. They tell if the `enter_mode`,
`exit_mode` and `heartbeat` methods are implemented in the Method
subclass. (should not to touch these manually)"""
Expand Down Expand Up @@ -172,7 +172,7 @@ def caniuse(
# could test that it exist on PATH. (and that the version is
# suitable)

def enter_mode(self):
def enter_mode(self) -> None:
"""Enter to a Mode using this Method. Pair with a `exit_mode`.

Returns
Expand Down Expand Up @@ -202,7 +202,7 @@ def enter_mode(self):
#
return

def exit_mode(self):
def exit_mode(self) -> None:
"""Exit from a Mode using this Method. Paired with `enter_mode`

Returns
Expand Down Expand Up @@ -232,7 +232,7 @@ def exit_mode(self):
`heartbeat()`.
"""

def heartbeat(self):
def heartbeat(self) -> None:
"""Called periodically, every `heartbeat_period` seconds.

Returns
Expand Down Expand Up @@ -270,14 +270,14 @@ def has_exit(self) -> bool:
def has_heartbeat(self) -> bool:
return self._has_heartbeat

def __str__(self):
def __str__(self) -> str:
return f"<wakepy Method: {self.__class__.__name__}>"

def __repr__(self):
def __repr__(self) -> str:
return f"<wakepy Method: {self.__class__.__name__} at {hex(id(self))}>"

@classmethod
def _is_unnamed(cls):
def _is_unnamed(cls) -> bool:
return cls.name == unnamed


Expand Down
34 changes: 28 additions & 6 deletions src/wakepy/core/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,9 @@ def create_mode(
modename: ModeName,
methods: Optional[StrCollection] = None,
omit: Optional[StrCollection] = None,
**kwargs,
methods_priority: Optional[MethodsPriorityOrder] = None,
on_fail: OnFail = "error",
dbus_adapter: Type[DBusAdapter] | DBusAdapterTypeSeq | None = None,
) -> Mode:
"""
Creates and returns a Mode (a context manager).
Expand All @@ -292,9 +294,23 @@ def create_mode(
a "blacklist" filter. Any Method in `omit` but not in the selected mode
will be silently ignored. Cannot be used same time with `methods`.
Optional.
**kwargs
Passed to Mode as initialization arguments.

on_fail: "error" | "warn" | "pass" | Callable
Determines what to do in case mode activation fails. Valid options
are: "error", "warn", "pass" and a callable. If the option is
"error", raises wakepy.ActivationError. Is selected "warn", issues
warning. If "pass", does nothing. If `on_fail` is a callable, it
must take one positional argument: result, which is an instance of
ActivationResult. The ActivationResult contains more detailed
information about the activation process.
methods_priority: list[str | set[str]]
The priority order, which is a list of method names or asterisk
('*'). The asterisk means "all rest methods" and may occur only
once in the priority order, and cannot be part of a set. All method
names must be unique and must be part of the `methods`.
dbus_adapter:
For using a custom dbus-adapter. Optional. If not given, the
default dbus adapter is used, which is :class:`~wakepy.dbus_adapters.\\
jeepney.JeepneyDBusAdapter`

Returns
-------
Expand All @@ -303,10 +319,16 @@ def create_mode(
"""
methods_for_mode = get_methods_for_mode(modename)
selected_methods = select_methods(methods_for_mode, use_only=methods, omit=omit)
return Mode(name=modename, methods=selected_methods, **kwargs)
return Mode(
name=modename,
methods=selected_methods,
methods_priority=methods_priority,
on_fail=on_fail,
dbus_adapter=dbus_adapter,
)


def handle_activation_fail(on_fail: OnFail, result: ActivationResult):
def handle_activation_fail(on_fail: OnFail, result: ActivationResult) -> None:
if on_fail == "pass":
return
elif on_fail == "warn" or on_fail == "error":
Expand Down
2 changes: 1 addition & 1 deletion src/wakepy/core/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class MethodRegistryError(RuntimeError):
"""Any error which is related to the method registry"""


def register_method(method_class: Type[Method]):
def register_method(method_class: Type[Method]) -> None:
"""Registers a subclass of Method to the method registry"""

if method_class._is_unnamed():
Expand Down
39 changes: 31 additions & 8 deletions src/wakepy/core/strenum.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@

from __future__ import annotations

import typing
from enum import Enum, EnumMeta, auto
from typing import Any, ValuesView

if typing.TYPE_CHECKING:
from enum import _EnumDict
from typing import Any, Callable, Dict, KeysView, Tuple, Type, ValuesView


class StrEnumMeta(EnumMeta):
Expand All @@ -18,21 +22,38 @@ class StrEnumMeta(EnumMeta):
2) `unique` parameter when creating constants.
"""

# The __prepare__ function signature has some problems in mypy 1.9.0
# CPython 3.10.12. It says that `Signature of "__prepare__" incompatible
# with supertype X` where X is "EnumMeta" or "type". Could not find a
# function signature which would be okay with both the EnumMeta and type
# superclasses so just ignoring the error.
@classmethod
def __prepare__(metacls, clsname, bases, **_):
def __prepare__( # type: ignore[override]
metacls: Type[StrEnumMeta],
clsname: str,
bases: Tuple[type, ...],
**_: Any,
) -> _EnumDict:
# This is needed since we have to allow passing kwargs to __init__
# Needed on 3.7.x, not needed on 3.10. (not tested 3.8 & 3.9)
return super().__prepare__(clsname, bases)

def __new__(metacls, clsname, bases, classdict, **_):
def __new__(
metacls: Type[StrEnumMeta],
clsname: str,
bases: Tuple[Type[object], ...],
classdict: _EnumDict,
**_: Dict[str, object],
) -> StrEnumMeta:
# This is needed since we have to allow passing kwargs to __init__

return super().__new__(metacls, clsname, bases, classdict)

def __init__(cls, *_, unique=False):
def __init__(cls, *_: Tuple[object, ...], unique: bool = False) -> None:
if unique:
cls._check_uniqueness()

def _check_uniqueness(cls):
def _check_uniqueness(cls) -> None:
vals: ValuesView[Enum] = cls.__members__.values()
if len(vals) > len(set(vals)):
raise ValueError("The values must be unique!")
Expand All @@ -52,11 +73,11 @@ def __contains__(cls, value: Any) -> bool:
return value in cls.values()

@property
def keys(cls):
def keys(cls) -> Callable[[], KeysView[str]]:
return cls.__members__.keys

@property
def values(cls):
def values(cls) -> Callable[[], ValuesView[str]]:
return cls.__members__.values


Expand Down Expand Up @@ -100,7 +121,9 @@ class StrEnum(str, Enum, metaclass=StrEnumMeta):
"""

@staticmethod
def _generate_next_value_(name: str, *_) -> str:
def _generate_next_value_(
name: str, start: int, count: int, last_values: list[Any]
) -> str:
"""Turn auto() value to be a string corresponding to the enumeration
member name

Expand Down
2 changes: 1 addition & 1 deletion src/wakepy/dbus_adapters/jeepney.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class JeepneyDBusAdapter(DBusAdapter):
# timeout for dbus calls, in seconds
timeout = 2

def process(self, call: DBusMethodCall):
def process(self, call: DBusMethodCall) -> object:
addr = DBusAddress(
object_path=call.method.path,
bus_name=call.method.service,
Expand Down
Loading