dbus-fast 2.39.5__tar.gz → 2.45.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of dbus-fast might be problematic. Click here for more details.

Files changed (44) hide show
  1. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/PKG-INFO +8 -7
  2. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/README.md +3 -3
  3. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/build_ext.py +1 -1
  4. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/pyproject.toml +11 -11
  5. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/setup.py +3 -3
  6. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/__version__.py +1 -1
  7. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/_private/marshaller.pxd +2 -2
  8. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/_private/marshaller.py +2 -1
  9. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/_private/unmarshaller.pxd +4 -1
  10. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/_private/unmarshaller.py +36 -24
  11. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/_private/util.py +2 -1
  12. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/aio/message_bus.py +2 -1
  13. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/aio/message_reader.py +1 -1
  14. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/auth.py +2 -3
  15. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/constants.py +3 -9
  16. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/errors.py +1 -4
  17. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/glib/message_bus.py +8 -10
  18. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/glib/proxy_object.py +1 -2
  19. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/introspection.py +19 -20
  20. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/message.pxd +1 -1
  21. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/message.py +19 -19
  22. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/message_bus.py +2 -1
  23. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/proxy_object.py +1 -2
  24. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/service.pxd +1 -1
  25. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/service.py +10 -6
  26. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/signature.pxd +2 -0
  27. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/signature.py +33 -11
  28. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/LICENSE +0 -0
  29. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/__init__.py +0 -0
  30. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/_private/__init__.py +0 -0
  31. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/_private/_cython_compat.py +0 -0
  32. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/_private/address.pxd +0 -0
  33. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/_private/address.py +0 -0
  34. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/_private/constants.py +0 -0
  35. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/aio/__init__.py +0 -0
  36. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/aio/message_reader.pxd +0 -0
  37. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/aio/proxy_object.py +0 -0
  38. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/glib/__init__.py +0 -0
  39. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/message_bus.pxd +0 -0
  40. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/py.typed +0 -0
  41. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/send_reply.py +0 -0
  42. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/unpack.pxd +0 -0
  43. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/unpack.py +0 -0
  44. {dbus_fast-2.39.5 → dbus_fast-2.45.2}/src/dbus_fast/validators.py +0 -0
@@ -1,22 +1,23 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: dbus-fast
3
- Version: 2.39.5
3
+ Version: 2.45.2
4
4
  Summary: A faster version of dbus-next
5
5
  License: MIT
6
+ License-File: LICENSE
6
7
  Author: Bluetooth Devices Authors
7
8
  Author-email: bluetooth@koston.org
8
- Requires-Python: >=3.9,<4.0
9
+ Requires-Python: >=3.10
9
10
  Classifier: Development Status :: 5 - Production/Stable
10
11
  Classifier: Intended Audience :: Developers
11
12
  Classifier: License :: OSI Approved :: MIT License
12
13
  Classifier: Natural Language :: English
13
14
  Classifier: Operating System :: OS Independent
14
15
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.9
16
16
  Classifier: Programming Language :: Python :: 3.10
17
17
  Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
20
21
  Classifier: Topic :: Software Development :: Libraries
21
22
  Project-URL: Bug Tracker, https://github.com/bluetooth-devices/dbus-fast/issues
22
23
  Project-URL: Changelog, https://github.com/bluetooth-devices/dbus-fast/blob/main/CHANGELOG.md
@@ -141,7 +142,7 @@ asyncio.run(main())
141
142
 
142
143
  To define a service on the bus, use the `ServiceInterface` class and decorate class methods to specify DBus methods, properties, and signals with their type signatures.
143
144
 
