pytest-revealtype-injector 0.1.1__py3-none-any.whl → 0.2.2__py3-none-any.whl

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.
@@ -1,3 +1,3 @@
1
1
  """Pytest plugin for replacing reveal_type() calls inside test functions with static and runtime type checking result comparison, for confirming type annotation validity.""" # noqa: E501
2
2
 
3
- __version__ = "0.1.1"
3
+ __version__ = "0.2.2"
@@ -1,7 +1,6 @@
1
1
  import ast
2
2
  import importlib
3
3
  import json
4
- import logging
5
4
  import pathlib
6
5
  import re
7
6
  from collections.abc import (
@@ -17,7 +16,9 @@ from typing import (
17
16
 
18
17
  import mypy.api
19
18
  import pytest
19
+ import schema as s
20
20
 
21
+ from ..log import get_logger
21
22
  from ..models import (
22
23
  FilePos,
23
24
  NameCollectorBase,
@@ -26,8 +27,7 @@ from ..models import (
26
27
  VarType,
27
28
  )
28
29
 
29
- _logger = logging.getLogger(__name__)
30
- _logger.setLevel(logging.INFO)
30
+ _logger = get_logger()
31
31
 
32
32
 
33
33
  class _MypyDiagObj(TypedDict):
@@ -67,7 +67,9 @@ class _NameCollector(NameCollectorBase):
67
67
  _ = self.visit(node.value)
68
68
 
69
69
  if resolved := getattr(self.collected[prefix], name, False):
70
- self.collected[ast.unparse(node)] = resolved
70
+ code = ast.unparse(node)
71
+ self.collected[code] = resolved
72
+ _logger.debug(f"Mypy NameCollector resolved '{code}' as {resolved}")
71
73
  return node
72
74
 
73
75
  # For class defined in local scope, mypy just prepends test
@@ -100,10 +102,13 @@ class _NameCollector(NameCollectorBase):
100
102
  pass
101
103
  else:
102
104
  self.collected[name] = mod
105
+ _logger.debug(f"Mypy NameCollector resolved '{name}' as {mod}")
103
106
  return node
104
107
 
105
108
  if hasattr(self.collected["typing"], name):
106
- self.collected[name] = getattr(self.collected["typing"], name)
109
+ obj = getattr(self.collected["typing"], name)
110
+ self.collected[name] = obj
111
+ _logger.debug(f"Mypy NameCollector resolved '{name}' as {obj}")
107
112
  return node
108
113
 
109
114
  raise NameError(f'Cannot resolve "{name}"')
@@ -124,6 +129,19 @@ class _MypyAdapter(TypeCheckerAdapter):
124
129
  id = "mypy"
125
130
  typechecker_result = {}
126
131
  _type_mesg_re = re.compile(r'^Revealed type is "(?P<type>.+?)"$')
132
+ _schema = s.Schema({
133
+ "file": str,
134
+ "line": int,
135
+ "column": int,
136
+ "message": str,
137
+ "hint": s.Or(str, s.Schema(None)),
138
+ "code": str,
139
+ "severity": s.Or(
140
+ s.Schema("note"),
141
+ s.Schema("warning"),
142
+ s.Schema("error"),
143
+ ),
144
+ })
127
145
 
128
146
  @classmethod
129
147
  def run_typechecker_on(cls, paths: Iterable[pathlib.Path]) -> None:
@@ -148,8 +166,10 @@ class _MypyAdapter(TypeCheckerAdapter):
148
166
  # So-called mypy json output is merely a line-by-line
149
167
  # transformation of plain text output into json object
150
168
  for line in stdout.splitlines():
151
- # TODO Mypy json schema validation
152
- diag = cast(_MypyDiagObj, json.loads(line))
169
+ if len(line) <= 2 or line[0] != "{":
170
+ continue
171
+ obj = json.loads(line)
172
+ diag = cast(_MypyDiagObj, cls._schema.validate(obj))
153
173
  filename = pathlib.Path(diag["file"]).name
154
174
  pos = FilePos(filename, diag["line"])
155
175
  if diag["severity"] != "note":
@@ -196,7 +216,6 @@ class _MypyAdapter(TypeCheckerAdapter):
196
216
 
197
217
  @classmethod
198
218
  def set_config_file(cls, config: pytest.Config) -> None:
199
- # Mypy doesn't have a default config file
200
219
  if (path_str := config.option.revealtype_mypy_config) is None:
201
220
  _logger.info("Using default mypy configuration")
202
221
  return
@@ -1,6 +1,5 @@
1
1
  import ast
2
2
  import json
3
- import logging
4
3
  import pathlib
5
4
  import re
6
5
  import shutil
@@ -11,10 +10,15 @@ from collections.abc import (
11
10
  from typing import (
12
11
  Any,
13
12
  ForwardRef,
13
+ Literal,
14
+ TypedDict,
15
+ cast,
14
16
  )
15
17
 
16
18
  import pytest
19
+ import schema as s
17
20
 
21
+ from ..log import get_logger
18
22
  from ..models import (
19
23
  FilePos,
20
24
  NameCollectorBase,
@@ -23,8 +27,22 @@ from ..models import (
23
27
  VarType,
24
28
  )
25
29
 
26
- _logger = logging.getLogger(__name__)
27
- _logger.setLevel(logging.INFO)
30
+ _logger = get_logger()
31
+
32
+
33
+ class _PyrightDiagPosition(TypedDict):
34
+ line: int
35
+ character: int
36
+
37
+ class _PyrightDiagRange(TypedDict):
38
+ start: _PyrightDiagPosition
39
+ end: _PyrightDiagPosition
40
+
41
+ class _PyrightDiagItem(TypedDict):
42
+ file: str
43
+ severity: Literal["information", "warning", "error"]
44
+ message: str
45
+ range: _PyrightDiagRange
28
46
 
29
47
 
30
48
  class _NameCollector(NameCollectorBase):
@@ -36,9 +54,12 @@ class _NameCollector(NameCollectorBase):
36
54
  eval(name, self._globalns, self._localns | self.collected)
37
55
  except NameError:
38
56
  for m in ("typing", "typing_extensions"):
39
- if hasattr(self.collected[m], name):
40
- self.collected[name] = getattr(self.collected[m], name)
41
- return node
57
+ if not hasattr(self.collected[m], name):
58
+ continue
59
+ obj = getattr(self.collected[m], name)
60
+ self.collected[name] = obj
61
+ _logger.debug(f"Pyright NameCollector resolved '{name}' as {obj}")
62
+ return node
42
63
  raise
43
64
  return node
44
65
 
@@ -47,6 +68,21 @@ class _PyrightAdapter(TypeCheckerAdapter):
47
68
  id = "pyright"
48
69
  typechecker_result = {}
49
70
  _type_mesg_re = re.compile('^Type of "(?P<var>.+?)" is "(?P<type>.+?)"$')
71
+ # We only care about diagnostic messages that contain type information.
72
+ # Metadata not specified here.
73
+ _schema = s.Schema({
74
+ "file": str,
75
+ "severity": s.Or(
76
+ s.Schema("information"),
77
+ s.Schema("warning"),
78
+ s.Schema("error"),
79
+ ),
80
+ "message": str,
81
+ "range": {
82
+ "start": {"line": int, "character": int},
83
+ "end": {"line": int, "character": int},
84
+ },
85
+ })
50
86
 
51
87
  @classmethod
52
88
  def run_typechecker_on(cls, paths: Iterable[pathlib.Path]) -> None:
@@ -67,22 +103,17 @@ class _PyrightAdapter(TypeCheckerAdapter):
67
103
  if len(proc.stderr):
68
104
  raise TypeCheckerError(proc.stderr.decode(), None, None)
69
105
 
70
- # TODO Pyright json schema validation
71
106
  report = json.loads(proc.stdout)
72
- if proc.returncode:
73
- for diag in report["generalDiagnostics"]:
74
- if diag["severity"] != "error":
75
- continue
76
- # Pyright report lineno is 0-based,
77
- # OTOH python frame lineno is 1-based
78
- lineno = diag["range"]["start"]["line"] + 1
79
- filename = pathlib.Path(diag["file"]).name
80
- raise TypeCheckerError(diag["message"], filename, lineno)
81
- for diag in report["generalDiagnostics"]:
82
- if diag["severity"] != "information":
107
+ for item in report["generalDiagnostics"]:
108
+ diag = cast(_PyrightDiagItem, cls._schema.validate(item))
109
+ if diag["severity"] != ("error" if proc.returncode else "information"):
83
110
  continue
111
+ # Pyright report lineno is 0-based, while
112
+ # python frame lineno is 1-based
84
113
  lineno = diag["range"]["start"]["line"] + 1
85
114
  filename = pathlib.Path(diag["file"]).name
115
+ if proc.returncode:
116
+ raise TypeCheckerError(diag["message"], filename, lineno)
86
117
  if (m := cls._type_mesg_re.match(diag["message"])) is None:
87
118
  continue
88
119
  pos = FilePos(filename, lineno)
@@ -1,15 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import inspect
4
- import logging
5
4
 
6
5
  import pytest
7
6
 
8
- from . import adapter
7
+ from . import adapter, log
9
8
  from .main import revealtype_injector
10
9
 
11
- _logger = logging.getLogger(__name__)
12
- _logger.setLevel(logging.INFO)
10
+ _logger = log.get_logger()
13
11
 
14
12
 
15
13
  def pytest_pyfunc_call(pyfuncitem: pytest.Function) -> None:
@@ -65,6 +63,7 @@ def pytest_addoption(parser: pytest.Parser) -> None:
65
63
 
66
64
 
67
65
  def pytest_configure(config: pytest.Config) -> None:
66
+ _logger.setLevel(config.get_verbosity(config.VERBOSITY_TEST_CASES))
68
67
  # Forget config stash, it can't store collection of unserialized objects
69
68
  for adp in adapter.discovery():
70
69
  if config.option.revealtype_disable_adapter == adp.id:
@@ -0,0 +1,19 @@
1
+ import logging
2
+
3
+ LOGGER_NAME = "revealtype-injector"
4
+
5
+ _logger = logging.getLogger(LOGGER_NAME)
6
+
7
+ # Mapping of pytest.Config.VERBOSITY_TEST_CASES to logging levels
8
+ _verbosity_map = {
9
+ 0: logging.WARNING,
10
+ 1: logging.INFO,
11
+ 2: logging.DEBUG,
12
+ }
13
+
14
+ def get_logger() -> logging.Logger:
15
+ return _logger
16
+
17
+
18
+ def set_verbosity(verbosity: int) -> None:
19
+ _logger.setLevel(_verbosity_map.get(verbosity, logging.DEBUG))
@@ -1,6 +1,5 @@
1
1
  import ast
2
2
  import inspect
3
- import logging
4
3
  import pathlib
5
4
  import sys
6
5
  from typing import (
@@ -15,7 +14,7 @@ from typeguard import (
15
14
  check_type_internal,
16
15
  )
17
16
 
18
- from . import adapter
17
+ from . import adapter, log
19
18
  from .models import (
20
19
  FilePos,
21
20
  TypeCheckerError,
@@ -24,8 +23,7 @@ from .models import (
24
23
 
25
24
  _T = TypeVar("_T")
26
25
 
27
- _logger = logging.getLogger(__name__)
28
- _logger.setLevel(logging.WARN)
26
+ _logger = log.get_logger()
29
27
 
30
28
 
31
29
  class RevealTypeExtractor(ast.NodeVisitor):
@@ -42,8 +40,15 @@ class RevealTypeExtractor(ast.NodeVisitor):
42
40
 
43
41
 
44
42
  def _get_var_name(frame: inspect.Traceback) -> str | None:
43
+ filename = pathlib.Path(frame.filename)
44
+ if not filename.exists():
45
+ _logger.warning(
46
+ f"Stack frame points to file '{filename}' "
47
+ "which doesn't exist on local system."
48
+ )
45
49
  ctxt, idx = frame.code_context, frame.index
46
- assert ctxt is not None and idx is not None
50
+ assert ctxt is not None
51
+ assert idx is not None
47
52
  code = ctxt[idx].strip()
48
53
 
49
54
  walker = RevealTypeExtractor()
@@ -51,7 +56,9 @@ def _get_var_name(frame: inspect.Traceback) -> str | None:
51
56
  # as much restriction on test code as 'eval' mode does.
52
57
  walker.visit(ast.parse(code, mode="eval"))
53
58
  assert walker.target is not None
54
- return ast.get_source_segment(code, walker.target)
59
+ result = ast.get_source_segment(code, walker.target)
60
+ _logger.debug(f"Extraction OK: {code=}, {result=}")
61
+ return result
55
62
 
56
63
 
57
64
  def revealtype_injector(var: _T) -> _T:
@@ -134,7 +141,8 @@ def revealtype_injector(var: _T) -> _T:
134
141
  try:
135
142
  check_type_internal(var, ref, memo)
136
143
  except TypeCheckError as e:
137
- e.args = (f"({adp.id}) " + e.args[0],) + e.args[1:]
144
+ # Only args[0] contains message
145
+ e.args = (e.args[0] + f" (from {adp.id})",) + e.args[1:]
138
146
  raise
139
147
 
140
148
  return var
@@ -15,6 +15,7 @@ from typing import (
15
15
  )
16
16
 
17
17
  import pytest
18
+ from schema import Schema
18
19
 
19
20
 
20
21
  class FilePos(NamedTuple):
@@ -91,6 +92,7 @@ class TypeCheckerAdapter:
91
92
  # {('file.py', 10): ('var_name', 'list[str]'), ...}
92
93
  typechecker_result: ClassVar[dict[FilePos, VarType]]
93
94
  _type_mesg_re: ClassVar[re.Pattern[str]]
95
+ _schema: ClassVar[Schema]
94
96
 
95
97
  @classmethod
96
98
  @abc.abstractmethod
@@ -1,15 +1,16 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: pytest-revealtype-injector
3
- Version: 0.1.1
3
+ Version: 0.2.2
4
4
  Summary: Pytest plugin for replacing reveal_type() calls inside test functions with static and runtime type checking result comparison, for confirming type annotation validity.
5
5
  Project-URL: homepage, https://github.com/abelcheung/pytest-revealtype-injector
6
6
  Author-email: Abel Cheung <abelcheung@gmail.com>
7
- License: MIT
7
+ License-Expression: MIT
8
+ License-File: COPYING
9
+ License-File: COPYING.mit
8
10
  Keywords: annotation,dynamic-typing,pytest,reveal_type,static-typing,stub,stubs,type-checking,types,typing
9
11
  Classifier: Development Status :: 4 - Beta
10
12
  Classifier: Framework :: Pytest
11
13
  Classifier: Intended Audience :: Developers
12
- Classifier: License :: OSI Approved :: MIT License
13
14
  Classifier: Programming Language :: Python
14
15
  Classifier: Programming Language :: Python :: 3
15
16
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -23,9 +24,15 @@ Requires-Python: >=3.10
23
24
  Requires-Dist: mypy>=1.11.2
24
25
  Requires-Dist: pyright~=1.1
25
26
  Requires-Dist: pytest>=7.0
27
+ Requires-Dist: schema==0.7.7
26
28
  Requires-Dist: typeguard~=4.3
27
29
  Description-Content-Type: text/markdown
28
30
 
31
+ ![PyPI - Version](https://img.shields.io/pypi/v/pytest-revealtype-injector)
32
+ ![GitHub Release Date](https://img.shields.io/github/release-date/abelcheung/pytest-revealtype-injector)
33
+ ![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fgithub.com%2Fabelcheung%2Fpytest-revealtype-injector%2Fraw%2Frefs%2Fheads%2Fmain%2Fpyproject.toml)
34
+ ![PyPI - Wheel](https://img.shields.io/pypi/wheel/pytest-revealtype-injector)
35
+
29
36
  `pytest-revealtype-injector` is a `pytest` plugin for replacing [`reveal_type()`](https://docs.python.org/3/library/typing.html#typing.reveal_type) calls inside test functions as something more sophisticated. It does the following tasks in parallel:
30
37
 
31
38
  - Launch external static type checkers (`pyright` and `mypy`) and store `reveal_type` results.
@@ -77,6 +84,31 @@ def test_something():
77
84
 
78
85
  2. `reveal_type()` calls have to stay in a single line, without anything else. This limitation comes from using [`eval` mode in AST parsing](https://docs.python.org/3/library/ast.html#ast.Expression).
79
86
 
87
+ ## Logging
88
+
89
+ This plugin uses standard [`logging`](https://docs.python.org/3/library/logging.html) internally. `pytest -v` can be used to reveal `INFO` and `DEBUG` logs. Given following example:
90
+
91
+ ```python
92
+ def test_superfluous(self) -> None:
93
+ x: list[str] = ['a', 'b', 'c', 1] # type: ignore # pyright: ignore
94
+ reveal_type(x)
95
+ ```
96
+
97
+ Something like this will be shown as test result:
98
+
99
+ ```
100
+ ...
101
+ raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
102
+ E typeguard.TypeCheckError: item 3 is not an instance of str (from pyright)
103
+ ------------------------------------------------------------- Captured log call -------------------------------------------------------------
104
+ INFO revealtype-injector:hooks.py:26 Replaced reveal_type() from global import with <function revealtype_injector at 0x00000238DB923D00>
105
+ DEBUG revealtype-injector:main.py:60 Extraction OK: code='reveal_type(x)', result='x'
106
+ ========================================================== short test summary info ==========================================================
107
+ FAILED tests/runtime/test_attrib.py::TestAttrib::test_superfluous - typeguard.TypeCheckError: item 3 is not an instance of str (from pyright)
108
+ ============================================================= 1 failed in 3.38s =============================================================
109
+ ```
110
+
111
+
80
112
  ## History
81
113
 
82
114
  This pytest plugin starts its life as part of testsuite related utilities within [`types-lxml`](https://github.com/abelcheung/types-lxml). As `lxml` is a `cython` project and probably never incorporate inline python annotation in future, there is need to compare runtime result to static type checker output for discrepancy. As time goes by, it starts to make sense to manage as an independent project.
@@ -0,0 +1,16 @@
1
+ pytest_revealtype_injector/__init__.py,sha256=3zGbYqpjy1u4__CnFV56_VB1fSpZ1LPbOgsI35Vfpz8,211
2
+ pytest_revealtype_injector/hooks.py,sha256=cj1iXJJrVtqylXvvdLfjZ61HGBt5wsGBUe-iAgu9WiU,2434
3
+ pytest_revealtype_injector/log.py,sha256=U9IvsoZzhFQcdFmVtuSmt2OTYxxFuIP5o321dyE4hiA,417
4
+ pytest_revealtype_injector/main.py,sha256=ftTxEY13fNCY7Z3_oPvh38OtHQzqmxYAYnyzzgit29w,4786
5
+ pytest_revealtype_injector/models.py,sha256=XsxUQGdv1U4CFi1bB1VeEojPpiwCZp7uxRIWuTGhIaM,3214
6
+ pytest_revealtype_injector/plugin.py,sha256=fkI6yF0dFVba0jEikIrsRp1NUQd2ohWLq4x2lSvFyH0,211
7
+ pytest_revealtype_injector/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ pytest_revealtype_injector/adapter/__init__.py,sha256=wQg3Ply0Xrfe9US_lzgqRgKFE89ZBvDFti6_77X_NKk,339
9
+ pytest_revealtype_injector/adapter/mypy_.py,sha256=q-stxOY2bffTAm2SoHiCAb7VA_f9jXc4UxlyimXV-RY,8840
10
+ pytest_revealtype_injector/adapter/pyright_.py,sha256=fn8lE0G3ZeRANH6xKpizDOEXyH8a9yHHYdLCgMSsiFg,4857
11
+ pytest_revealtype_injector-0.2.2.dist-info/METADATA,sha256=JXyzhgJp_UwveDqAvKDIWeFkJcdFv5lEEUrKS4I8A2o,5520
12
+ pytest_revealtype_injector-0.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
+ pytest_revealtype_injector-0.2.2.dist-info/entry_points.txt,sha256=UfOm7y3WQnOoGV1mgTMb42MI6iBRPIl88FJiAOnt6SY,74
14
+ pytest_revealtype_injector-0.2.2.dist-info/licenses/COPYING,sha256=LSYUX8PcSMvHCkhM5oi07eOrSLV89qdEJ-FVZmbcpNE,355
15
+ pytest_revealtype_injector-0.2.2.dist-info/licenses/COPYING.mit,sha256=IzYEFDIOECyuupg_B3O9FvgjnU9i4JtambpbleoYHdQ,1060
16
+ pytest_revealtype_injector-0.2.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.26.3
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,6 @@
1
+ pytest-revealtype-injector is released under MIT license (see COPYING.mit).
2
+
3
+ Original source code comes from part of types-lxml project, which is
4
+ release under Apache-2.0 license. But as the sole author of all source
5
+ code inside this repository, it is at my own discretion to follow pytest
6
+ license because this project is taking shape as a pytest plugin.
@@ -1,4 +1,4 @@
1
- Copyright (c) 2023-2025 Abel Cheung
1
+ Copyright (c) 2023-2024 Abel Cheung
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
@@ -17,13 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
17
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
18
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
19
  SOFTWARE.
20
-
21
-
22
- ---------------------
23
-
24
- pytest-revealtype-injector is released under MIT license (see above).
25
-
26
- Original source code comes from part of types-lxml project, which is released
27
- under Apache-2.0 license. But as the sole author of all relevant source code, it
28
- is at my own discretion to follow pytest license because this project is taking
29
- form of a pytest plugin.
@@ -1,14 +0,0 @@
1
- pytest_revealtype_injector/__init__.py,sha256=-sBG0VW8hqWI1JUSqzgJ6M0nptG5OWWQw9dzjnVpRQ8,211
2
- pytest_revealtype_injector/hooks.py,sha256=5V2r19I47xQtCDd8ZAzXlqt3nYaBZ4CaMAyGP6w_Y0k,2414
3
- pytest_revealtype_injector/main.py,sha256=dBlXcGc2NBcEHp0YX75Bp4Q7KY-t1oxlmPQS6o08U24,4490
4
- pytest_revealtype_injector/models.py,sha256=hWo9Oz2ausmht-QpfMTTiCtdE3980IA2KEuch7mo_jk,3158
5
- pytest_revealtype_injector/plugin.py,sha256=fkI6yF0dFVba0jEikIrsRp1NUQd2ohWLq4x2lSvFyH0,211
6
- pytest_revealtype_injector/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- pytest_revealtype_injector/adapter/__init__.py,sha256=wQg3Ply0Xrfe9US_lzgqRgKFE89ZBvDFti6_77X_NKk,339
8
- pytest_revealtype_injector/adapter/mypy_.py,sha256=qXZKppEwPYtCcRVBAvg8cLx2heI673w8cT9VftHNU_c,8237
9
- pytest_revealtype_injector/adapter/pyright_.py,sha256=SEibrnMa0AAa1M3S4BmwEQ7ZgwQEUttsFOi-GWCrisA,4105
10
- pytest_revealtype_injector-0.1.1.dist-info/METADATA,sha256=ZNYRvvNfc1Hm5LAPyUvPcJWmqHqdFQxlQwviUovZJAE,3649
11
- pytest_revealtype_injector-0.1.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
12
- pytest_revealtype_injector-0.1.1.dist-info/entry_points.txt,sha256=UfOm7y3WQnOoGV1mgTMb42MI6iBRPIl88FJiAOnt6SY,74
13
- pytest_revealtype_injector-0.1.1.dist-info/licenses/LICENSE,sha256=nLEPjqH24l4RByIq0dHIFhblIpcxpDrRX7sT3lBNoeM,1420
14
- pytest_revealtype_injector-0.1.1.dist-info/RECORD,,