cocotb 1.9.2__cp313-cp313-win32.whl → 2.0.0rc2__cp313-cp313-win32.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.

Potentially problematic release.


This version of cocotb might be problematic. Click here for more details.

Files changed (161) hide show
  1. cocotb/_ANSI.py +65 -0
  2. cocotb/__init__.py +81 -327
  3. cocotb/_base_triggers.py +515 -0
  4. cocotb/_bridge.py +186 -0
  5. cocotb/_decorators.py +515 -0
  6. cocotb/_deprecation.py +3 -3
  7. cocotb/_exceptions.py +7 -0
  8. cocotb/_extended_awaitables.py +419 -0
  9. cocotb/_gpi_triggers.py +385 -0
  10. cocotb/_init.py +301 -0
  11. cocotb/_outcomes.py +54 -0
  12. cocotb/_profiling.py +46 -0
  13. cocotb/_py_compat.py +114 -29
  14. cocotb/_scheduler.py +448 -0
  15. cocotb/_test.py +248 -0
  16. cocotb/_test_factory.py +312 -0
  17. cocotb/_test_functions.py +42 -0
  18. cocotb/_typing.py +7 -0
  19. cocotb/_utils.py +274 -0
  20. cocotb/_version.py +3 -7
  21. cocotb/_xunit_reporter.py +66 -0
  22. cocotb/clock.py +353 -108
  23. cocotb/debug.py +24 -0
  24. cocotb/handle.py +1370 -793
  25. cocotb/libs/cocotb.dll +0 -0
  26. cocotb/libs/cocotb.exp +0 -0
  27. cocotb/libs/cocotb.lib +0 -0
  28. cocotb/libs/cocotbfli_modelsim.dll +0 -0
  29. cocotb/libs/cocotbfli_modelsim.exp +0 -0
  30. cocotb/libs/cocotbfli_modelsim.lib +0 -0
  31. cocotb/libs/cocotbutils.dll +0 -0
  32. cocotb/libs/cocotbutils.exp +0 -0
  33. cocotb/libs/cocotbutils.lib +0 -0
  34. cocotb/libs/cocotbvhpi_aldec.dll +0 -0
  35. cocotb/libs/cocotbvhpi_aldec.exp +0 -0
  36. cocotb/libs/cocotbvhpi_aldec.lib +0 -0
  37. cocotb/libs/cocotbvhpi_modelsim.dll +0 -0
  38. cocotb/libs/cocotbvhpi_modelsim.exp +0 -0
  39. cocotb/libs/cocotbvhpi_modelsim.lib +0 -0
  40. cocotb/libs/cocotbvpi_aldec.dll +0 -0
  41. cocotb/libs/cocotbvpi_aldec.exp +0 -0
  42. cocotb/libs/cocotbvpi_aldec.lib +0 -0
  43. cocotb/libs/cocotbvpi_ghdl.dll +0 -0
  44. cocotb/libs/cocotbvpi_ghdl.exp +0 -0
  45. cocotb/libs/cocotbvpi_ghdl.lib +0 -0
  46. cocotb/libs/cocotbvpi_icarus.exp +0 -0
  47. cocotb/libs/cocotbvpi_icarus.lib +0 -0
  48. cocotb/libs/cocotbvpi_icarus.vpl +0 -0
  49. cocotb/libs/cocotbvpi_modelsim.dll +0 -0
  50. cocotb/libs/cocotbvpi_modelsim.exp +0 -0
  51. cocotb/libs/cocotbvpi_modelsim.lib +0 -0
  52. cocotb/libs/embed.dll +0 -0
  53. cocotb/libs/embed.exp +0 -0
  54. cocotb/libs/embed.lib +0 -0
  55. cocotb/libs/gpi.dll +0 -0
  56. cocotb/libs/gpi.exp +0 -0
  57. cocotb/libs/gpi.lib +0 -0
  58. cocotb/libs/gpilog.dll +0 -0
  59. cocotb/libs/gpilog.exp +0 -0
  60. cocotb/libs/gpilog.lib +0 -0
  61. cocotb/libs/pygpilog.dll +0 -0
  62. cocotb/libs/pygpilog.exp +0 -0
  63. cocotb/libs/pygpilog.lib +0 -0
  64. cocotb/logging.py +424 -0
  65. cocotb/queue.py +103 -57
  66. cocotb/regression.py +680 -717
  67. cocotb/result.py +17 -188
  68. cocotb/share/def/aldec.exp +0 -0
  69. cocotb/share/def/aldec.lib +0 -0
  70. cocotb/share/def/ghdl.exp +0 -0
  71. cocotb/share/def/ghdl.lib +0 -0
  72. cocotb/share/def/icarus.exp +0 -0
  73. cocotb/share/def/icarus.lib +0 -0
  74. cocotb/share/def/modelsim.def +1 -0
  75. cocotb/share/def/modelsim.exp +0 -0
  76. cocotb/share/def/modelsim.lib +0 -0
  77. cocotb/share/include/cocotb_utils.h +9 -32
  78. cocotb/share/include/embed.h +7 -30
  79. cocotb/share/include/gpi.h +331 -137
  80. cocotb/share/include/gpi_logging.h +221 -142
  81. cocotb/share/include/py_gpi_logging.h +8 -5
  82. cocotb/share/include/vpi_user_ext.h +4 -26
  83. cocotb/share/lib/verilator/verilator.cpp +80 -67
  84. cocotb/simtime.py +230 -0
  85. cocotb/simulator.cp313-win32.exp +0 -0
  86. cocotb/simulator.cp313-win32.lib +0 -0
  87. cocotb/simulator.cp313-win32.pyd +0 -0
  88. cocotb/simulator.pyi +107 -0
  89. cocotb/task.py +478 -213
  90. cocotb/triggers.py +55 -1092
  91. cocotb/types/__init__.py +28 -47
  92. cocotb/types/_abstract_array.py +151 -0
  93. cocotb/types/_array.py +295 -0
  94. cocotb/types/_indexing.py +17 -0
  95. cocotb/types/_logic.py +333 -0
  96. cocotb/types/_logic_array.py +868 -0
  97. cocotb/types/{range.py → _range.py} +47 -48
  98. cocotb/types/_resolve.py +76 -0
  99. cocotb/utils.py +58 -646
  100. cocotb-2.0.0rc2.dist-info/METADATA +60 -0
  101. cocotb-2.0.0rc2.dist-info/RECORD +146 -0
  102. {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info}/WHEEL +1 -1
  103. cocotb-2.0.0rc2.dist-info/entry_points.txt +2 -0
  104. {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info/licenses}/LICENSE +1 -0
  105. {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info}/top_level.txt +1 -0
  106. cocotb_tools/__init__.py +0 -0
  107. cocotb_tools/_coverage.py +33 -0
  108. cocotb_tools/_vendor/__init__.py +3 -0
  109. cocotb_tools/check_results.py +65 -0
  110. cocotb_tools/combine_results.py +152 -0
  111. cocotb_tools/config.py +241 -0
  112. {cocotb → cocotb_tools}/ipython_support.py +29 -22
  113. cocotb_tools/makefiles/Makefile.deprecations +27 -0
  114. {cocotb/share → cocotb_tools}/makefiles/Makefile.inc +77 -55
  115. {cocotb/share → cocotb_tools}/makefiles/Makefile.sim +16 -33
  116. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.activehdl +9 -16
  117. cocotb_tools/makefiles/simulators/Makefile.cvc +61 -0
  118. cocotb_tools/makefiles/simulators/Makefile.dsim +39 -0
  119. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.ghdl +13 -42
  120. cocotb_tools/makefiles/simulators/Makefile.icarus +80 -0
  121. cocotb_tools/makefiles/simulators/Makefile.ius +93 -0
  122. cocotb_tools/makefiles/simulators/Makefile.modelsim +9 -0
  123. cocotb_tools/makefiles/simulators/Makefile.nvc +60 -0
  124. cocotb_tools/makefiles/simulators/Makefile.questa +29 -0
  125. cocotb/share/makefiles/simulators/Makefile.questa → cocotb_tools/makefiles/simulators/Makefile.questa-compat +26 -54
  126. cocotb_tools/makefiles/simulators/Makefile.questa-qisqrun +149 -0
  127. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.riviera +17 -56
  128. cocotb_tools/makefiles/simulators/Makefile.vcs +65 -0
  129. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.verilator +15 -22
  130. {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.xcelium +20 -52
  131. cocotb_tools/py.typed +0 -0
  132. cocotb_tools/runner.py +1868 -0
  133. cocotb/_sim_versions.py → cocotb_tools/sim_versions.py +16 -21
  134. pygpi/entry.py +34 -18
  135. pygpi/py.typed +0 -0
  136. cocotb/ANSI.py +0 -92
  137. cocotb/binary.py +0 -858
  138. cocotb/config.py +0 -289
  139. cocotb/decorators.py +0 -332
  140. cocotb/log.py +0 -303
  141. cocotb/memdebug.py +0 -35
  142. cocotb/outcomes.py +0 -56
  143. cocotb/runner.py +0 -1400
  144. cocotb/scheduler.py +0 -1099
  145. cocotb/share/makefiles/Makefile.deprecations +0 -12
  146. cocotb/share/makefiles/simulators/Makefile.cvc +0 -94
  147. cocotb/share/makefiles/simulators/Makefile.icarus +0 -111
  148. cocotb/share/makefiles/simulators/Makefile.ius +0 -125
  149. cocotb/share/makefiles/simulators/Makefile.modelsim +0 -32
  150. cocotb/share/makefiles/simulators/Makefile.nvc +0 -64
  151. cocotb/share/makefiles/simulators/Makefile.vcs +0 -98
  152. cocotb/types/array.py +0 -309
  153. cocotb/types/logic.py +0 -292
  154. cocotb/types/logic_array.py +0 -298
  155. cocotb/wavedrom.py +0 -199
  156. cocotb/xunit_reporter.py +0 -80
  157. cocotb-1.9.2.dist-info/METADATA +0 -168
  158. cocotb-1.9.2.dist-info/RECORD +0 -121
  159. cocotb-1.9.2.dist-info/entry_points.txt +0 -2
  160. /cocotb/{_vendor/__init__.py → py.typed} +0 -0
  161. {cocotb → cocotb_tools}/_vendor/distutils_version.py +0 -0
cocotb/_decorators.py ADDED
@@ -0,0 +1,515 @@
1
+ # Copyright cocotb contributors
2
+ # Copyright (c) 2013 Potential Ventures Ltd
3
+ # Copyright (c) 2013 SolarFlare Communications Inc
4
+ # Licensed under the Revised BSD License, see LICENSE for details.
5
+ # SPDX-License-Identifier: BSD-3-Clause
6
+
7
+ import functools
8
+ import inspect
9
+ from enum import Enum
10
+ from itertools import product
11
+ from typing import (
12
+ Callable,
13
+ Coroutine,
14
+ Dict,
15
+ Iterable,
16
+ List,
17
+ Optional,
18
+ Sequence,
19
+ Tuple,
20
+ Type,
21
+ Union,
22
+ cast,
23
+ overload,
24
+ )
25
+
26
+ from cocotb._base_triggers import Trigger
27
+ from cocotb._py_compat import Protocol, TypeAlias
28
+ from cocotb._typing import TimeUnit
29
+
30
+
31
+ class Test:
32
+ """A cocotb test in a regression.
33
+
34
+ Args:
35
+ func:
36
+ The test function object.
37
+
38
+ name:
39
+ The name of the test function.
40
+ Defaults to ``func.__qualname__`` (the dotted path to the test function in the module).
41
+
42
+ module:
43
+ The name of the module containing the test function.
44
+ Defaults to ``func.__module__`` (the name of the module containing the test function).
45
+
46
+ doc:
47
+ The docstring for the test.
48
+ Defaults to ``func.__doc__`` (the docstring of the test function).
49
+
50
+ timeout_time:
51
+ Simulation time duration before the test is forced to fail with a :exc:`~cocotb.triggers.SimTimeoutError`.
52
+
53
+ timeout_unit:
54
+ Unit of ``timeout_time``, accepts any unit that :class:`~cocotb.triggers.Timer` does.
55
+
56
+ expect_fail:
57
+ If ``True`` and the test fails a functional check via an :keyword:`assert` statement, :func:`pytest.raises`,
58
+ :func:`pytest.warns`, or :func:`pytest.deprecated_call`, the test is considered to have passed.
59
+ If ``True`` and the test passes successfully, the test is considered to have failed.
60
+
61
+ expect_error:
62
+ Mark the result as a pass only if one of the given exception types is raised in the test.
63
+
64
+ skip:
65
+ Don't execute this test as part of the regression.
66
+ The test can still be run manually by setting :envvar:`COCOTB_TESTCASE`.
67
+
68
+ stage:
69
+ Order tests logically into stages.
70
+ Tests from earlier stages are run before tests from later stages.
71
+ """
72
+
73
+ def __init__(
74
+ self,
75
+ *,
76
+ func: Callable[..., Coroutine[Trigger, None, None]],
77
+ name: Optional[str] = None,
78
+ module: Optional[str] = None,
79
+ doc: Optional[str] = None,
80
+ timeout_time: Optional[float] = None,
81
+ timeout_unit: TimeUnit = "step",
82
+ expect_fail: bool = False,
83
+ expect_error: Union[Type[BaseException], Tuple[Type[BaseException], ...]] = (),
84
+ skip: bool = False,
85
+ stage: int = 0,
86
+ ) -> None:
87
+ self.func: Callable[..., Coroutine[Trigger, None, None]] = func
88
+ self.timeout_time = timeout_time
89
+ self.timeout_unit = timeout_unit
90
+ self.expect_fail = expect_fail
91
+ if isinstance(expect_error, type):
92
+ expect_error = (expect_error,)
93
+ self.expect_error = expect_error
94
+ self.skip = skip
95
+ self.stage = stage
96
+ self.name = self.func.__qualname__ if name is None else name
97
+ self.module = self.func.__module__ if module is None else module
98
+ self.doc = self.func.__doc__ if doc is None else doc
99
+ if self.doc is not None:
100
+ # cleanup docstring using `trim` function from PEP257
101
+ self.doc = inspect.cleandoc(self.doc)
102
+ self.fullname = f"{self.module}.{self.name}"
103
+
104
+
105
+ TestFuncType: TypeAlias = Callable[..., Coroutine[Trigger, None, None]]
106
+
107
+
108
+ class Parameterized:
109
+ def __init__(
110
+ self,
111
+ test_function: TestFuncType,
112
+ options: List[
113
+ Union[
114
+ Tuple[str, Sequence[object]],
115
+ Tuple[Sequence[str], Sequence[Sequence[object]]],
116
+ ]
117
+ ],
118
+ ) -> None:
119
+ self.test_template = Test(func=test_function)
120
+ self.options = options
121
+ # we are assuming the input checking is done in parametrize()
122
+
123
+ self._option_reprs: Dict[str, List[str]] = {}
124
+
125
+ for name, values in options:
126
+ if isinstance(name, str):
127
+ self._option_reprs[name] = _reprs(values)
128
+ else:
129
+ # transform to Dict[name, values]
130
+ transformed: Dict[str, List[object]] = {}
131
+ for nam_idx, nam in enumerate(name):
132
+ transformed[nam] = []
133
+ for value_array in cast("Sequence[Sequence[object]]", values):
134
+ value = value_array[nam_idx]
135
+ transformed[nam].append(value)
136
+ for n, vs in transformed.items():
137
+ self._option_reprs[n] = _reprs(vs)
138
+
139
+ def generate_tests(self) -> Iterable[Test]:
140
+ test_func = self.test_template.func
141
+ test_func_name = self.test_template.name
142
+
143
+ # this value is a list of ranges of the same length as each set of values in self.options for passing to itertools.product
144
+ option_indexes = [range(len(option[1])) for option in self.options]
145
+
146
+ # go through the cartesian product of all values of all options
147
+ for selected_options in product(*option_indexes):
148
+ test_kwargs: Dict[str, object] = {}
149
+ test_name_pieces: List[str] = [test_func_name]
150
+ for option_idx, select_idx in enumerate(selected_options):
151
+ option_name, option_values = self.options[option_idx]
152
+ selected_value = option_values[select_idx]
153
+
154
+ if isinstance(option_name, str):
155
+ # single params per option
156
+ selected_value = cast("Sequence[object]", selected_value)
157
+ test_kwargs[option_name] = selected_value
158
+ test_name_pieces.append(
159
+ f"/{option_name}={self._option_reprs[option_name][select_idx]}"
160
+ )
161
+ else:
162
+ # multiple params per option
163
+ selected_value = cast("Sequence[object]", selected_value)
164
+ for n, v in zip(option_name, selected_value):
165
+ test_kwargs[n] = v
166
+ test_name_pieces.append(
167
+ f"/{n}={self._option_reprs[n][select_idx]}"
168
+ )
169
+
170
+ parametrized_test_name = "".join(test_name_pieces)
171
+
172
+ # create wrapper function to bind kwargs
173
+ @functools.wraps(test_func)
174
+ async def _my_test(
175
+ dut: object, kwargs: Dict[str, object] = test_kwargs
176
+ ) -> None:
177
+ await test_func(dut, **kwargs)
178
+
179
+ yield Test(
180
+ func=_my_test,
181
+ name=parametrized_test_name,
182
+ timeout_time=self.test_template.timeout_time,
183
+ timeout_unit=self.test_template.timeout_unit,
184
+ expect_fail=self.test_template.expect_fail,
185
+ expect_error=self.test_template.expect_error,
186
+ skip=self.test_template.skip,
187
+ stage=self.test_template.stage,
188
+ )
189
+
190
+
191
+ def _reprs(values: Sequence[object]) -> List[str]:
192
+ result: List[str] = []
193
+ for value in values:
194
+ value_repr = _repr(value)
195
+ if value_repr is None:
196
+ # non-representable value in option, so default to index strings and give up
197
+ return [str(i) for i in range(len(values))]
198
+ else:
199
+ result.append(value_repr)
200
+ return result
201
+
202
+
203
+ def _repr(v: object) -> Optional[str]:
204
+ if isinstance(v, Enum):
205
+ return v.name
206
+ elif isinstance(v, str):
207
+ if len(v) <= 10 and v.isidentifier():
208
+ return v
209
+ else:
210
+ return None
211
+ elif isinstance(v, (int, float, bool, type(None))):
212
+ return repr(v)
213
+ elif isinstance(v, type):
214
+ return v.__qualname__
215
+ elif hasattr(v, "__qualname__"):
216
+ return v.__qualname__
217
+ else:
218
+ return None
219
+
220
+
221
+ class TestDecoratorType(Protocol):
222
+ # TODO use position only argument for *obj* so we aren't tied to that name.
223
+ @overload
224
+ def __call__(self, obj: TestFuncType) -> Test: ...
225
+ @overload
226
+ def __call__(self, obj: Parameterized) -> Parameterized: ...
227
+
228
+
229
+ @overload
230
+ def test(obj: TestFuncType) -> Test: ...
231
+
232
+
233
+ @overload
234
+ def test(obj: Parameterized) -> Parameterized: ...
235
+
236
+
237
+ @overload
238
+ def test(
239
+ *,
240
+ timeout_time: Optional[float] = None,
241
+ timeout_unit: TimeUnit = "step",
242
+ expect_fail: bool = False,
243
+ expect_error: Union[Type[BaseException], Tuple[Type[BaseException], ...]] = (),
244
+ skip: bool = False,
245
+ stage: int = 0,
246
+ name: Optional[str] = None,
247
+ ) -> TestDecoratorType: ...
248
+
249
+
250
+ def test(
251
+ obj: Optional[Union[TestFuncType, Parameterized]] = None,
252
+ *,
253
+ timeout_time: Optional[float] = None,
254
+ timeout_unit: TimeUnit = "step",
255
+ expect_fail: bool = False,
256
+ expect_error: Union[Type[BaseException], Tuple[Type[BaseException], ...]] = (),
257
+ skip: bool = False,
258
+ stage: int = 0,
259
+ name: Optional[str] = None,
260
+ ) -> Union[
261
+ Test,
262
+ Parameterized,
263
+ TestDecoratorType,
264
+ ]:
265
+ r"""
266
+ Decorator to register a Callable which returns a Coroutine as a test.
267
+
268
+ The test decorator provides a test timeout, and allows us to mark tests as skipped or expecting errors or failures.
269
+ Tests are evaluated in the order they are defined in a test module.
270
+
271
+ Usage:
272
+ .. code-block:: python
273
+
274
+ @cocotb.test(timeout_time=10, timeout_unit="ms")
275
+ async def test_thing(dut): ...
276
+
277
+ Args:
278
+ timeout_time:
279
+ Simulation time duration before timeout occurs.
280
+
281
+ .. versionadded:: 1.3
282
+
283
+ .. note::
284
+ Test timeout is intended for protection against deadlock.
285
+ Users should use :class:`~cocotb.triggers.with_timeout` if they require a
286
+ more general-purpose timeout mechanism.
287
+
288
+ timeout_unit:
289
+ Units of timeout_time, accepts any units that :class:`~cocotb.triggers.Timer` does.
290
+
291
+ .. versionadded:: 1.3
292
+
293
+ .. versionchanged:: 2.0
294
+ Passing ``None`` as the *timeout_unit* argument was removed, use ``'step'`` instead.
295
+
296
+ expect_fail:
297
+ If ``True`` and the test fails a functional check via an :keyword:`assert` statement, :class:`pytest.raises`,
298
+ :class:`pytest.warns`, or :class:`pytest.deprecated_call` the test is considered to have passed.
299
+ If ``True`` and the test passes successfully, the test is considered to have failed.
300
+
301
+ expect_error:
302
+ Mark the result as a pass only if one of the exception types is raised in the test.
303
+ This is primarily for cocotb internal regression use for when a simulator error is expected.
304
+
305
+ Users are encouraged to use the following idiom instead::
306
+
307
+ @cocotb.test()
308
+ async def my_test(dut):
309
+ try:
310
+ await thing_that_should_fail()
311
+ except ExceptionIExpect:
312
+ pass
313
+ else:
314
+ assert False, "Exception did not occur"
315
+
316
+ .. versionchanged:: 1.3
317
+ Specific exception types can be expected
318
+
319
+ .. versionchanged:: 2.0
320
+ Passing a :class:`bool` value was removed.
321
+ Pass a specific :class:`Exception` or a tuple of Exceptions instead.
322
+
323
+ skip:
324
+ Don't execute this test as part of the regression. Test can still be run
325
+ manually by setting :make:var:`COCOTB_TESTCASE`.
326
+
327
+ stage:
328
+ Order tests logically into stages, where multiple tests can share a stage.
329
+ Defaults to 0.
330
+
331
+ name:
332
+ Override the default name of the test.
333
+ The default test name is the :meth:`__qualname__` of the decorated test function.
334
+
335
+ .. versionadded:: 2.0
336
+
337
+ Returns:
338
+ The test function to which the decorator is applied.
339
+
340
+ .. note::
341
+
342
+ To extend the test decorator, use the following template to create a new
343
+ ``cocotb.test``\-like wrapper.
344
+
345
+ .. code-block:: python
346
+
347
+ import functools
348
+
349
+
350
+ def test_extender(**decorator_kwargs):
351
+ def decorator(obj):
352
+ @cocotb.test(**decorator_kwargs)
353
+ @functools.wraps(obj)
354
+ async def test(dut, **test_kwargs):
355
+ # your code here
356
+ ...
357
+
358
+ return obj
359
+
360
+ return decorator
361
+
362
+ .. versionchanged:: 2.0
363
+ Support using decorator on test function without supplying parameters first.
364
+
365
+ Assumes all default values for the test parameters.
366
+
367
+ .. code-block:: python
368
+
369
+ @cocotb.test
370
+ async def test_thing(dut): ...
371
+
372
+
373
+ .. versionchanged:: 2.0
374
+ Decorated tests now return the decorated object.
375
+
376
+ """
377
+ if obj is not None:
378
+ if isinstance(obj, Parameterized):
379
+ return obj
380
+ else:
381
+ return Test(func=obj)
382
+
383
+ @overload
384
+ def wrapper(obj: TestFuncType) -> Test: ...
385
+
386
+ @overload
387
+ def wrapper(obj: Parameterized) -> Parameterized: ...
388
+
389
+ def wrapper(obj: Union[TestFuncType, Parameterized]) -> Union[Test, Parameterized]:
390
+ if isinstance(obj, Parameterized):
391
+ obj.test_template = Test(
392
+ func=obj.test_template.func,
393
+ name=name,
394
+ timeout_time=timeout_time,
395
+ timeout_unit=timeout_unit,
396
+ expect_fail=expect_fail,
397
+ expect_error=expect_error,
398
+ skip=skip,
399
+ stage=stage,
400
+ )
401
+ return obj
402
+ else:
403
+ return Test(
404
+ func=obj,
405
+ name=name,
406
+ timeout_time=timeout_time,
407
+ timeout_unit=timeout_unit,
408
+ expect_fail=expect_fail,
409
+ expect_error=expect_error,
410
+ skip=skip,
411
+ stage=stage,
412
+ )
413
+
414
+ return wrapper
415
+
416
+
417
+ def parametrize(
418
+ *options_by_tuple: Union[
419
+ Tuple[str, Sequence[object]], Tuple[Sequence[str], Sequence[Sequence[object]]]
420
+ ],
421
+ **options_by_name: Sequence[object],
422
+ ) -> Callable[[TestFuncType], Parameterized]:
423
+ """Decorator to generate parametrized tests from a single test function.
424
+
425
+ Decorates a test function with named test parameters.
426
+ The call to ``parametrize`` should include the name of each test parameter and the possible values each parameter can hold.
427
+ This will generate a test for each of the Cartesian products of the parameters and their values.
428
+
429
+ .. code-block:: python
430
+
431
+ @cocotb.test(
432
+ skip=False,
433
+ )
434
+ @cocotb.parametrize(
435
+ arg1=[0, 1],
436
+ arg2=["a", "b"],
437
+ )
438
+ async def my_test(arg1: int, arg2: str) -> None: ...
439
+
440
+ The above is equivalent to the following.
441
+
442
+ .. code-block:: python
443
+
444
+ @cocotb.test(skip=False)
445
+ async def my_test_0_a() -> None:
446
+ arg1, arg2 = 0, "a"
447
+ ...
448
+
449
+
450
+ @cocotb.test(skip=False)
451
+ async def my_test_0_b() -> None:
452
+ arg1, arg2 = 0, "b"
453
+ ...
454
+
455
+
456
+ @cocotb.test(skip=False)
457
+ async def my_test_1_a() -> None:
458
+ arg1, arg2 = 1, "a"
459
+ ...
460
+
461
+
462
+ @cocotb.test(skip=False)
463
+ async def my_test_1_b() -> None:
464
+ arg1, arg2 = 1, "b"
465
+ ...
466
+
467
+ Options can also be specified in much the same way that :meth:`TestFactory.add_option <cocotb.regression.TestFactory.add_option>` can,
468
+ either by supplying tuples of the parameter name to values,
469
+ or a sequence of variable names and a sequence of values.
470
+
471
+ .. code-block:: python
472
+
473
+ @cocotb.parametrize(
474
+ ("arg1", [0, 1]),
475
+ (("arg2", "arg3"), [(1, 2), (3, 4)]),
476
+ )
477
+ async def my_test_2(arg1: int, arg2: int, arg3: int) -> None: ...
478
+
479
+ Args:
480
+ options_by_tuple:
481
+ Tuple of parameter name to sequence of values for that parameter,
482
+ or tuple of sequence of parameter names to sequence of sequences of values for that pack of parameters.
483
+
484
+ options_by_name:
485
+ Mapping of parameter name to sequence of values for that parameter.
486
+
487
+ .. versionadded:: 2.0
488
+ """
489
+
490
+ # check good inputs
491
+ for i, option_by_tuple in enumerate(options_by_tuple):
492
+ if len(option_by_tuple) != 2:
493
+ raise ValueError(
494
+ f"Invalid option tuple {i}, expected exactly two fields `(name, values)`"
495
+ )
496
+ name, values = option_by_tuple
497
+ if not isinstance(name, str):
498
+ for n in name:
499
+ if not n.isidentifier():
500
+ raise ValueError("Option names must be valid Python identifiers")
501
+ values = cast("Sequence[Sequence[object]]", values)
502
+ for value in values:
503
+ if len(name) != len(value):
504
+ raise ValueError(
505
+ f"Invalid option tuple {i}, mismatching number of parameters ({name}) and values ({value})"
506
+ )
507
+ elif not name.isidentifier():
508
+ raise ValueError("Option names must be valid Python identifiers")
509
+
510
+ options = [*options_by_tuple, *options_by_name.items()]
511
+
512
+ def wrapper(f: TestFuncType) -> Parameterized:
513
+ return Parameterized(f, options)
514
+
515
+ return wrapper
cocotb/_deprecation.py CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  import functools
6
6
  import warnings
7
- from typing import Any, Callable, Type, TypeVar
7
+ from typing import Callable, Type, TypeVar
8
8
 
9
9
  AnyCallableT = TypeVar("AnyCallableT", bound=Callable[..., object])
10
10
 
@@ -27,10 +27,10 @@ def deprecated(
27
27
 
28
28
  def decorator(f: AnyCallableT) -> AnyCallableT:
29
29
  @functools.wraps(f)
30
- def wrapper(*args: Any, **kwargs: Any) -> Any:
30
+ def wrapper(*args: object, **kwargs: object) -> object:
31
31
  warnings.warn(msg, category=category, stacklevel=2)
32
32
  return f(*args, **kwargs)
33
33
 
34
- return wrapper
34
+ return wrapper # type: ignore[return-value] # type checkers get confused about this
35
35
 
36
36
  return decorator
cocotb/_exceptions.py ADDED
@@ -0,0 +1,7 @@
1
+ # Copyright cocotb contributors
2
+ # Licensed under the Revised BSD License, see LICENSE for details.
3
+ # SPDX-License-Identifier: BSD-3-Clause
4
+
5
+
6
+ class InternalError(BaseException):
7
+ """An error internal to scheduler. If you see this, report a bug!"""