mode-streaming 0.5.0__tar.gz → 0.5.1__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.
Files changed (163) hide show
  1. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/PKG-INFO +1 -1
  2. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/objects.py +62 -37
  3. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode_streaming.egg-info/PKG-INFO +1 -1
  4. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode_streaming.egg-info/scm_version.json +2 -2
  5. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/unit/utils/test_objects.py +24 -0
  6. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/.editorconfig +0 -0
  7. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/.github/ISSUE_TEMPLATE.md +0 -0
  8. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  9. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/.github/workflows/deploy-docs.yml +0 -0
  10. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/.github/workflows/publish.yml +0 -0
  11. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/.github/workflows/tests.yml +0 -0
  12. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/.gitignore +0 -0
  13. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/.pre-commit-config.yaml +0 -0
  14. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/AUTHORS +0 -0
  15. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/CHANGELOG.md +0 -0
  16. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/CODE_OF_CONDUCT.md +0 -0
  17. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/CONTRIBUTING.md +0 -0
  18. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/LICENSE +0 -0
  19. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/README.md +0 -0
  20. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/changelog.md +0 -0
  21. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/contributing.md +0 -0
  22. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/creating-service.md +0 -0
  23. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/example-service.md +0 -0
  24. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/example-webapp.md +0 -0
  25. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/index.md +0 -0
  26. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.debug.md +0 -0
  27. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.exceptions.md +0 -0
  28. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.locals.md +0 -0
  29. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.loop.eventlet.md +0 -0
  30. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.loop.gevent.md +0 -0
  31. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.loop.md +0 -0
  32. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.loop.uvloop.md +0 -0
  33. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.proxy.md +0 -0
  34. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.services.md +0 -0
  35. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.signals.md +0 -0
  36. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.supervisors.md +0 -0
  37. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.threads.md +0 -0
  38. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.timers.md +0 -0
  39. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.types.md +0 -0
  40. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.types.services.md +0 -0
  41. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.types.signals.md +0 -0
  42. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.types.supervisors.md +0 -0
  43. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.aiter.md +0 -0
  44. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.collections.md +0 -0
  45. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.compat.md +0 -0
  46. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.contexts.md +0 -0
  47. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.cron.md +0 -0
  48. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.futures.md +0 -0
  49. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.graphs.md +0 -0
  50. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.imports.md +0 -0
  51. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.locals.md +0 -0
  52. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.locks.md +0 -0
  53. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.logging.md +0 -0
  54. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.loops.md +0 -0
  55. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.mocks.md +0 -0
  56. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.objects.md +0 -0
  57. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.queues.md +0 -0
  58. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.text.md +0 -0
  59. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.times.md +0 -0
  60. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.tracebacks.md +0 -0
  61. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.trees.md +0 -0
  62. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.types.graphs.md +0 -0
  63. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.utils.types.trees.md +0 -0
  64. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/docs/references/mode.worker.md +0 -0
  65. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/examples/service.py +0 -0
  66. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/examples/webapp.py +0 -0
  67. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mkdocs.yml +0 -0
  68. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/__init__.py +0 -0
  69. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/debug.py +0 -0
  70. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/exceptions.py +0 -0
  71. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/locals.py +0 -0
  72. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/loop/__init__.py +0 -0
  73. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/loop/_gevent_loop.py +0 -0
  74. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/loop/eventlet.py +0 -0
  75. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/loop/gevent.py +0 -0
  76. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/loop/uvloop.py +0 -0
  77. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/proxy.py +0 -0
  78. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/py.typed +0 -0
  79. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/services.py +0 -0
  80. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/signals.py +0 -0
  81. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/supervisors.py +0 -0
  82. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/threads.py +0 -0
  83. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/timers.py +0 -0
  84. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/types/__init__.py +0 -0
  85. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/types/services.py +0 -0
  86. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/types/signals.py +0 -0
  87. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/types/supervisors.py +0 -0
  88. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/__init__.py +0 -0
  89. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/aiter.py +0 -0
  90. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/collections.py +0 -0
  91. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/compat.py +0 -0
  92. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/contexts.py +0 -0
  93. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/cron.py +0 -0
  94. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/futures.py +0 -0
  95. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/graphs/__init__.py +0 -0
  96. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/graphs/formatter.py +0 -0
  97. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/graphs/graph.py +0 -0
  98. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/imports.py +0 -0
  99. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/locals.py +0 -0
  100. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/locks.py +0 -0
  101. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/logging.py +0 -0
  102. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/loops.py +0 -0
  103. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/mocks.py +0 -0
  104. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/queues.py +0 -0
  105. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/text.py +0 -0
  106. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/times.py +0 -0
  107. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/tracebacks.py +0 -0
  108. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/trees.py +0 -0
  109. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/types/__init__.py +0 -0
  110. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/types/graphs.py +0 -0
  111. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/utils/types/trees.py +0 -0
  112. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode/worker.py +0 -0
  113. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode_streaming.egg-info/SOURCES.txt +0 -0
  114. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode_streaming.egg-info/dependency_links.txt +0 -0
  115. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode_streaming.egg-info/requires.txt +0 -0
  116. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode_streaming.egg-info/scm_file_list.json +0 -0
  117. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/mode_streaming.egg-info/top_level.txt +0 -0
  118. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/pyproject.toml +0 -0
  119. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/requirements-docs.txt +0 -0
  120. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/requirements-tests.txt +0 -0
  121. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/requirements.txt +0 -0
  122. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/scripts/build-docs.sh +0 -0
  123. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/scripts/build.sh +0 -0
  124. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/scripts/bump.sh +0 -0
  125. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/scripts/clean.sh +0 -0
  126. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/scripts/format.sh +0 -0
  127. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/scripts/lint.sh +0 -0
  128. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/scripts/tests.sh +0 -0
  129. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/setup.cfg +0 -0
  130. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/__init__.py +0 -0
  131. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/conftest.py +0 -0
  132. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/__init__.py +0 -0
  133. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/test_mode.py +0 -0
  134. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/test_proxy.py +0 -0
  135. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/test_service.py +0 -0
  136. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/test_signals.py +0 -0
  137. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/test_supervisors.py +0 -0
  138. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/test_timers.py +0 -0
  139. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/utils/__init__.py +0 -0
  140. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/utils/test_aiter.py +0 -0
  141. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/utils/test_collections.py +0 -0
  142. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/utils/test_compat.py +0 -0
  143. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/utils/test_contexts.py +0 -0
  144. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/utils/test_futures.py +0 -0
  145. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/utils/test_locals.py +0 -0
  146. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/utils/test_queues.py +0 -0
  147. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/utils/test_text.py +0 -0
  148. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/utils/test_times.py +0 -0
  149. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/utils/test_tracebacks.py +0 -0
  150. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/functional/utils/test_trees.py +0 -0
  151. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/unit/__init__.py +0 -0
  152. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/unit/test_debug.py +0 -0
  153. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/unit/test_locals.py +0 -0
  154. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/unit/test_services.py +0 -0
  155. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/unit/test_supervisors.py +0 -0
  156. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/unit/test_threads.py +0 -0
  157. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/unit/test_worker.py +0 -0
  158. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/unit/utils/__init__.py +0 -0
  159. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/unit/utils/test_cron.py +0 -0
  160. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/unit/utils/test_imports.py +0 -0
  161. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/unit/utils/test_locks.py +0 -0
  162. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/unit/utils/test_logging.py +0 -0
  163. {mode_streaming-0.5.0 → mode_streaming-0.5.1}/tests/unit/utils/test_loops.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mode-streaming
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  Summary: AsyncIO Service-based programming
5
5
  Author-email: Ask Solem Hoel <ask@robinhood.com>
