Skip to main content
Glama

propublica-mcp

threadlocal.py•9.16 kB
# SPDX-License-Identifier: MIT OR Apache-2.0 # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the MIT License. See the LICENSE file in the root of this # repository for complete details. """ **Deprecated** primitives to keep context global but thread (and greenlet) local. See `thread-local`, but please use :doc:`contextvars` instead. .. deprecated:: 22.1.0 """ from __future__ import annotations import contextlib import sys import threading import uuid import warnings from typing import Any, Generator, Iterator, TypeVar import structlog from ._config import BoundLoggerLazyProxy from .typing import BindableLogger, Context, EventDict, WrappedLogger def _determine_threadlocal() -> type[Any]: """ Return a dict-like threadlocal storage depending on whether we run with greenlets or not. """ try: from ._greenlets import GreenThreadLocal except ImportError: from threading import local return local return GreenThreadLocal # pragma: no cover ThreadLocal = _determine_threadlocal() def _deprecated() -> None: """ Raise a warning with best-effort stacklevel adjustment. """ callsite = "" with contextlib.suppress(Exception): f = sys._getframe() callsite = f.f_back.f_back.f_globals[ # type: ignore[union-attr] "__name__" ] # Avoid double warnings if TL functions call themselves. if callsite == "structlog.threadlocal": return stacklevel = 3 # If a function is used as a decorator, we need to add two stack levels. # This logic will probably break eventually, but it's not worth any more # complexity. if callsite == "contextlib": stacklevel += 2 warnings.warn( "`structlog.threadlocal` is deprecated, please use " "`structlog.contextvars` instead.", DeprecationWarning, stacklevel=stacklevel, ) def wrap_dict(dict_class: type[Context]) -> type[Context]: """ Wrap a dict-like class and return the resulting class. The wrapped class and used to keep global in the current thread. Args: dict_class: Class used for keeping context. .. deprecated:: 22.1.0 """ _deprecated() Wrapped = type( "WrappedDict-" + str(uuid.uuid4()), (_ThreadLocalDictWrapper,), {} ) Wrapped._tl = ThreadLocal() # type: ignore[attr-defined] Wrapped._dict_class = dict_class # type: ignore[attr-defined] return Wrapped TLLogger = TypeVar("TLLogger", bound=BindableLogger) def as_immutable(logger: TLLogger) -> TLLogger: """ Extract the context from a thread local logger into an immutable logger. Args: logger (structlog.typing.BindableLogger): A logger with *possibly* thread local state. Returns: :class:`~structlog.BoundLogger` with an immutable context. .. deprecated:: 22.1.0 """ _deprecated() if isinstance(logger, BoundLoggerLazyProxy): logger = logger.bind() try: ctx = logger._context._tl.dict_.__class__( # type: ignore[union-attr] logger._context._dict # type: ignore[union-attr] ) bl = logger.__class__( logger._logger, # type: ignore[attr-defined, call-arg] processors=logger._processors, # type: ignore[attr-defined] context={}, ) bl._context = ctx return bl except AttributeError: return logger @contextlib.contextmanager def tmp_bind( logger: TLLogger, **tmp_values: Any ) -> Generator[TLLogger, None, None]: """ Bind *tmp_values* to *logger* & memorize current state. Rewind afterwards. Only works with `structlog.threadlocal.wrap_dict`-based contexts. Use :func:`~structlog.threadlocal.bound_threadlocal` for new code. .. deprecated:: 22.1.0 """ _deprecated() if isinstance(logger, BoundLoggerLazyProxy): logger = logger.bind() saved = as_immutable(logger)._context try: yield logger.bind(**tmp_values) finally: logger._context.clear() logger._context.update(saved) class _ThreadLocalDictWrapper: """ Wrap a dict-like class and keep the state *global* but *thread-local*. Attempts to re-initialize only updates the wrapped dictionary. Useful for short-lived threaded applications like requests in web app. Use :func:`wrap` to instantiate and use :func:`structlog.BoundLogger.new` to clear the context. """ _tl: Any _dict_class: type[dict[str, Any]] def __init__(self, *args: Any, **kw: Any) -> None: """ We cheat. A context dict gets never recreated. """ if args and isinstance(args[0], self.__class__): # our state is global, no need to look at args[0] if it's of our # class self._dict.update(**kw) else: self._dict.update(*args, **kw) @property def _dict(self) -> Context: """ Return or create and return the current context. """ try: return self.__class__._tl.dict_ except AttributeError: self.__class__._tl.dict_ = self.__class__._dict_class() return self.__class__._tl.dict_ def __repr__(self) -> str: return f"<{self.__class__.__name__}({self._dict!r})>" def __eq__(self, other: object) -> bool: # Same class == same dictionary return self.__class__ == other.__class__ def __ne__(self, other: object) -> bool: return not self.__eq__(other) # Proxy methods necessary for structlog. # Dunder methods don't trigger __getattr__ so we need to proxy by hand. def __iter__(self) -> Iterator[str]: return self._dict.__iter__() def __setitem__(self, key: str, value: Any) -> None: self._dict[key] = value def __delitem__(self, key: str) -> None: self._dict.__delitem__(key) def __len__(self) -> int: return self._dict.__len__() def __getattr__(self, name: str) -> Any: return getattr(self._dict, name) _CONTEXT = threading.local() def get_threadlocal() -> Context: """ Return a copy of the current thread-local context. .. versionadded:: 21.2.0 .. deprecated:: 22.1.0 """ _deprecated() return _get_context().copy() def get_merged_threadlocal(bound_logger: BindableLogger) -> Context: """ Return a copy of the current thread-local context merged with the context from *bound_logger*. .. versionadded:: 21.2.0 .. deprecated:: 22.1.0 """ _deprecated() ctx = _get_context().copy() ctx.update(structlog.get_context(bound_logger)) return ctx def merge_threadlocal( logger: WrappedLogger, method_name: str, event_dict: EventDict ) -> EventDict: """ A processor that merges in a global (thread-local) context. Use this as your first processor in :func:`structlog.configure` to ensure thread-local context is included in all log calls. .. versionadded:: 19.2.0 .. versionchanged:: 20.1.0 This function used to be called ``merge_threadlocal_context`` and that name is still kept around for backward compatibility. .. deprecated:: 22.1.0 """ _deprecated() context = _get_context().copy() context.update(event_dict) return context # Alias that shouldn't be used anymore. merge_threadlocal_context = merge_threadlocal def clear_threadlocal() -> None: """ Clear the thread-local context. The typical use-case for this function is to invoke it early in request-handling code. .. versionadded:: 19.2.0 .. deprecated:: 22.1.0 """ _deprecated() _CONTEXT.context = {} def bind_threadlocal(**kw: Any) -> None: """ Put keys and values into the thread-local context. Use this instead of :func:`~structlog.BoundLogger.bind` when you want some context to be global (thread-local). .. versionadded:: 19.2.0 .. deprecated:: 22.1.0 """ _deprecated() _get_context().update(kw) def unbind_threadlocal(*keys: str) -> None: """ Tries to remove bound *keys* from threadlocal logging context if present. .. versionadded:: 20.1.0 .. deprecated:: 22.1.0 """ _deprecated() context = _get_context() for key in keys: context.pop(key, None) @contextlib.contextmanager def bound_threadlocal(**kw: Any) -> Generator[None, None, None]: """ Bind *kw* to the current thread-local context. Unbind or restore *kw* afterwards. Do **not** affect other keys. Can be used as a context manager or decorator. .. versionadded:: 21.4.0 .. deprecated:: 22.1.0 """ _deprecated() context = get_threadlocal() saved = {k: context[k] for k in context.keys() & kw.keys()} bind_threadlocal(**kw) try: yield finally: unbind_threadlocal(*kw.keys()) bind_threadlocal(**saved) def _get_context() -> Context: try: return _CONTEXT.context except AttributeError: _CONTEXT.context = {} return _CONTEXT.context

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/asachs01/propublica-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server