Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 30 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,40 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ['3.10', '3.11', '3.12', '3.13']
python: ['3.11', '3.12', '3.13', '3.14']
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
python-version: ${{ matrix.python }}
- name: update APT
run: sudo apt-get update
- name: Install dependencies
run: sudo apt-get install -y libsystemd-dev
- name: Install pystemd
run: pip install -e '.[t]'
- name: Install environment
run: uv sync --all-extras --dev

- name: Assert Python Version
run: uv run python -V

- name: Run unit tests
run: pytest --cov=pystemd tests
run: uv run pytest --cov=pystemd tests

formatting:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Run black
uses: psf/black@stable
- name: Run ruff
uses: astral-sh/ruff-action@v3
with:
src: >-
pystemd
examples
tests
- name: Run isort
uses: isort/isort-action@v1

Expand All @@ -42,7 +51,16 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: install mypy
run: pip install mypy types-psutil
- name: run mypy
run: mypy
- name: Install uv
uses: astral-sh/setup-uv@v7

- name: update APT
run: sudo apt-get update
- name: Install dependencies
run: sudo apt-get install -y libsystemd-dev

- name: install environment
run: uv sync --all-extras --dev
- name: run pyrefly
run: uv run pyrefly check pystemd examples tests

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,6 @@ pystemd/RELEASE
/pystemd-*.tar.gz
/python-pystemd-*.src.rpm
/python-pystemd.spec

# not a fan of keeping uv lock files around
uv.lock
1 change: 1 addition & 0 deletions examples/future_cpucap_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def __init__(self, timeout, properties):
self.timeout = timeout
super().__init__(properties=properties)

# pyrefly: ignore [bad-override]
def run(self):
"""
This is suppose to waste a bunch of CPU.
Expand Down
2 changes: 2 additions & 0 deletions examples/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

def list_units():
with Manager() as manager:
# pyrefly: ignore [missing-attribute]
print("Version", manager.Manager.Version)
# pyrefly: ignore [missing-attribute]
print("Architecture", manager.Manager.Architecture)

# List Units
Expand Down
1 change: 1 addition & 0 deletions examples/monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def monitor(*args):
cargs,
)

# pyrefly: ignore [missing-attribute]
name = bus.get_unique_name()
while True:
msg = bus.process()
Expand Down
1 change: 1 addition & 0 deletions examples/monitor_from_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def process(msg, error=None, userdata=None):

if msg.body[1].get(b"SubState") in (b"exited", b"failed", b"dead"):
print("Unit is dead, exiting select loop")
# pyrefly: ignore [missing-attribute]
userdata.EXIT = True
print("#" * 80)
print("\n")
Expand Down
6 changes: 3 additions & 3 deletions examples/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import sys
from pathlib import Path

# pyrefly: ignore [missing-import]
from IPython.terminal.embed import InteractiveShellEmbed

import pystemd
Expand All @@ -27,9 +28,7 @@

display_banner = """
Welcome to pystemd {pystemd.__version__} interactive shell for python {sys.version}.
""".format(
pystemd=pystemd, sys=sys
)
""".format(pystemd=pystemd, sys=sys)


def shell() -> None:
Expand All @@ -39,6 +38,7 @@ def shell() -> None:


def main(mod: Path) -> None:
# pyrefly: ignore [bad-argument-type]
runpy.run_path(mod, {}, "__main__")


Expand Down
2 changes: 2 additions & 0 deletions examples/start_transient_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ def start_transient_unit(cmd="/bin/sleep 15"):
}
# if we need interactive prompts for passwords, we can create our own DBus object.
# if we dont need interactive, we would just do `with Manager() as manager:`.
# pyrefly: ignore [unexpected-keyword]
with DBus(interactive=True) as bus, Manager(bus=bus) as manager:
# pyrefly: ignore [missing-argument]
manager.Manager.StartTransientUnit(random_unit_name, b"fail", unit)

