Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-jeepney for openSUSE:Factory 
checked in at 2025-07-15 16:42:59
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-jeepney (Old)
 and      /work/SRC/openSUSE:Factory/.python-jeepney.new.7373 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-jeepney"

Tue Jul 15 16:42:59 2025 rev:12 rq:1292494 version:0.9.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-jeepney/python-jeepney.changes    
2025-05-31 19:14:48.085636814 +0200
+++ /work/SRC/openSUSE:Factory/.python-jeepney.new.7373/python-jeepney.changes  
2025-07-15 16:43:36.063302650 +0200
@@ -1,0 +2,15 @@
+Sun Jul 13 12:14:17 UTC 2025 - Dirk Müller <dmuel...@suse.com>
+
+- update to 0.9.0:
+  * Fixed subscribing to messages on the message bus with a
+    path_namespace parameter (:mr:`38`)
+  * Fixed authentication on (some?) BSDs, using SCM_CREDS
+    (:mr:`33`), for all integrations except for asyncio
+  * :class:`~.DBusAddress` and message generators will now raise
+    :exc:`ValueError` if given invalid D-Bus names
+  * Bindings can now be :doc:`generated <bindgen>` from D-Bus XML
+    in a file with the new :option:`--file` option (:mr:`34`).
+  * The async_timeout package is no longer required for running
+    the tests on Python 3.11 or above (:mr:`39`).
+
+-------------------------------------------------------------------

Old:
----
  jeepney-0.8.0.tar.gz

New:
----
  jeepney-0.9.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-jeepney.spec ++++++
--- /var/tmp/diff_new_pack.G4sYnN/_old  2025-07-15 16:43:36.855332105 +0200
+++ /var/tmp/diff_new_pack.G4sYnN/_new  2025-07-15 16:43:36.855332105 +0200
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-jeepney
-Version:        0.8.0
+Version:        0.9.0
 Release:        0
 Summary:        Low-level, pure Python DBus protocol wrapper
 License:        MIT

++++++ jeepney-0.8.0.tar.gz -> jeepney-0.9.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/.gitignore new/jeepney-0.9.0/.gitignore
--- old/jeepney-0.8.0/.gitignore        2018-09-11 21:07:32.033692000 +0200
+++ new/jeepney-0.9.0/.gitignore        1970-01-01 01:00:00.000000000 +0100
@@ -1,4 +0,0 @@
-__pycache__/
-/dist/
-/docs/_build/
-.pytest_cache/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/.gitlab-ci.yml 
new/jeepney-0.9.0/.gitlab-ci.yml
--- old/jeepney-0.8.0/.gitlab-ci.yml    2022-04-03 19:31:55.796912400 +0200
+++ new/jeepney-0.9.0/.gitlab-ci.yml    1970-01-01 01:00:00.000000000 +0100
@@ -1,35 +0,0 @@
-before_script:
-  - pip install '.[test]'
-
-test_job_py310:
-  image: python:3.10
-  script:
-    - pytest
-  stage: test
-
-test_job_py39:
-  image: python:3.9
-  script:
-    - pytest
-  stage: test
-
-test_job_py38:
-  image: python:3.8
-  script:
-    - pytest
-  stage: test
-
-test_job_py37:
-  image: python:3.7
-  script:
-    - pytest
-  stage: test
-
-test_job_integration:
-  image: fedora:32
-  before_script:
-    - dnf install -y dbus-daemon python3-pip
-    - pip install '.[test,trio]'
-  script:
-    - dbus-run-session -- pytest
-  stage: test
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/PKG-INFO new/jeepney-0.9.0/PKG-INFO
--- old/jeepney-0.8.0/PKG-INFO  1970-01-01 01:00:00.000000000 +0100
+++ new/jeepney-0.9.0/PKG-INFO  1970-01-01 01:00:00.000000000 +0100
@@ -1,24 +1,23 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: jeepney
-Version: 0.8.0
+Version: 0.9.0
 Summary: Low-level, pure Python DBus protocol wrapper.
-Home-page: https://gitlab.com/takluyver/jeepney
-Author: Thomas Kluyver
-Author-email: tho...@kluyver.me.uk
+Author-email: Thomas Kluyver <tho...@kluyver.me.uk>
 Requires-Python: >=3.7
 Description-Content-Type: text/x-rst
-Classifier: License :: OSI Approved :: MIT License
+License-Expression: MIT
 Classifier: Programming Language :: Python :: 3
 Classifier: Topic :: Desktop Environment
+License-File: LICENSE
 Requires-Dist: pytest ; extra == "test"
 Requires-Dist: pytest-trio ; extra == "test"
 Requires-Dist: pytest-asyncio >=0.17 ; extra == "test"
 Requires-Dist: testpath ; extra == "test"
 Requires-Dist: trio ; extra == "test"
-Requires-Dist: async-timeout ; extra == "test"
+Requires-Dist: async-timeout ; extra == "test" and ( python_version < '3.11')
 Requires-Dist: trio ; extra == "trio"
-Requires-Dist: async_generator ; extra == "trio" and ( python_version == '3.6')
 Project-URL: Documentation, https://jeepney.readthedocs.io/en/latest/
