mode-streaming 0.5.1__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.
Files changed (163) hide show
  1. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/PKG-INFO +1 -1
  2. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/objects.py +37 -6
  3. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode_streaming.egg-info/PKG-INFO +1 -1
  4. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode_streaming.egg-info/scm_version.json +2 -2
  5. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/unit/utils/test_objects.py +32 -0
  6. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/.editorconfig +0 -0
  7. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/.github/ISSUE_TEMPLATE.md +0 -0
  8. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  9. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/.github/workflows/deploy-docs.yml +0 -0
  10. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/.github/workflows/publish.yml +0 -0
  11. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/.github/workflows/tests.yml +0 -0
  12. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/.gitignore +0 -0
  13. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/.pre-commit-config.yaml +0 -0
  14. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/AUTHORS +0 -0
  15. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/CHANGELOG.md +0 -0
  16. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/CODE_OF_CONDUCT.md +0 -0
  17. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/CONTRIBUTING.md +0 -0
  18. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/LICENSE +0 -0
  19. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/README.md +0 -0
  20. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/changelog.md +0 -0
  21. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/contributing.md +0 -0
  22. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/creating-service.md +0 -0
  23. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/example-service.md +0 -0
  24. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/example-webapp.md +0 -0
  25. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/index.md +0 -0
  26. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.debug.md +0 -0
  27. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.exceptions.md +0 -0
  28. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.locals.md +0 -0
  29. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.loop.eventlet.md +0 -0
  30. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.loop.gevent.md +0 -0
  31. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.loop.md +0 -0
  32. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.loop.uvloop.md +0 -0
  33. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.proxy.md +0 -0
  34. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.services.md +0 -0
  35. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.signals.md +0 -0
  36. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.supervisors.md +0 -0
  37. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.threads.md +0 -0
  38. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.timers.md +0 -0
  39. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.types.md +0 -0
  40. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.types.services.md +0 -0
  41. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.types.signals.md +0 -0
  42. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.types.supervisors.md +0 -0
  43. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.aiter.md +0 -0
  44. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.collections.md +0 -0
  45. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.compat.md +0 -0
  46. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.contexts.md +0 -0
  47. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.cron.md +0 -0
  48. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.futures.md +0 -0
  49. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.graphs.md +0 -0
  50. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.imports.md +0 -0
  51. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.locals.md +0 -0
  52. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.locks.md +0 -0
  53. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.logging.md +0 -0
  54. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.loops.md +0 -0
  55. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.mocks.md +0 -0
  56. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.objects.md +0 -0
  57. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.queues.md +0 -0
  58. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.text.md +0 -0
  59. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.times.md +0 -0
  60. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.tracebacks.md +0 -0
  61. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.trees.md +0 -0
  62. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.types.graphs.md +0 -0
  63. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.utils.types.trees.md +0 -0
  64. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/docs/references/mode.worker.md +0 -0
  65. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/examples/service.py +0 -0
  66. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/examples/webapp.py +0 -0
  67. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mkdocs.yml +0 -0
  68. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/__init__.py +0 -0
  69. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/debug.py +0 -0
  70. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/exceptions.py +0 -0
  71. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/locals.py +0 -0
  72. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/loop/__init__.py +0 -0
  73. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/loop/_gevent_loop.py +0 -0
  74. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/loop/eventlet.py +0 -0
  75. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/loop/gevent.py +0 -0
  76. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/loop/uvloop.py +0 -0
  77. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/proxy.py +0 -0
  78. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/py.typed +0 -0
  79. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/services.py +0 -0
  80. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/signals.py +0 -0
  81. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/supervisors.py +0 -0
  82. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/threads.py +0 -0
  83. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/timers.py +0 -0
  84. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/types/__init__.py +0 -0
  85. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/types/services.py +0 -0
  86. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/types/signals.py +0 -0
  87. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/types/supervisors.py +0 -0
  88. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/__init__.py +0 -0
  89. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/aiter.py +0 -0
  90. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/collections.py +0 -0
  91. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/compat.py +0 -0
  92. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/contexts.py +0 -0
  93. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/cron.py +0 -0
  94. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/futures.py +0 -0
  95. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/graphs/__init__.py +0 -0
  96. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/graphs/formatter.py +0 -0
  97. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/graphs/graph.py +0 -0
  98. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/imports.py +0 -0
  99. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/locals.py +0 -0
  100. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/locks.py +0 -0
  101. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/logging.py +0 -0
  102. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/loops.py +0 -0
  103. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/mocks.py +0 -0
  104. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/queues.py +0 -0
  105. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/text.py +0 -0
  106. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/times.py +0 -0
  107. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/tracebacks.py +0 -0
  108. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/trees.py +0 -0
  109. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/types/__init__.py +0 -0
  110. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/types/graphs.py +0 -0
  111. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/utils/types/trees.py +0 -0
  112. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode/worker.py +0 -0
  113. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode_streaming.egg-info/SOURCES.txt +0 -0
  114. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode_streaming.egg-info/dependency_links.txt +0 -0
  115. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode_streaming.egg-info/requires.txt +0 -0
  116. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode_streaming.egg-info/scm_file_list.json +0 -0
  117. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/mode_streaming.egg-info/top_level.txt +0 -0
  118. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/pyproject.toml +0 -0
  119. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/requirements-docs.txt +0 -0
  120. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/requirements-tests.txt +0 -0
  121. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/requirements.txt +0 -0
  122. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/scripts/build-docs.sh +0 -0
  123. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/scripts/build.sh +0 -0
  124. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/scripts/bump.sh +0 -0
  125. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/scripts/clean.sh +0 -0
  126. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/scripts/format.sh +0 -0
  127. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/scripts/lint.sh +0 -0
  128. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/scripts/tests.sh +0 -0
  129. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/setup.cfg +0 -0
  130. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/__init__.py +0 -0
  131. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/conftest.py +0 -0
  132. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/__init__.py +0 -0
  133. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/test_mode.py +0 -0
  134. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/test_proxy.py +0 -0
  135. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/test_service.py +0 -0
  136. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/test_signals.py +0 -0
  137. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/test_supervisors.py +0 -0
  138. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/test_timers.py +0 -0
  139. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/utils/__init__.py +0 -0
  140. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/utils/test_aiter.py +0 -0
  141. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/utils/test_collections.py +0 -0
  142. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/utils/test_compat.py +0 -0
  143. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/utils/test_contexts.py +0 -0
  144. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/utils/test_futures.py +0 -0
  145. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/utils/test_locals.py +0 -0
  146. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/utils/test_queues.py +0 -0
  147. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/utils/test_text.py +0 -0
  148. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/utils/test_times.py +0 -0
  149. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/utils/test_tracebacks.py +0 -0
  150. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/functional/utils/test_trees.py +0 -0
  151. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/unit/__init__.py +0 -0
  152. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/unit/test_debug.py +0 -0
  153. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/unit/test_locals.py +0 -0
  154. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/unit/test_services.py +0 -0
  155. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/unit/test_supervisors.py +0 -0
  156. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/unit/test_threads.py +0 -0
  157. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/unit/test_worker.py +0 -0
  158. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/unit/utils/__init__.py +0 -0
  159. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/unit/utils/test_cron.py +0 -0
  160. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/unit/utils/test_imports.py +0 -0
  161. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/unit/utils/test_locks.py +0 -0
  162. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/tests/unit/utils/test_logging.py +0 -0
  163. {mode_streaming-0.5.1 → mode_streaming-0.5.2}/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.1
