mode-streaming 0.5.0__tar.gz → 0.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/PKG-INFO +1 -1
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/objects.py +98 -42
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode_streaming.egg-info/PKG-INFO +1 -1
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode_streaming.egg-info/scm_version.json +2 -2
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/unit/utils/test_objects.py +56 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/.editorconfig +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/.github/ISSUE_TEMPLATE.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/.github/workflows/deploy-docs.yml +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/.github/workflows/publish.yml +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/.github/workflows/tests.yml +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/.gitignore +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/.pre-commit-config.yaml +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/AUTHORS +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/CHANGELOG.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/CODE_OF_CONDUCT.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/CONTRIBUTING.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/LICENSE +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/README.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/changelog.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/contributing.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/creating-service.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/example-service.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/example-webapp.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/index.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.debug.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.exceptions.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.locals.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.loop.eventlet.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.loop.gevent.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.loop.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.loop.uvloop.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.proxy.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.services.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.signals.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.supervisors.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.threads.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.timers.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.types.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.types.services.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.types.signals.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.types.supervisors.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.aiter.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.collections.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.compat.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.contexts.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.cron.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.futures.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.graphs.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.imports.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.locals.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.locks.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.logging.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.loops.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.mocks.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.objects.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.queues.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.text.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.times.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.tracebacks.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.trees.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.types.graphs.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.utils.types.trees.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/docs/references/mode.worker.md +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/examples/service.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/examples/webapp.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mkdocs.yml +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/__init__.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/debug.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/exceptions.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/locals.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/loop/__init__.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/loop/_gevent_loop.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/loop/eventlet.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/loop/gevent.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/loop/uvloop.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/proxy.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/py.typed +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/services.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/signals.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/supervisors.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/threads.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/timers.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/types/__init__.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/types/services.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/types/signals.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/types/supervisors.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/__init__.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/aiter.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/collections.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/compat.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/contexts.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/cron.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/futures.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/graphs/__init__.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/graphs/formatter.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/graphs/graph.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/imports.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/locals.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/locks.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/logging.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/loops.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/mocks.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/queues.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/text.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/times.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/tracebacks.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/trees.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/types/__init__.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/types/graphs.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/utils/types/trees.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode/worker.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode_streaming.egg-info/SOURCES.txt +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode_streaming.egg-info/dependency_links.txt +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode_streaming.egg-info/requires.txt +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode_streaming.egg-info/scm_file_list.json +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/mode_streaming.egg-info/top_level.txt +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/pyproject.toml +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/requirements-docs.txt +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/requirements-tests.txt +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/requirements.txt +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/scripts/build-docs.sh +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/scripts/build.sh +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/scripts/bump.sh +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/scripts/clean.sh +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/scripts/format.sh +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/scripts/lint.sh +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/scripts/tests.sh +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/setup.cfg +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/__init__.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/conftest.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/__init__.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/test_mode.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/test_proxy.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/test_service.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/test_signals.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/test_supervisors.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/test_timers.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/utils/__init__.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/utils/test_aiter.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/utils/test_collections.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/utils/test_compat.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/utils/test_contexts.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/utils/test_futures.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/utils/test_locals.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/utils/test_queues.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/utils/test_text.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/utils/test_times.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/utils/test_tracebacks.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/functional/utils/test_trees.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/unit/__init__.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/unit/test_debug.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/unit/test_locals.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/unit/test_services.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/unit/test_supervisors.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/unit/test_threads.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/unit/test_worker.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/unit/utils/__init__.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/unit/utils/test_cron.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/unit/utils/test_imports.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/unit/utils/test_locks.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/unit/utils/test_logging.py +0 -0
- {mode_streaming-0.5.0 → mode_streaming-0.5.2}/tests/unit/utils/test_loops.py +0 -0
|
@@ -28,7 +28,6 @@ from typing import (
|
|
|
28
28
|
cast,
|
|
29
29
|
get_args,
|
|
30
30
|
get_origin,
|
|
31
|
-
get_type_hints,
|
|
32
31
|
)
|
|
33
32
|
|
|
34
33
|
try:
|
|
@@ -39,20 +38,39 @@ except ImportError:
|
|
|
39
38
|
return t
|
|
40
39
|
|
|
41
40
|
|
|
42
|
-
try:
|
|
43
|
-
from typing import _type_check # type: ignore
|
|
44
|
-
except ImportError:
|
|
45
|
-
|
|
46
|
-
def _type_check(arg, msg, is_argument=True, module=None): # type: ignore
|
|
47
|
-
return arg
|
|
48
|
-
|
|
49
|
-
|
|
50
41
|
def _is_class_var(typ):
|
|
51
42
|
# Works for typing.ClassVar and types.GenericAlias (Python 3.9+)
|
|
52
43
|
origin = getattr(typ, "__origin__", None)
|
|
53
44
|
return origin is ClassVar
|
|
54
45
|
|
|
55
46
|
|
|
47
|
+
if sys.version_info >= (3, 10):
|
|
48
|
+
from inspect import get_annotations as _own_annotations
|
|
49
|
+
else:
|
|
50
|
+
|
|
51
|
+
def _own_annotations(
|
|
52
|
+
cls: type,
|
|
53
|
+
*,
|
|
54
|
+
globals: Optional[dict] = None,
|
|
55
|
+
locals: Optional[dict] = None,
|
|
56
|
+
eval_str: bool = False,
|
|
57
|
+
) -> dict:
|
|
58
|
+
"""Backport of :func:`inspect.get_annotations` for Python 3.9.
|
|
59
|
+
|
|
60
|
+
Returns only the annotations declared directly in ``cls.__dict__``,
|
|
61
|
+
never inherited ones -- matching the 3.10+ stdlib function this
|
|
62
|
+
shadows, which is what makes it safe to use per-class inside a
|
|
63
|
+
bounded MRO walk (see ``local_annotations`` below).
|
|
64
|
+
"""
|
|
65
|
+
ann = cls.__dict__.get("__annotations__", {})
|
|
66
|
+
if eval_str:
|
|
67
|
+
ann = {
|
|
68
|
+
k: (eval(v, globals, locals) if isinstance(v, str) else v) # noqa: S307
|
|
69
|
+
for k, v in ann.items()
|
|
70
|
+
}
|
|
71
|
+
return dict(ann)
|
|
72
|
+
|
|
73
|
+
|
|
56
74
|
def _get_globalns(cls):
|
|
57
75
|
# Get the global namespace for a class
|
|
58
76
|
module = sys.modules.get(cls.__module__)
|
|
@@ -348,11 +366,18 @@ def annotations(
|
|
|
348
366
|
)
|
|
349
367
|
)
|
|
350
368
|
|
|
351
|
-
#
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
369
|
+
# NOTE: Return the resolved field types as-is. `_resolve_refs`/`eval_type`
|
|
370
|
+
# above already turned string/ForwardRef annotations into real types, so
|
|
371
|
+
# nothing further is needed here. Do NOT run `_normalize_forwardref` over
|
|
372
|
+
# the result: it rewrites any type whose ``__qualname__`` contains
|
|
373
|
+
# "<locals>" (i.e. every class defined inside a function) down to a bare
|
|
374
|
+
# ``__name__`` *string*, destroying the actual class object. Callers such
|
|
375
|
+
# as faust.Record rely on identity of the returned types (e.g. matching a
|
|
376
|
+
# field type against a custom coercion mapping), which silently breaks
|
|
377
|
+
# when the type is replaced by its name string. `_normalize_forwardref`
|
|
378
|
+
# remains available as a comparison helper for callers/tests that want a
|
|
379
|
+
# canonical, hashable-by-name form.
|
|
380
|
+
return fields, defaults
|
|
356
381
|
|
|
357
382
|
|
|
358
383
|
def local_annotations(
|
|
@@ -364,9 +389,31 @@ def local_annotations(
|
|
|
364
389
|
globalns: Optional[dict[str, Any]] = None,
|
|
365
390
|
localns: Optional[dict[str, Any]] = None,
|
|
366
391
|
) -> Iterable[tuple[str, type]]:
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
)
|
|
392
|
+
# NOTE: Must only consider annotations defined directly on `cls`, not
|
|
393
|
+
# inherited ones. `annotations()` (the caller) already walks the MRO
|
|
394
|
+
# itself via `iter_mro_reversed(cls, stop=stop)` and calls
|
|
395
|
+
# `local_annotations` once per class in that bounded range, so callers
|
|
396
|
+
# can exclude base classes at/above `stop` (e.g. faust.Record excludes
|
|
397
|
+
# its internal ModelT/Model bases this way). Using
|
|
398
|
+
# `typing.get_type_hints(cls, ...)` here defeats that: it always merges
|
|
399
|
+
# annotations from the *entire* real MRO regardless of `stop`, so a
|
|
400
|
+
# non-ClassVar annotation on an excluded base (e.g.
|
|
401
|
+
# `ModelT.__evaluated_fields__`) would leak back in as if it were a
|
|
402
|
+
# field of every subclass.
|
|
403
|
+
#
|
|
404
|
+
# Plain `cls.__annotations__` attribute access is *not* safe for this
|
|
405
|
+
# either: on Python < 3.10 it falls back to an inherited base's
|
|
406
|
+
# `__annotations__` via normal MRO lookup when `cls` itself has none of
|
|
407
|
+
# its own (re-introducing the same kind of leak), and on Python 3.14+
|
|
408
|
+
# (PEP 649, deferred evaluation of annotations) direct `__annotations__`
|
|
409
|
+
# access on a class can behave unreliably -- the stdlib explicitly
|
|
410
|
+
# recommends `inspect.get_annotations()` instead, which reads only
|
|
411
|
+
# `cls.__dict__["__annotations__"]` (own annotations, no MRO fallback)
|
|
412
|
+
# and is written to handle PEP 649 correctly. `_own_annotations` here is
|
|
413
|
+
# that function, with a same-behavior backport for 3.9. String/ForwardRef
|
|
414
|
+
# annotations are still resolved below, per value, via
|
|
415
|
+
# `_resolve_refs`/`eval_type`.
|
|
416
|
+
d = _own_annotations(cls)
|
|
370
417
|
return _resolve_refs(
|
|
371
418
|
d,
|
|
372
419
|
globalns if globalns is not None else _get_globalns(cls),
|
|
@@ -413,39 +460,48 @@ def eval_type(
|
|
|
413
460
|
"""
|
|
414
461
|
invalid_types = invalid_types or set()
|
|
415
462
|
alias_types = alias_types or {}
|
|
463
|
+
original_str = typ if isinstance(typ, str) else None
|
|
416
464
|
if isinstance(typ, str):
|
|
417
465
|
typ = ForwardRef(typ)
|
|
466
|
+
# `typing._eval_type` (imported above as `_eval_type`) already resolves
|
|
467
|
+
# ForwardRef instances directly -- it is the same stdlib function
|
|
468
|
+
# `typing.get_type_hints()` itself relies on, so it always matches
|
|
469
|
+
# whatever internal ForwardRef implementation the running interpreter
|
|
470
|
+
# has (this previously went through a hand-rolled evaluator here that
|
|
471
|
+
# poked at ForwardRef's private __forward_evaluated__/__forward_code__/
|
|
472
|
+
# __forward_value__ attributes as a workaround for Python 3.6/3.7; those
|
|
473
|
+
# attributes are not part of any stable API and are no longer present at
|
|
474
|
+
# all on Python 3.14's ForwardRef, which raised AttributeError. mode's
|
|
475
|
+
# floor is Python 3.9, well past the versions that workaround targeted).
|
|
418
476
|
if isinstance(typ, ForwardRef):
|
|
419
|
-
|
|
420
|
-
|
|
477
|
+
try:
|
|
478
|
+
typ = _eval_type(typ, globalns, localns)
|
|
479
|
+
except TypeError:
|
|
480
|
+
# `_eval_type` runs the forward ref through `typing._type_check`,
|
|
481
|
+
# which rejects `ClassVar[...]` with "is not valid as type
|
|
482
|
+
# argument". But a *string* ClassVar annotation is legitimate --
|
|
483
|
+
# `from __future__ import annotations` stringizes every
|
|
484
|
+
# annotation, ClassVars included -- and callers (see
|
|
485
|
+
# `_resolve_refs` + `skip_classvar`) still want to see it so they
|
|
486
|
+
# can drop it. Evaluate the forward arg directly and accept it
|
|
487
|
+
# when it is a ClassVar; otherwise re-raise the real error.
|
|
488
|
+
src = (
|
|
489
|
+
original_str
|
|
490
|
+
if original_str is not None
|
|
491
|
+
else typ.__forward_arg__
|
|
492
|
+
)
|
|
493
|
+
evaluated = eval(src, globalns, localns) # noqa: S307
|
|
494
|
+
if _is_class_var(evaluated):
|
|
495
|
+
typ = evaluated
|
|
496
|
+
else:
|
|
497
|
+
raise
|
|
498
|
+
else:
|
|
499
|
+
typ = _eval_type(typ, globalns, localns)
|
|
421
500
|
if typ in invalid_types:
|
|
422
501
|
raise InvalidAnnotation(typ)
|
|
423
502
|
return alias_types.get(typ, typ)
|
|
424
503
|
|
|
425
504
|
|
|
426
|
-
def _ForwardRef_safe_eval(
|
|
427
|
-
ref: ForwardRef,
|
|
428
|
-
globalns: Optional[dict[str, Any]] = None,
|
|
429
|
-
localns: Optional[dict[str, Any]] = None,
|
|
430
|
-
) -> type:
|
|
431
|
-
# On 3.6/3.7 ForwardRef._evaluate crashes if str references ClassVar
|
|
432
|
-
if not ref.__forward_evaluated__:
|
|
433
|
-
if globalns is None and localns is None:
|
|
434
|
-
globalns = localns = {}
|
|
435
|
-
elif globalns is None:
|
|
436
|
-
globalns = localns
|
|
437
|
-
elif localns is None:
|
|
438
|
-
localns = globalns
|
|
439
|
-
val = eval(ref.__forward_code__, globalns, localns) # noqa: S307
|
|
440
|
-
if not _is_class_var(val):
|
|
441
|
-
val = _type_check(
|
|
442
|
-
val, "Forward references must evaluate to types."
|
|
443
|
-
)
|
|
444
|
-
ref.__forward_value__ = val
|
|
445
|
-
ref.__forward_evaluated__ = True
|
|
446
|
-
return ref.__forward_value__
|
|
447
|
-
|
|
448
|
-
|
|
449
505
|
def iter_mro_reversed(cls: type, stop: type) -> Iterable[type]:
|
|
450
506
|
"""Iterate over superclasses, in reverse Method Resolution Order.
|
|
451
507
|
|
|
@@ -251,6 +251,62 @@ def test_annotations__no_local_ns_raises():
|
|
|
251
251
|
annotations(X, globalns=None, localns=None)
|
|
252
252
|
|
|
253
253
|
|
|
254
|
+
def test_annotations__stop_excludes_base_above_stop():
|
|
255
|
+
# Regression test: a non-ClassVar annotation on a base class *above*
|
|
256
|
+
# `stop` must not leak into the fields of a subclass, even though
|
|
257
|
+
# Python's typing.get_type_hints() (which local_annotations() must NOT
|
|
258
|
+
# use directly) would normally merge annotations across the whole real
|
|
259
|
+
# MRO regardless of any `stop` boundary.
|
|
260
|
+
class Base:
|
|
261
|
+
# Deliberately not ClassVar, mirroring faust ModelT.__evaluated_fields__.
|
|
262
|
+
internal: int = 0
|
|
263
|
+
|
|
264
|
+
class Middle(Base):
|
|
265
|
+
pass
|
|
266
|
+
|
|
267
|
+
class Leaf(Middle):
|
|
268
|
+
foo: int
|
|
269
|
+
|
|
270
|
+
fields, _ = annotations(
|
|
271
|
+
Leaf, stop=Middle, globalns=globals(), localns=locals()
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
assert "internal" not in fields
|
|
275
|
+
assert fields == {"foo": int}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def test_annotations__preserves_function_local_class_identity():
|
|
279
|
+
# Regression test: a field annotated with a class defined inside a
|
|
280
|
+
# function (``__qualname__`` contains "<locals>") must be returned as the
|
|
281
|
+
# actual class object, not rewritten to its bare name string. Callers
|
|
282
|
+
# such as faust.Record match a field's resolved type by identity against
|
|
283
|
+
# a coercion mapping, which silently breaks if the type is replaced by a
|
|
284
|
+
# string.
|
|
285
|
+
class Local:
|
|
286
|
+
value: int
|
|
287
|
+
|
|
288
|
+
class Holder:
|
|
289
|
+
item: Local
|
|
290
|
+
|
|
291
|
+
fields, _ = annotations(Holder, globalns=globals(), localns=locals())
|
|
292
|
+
|
|
293
|
+
assert fields["item"] is Local
|
|
294
|
+
assert not isinstance(fields["item"], str)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def test_eval_type__string_classvar_is_not_rejected():
|
|
298
|
+
# Regression test: a *string* ClassVar annotation (as produced by
|
|
299
|
+
# `from __future__ import annotations`) must not raise. Passing the
|
|
300
|
+
# forward ref straight to typing._eval_type runs typing._type_check,
|
|
301
|
+
# which rejects ClassVar[...] with "is not valid as type argument".
|
|
302
|
+
from typing import ClassVar as _ClassVar
|
|
303
|
+
|
|
304
|
+
result = eval_type(
|
|
305
|
+
"ClassVar[int]", globalns={"ClassVar": _ClassVar}, localns={}
|
|
306
|
+
)
|
|
307
|
+
assert result == _ClassVar[int]
|
|
308
|
+
|
|
309
|
+
|
|
254
310
|
@pytest.mark.parametrize(
|
|
255
311
|
"input,expected",
|
|
256
312
|
[
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|