+Project-URL: Source, https://gitlab.com/takluyver/jeepney
 Provides-Extra: test
 Provides-Extra: trio
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/docs/bindgen.rst 
new/jeepney-0.9.0/docs/bindgen.rst
--- old/jeepney-0.8.0/docs/bindgen.rst  2021-07-10 17:19:49.182029500 +0200
+++ new/jeepney-0.9.0/docs/bindgen.rst  2025-02-27 19:48:26.862029000 +0100
@@ -18,3 +18,33 @@
 You are welcome to edit the generated code, e.g. to add docstrings or give
 parameters meaningful names. Names like ``arg_1`` are created when
 introspection doesn't provide a name.
+
+
+Bindgen command options
+-----------------------
+
+.. program:: python -m jeepney.bindgen
+
+.. option:: -n <bus name>, --name <bus name>
+
+   Bus name to introspect, required unless using :option:`--file`.
+
+.. option:: -p <object path>, --path <object path>
+
+   Object path to introspect, required unless using :option:`--file`.
+   Bindings will be generated for all interfaces this object exposes, except
+   for common interfaces like 'Introspectable'.
+
+.. option:: --bus <bus>
+
+   Bus to connect to, SESSION (default) or SYSTEM.
+
+.. option:: -f <path>, --file <path>
+
+   An XML file to use as input instead of connecting to D-Bus and using
+   introspection. The options above are ignored if this is used.
+
+.. option:: -o <path>, --output <path>
+
+   Write the output (Python code) to the specified file.
+   By default, a filename is chosen based on the input.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/docs/conf.py 
new/jeepney-0.9.0/docs/conf.py
--- old/jeepney-0.8.0/docs/conf.py      2021-07-10 17:19:49.185029500 +0200
+++ new/jeepney-0.9.0/docs/conf.py      2025-02-27 19:48:26.862029000 +0100
@@ -34,8 +34,14 @@
     'sphinx.ext.autodoc',
     'sphinx.ext.viewcode',
     'sphinx.ext.intersphinx',
+    'sphinx.ext.extlinks',
 ]
 
