Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-logging-journald for
openSUSE:Factory checked in at 2026-10-26 10:00:47
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-logging-journald (Old)
and /work/SRC/openSUSE:Factory/.python-logging-journald.new.2252680 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-logging-journald"
Mon Oct 26 10:00:47 2026 rev:7 rq:1392448 version:0.6.12
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-logging-journald/python-logging-journald.changes
2025-05-05 22:59:27.572197511 +0200
+++
/work/SRC/openSUSE:Factory/.python-logging-journald.new.2252680/python-logging-journald.changes
2026-10-26 10:02:01.310795690 +0100
@@ -1,0 +2,7 @@
+Sun Oct 25 15:02:04 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 0.6.12:
+ * Namespace support
+ * Modernize library
+
+-------------------------------------------------------------------
Old:
----
logging_journald-0.6.11.tar.gz
New:
----
logging_journald-0.6.12.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-logging-journald.spec ++++++
--- /var/tmp/diff_new_pack.rb7WaQ/_old 2026-10-26 10:02:02.142830583 +0100
+++ /var/tmp/diff_new_pack.rb7WaQ/_new 2026-10-26 10:02:02.143830625 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-logging-journald
#
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,14 +18,14 @@
%{?sle15allpythons}
Name: python-logging-journald
-Version: 0.6.11
+Version: 0.6.12
Release: 0
Summary: Logging Handler For Sending Logs to journald
License: MIT
URL: https://github.com/mosquito/logging-journald
Source:
https://files.pythonhosted.org/packages/source/l/logging_journald/logging_journald-%{version}.tar.gz
+BuildRequires: %{python_module hatchling}
BuildRequires: %{python_module pip}
-BuildRequires: %{python_module poetry-core}
BuildRequires: %{python_module setuptools}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
++++++ logging_journald-0.6.11.tar.gz -> logging_journald-0.6.12.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/logging_journald-0.6.11/.gitignore
new/logging_journald-0.6.12/.gitignore
--- old/logging_journald-0.6.11/.gitignore 1970-01-01 01:00:00.000000000
+0100
+++ new/logging_journald-0.6.12/.gitignore 2020-02-02 01:00:00.000000000
+0100
@@ -0,0 +1,169 @@
+# Created by .ignore support plugin (hsz.mobi)
+### VirtualEnv template
+# Virtualenv
+# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
+.Python
+[Bb]in
+[Ii]nclude
+[Ll]ib
+[Ll]ib64
+[Ll]ocal
+[Ss]cripts
+pyvenv.cfg
+.venv
+pip-selfcheck.json
+### IPythonNotebook template
+# Temporary data
+.ipynb_checkpoints/
+### Python template
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+docs/source/apidoc
+
+# PyBuilder
+target/
+
+# IPython Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# dotenv
+.env
+
+# virtualenv
+venv/
+ENV/
+
+# Spyder project settings
+.spyderproject
+
+# Rope project settings
+.ropeproject
+### JetBrains template
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm,
CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff:
+.idea/
+
+## File-based project format:
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+/out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+/htmlcov
+/temp
+.DS_Store
+
+
+.pytest_cache
+
+# Created by https://www.gitignore.io/api/vim
+# Edit at https://www.gitignore.io/?templates=vim
+
+### Vim ###
+# Swap
+[._]*.s[a-v][a-z]
+[._]*.sw[a-p]
+[._]s[a-rt-v][a-z]
+[._]ss[a-gi-z]
+[._]sw[a-p]
+
+# Session
+Session.vim
+
+# Temporary
+.netrwhist
+*~
+# Auto-generated tag files
+tags
+# Persistent undo
+[._]*.un~
+
+# End of https://www.gitignore.io/api/vim
+.mypy_cache
+*.cprof
+/aiomisc/version.py
+scratch*.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/logging_journald-0.6.11/PKG-INFO
new/logging_journald-0.6.12/PKG-INFO
--- old/logging_journald-0.6.11/PKG-INFO 1970-01-01 01:00:00.000000000
+0100
+++ new/logging_journald-0.6.12/PKG-INFO 2020-02-02 01:00:00.000000000
+0100
@@ -1,25 +1,27 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
Name: logging-journald
-Version: 0.6.11
+Version: 0.6.12
Summary: Pure python logging handler for writing logs to the journald using
native protocol
-Home-page: https://github.com/mosquito/logging-journald
-License: MIT
-Author: Dmitry Orlov
-Author-email: [email protected]
-Requires-Python: >=3.8,<4.0
+Project-URL: Homepage, https://github.com/mosquito/logging-journald
+Project-URL: Source, https://github.com/mosquito/logging-journald
+Project-URL: Tracker, https://github.com/mosquito/logging-journald/issues
+Author-email: Dmitry Orlov <[email protected]>
+License-Expression: MIT
+License-File: COPYING
+Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX :: Linux
-Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
-Project-URL: Source, https://github.com/mosquito/logging-journald
-Project-URL: Tracker, https://github.com/mosquito/logging-journald/issues
+Classifier: Typing :: Typed
+Requires-Python: <4,>=3.10
Description-Content-Type: text/markdown
logging-journald
@@ -72,4 +74,4 @@
handler = JournaldLogHandler(use_message_id=False)
...
-```
+```
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/logging_journald-0.6.11/logging_journald.py
new/logging_journald-0.6.12/logging_journald.py
--- old/logging_journald-0.6.11/logging_journald.py 2024-12-13
09:16:04.258108900 +0100
+++ new/logging_journald-0.6.12/logging_journald.py 2020-02-02
01:00:00.000000000 +0100
@@ -1,4 +1,5 @@
import array
+import errno
import fcntl
import logging
import os
@@ -8,11 +9,12 @@
import tempfile
import traceback
import uuid
+from collections.abc import Iterable
from enum import IntEnum, unique
from io import BytesIO
from pathlib import Path
from types import MappingProxyType
-from typing import IO, Any, Iterable, List, Optional, Tuple, Union
+from typing import IO, Any
@unique
@@ -47,16 +49,55 @@
VALUE_LEN_STRUCT = struct.Struct("@Q")
SOCKET_PATH = Path("/run/systemd/journal/socket")
- def __init__(self, socket_path: Union[str, Path] = SOCKET_PATH):
- self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
- self.socket.connect(str(self.SOCKET_PATH))
+ # Sending failed because the socket we hold is no longer the one at the
path, so a
+ # fresh connection is worth trying.
+ RECONNECT_ERRNOS = frozenset({errno.ECONNREFUSED, errno.ENOTCONN,
errno.EPIPE})
+
+ # Whether to connect the socket once, or address the path on every send
the way
+ # libsystemd's sd_journal_sendv() does. Connecting is a little cheaper per
message
+ # and is the default because it is what this has always done. Addressing
per send
+ # survives the socket at that path being replaced, which is what a
socket-activated
+ # journald does every time it has been idle -- see send().
+ CONNECTED = True
+
+ def __init__(
+ self,
+ socket_path: str | Path | None = None,
+ connected: bool | None = None,
+ ):
+ # Resolved here rather than as a default argument value: a default is
bound
+ # when the class is defined, so overriding SOCKET_PATH on the class
(or in a
+ # subclass) would have no effect on it.
+ self.socket_path = Path(socket_path) if socket_path is not None else
self.SOCKET_PATH
+ self.connected = self.CONNECTED if connected is None else connected
+ self.socket = self._connect() if self.connected else self._open()
+
+ # This method is private because it is an internal helper of
__init__/send, not public API
+ def _open(self) -> socket.socket:
+ return socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+
+ # This method is private because it is an internal helper of
__init__/send, not public API
+ def _connect(self) -> socket.socket:
+ sock = self._open()
+ sock.connect(str(self.socket_path))
+ return sock
+
+ # F_ADD_SEALS is exposed by the fcntl module only when it was defined in
the
+ # Linux headers Python was built against; some Python builds (observed with
+ # python-build-standalone on GitHub Actions' ubuntu-latest, for several
+ # versions) have memfd_create but not the sealing constants, so both are
+ # checked rather than assuming one implies the other.
+ if hasattr(os, "memfd_create") and hasattr(fcntl, "F_ADD_SEALS"):
- if hasattr(os, "memfd_create"):
@staticmethod
def memfd_open(*args: Any, **kwargs: Any) -> IO[bytes]:
- """ Return memfd file-like object """
+ """Return memfd file-like object"""
+ # memfd_create never touches the filesystem, so the name is
+ # just a debug label (visible in /proc/self/fd) and doesn't
+ # need tempfile's path-uniqueness guarantees.
fd: int = os.memfd_create(
- tempfile.mktemp(), os.MFD_ALLOW_SEALING,
+ uuid.uuid4().hex,
+ os.MFD_ALLOW_SEALING,
)
return os.fdopen(fd, *args, **kwargs)
@@ -66,13 +107,13 @@
fcntl.fcntl(
fp.fileno(),
fcntl.F_ADD_SEALS,
- fcntl.F_SEAL_SHRINK | fcntl.F_SEAL_GROW |
- fcntl.F_SEAL_WRITE | fcntl.F_SEAL_SEAL,
+ fcntl.F_SEAL_SHRINK | fcntl.F_SEAL_GROW | fcntl.F_SEAL_WRITE |
fcntl.F_SEAL_SEAL,
)
else:
+
@staticmethod
def memfd_open(*args: Any, **kwargs: Any) -> IO[bytes]:
- """ Return python temporary file object """
+ """Return python temporary file object"""
return tempfile.TemporaryFile(*args, **kwargs)
@staticmethod
@@ -81,7 +122,7 @@
@staticmethod
def _encode_short(key: str, value: Any) -> bytes:
- return "{}={}\n".format(key.upper(), value).encode()
+ return f"{key.upper()}={value}\n".encode()
@classmethod
def _encode_long(cls, key: str, value: bytes) -> bytes:
@@ -106,39 +147,67 @@
return
elif isinstance(value, (list, tuple)):
for idx, item in enumerate(value):
- cls.pack(fp, "{}_{}".format(key, idx), item)
+ cls.pack(fp, f"{key}_{idx}", item)
return
elif isinstance(value, dict):
for d_key, d_value in value.items():
- cls.pack(fp, "{}_{}".format(key, d_key), d_value)
+ cls.pack(fp, f"{key}_{d_key}", d_value)
return
cls.pack(fp, key, str(value).encode())
return
- def send(self, pairs: Iterable[Tuple[str, Any]]) -> None:
+ def send(self, pairs: Iterable[tuple[str, Any]]) -> None:
with BytesIO() as fp:
for key, value in pairs:
self.pack(fp, key, value)
value = fp.getvalue()
- # noinspection PyBroadException
try:
- self.socket.sendall(value)
- except OSError:
+ self._send(value)
+ except OSError as e:
+ if not self.connected or e.errno not in self.RECONNECT_ERRNOS:
+ raise
+ # journald's socket unit can be restarted underneath us, which
replaces the
+ # socket file: a socket connected to the old one stays dead, and
every send
+ # after that fails. Reconnect and try once more. Unconnected there
is
+ # nothing to repair -- the path is resolved by each send already
-- so the
+ # error is the caller's to deal with.
+ self.socket.close()
+ self.socket = self._connect()
+ self._send(value)
+
+ def _send(self, value: bytes) -> None:
+ try:
+ if self.connected:
+ self.socket.sendall(value)
+ else:
+ self.socket.sendto(value, str(self.socket_path))
+ except OSError as e:
+ if e.errno != errno.EMSGSIZE:
+ # Anything else -- journald not listening, socket replaced,
permission
+ # denied -- is not something a file descriptor fixes, and
going down
+ # that path only replaces the error with one from the fallback.
+ raise
# the systemd standard way to handle long payloads
with self.memfd_open("wb+") as mfp:
# copy content to memfd
mfp.write(value)
self.memfd_seal(mfp)
- self.socket.sendmsg(
- [], [(socket.SOL_SOCKET, socket.SCM_RIGHTS,
array.array("i", [mfp.fileno()]))],
- )
+ # sendmsg's data buffer must carry at least one (possibly
empty)
+ # element -- an actually empty list raises EMSGSIZE on some
+ # platforms (observed on macOS) even though the ancillary data
+ # is what matters here.
+ ancillary = [(socket.SOL_SOCKET, socket.SCM_RIGHTS,
array.array("i", [mfp.fileno()]))]
+ if self.connected:
+ self.socket.sendmsg([b""], ancillary)
+ else:
+ self.socket.sendmsg([b""], ancillary, 0,
str(self.socket_path))
def check_journal_stream() -> bool:
- """ Returns True if journald is listening on stderr otherwise False """
+ """Returns True if journald is listening on stderr otherwise False"""
journal_stream = os.getenv("JOURNAL_STREAM", "")
if not journal_stream:
@@ -154,51 +223,60 @@
class JournaldLogHandler(logging.Handler):
- LEVELS = MappingProxyType({
- logging.CRITICAL: 2,
- logging.DEBUG: 7,
- logging.FATAL: 0,
- logging.ERROR: 3,
- logging.INFO: 6,
- logging.NOTSET: 16,
- logging.WARNING: 4,
- })
-
- RECORD_FIELDS_MAP = MappingProxyType({
- "args": "arguments",
- "created": None,
- "exc_info": None,
- "exc_text": None,
- "filename": None,
- "funcName": None,
- "levelname": None,
- "levelno": None,
- "lineno": None,
- "message": None,
- "module": None,
- "msecs": None,
- "msg": "message_raw",
- "name": "logger_name",
- "pathname": None,
- "process": "pid",
- "processName": "process_name",
- "relativeCreated": None,
- "thread": "thread_id",
- "threadName": "thread_name",
- })
+ LEVELS = MappingProxyType(
+ {
+ logging.CRITICAL: 2,
+ logging.DEBUG: 7,
+ logging.FATAL: 0,
+ logging.ERROR: 3,
+ logging.INFO: 6,
+ logging.NOTSET: 16,
+ logging.WARNING: 4,
+ }
+ )
+
+ RECORD_FIELDS_MAP = MappingProxyType(
+ {
+ "args": "arguments",
+ "created": None,
+ "exc_info": None,
+ "exc_text": None,
+ "filename": None,
+ "funcName": None,
+ "levelname": None,
+ "levelno": None,
+ "lineno": None,
+ "message": None,
+ "module": None,
+ "msecs": None,
+ "msg": "message_raw",
+ "name": "logger_name",
+ "pathname": None,
+ "process": "pid",
+ "processName": "process_name",
+ "relativeCreated": None,
+ "thread": "thread_id",
+ "threadName": "thread_name",
+ }
+ )
__slots__ = ("_facility", "socket", "_identifier")
SOCKET_PATH = JournaldTransport.SOCKET_PATH
def __init__(
- self, identifier: Optional[str] = None,
+ self,
+ identifier: str | None = None,
facility: int = Facility.LOCAL7,
use_message_id: bool = True,
- socket_path: Union[str, Path] = SOCKET_PATH,
+ socket_path: str | Path | None = None,
):
super().__init__()
- self.transport = JournaldTransport(socket_path=socket_path)
+ # As in JournaldTransport: resolved here so that overriding
SOCKET_PATH on
+ # this class keeps working.
+ self.transport = JournaldTransport(
+ socket_path=socket_path if socket_path is not None else
self.SOCKET_PATH,
+ )
self._identifier = identifier
self._facility = int(facility)
self.use_message_id = use_message_id
@@ -207,13 +285,13 @@
def _to_usec(ts: float) -> int:
return int(ts * 1000000)
- def _format_record(self, record: logging.LogRecord) -> List[Tuple[str,
Any]]:
+ def _format_record(self, record: logging.LogRecord) -> list[tuple[str,
Any]]:
message = self.format(record)
message_traceback = ""
message_level = self.LEVELS[record.levelno]
message_facility = self._facility
message_identifier = self._identifier
- message_code_string = "{}.{}:{}".format(record.module,
record.funcName, record.lineno)
+ message_code_string =
f"{record.module}.{record.funcName}:{record.lineno}"
result = [
("message", message),
@@ -237,9 +315,14 @@
if self.use_message_id:
message_hash = "\0".join(
map(
- str, (
- message, traceback, message_level, message_facility,
- message_identifier, message_code_string,
+ str,
+ (
+ message,
+ traceback,
+ message_level,
+ message_facility,
+ message_identifier,
+ message_code_string,
),
),
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/logging_journald-0.6.11/pyproject.toml
new/logging_journald-0.6.12/pyproject.toml
--- old/logging_journald-0.6.11/pyproject.toml 2024-12-13 09:16:21.325329800
+0100
+++ new/logging_journald-0.6.12/pyproject.toml 2020-02-02 01:00:00.000000000
+0100
@@ -1,63 +1,72 @@
-[tool.poetry]
+[project]
name = "logging-journald"
-version = "0.6.11"
-license = "MIT"
+version = "0.6.12"
description = "Pure python logging handler for writing logs to the journald
using native protocol"
-authors = ["Dmitry Orlov <[email protected]>"]
+authors = [{ name = "Dmitry Orlov", email = "[email protected]" }]
+requires-python = ">=3.10,<4"
readme = "README.md"
-homepage = "https://github.com/mosquito/logging-journald"
+license = "MIT"
classifiers = [
+ "Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
+ "License :: OSI Approved :: MIT License",
"Operating System :: POSIX :: Linux",
- "Programming Language :: Python",
+ "Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
"Programming Language :: Python :: Implementation :: CPython",
+ "Typing :: Typed",
]
-packages = [
- { include = "logging_journald.py" },
+
+[project.urls]
+Homepage = "https://github.com/mosquito/logging-journald"
+Source = "https://github.com/mosquito/logging-journald"
+Tracker = "https://github.com/mosquito/logging-journald/issues"
+
+[dependency-groups]
+dev = [
+ "pytest",
+ "coveralls",
+ "pytest-cov",
+ "ruff",
+ "ty",
]
-[tool.poetry.urls]
-"Source" = "https://github.com/mosquito/logging-journald"
-"Tracker" = "https://github.com/mosquito/logging-journald/issues"
-
-[tool.poetry.dependencies]
-python = "^3.8"
-
-[tool.poetry.group.dev.dependencies]
-pytest = "^7.2.0"
-pytest-subtests = "*"
-aiomisc = "^16.2.10"
-mypy = "^0.991"
-coveralls = "^3.3.1"
-pytest-cov = "^4.0.0"
-pylama = {extras = ["toml"], version = "^8.4.1"}
-setuptools = "^72.1.0"
+[tool.ruff]
+line-length = 119
+extend-exclude = ["README.md"]
+
+[tool.ruff.lint]
+select = ["E", "F", "W", "C90", "UP"]
+ignore = ["C901"]
+
+[tool.ty.environment]
+python-platform = "linux"
+
+[tool.ty.src]
+exclude = ["examples/"]
+
+[tool.coverage.run]
+branch = true
+source = ["logging_journald"]
+
+[tool.coverage.report]
+exclude_lines = [
+ "pragma: no cover",
+ "raise NotImplementedError",
+]
+show_missing = true
-[build-system]
-requires = ["poetry-core"]
-build-backend = "poetry.core.masonry.api"
+[tool.hatch.build.targets.sdist]
+include = ["logging_journald.py"]
-[tool.mypy]
-allow_subclassing_any = true
-allow_untyped_calls = true
-allow_untyped_decorators = true
-ignore_missing_imports = true
-implicit_reexport = true
-pretty = true
-show_error_codes = true
-strict = true
-warn_return_any = false
-files = "logging_journald.py, tests"
-
-[tool.pylama]
-max_line_length = 119
-skip = "*/.venv/*, */.tox/*, */mypy_cache/*, ./dist, ./docs"
+[tool.hatch.build.targets.wheel]
+include = ["logging_journald.py"]
-[tool.pylama.linter.mccabe]
-max-complexity = 15
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"