Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ You can find our backwards-compatibility policy [here](https://github.com/hynek/

## [Unreleased](https://github.com/hynek/structlog/compare/25.5.0...HEAD)

### Removed

- Python 3.8 support.


## [25.5.0](https://github.com/hynek/structlog/compare/25.4.0...25.5.0) - 2025-10-27

Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ dynamic = ["readme", "version"]
name = "structlog"
description = "Structured Logging for Python"
authors = [{ name = "Hynek Schlawack", email = "hs@ox.cx" }]
requires-python = ">=3.8"
requires-python = ">=3.9"
license = "MIT OR Apache-2.0"
keywords = ["logging", "structured", "structure", "log"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"License :: OSI Approved :: Apache Software License",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand Down
3 changes: 2 additions & 1 deletion src/structlog/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@

import sys

from typing import Any, Iterable, Mapping, Sequence
from collections.abc import Iterable, Mapping, Sequence
from typing import Any

from structlog.exceptions import DropEvent

Expand Down
5 changes: 3 additions & 2 deletions src/structlog/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
import sys
import warnings

from typing import Any, Callable, Iterable, Sequence, Type, cast
from collections.abc import Iterable, Sequence
from typing import Any, Callable, cast

from ._native import make_filtering_bound_logger
from ._output import PrintLoggerFactory
Expand Down Expand Up @@ -53,7 +54,7 @@
force_colors=_force_colors,
),
]
_BUILTIN_DEFAULT_CONTEXT_CLASS = cast(Type[Context], dict)
_BUILTIN_DEFAULT_CONTEXT_CLASS = cast(type[Context], dict)
_BUILTIN_DEFAULT_WRAPPER_CLASS = make_filtering_bound_logger(0)
_BUILTIN_DEFAULT_LOGGER_FACTORY = PrintLoggerFactory()
_BUILTIN_CACHE_LOGGER_ON_FIRST_USE = False
Expand Down
3 changes: 2 additions & 1 deletion src/structlog/contextvars.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
import contextlib
import contextvars

from collections.abc import Generator, Mapping
from types import FrameType
from typing import Any, Generator, Mapping
from typing import Any

import structlog

Expand Down
2 changes: 1 addition & 1 deletion src/structlog/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import sys
import warnings

from collections.abc import Sequence
from dataclasses import dataclass
from io import StringIO
from types import ModuleType
Expand All @@ -22,7 +23,6 @@
Callable,
Literal,
Protocol,
Sequence,
TextIO,
cast,
)
Expand Down
3 changes: 1 addition & 2 deletions src/structlog/processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,13 @@
import threading
import time

from collections.abc import Collection, Sequence
from types import FrameType, TracebackType
from typing import (
Any,
Callable,
ClassVar,
Collection,
NamedTuple,
Sequence,
TextIO,
cast,
)
Expand Down
5 changes: 3 additions & 2 deletions src/structlog/stdlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
import sys
import warnings

from collections.abc import Collection, Iterable, Sequence
from functools import partial
from typing import Any, Callable, Collection, Dict, Iterable, Sequence, cast
from typing import Any, Callable, cast


if sys.version_info >= (3, 11):
Expand Down Expand Up @@ -1121,7 +1122,7 @@ def format(self, record: logging.LogRecord) -> str:
# We need to copy because it's possible that the same record gets
# processed by multiple logging formatters. LogRecord.getMessage
# would transform our dict into a str.
ed = cast(Dict[str, Any], record.msg).copy()
ed = cast(dict[str, Any], record.msg).copy()
ed["_record"] = record
ed["_from_structlog"] = True
else:
Expand Down
3 changes: 2 additions & 1 deletion src/structlog/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@

from __future__ import annotations

from collections.abc import Generator, Iterable
from contextlib import contextmanager
from typing import Any, Generator, Iterable, NamedTuple, NoReturn
from typing import Any, NamedTuple, NoReturn

from ._config import configure, get_config
from ._log_levels import map_method_name
Expand Down
3 changes: 2 additions & 1 deletion src/structlog/threadlocal.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
import uuid
import warnings

from typing import Any, Generator, Iterator, TypeVar
from collections.abc import Generator, Iterator
from typing import Any, TypeVar

import structlog

Expand Down
5 changes: 3 additions & 2 deletions src/structlog/tracebacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
import os.path
import sys

from collections.abc import Iterable, Sequence
from dataclasses import asdict, dataclass, field
from traceback import walk_tb
from types import ModuleType, TracebackType
from typing import Any, Iterable, Sequence, Tuple, Union
from typing import Any, Union


try:
Expand Down Expand Up @@ -50,7 +51,7 @@
LOCALS_MAX_STRING = 80
MAX_FRAMES = 50

