cocotb 2.0.0b1__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 (95) hide show
  1. cocotb/_ANSI.py +47 -54
  2. cocotb/__init__.py +12 -2
  3. cocotb/_base_triggers.py +11 -9
  4. cocotb/_bridge.py +8 -9
  5. cocotb/_extended_awaitables.py +1 -1
  6. cocotb/_gpi_triggers.py +4 -1
  7. cocotb/_init.py +17 -11
  8. cocotb/_py_compat.py +24 -10
  9. cocotb/_scheduler.py +25 -31
  10. cocotb/_test.py +6 -3
  11. cocotb/_test_factory.py +4 -1
  12. cocotb/_utils.py +1 -23
  13. cocotb/_version.py +1 -1
  14. cocotb/clock.py +98 -16
  15. cocotb/debug.py +24 -0
  16. cocotb/handle.py +62 -32
  17. cocotb/libs/cocotb.dll +0 -0
  18. cocotb/libs/cocotb.exp +0 -0
  19. cocotb/libs/cocotb.lib +0 -0
  20. cocotb/libs/cocotbfli_modelsim.dll +0 -0
  21. cocotb/libs/cocotbfli_modelsim.exp +0 -0
  22. cocotb/libs/cocotbfli_modelsim.lib +0 -0
  23. cocotb/libs/cocotbutils.dll +0 -0
  24. cocotb/libs/cocotbutils.exp +0 -0
  25. cocotb/libs/cocotbutils.lib +0 -0
  26. cocotb/libs/cocotbvhpi_aldec.dll +0 -0
  27. cocotb/libs/cocotbvhpi_aldec.exp +0 -0
  28. cocotb/libs/cocotbvhpi_aldec.lib +0 -0
  29. cocotb/libs/cocotbvhpi_modelsim.dll +0 -0
  30. cocotb/libs/cocotbvhpi_modelsim.exp +0 -0
  31. cocotb/libs/cocotbvhpi_modelsim.lib +0 -0
  32. cocotb/libs/cocotbvpi_aldec.dll +0 -0
  33. cocotb/libs/cocotbvpi_aldec.exp +0 -0
  34. cocotb/libs/cocotbvpi_aldec.lib +0 -0
  35. cocotb/libs/cocotbvpi_ghdl.dll +0 -0
  36. cocotb/libs/cocotbvpi_ghdl.exp +0 -0
  37. cocotb/libs/cocotbvpi_ghdl.lib +0 -0
  38. cocotb/libs/cocotbvpi_icarus.exp +0 -0
  39. cocotb/libs/cocotbvpi_icarus.lib +0 -0
  40. cocotb/libs/cocotbvpi_icarus.vpl +0 -0
  41. cocotb/libs/cocotbvpi_modelsim.dll +0 -0
  42. cocotb/libs/cocotbvpi_modelsim.exp +0 -0
  43. cocotb/libs/cocotbvpi_modelsim.lib +0 -0
  44. cocotb/libs/embed.dll +0 -0
  45. cocotb/libs/embed.exp +0 -0
  46. cocotb/libs/embed.lib +0 -0
  47. cocotb/libs/gpi.dll +0 -0
  48. cocotb/libs/gpi.exp +0 -0
  49. cocotb/libs/gpi.lib +0 -0
  50. cocotb/libs/gpilog.dll +0 -0
  51. cocotb/libs/gpilog.exp +0 -0
  52. cocotb/libs/gpilog.lib +0 -0
  53. cocotb/libs/pygpilog.dll +0 -0
  54. cocotb/libs/pygpilog.exp +0 -0
  55. cocotb/libs/pygpilog.lib +0 -0
  56. cocotb/logging.py +243 -117
  57. cocotb/regression.py +43 -35
  58. cocotb/share/def/aldec.exp +0 -0
  59. cocotb/share/def/aldec.lib +0 -0
  60. cocotb/share/def/ghdl.exp +0 -0
  61. cocotb/share/def/ghdl.lib +0 -0
  62. cocotb/share/def/icarus.exp +0 -0
  63. cocotb/share/def/icarus.lib +0 -0
  64. cocotb/share/def/modelsim.exp +0 -0
  65. cocotb/share/def/modelsim.lib +0 -0
  66. cocotb/share/include/cocotb_utils.h +3 -3
  67. cocotb/share/include/embed.h +2 -2
  68. cocotb/share/include/gpi.h +258 -109
  69. cocotb/share/include/py_gpi_logging.h +3 -3
  70. cocotb/share/lib/verilator/verilator.cpp +23 -15
  71. cocotb/simtime.py +230 -0
  72. cocotb/simulator.cp313-win32.exp +0 -0
  73. cocotb/simulator.cp313-win32.lib +0 -0
  74. cocotb/simulator.cp313-win32.pyd +0 -0
  75. cocotb/task.py +54 -11
  76. cocotb/types/__init__.py +4 -1
  77. cocotb/types/_array.py +33 -2
  78. cocotb/types/_indexing.py +17 -0
  79. cocotb/types/_logic.py +96 -59
  80. cocotb/types/_logic_array.py +56 -22
  81. cocotb/types/_range.py +12 -5
  82. cocotb/utils.py +9 -129
  83. {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/METADATA +1 -1
  84. cocotb-2.0.0rc2.dist-info/RECORD +146 -0
  85. {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/licenses/LICENSE +1 -0
  86. cocotb_tools/config.py +5 -5
  87. cocotb_tools/makefiles/Makefile.inc +3 -9
  88. cocotb_tools/makefiles/Makefile.sim +9 -1
  89. cocotb_tools/makefiles/simulators/Makefile.vcs +1 -1
  90. cocotb_tools/runner.py +94 -59
  91. pygpi/entry.py +5 -6
  92. cocotb-2.0.0b1.dist-info/RECORD +0 -143
  93. {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/WHEEL +0 -0
  94. {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/entry_points.txt +0 -0
  95. {cocotb-2.0.0b1.dist-info → cocotb-2.0.0rc2.dist-info}/top_level.txt +0 -0
cocotb/logging.py CHANGED
@@ -10,74 +10,125 @@ Everything related to logging
10
10
 
11
11
  import logging
12
12
  import os
13
+ import re
13
14
  import sys
15
+ import time
16
+ import warnings
14
17
  from functools import wraps
15
18
  from pathlib import Path
16
- from typing import Union
19
+ from typing import Optional, Tuple, Union
17
20
 
18
- from cocotb import _ANSI, simulator
21
+ import cocotb.simtime
22
+ from cocotb import simulator
23
+ from cocotb._ANSI import ANSI
19
24
  from cocotb._deprecation import deprecated
20
- from cocotb._utils import want_color_output
21
- from cocotb.utils import get_sim_time, get_time_from_sim_steps
25
+ from cocotb.simtime import get_sim_time
26
+ from cocotb.utils import get_time_from_sim_steps
22
27
 
23
- try:
24
- _suppress = int(os.environ.get("COCOTB_REDUCED_LOG_FMT", "1"))
25
- except ValueError:
26
- _suppress = 1
28
+ __all__ = (
29
+ "ANSI",
30
+ "SimLog",
31
+ "SimLogFormatter",
32
+ "SimTimeContextFilter",
33
+ "default_config",
34
+ "strip_ansi",
35
+ )
27
36
 
28
- # Column alignment
29
- _LEVEL_CHARS = len("CRITICAL")
30
- _RECORD_CHARS = 34
31
- _FILENAME_CHARS = 20
32
- _LINENO_CHARS = 4
33
- _FUNCNAME_CHARS = 31
37
+ ANSI.__module__ = __name__
34
38
 
35
39
  # Custom log level
36
40
  logging.TRACE = 5 # type: ignore[attr-defined] # type checkers don't like adding module attributes after the fact
37
41
  logging.addLevelName(5, "TRACE")
38
42
 
39
43
 
40
- __all__ = (
41
- "SimColourLogFormatter",
42
- "SimLog",
43
- "SimLogFormatter",
44
- "SimTimeContextFilter",
45
- "default_config",
46
- )
44
+ strip_ansi: bool = False
45
+ """Whether the default formatter should strip ANSI (color) escape codes from log messages.
46
+
47
+ Defaults to ``True`` if ``stdout`` is not a TTY and ``False`` otherwise;
48
+ but can be overridden with the :envvar:`NO_COLOR` or :envvar:`COCOTB_ANSI_OUTPUT` environment variable.
49
+ """
47
50
 
48
51
 
49
- def default_config() -> None:
52
+ def default_config(
53
+ reduced_log_fmt: bool = True,
54
+ strip_ansi: Optional[bool] = None,
55
+ prefix_format: Optional[str] = None,
56
+ ) -> None:
50
57
  """Apply the default cocotb log formatting to the root logger.
51
58
 
52
- This hooks up the logger to write to stdout, using either
53
- :class:`SimColourLogFormatter` or :class:`SimLogFormatter` depending
54
- on whether colored output is requested. It also adds a
55
- :class:`SimTimeContextFilter` filter so that
56
- :attr:`~logging.LogRecord.created_sim_time` is available to the formatter.
59
+ This hooks up the logger to write to stdout, using :class:`SimLogFormatter` for formatting.
60
+ It also adds a :class:`SimTimeContextFilter` filter so that the
61
+ :attr:`~logging.LogRecord.created_sim_time` attribute on :class:`~logging.LogRecord`
62
+ is available to the formatter.
57
63
 
58
64
  If desired, this logging configuration can be overwritten by calling
59
- ``logging.basicConfig(..., force=True)`` (in Python 3.8 onwards), or by
60
- manually resetting the root logger instance.
65
+ ``logging.basicConfig(..., force=True)`` (in Python 3.8 onwards),
66
+ or by manually resetting the root logger instance.
61
67
  An example of this can be found in the section on :ref:`rotating-logger`.
62
68
 
69
+ Args:
70
+ reduced_log_fmt:
71
+ If ``True``, use a reduced log format that does not include the
72
+ filename, line number, and function name in the log prefix.
73
+
74
+ .. versionadded:: 2.0
75
+
76
+ strip_ansi:
77
+ If ``True``, strip ANSI (color) escape codes in log messages.
78
+ If ``False``, do not strip ANSI escape codes in log messages.
79
+ If ``None``, use the value of :data:`strip_ansi`.
80
+
81
+ .. versionadded:: 2.0
82
+
83
+ prefix_format:
84
+ An f-string to build a prefix for each log message.
85
+
86
+ .. versionadded:: 2.0
87
+
63
88
  .. versionadded:: 1.4
64
89
 
65
90
  .. versionchanged:: 2.0
66
- No longer set the log level of the ``cocotb`` and ``gpi`` loggers.
91
+ Now captures warnings and outputs them through the logging system using
92
+ :func:`logging.captureWarnings`.
67
93
  """
94
+ logging.basicConfig()
95
+
68
96
  hdlr = logging.StreamHandler(sys.stdout)
69
97
  hdlr.addFilter(SimTimeContextFilter())
70
- if want_color_output():
71
- hdlr.setFormatter(SimColourLogFormatter())
72
- else:
73
- hdlr.setFormatter(SimLogFormatter())
74
-
75
- logging.basicConfig()
98
+ hdlr.setFormatter(
99
+ SimLogFormatter(
100
+ reduced_log_fmt=reduced_log_fmt,
101
+ strip_ansi=strip_ansi,
102
+ prefix_format=prefix_format,
103
+ )
104
+ )
76
105
  logging.getLogger().handlers = [hdlr] # overwrite default handlers
77
106
 
107
+ logging.getLogger("cocotb").setLevel(logging.INFO)
108
+ logging.getLogger("gpi").setLevel(logging.INFO)
78
109
 
79
- def _init(_: object) -> None:
80
- """Set cocotb and pygpi log levels."""
110
+ logging.captureWarnings(True)
111
+
112
+
113
+ def _init() -> None:
114
+ """cocotb-specific logging setup.
115
+
116
+ - Decides whether ANSI escape code stripping is desired by checking
117
+ :envvar:`NO_COLOR` and :envvar:`COCOTB_ANSI_OUTPUT`.
118
+ - Initializes the GPI logger and sets up the GPI logging optimization.
119
+ - Sets the log level of the ``"cocotb"`` and ``"gpi"`` loggers based on
120
+ :envvar:`COCOTB_LOG_LEVEL` and :envvar:`GPI_LOG_LEVEL`, respectively.
121
+ """
122
+ global strip_ansi
123
+ strip_ansi = not sys.stdout.isatty() # default to color for TTYs
124
+ if os.getenv("NO_COLOR", ""):
125
+ strip_ansi = True
126
+ ansi_output = os.getenv("COCOTB_ANSI_OUTPUT")
127
+ if ansi_output is not None:
128
+ strip_ansi = not int(ansi_output)
129
+ in_gui = os.getenv("GUI")
130
+ if in_gui is not None:
131
+ strip_ansi = bool(int(in_gui))
81
132
 
82
133
  # Monkeypatch "gpi" logger with function that also sets a PyGPI-local logger level
83
134
  # as an optimization.
@@ -95,8 +146,11 @@ def _init(_: object) -> None:
95
146
  simulator.initialize_logger(_log_from_c, logging.getLogger)
96
147
 
97
148
  # Set "cocotb" and "gpi" logger based on environment variables
98
- def set_level(logger_name: str, envvar: str, default_level: str) -> None:
99
- log_level = os.environ.get(envvar, default_level)
149
+ def set_level(logger_name: str, envvar: str) -> None:
150
+ log_level = os.environ.get(envvar)
151
+ if log_level is None:
152
+ return
153
+
100
154
  log_level = log_level.upper()
101
155
 
102
156
  logger = logging.getLogger(logger_name)
@@ -113,18 +167,24 @@ def _init(_: object) -> None:
113
167
  f"levels: {valid_levels}"
114
168
  ) from None
115
169
 
116
- set_level("gpi", "GPI_LOG_LEVEL", "INFO")
117
- set_level("cocotb", "COCOTB_LOG_LEVEL", "INFO")
170
+ set_level("gpi", "GPI_LOG_LEVEL")
171
+ set_level("cocotb", "COCOTB_LOG_LEVEL")
118
172
 
119
173
 
120
- def _setup_formatter(_: object) -> None:
121
- """Setup cocotb's logging formatter."""
122
- default_config()
174
+ def _configure(_: object) -> None:
175
+ """Configure basic logging."""
176
+ reduced_log_fmt = True
177
+ try:
178
+ reduced_log_fmt = bool(int(os.environ.get("COCOTB_REDUCED_LOG_FMT", "1")))
179
+ except ValueError:
180
+ pass
181
+ prefix_format = os.environ.get("COCOTB_LOG_PREFIX", None)
182
+ default_config(reduced_log_fmt=reduced_log_fmt, prefix_format=prefix_format)
123
183
 
124
184
 
125
185
  @deprecated('Use `logging.getLogger(f"{name}.0x{ident:x}")` instead')
126
186
  def SimLog(name: str, ident: Union[int, None] = None) -> logging.Logger:
127
- """Like logging.getLogger, but append a numeric identifier to the name.
187
+ """Like :func:`logging.getLogger`, but append a numeric identifier to the name.
128
188
 
129
189
  Args:
130
190
  name: Logger name.
@@ -146,18 +206,12 @@ class SimTimeContextFilter(logging.Filter):
146
206
  """
147
207
  A filter to inject simulator times into the log records.
148
208
 
149
- This uses the approach described in the :ref:`Python logging cookbook <python:filters-contextual>`.
150
-
151
- This adds the :attr:`~logging.LogRecord.created_sim_time` attribute.
209
+ This uses the approach described in the :ref:`Python logging cookbook <python:filters-contextual>`
210
+ which adds the :attr:`~logging.LogRecord.created_sim_time` attribute.
152
211
 
153
212
  .. versionadded:: 1.4
154
213
  """
155
214
 
156
- # needed to make our docs render well
157
- def __init__(self) -> None:
158
- """"""
159
- super().__init__()
160
-
161
215
  def filter(self, record: logging.LogRecord) -> bool:
162
216
  try:
163
217
  record.created_sim_time = get_sim_time()
@@ -174,13 +228,65 @@ class SimLogFormatter(logging.Formatter):
174
228
  This will only add simulator timestamps if the handler object this
175
229
  formatter is attached to has a :class:`SimTimeContextFilter` filter
176
230
  attached, which cocotb ensures by default.
231
+
232
+ See :func:`.default_config` for a description of the arguments.
177
233
  """
178
234
 
179
- # Removes the arguments from the base class. Docstring needed to make
180
- # sphinx happy.
181
- def __init__(self) -> None:
182
- """Takes no arguments."""
183
- super().__init__()
235
+ loglevel2colour = {
236
+ logging.TRACE: "", # type: ignore[attr-defined] # type checkers don't like adding module attributes after the fact
237
+ logging.DEBUG: "",
238
+ logging.INFO: "",
239
+ logging.WARNING: ANSI.YELLOW_FG,
240
+ logging.ERROR: ANSI.RED_FG,
241
+ logging.CRITICAL: ANSI.RED_BG + ANSI.BLACK_FG,
242
+ }
243
+
244
+ prefix_func_globals = {
245
+ "time": time,
246
+ "simtime": cocotb.simtime,
247
+ "ANSI": ANSI,
248
+ }
249
+
250
+ def __init__(
251
+ self,
252
+ *,
253
+ reduced_log_fmt: bool = True,
254
+ strip_ansi: Union[bool, None] = None,
255
+ prefix_format: Optional[str] = None,
256
+ ) -> None:
257
+ self._reduced_log_fmt = reduced_log_fmt
258
+ self._strip_ansi = strip_ansi
259
+ self._prefix_func = (
260
+ eval(
261
+ f"lambda record: f'''{prefix_format}'''",
262
+ type(self).prefix_func_globals,
263
+ )
264
+ if prefix_format is not None
265
+ else None
266
+ )
267
+ self._ansi_escape_pattern = re.compile(
268
+ r"""
269
+ (?: # either 7-bit C1, two bytes, ESC Fe (omitting CSI)
270
+ \x1B
271
+ [@-Z\\-_]
272
+ | # or a single 8-bit byte Fe (omitting CSI)
273
+ [\x80-\x9A\x9C-\x9F]
274
+ | # or CSI + control codes
275
+ (?: # 7-bit CSI, ESC [
276
+ \x1B\[
277
+ | # 8-bit CSI, 9B
278
+ \x9B
279
+ )
280
+ [0-?]* # Parameter bytes
281
+ [ -/]* # Intermediate bytes
282
+ [@-~] # Final byte
283
+ )
284
+ """,
285
+ re.VERBOSE,
286
+ )
287
+
288
+ def strip_ansi(self) -> bool:
289
+ return strip_ansi if self._strip_ansi is None else self._strip_ansi
184
290
 
185
291
  # Justify and truncate
186
292
  @staticmethod
@@ -195,88 +301,108 @@ class SimLogFormatter(logging.Formatter):
195
301
  return ".." + string[(chars - 2) * -1 :]
196
302
  return string.rjust(chars)
197
303
 
198
- def _format(
199
- self, level: str, record: logging.LogRecord, msg: str, coloured: bool = False
200
- ) -> str:
304
+ def formatPrefix(self, record: logging.LogRecord) -> Tuple[str, int]:
201
305
  sim_time = getattr(record, "created_sim_time", None)
202
306
  if sim_time is None:
203
307
  sim_time_str = " -.--ns"
204
308
  else:
205
309
  time_ns = get_time_from_sim_steps(sim_time, "ns")
206
310
  sim_time_str = f"{time_ns:6.2f}ns"
207
- prefix = (
208
- sim_time_str.rjust(11)
209
- + " "
210
- + level
211
- + " "
212
- + self.ljust(record.name, _RECORD_CHARS)
213
- + " "
214
- )
215
- if not _suppress:
216
- prefix += (
217
- self.rjust(Path(record.filename).name, _FILENAME_CHARS)
218
- + ":"
219
- + self.ljust(str(record.lineno), _LINENO_CHARS)
220
- + " in "
221
- + self.ljust(str(record.funcName), _FUNCNAME_CHARS)
222
- + " "
223
- )
224
311
 
225
- # these lines are copied from the built-in logger
312
+ if self.strip_ansi():
313
+ highlight_start = ""
314
+ highlight_end = ""
315
+ else:
316
+ highlight_start = self.loglevel2colour.get(record.levelno, "")
317
+ highlight_end = ANSI.DEFAULT
318
+
319
+ if self._prefix_func is not None:
320
+ prefix = self._prefix_func(record)
321
+ stripped_prefix = self._ansi_escape_pattern.sub("", prefix)
322
+ prefix_len = len(stripped_prefix)
323
+ return prefix, prefix_len
324
+ else:
325
+ prefix = f"{sim_time_str:>11} {highlight_start}{record.levelname:<8}{highlight_end} {self.ljust(record.name, 34)} "
326
+ if not self._reduced_log_fmt:
327
+ prefix = f"{prefix}{self.rjust(Path(record.filename).name, 20)}:{record.lineno:<4} in {self.ljust(str(record.funcName), 31)} "
328
+
329
+ prefix_len = len(prefix) - len(highlight_start) - len(highlight_end)
330
+ return prefix, prefix_len
331
+
332
+ def formatMessage(self, record: logging.LogRecord) -> str:
333
+ msg = record.getMessage()
334
+
335
+ if self.strip_ansi():
336
+ highlight_start = ""
337
+ highlight_end = ""
338
+ msg = self._ansi_escape_pattern.sub("", msg)
339
+ else:
340
+ highlight_start = self.loglevel2colour.get(record.levelno, "")
341
+ highlight_end = ANSI.DEFAULT
342
+
343
+ msg = f"{highlight_start}{msg}{highlight_end}"
344
+
345
+ return msg
346
+
347
+ def formatExcInfo(self, record: logging.LogRecord) -> str:
348
+ msg = ""
349
+
350
+ # these lines are copied and updated from the built-in logger
226
351
  if record.exc_info:
227
352
  # Cache the traceback text to avoid converting it multiple times
228
353
  # (it's constant anyway)
229
354
  if not record.exc_text:
230
355
  record.exc_text = self.formatException(record.exc_info)
231
356
  if record.exc_text:
232
- if msg[-1:] != "\n":
233
- msg = msg + "\n"
234
- msg = msg + record.exc_text
357
+ msg += record.exc_text
358
+ if record.stack_info:
359
+ if not msg.endswith("\n"):
360
+ msg += "\n"
361
+ msg += self.formatStack(record.stack_info)
235
362
 
236
- prefix_len = len(prefix)
237
- if coloured:
238
- prefix_len -= len(level) - _LEVEL_CHARS
239
- pad = "\n" + " " * (prefix_len)
240
- return prefix + pad.join(msg.split("\n"))
363
+ return msg
241
364
 
242
365
  def format(self, record: logging.LogRecord) -> str:
243
- """Prettify the log output by annotating with simulation time."""
366
+ prefix, prefix_len = self.formatPrefix(record)
367
+ msg = self.formatMessage(record)
368
+ exc_info = self.formatExcInfo(record)
244
369
 
245
- msg = record.getMessage()
246
- level = record.levelname.ljust(_LEVEL_CHARS)
370
+ lines = msg.splitlines()
371
+ lines.extend(exc_info.splitlines())
247
372
 
248
- return self._format(level, record, msg)
373
+ # add prefix to first line of message
374
+ lines[0] = prefix + lines[0]
249
375
 
376
+ # add padding to each line of message
377
+ pad = "\n" + " " * prefix_len
378
+ return pad.join(lines)
250
379
 
251
- class SimColourLogFormatter(SimLogFormatter):
252
- """Log formatter to provide consistent log message handling."""
253
380
 
254
- loglevel2colour = {
255
- logging.TRACE: "%s", # type: ignore[attr-defined] # type checkers don't like adding module attributes after the fact
256
- logging.DEBUG: "%s",
257
- logging.INFO: "%s",
258
- logging.WARNING: _ANSI.COLOR_WARNING + "%s" + _ANSI.COLOR_DEFAULT,
259
- logging.ERROR: _ANSI.COLOR_ERROR + "%s" + _ANSI.COLOR_DEFAULT,
260
- logging.CRITICAL: _ANSI.COLOR_CRITICAL + "%s" + _ANSI.COLOR_DEFAULT,
261
- }
262
-
263
- def format(self, record: logging.LogRecord) -> str:
264
- """Prettify the log output by annotating with simulation time."""
381
+ class SimColourLogFormatter(SimLogFormatter):
382
+ """Log formatter similar to :class:`SimLogFormatter`, but with colored output by default.
265
383
 
266
- msg = record.getMessage()
384
+ .. deprecated:: 2.0
385
+ Use :class:`!SimLogFormatter` with ``strip_ansi=False`` instead.
386
+ """
267
387
 
268
- # Need to colour each line in case coloring is applied in the message
269
- msg = "\n".join(
270
- [
271
- SimColourLogFormatter.loglevel2colour.get(record.levelno, "%s") % line
272
- for line in msg.split("\n")
273
- ]
388
+ def __init__(
389
+ self,
390
+ *,
391
+ reduced_log_fmt: bool = True,
392
+ strip_ansi: Union[bool, None] = False,
393
+ prefix_format: Optional[str] = None,
394
+ ) -> None:
395
+ warnings.warn(
396
+ "SimColourLogFormatter is deprecated and will be removed in a future release. "
397
+ "Use SimLogFormatter with `strip_ansi=False` instead.",
398
+ DeprecationWarning,
399
+ stacklevel=2,
400
+ )
401
+ super().__init__(
402
+ reduced_log_fmt=reduced_log_fmt,
403
+ strip_ansi=strip_ansi,
404
+ prefix_format=prefix_format,
274
405
  )
275
- level = SimColourLogFormatter.loglevel2colour.get(
276
- record.levelno, "%s"
277
- ) % record.levelname.ljust(_LEVEL_CHARS)
278
-
279
- return self._format(level, record, msg, coloured=True)
280
406
 
281
407
 
282
408
  def _log_from_c(
cocotb/regression.py CHANGED
@@ -18,6 +18,7 @@ import warnings
18
18
  from enum import auto
19
19
  from importlib import import_module
20
20
  from typing import (
21
+ TYPE_CHECKING,
21
22
  Callable,
22
23
  Coroutine,
23
24
  List,
@@ -27,10 +28,10 @@ from typing import (
27
28
  import cocotb
28
29
  import cocotb._gpi_triggers
29
30
  import cocotb.handle
30
- from cocotb import _ANSI, simulator
31
- from cocotb._base_triggers import Trigger
31
+ from cocotb import logging as cocotb_logging
32
+ from cocotb import simulator
32
33
  from cocotb._decorators import Parameterized, Test
33
- from cocotb._extended_awaitables import SimTimeoutError, with_timeout
34
+ from cocotb._extended_awaitables import with_timeout
34
35
  from cocotb._gpi_triggers import GPITrigger, Timer
35
36
  from cocotb._outcomes import Error, Outcome
36
37
  from cocotb._test import RunningTest
@@ -40,11 +41,14 @@ from cocotb._utils import (
40
41
  DocEnum,
41
42
  remove_traceback_frames,
42
43
  safe_divide,
43
- want_color_output,
44
44
  )
45
45
  from cocotb._xunit_reporter import XUnitReporter
46
+ from cocotb.logging import ANSI
47
+ from cocotb.simtime import get_sim_time
46
48
  from cocotb.task import Task
47
- from cocotb.utils import get_sim_time
49
+
50
+ if TYPE_CHECKING:
51
+ from cocotb._base_triggers import Trigger
48
52
 
49
53
  __all__ = (
50
54
  "Parameterized",
@@ -62,7 +66,12 @@ TestFactory.__module__ = __name__
62
66
 
63
67
 
64
68
  class SimFailure(BaseException):
65
- """A Test failure due to simulator failure."""
69
+ """A Test failure due to simulator failure.
70
+
71
+ .. caution::
72
+ Not to be raised or caught within a test.
73
+ Only used for marking expected failure with ``expect_error`` in :func:`cocotb.test`.
74
+ """
66
75
 
67
76
 
68
77
  _logger = logging.getLogger(__name__)
@@ -128,6 +137,11 @@ class RegressionManager:
128
137
  :attr:`skipped`, and :attr:`failures` hold placeholder values.
129
138
  """
130
139
 
140
+ COLOR_TEST = ANSI.BLUE_FG
141
+ COLOR_PASSED = ANSI.GREEN_FG
142
+ COLOR_SKIPPED = ANSI.YELLOW_FG
143
+ COLOR_FAILED = ANSI.RED_FG
144
+
131
145
  _timer1 = Timer(1)
132
146
 
133
147
  def __init__(self) -> None:
@@ -365,13 +379,7 @@ class RegressionManager:
365
379
 
366
380
  @functools.wraps(f)
367
381
  async def func(*args: object, **kwargs: object) -> None:
368
- running_co = Task(f(*args, **kwargs))
369
-
370
- try:
371
- await with_timeout(running_co, timeout, self._test.timeout_unit)
372
- except SimTimeoutError:
373
- running_co.cancel()
374
- raise
382
+ await with_timeout(f(*args, **kwargs), timeout, self._test.timeout_unit)
375
383
  else:
376
384
  func = self._test.func
377
385
 
@@ -539,8 +547,8 @@ class RegressionManager:
539
547
 
540
548
  def _log_test_start(self) -> None:
541
549
  """Called by :meth:`_execute` to log that a test is starting."""
542
- hilight_start = _ANSI.COLOR_TEST if want_color_output() else ""
543
- hilight_end = _ANSI.COLOR_DEFAULT if want_color_output() else ""
550
+ hilight_start = "" if cocotb_logging.strip_ansi else self.COLOR_TEST
551
+ hilight_end = "" if cocotb_logging.strip_ansi else ANSI.DEFAULT
544
552
  self.log.info(
545
553
  "%srunning%s %s (%d/%d)%s",
546
554
  hilight_start,
@@ -573,8 +581,8 @@ class RegressionManager:
573
581
  """Called by :meth:`_execute` when a test is skipped."""
574
582
 
575
583
  # log test results
576
- hilight_start = _ANSI.COLOR_SKIPPED if want_color_output() else ""
577
- hilight_end = _ANSI.COLOR_DEFAULT if want_color_output() else ""
584
+ hilight_start = "" if cocotb_logging.strip_ansi else self.COLOR_SKIPPED
585
+ hilight_end = "" if cocotb_logging.strip_ansi else ANSI.DEFAULT
578
586
  self.log.info(
579
587
  "%sskipping%s %s (%d/%d)%s",
580
588
  hilight_start,
@@ -616,8 +624,8 @@ class RegressionManager:
616
624
  """Called by :meth:`_execute` when a test initialization fails."""
617
625
 
618
626
  # log test results
619
- hilight_start = _ANSI.COLOR_FAILED if want_color_output() else ""
620
- hilight_end = _ANSI.COLOR_DEFAULT if want_color_output() else ""
627
+ hilight_start = "" if cocotb_logging.strip_ansi else self.COLOR_FAILED
628
+ hilight_end = "" if cocotb_logging.strip_ansi else ANSI.DEFAULT
621
629
  self.log.exception(
622
630
  "%sFailed to initialize%s %s! (%d/%d)%s",
623
631
  hilight_start,
@@ -662,8 +670,8 @@ class RegressionManager:
662
670
  result: Union[Exception, None],
663
671
  msg: Union[str, None],
664
672
  ) -> None:
665
- start_hilight = _ANSI.COLOR_PASSED if want_color_output() else ""
666
- stop_hilight = _ANSI.COLOR_DEFAULT if want_color_output() else ""
673
+ start_hilight = "" if cocotb_logging.strip_ansi else self.COLOR_PASSED
674
+ stop_hilight = "" if cocotb_logging.strip_ansi else ANSI.DEFAULT
667
675
  if msg is None:
668
676
  rest = ""
669
677
  else:
@@ -715,8 +723,8 @@ class RegressionManager:
715
723
  result: Union[BaseException, None],
716
724
  msg: Union[str, None],
717
725
  ) -> None:
718
- start_hilight = _ANSI.COLOR_FAILED if want_color_output() else ""
719
- stop_hilight = _ANSI.COLOR_DEFAULT if want_color_output() else ""
726
+ start_hilight = "" if cocotb_logging.strip_ansi else self.COLOR_FAILED
727
+ stop_hilight = "" if cocotb_logging.strip_ansi else ANSI.DEFAULT
720
728
  if msg is None:
721
729
  rest = ""
722
730
  else:
@@ -821,28 +829,28 @@ class RegressionManager:
821
829
  summary += LINE_SEP
822
830
 
823
831
  test_line = "** {a:<{a_len}} {start}{b:^{b_len}}{end} {c:>{c_len}.2f} {d:>{d_len}.2f} {e:>{e_len}} **\n"
832
+ hilite: str
833
+ lolite: str
824
834
  for result in self._test_results:
825
- hilite = ""
826
- lolite = ""
827
-
828
835
  if result.passed is None:
829
836
  ratio = "-.--"
830
837
  pass_fail_str = "SKIP"
831
- if want_color_output():
832
- hilite = _ANSI.COLOR_SKIPPED
833
- lolite = _ANSI.COLOR_DEFAULT
838
+ hilite = self.COLOR_SKIPPED
839
+ lolite = ANSI.DEFAULT
834
840
  elif result.passed:
835
841
  ratio = format(result.ratio, "0.2f")
836
842
  pass_fail_str = "PASS"
837
- if want_color_output():
838
- hilite = _ANSI.COLOR_PASSED
839
- lolite = _ANSI.COLOR_DEFAULT
843
+ hilite = self.COLOR_PASSED
844
+ lolite = ANSI.DEFAULT
840
845
  else:
841
846
  ratio = format(result.ratio, "0.2f")
842
847
  pass_fail_str = "FAIL"
843
- if want_color_output():
844
- hilite = _ANSI.COLOR_FAILED
845
- lolite = _ANSI.COLOR_DEFAULT
848
+ hilite = self.COLOR_FAILED
849
+ lolite = ANSI.DEFAULT
850
+
851
+ if cocotb_logging.strip_ansi:
852
+ hilite = ""
853
+ lolite = ""
846
854
 
847
855
  test_dict = {
848
856
  "a": result.test_fullname,
Binary file
Binary file
cocotb/share/def/ghdl.exp CHANGED
Binary file
cocotb/share/def/ghdl.lib CHANGED
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -19,9 +19,9 @@
19
19
  #define xstr(a) str(a)
20
20
  #define str(a) #a
21
21
 
22
- extern "C" COCOTBUTILS_EXPORT void* utils_dyn_open(const char* lib_name);
23
- extern "C" COCOTBUTILS_EXPORT void* utils_dyn_sym(void* handle,
24
- const char* sym_name);
22
+ extern "C" COCOTBUTILS_EXPORT void *utils_dyn_open(const char *lib_name);
23
+ extern "C" COCOTBUTILS_EXPORT void *utils_dyn_sym(void *handle,
24
+ const char *sym_name);
25
25
  extern "C" COCOTBUTILS_EXPORT int is_python_context;
26
26
 
27
27
  // to_python and to_simulator are implemented as macros instead of functions so