3
+ Version: 0.5.2
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>
@@ -366,11 +366,18 @@ def annotations(
366
366
  )
367
367
  )
368
368
 
369
- # Normalize all field types for forward refs
370
- normalized_fields = {
371
- k: _normalize_forwardref(v) for k, v in fields.items()
372
- }
373
- return normalized_fields, defaults
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
374
381
 
375
382
 
376
383
  def local_annotations(
@@ -453,6 +460,7 @@ def eval_type(
453
460
  """
454
461
  invalid_types = invalid_types or set()
455
462
  alias_types = alias_types or {}
463
+ original_str = typ if isinstance(typ, str) else None
456
464
  if isinstance(typ, str):
457
465
  typ = ForwardRef(typ)
458
466
  # `typing._eval_type` (imported above as `_eval_type`) already resolves
@@ -465,7 +473,30 @@ def eval_type(
465
473
  # attributes are not part of any stable API and are no longer present at
466
474
  # all on Python 3.14's ForwardRef, which raised AttributeError. mode's
467
475
  # floor is Python 3.9, well past the versions that workaround targeted).
468
- typ = _eval_type(typ, globalns, localns)
476
+ if isinstance(typ, ForwardRef):
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)
469
500
  if typ in invalid_types:
470
501
  raise InvalidAnnotation(typ)
471
502
  return alias_types.get(typ, typ)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mode-streaming
3
- Version: 0.5.1
3
+ Version: 0.5.2
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.1",
2
+ "tag": "0.5.2",
3
3
  "distance": 0,
4
- "node": "gb48d2dd45818a9f01f6e78159e12330e010d1f71",
4
+ "node": "g95118563cf82e6445e255bcf2fa7beeb1b23b5d3",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-07-19"
@@ -275,6 +275,38 @@ def test_annotations__stop_excludes_base_above_stop():
275
275
  assert fields == {"foo": int}
276
276
 
277
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
+
278
310
  @pytest.mark.parametrize(
279
311
  "input,expected",
280
312
  [
File without changes
File without changes
File without changes
File without changes