OptExcInfo = Union[ExcInfo, Tuple[None, None, None]]
OptExcInfo = Union[ExcInfo, tuple[None, None, None]]


@dataclass
Expand Down
3 changes: 2 additions & 1 deletion src/structlog/twisted.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
import json
import sys

from typing import Any, Callable, Sequence, TextIO
from collections.abc import Sequence
from typing import Any, Callable, TextIO

from twisted.python import log
from twisted.python.failure import Failure
Expand Down
12 changes: 4 additions & 8 deletions src/structlog/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,14 @@

import sys

from collections.abc import Mapping, MutableMapping
from types import TracebackType
from typing import (
Any,
Callable,
Dict,
Mapping,
MutableMapping,
Optional,
Protocol,
TextIO,
Tuple,
Type,
Union,
runtime_checkable,
)
Expand All @@ -50,7 +46,7 @@
"""


Context = Union[Dict[str, Any], Dict[Any, Any]]
Context = Union[dict[str, Any], dict[Any, Any]]
"""
A dict-like context carrier.

Expand All @@ -69,7 +65,7 @@
"""

ProcessorReturnValue = Union[
Mapping[str, Any], str, bytes, bytearray, Tuple[Any, ...]
Mapping[str, Any], str, bytes, bytearray, tuple[Any, ...]
]
"""
A value returned by a processor.
Expand All @@ -84,7 +80,7 @@
.. versionadded:: 20.2.0
"""

ExcInfo = Tuple[Type[BaseException], BaseException, Optional[TracebackType]]
ExcInfo = tuple[type[BaseException], BaseException, Optional[TracebackType]]
"""
An exception info tuple as returned by `sys.exc_info`.

Expand Down
9 changes: 4 additions & 5 deletions tests/test_dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,10 @@ def test_initializes_colorama_on_windows_with_force_colors(self):
"""
On Windows with colorama, force_colors=True reinitializes colorama.
"""
with mock.patch.object(
dev.colorama, "init"
) as mock_init, mock.patch.object(
dev.colorama, "deinit"
) as mock_deinit:
with (
mock.patch.object(dev.colorama, "init") as mock_init,
mock.patch.object(dev.colorama, "deinit") as mock_deinit,
):
styles = dev.ConsoleRenderer.get_default_column_styles(
colors=True, force_colors=True
)
Expand Down
7 changes: 3 additions & 4 deletions tests/test_stdlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
import os
import sys

from collections.abc import Collection
from io import StringIO
from typing import Any, Callable, Collection, Dict
from typing import Any, Callable
from unittest.mock import patch

import pytest
Expand Down Expand Up @@ -1409,9 +1410,7 @@ def test_logrecord_exc_info(self):
# handlers will receive LogRecord objects that come from both structlog
# and non-structlog loggers.

records: Dict[ # noqa: UP006 - dict isn't generic until Python 3.9
str, logging.LogRecord
] = {}
records: dict[str, logging.LogRecord] = {}

class DummyHandler(logging.Handler):
def emit(self, record):
Expand Down
8 changes: 5 additions & 3 deletions tests/test_threadlocal.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,11 @@ def test_bind_exc(self, log):
"""
log = log.bind(y=23)

with pytest.raises( # noqa: PT012
CustomError
), pytest.deprecated_call(), tmp_bind(log, x=42, y="foo") as tmp_log:
with ( # noqa: PT012
pytest.raises(CustomError),
pytest.deprecated_call(),
tmp_bind(log, x=42, y="foo") as tmp_log,
):
assert (
{"y": "foo", "x": 42}
== tmp_log._context._dict
Expand Down
8 changes: 4 additions & 4 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
min_version = 4
env_list =
pre-commit,
py3{8,9,10,11,12,13,14}-{tests,mypy}
py3{8,13}-tests-{colorama,be,rich},
py3{9,10,11,12,13,14}-{tests,mypy}
py3{9,13}-tests-{colorama,be,rich},
typing-{mypy,pyright,ty,pyrefly},
docs-{sponsors,build,doctests},
coverage-{combine,report}
Expand All @@ -22,7 +22,7 @@ commands =

# Run oldest and latest under Coverage.
# Keep in-sync with coverage `depends below.
[testenv:py3{8,13}-tests{,-colorama,-be,-rich}]
[testenv:py3{9,13}-tests{,-colorama,-be,-rich}]
deps =
coverage[toml]
py313: twisted
Expand All @@ -37,7 +37,7 @@ commands = coverage run -m pytest {posargs}
# Keep base_python in-sync with .python-version-default
base_python = py313
# Keep in-sync with test env definition above.
depends = py3{8,13}-tests{,-colorama,-be,-rich}
depends = py3{9,13}-tests{,-colorama,-be,-rich}
skip_install = true
deps = coverage
commands = coverage combine
Expand Down