144
- For more information, see the [overview for the high-level service](https://python-dbus-fast.readthedocs.io/en/latest/high-level-service/index.html).
145
+ For more information, see the [overview for the high-level service](https://dbus-fast.readthedocs.io/en/latest/high-level-service/index.html).
145
146
 
146
147
  ```python
147
148
  from dbus_fast.service import ServiceInterface, method, dbus_property, signal, Variant
@@ -194,7 +195,7 @@ asyncio.run(main())
194
195
 
195
196
  The low-level interface works with DBus messages directly.
196
197
 
197
- For more information, see the [overview for the low-level interface](https://python-dbus-fast.readthedocs.io/en/latest/low-level-interface/index.html).
198
+ For more information, see the [overview for the low-level interface](https://dbus-fast.readthedocs.io/en/latest/low-level-interface/index.html).
198
199
 
199
200
  ```python
200
201
  from dbus_fast.message import Message, MessageType
@@ -222,7 +223,7 @@ async def main():
222
223
  asyncio.run(main())
223
224
  ```
224
225
 
225
- ## Projects that use python-dbus-fast
226
+ ## Projects that use dbus-fast
226
227
 
227
228
  - [Bluetooth Adapters](https://github.com/bluetooth-devices/bluetooth-adapters)
228
229
 
@@ -115,7 +115,7 @@ asyncio.run(main())
115
115
 
116
116
  To define a service on the bus, use the `ServiceInterface` class and decorate class methods to specify DBus methods, properties, and signals with their type signatures.
117
117
 
118
- For more information, see the [overview for the high-level service](https://python-dbus-fast.readthedocs.io/en/latest/high-level-service/index.html).
118
+ For more information, see the [overview for the high-level service](https://dbus-fast.readthedocs.io/en/latest/high-level-service/index.html).
119
119
 
120
120
  ```python
121
121
  from dbus_fast.service import ServiceInterface, method, dbus_property, signal, Variant
@@ -168,7 +168,7 @@ asyncio.run(main())
168
168
 
169
169
  The low-level interface works with DBus messages directly.
170
170
 
171
- For more information, see the [overview for the low-level interface](https://python-dbus-fast.readthedocs.io/en/latest/low-level-interface/index.html).
171
+ For more information, see the [overview for the low-level interface](https://dbus-fast.readthedocs.io/en/latest/low-level-interface/index.html).
172
172
 
173
173
  ```python
174
174
  from dbus_fast.message import Message, MessageType
@@ -196,7 +196,7 @@ async def main():
196
196
  asyncio.run(main())
197
197
  ```
198
198
 
199
- ## Projects that use python-dbus-fast
199
+ ## Projects that use dbus-fast
200
200
 
201
201
  - [Bluetooth Adapters](https://github.com/bluetooth-devices/bluetooth-adapters)
202
202
 
@@ -47,7 +47,7 @@ def build(setup_kwargs):
47
47
  if os.environ.get("SKIP_CYTHON"):
48
48
  return
49
49
  try:
50
- from Cython.Build import cythonize
50
+ from Cython.Build import cythonize # noqa: PLC0415
51
51
 
52
52
  setup_kwargs.update(
53
53
  {
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "dbus-fast"
3
- version = "2.39.5"
3
+ version = "2.45.2"
4
4
  description = "A faster version of dbus-next"
5
5
  authors = ["Bluetooth Devices Authors <bluetooth@koston.org>"]
6
6
  license = "MIT"
@@ -13,11 +13,11 @@ classifiers = [
13
13
  "Natural Language :: English",
14
14
  "Operating System :: OS Independent",
15
15
  "Topic :: Software Development :: Libraries",
16
- "Programming Language :: Python :: 3.9",
17
16
  "Programming Language :: Python :: 3.10",
18
17
  "Programming Language :: Python :: 3.11",
19
18
  "Programming Language :: Python :: 3.12",
20
19
  "Programming Language :: Python :: 3.13",
20
+ "Programming Language :: Python :: 3.14",
21
21
  ]
22
22
  packages = [
23
23
  { include = "dbus_fast", from = "src" },
@@ -34,7 +34,7 @@ script = "build_ext.py"
34
34
  "Changelog" = "https://github.com/bluetooth-devices/dbus-fast/blob/main/CHANGELOG.md"
35
35
 
36
36
  [tool.poetry.dependencies]
37
- python = "^3.9"
37
+ python = ">=3.10"
38
38
 
39
39
  # duplicated in docs/requirements.txt for readthedocs compatibility
40
40
  [tool.poetry.group.docs.dependencies]
@@ -46,14 +46,14 @@ sphinxcontrib-fulltoc = "^1.2.0"
46
46
 
47
47
  [tool.poetry.group.dev.dependencies]
48
48
  pytest = ">=7,<9"
49
- pytest-cov = ">=3,<7"
50
- pytest-asyncio = ">=0.19,<0.26"
49
+ pytest-cov = ">=3,<8"
50
+ pytest-asyncio = ">=0.19,<1.3"
51
51
  pycairo = "^1.21.0"
52
- PyGObject = "^3.42.2"
53
- Cython = ">=3,<3.1.0"
54
- setuptools = ">=65.4.1,<77.0.0"
52
+ PyGObject = {version = ">=3.50,<3.51", python = "<4"}
53
+ Cython = ">=3,<3.3.0"
54
+ setuptools = ">=65.4.1,<81.0.0"
55
55
  pytest-timeout = "^2.1.0"
56
- pytest-codspeed = "^3.1.1"
56
+ pytest-codspeed = ">=3.1.1,<5.0.0"
57
57
  covdefaults = "^2.3.0"
58
58
 
59
59
  [tool.semantic_release]
@@ -104,11 +104,11 @@ module = "docs.*"
104
104
  ignore_errors = true
105
105
 
106
106
  [build-system]
107
- requires = ['setuptools>=65.4.1', 'wheel', 'Cython>=3,<3.1.0', "poetry-core>=1.0.0"]
107
+ requires = ['setuptools>=65.4.1', 'wheel', 'Cython>=3,<3.3.0', "poetry-core>=1.0.0"]
108
108
  build-backend = "poetry.core.masonry.api"
109
109
 
110
110
  [tool.ruff]
111
- target-version = "py39"
111
+ target-version = "py310"
112
112
  line-length = 88
113
113
 
114
114
  [tool.ruff.lint]
@@ -12,9 +12,9 @@ package_data = \
12
12
 
13
13
  setup_kwargs = {
14
14
  'name': 'dbus-fast',
15
- 'version': '2.39.5',
15
+ 'version': '2.45.2',
16
16
  'description': 'A faster version of dbus-next',
17
- 'long_description': '# dbus-fast\n\n<p align="center">\n <a href="https://github.com/bluetooth-devices/dbus-fast/actions?query=workflow%3ACI">\n <img src="https://img.shields.io/github/workflow/status/bluetooth-devices/dbus-fast/CI/main?label=CI&logo=github&style=flat-square" alt="CI Status" >\n </a>\n <a href="https://dbus-fast.readthedocs.io">\n <img src="https://img.shields.io/readthedocs/dbus-fast.svg?logo=read-the-docs&logoColor=fff&style=flat-square" alt="Documentation Status">\n </a>\n <a href="https://codecov.io/gh/bluetooth-devices/dbus-fast">\n <img src="https://img.shields.io/codecov/c/github/bluetooth-devices/dbus-fast.svg?logo=codecov&logoColor=fff&style=flat-square" alt="Test coverage percentage">\n </a>\n</p>\n<p align="center">\n <a href="https://python-poetry.org/">\n <img src="https://img.shields.io/badge/packaging-poetry-299bd7?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAASCAYAAABrXO8xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJJSURBVHgBfZLPa1NBEMe/s7tNXoxW1KJQKaUHkXhQvHgW6UHQQ09CBS/6V3hKc/AP8CqCrUcpmop3Cx48eDB4yEECjVQrlZb80CRN8t6OM/teagVxYZi38+Yz853dJbzoMV3MM8cJUcLMSUKIE8AzQ2PieZzFxEJOHMOgMQQ+dUgSAckNXhapU/NMhDSWLs1B24A8sO1xrN4NECkcAC9ASkiIJc6k5TRiUDPhnyMMdhKc+Zx19l6SgyeW76BEONY9exVQMzKExGKwwPsCzza7KGSSWRWEQhyEaDXp6ZHEr416ygbiKYOd7TEWvvcQIeusHYMJGhTwF9y7sGnSwaWyFAiyoxzqW0PM/RjghPxF2pWReAowTEXnDh0xgcLs8l2YQmOrj3N7ByiqEoH0cARs4u78WgAVkoEDIDoOi3AkcLOHU60RIg5wC4ZuTC7FaHKQm8Hq1fQuSOBvX/sodmNJSB5geaF5CPIkUeecdMxieoRO5jz9bheL6/tXjrwCyX/UYBUcjCaWHljx1xiX6z9xEjkYAzbGVnB8pvLmyXm9ep+W8CmsSHQQY77Zx1zboxAV0w7ybMhQmfqdmmw3nEp1I0Z+FGO6M8LZdoyZnuzzBdjISicKRnpxzI9fPb+0oYXsNdyi+d3h9bm9MWYHFtPeIZfLwzmFDKy1ai3p+PDls1Llz4yyFpferxjnyjJDSEy9CaCx5m2cJPerq6Xm34eTrZt3PqxYO1XOwDYZrFlH1fWnpU38Y9HRze3lj0vOujZcXKuuXm3jP+s3KbZVra7y2EAAAAAASUVORK5CYII=" alt="Poetry">\n </a>\n <a href="https://github.com/astral-sh/ruff">\n <img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff">\n </a>\n <a href="https://github.com/pre-commit/pre-commit">\n <img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square" alt="pre-commit">\n </a>\n <a href="https://codspeed.io/Bluetooth-Devices/dbus-fast"><img src="https://img.shields.io/endpoint?url=https://codspeed.io/badge.json" alt="CodSpeed Badge"/></a>\n</p>\n<p align="center">\n <a href="https://pypi.org/project/dbus-fast/">\n <img src="https://img.shields.io/pypi/v/dbus-fast.svg?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">\n </a>\n <img src="https://img.shields.io/pypi/pyversions/dbus-fast.svg?style=flat-square&logo=python&amp;logoColor=fff" alt="Supported Python versions">\n <img src="https://img.shields.io/pypi/l/dbus-fast.svg?style=flat-square" alt="License">\n</p>\n\nA faster version of dbus-next originally from the [great DBus next library](https://github.com/altdesktop/python-dbus-next) ❤️\n\n## Installation\n\nInstall this via pip (or your favourite package manager):\n\n`pip install dbus-fast`\n\n[Documentation](https://dbus-fast.readthedocs.io/en/latest/)\n\ndbus-fast is a Python library for DBus that aims to be a performant fully featured high level library primarily geared towards integration of applications into Linux desktop and mobile environments.\n\nDesktop application developers can use this library for integrating their applications into desktop environments by implementing common DBus standard interfaces or creating custom plugin interfaces.\n\nDesktop users can use this library to create their own scripts and utilities to interact with those interfaces for customization of their desktop environment.\n\ndbus-fast plans to improve over other DBus libraries for Python in the following ways:\n\n- Zero dependencies and pure Python 3\n- An optional cython extension is available to speed up (un)marshalling\n- Focus on performance\n- Support for multiple IO backends including asyncio and the GLib main loop.\n- Nonblocking IO suitable for GUI development.\n- Target the latest language features of Python for beautiful services and clients.\n- Complete implementation of the DBus type system without ever guessing types.\n- Integration tests for all features of the library.\n- Completely documented public API.\n\n## Installing\n\nThis library is available on PyPi as [dbus-fast](https://pypi.org/project/dbus-fast/).\n\n```\npip3 install dbus-fast\n```\n\n## The Client Interface\n\nTo use a service on the bus, the library constructs a proxy object you can use to call methods, get and set properties, and listen to signals.\n\nFor more information, see the [overview for the high-level client](https://dbus-fast.readthedocs.io/en/latest/high-level-client/index.html).\n\nThis example connects to a media player and controls it with the [MPRIS](https://specifications.freedesktop.org/mpris-spec/latest/) DBus interface.\n\n```python\nfrom dbus_fast.aio import MessageBus\n\nimport asyncio\n\n\nasync def main():\n bus = await MessageBus().connect()\n # the introspection xml would normally be included in your project, but\n # this is convenient for development\n introspection = await bus.introspect(\'org.mpris.MediaPlayer2.vlc\', \'/org/mpris/MediaPlayer2\')\n\n obj = bus.get_proxy_object(\'org.mpris.MediaPlayer2.vlc\', \'/org/mpris/MediaPlayer2\', introspection)\n player = obj.get_interface(\'org.mpris.MediaPlayer2.Player\')\n properties = obj.get_interface(\'org.freedesktop.DBus.Properties\')\n\n # call methods on the interface (this causes the media player to play)\n await player.call_play()\n\n volume = await player.get_volume()\n print(f\'current volume: {volume}, setting to 0.5\')\n\n await player.set_volume(0.5)\n\n # listen to signals\n def on_properties_changed(interface_name, changed_properties, invalidated_properties):\n for changed, variant in changed_properties.items():\n print(f\'property changed: {changed} - {variant.value}\')\n\n properties.on_properties_changed(on_properties_changed)\n\n await asyncio.Event().wait()\n\nasyncio.run(main())\n```\n\n## The Service Interface\n\nTo define a service on the bus, use the `ServiceInterface` class and decorate class methods to specify DBus methods, properties, and signals with their type signatures.\n\nFor more information, see the [overview for the high-level service](https://python-dbus-fast.readthedocs.io/en/latest/high-level-service/index.html).\n\n```python\nfrom dbus_fast.service import ServiceInterface, method, dbus_property, signal, Variant\nfrom dbus_fast.aio MessageBus\n\nimport asyncio\n\nclass ExampleInterface(ServiceInterface):\n def __init__(self, name):\n super().__init__(name)\n self._string_prop = \'kevin\'\n\n @method()\n def Echo(self, what: \'s\') -> \'s\':\n return what\n\n @method()\n def GetVariantDict() -> \'a{sv}\':\n return {\n \'foo\': Variant(\'s\', \'bar\'),\n \'bat\': Variant(\'x\', -55),\n \'a_list\': Variant(\'as\', [\'hello\', \'world\'])\n }\n\n @dbus_property()\n def string_prop(self) -> \'s\':\n return self._string_prop\n\n @string_prop.setter\n def string_prop_setter(self, val: \'s\'):\n self._string_prop = val\n\n @signal()\n def signal_simple(self) -> \'s\':\n return \'hello\'\n\nasync def main():\n bus = await MessageBus().connect()\n interface = ExampleInterface(\'test.interface\')\n bus.export(\'/test/path\', interface)\n # now that we are ready to handle requests, we can request name from D-Bus\n await bus.request_name(\'test.name\')\n # wait indefinitely\n await asyncio.Event().wait()\n\nasyncio.run(main())\n```\n\n## The Low-Level Interface\n\nThe low-level interface works with DBus messages directly.\n\nFor more information, see the [overview for the low-level interface](https://python-dbus-fast.readthedocs.io/en/latest/low-level-interface/index.html).\n\n```python\nfrom dbus_fast.message import Message, MessageType\nfrom dbus_fast.aio import MessageBus\n\nimport asyncio\nimport json\n\n\nasync def main():\n bus = await MessageBus().connect()\n\n reply = await bus.call(\n Message(destination=\'org.freedesktop.DBus\',\n path=\'/org/freedesktop/DBus\',\n interface=\'org.freedesktop.DBus\',\n member=\'ListNames\'))\n\n if reply.message_type == MessageType.ERROR:\n raise Exception(reply.body[0])\n\n print(json.dumps(reply.body[0], indent=2))\n\n\nasyncio.run(main())\n```\n\n## Projects that use python-dbus-fast\n\n- [Bluetooth Adapters](https://github.com/bluetooth-devices/bluetooth-adapters)\n\n## Contributing\n\nContributions are welcome. Development happens on [Github](https://github.com/Bluetooth-Devices/dbus-fast).\n\nBefore you commit, run `pre-commit run --all-files` to run the linter, code formatter, and the test suite.\n\n## Copyright\n\nYou can use this code under an MIT license (see LICENSE).\n\n- © 2019, Tony Crisci\n- © 2022, Bluetooth Devices authors\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n<!-- prettier-ignore-start -->\n<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->\n<!-- markdownlint-disable -->\n<!-- markdownlint-enable -->\n<!-- ALL-CONTRIBUTORS-LIST:END -->\n<!-- prettier-ignore-end -->\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n\n## Credits\n\nThis package was created with\n[Cookiecutter](https://github.com/audreyr/cookiecutter) and the\n[browniebroke/cookiecutter-pypackage](https://github.com/browniebroke/cookiecutter-pypackage)\nproject template.\n',
17
+ 'long_description': '# dbus-fast\n\n<p align="center">\n <a href="https://github.com/bluetooth-devices/dbus-fast/actions?query=workflow%3ACI">\n <img src="https://img.shields.io/github/workflow/status/bluetooth-devices/dbus-fast/CI/main?label=CI&logo=github&style=flat-square" alt="CI Status" >\n </a>\n <a href="https://dbus-fast.readthedocs.io">\n <img src="https://img.shields.io/readthedocs/dbus-fast.svg?logo=read-the-docs&logoColor=fff&style=flat-square" alt="Documentation Status">\n </a>\n <a href="https://codecov.io/gh/bluetooth-devices/dbus-fast">\n <img src="https://img.shields.io/codecov/c/github/bluetooth-devices/dbus-fast.svg?logo=codecov&logoColor=fff&style=flat-square" alt="Test coverage percentage">\n </a>\n</p>\n<p align="center">\n <a href="https://python-poetry.org/">\n <img src="https://img.shields.io/badge/packaging-poetry-299bd7?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAASCAYAAABrXO8xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJJSURBVHgBfZLPa1NBEMe/s7tNXoxW1KJQKaUHkXhQvHgW6UHQQ09CBS/6V3hKc/AP8CqCrUcpmop3Cx48eDB4yEECjVQrlZb80CRN8t6OM/teagVxYZi38+Yz853dJbzoMV3MM8cJUcLMSUKIE8AzQ2PieZzFxEJOHMOgMQQ+dUgSAckNXhapU/NMhDSWLs1B24A8sO1xrN4NECkcAC9ASkiIJc6k5TRiUDPhnyMMdhKc+Zx19l6SgyeW76BEONY9exVQMzKExGKwwPsCzza7KGSSWRWEQhyEaDXp6ZHEr416ygbiKYOd7TEWvvcQIeusHYMJGhTwF9y7sGnSwaWyFAiyoxzqW0PM/RjghPxF2pWReAowTEXnDh0xgcLs8l2YQmOrj3N7ByiqEoH0cARs4u78WgAVkoEDIDoOi3AkcLOHU60RIg5wC4ZuTC7FaHKQm8Hq1fQuSOBvX/sodmNJSB5geaF5CPIkUeecdMxieoRO5jz9bheL6/tXjrwCyX/UYBUcjCaWHljx1xiX6z9xEjkYAzbGVnB8pvLmyXm9ep+W8CmsSHQQY77Zx1zboxAV0w7ybMhQmfqdmmw3nEp1I0Z+FGO6M8LZdoyZnuzzBdjISicKRnpxzI9fPb+0oYXsNdyi+d3h9bm9MWYHFtPeIZfLwzmFDKy1ai3p+PDls1Llz4yyFpferxjnyjJDSEy9CaCx5m2cJPerq6Xm34eTrZt3PqxYO1XOwDYZrFlH1fWnpU38Y9HRze3lj0vOujZcXKuuXm3jP+s3KbZVra7y2EAAAAAASUVORK5CYII=" alt="Poetry">\n </a>\n <a href="https://github.com/astral-sh/ruff">\n <img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff">\n </a>\n <a href="https://github.com/pre-commit/pre-commit">\n <img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square" alt="pre-commit">\n </a>\n <a href="https://codspeed.io/Bluetooth-Devices/dbus-fast"><img src="https://img.shields.io/endpoint?url=https://codspeed.io/badge.json" alt="CodSpeed Badge"/></a>\n</p>\n<p align="center">\n <a href="https://pypi.org/project/dbus-fast/">\n <img src="https://img.shields.io/pypi/v/dbus-fast.svg?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">\n </a>\n <img src="https://img.shields.io/pypi/pyversions/dbus-fast.svg?style=flat-square&logo=python&amp;logoColor=fff" alt="Supported Python versions">\n <img src="https://img.shields.io/pypi/l/dbus-fast.svg?style=flat-square" alt="License">\n</p>\n\nA faster version of dbus-next originally from the [great DBus next library](https://github.com/altdesktop/python-dbus-next) ❤️\n\n## Installation\n\nInstall this via pip (or your favourite package manager):\n\n`pip install dbus-fast`\n\n[Documentation](https://dbus-fast.readthedocs.io/en/latest/)\n\ndbus-fast is a Python library for DBus that aims to be a performant fully featured high level library primarily geared towards integration of applications into Linux desktop and mobile environments.\n\nDesktop application developers can use this library for integrating their applications into desktop environments by implementing common DBus standard interfaces or creating custom plugin interfaces.\n\nDesktop users can use this library to create their own scripts and utilities to interact with those interfaces for customization of their desktop environment.\n\ndbus-fast plans to improve over other DBus libraries for Python in the following ways:\n\n- Zero dependencies and pure Python 3\n- An optional cython extension is available to speed up (un)marshalling\n- Focus on performance\n- Support for multiple IO backends including asyncio and the GLib main loop.\n- Nonblocking IO suitable for GUI development.\n- Target the latest language features of Python for beautiful services and clients.\n- Complete implementation of the DBus type system without ever guessing types.\n- Integration tests for all features of the library.\n- Completely documented public API.\n\n## Installing\n\nThis library is available on PyPi as [dbus-fast](https://pypi.org/project/dbus-fast/).\n\n```\npip3 install dbus-fast\n```\n\n## The Client Interface\n\nTo use a service on the bus, the library constructs a proxy object you can use to call methods, get and set properties, and listen to signals.\n\nFor more information, see the [overview for the high-level client](https://dbus-fast.readthedocs.io/en/latest/high-level-client/index.html).\n\nThis example connects to a media player and controls it with the [MPRIS](https://specifications.freedesktop.org/mpris-spec/latest/) DBus interface.\n\n```python\nfrom dbus_fast.aio import MessageBus\n\nimport asyncio\n\n\nasync def main():\n bus = await MessageBus().connect()\n # the introspection xml would normally be included in your project, but\n # this is convenient for development\n introspection = await bus.introspect(\'org.mpris.MediaPlayer2.vlc\', \'/org/mpris/MediaPlayer2\')\n\n obj = bus.get_proxy_object(\'org.mpris.MediaPlayer2.vlc\', \'/org/mpris/MediaPlayer2\', introspection)\n player = obj.get_interface(\'org.mpris.MediaPlayer2.Player\')\n properties = obj.get_interface(\'org.freedesktop.DBus.Properties\')\n\n # call methods on the interface (this causes the media player to play)\n await player.call_play()\n\n volume = await player.get_volume()\n print(f\'current volume: {volume}, setting to 0.5\')\n\n await player.set_volume(0.5)\n\n # listen to signals\n def on_properties_changed(interface_name, changed_properties, invalidated_properties):\n for changed, variant in changed_properties.items():\n print(f\'property changed: {changed} - {variant.value}\')\n\n properties.on_properties_changed(on_properties_changed)\n\n await asyncio.Event().wait()\n\nasyncio.run(main())\n```\n\n## The Service Interface\n\nTo define a service on the bus, use the `ServiceInterface` class and decorate class methods to specify DBus methods, properties, and signals with their type signatures.\n\nFor more information, see the [overview for the high-level service](https://dbus-fast.readthedocs.io/en/latest/high-level-service/index.html).\n\n```python\nfrom dbus_fast.service import ServiceInterface, method, dbus_property, signal, Variant\nfrom dbus_fast.aio MessageBus\n\nimport asyncio\n\nclass ExampleInterface(ServiceInterface):\n def __init__(self, name):\n super().__init__(name)\n self._string_prop = \'kevin\'\n\n @method()\n def Echo(self, what: \'s\') -> \'s\':\n return what\n\n @method()\n def GetVariantDict() -> \'a{sv}\':\n return {\n \'foo\': Variant(\'s\', \'bar\'),\n \'bat\': Variant(\'x\', -55),\n \'a_list\': Variant(\'as\', [\'hello\', \'world\'])\n }\n\n @dbus_property()\n def string_prop(self) -> \'s\':\n return self._string_prop\n\n @string_prop.setter\n def string_prop_setter(self, val: \'s\'):\n self._string_prop = val\n\n @signal()\n def signal_simple(self) -> \'s\':\n return \'hello\'\n\nasync def main():\n bus = await MessageBus().connect()\n interface = ExampleInterface(\'test.interface\')\n bus.export(\'/test/path\', interface)\n # now that we are ready to handle requests, we can request name from D-Bus\n await bus.request_name(\'test.name\')\n # wait indefinitely\n await asyncio.Event().wait()\n\nasyncio.run(main())\n```\n\n## The Low-Level Interface\n\nThe low-level interface works with DBus messages directly.\n\nFor more information, see the [overview for the low-level interface](https://dbus-fast.readthedocs.io/en/latest/low-level-interface/index.html).\n\n```python\nfrom dbus_fast.message import Message, MessageType\nfrom dbus_fast.aio import MessageBus\n\nimport asyncio\nimport json\n\n\nasync def main():\n bus = await MessageBus().connect()\n\n reply = await bus.call(\n Message(destination=\'org.freedesktop.DBus\',\n path=\'/org/freedesktop/DBus\',\n interface=\'org.freedesktop.DBus\',\n member=\'ListNames\'))\n\n if reply.message_type == MessageType.ERROR:\n raise Exception(reply.body[0])\n\n print(json.dumps(reply.body[0], indent=2))\n\n\nasyncio.run(main())\n```\n\n## Projects that use dbus-fast\n\n- [Bluetooth Adapters](https://github.com/bluetooth-devices/bluetooth-adapters)\n\n## Contributing\n\nContributions are welcome. Development happens on [Github](https://github.com/Bluetooth-Devices/dbus-fast).\n\nBefore you commit, run `pre-commit run --all-files` to run the linter, code formatter, and the test suite.\n\n## Copyright\n\nYou can use this code under an MIT license (see LICENSE).\n\n- © 2019, Tony Crisci\n- © 2022, Bluetooth Devices authors\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n<!-- prettier-ignore-start -->\n<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->\n<!-- markdownlint-disable -->\n<!-- markdownlint-enable -->\n<!-- ALL-CONTRIBUTORS-LIST:END -->\n<!-- prettier-ignore-end -->\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n\n## Credits\n\nThis package was created with\n[Cookiecutter](https://github.com/audreyr/cookiecutter) and the\n[browniebroke/cookiecutter-pypackage](https://github.com/browniebroke/cookiecutter-pypackage)\nproject template.\n',
18
18
  'author': 'Bluetooth Devices Authors',
19
19
  'author_email': 'bluetooth@koston.org',
20
20
  'maintainer': 'None',
@@ -23,7 +23,7 @@ setup_kwargs = {
23
23
  'package_dir': package_dir,
24
24
  'packages': packages,
25
25
  'package_data': package_data,
26
- 'python_requires': '>=3.9,<4.0',
26
+ 'python_requires': '>=3.10',
27
27
  }
28
28
  from build_ext import *
29
29
  build(setup_kwargs)
@@ -3,7 +3,7 @@ __description__ = (
3
3
  "A performant zero-dependency DBus library for Python with asyncio support"
4
4
  )
5
5
  __url__ = "https://github.com/bluetooth-devices/dbus-fast"
6
- __version__ = "2.39.5"
6
+ __version__ = "2.45.2"
7
7
  __author__ = "Bluetooth Devices authors, Tony Crisci"
8
8
  __author_email__ = "bluetooth@koston.org"
9
9
  __license__ = "MIT"
@@ -28,12 +28,12 @@ cdef class Marshaller:
28
28
  )
29
29
  cdef unsigned int _align(self, unsigned int n)
30
30
 
31
- cpdef write_boolean(self, object boolean, SignatureType type_)
31
+ cpdef write_boolean(self, bint boolean, SignatureType type_)
32
32
 
33
33
  @cython.locals(
34
34
  written=cython.uint,
35
35
  )
36
- cdef unsigned int _write_boolean(self, object boolean)
36
+ cdef unsigned int _write_boolean(self, bint boolean)
37
37
 
38
38
  cpdef write_string(self, object value, SignatureType type_)
39
39
 
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Callable
3
4
  from struct import Struct, error
4
- from typing import Any, Callable
5
+ from typing import Any
5
6
 
6
7
  from ..signature import SignatureType, Variant, get_signature_tree
7
8
 
@@ -109,6 +109,9 @@ cdef unsigned int TOKEN_LEFT_PAREN_AS_INT
109
109
  cdef object MARSHALL_STREAM_END_ERROR
110
110
  cdef object DEFAULT_BUFFER_SIZE
111
111
 
112
+ cdef Variant VARIANT_BOOL_TRUE
113
+ cdef Variant VARIANT_BOOL_FALSE
114
+
112
115
  cdef list _EMPTY_HEADERS
113
116
 
114
117
  cdef cython.uint EAGAIN
@@ -178,7 +181,7 @@ cdef class Unmarshaller:
178
181
 
179
182
  cpdef read_boolean(self, SignatureType type_)
180
183
 
181
- cdef _read_boolean(self)
184
+ cdef bint _read_boolean(self)
182
185
 
183
186
  cpdef read_uint32_unpack(self, SignatureType type_)
184
187
 
@@ -5,9 +5,9 @@ import errno
5
5
  import io
6
6
  import socket
7
7
  import sys
8
- from collections.abc import Iterable
8
+ from collections.abc import Callable, Iterable
9
9
  from struct import Struct
10
- from typing import TYPE_CHECKING, Any, Callable
10
+ from typing import TYPE_CHECKING, Any
11
11
 
12
12
  from ..constants import MESSAGE_FLAG_MAP, MESSAGE_TYPE_MAP, MessageFlag
13
13
  from ..errors import InvalidMessageError
@@ -19,7 +19,7 @@ MESSAGE_FLAG_INTENUM = MessageFlag
19
19
 
20
20
  MAX_UNIX_FDS = 16
21
21
  MAX_UNIX_FDS_SIZE = array.array("i").itemsize
22
- UNIX_FDS_CMSG_LENGTH = socket.CMSG_LEN(MAX_UNIX_FDS_SIZE)
22
+ UNIX_FDS_CMSG_LENGTH = socket.CMSG_LEN(MAX_UNIX_FDS_SIZE * MAX_UNIX_FDS)
23
23
 
24
24
  UNPACK_SYMBOL = {LITTLE_ENDIAN: "<", BIG_ENDIAN: ">"}
25
25
 
@@ -120,6 +120,10 @@ TOKEN_LEFT_CURLY_AS_INT = ord("{")
120
120
  TOKEN_LEFT_PAREN_AS_INT = ord("(")
121
121
 
122
122
 
123
+ VARIANT_BOOL_TRUE = Variant._factory(SIGNATURE_TREE_B, True)
124
+ VARIANT_BOOL_FALSE = Variant._factory(SIGNATURE_TREE_B, False)
125
+
126
+
123
127
  ARRAY = array.array
124
128
  SOL_SOCKET = socket.SOL_SOCKET
125
129
  SCM_RIGHTS = socket.SCM_RIGHTS
@@ -539,14 +543,17 @@ class Unmarshaller:
539
543
  if token_as_int == TOKEN_S_AS_INT:
540
544
  return Variant._factory(SIGNATURE_TREE_S, self._read_string_unpack())
541
545
  if token_as_int == TOKEN_B_AS_INT:
542
- return Variant._factory(SIGNATURE_TREE_B, self._read_boolean())
546
+ return VARIANT_BOOL_TRUE if self._read_boolean() else VARIANT_BOOL_FALSE
543
547
  if token_as_int == TOKEN_O_AS_INT:
544
548
  return Variant._factory(SIGNATURE_TREE_O, self._read_string_unpack())
545
549
  if token_as_int == TOKEN_U_AS_INT:
546
550
  return Variant._factory(SIGNATURE_TREE_U, self._read_uint32_unpack())
547
551
  if token_as_int == TOKEN_Y_AS_INT:
552
+ if cython.compiled:
553
+ if self._buf_len < self._pos:
554
+ raise IndexError("Not enough data to read byte")
548
555
  self._pos += 1
549
- return Variant._factory(SIGNATURE_TREE_Y, self._buf[self._pos - 1])
556
+ return Variant._factory(SIGNATURE_TREE_Y, self._buf_ustr[self._pos - 1])
550
557
  elif token_as_int == TOKEN_A_AS_INT:
551
558
  if signature == "ay":
552
559
  return Variant._factory(
@@ -600,39 +607,41 @@ class Unmarshaller:
600
607
  )
601
608
  else:
602
609
  array_length = self._uint32_unpack(self._buf, self._pos - UINT32_SIZE)[0]
603
- child_type: SignatureType = type_.children[0]
610
+ child_type = type_._child_0
604
611
  token_as_int = child_type.token_as_int
605
612
 
606
- if (
607
- token_as_int == TOKEN_X_AS_INT
608
- or token_as_int == TOKEN_T_AS_INT
609
- or token_as_int == TOKEN_D_AS_INT
610
- or token_as_int == TOKEN_LEFT_CURLY_AS_INT
611
- or token_as_int == TOKEN_LEFT_PAREN_AS_INT
612
- ):
613
+ if token_as_int in {
614
+ TOKEN_X_AS_INT,
615
+ TOKEN_T_AS_INT,
616
+ TOKEN_D_AS_INT,
617
+ TOKEN_LEFT_CURLY_AS_INT,
618
+ TOKEN_LEFT_PAREN_AS_INT,
619
+ }:
613
620
  # the first alignment is not included in the array size
614
621
  self._pos += -self._pos & 7 # align 8
615
622
 
616
623
  if token_as_int == TOKEN_Y_AS_INT:
617
624
  self._pos += array_length
618
- return self._buf[self._pos - array_length : self._pos]
625
+ if cython.compiled:
626
+ if self._buf_len < self._pos:
627
+ raise IndexError("Not enough data to read byte")
628
+ return self._buf_ustr[self._pos - array_length : self._pos]
619
629
 
620
630
  if token_as_int == TOKEN_LEFT_CURLY_AS_INT:
621
631
  result_dict: dict[Any, Any] = {}
622
632
  key: str | int
623
633
  beginning_pos = self._pos
624
- children = child_type.children
625
- child_0 = children[0]
626
- child_1 = children[1]
634
+ child_0 = child_type._child_0
635
+ child_1 = child_type._child_1
627
636
  child_0_token_as_int = child_0.token_as_int
628
637
  child_1_token_as_int = child_1.token_as_int
629
638
  # Strings with variant values are the most common case
630
639
  # so we optimize for that by inlining the string reading
631
640
  # and the variant reading here
632
641
  if (
633
- child_0_token_as_int == TOKEN_O_AS_INT
634
- or child_0_token_as_int == TOKEN_S_AS_INT
635
- ) and child_1_token_as_int == TOKEN_V_AS_INT:
642
+ child_0_token_as_int in {TOKEN_O_AS_INT, TOKEN_S_AS_INT}
643
+ and child_1_token_as_int == TOKEN_V_AS_INT
644
+ ):
636
645
  while self._pos - beginning_pos < array_length:
637
646
  self._pos += -self._pos & 7 # align 8
638
647
  key = self._read_string_unpack()
@@ -646,9 +655,9 @@ class Unmarshaller:
646
655
  key = self._read_uint16_unpack()
647
656
  result_dict[key] = self._read_variant()
648
657
  elif (
649
- child_0_token_as_int == TOKEN_O_AS_INT
650
- or child_0_token_as_int == TOKEN_S_AS_INT
651
- ) and child_1_token_as_int == TOKEN_A_AS_INT:
658
+ child_0_token_as_int in {TOKEN_O_AS_INT, TOKEN_S_AS_INT}
659
+ and child_1_token_as_int == TOKEN_A_AS_INT
660
+ ):
652
661
  while self._pos - beginning_pos < array_length:
653
662
  self._pos += -self._pos & 7 # align 8
654
663
  key = self._read_string_unpack()
@@ -693,6 +702,9 @@ class Unmarshaller:
693
702
  # first we read the signature
694
703
  signature_len = self._buf_ustr[self._pos] # byte
695
704
  o = self._pos + 1
705
+ if cython.compiled:
706
+ if self._buf_len < o + signature_len:
707
+ raise IndexError("Not enough data to read signature")
696
708
  self._pos += signature_len + 2 # one for the byte, one for the '\0'
697
709
  if field_0 == HEADER_UNIX_FDS_IDX: # defined by self._unix_fds
698
710
  continue
@@ -705,7 +717,7 @@ class Unmarshaller:
705
717
  elif token_as_int == TOKEN_G_AS_INT:
706
718
  headers[field_0] = self._read_signature()
707
719
  else:
708
- token = self._buf[o : o + signature_len].decode()
720
+ token = self._buf_ustr[o : o + signature_len].decode()
709
721
  # There shouldn't be any other types in the header
710
722
  # but just in case, we'll read it using the slow path
711
723
  headers[field_0] = self._readers[token](
@@ -2,7 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  import ast
4
4
  import inspect
5
- from typing import Any, Callable
5
+ from collections.abc import Callable
6
+ from typing import Any
6
7
 
7
8
  from ..signature import SignatureTree, SignatureType, Variant, get_signature_tree
8
9
 
@@ -6,9 +6,10 @@ import contextlib
6
6
  import logging
7
7
  import socket
8
8
  from collections import deque
9
+ from collections.abc import Callable
9
10
  from copy import copy
10
11
  from functools import partial
11
- from typing import Any, Callable
12
+ from typing import Any
12
13
 
13
14
  from .. import introspection as intr
14
15
  from ..auth import Authenticator, AuthExternal
@@ -2,8 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  import logging
4
4
  import socket
5
+ from collections.abc import Callable
5
6
  from functools import partial
6
- from typing import Callable
7
7
 
8
8
  from .._private.unmarshaller import Unmarshaller
9
9
  from ..message import Message
@@ -1,6 +1,5 @@
1
1
  import enum
2
2
  import os
3
- from typing import Optional
4
3
 
5
4
  from .errors import AuthError
6
5
 
@@ -65,10 +64,10 @@ class AuthExternal(Authenticator):
65
64
  :sealso: https://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol
66
65
  """
67
66
 
68
- def __init__(self, uid: Optional[int] = None) -> None:
67
+ def __init__(self, uid: int | None = None) -> None:
69
68
  self.negotiate_unix_fd: bool = False
70
69
  self.negotiating_fds: bool = False
71
- self.uid: Optional[int] = uid
70
+ self.uid: int | None = uid
72
71
 
73
72
  def _authentication_start(self, negotiate_unix_fd: bool = False) -> str:
74
73
  self.negotiate_unix_fd = negotiate_unix_fd
@@ -108,15 +108,9 @@ class ErrorType(str, Enum):
108
108
  :seealso: http://man7.org/linux/man-pages/man3/sd-bus-errors.3.html
109
109
  """
110
110
 
111
- SERVICE_ERROR = (
112
- "com.dubstepdish.dbus.next.ServiceError"
113
- ) #: A custom error to indicate an exported service threw an exception.
114
- INTERNAL_ERROR = (
115
- "com.dubstepdish.dbus.next.InternalError"
116
- ) #: A custom error to indicate something went wrong with the library.
117
- CLIENT_ERROR = (
118
- "com.dubstepdish.dbus.next.ClientError"
119
- ) #: A custom error to indicate something went wrong with the client.
111
+ SERVICE_ERROR = "com.dubstepdish.dbus.next.ServiceError" #: A custom error to indicate an exported service threw an exception.
112
+ INTERNAL_ERROR = "com.dubstepdish.dbus.next.InternalError" #: A custom error to indicate something went wrong with the library.
113
+ CLIENT_ERROR = "com.dubstepdish.dbus.next.ClientError" #: A custom error to indicate something went wrong with the client.
120
114
 
121
115
  FAILED = "org.freedesktop.DBus.Error.Failed"
122
116
  NO_MEMORY = "org.freedesktop.DBus.Error.NoMemory"
@@ -1,6 +1,3 @@
1
- from typing import Optional, Union
2
-
3
-
4
1
  class SignatureBodyMismatchError(ValueError):
5
2
  pass
6
3
 
@@ -60,7 +57,7 @@ from .validators import assert_interface_name_valid # noqa: E402
60
57
 
61
58
  class DBusError(Exception):
62
59
  def __init__(
63
- self, type_: Union[ErrorType, str], text: str, reply: Optional[Message] = None
60
+ self, type_: ErrorType | str, text: str, reply: Message | None = None
64
61
  ) -> None:
65
62
  super().__init__(text)
66
63
 
@@ -1,7 +1,7 @@
1
1
  import io
2
2
  import logging
3
3
  import traceback
4
- from typing import Callable, Optional
4
+ from collections.abc import Callable
5
5
 
6
6
  from .. import introspection as intr
7
7
  from .._private.unmarshaller import Unmarshaller
@@ -161,9 +161,9 @@ class MessageBus(BaseMessageBus):
161
161
 
162
162
  def __init__(
163
163
  self,
164
- bus_address: Optional[str] = None,
164
+ bus_address: str | None = None,
165
165
  bus_type: BusType = BusType.SESSION,
166
- auth: Optional[Authenticator] = None,
166
+ auth: Authenticator | None = None,
167
167
  ):
168
168
  if _import_error:
169
169
  raise _import_error
@@ -188,9 +188,8 @@ class MessageBus(BaseMessageBus):
188
188
 
189
189
  def connect(
190
190
  self,
191
- connect_notify: Optional[
192
- Callable[["MessageBus", Optional[Exception]], None]
193
- ] = None,
191
+ connect_notify: None
192
+ | (Callable[["MessageBus", Exception | None], None]) = None,
194
193
  ):
195
194
  """Connect this message bus to the DBus daemon.
196
195
 
@@ -277,9 +276,8 @@ class MessageBus(BaseMessageBus):
277
276
  def call(
278
277
  self,
279
278
  msg: Message,
280
- reply_notify: Optional[
281
- Callable[[Optional[Message], Optional[Exception]], None]
282
- ] = None,
279
+ reply_notify: None
280
+ | (Callable[[Message | None, Exception | None], None]) = None,
283
281
  ):
284
282
  """Send a method call and asynchronously wait for a reply from the DBus
285
283
  daemon.
@@ -293,7 +291,7 @@ class MessageBus(BaseMessageBus):
293
291
  BaseMessageBus._check_callback_type(reply_notify)
294
292
  self._call(msg, reply_notify)
295
293
 
296
- def call_sync(self, msg: Message) -> Optional[Message]:
294
+ def call_sync(self, msg: Message) -> Message | None:
297
295
  """Send a method call and synchronously wait for a reply from the DBus
298
296
  daemon.
299
297
 
@@ -1,5 +1,4 @@
1
1
  import xml.etree.ElementTree as ET
2
- from typing import Union
3
2
 
4
3
  from .. import introspection as intr
5
4
  from ..constants import ErrorType
@@ -307,7 +306,7 @@ class ProxyObject(BaseProxyObject):
307
306
  self,
308
307
  bus_name: str,
309
308
  path: str,
310
- introspection: Union[intr.Node, str, ET.Element],
309
+ introspection: intr.Node | str | ET.Element,
311
310
  bus: BaseMessageBus,
312
311
  ):
313
312
  super().__init__(bus_name, path, introspection, bus, ProxyInterface)
@@ -1,5 +1,4 @@
1
1
  import xml.etree.ElementTree as ET
2
- from typing import Optional, Union
3
2
 
4
3
  from .constants import ArgDirection, PropertyAccess
5
4
  from .errors import InvalidIntrospectionError
@@ -50,10 +49,10 @@ class Arg:
50
49
 
51
50
  def __init__(
52
51
  self,
53
- signature: Union[SignatureType, str],
54
- direction: Optional[ArgDirection] = None,
55
- name: Optional[str] = None,
56
- annotations: Optional[dict[str, str]] = None,
52
+ signature: SignatureType | str,
53
+ direction: ArgDirection | None = None,
54
+ name: str | None = None,
55
+ annotations: dict[str, str] | None = None,
57
56
  ):
58
57
  type_ = None
59
58
  if type(signature) is SignatureType:
@@ -132,9 +131,9 @@ class Signal:
132
131
 
133
132
  def __init__(
134
133
  self,
135
- name: Optional[str],
136
- args: Optional[list[Arg]] = None,
137
- annotations: Optional[dict[str, str]] = None,
134
+ name: str,
135
+ args: list[Arg] | None = None,
136
+ annotations: dict[str, str] | None = None,
138
137
  ):
139
138
  if name is not None:
140
139
  assert_member_name_valid(name)
@@ -210,7 +209,7 @@ class Method:
210
209
  name: str,
211
210
  in_args: list[Arg] = [],
212
211
  out_args: list[Arg] = [],
213
- annotations: Optional[dict[str, str]] = None,
212
+ annotations: dict[str, str] | None = None,
214
213
  ):
215
214
  assert_member_name_valid(name)
216
215
 
@@ -295,7 +294,7 @@ class Property:
295
294
  name: str,
296
295
  signature: str,
297
296
  access: PropertyAccess = PropertyAccess.READWRITE,
298
- annotations: Optional[dict[str, str]] = None,
297
+ annotations: dict[str, str] | None = None,
299
298
  validate: bool = True,
300
299
  ):
301
300
  if validate:
@@ -373,10 +372,10 @@ class Interface:
373
372
  def __init__(
374
373
  self,
375
374
  name: str,
376
- methods: Optional[list[Method]] = None,
377
- signals: Optional[list[Signal]] = None,
378
- properties: Optional[list[Property]] = None,
379
- annotations: Optional[dict[str, str]] = None,
375
+ methods: list[Method] | None = None,
376
+ signals: list[Signal] | None = None,
377
+ properties: list[Property] | None = None,
378
+ annotations: dict[str, str] | None = None,
380
379
  ):
381
380
  assert_interface_name_valid(name)
382
381
 
@@ -467,15 +466,15 @@ class Node:
467
466
 
468
467
  def __init__(
469
468
  self,
470
- name: Optional[str] = None,
471
- interfaces: Optional[list[Interface]] = None,
469
+ name: str | None = None,
470
+ interfaces: list[Interface] | None = None,
472
471
  is_root: bool = True,
473
472
  ):
474
473
  if not is_root and not name:
475
474
  raise InvalidIntrospectionError('child nodes must have a "name" attribute')
476
475
 
477
476
  self.interfaces = interfaces if interfaces is not None else []
478
- self.nodes = []
477
+ self.nodes: list[Node] = []
479
478
  self.name = name
480
479
  self.is_root = is_root
481
480
 
@@ -564,8 +563,8 @@ class Node:
564
563
  elem.text = i + " "
565
564
  if not elem.tail or not elem.tail.strip():
566
565
  elem.tail = i
567
- for elem in elem:
568
- indent(elem, level + 1)
566
+ for elem_ in elem:
567
+ indent(elem_, level + 1)
569
568
  if not elem.tail or not elem.tail.strip():
570
569
  elem.tail = i
571
570
  elif level and (not elem.tail or not elem.tail.strip()):
@@ -576,7 +575,7 @@ class Node:
576
575
  return header + ET.tostring(xml, encoding="unicode").rstrip()
577
576
 
578
577
  @staticmethod
579
- def default(name: Optional[str] = None) -> "Node":
578
+ def default(name: str | None = None) -> "Node":
580
579
  """Create a :class:`Node` with the default interfaces supported by this library.
581
580
 
582
581
  The default interfaces include:
@@ -55,7 +55,7 @@ cdef class Message:
55
55
  header_buffer=bytearray,
56
56
  var=Variant
57
57
  )
58
- cpdef _marshall(self, object negotiate_unix_fd)
58
+ cpdef _marshall(self, bint negotiate_unix_fd)
59
59
 
60
60
  cdef _fast_init(
61
61
  self,
@@ -1,4 +1,4 @@
1
- from typing import Any, Optional, Union
1
+ from typing import Any
2
2
 
3
3
  from ._private.constants import LITTLE_ENDIAN, PROTOCOL_VERSION, HeaderField
4
4
  from ._private.marshaller import Marshaller
@@ -112,19 +112,19 @@ class Message:
112
112
 
113
113
  def __init__(
114
114
  self,
115
- destination: Optional[str] = None,
116
- path: Optional[str] = None,
117
- interface: Optional[str] = None,
118
- member: Optional[str] = None,
115
+ destination: str | None = None,
116
+ path: str | None = None,
117
+ interface: str | None = None,
118
+ member: str | None = None,
119
119
  message_type: MessageType = MESSAGE_TYPE_METHOD_CALL,
120
- flags: Union[MessageFlag, int] = MESSAGE_FLAG_NONE,
121
- error_name: Optional[Union[str, ErrorType]] = None,
122
- reply_serial: Optional[int] = None,
123
- sender: Optional[str] = None,
120
+ flags: MessageFlag | int = MESSAGE_FLAG_NONE,
121
+ error_name: str | ErrorType | None = None,
122
+ reply_serial: int | None = None,
123
+ sender: str | None = None,
124
124
  unix_fds: list[int] = [],
125
- signature: Optional[Union[SignatureTree, str]] = None,
125
+ signature: SignatureTree | str | None = None,
126
126
  body: list[Any] = [],
127
- serial: Optional[int] = None,
127
+ serial: int | None = None,
128
128
  validate: bool = True,
129
129
  ) -> None:
130
130
  self._fast_init(
@@ -148,13 +148,13 @@ class Message:
148
148
 
149
149
  def _fast_init(
150
150
  self,
151
- destination: Optional[_str],
152
- path: Optional[_str],
153
- interface: Optional[_str],
154
- member: Optional[_str],
151
+ destination: _str | None,
152
+ path: _str | None,
153
+ interface: _str | None,
154
+ member: _str | None,
155
155
  message_type: _MessageType,
156
156
  flags: _MessageFlag,
157
- error_name: Optional[_str],
157
+ error_name: _str | None,
158
158
  reply_serial: _int,
159
159
  sender: _str,
160
160
  unix_fds: _list[int],
@@ -216,7 +216,7 @@ class Message:
216
216
 
217
217
  @staticmethod
218
218
  def new_error(
219
- msg: "Message", error_name: Union[str, ErrorType], error_text: str
219
+ msg: "Message", error_name: str | ErrorType, error_text: str
220
220
  ) -> "Message":
221
221
  """A convenience constructor to create an error message in reply to the given message.
222
222
 
@@ -280,8 +280,8 @@ class Message:
280
280
  interface: str,
281
281
  member: str,
282
282
  signature: str = "",
283
- body: Optional[list[Any]] = None,
284
- unix_fds: Optional[list[int]] = None,
283
+ body: list[Any] | None = None,
284
+ unix_fds: list[int] | None = None,
285
285
  ) -> "Message":
286
286
  """A convenience constructor to create a new signal message.
287
287
 
@@ -5,8 +5,9 @@ import logging
5
5
  import socket
6
6
  import traceback
7
7
  import xml.etree.ElementTree as ET
8
+ from collections.abc import Callable
8
9
  from functools import partial
9
- from typing import TYPE_CHECKING, Any, Callable
10
+ from typing import TYPE_CHECKING, Any
10
11
 
11
12
  from . import introspection as intr
12
13
  from ._private.address import get_bus_address, parse_address
@@ -5,10 +5,9 @@ import inspect
5
5
  import logging
6
6
  import re
7
7
  import xml.etree.ElementTree as ET
8
- from collections.abc import Coroutine
8
+ from collections.abc import Callable, Coroutine
9
9
  from dataclasses import dataclass
10
10
  from functools import lru_cache
11
- from typing import Callable
12
11
 
13
12
  from . import introspection as intr
14
13
  from . import message_bus
@@ -33,7 +33,7 @@ cdef class ServiceInterface:
33
33
  cdef list __signals
34
34
  cdef set __buses
35
35
  cdef dict __handlers
36
- cdef dict __enabled_handlers_by_name_signature
36
+ cdef dict __handlers_by_name_signature
37
37
 
38
38
  @cython.locals(handlers=dict,in_signature=str,method=_Method)
39
39
  @staticmethod
@@ -3,8 +3,9 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import copy
5
5
  import inspect
6
+ from collections.abc import Callable
6
7
  from functools import wraps
7
- from typing import TYPE_CHECKING, Any, Callable, Protocol
8
+ from typing import TYPE_CHECKING, Any, Protocol
8
9
 
9
10
  from . import introspection as intr
10
11
  from ._private.util import (
@@ -33,8 +34,7 @@ HandlerType = Callable[[Message, SendReply], None]
33
34
 
34
35
 
35
36
  class _MethodCallbackProtocol(Protocol):
36
- def __call__(self, interface: ServiceInterface, *args: Any) -> Any:
37
- ...
37
+ def __call__(self, interface: ServiceInterface, *args: Any) -> Any: ...
38
38
 
39
39
 
40
40
  class _Method:
@@ -381,7 +381,7 @@ class ServiceInterface:
381
381
  self.__handlers_by_name_signature: dict[
382
382
  BaseMessageBus, dict[str, tuple[_Method, HandlerType]]
383
383
  ] = {}
384
- for name, member in inspect.getmembers(type(self)):
384
+ for _, member in inspect.getmembers(type(self)):
385
385
  member_dict = getattr(member, "__dict__", {})
386
386
  if type(member) is _Property:
387
387
  # XXX The getter and the setter may show up as different
@@ -516,6 +516,10 @@ class ServiceInterface:
516
516
  bus: BaseMessageBus,
517
517
  maker: Callable[[ServiceInterface, _Method], HandlerType],
518
518
  ) -> None:
519
+ if bus in interface.__buses:
520
+ raise ValueError(
521
+ "Same interface instance cannot be added to the same bus twice"
522
+ )
519
523
  interface.__buses.add(bus)
520
524
  interface.__handlers[bus] = {
521
525
  method: maker(interface, method) for method in interface.__methods
@@ -668,8 +672,8 @@ class ServiceInterface:
668
672
  else:
669
673
  try:
670
674
  result[prop.name] = Variant(prop.signature, value)
671
- except SignatureBodyMismatchError as e:
672
- result_error = e
675
+ except SignatureBodyMismatchError as exc:
676
+ result_error = exc
673
677
  del result[prop.name]
674
678
 
675
679
  if any(v is None for v in result.values()):
@@ -9,6 +9,8 @@ cdef class SignatureType:
9
9
  cdef public unsigned int token_as_int
10
10
  cdef public list children
11
11
  cdef str _signature
12
+ cdef public SignatureType _child_0
13
+ cdef public SignatureType _child_1
12
14
 
13
15
 
14
16
  cdef class SignatureTree:
@@ -1,11 +1,12 @@
1
+ from collections.abc import Callable
1
2
  from functools import lru_cache
2
- from typing import Any, Callable, Optional, Union
3
+ from typing import Any
3
4
 
4
5
  from .errors import InvalidSignatureError, SignatureBodyMismatchError
5
6
  from .validators import is_object_path_valid
6
7
 
7
8
 
8
- class SignatureType:
9
+ class SignatureType: # noqa: PLW1641
9
10
  """A class that represents a single complete type within a signature.
10
11
 
11
12
  This class is not meant to be constructed directly. Use the :class:`SignatureTree`
@@ -21,14 +22,23 @@ class SignatureType:
21
22
  """
22
23
 
23
24
  _tokens = "ybnqiuxtdsogavh({"
24
- __slots__ = ("_signature", "children", "token", "token_as_int")
25
+ __slots__ = (
26
+ "_child_0",
27
+ "_child_1",
28
+ "_signature",
29
+ "children",
30
+ "token",
31
+ "token_as_int",
32
+ )
25
33
 
26
34
  def __init__(self, token: str) -> None:
27
35
  """Init a new SignatureType."""
28
36
  self.token: str = token
29
37
  self.token_as_int = ord(token)
30
38
  self.children: list[SignatureType] = []
31
- self._signature: Optional[str] = None
39
+ self._child_0: SignatureType | None = None
40
+ self._child_1: SignatureType | None = None
41
+ self._signature: str | None = None
32
42
 
33
43
  def __eq__(self, other: object) -> bool:
34
44
  """Compare this type to another type or signature string."""
@@ -60,6 +70,18 @@ class SignatureType:
60
70
  self._signature = self._collapse()
61
71
  return self._signature
62
72
 
73
+ def _add_child(self, child: "SignatureType") -> None:
74
+ """Add a child type to this type.
75
+
76
+ :param child: The child type to add.
77
+ :type child: :class:`SignatureType`
78
+ """
79
+ if self._child_0 is None:
80
+ self._child_0 = child
81
+ elif self._child_1 is None:
82
+ self._child_1 = child
83
+ self.children.append(child)
84
+
63
85
  @staticmethod
64
86
  def _parse_next(signature: str) -> tuple["SignatureType", str]:
65
87
  if not signature:
@@ -76,7 +98,7 @@ class SignatureType:
76
98
  (child, signature) = SignatureType._parse_next(signature[1:])
77
99
  if not child:
78
100
  raise InvalidSignatureError("missing type for array")
79
- self.children.append(child)
101
+ self._add_child(child)
80
102
  return (self, signature)
81
103
  if token == "(":
82
104
  self = SignatureType("(")
@@ -85,7 +107,7 @@ class SignatureType:
85
107
  (child, signature) = SignatureType._parse_next(signature)
86
108
  if not signature:
87
109
  raise InvalidSignatureError('missing closing ")" for struct')
88
- self.children.append(child)
110
+ self._add_child(child)
89
111
  if signature[0] == ")":
90
112
  return (self, signature[1:])
91
113
  elif token == "{":
@@ -94,13 +116,13 @@ class SignatureType:
94
116
  (key_child, signature) = SignatureType._parse_next(signature)
95
117
  if not key_child or len(key_child.children):
96
118
  raise InvalidSignatureError("expected a simple type for dict entry key")
97
- self.children.append(key_child)
119
+ self._add_child(key_child)
98
120
  (value_child, signature) = SignatureType._parse_next(signature)
99
121
  if not value_child:
100
122
  raise InvalidSignatureError("expected a value for dict entry")
101
123
  if not signature or signature[0] != "}":
102
124
  raise InvalidSignatureError('missing closing "}" for dict entry')
103
- self.children.append(value_child)
125
+ self._add_child(value_child)
104
126
  return (self, signature[1:])
105
127
 
106
128
  # basic type
@@ -316,7 +338,7 @@ class SignatureType:
316
338
  }
317
339
 
318
340
 
319
- class SignatureTree:
341
+ class SignatureTree: # noqa: PLW1641
320
342
  """A class that represents a signature as a tree structure for conveniently
321
343
  working with DBus signatures.
322
344
 
@@ -381,7 +403,7 @@ class SignatureTree:
381
403
  return True
382
404
 
383
405
 
384
- class Variant:
406
+ class Variant: # noqa: PLW1641
385
407
  """A class to represent a DBus variant (type "v").
386
408
 
387
409
  This class is used in message bodies to represent variants. The user can
@@ -403,7 +425,7 @@ class Variant:
403
425
 
404
426
  def __init__(
405
427
  self,
406
- signature: Union[str, SignatureTree, SignatureType],
428
+ signature: str | SignatureTree | SignatureType,
407
429
  value: Any,
408
430
  verify: bool = True,
409
431
  ) -> None:
File without changes