+extlinks = {
+    'issue': ('https://gitlab.com/takluyver/jeepney/-/issues/%s', "issue #%s"),
+    'mr': ('https://gitlab.com/takluyver/jeepney/-/merge_requests/%s', "MR 
!%s"),
+}
+
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
 
@@ -129,10 +135,7 @@
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
 #
-import sphinx_rtd_theme
-
 html_theme = "sphinx_rtd_theme"
-html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/docs/release-notes.rst 
new/jeepney-0.9.0/docs/release-notes.rst
--- old/jeepney-0.8.0/docs/release-notes.rst    2022-04-03 19:51:40.362281800 
+0200
+++ new/jeepney-0.9.0/docs/release-notes.rst    2025-02-27 19:48:26.862029000 
+0100
@@ -1,6 +1,32 @@
 Release notes
 =============
 
+0.9
+---
+
+2025-02-27
+
+* Fixed subscribing to messages on the message bus with a ``path_namespace``
+  parameter (:mr:`38`)
+* Fixed authentication on (some?) BSDs, using SCM_CREDS (:mr:`33`), for all
+  integrations except for asyncio (which does not expose ``sendmsg``).
+* :class:`~.DBusAddress` and message generators will now raise 
:exc:`ValueError`
+  if given invalid D-Bus names - bus names, object paths, or interface names
+  (:mr:`36`). Previously these could easily be sent in messages, resulting in
+  the bus closing the connection.
+* Bindings can now be :doc:`generated <bindgen>` from D-Bus XML in a file
+  with the new :option:`--file` option (:mr:`34`).
+* The ``async_timeout`` package is no longer required for running the tests on
+  Python 3.11 or above (:mr:`39`).
+
+Breaking changes
+~~~~~~~~~~~~~~~~
+
+* Removed the deprecated ``connection.router`` API in the blocking IO
+  integration (:mr:`40`).
+* Removed the deprecated ``unwrap`` parameter from ``send_and_get_reply`` in
+  the blocking integration (:mr:`37`).
+
 0.8
 ---
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/__init__.py 
new/jeepney-0.9.0/jeepney/__init__.py
--- old/jeepney-0.8.0/jeepney/__init__.py       2022-04-03 19:52:40.934471100 
+0200
+++ new/jeepney-0.9.0/jeepney/__init__.py       2025-02-27 19:48:26.865029000 
+0100
@@ -10,4 +10,4 @@
 from .fds import FileDescriptor, NoFDError
 from .wrappers import *
 
-__version__ = '0.8.0'
+__version__ = '0.9.0'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/auth.py 
new/jeepney-0.9.0/jeepney/auth.py
--- old/jeepney-0.8.0/jeepney/auth.py   2021-07-10 17:19:49.190029600 +0200
+++ new/jeepney-0.9.0/jeepney/auth.py   2025-02-27 19:48:26.865029000 +0100
@@ -54,12 +54,20 @@
     """Process data for the SASL authentication conversation
 
     If enable_fds is True, this includes negotiating support for passing
-    file descriptors.
+    file descriptors. If inc_null_byte is True, sends the '\0' byte
+    at the beginning of the negotiations, which was the past behavior,
+    but which prevents sending the SCM_CREDS ancillary data over the socket,
+    breaking authentication on *BSD; the caller should rather send that
+    null byte and ancillary data and pass inc_null_byte=False to prevent
+    it being done here.
     """
-    def __init__(self, enable_fds=False):
+    def __init__(self, enable_fds=False, inc_null_byte=True):
         self.enable_fds = enable_fds
         self.buffer = bytearray()
-        self._to_send = b'\0' + make_auth_external()
+        if inc_null_byte:
+            self._to_send = b'\0' + make_auth_external()
+        else:
+            self._to_send = make_auth_external()
         self.state = ClientState.WaitingForOk
         self.error = None
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/bindgen.py 
new/jeepney-0.9.0/jeepney/bindgen.py
--- old/jeepney-0.8.0/jeepney/bindgen.py        2021-07-10 17:19:49.190029600 
+0200
+++ new/jeepney-0.9.0/jeepney/bindgen.py        2025-02-27 19:48:26.865029000 
+0100
@@ -1,7 +1,9 @@
 """Generate a wrapper class from DBus introspection data"""
 import argparse
-from textwrap import indent
+import os.path
+import sys
 import xml.etree.ElementTree as ET
+from textwrap import indent
 
 from jeepney.wrappers import Introspectable
 from jeepney.io.blocking import open_dbus_connection, Proxy
@@ -42,8 +44,8 @@
 class {cls_name}(MessageGenerator):
     interface = {interface!r}
 
-    def __init__(self, object_path={path!r},
-                 bus_name={bus_name!r}):
+    def __init__(self, object_path{path_default},
+                 bus_name{name_default}):
         super().__init__(object_path=object_path, bus_name=bus_name)
 """
 
@@ -56,8 +58,12 @@
 
     def make_code(self):
         cls_name = self.name.split('.')[-1]
-        chunks = [INTERFACE_CLASS_TEMPLATE.format(cls_name=cls_name,
-              interface=self.name, path=self.path, bus_name=self.bus_name)]
+        chunks = [INTERFACE_CLASS_TEMPLATE.format(
+            cls_name=cls_name,
+            interface=self.name,
+            path_default='' if self.path is None else f'={self.path!r}',
+            name_default='' if self.bus_name is None else f'={self.bus_name!r}'
+        )]
         for method in self.methods:
             chunks.append(indent(method.make_code(), ' ' * 4))
         return '\n'.join(chunks)
@@ -100,7 +106,12 @@
 
     return i
 
-def generate(path, name, output_file, bus='SESSION'):
+def generate_from_introspection(path, name, output_file, bus='SESSION'):
+    # Many D-Bus services have a main object at a predictable name, e.g.
+    # org.freedesktop.Notifications -> /org/freedesktop/Notifications
+    if not path:
+        path = '/' + name.replace('.', '/')
+
     conn = open_dbus_connection(bus)
     introspectable = Proxy(Introspectable(path, name), conn)
     xml, = introspectable.Introspect()
@@ -109,17 +120,50 @@
     n_interfaces = code_from_xml(xml, path, name, output_file)
     print("Written {} interface wrappers to {}".format(n_interfaces, 
output_file))
 
+def generate_from_file(input_file, path, name, output_file):
+    with open(input_file, encoding='utf-8') as f:
+        xml = f.read()
+
+    n_interfaces = code_from_xml(xml, path, name, output_file)
+    print("Written {} interface wrappers to {}".format(n_interfaces, 
output_file))
+
 def main():
-    ap = argparse.ArgumentParser()
-    ap.add_argument('-n', '--name', required=True)
-    ap.add_argument('-p', '--path', required=True)
-    ap.add_argument('--bus', default='SESSION')
-    ap.add_argument('-o', '--output')
+    ap = argparse.ArgumentParser(
+        description="Generate a simple wrapper module to call D-Bus methods.",
+        epilog="If you don't use --file, this will connect to D-Bus and 
introspect the "
+               "given name and path. --name and --path can also be used with 
--file, "
+               "to give defaults for the generated class."
+    )
+    ap.add_argument('-n', '--name',
+                    help='Bus name to introspect, required unless using file')
+    ap.add_argument('-p', '--path',
+                    help='Object path to introspect. If not specified, a path 
matching '
+                         'the name will be used, e.g. 
/org/freedesktop/Notifications for org.freedesktop.Notifications')
+    ap.add_argument('--bus', default='SESSION',
+                    help='Bus to connect to for introspection 
(SESSION/SYSTEM), default SESSION')
+    ap.add_argument('-f', '--file',
+                    help='XML file to use instead of D-Bus introspection')
+    ap.add_argument('-o', '--output',
+                    help='Output filename')
     args = ap.parse_args()
 
-    output = args.output or (args.path[1:].replace('/', '_') + '.py')
+    if not (args.file or args.name):
+        sys.exit("Either --name or --file is required")
 
-    generate(args.path, args.name, output, args.bus)
+    # If no --output, guess a (hopefully) reasonable name.
+    if args.output:
+        output = args.output
+    elif args.file:
+        output = os.path.splitext(os.path.basename(args.file))[0] + '.py'
+    elif args.path and len(args.path) > 1:
+        output = args.path[1:].replace('/', '_') + '.py'
+    else:  # e.g. path is '/'
+        output = args.name.replace('.', '_') + '.py'
+
+    if args.file:
+        generate_from_file(args.file, args.path, args.name, output)
+    else:
+        generate_from_introspection(args.path, args.name, output, args.bus)
 
 
 if __name__ == '__main__':
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/bus_messages.py 
new/jeepney-0.9.0/jeepney/bus_messages.py
--- old/jeepney-0.8.0/jeepney/bus_messages.py   2021-07-10 17:19:49.190029600 
+0200
+++ new/jeepney-0.9.0/jeepney/bus_messages.py   2025-02-27 19:48:26.865029000 
+0100
@@ -176,6 +176,9 @@
         if self.message_type:
             pairs.append(('type', self.message_type.name))
 
+        if self.path_namespace:
+            pairs.append(('path_namespace', self.path_namespace))
+
         if self.eavesdrop:
             pairs.append(('eavesdrop', 'true'))
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/io/blocking.py 
new/jeepney-0.9.0/jeepney/io/blocking.py
--- old/jeepney-0.8.0/jeepney/io/blocking.py    2022-04-03 19:31:55.796912400 
+0200
+++ new/jeepney-0.9.0/jeepney/io/blocking.py    2025-02-27 19:48:26.866029000 
+0100
@@ -10,14 +10,12 @@
 import socket
 import time
 from typing import Optional
-from warnings import warn
 
 from jeepney import Parser, Message, MessageType, HeaderFields
 from jeepney.auth import Authenticator, BEGIN
 from jeepney.bus import get_bus
 from jeepney.fds import FileDescriptor, fds_buf_size
 from jeepney.wrappers import ProxyBase, unwrap_msg
-from jeepney.routing import Router
 from jeepney.bus_messages import message_bus
 from .common import MessageFilters, FilterHandle, check_replyable
 
@@ -131,7 +129,6 @@
         super().__init__(sock, enable_fds)
 
         # Message routing machinery
-        self._router = Router(_Future)  # Old interface, for backwards compat
         self._filters = MessageFilters()
 
         # Say Hello, get our unique name
@@ -139,12 +136,6 @@
         hello_reply = self.bus_proxy.Hello()
         self.unique_name = hello_reply[0]
 
-    @property
-    def router(self):
-        warn("conn.router is deprecated, see the docs for APIs to use 
instead.",
-             stacklevel=2)
-        return self._router
-
     def send(self, message: Message, serial=None):
         """Serialise and send a :class:`~.Message` object"""
         data, fds = self._serialise(message, serial)
@@ -170,11 +161,10 @@
         See :meth:`filter`. Returns nothing.
         """
         msg = self.receive(timeout=timeout)
-        self._router.incoming(msg)
         for filter in self._filters.matches(msg):
             filter.queue.append(msg)
 
-    def send_and_get_reply(self, message, *, timeout=None, unwrap=None):
+    def send_and_get_reply(self, message, *, timeout=None):
         """Send a message, wait for the reply and return it
 
         Filters are applied to other messages received before the reply -
@@ -183,24 +173,15 @@
         check_replyable(message)
         deadline = timeout_to_deadline(timeout)
 
-        if unwrap is None:
-            unwrap = False
-        else:
-            warn("Passing unwrap= to .send_and_get_reply() is deprecated and "
-                 "will break in a future version of Jeepney.", stacklevel=2)
-
         serial = next(self.outgoing_serial)
         self.send_message(message, serial=serial)
         while True:
             msg_in = self.receive(timeout=deadline_to_timeout(deadline))
             reply_to = msg_in.header.fields.get(HeaderFields.reply_serial, -1)
             if reply_to == serial:
-                if unwrap:
-                    return unwrap_msg(msg_in)
                 return msg_in
 
             # Not the reply
-            self._router.incoming(msg_in)
             for filter in self._filters.matches(msg_in):
                 filter.queue.append(msg_in)
 
@@ -308,7 +289,13 @@
 
     try:
         with_sock_deadline(sock.connect, addr)
-        authr = Authenticator(enable_fds=enable_fds)
+        authr = Authenticator(enable_fds=enable_fds, inc_null_byte=False)
+        if hasattr(socket, 'SCM_CREDS'):
+            # BSD: send credentials message to authenticate (kernel fills in 
data)
+            sock.sendmsg([b'\0'], [(socket.SOL_SOCKET, socket.SCM_CREDS, 
bytes(512))])
+        else:
+            # Linux: no ancillary data needed, bus checks with SO_PEERCRED
+            sock.send(b'\0')
         for req_data in authr:
             with_sock_deadline(sock.sendall, req_data)
             authr.feed(unwrap_read(with_sock_deadline(sock.recv, 1024)))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/io/tests/test_asyncio.py 
new/jeepney-0.9.0/jeepney/io/tests/test_asyncio.py
--- old/jeepney-0.8.0/jeepney/io/tests/test_asyncio.py  2022-04-03 
19:31:55.797912400 +0200
+++ new/jeepney-0.9.0/jeepney/io/tests/test_asyncio.py  2025-02-27 
19:48:26.866029000 +0100
@@ -1,6 +1,10 @@
 import asyncio
+import sys
 
-import async_timeout
+if sys.version_info >= (3, 11):
+    from asyncio import timeout
+else:
+    from async_timeout import timeout
 import pytest
 import pytest_asyncio
 
@@ -85,7 +89,7 @@
     conn = await open_dbus_connection(bus='SESSION')
     try:
         with pytest.raises(asyncio.TimeoutError):
-            async with async_timeout.timeout(0):
+            async with timeout(0):
                 await conn.receive()
     finally:
         await conn.close()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/io/tests/test_blocking.py 
new/jeepney-0.9.0/jeepney/io/tests/test_blocking.py
--- old/jeepney-0.8.0/jeepney/io/tests/test_blocking.py 2022-04-03 
19:31:55.797912400 +0200
+++ new/jeepney-0.9.0/jeepney/io/tests/test_blocking.py 2025-02-27 
19:48:26.866029000 +0100
@@ -30,10 +30,6 @@
     assert reply.header.message_type == MessageType.method_return
     assert reply.body == ()
 
-    ping_call = new_method_call(bus_peer, 'Ping')
-    reply_body = session_conn.send_and_get_reply(ping_call, timeout=5, 
unwrap=True)
-    assert reply_body == ()
-
 def test_proxy(session_conn):
     proxy = Proxy(message_bus, session_conn, timeout=5)
     name = "io.gitlab.takluyver.jeepney.examples.Server"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/io/trio.py 
new/jeepney-0.9.0/jeepney/io/trio.py
--- old/jeepney-0.8.0/jeepney/io/trio.py        2021-07-10 17:19:49.193029600 
+0200
+++ new/jeepney-0.9.0/jeepney/io/trio.py        2025-02-27 19:48:26.866029000 
+0100
@@ -1,15 +1,11 @@
 import array
-from contextlib import contextmanager
 import errno
-from itertools import count
 import logging
+import socket
+from contextlib import asynccontextmanager, contextmanager
+from itertools import count
 from typing import Optional
 
-try:
-    from contextlib import asynccontextmanager  # Python 3.7
-except ImportError:
-    from async_generator import asynccontextmanager  # Backport for Python 3.6
-
 from outcome import Value, Error
 import trio
 from trio.abc import Channel
@@ -195,7 +191,15 @@
     sock : trio.SocketStream = await trio.open_unix_socket(bus_addr)
 
     # Authentication
-    authr = Authenticator(enable_fds=enable_fds)
+    authr = Authenticator(enable_fds=enable_fds, inc_null_byte=False)
+    if hasattr(socket, 'SCM_CREDS'):
+        # BSD: send credentials message to authenticate (kernel fills in data)
+        await sock.socket.sendmsg(
+            [b'\0'], [(socket.SOL_SOCKET, socket.SCM_CREDS, bytes(512))]
+        )
+    else:
+        # Linux: no ancillary data needed, bus checks with SO_PEERCRED
+        await sock.send_all(b'\0')
     for req_data in authr:
         await sock.send_all(req_data)
         authr.feed(await sock.receive_some())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/low_level.py 
new/jeepney-0.9.0/jeepney/low_level.py
--- old/jeepney-0.8.0/jeepney/low_level.py      2021-07-10 17:19:49.193029600 
+0200
+++ new/jeepney-0.9.0/jeepney/low_level.py      2025-02-27 19:48:26.867029200 
+0100
@@ -1,6 +1,7 @@
+import string
+import struct
 from collections import deque
 from enum import Enum, IntEnum, IntFlag
-import struct
 from typing import Optional
 
 class SizeLimitError(ValueError):
@@ -156,9 +157,12 @@
         assert buf[end:end + 1] == b'\0'
         return val, end + 1
 
-    def serialise(self, data, pos, endianness, fds=None):
+    def check_data(self, data):
         if not isinstance(data, str):
             raise TypeError("Expected str, not {!r}".format(data))
+
+    def serialise(self, data, pos, endianness, fds=None):
+        self.check_data(data)
         encoded = data.encode('utf-8')
         len_data = self.length_type.serialise(len(encoded), pos, endianness)
         return len_data + encoded + b'\0'
@@ -171,9 +175,28 @@
                and (self.length_type == other.length_type)
 
 
+class ObjectPathType(StringType):
+    def __init__(self):
+        super().__init__(simple_types['u'])
+
+    def check_data(self, data):
+        super().check_data(data)
+        if not data.startswith('/'):
+            raise ValueError(f"Object path ({data!r}) must start with /")
+        if data.endswith('/') and len(data) > 1:
+            raise ValueError(f"Object path ({data!r}) cannot end with /")
+        if '//' in data:
+            raise ValueError(f"Object path ({data!r}) cannot contain double /")
+        valid_chars = string.ascii_letters + string.digits + '/_'
+        if any(c not in valid_chars for c in data):
+            raise ValueError(
+                f"Object path ({data!r}) can only contain A-Z, a-z, 0-9, / and 
_"
+            )
+
+
 simple_types.update({
     's': StringType(simple_types['u']),  # String
-    'o': StringType(simple_types['u']),  # Object path
+    'o': ObjectPathType(),               # Object path
     'g': StringType(simple_types['y']),  # Signature
 })
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/routing.py 
new/jeepney-0.9.0/jeepney/routing.py
--- old/jeepney-0.8.0/jeepney/routing.py        2021-07-10 17:28:19.764039500 
+0200
+++ new/jeepney-0.9.0/jeepney/routing.py        1970-01-01 01:00:00.000000000 
+0100
@@ -1,76 +0,0 @@
-from warnings import warn
-
-from .low_level import MessageType, HeaderFields
-from .wrappers import DBusErrorResponse
-
-class Router:
-    """Routing for messages coming back to a client application.
-    
-    :param handle_factory: Constructor for an object like asyncio.Future,
-        with methods *set_result* and *set_exception*. Outgoing method call
-        messages will get a handle associated with them.
-    :param on_unhandled: Callback for messages not otherwise dispatched.
-    """
-    def __init__(self, handle_factory, on_unhandled=None):
-        self.handle_factory = handle_factory
-        self._on_unhandled = on_unhandled
-        self.outgoing_serial = 0
-        self.awaiting_reply = {}
-        self.signal_callbacks = {}
-
-    @property
-    def on_unhandled(self):
-        return self._on_unhandled
-
-    @on_unhandled.setter
-    def on_unhandled(self, value):
-        warn("Setting on_unhandled is deprecated. Please use the filter() "
-             "method or simple receive() calls instead.", stacklevel=2)
-        self._on_unhandled = value
-
-    def outgoing(self, msg):
-        """Set the serial number in the message & make a handle if a method 
call
-        """
-        self.outgoing_serial += 1
-        msg.header.serial = self.outgoing_serial
-
-        if msg.header.message_type is MessageType.method_call:
-            self.awaiting_reply[msg.header.serial] = handle = 
self.handle_factory()
-            return handle
-
-    def subscribe_signal(self, callback, path, interface, member):
-        """Add a callback for a signal.
-        """
-        warn("The subscribe_signal() method is deprecated. "
-             "Please use the filter() API instead.", stacklevel=2)
-        self.signal_callbacks[(path, interface, member)] = callback
-
-    def incoming(self, msg):
-        """Route an incoming message.
-        """
-        hdr = msg.header
-
-        # Signals:
-        if hdr.message_type is MessageType.signal:
-            key = (hdr.fields.get(HeaderFields.path, None),
-                   hdr.fields.get(HeaderFields.interface, None),
-                   hdr.fields.get(HeaderFields.member, None)
-                  )
-            cb = self.signal_callbacks.get(key, None)
-            if cb is not None:
-                cb(msg.body)
-                return
-
-        # Method returns & errors
-        reply_serial = hdr.fields.get(HeaderFields.reply_serial, -1)
-        reply_handle = self.awaiting_reply.pop(reply_serial, None)
-        if reply_handle is not None:
-            if hdr.message_type is MessageType.method_return:
-                reply_handle.set_result(msg.body)
-                return
-            elif hdr.message_type is MessageType.error:
-                reply_handle.set_exception(DBusErrorResponse(msg))
-                return
-
-        if self.on_unhandled:
-            self.on_unhandled(msg)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/tests/test_bus_messages.py 
new/jeepney-0.9.0/jeepney/tests/test_bus_messages.py
--- old/jeepney-0.8.0/jeepney/tests/test_bus_messages.py        2021-07-10 
17:19:49.194029600 +0200
+++ new/jeepney-0.9.0/jeepney/tests/test_bus_messages.py        2025-02-27 
19:48:26.867029200 +0100
@@ -27,6 +27,9 @@
     assert MatchRule(path_namespace='/org/freedesktop/portal').matches(
         new_signal(portal_req_iface, 'Response')
     )
+    assert "/freedesktop/" in (
+        MatchRule(path_namespace='/org/freedesktop/portal').serialise()
+    )
 
     # Prefix but not a parent in the path hierarchy
     assert not MatchRule(path_namespace='/org/freedesktop/por').matches(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/tests/test_low_level.py 
new/jeepney-0.9.0/jeepney/tests/test_low_level.py
--- old/jeepney-0.8.0/jeepney/tests/test_low_level.py   2018-09-11 
21:07:32.036692000 +0200
+++ new/jeepney-0.9.0/jeepney/tests/test_low_level.py   2025-02-27 
19:48:26.867029200 +0100
@@ -85,3 +85,17 @@
     a.serialise(fake_list(100), 0, Endianness.little)
     with pytest.raises(SizeLimitError):
         a.serialise(fake_list(2**23 + 1), 0, Endianness.little)
+
+
+def test_bad_object_path():
+    with pytest.raises(ValueError):
+        ObjectPathType().check_data('org/freedesktop/DBus')
+
+    with pytest.raises(ValueError):
+        ObjectPathType().check_data('/org/freedesktop/DBus/')
+
+    with pytest.raises(ValueError):
+        ObjectPathType().check_data('/org//freedesktop/DBus')
+
+    with pytest.raises(ValueError):
+        ObjectPathType().check_data('/org/freedesktop/DBüs')  # Non-ASCII 
character
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/tests/test_routing.py 
new/jeepney-0.9.0/jeepney/tests/test_routing.py
--- old/jeepney-0.8.0/jeepney/tests/test_routing.py     2017-05-29 
13:45:52.322167000 +0200
+++ new/jeepney-0.9.0/jeepney/tests/test_routing.py     1970-01-01 
01:00:00.000000000 +0100
@@ -1,32 +0,0 @@
-from asyncio import Future
-import pytest
-
-from jeepney.routing import Router
-from jeepney.wrappers import new_method_return, new_error, DBusErrorResponse
-from jeepney.bus_messages import message_bus
-
-def test_message_reply():
-    router = Router(Future)
-    call = message_bus.Hello()
-    future = router.outgoing(call)
-    router.incoming(new_method_return(call, 's', ('test',)))
-    assert future.result() == ('test',)
-
-def test_error():
-    router = Router(Future)
-    call = message_bus.Hello()
-    future = router.outgoing(call)
-    router.incoming(new_error(call, 'TestError', 'u', (31,)))
-    with pytest.raises(DBusErrorResponse) as e:
-        future.result()
-    assert e.value.name == 'TestError'
-    assert e.value.data == (31,)
-
-def test_unhandled():
-    unhandled = []
-    router = Router(Future, on_unhandled=unhandled.append)
-    msg = message_bus.Hello()
-    router.incoming(msg)
-    assert len(unhandled) == 1
-    assert unhandled[0] == msg
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/tests/test_wrappers.py 
new/jeepney-0.9.0/jeepney/tests/test_wrappers.py
--- old/jeepney-0.8.0/jeepney/tests/test_wrappers.py    1970-01-01 
01:00:00.000000000 +0100
+++ new/jeepney-0.9.0/jeepney/tests/test_wrappers.py    2025-02-27 
19:48:26.867029200 +0100
@@ -0,0 +1,74 @@
+import pytest
+
+from jeepney.wrappers import *
+
+def test_bad_bus_name():
+    obj = '/com/example/foo'
+    DBusAddress(obj, 'com.example.a')  # Valid (well known name)
+    DBusAddress(obj, 'com.example.a-b')  # Valid but discouraged
+    DBusAddress(obj, ':1.13')  # Valid (unique name)
+
+    with pytest.raises(ValueError, match='too long'):
+        DBusAddress(obj, 'com.example.' + ('a' * 256))
+
+    with pytest.raises(ValueError):
+        DBusAddress(obj, '.com.example.a')
+
+    with pytest.raises(ValueError):
+        DBusAddress(obj, 'com..example.a')
+
+    with pytest.raises(ValueError):
+        DBusAddress(obj, 'com.2example.a')
+
+    with pytest.raises(ValueError):
+        DBusAddress(obj, 'cöm.example.a')  # Non-ASCII character
+
+    with pytest.raises(ValueError):
+        DBusAddress(obj, 'com')
+
+def test_bad_interface():
+    obj = '/com/example/foo'
+    busname = 'com.example.foo'
+    DBusAddress(obj, 'com.example.a', 'com.example.a_b')  # Valid
+
+    with pytest.raises(ValueError, match='too long'):
+        DBusAddress(obj, 'com.example.a', 'com.example.' + ('a' * 256))
+
+    with pytest.raises(ValueError):
+        DBusAddress(obj, 'com.example.a', 'com.example.a-b')  # No hyphens
+
+    with pytest.raises(ValueError):
+        DBusAddress(obj, busname, '.com.example.a')
+
+    with pytest.raises(ValueError):
+        DBusAddress(obj, busname, 'com..example.a')
+
+    with pytest.raises(ValueError):
+        DBusAddress(obj, busname, 'com.2example.a')
+
+    with pytest.raises(ValueError):
+        DBusAddress(obj, busname, 'cöm.example.a')  # Non-ASCII character
+
+    with pytest.raises(ValueError):
+        DBusAddress(obj, busname, 'com')
+
+
+def test_bad_member_name():
+    addr = DBusAddress(
+        '/org/freedesktop/DBus',
+        bus_name='org.freedesktop.DBus',
+        interface='org.freedesktop.DBus',
+    )
+    new_method_call(addr, 'Hello')
+
+    with pytest.raises(ValueError, match='too long'):
+        new_method_call(addr, 'Hell' + ('o' * 256))
+
+    with pytest.raises(ValueError):
+        new_method_call(addr, 'org.Hello')
+
+    with pytest.raises(ValueError):
+        new_method_call(addr, '9Hello')
+
+    with pytest.raises(ValueError):
+        new_method_call(addr, '')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/jeepney/wrappers.py 
new/jeepney-0.9.0/jeepney/wrappers.py
--- old/jeepney-0.8.0/jeepney/wrappers.py       2021-07-10 17:19:49.194029600 
+0200
+++ new/jeepney-0.9.0/jeepney/wrappers.py       2025-02-27 19:48:26.867029200 
+0100
@@ -1,3 +1,4 @@
+import re
 from typing import Union
 from warnings import warn
 
@@ -15,6 +16,37 @@
     'DBusErrorResponse',
 ]
 
+bus_name_pat = re.compile(
+    r'([A-Za-z_-][A-Za-z0-9_-]*(\.[A-Za-z_-][A-Za-z0-9_-]*)+'  # Well known 
name
+    r'|:[A-Za-z0-9_-]+(\.[A-Za-z0-9_-]+))$',  # Unique name
+)
+
+def check_bus_name(name):
+    if len(name) > 255:
+        abbr = name[:8] + '...'
+        raise ValueError(f"Bus name ({abbr!r}) is too long (> 255 characters)")
+    if not bus_name_pat.match(name):
+        raise ValueError(f"Bus name ({name!r}) is not valid")
+
+interface_pat = 
re.compile(r'[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)+$')
+
+def check_interface(name):
+    if len(name) > 255:
+        abbr = name[:8] + '...'
+        raise ValueError(f"Interface name ({abbr!r}) is too long (> 255 
characters)")
+    if not interface_pat.match(name):
+        raise ValueError(f"Interface name ({name!r}) is not valid")
+
+member_name_pat = re.compile(r'[A-Za-z_][A-Za-z0-9_]*$')
+
+def check_member_name(name):
+    if len(name) > 255:
+        abbr = name[:8] + '...'
+        raise ValueError(f"Member name ({abbr!r}) is too long (> 255 
characters)")
+    if not member_name_pat.match(name):
+        raise ValueError(f"Member name ({name!r} is not valid")
+
+
 class DBusAddress:
     """This identifies the object and interface a message is for.
 
@@ -25,8 +57,15 @@
                     interface='org.freedesktop.Notifications')
     """
     def __init__(self, object_path, bus_name=None, interface=None):
+        ObjectPathType().check_data(object_path)
         self.object_path = object_path
+
+        if bus_name is not None:
+            check_bus_name(bus_name)
         self.bus_name = bus_name
+
+        if interface is not None:
+            check_interface(interface)
         self.interface = interface
 
     def __repr__(self):
@@ -34,6 +73,7 @@
                     self.object_path, self.bus_name, self.interface)
 
     def with_interface(self, interface):
+        check_interface(interface)
         return type(self)(self.object_path, self.bus_name, interface)
 
 class DBusObject(DBusAddress):
@@ -57,6 +97,7 @@
     :param str signature: The DBus signature of the body data
     :param tuple body: Body data (i.e. method parameters)
     """
