diff --git a/pyproject.toml b/pyproject.toml index 6d37736..cbf0271 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,42 @@ log_cli = true [tool.ruff] line-length = 118 +select = [ + "A", # flake8-builtins + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "D300", # pydocstyle: Forbid ''' in docstrings + "DTZ", # flake8-datetimez + "E", # pycodestyle + "EXE", # flake8-executable + "F", # pyflakes + "G", # flake8-logging-format + "I", # isort + "ICN", # flake8-import-conventions + "ISC", # flake8-implicit-str-concat + "PLE", # pylint errors + "PGH", # pygrep-hooks + "PT", # flake8-pytest-style + "RSE", # flake8-raise + "RUF", # ruff rules + "T10", # flake8-debugger + "TCH", # flake8-type-checking + "W", # warnings (mostly whitespace) + "YTT", # flake8-2020 +] +ignore = [ + "A003", # Class attribute is shadowing a python builtin + "B905", # `zip()` without an explicit `strict=` parameter + "PT009", # Use a regular `assert` instead of unittest-style `assertEqual` + "PT017", # Found assertion on exception `exc` in `except` block, use `pytest.raises()` instead +] + +[tool.ruff.flake8-pytest-style] +fixture-parentheses = false +mark-parentheses = false + +[tool.ruff.isort] +known-first-party = ["systemd_ctypes"] [tool.coverage.paths] source = ["src", "*/site-packages"] diff --git a/src/systemd_ctypes/__init__.py b/src/systemd_ctypes/__init__.py index 53e1f79..87a2f5e 100644 --- a/src/systemd_ctypes/__init__.py +++ b/src/systemd_ctypes/__init__.py @@ -20,9 +20,9 @@ __version__ = "0" from .bus import Bus, BusError, BusMessage +from .bustypes import BusType, JSONEncoder, Variant from .event import Event, EventLoopPolicy, run_async from .pathwatch import Handle, PathWatch -from .bustypes import BusType, JSONEncoder, Variant __all__ = [ "Bus", diff --git a/src/systemd_ctypes/bus.py b/src/systemd_ctypes/bus.py index 422a094..a40e9b1 100644 --- a/src/systemd_ctypes/bus.py +++ b/src/systemd_ctypes/bus.py @@ -442,7 +442,8 @@ def emit_signal( :args: the arguments, according to the signature :returns: True """ - assert self._dbus_bus is not None and self._dbus_path is not None + assert self._dbus_bus is not None + assert self._dbus_path is not None return self._dbus_bus.message_new_signal(self._dbus_path, interface, name, signature, *args).send() def message_received(self, message: BusMessage) -> bool: diff --git a/src/systemd_ctypes/bustypes.py b/src/systemd_ctypes/bustypes.py index c935db5..9fe6b1b 100644 --- a/src/systemd_ctypes/bustypes.py +++ b/src/systemd_ctypes/bustypes.py @@ -347,10 +347,10 @@ def dict_reader(message: libsystemd.sd_bus_message) -> object: def get_writer(self, a, item_type, e, type_contents, open_container, close_container, key_writer, value_writer): def dict_writer(message: libsystemd.sd_bus_message, value: object) -> None: open_container(message, a, item_type) # array - for key, value in value.items(): # type: ignore[attr-defined] # can raise AttributeError + for key, val in value.items(): # type: ignore[attr-defined] # can raise AttributeError open_container(message, e, type_contents) # entry key_writer(message, key) # key - value_writer(message, value) # value + value_writer(message, val) # value close_container(message) # end entry close_container(message) # end array return dict_writer @@ -475,7 +475,7 @@ def from_annotation(annotation: Union[str, type, BusType]) -> Type: args = [from_annotation(arg) for arg in typing.get_args(annotation)] return factory(*args) except (AssertionError, AttributeError, KeyError, TypeError): - raise TypeError(f"Cannot interpret {annotation} as a dbus type") + raise TypeError(f"Cannot interpret {annotation} as a dbus type") from None _base_typestring_map: Dict[str, Type] = { diff --git a/src/systemd_ctypes/event.py b/src/systemd_ctypes/event.py index 7d19601..0cafed7 100644 --- a/src/systemd_ctypes/event.py +++ b/src/systemd_ctypes/event.py @@ -20,11 +20,9 @@ import sys from typing import Callable, ClassVar, Coroutine, List, Optional, Tuple +from . import inotify, libsystemd from .librarywrapper import Callback, Reference, UserData, byref -from . import inotify -from . import libsystemd - class Event(libsystemd.sd_event): class Source(libsystemd.sd_event_source): diff --git a/src/systemd_ctypes/inotify.py b/src/systemd_ctypes/inotify.py index 8586325..6f02595 100644 --- a/src/systemd_ctypes/inotify.py +++ b/src/systemd_ctypes/inotify.py @@ -21,12 +21,12 @@ class inotify_event(ctypes.Structure): - _fields_ = [ + _fields_ = ( ('wd', ctypes.c_int32), ('mask', ctypes.c_uint32), ('cookie', ctypes.c_uint32), ('len', ctypes.c_uint32), - ] + ) @property def name(self) -> Optional[bytes]: @@ -34,7 +34,7 @@ def name(self) -> Optional[bytes]: return None class event_with_name(ctypes.Structure): - _fields_ = [*inotify_event._fields_, ('name', ctypes.c_char * self.len)] + _fields_ = (*inotify_event._fields_, ('name', ctypes.c_char * self.len)) name = ctypes.cast(ctypes.addressof(self), ctypes.POINTER(event_with_name)).contents.name assert isinstance(name, bytes) diff --git a/src/systemd_ctypes/introspection.py b/src/systemd_ctypes/introspection.py index 6fe1419..f7bfd25 100644 --- a/src/systemd_ctypes/introspection.py +++ b/src/systemd_ctypes/introspection.py @@ -56,7 +56,7 @@ def element(tag, children=(), **kwargs): return tag -def method(name, method_info): +def method_to_xml(name, method_info): return element('method', name=name, children=[ element('arg', type=arg_type, direction=direction) @@ -65,28 +65,28 @@ def method(name, method_info): ]) -def property(name, property_info): +def property_to_xml(name, property_info): return element('property', name=name, access='write' if property_info['flags'] == 'w' else 'read', type=property_info['type']) -def signal(name, signal_info): +def signal_to_xml(name, signal_info): return element('signal', name=name, children=[ element('arg', type=arg_type) for arg_type in signal_info['in'] ]) -def interface(name, interface_info): +def interface_to_xml(name, interface_info): return element('interface', name=name, children=[ - *(method(name, info) for name, info in interface_info['methods'].items()), - *(property(name, info) for name, info in interface_info['properties'].items()), - *(signal(name, info) for name, info in interface_info['signals'].items()), + *(method_to_xml(name, info) for name, info in interface_info['methods'].items()), + *(property_to_xml(name, info) for name, info in interface_info['properties'].items()), + *(signal_to_xml(name, info) for name, info in interface_info['signals'].items()), ]) def to_xml(interfaces): - node = element('node', children=(interface(name, members) for name, members in interfaces.items())) + node = element('node', children=(interface_to_xml(name, members) for name, members in interfaces.items())) return ET.tostring(node, encoding='unicode') diff --git a/test/test_basic.py b/test/test_basic.py index d5874bb..37d6b98 100644 --- a/test/test_basic.py +++ b/test/test_basic.py @@ -15,15 +15,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import json import tempfile import unittest import dbusmock # type: ignore[import] # not typed -import json import systemd_ctypes -from systemd_ctypes import introspection, bus - +from systemd_ctypes import bus, introspection TEST_ADDR = ('org.freedesktop.Test', '/', 'org.freedesktop.Test.Main') diff --git a/test/test_bustypes.py b/test/test_bustypes.py index e6561b3..20e4143 100644 --- a/test/test_bustypes.py +++ b/test/test_bustypes.py @@ -5,6 +5,7 @@ from typing import List import pytest + from systemd_ctypes import Bus, BusMessage, BusType, bustypes @@ -18,7 +19,7 @@ def message(bus): return bus.message_new_method_call('x.y', '/y', 'z.a', 'a') -@pytest.mark.parametrize('annotation,typestring', [ +@pytest.mark.parametrize(('annotation', 'typestring'), [ (BusType.boolean, 'b'), (BusType.byte, 'y'), (BusType.int16, 'n'), @@ -97,14 +98,14 @@ def test_simple(message: BusMessage) -> None: def test_bad_path(message: BusMessage) -> None: writer = bustypes.from_annotation(BusType.objectpath).writer writer(message, '/path') - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="Invalid value provided for type 'o'"): writer(message, 'path') def test_bad_signature(message: BusMessage) -> None: writer = bustypes.from_annotation(BusType.signature).writer writer(message, 'a{sv}') - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="Invalid value provided for type 'g'"): writer(message, 'a{vs}') @@ -112,7 +113,7 @@ def test_bad_base64(message: BusMessage) -> None: writer = bustypes.from_annotation(BusType.bytestring).writer writer(message, '') writer(message, 'aaaa') - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="invalid base64"): writer(message, 'a') @@ -120,11 +121,11 @@ def test_bad_mapping(message: BusMessage) -> None: writer = bustypes.from_annotation(typing.Dict[str, str]).writer writer(message, {}) writer(message, {'a': 'b', 'c': 'd'}) - with pytest.raises(AttributeError): + with pytest.raises(AttributeError, match="'set'.*'items'"): writer(message, {'a', 'b', 'c'}) # no '.items()' - with pytest.raises(TypeError): + with pytest.raises(TypeError, match="'str'.*'int'"): writer(message, {1: 'a'}) # wrong key type - with pytest.raises(TypeError): + with pytest.raises(TypeError, match="'str'.*'int'"): writer(message, {'a': 1}) # wrong value type class weird: @@ -137,9 +138,9 @@ def items(self): writer(message, weird([])) with pytest.raises(TypeError): writer(message, weird([1])) # can't unpack '1' as key, value -- wrong type - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="not enough values to unpack"): writer(message, weird([()])) # can't unpack () as key, value -- wrong value - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="too many values to unpack"): writer(message, weird([(1, 2, 3)])) # ditto @@ -154,7 +155,7 @@ def test_bytestring(message: BusMessage) -> None: one_third = Fraction(1, 3) -@pytest.mark.parametrize('typestring,value', [ +@pytest.mark.parametrize(('typestring', 'value'), [ ('y', 0), ('y', 255), ('n', -2**15), ('n', 2**15 - 1), ('q', 0), ('q', 2**16 - 1), ('i', -2**31), ('i', 2**31 - 1), ('u', 0), ('u', 2**32 - 1), @@ -168,7 +169,7 @@ def test_number_limits(message: BusMessage, typestring: str, value: object) -> N assert x == value -@pytest.mark.parametrize('typestring,value', [ +@pytest.mark.parametrize(('typestring', 'value'), [ ('y', 256), ('y', -1), ('y', one_half), ('n', -2**15 - 1), ('n', 2**15), ('n', one_half), ('q', -1), ('q', 2**16), ('q', one_half), ('i', -2**31 - 1), ('i', 2**31), ('i', one_half), ('u', -1), ('u', 2**32), ('u', one_half), diff --git a/test/test_p2p.py b/test/test_p2p.py index 66da880..1641390 100644 --- a/test/test_p2p.py +++ b/test/test_p2p.py @@ -1,10 +1,11 @@ import asyncio import socket -import pytest import unittest from tempfile import TemporaryDirectory -from systemd_ctypes import bus, introspection, run_async, BusError +import pytest + +from systemd_ctypes import BusError, bus, introspection, run_async class CommonTests: diff --git a/test/test_pathwatch.py b/test/test_pathwatch.py index fecbc39..1b425fe 100644 --- a/test/test_pathwatch.py +++ b/test/test_pathwatch.py @@ -20,13 +20,11 @@ import os import tempfile import unittest - from unittest.mock import MagicMock import systemd_ctypes from systemd_ctypes.inotify import Event - with open("/etc/os-release") as f: os_release = f.read() @@ -38,7 +36,7 @@ def setUp(self): def async_wait_cond(self, cond): async def _call(): - for retry in range(50): + for _ in range(50): if cond(): break await asyncio.sleep(0.1) diff --git a/test/test_refloop.py b/test/test_refloop.py index 9a5759d..8728223 100644 --- a/test/test_refloop.py +++ b/test/test_refloop.py @@ -1,9 +1,9 @@ import errno import gc import os +import socket import unittest -import socket from systemd_ctypes import bus