Skip to content

fix(compat): restore __class_getitem__ on StreamHandler and LoggerAdapter - #10

Closed
coseto6125 wants to merge 1 commit into
Indosaram:mainfrom
coseto6125:fix/generic-alias-compat
Closed

coseto6125 wants to merge 1 commit into
Indosaram:mainfrom
coseto6125:fix/generic-alias-compat

Conversation

@coseto6125

Copy link
Copy Markdown
Contributor

Fixes #9.

What

cpython's Lib/logging/__init__.py makes two classes generic:

logging/__init__.py:1187  class StreamHandler ...  __class_getitem__ = classmethod(GenericAlias)
logging/__init__.py:1995  class LoggerAdapter ...  __class_getitem__ = classmethod(GenericAlias)

logxide's replacements did not carry those lines. Since logxide swaps sys.modules["logging"], any third-party library that subscripts either class at runtime gets logxide's non-generic class and raises TypeError.

Why it matters now

websockets 17.0 removed the TYPE_CHECKING guard around that subscript, so it is evaluated at import time:

# 16.0 — runtime never evaluates it
if TYPE_CHECKING:
    LoggerLike = logging.Logger | logging.LoggerAdapter[Any]
else:  # remove this branch when dropping support for Python < 3.11
    LoggerLike = logging.Logger | logging.LoggerAdapter

# 17.0 — evaluated on import
LoggerLike = logging.Logger | logging.LoggerAdapter[Any]
import logxide
import websockets.typing
TypeError: type 'LoggerAdapter' is not subscriptable

Sanic imports websockets, so every Sanic entry point in a project using logxide fails to start. We hit this on a production service and are currently carrying a local monkeypatch.

Scope

Logger is deliberately left alone — stdlib's Logger is not subscriptable either, so logxide already matches there. test_logger_is_not_subscriptable_like_stdlib pins that, so the fix cannot silently overreach.

Verification

logxide 0.2.2 + websockets 17.0.1 on Python 3.14.6, patch applied to the installed package:

before:  TypeError: type 'LoggerAdapter' is not subscriptable
after:   import OK -> PyLogger | logxide.compat_handlers.LoggerAdapter[typing.Any]

New tests, run against the installed package:

with the fix:     4 passed
without the fix:  3 failed, 1 passed

The one that passes either way is the Logger-stays-non-generic guard, which is the intended behavior.

I could not run cargo test / the full pytest tests/ locally — no Rust toolchain on this machine, so the extension module could not be built. The change is pure Python and touches no Rust, but CI should confirm the rest of the suite.

Related

#8 reports AttributeError: module 'logging' has no attribute 'RootLogger' breaking joblib/cloudpickle. Same shape, different attribute — the replacement module does not reproduce everything stdlib exposes.

Comparing the two surfaces on 3.14.6, 23 public names resolve on stdlib logging but raise AttributeError under logxide. About half are incidental re-exports (os, re, sys, time, threading, traceback, warnings, weakref, collections, io, atexit, Template, GenericAlias); the rest are real logging API:

BufferingFormatter, Filterer, Manager, PlaceHolder, RootLogger, currentframe,
logAsyncioTasks, logMultiprocessing, logProcesses, logThreads

A parity test that diffs the two surfaces would catch this whole class of bug up front. Happy to send that as a separate PR if you want it — I left it out here to keep this one reviewable.

…pter

cpython's Lib/logging/__init__.py makes both classes generic:

    logging/__init__.py:1187  class StreamHandler ...
        __class_getitem__ = classmethod(GenericAlias)
    logging/__init__.py:1995  class LoggerAdapter ...
        __class_getitem__ = classmethod(GenericAlias)

logxide's replacements did not carry those lines, so once
sys.modules["logging"] is swapped, `logging.LoggerAdapter[...]` raises
TypeError. Any third-party library that subscripts either class at
runtime breaks.

websockets 17.0 does exactly that. 16.0 kept the subscript behind a
TYPE_CHECKING guard with a runtime fallback; 17.0 dropped the fallback
and evaluates `logging.LoggerAdapter[Any]` at module import:

    import logxide
    import websockets.typing
    TypeError: type 'LoggerAdapter' is not subscriptable

Sanic imports websockets, so every Sanic entry point in a project using
logxide fails to start.

Logger is deliberately left alone — stdlib's Logger is not subscriptable
either, and a test pins that so the fix does not overreach.

Closes Indosaram#9
@Indosaram

Copy link
Copy Markdown
Owner

Merged into main via commit 2846611 (conflicts in CHANGELOG.md and compat_handlers.py resolved against v0.3.0). Thank you @coseto6125!

@Indosaram Indosaram closed this Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LoggerAdapter and StreamHandler are not subscriptable, breaking websockets>=17 at import

2 participants