+    check_member_name(method)
     header = new_header(MessageType.method_call)
     header.fields[HeaderFields.path] = remote_obj.object_path
     if remote_obj.bus_name is None:
@@ -112,6 +153,7 @@
     :param str signature: The DBus signature of the body data
     :param tuple body: Body data
     """
+    check_member_name(signal)
     header = new_header(MessageType.signal)
     header.fields[HeaderFields.path] = emitter.object_path
     if emitter.interface is None:
@@ -128,7 +170,14 @@
     
     jeepney.bindgen can automatically create subclasses using introspection.
     """
+    interface: Optional[str] = None
+
     def __init__(self, object_path, bus_name):
+        ObjectPathType().check_data(object_path)
+        check_bus_name(bus_name)
+        if self.interface is not None:
+            check_interface(self.interface)
+
         self.object_path = object_path
         self.bus_name = bus_name
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jeepney-0.8.0/pyproject.toml 
new/jeepney-0.9.0/pyproject.toml
--- old/jeepney-0.8.0/pyproject.toml    2022-04-03 19:31:55.797912400 +0200
+++ new/jeepney-0.9.0/pyproject.toml    2025-02-27 19:48:26.867029200 +0100
@@ -1,34 +1,39 @@
 [build-system]
-requires = ["flit_core >=2,<4"]
+requires = ["flit_core >=3.11,<4"]
 build-backend = "flit_core.buildapi"
 