with Unit(random_unit_name, bus=bus) as unit:
Expand Down
15 changes: 8 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ name = "pystemd"
version = "0.14.0"
readme = "README.md"
description="A systemd binding for python"
requires-python=">=3.11"
dependencies = [
"lxml",
"psutil"
Expand All @@ -21,12 +22,11 @@ classifiers = [
"Operating System :: POSIX :: Linux",
"Intended Audience :: Developers",
"Intended Audience :: System Administrators",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Development Status :: 5 - Production/Stable",
"Topic :: Utilities",
"License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)",
Expand All @@ -40,11 +40,12 @@ t = [
"pytest",
"pytest-cov",
# cstq and toml are used for testing version
"cstq", "toml"
"cstq",
"toml",
]
lint = [
"black",
"mypi",
"ruff",
"pyrefly",
"isort",
]

Expand Down
2 changes: 1 addition & 1 deletion pystemd/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from typing import Any

from pystemd import DBus, __version__, machine1, systemd1
from pystemd import machine1, systemd1

SDUnit = systemd1.Unit
SDManager = systemd1.Manager
Expand Down
34 changes: 22 additions & 12 deletions pystemd/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
# This source code is licensed under the license found in the LICENSE file in
# the root directory of this source tree.
#
from __future__ import annotations

import re
from contextlib import contextmanager
from typing import AnyStr, Iterator, Optional

from lxml import etree

Expand All @@ -16,7 +18,13 @@


class SDObject(object):
def __init__(self, destination, path, bus=None, _autoload=False):
def __init__(
self,
destination: AnyStr,
path: AnyStr,
bus: Optional[DBus] = None,
_autoload: bool = False,
):
self.destination = x2char_star(destination)
self.path = x2char_star(path)

Expand All @@ -35,14 +43,14 @@ def __getstate__(self):
"_autoload": self._loaded,
}

def __setstate__(self, state):
def __setstate__(self, state) -> None:
self.__init__(**state)

def __enter__(self):
def __enter__(self) -> SDObject:
self.load()
return self

def __exit__(self, exception_type, exception_value, traceback):
def __exit__(self, exception_type, exception_value, traceback) -> None:
pass

def __getattr__(self, name):
Expand All @@ -57,7 +65,7 @@ def __getattr__(self, name):

# At some point we should verify that 2 interface do not have the same
# methods and/or properties. But in the meantime, we can trust that
# the fine folks at systemd will not do this. they have actually go
# the fine folks at systemd will not do this. They have actually go
# out of their way to make this true:
# https://github.com/systemd/systemd/blob/119f0f2876ea340cc41525e844487aa88551c219/src/core/dbus-unit.c#L1738-L1746

Expand All @@ -67,7 +75,7 @@ def __getattr__(self, name):
raise AttributeError()

@contextmanager
def bus_context(self):
def bus_context(self) -> Iterator[DBus]:
close_bus_at_end = self._bus is None
try:
if self._bus is None:
Expand All @@ -80,7 +88,7 @@ def bus_context(self):
if close_bus_at_end:
bus.close()

def get_introspect_xml(self):
def get_introspect_xml(self) -> etree._Element:
with self.bus_context() as bus:
xml_doc = etree.fromstring(
bus.call_method(
Expand All @@ -94,7 +102,7 @@ def get_introspect_xml(self):

return xml_doc

def load(self, force=False):
def load(self, force=False) -> Optional[bool]:
if self._loaded and not force:
return

Expand Down Expand Up @@ -127,11 +135,13 @@ def load(self, force=False):


class SDInterface(object):
def __init__(self, sd_object, interface_name):
def __init__(self, sd_object: SDObject, interface_name: AnyStr) -> None:
self.sd_object = sd_object
self.interface_name = x2char_star(interface_name)
self._properties_xml = getattr(self, "_properties_xml", {})
self._methods_xml = getattr(self, "_methods_xml", {})

def __repr__(self):
def __repr__(self) -> str:
return "<%s of %s>" % (self.interface_name, self.sd_object.path.decode())

def _get_property(self, property_name):
Expand All @@ -145,7 +155,7 @@ def _get_property(self, property_name):
x2char_star(prop_type),
)

def _set_property(self, property_name, value):
def _set_property(self, property_name, value) -> None:
prop_access = self._properties_xml[property_name].attrib.get("access")
if prop_access == "read":
raise AttributeError("{} is read-only".format(property_name))
Expand Down Expand Up @@ -182,7 +192,7 @@ def _auto_call_dbus_method(self, method_name, in_args, *args):
block_chars = re.compile(r"v|\{")
if any(any(block_chars.finditer(arg)) for arg in in_args):
raise NotImplementedError(
"still not implemented methods with complex " "arguments"
"still not implemented methods with complex arguments"
)

in_signature = "".join(in_args)
Expand Down
35 changes: 0 additions & 35 deletions pystemd/base.pyi

This file was deleted.

2 changes: 1 addition & 1 deletion pystemd/daemon.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ class PystemdDaemonError(Exception):
pass

def listen_fds(unset_environment: bool) -> int: ...
def notify(unset_environment: bool, *args: str, **kwargs: Union[int, str]) -> None: ...
def notify(unset_environment: bool, *args: Union[str, bytes], **kwargs: Union[int, str]) -> None: ...
def booted() -> bool: ...
def watchdog_enabled(unset_environment: bool) -> int: ...
7 changes: 7 additions & 0 deletions pystemd/dbuslib.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ class DBusRemote(DBus):
class DBusAddress(DBus):
def __init__(self, address: bytes, peer_to_peer: bool) -> None: ...


def compile_args(*args) -> list:...
def compile_array(*args):...
def compile_simple(*args):...
def compile_struct(*args):...
def find_closure(args:bytes, open:int, close:int) -> int:...

def apply_signature(signature: bytes, values: List[Any]) -> list[Tuple[int, Any]]: ...
def path_encode(prefix: bytes, external_id: bytes) -> bytes: ...
def path_decode(path: bytes, prefix: bytes) -> bytes: ...
Loading