cocotb 1.9.2__cp311-cp311-win_amd64.whl → 2.0.0rc2__cp311-cp311-win_amd64.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.
- cocotb/_ANSI.py +65 -0
- cocotb/__init__.py +81 -327
- cocotb/_base_triggers.py +515 -0
- cocotb/_bridge.py +186 -0
- cocotb/_decorators.py +515 -0
- cocotb/_deprecation.py +3 -3
- cocotb/_exceptions.py +7 -0
- cocotb/_extended_awaitables.py +419 -0
- cocotb/_gpi_triggers.py +385 -0
- cocotb/_init.py +301 -0
- cocotb/_outcomes.py +54 -0
- cocotb/_profiling.py +46 -0
- cocotb/_py_compat.py +114 -29
- cocotb/_scheduler.py +448 -0
- cocotb/_test.py +248 -0
- cocotb/_test_factory.py +312 -0
- cocotb/_test_functions.py +42 -0
- cocotb/_typing.py +7 -0
- cocotb/_utils.py +274 -0
- cocotb/_version.py +3 -7
- cocotb/_xunit_reporter.py +66 -0
- cocotb/clock.py +353 -108
- cocotb/debug.py +24 -0
- cocotb/handle.py +1370 -793
- cocotb/libs/cocotb.dll +0 -0
- cocotb/libs/cocotb.exp +0 -0
- cocotb/libs/cocotb.lib +0 -0
- cocotb/libs/cocotbfli_modelsim.dll +0 -0
- cocotb/libs/cocotbfli_modelsim.exp +0 -0
- cocotb/libs/cocotbfli_modelsim.lib +0 -0
- cocotb/libs/cocotbutils.dll +0 -0
- cocotb/libs/cocotbutils.exp +0 -0
- cocotb/libs/cocotbutils.lib +0 -0
- cocotb/libs/cocotbvhpi_aldec.dll +0 -0
- cocotb/libs/cocotbvhpi_aldec.exp +0 -0
- cocotb/libs/cocotbvhpi_aldec.lib +0 -0
- cocotb/libs/cocotbvhpi_modelsim.dll +0 -0
- cocotb/libs/cocotbvhpi_modelsim.exp +0 -0
- cocotb/libs/cocotbvhpi_modelsim.lib +0 -0
- cocotb/libs/cocotbvpi_aldec.dll +0 -0
- cocotb/libs/cocotbvpi_aldec.exp +0 -0
- cocotb/libs/cocotbvpi_aldec.lib +0 -0
- cocotb/libs/cocotbvpi_ghdl.dll +0 -0
- cocotb/libs/cocotbvpi_ghdl.exp +0 -0
- cocotb/libs/cocotbvpi_ghdl.lib +0 -0
- cocotb/libs/cocotbvpi_icarus.exp +0 -0
- cocotb/libs/cocotbvpi_icarus.lib +0 -0
- cocotb/libs/cocotbvpi_icarus.vpl +0 -0
- cocotb/libs/cocotbvpi_modelsim.dll +0 -0
- cocotb/libs/cocotbvpi_modelsim.exp +0 -0
- cocotb/libs/cocotbvpi_modelsim.lib +0 -0
- cocotb/libs/embed.dll +0 -0
- cocotb/libs/embed.exp +0 -0
- cocotb/libs/embed.lib +0 -0
- cocotb/libs/gpi.dll +0 -0
- cocotb/libs/gpi.exp +0 -0
- cocotb/libs/gpi.lib +0 -0
- cocotb/libs/gpilog.dll +0 -0
- cocotb/libs/gpilog.exp +0 -0
- cocotb/libs/gpilog.lib +0 -0
- cocotb/libs/pygpilog.dll +0 -0
- cocotb/libs/pygpilog.exp +0 -0
- cocotb/libs/pygpilog.lib +0 -0
- cocotb/logging.py +424 -0
- cocotb/queue.py +103 -57
- cocotb/regression.py +680 -717
- cocotb/result.py +17 -188
- cocotb/share/def/aldec.exp +0 -0
- cocotb/share/def/aldec.lib +0 -0
- cocotb/share/def/ghdl.exp +0 -0
- cocotb/share/def/ghdl.lib +0 -0
- cocotb/share/def/icarus.exp +0 -0
- cocotb/share/def/icarus.lib +0 -0
- cocotb/share/def/modelsim.def +1 -0
- cocotb/share/def/modelsim.exp +0 -0
- cocotb/share/def/modelsim.lib +0 -0
- cocotb/share/include/cocotb_utils.h +9 -32
- cocotb/share/include/embed.h +7 -30
- cocotb/share/include/gpi.h +331 -137
- cocotb/share/include/gpi_logging.h +221 -142
- cocotb/share/include/py_gpi_logging.h +8 -5
- cocotb/share/include/vpi_user_ext.h +4 -26
- cocotb/share/lib/verilator/verilator.cpp +80 -67
- cocotb/simtime.py +230 -0
- cocotb/simulator.cp311-win_amd64.exp +0 -0
- cocotb/simulator.cp311-win_amd64.lib +0 -0
- cocotb/simulator.cp311-win_amd64.pyd +0 -0
- cocotb/simulator.pyi +107 -0
- cocotb/task.py +478 -213
- cocotb/triggers.py +55 -1092
- cocotb/types/__init__.py +28 -47
- cocotb/types/_abstract_array.py +151 -0
- cocotb/types/_array.py +295 -0
- cocotb/types/_indexing.py +17 -0
- cocotb/types/_logic.py +333 -0
- cocotb/types/_logic_array.py +868 -0
- cocotb/types/{range.py → _range.py} +47 -48
- cocotb/types/_resolve.py +76 -0
- cocotb/utils.py +58 -646
- cocotb-2.0.0rc2.dist-info/METADATA +60 -0
- cocotb-2.0.0rc2.dist-info/RECORD +146 -0
- {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info}/WHEEL +1 -1
- cocotb-2.0.0rc2.dist-info/entry_points.txt +2 -0
- {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info/licenses}/LICENSE +1 -0
- {cocotb-1.9.2.dist-info → cocotb-2.0.0rc2.dist-info}/top_level.txt +1 -0
- cocotb_tools/__init__.py +0 -0
- cocotb_tools/_coverage.py +33 -0
- cocotb_tools/_vendor/__init__.py +3 -0
- cocotb_tools/check_results.py +65 -0
- cocotb_tools/combine_results.py +152 -0
- cocotb_tools/config.py +241 -0
- {cocotb → cocotb_tools}/ipython_support.py +29 -22
- cocotb_tools/makefiles/Makefile.deprecations +27 -0
- {cocotb/share → cocotb_tools}/makefiles/Makefile.inc +77 -55
- {cocotb/share → cocotb_tools}/makefiles/Makefile.sim +16 -33
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.activehdl +9 -16
- cocotb_tools/makefiles/simulators/Makefile.cvc +61 -0
- cocotb_tools/makefiles/simulators/Makefile.dsim +39 -0
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.ghdl +13 -42
- cocotb_tools/makefiles/simulators/Makefile.icarus +80 -0
- cocotb_tools/makefiles/simulators/Makefile.ius +93 -0
- cocotb_tools/makefiles/simulators/Makefile.modelsim +9 -0
- cocotb_tools/makefiles/simulators/Makefile.nvc +60 -0
- cocotb_tools/makefiles/simulators/Makefile.questa +29 -0
- cocotb/share/makefiles/simulators/Makefile.questa → cocotb_tools/makefiles/simulators/Makefile.questa-compat +26 -54
- cocotb_tools/makefiles/simulators/Makefile.questa-qisqrun +149 -0
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.riviera +17 -56
- cocotb_tools/makefiles/simulators/Makefile.vcs +65 -0
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.verilator +15 -22
- {cocotb/share → cocotb_tools}/makefiles/simulators/Makefile.xcelium +20 -52
- cocotb_tools/py.typed +0 -0
- cocotb_tools/runner.py +1868 -0
- cocotb/_sim_versions.py → cocotb_tools/sim_versions.py +16 -21
- pygpi/entry.py +34 -18
- pygpi/py.typed +0 -0
- cocotb/ANSI.py +0 -92
- cocotb/binary.py +0 -858
- cocotb/config.py +0 -289
- cocotb/decorators.py +0 -332
- cocotb/log.py +0 -303
- cocotb/memdebug.py +0 -35
- cocotb/outcomes.py +0 -56
- cocotb/runner.py +0 -1400
- cocotb/scheduler.py +0 -1099
- cocotb/share/makefiles/Makefile.deprecations +0 -12
- cocotb/share/makefiles/simulators/Makefile.cvc +0 -94
- cocotb/share/makefiles/simulators/Makefile.icarus +0 -111
- cocotb/share/makefiles/simulators/Makefile.ius +0 -125
- cocotb/share/makefiles/simulators/Makefile.modelsim +0 -32
- cocotb/share/makefiles/simulators/Makefile.nvc +0 -64
- cocotb/share/makefiles/simulators/Makefile.vcs +0 -98
- cocotb/types/array.py +0 -309
- cocotb/types/logic.py +0 -292
- cocotb/types/logic_array.py +0 -298
- cocotb/wavedrom.py +0 -199
- cocotb/xunit_reporter.py +0 -80
- cocotb-1.9.2.dist-info/METADATA +0 -168
- cocotb-1.9.2.dist-info/RECORD +0 -121
- cocotb-1.9.2.dist-info/entry_points.txt +0 -2
- /cocotb/{_vendor/__init__.py → py.typed} +0 -0
- {cocotb → cocotb_tools}/_vendor/distutils_version.py +0 -0
cocotb/types/__init__.py
CHANGED
|
@@ -1,50 +1,31 @@
|
|
|
1
1
|
# Copyright cocotb contributors
|
|
2
2
|
# Licensed under the Revised BSD License, see LICENSE for details.
|
|
3
3
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
from .
|
|
7
|
-
from .
|
|
8
|
-
from .
|
|
9
|
-
from .
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
""
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
elif type_a is not type_b:
|
|
33
|
-
# normal call order
|
|
34
|
-
call_order = [(a, a_concat, b), (b, b_rconcat, a)]
|
|
35
|
-
else:
|
|
36
|
-
# types are the same, we expect implementation of 'concat(a, b)' to be in 'a.__concat__'
|
|
37
|
-
call_order = [(a, a_concat, b)]
|
|
38
|
-
|
|
39
|
-
for lhs, method, rhs in call_order:
|
|
40
|
-
if method is MISSING:
|
|
41
|
-
continue
|
|
42
|
-
res = method(lhs, rhs)
|
|
43
|
-
if res is not NotImplemented:
|
|
44
|
-
return res
|
|
45
|
-
|
|
46
|
-
raise TypeError(
|
|
47
|
-
"cannot concatenate {!r} with {!r}".format(
|
|
48
|
-
type_a.__qualname__, type_b.__qualname__
|
|
49
|
-
)
|
|
50
|
-
)
|
|
4
|
+
from ._abstract_array import AbstractArray, AbstractMutableArray
|
|
5
|
+
from ._array import Array
|
|
6
|
+
from ._indexing import IndexingChangedWarning
|
|
7
|
+
from ._logic import Bit, Logic
|
|
8
|
+
from ._logic_array import LogicArray
|
|
9
|
+
from ._range import Range
|
|
10
|
+
|
|
11
|
+
# isort: split
|
|
12
|
+
# These are imports for doctests in the submodules. Since we fix up the `__module__`
|
|
13
|
+
# attribute, `--doctest-modules` thinks this is the module the types were defined in
|
|
14
|
+
# and will evaluate this module first before running tests.
|
|
15
|
+
from typing import Tuple # noqa: F401
|
|
16
|
+
|
|
17
|
+
__all__ = (
|
|
18
|
+
"AbstractArray",
|
|
19
|
+
"AbstractMutableArray",
|
|
20
|
+
"Array",
|
|
21
|
+
"Bit",
|
|
22
|
+
"IndexingChangedWarning",
|
|
23
|
+
"Logic",
|
|
24
|
+
"LogicArray",
|
|
25
|
+
"Range",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Set __module__ on re-exports
|
|
29
|
+
for name in __all__:
|
|
30
|
+
obj = globals()[name]
|
|
31
|
+
obj.__module__ = __name__
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Copyright cocotb contributors
|
|
2
|
+
# Licensed under the Revised BSD License, see LICENSE for details.
|
|
3
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
4
|
+
from abc import abstractmethod
|
|
5
|
+
from typing import Generic, Iterable, Iterator, Optional, TypeVar, Union, overload
|
|
6
|
+
|
|
7
|
+
from cocotb.types._range import Range
|
|
8
|
+
|
|
9
|
+
T_co = TypeVar("T_co", covariant=True)
|
|
10
|
+
T = TypeVar("T")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AbstractArray(Generic[T_co]):
|
|
14
|
+
r"""Abstract base class for non-mutating Array-like collections.
|
|
15
|
+
|
|
16
|
+
Arrays are similar to :class:`~collections.abc.Sequence`\ s,
|
|
17
|
+
but their size cannot change after creation and they support arbitrary indexing schemes.
|
|
18
|
+
|
|
19
|
+
Abstract methods
|
|
20
|
+
^^^^^^^^^^^^^^^^
|
|
21
|
+
|
|
22
|
+
* :attr:`range`
|
|
23
|
+
* :meth:`!__getitem__`
|
|
24
|
+
|
|
25
|
+
Mixin methods
|
|
26
|
+
^^^^^^^^^^^^^
|
|
27
|
+
|
|
28
|
+
* :attr:`left`, :attr:`right`, and :attr:`direction`
|
|
29
|
+
* :meth:`!__len__`
|
|
30
|
+
* :meth:`!__iter__` and :meth:`!__reversed__`
|
|
31
|
+
* :meth:`!__contains__`
|
|
32
|
+
* :meth:`index` and :meth:`count`
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def left(self) -> int:
|
|
37
|
+
"""Leftmost index of the array."""
|
|
38
|
+
return self.range.left
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def direction(self) -> str:
|
|
42
|
+
"""``"to"`` if indexes are ascending, ``"downto"`` otherwise."""
|
|
43
|
+
return self.range.direction
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def right(self) -> int:
|
|
47
|
+
"""Rightmost index of the array."""
|
|
48
|
+
return self.range.right
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
@abstractmethod
|
|
52
|
+
def range(self) -> Range:
|
|
53
|
+
""":class:`Range` of the indexes of the array."""
|
|
54
|
+
|
|
55
|
+
@range.setter
|
|
56
|
+
@abstractmethod
|
|
57
|
+
def range(self, new_range: Range) -> None:
|
|
58
|
+
"""Set a new indexing scheme on the array.
|
|
59
|
+
|
|
60
|
+
Must be the same size.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __len__(self) -> int:
|
|
64
|
+
return len(self.range)
|
|
65
|
+
|
|
66
|
+
def __iter__(self) -> Iterator[T_co]:
|
|
67
|
+
for i in self.range:
|
|
68
|
+
yield self[i]
|
|
69
|
+
|
|
70
|
+
def __reversed__(self) -> Iterator[T_co]:
|
|
71
|
+
for i in reversed(self.range):
|
|
72
|
+
yield self[i]
|
|
73
|
+
|
|
74
|
+
def __contains__(self, item: object) -> bool:
|
|
75
|
+
return any(v == item for v in self)
|
|
76
|
+
|
|
77
|
+
@overload
|
|
78
|
+
def __getitem__(self, item: int) -> T_co: ...
|
|
79
|
+
|
|
80
|
+
@overload
|
|
81
|
+
def __getitem__(self, item: slice) -> "AbstractArray[T_co]": ...
|
|
82
|
+
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def __getitem__(
|
|
85
|
+
self, item: Union[int, slice]
|
|
86
|
+
) -> Union[T_co, "AbstractArray[T_co]"]: ...
|
|
87
|
+
|
|
88
|
+
def index(
|
|
89
|
+
self,
|
|
90
|
+
value: object,
|
|
91
|
+
start: Optional[int] = None,
|
|
92
|
+
stop: Optional[int] = None,
|
|
93
|
+
) -> int:
|
|
94
|
+
"""Find first occurrence of value.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
value: Value to search for.
|
|
98
|
+
start: Index to start search at.
|
|
99
|
+
stop: Index to stop search at.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Index of first occurrence of *value*.
|
|
103
|
+
|
|
104
|
+
Raises:
|
|
105
|
+
ValueError: If the value is not present.
|
|
106
|
+
"""
|
|
107
|
+
if start is None:
|
|
108
|
+
start = self.left
|
|
109
|
+
if stop is None:
|
|
110
|
+
stop = self.right
|
|
111
|
+
for i in Range(start, self.direction, stop):
|
|
112
|
+
if self[i] == value:
|
|
113
|
+
return i
|
|
114
|
+
raise IndexError(f"{value!r} not in array")
|
|
115
|
+
|
|
116
|
+
def count(self, value: object) -> int:
|
|
117
|
+
"""Return number of occurrences of value.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
value: Value to search for.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Number of occurrences of *value*.
|
|
124
|
+
"""
|
|
125
|
+
count: int = 0
|
|
126
|
+
for v in self:
|
|
127
|
+
if v == value:
|
|
128
|
+
count += 1
|
|
129
|
+
return count
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class AbstractMutableArray(AbstractArray[T]):
|
|
133
|
+
"""Abstract base class for mutating Array-like collections.
|
|
134
|
+
|
|
135
|
+
See :class:`.AbstractArray` for more details.
|
|
136
|
+
|
|
137
|
+
Additional abstract methods:
|
|
138
|
+
|
|
139
|
+
* :meth:`!__setitem__`
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
@overload
|
|
143
|
+
def __setitem__(self, item: int, value: T) -> None: ...
|
|
144
|
+
|
|
145
|
+
@overload
|
|
146
|
+
def __setitem__(self, item: slice, value: Iterable[T]) -> None: ...
|
|
147
|
+
|
|
148
|
+
@abstractmethod
|
|
149
|
+
def __setitem__(
|
|
150
|
+
self, item: Union[int, slice], value: Union[T, Iterable[T]]
|
|
151
|
+
) -> None: ...
|
cocotb/types/_array.py
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# Copyright cocotb contributors
|
|
2
|
+
# Licensed under the Revised BSD License, see LICENSE for details.
|
|
3
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
4
|
+
import copy
|
|
5
|
+
import warnings
|
|
6
|
+
from typing import Any, Dict, Iterable, Iterator, List, TypeVar, Union, cast, overload
|
|
7
|
+
|
|
8
|
+
from cocotb.types._abstract_array import AbstractMutableArray
|
|
9
|
+
from cocotb.types._indexing import IndexingChangedWarning
|
|
10
|
+
from cocotb.types._range import Range
|
|
11
|
+
|
|
12
|
+
T = TypeVar("T")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Array(AbstractMutableArray[T]):
|
|
16
|
+
r"""Fixed-size, arbitrarily-indexed, homogeneous collection type.
|
|
17
|
+
|
|
18
|
+
Arrays are similar to, but different from Python :class:`list`\ s.
|
|
19
|
+
An array can store values of any type or values of multiple types at a time, just like a :class:`!list`.
|
|
20
|
+
Array constructor values can be any Iterable.
|
|
21
|
+
|
|
22
|
+
.. code-block:: pycon3
|
|
23
|
+
|
|
24
|
+
>>> Array([1, False, "example", None])
|
|
25
|
+
Array([1, False, 'example', None], Range(0, 'to', 3))
|
|
26
|
+
|
|
27
|
+
>>> Array("Hello!")
|
|
28
|
+
Array(['H', 'e', 'l', 'l', 'o', '!'], Range(0, 'to', 5))
|
|
29
|
+
|
|
30
|
+
Unlike :class:`!list`\ s, an array's size cannot change.
|
|
31
|
+
This means they do not support methods like :meth:`~list.append` or :meth:`~list.insert`.
|
|
32
|
+
|
|
33
|
+
The indexes of an Array can start or end at any integer value, they are not limited to 0-based indexing.
|
|
34
|
+
Indexing schemes are selected using :class:`Range` objects.
|
|
35
|
+
If *range* is not given, the range ``Range(0, "to", len(value)-1)`` is used.
|
|
36
|
+
If an :class:`int` is passed for *range*, the range ``Range(0, "to", range-1)`` is used.
|
|
37
|
+
|
|
38
|
+
.. code-block:: pycon3
|
|
39
|
+
|
|
40
|
+
>>> a = Array([0, 1, 2, 3], Range(-1, "downto", -4))
|
|
41
|
+
>>> a[-1]
|
|
42
|
+
0
|
|
43
|
+
>>> a[-4]
|
|
44
|
+
3
|
|
45
|
+
|
|
46
|
+
Arrays can also have 0 length using "null" :class:`Range`\ s.
|
|
47
|
+
|
|
48
|
+
.. code-block:: pycon3
|
|
49
|
+
|
|
50
|
+
>>> Array([], Range(1, "to", 0)) # 1 to 0 is a null Range
|
|
51
|
+
Array([], Range(1, 'to', 0))
|
|
52
|
+
|
|
53
|
+
Indexing and slicing is very similar to :class:`!list`\ s, but it uses the indexing scheme specified with the *range*.
|
|
54
|
+
Unlike :class:`!list`, slicing uses an inclusive right bound, which is common in HDLs.
|
|
55
|
+
But like :class:`!list`, if a start or stop index is not specified in the slice, it is inferred as the start or end of the array.
|
|
56
|
+
Slicing an Array returns a new :class:`~cocotb.types.Array` object, whose bounds are the slice indexes.
|
|
57
|
+
|
|
58
|
+
.. code-block:: pycon3
|
|
59
|
+
|
|
60
|
+
>>> a = Array("1234abcd")
|
|
61
|
+
>>> a[7]
|
|
62
|
+
'd'
|
|
63
|
+
>>> a[2:5]
|
|
64
|
+
Array(['3', '4', 'a', 'b'], Range(2, 'to', 5))
|
|
65
|
+
>>> a[2:5] = reversed(a[2:5])
|
|
66
|
+
>>> "".join(a)
|
|
67
|
+
'12ba43cd'
|
|
68
|
+
|
|
69
|
+
>>> b = Array("1234", Range(0, -3))
|
|
70
|
+
>>> b[-2]
|
|
71
|
+
'3'
|
|
72
|
+
>>> b[-1:]
|
|
73
|
+
Array(['2', '3', '4'], Range(-1, 'downto', -3))
|
|
74
|
+
>>> b[:] = reversed(b)
|
|
75
|
+
>>> b
|
|
76
|
+
Array(['4', '3', '2', '1'], Range(0, 'downto', -3))
|
|
77
|
+
|
|
78
|
+
.. warning::
|
|
79
|
+
Arrays behave differently in certain situations than Python's built-in sequence types (:class:`list`, :class:`tuple`, etc.).
|
|
80
|
+
|
|
81
|
+
- Arrays are not necessarily 0-based and slices use inclusive right bounds,
|
|
82
|
+
so many functions that work on Python sequences by index (like :mod:`bisect`) may not work on arrays.
|
|
83
|
+
- Slice indexes must be specified in the same direction as the array and do not support specifying a "step".
|
|
84
|
+
- When setting a slice, the new value must be an iterable of the same size as the slice.
|
|
85
|
+
- Negative indexes are *not* treated as an offset from the end of the array, but are treated literally.
|
|
86
|
+
|
|
87
|
+
Arrays are equal to other arrays of the same length with the same values (structural equality).
|
|
88
|
+
Bounds do not matter for equality.
|
|
89
|
+
|
|
90
|
+
.. code-block:: pycon3
|
|
91
|
+
|
|
92
|
+
>>> a = Array([1, 1, 2, 3, 5], Range(4, "downto", 0))
|
|
93
|
+
>>> b = Array([1, 1, 2, 3, 5], Range(-2, "to", 2))
|
|
94
|
+
>>> a == b
|
|
95
|
+
True
|
|
96
|
+
|
|
97
|
+
You can change the bounds of an array by setting the :attr:`range` to a new value.
|
|
98
|
+
The new bounds must be the same length of the array.
|
|
99
|
+
|
|
100
|
+
.. code-block:: pycon3
|
|
101
|
+
|
|
102
|
+
>>> a = Array("1234")
|
|
103
|
+
>>> a.range
|
|
104
|
+
Range(0, 'to', 3)
|
|
105
|
+
>>> a.range = Range(3, "downto", 0)
|
|
106
|
+
>>> a.range
|
|
107
|
+
Range(3, 'downto', 0)
|
|
108
|
+
|
|
109
|
+
Arrays support many of the methods and semantics defined by :class:`collections.abc.Sequence`.
|
|
110
|
+
|
|
111
|
+
.. code-block:: pycon3
|
|
112
|
+
|
|
113
|
+
>>> a = Array("stuff", Range(2, "downto", -2))
|
|
114
|
+
>>> len(a)
|
|
115
|
+
5
|
|
116
|
+
>>> "t" in a
|
|
117
|
+
True
|
|
118
|
+
>>> a.index("u")
|
|
119
|
+
0
|
|
120
|
+
>>> for c in a:
|
|
121
|
+
... print(c)
|
|
122
|
+
s
|
|
123
|
+
t
|
|
124
|
+
u
|
|
125
|
+
f
|
|
126
|
+
f
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
value: Initial value for the Array.
|
|
130
|
+
range: The indexing scheme of the Array.
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
ValueError: When argument values cannot be used to construct an Array.
|
|
134
|
+
TypeError: When invalid argument types are used.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
__slots__ = ("_value", "_range", "_warn_indexing")
|
|
138
|
+
|
|
139
|
+
def __init__(
|
|
140
|
+
self, value: Iterable[T], range: Union[Range, int, None] = None
|
|
141
|
+
) -> None:
|
|
142
|
+
self._warn_indexing = False
|
|
143
|
+
self._value = list(value)
|
|
144
|
+
if range is None:
|
|
145
|
+
self._range = Range(0, "to", len(self._value) - 1)
|
|
146
|
+
else:
|
|
147
|
+
if isinstance(range, int):
|
|
148
|
+
self._range = Range(0, "to", range - 1)
|
|
149
|
+
elif isinstance(range, Range):
|
|
150
|
+
self._range = range
|
|
151
|
+
else:
|
|
152
|
+
raise TypeError(
|
|
153
|
+
f"Expected Range or int for parameter 'range', not {type(range).__qualname__}"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
if len(self._value) != len(self._range):
|
|
157
|
+
raise ValueError(
|
|
158
|
+
f"Value of length {len(self._value)!r} does not fit in {self._range!r}"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
@classmethod
|
|
162
|
+
def _from_handle(
|
|
163
|
+
cls, value: List[T], range: Range, warn_indexing: bool
|
|
164
|
+
) -> "Array[T]":
|
|
165
|
+
self = cls.__new__(cls)
|
|
166
|
+
self._warn_indexing = warn_indexing
|
|
167
|
+
self._value = value
|
|
168
|
+
self._range = range
|
|
169
|
+
return self
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def range(self) -> Range:
|
|
173
|
+
""":class:`Range` of the indexes of the array."""
|
|
174
|
+
return self._range
|
|
175
|
+
|
|
176
|
+
@range.setter
|
|
177
|
+
def range(self, new_range: Range) -> None:
|
|
178
|
+
"""Sets a new indexing scheme on the array, must be the same size"""
|
|
179
|
+
if not isinstance(new_range, Range):
|
|
180
|
+
raise TypeError("range argument must be of type 'Range'")
|
|
181
|
+
if len(new_range) != len(self):
|
|
182
|
+
raise ValueError(
|
|
183
|
+
f"{new_range!r} not the same length as old range ({self._range!r})."
|
|
184
|
+
)
|
|
185
|
+
self._range = new_range
|
|
186
|
+
|
|
187
|
+
def __iter__(self) -> Iterator[T]:
|
|
188
|
+
return iter(self._value)
|
|
189
|
+
|
|
190
|
+
def __reversed__(self) -> Iterator[T]:
|
|
191
|
+
return reversed(self._value)
|
|
192
|
+
|
|
193
|
+
def __contains__(self, item: object) -> bool:
|
|
194
|
+
return item in self._value
|
|
195
|
+
|
|
196
|
+
def __eq__(self, other: object) -> bool:
|
|
197
|
+
if isinstance(other, Array):
|
|
198
|
+
return self._value == other._value
|
|
199
|
+
elif isinstance(other, list):
|
|
200
|
+
return self._value == other
|
|
201
|
+
elif isinstance(other, tuple):
|
|
202
|
+
return tuple(self._value) == other
|
|
203
|
+
else:
|
|
204
|
+
return NotImplemented
|
|
205
|
+
|
|
206
|
+
@overload
|
|
207
|
+
def __getitem__(self, item: int) -> T: ...
|
|
208
|
+
|
|
209
|
+
@overload
|
|
210
|
+
def __getitem__(self, item: slice) -> "Array[T]": ...
|
|
211
|
+
|
|
212
|
+
def __getitem__(self, item: Union[int, slice]) -> Union[T, "Array[T]"]:
|
|
213
|
+
if isinstance(item, int):
|
|
214
|
+
if self._warn_indexing:
|
|
215
|
+
warnings.warn(
|
|
216
|
+
f"Update index {item} to {self.range[item]}",
|
|
217
|
+
IndexingChangedWarning,
|
|
218
|
+
stacklevel=2,
|
|
219
|
+
)
|
|
220
|
+
idx = self._translate_index(item)
|
|
221
|
+
return self._value[idx]
|
|
222
|
+
elif isinstance(item, slice):
|
|
223
|
+
if self._warn_indexing:
|
|
224
|
+
start = item.start if item.start is not None else 0
|
|
225
|
+
stop = item.stop if item.stop is not None else len(self) - 1
|
|
226
|
+
warnings.warn(
|
|
227
|
+
f"Update slice {start}:{stop} to {self.range[start]}:{self.range[stop]}",
|
|
228
|
+
IndexingChangedWarning,
|
|
229
|
+
stacklevel=2,
|
|
230
|
+
)
|
|
231
|
+
start = item.start if item.start is not None else self.left
|
|
232
|
+
stop = item.stop if item.stop is not None else self.right
|
|
233
|
+
if item.step is not None:
|
|
234
|
+
raise IndexError("do not specify step")
|
|
235
|
+
start_i = self._translate_index(start)
|
|
236
|
+
stop_i = self._translate_index(stop)
|
|
237
|
+
if start_i > stop_i:
|
|
238
|
+
raise IndexError(
|
|
239
|
+
f"slice [{start}:{stop}] direction does not match array direction [{self.left}:{self.right}]"
|
|
240
|
+
)
|
|
241
|
+
value = self._value[start_i : stop_i + 1]
|
|
242
|
+
range = Range(start, self.direction, stop)
|
|
243
|
+
return Array(value=value, range=range)
|
|
244
|
+
raise TypeError(f"indexes must be ints or slices, not {type(item).__name__}")
|
|
245
|
+
|
|
246
|
+
@overload
|
|
247
|
+
def __setitem__(self, item: int, value: T) -> None: ...
|
|
248
|
+
|
|
249
|
+
@overload
|
|
250
|
+
def __setitem__(self, item: slice, value: Iterable[T]) -> None: ...
|
|
251
|
+
|
|
252
|
+
def __setitem__(
|
|
253
|
+
self, item: Union[int, slice], value: Union[T, Iterable[T]]
|
|
254
|
+
) -> None:
|
|
255
|
+
if isinstance(item, int):
|
|
256
|
+
idx = self._translate_index(item)
|
|
257
|
+
self._value[idx] = cast("T", value)
|
|
258
|
+
elif isinstance(item, slice):
|
|
259
|
+
start = item.start if item.start is not None else self.left
|
|
260
|
+
stop = item.stop if item.stop is not None else self.right
|
|
261
|
+
if item.step is not None:
|
|
262
|
+
raise IndexError("do not specify step")
|
|
263
|
+
start_i = self._translate_index(start)
|
|
264
|
+
stop_i = self._translate_index(stop)
|
|
265
|
+
if start_i > stop_i:
|
|
266
|
+
raise IndexError(
|
|
267
|
+
f"slice [{start}:{stop}] direction does not match array direction [{self.left}:{self.right}]"
|
|
268
|
+
)
|
|
269
|
+
value = list(cast("Iterable[T]", value))
|
|
270
|
+
if len(value) != (stop_i - start_i + 1):
|
|
271
|
+
raise ValueError(
|
|
272
|
+
f"value of length {len(value)!r} will not fit in slice [{start}:{stop}]"
|
|
273
|
+
)
|
|
274
|
+
self._value[start_i : stop_i + 1] = value
|
|
275
|
+
else:
|
|
276
|
+
raise TypeError(
|
|
277
|
+
f"indexes must be ints or slices, not {type(item).__name__}"
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
def __repr__(self) -> str:
|
|
281
|
+
return f"{type(self).__name__}({self._value!r}, {self._range!r})"
|
|
282
|
+
|
|
283
|
+
def _translate_index(self, item: int) -> int:
|
|
284
|
+
try:
|
|
285
|
+
return self._range.index(item)
|
|
286
|
+
except ValueError:
|
|
287
|
+
raise IndexError(f"index {item} out of range") from None
|
|
288
|
+
|
|
289
|
+
def __copy__(self) -> "Array":
|
|
290
|
+
return Array(self._value, self._range)
|
|
291
|
+
|
|
292
|
+
def __deepcopy__(self, memo: Dict[int, Any]) -> "Array":
|
|
293
|
+
return Array(
|
|
294
|
+
copy.deepcopy(self._value, memo=memo), copy.deepcopy(self._range, memo=memo)
|
|
295
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Copyright cocotb contributors
|
|
2
|
+
# Licensed under the Revised BSD License, see LICENSE for details.
|
|
3
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
from cocotb.types._range import Range
|
|
8
|
+
|
|
9
|
+
do_indexing_changed_warning = os.environ.get("COCOTB_INDEXING_CHANGED_WARNING")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def indexing_changed(range: Range) -> bool:
|
|
13
|
+
return not (range.left == 0 and range.direction == "to")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class IndexingChangedWarning(UserWarning):
|
|
17
|
+
"""Warning issued when a value is indexed in a way that is different between cocotb 1.x and 2.x."""
|