-[tool.flit.metadata]
-module = "jeepney"
-author = "Thomas Kluyver"
-author-email = "tho...@kluyver.me.uk"
-home-page = "https://gitlab.com/takluyver/jeepney";
-description-file = "README.rst"
+[project]
+name = "jeepney"
+authors = [
+    {name = "Thomas Kluyver", email = "tho...@kluyver.me.uk"},
+]
+readme = "README.rst"
 requires-python = ">=3.7"
+license = "MIT"
+license-files = ["LICENSE"]
 classifiers = [
-    "License :: OSI Approved :: MIT License",
     "Programming Language :: Python :: 3",
     "Topic :: Desktop Environment"
 ]
+dynamic = ["version", "description"]
 
-[tool.flit.metadata.requires-extra]
+[project.optional-dependencies]
 test = [
     "pytest",
     "pytest-trio",
     "pytest-asyncio >=0.17",
     "testpath",
     "trio",
-    "async-timeout",
+    "async-timeout; python_version < '3.11'",
 ]
 trio = [
     "trio",
-    "async_generator; python_version == '3.6'",
 ]
 
-[tool.flit.metadata.urls]
+[project.urls]
 Documentation = "https://jeepney.readthedocs.io/en/latest/";
+Source = "https://gitlab.com/takluyver/jeepney";
 
+[tool.flit.sdist]
+include = ["docs", "examples", "pytest.ini"]
+exclude = ["docs/_build"]

Reply via email to