/
niceSOFT
/
python3-attrs
Обзор
Документация
Войти
/
niceSOFT
/
python3-attrs
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/attr/_compat.py
127 строк
3 KB
Hynek Schlawack
Drop Python 3.9 (#1603)
02 авг 2026, 12:06
Не верифицирован
02 авг 2026, 12:06
9b98a73
Код
Авторство
О чём код?
# SPDX-License-Identifier: MIT import sys import threading from collections.abc import Mapping, Sequence # noqa: F401 from typing import Callable, _GenericAlias PYPY = sys.implementation.name == "pypy" PY_3_11_PLUS = sys.version_info[:2] >= (3, 11) PY_3_12_PLUS = sys.version_info[:2] >= (3, 12) PY_3_13_PLUS = sys.version_info[:2] >= (3, 13) PY_3_14_PLUS = sys.version_info[:2] >= (3, 14) if PY_3_14_PLUS: # We request forward-ref annotations to not break in the presence of # forward references. def _get_annotations(cls): import annotationlib return annotationlib.get_annotations( cls, format=annotationlib.Format.FORWARDREF ) else: def _get_annotations(cls): """ Get annotations for *cls*. """ return cls.__dict__.get("__annotations__", {}) # noqa: RUF063 class _AnnotationExtractor: """ Extract type annotations from a callable, returning None whenever there is none. """ __slots__ = ["sig"] def __init__(self, callable): import inspect try: self.sig = inspect.signature(callable) except (ValueError, TypeError): # inspect failed self.sig = None def get_first_param_type(self): """ Return the type annotation of the first argument if it's not empty. """ import inspect if not self.sig: return None params = list(self.sig.parameters.values()) if params and params[0].annotation is not inspect.Parameter.empty: return params[0].annotation return None def get_return_type(self): """ Return the return type if it's not empty. """ import inspect if ( self.sig and self.sig.return_annotation is not inspect.Signature.empty ): return self.sig.return_annotation return None # Thread-local global to track attrs instances which are already being repr'd. # This is needed because there is no other (thread-safe) way to pass info # about the instances that are already being repr'd through the call stack # in order to ensure we don't perform infinite recursion. # # For instance, if an instance contains a dict which contains that instance, # we need to know that we're already repr'ing the outside instance from within # the dict's repr() call. # # This lives here rather than in _make.py so that the functions in _make.py # don't have a direct reference to the thread-local in their globals dict. # If they have such a reference, it breaks cloudpickle. repr_context = threading.local() def get_generic_base(cl): """If this is a generic class (A[str]), return the generic base for it.""" if cl.__class__ is _GenericAlias: return cl.__origin__ return None _IS_GENERATOR_RESULTS = {} def _lazy_is_generator(f: Callable) -> Callable[[], bool]: """ Return a caching closure over callable f that returns whether f is a generator function. Not thread-safe but doesn't matter. """ def is_gen() -> bool: import inspect try: if f not in _IS_GENERATOR_RESULTS: _IS_GENERATOR_RESULTS[f] = inspect.isgeneratorfunction(f) except TypeError: # f is not hashable return inspect.isgeneratorfunction(f) return _IS_GENERATOR_RESULTS[f] return is_gen