6
6
  Maintainer-email: William Barnhart <williambbarnhart@gmail.com>
@@ -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__)
@@ -364,9 +382,31 @@ def local_annotations(
364
382
  globalns: Optional[dict[str, Any]] = None,
365
383
  localns: Optional[dict[str, Any]] = None,
366
384
  ) -> Iterable[tuple[str, type]]:
367
- d = get_type_hints(
368
- cls, globalns if globalns is not None else _get_globalns(cls), localns
369
- )
385
+ # NOTE: Must only consider annotations defined directly on `cls`, not
386
+ # inherited ones. `annotations()` (the caller) already walks the MRO
387
+ # itself via `iter_mro_reversed(cls, stop=stop)` and calls
388
+ # `local_annotations` once per class in that bounded range, so callers
389
+ # can exclude base classes at/above `stop` (e.g. faust.Record excludes
390
+ # its internal ModelT/Model bases this way). Using
391
+ # `typing.get_type_hints(cls, ...)` here defeats that: it always merges
392
+ # annotations from the *entire* real MRO regardless of `stop`, so a
393
+ # non-ClassVar annotation on an excluded base (e.g.
394
+ # `ModelT.__evaluated_fields__`) would leak back in as if it were a
395
+ # field of every subclass.
396
+ #
397
+ # Plain `cls.__annotations__` attribute access is *not* safe for this
398
+ # either: on Python < 3.10 it falls back to an inherited base's
399
+ # `__annotations__` via normal MRO lookup when `cls` itself has none of
400
+ # its own (re-introducing the same kind of leak), and on Python 3.14+
401
+ # (PEP 649, deferred evaluation of annotations) direct `__annotations__`
402
+ # access on a class can behave unreliably -- the stdlib explicitly
403
+ # recommends `inspect.get_annotations()` instead, which reads only
404
+ # `cls.__dict__["__annotations__"]` (own annotations, no MRO fallback)
405
+ # and is written to handle PEP 649 correctly. `_own_annotations` here is
406
+ # that function, with a same-behavior backport for 3.9. String/ForwardRef
407
+ # annotations are still resolved below, per value, via
408
+ # `_resolve_refs`/`eval_type`.
409
+ d = _own_annotations(cls)
370
410
  return _resolve_refs(
371
411
  d,
372
412
  globalns if globalns is not None else _get_globalns(cls),
@@ -415,37 +455,22 @@ def eval_type(
415
455
  alias_types = alias_types or {}
416
456
  if isinstance(typ, str):
417
457
  typ = ForwardRef(typ)
418
- if isinstance(typ, ForwardRef):
419
- typ = _ForwardRef_safe_eval(typ, globalns, localns)
458
+ # `typing._eval_type` (imported above as `_eval_type`) already resolves
459
+ # ForwardRef instances directly -- it is the same stdlib function
460
+ # `typing.get_type_hints()` itself relies on, so it always matches
461
+ # whatever internal ForwardRef implementation the running interpreter
462
+ # has (this previously went through a hand-rolled evaluator here that
463
+ # poked at ForwardRef's private __forward_evaluated__/__forward_code__/
464
+ # __forward_value__ attributes as a workaround for Python 3.6/3.7; those
465
+ # attributes are not part of any stable API and are no longer present at
466
+ # all on Python 3.14's ForwardRef, which raised AttributeError. mode's
467
+ # floor is Python 3.9, well past the versions that workaround targeted).
420
468
  typ = _eval_type(typ, globalns, localns)
421
469
  if typ in invalid_types:
422
470
  raise InvalidAnnotation(typ)
423
471
  return alias_types.get(typ, typ)
424
472
 
425
473
 
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
474
  def iter_mro_reversed(cls: type, stop: type) -> Iterable[type]:
450
475
  """Iterate over superclasses, in reverse Method Resolution Order.
451
476
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mode-streaming
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  Summary: AsyncIO Service-based programming
5
5
  Author-email: Ask Solem Hoel <ask@robinhood.com>
6
6
  Maintainer-email: William Barnhart <williambbarnhart@gmail.com>
@@ -1,7 +1,7 @@
1
1
  {
2
- "tag": "0.5.0",
2
+ "tag": "0.5.1",
3
3
  "distance": 0,
4
- "node": "gea836e7025a4ac52dcf644871e1b20c3c43710f9",
4
+ "node": "gb48d2dd45818a9f01f6e78159e12330e010d1f71",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-07-19"
@@ -251,6 +251,30 @@ 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
+
254
278
  @pytest.mark.parametrize(
255
279
  "input,expected",
256
280
  [
File without changes
File without changes
File without changes
File without changes