Stopwatch
Summary
- Package:
- Doc-string:
documented
File |
Expected |
Covered |
Uncovered |
Coverage |
|---|---|---|---|---|
__init__.py |
93 |
93 |
0 |
100.0% |
Overall (1 files) |
93 |
93 |
0 |
100.0% |
Source
pyTooling/Stopwatch/__init__.pydocumentedundocumentedignoredexcludednot counted
31"""
32A stopwatch to measure execution times.
33
34.. hint::
35
36 See :ref:`high-level help <COMMON/Stopwatch>` for explanations and usage examples.
37
38.. seealso::
39
40 :mod:`pyTooling.Tracing`
41 |rarr| Nested timespans instead of a single measurement, for tracing an execution.
42 :mod:`pyTooling.Process`
43 |rarr| The process' memory usage, next to its runtime.
44"""
...
56
58@export
59class StopwatchError(ToolingException):
60 """This exception is caused by wrong usage of the stopwatch."""
61
63@export
64class Duration(timedelta):
65 """
66 A :class:`~datetime.timedelta` resolving nanoseconds, which can be formatted by a format specification.
67
68 The constructor takes the parameters of :class:`~datetime.timedelta` (``days``, ``seconds``, ``microseconds``,
69 ``milliseconds``, ``minutes``, ``hours``, ``weeks``) and ``nanoseconds``; all of them are added up. Like
70 :class:`~datetime.timedelta`, a duration is normalized into components: :attr:`~datetime.timedelta.days` (the only
71 one with a sign), :attr:`~datetime.timedelta.seconds` (``0`` to ``86399``) and :attr:`nanoseconds` (``0`` to
72 ``999999999``). The inherited :attr:`~datetime.timedelta.microseconds` is ``nanoseconds // 1000``, so code written
73 for :class:`~datetime.timedelta` sees the duration truncated to microseconds. An existing
74 :class:`~datetime.timedelta` is converted by :meth:`FromTimedelta`.
75
76 Arithmetic and comparisons involving a duration calculate in nanoseconds and return a duration (or a number, where
77 :class:`~datetime.timedelta` returns one). A :class:`~datetime.timedelta` operand is converted.
78 ``datetime + duration`` stays the arithmetic of :class:`~datetime.datetime`, which resolves microseconds.
79
80 .. note::
81
82 :class:`~datetime.timedelta` is a C type, so this is a plain class with ``__slots__``, not an
83 :class:`~pyTooling.MetaClasses.ExtendedType` class.
84 """
85
86 __slots__ = ("_nanoseconds",)
87
88 _nanoseconds: int #: Sub-second component in nanoseconds, ``0`` to ``999999999``.
89
90 _FIELDS: ClassVar[dict[str, Callable[[int], str]]] = {
91 "H": lambda nanoseconds: f"{nanoseconds // 3_600_000_000_000:02}",
92 "M": lambda nanoseconds: f"{nanoseconds // 60_000_000_000 % 60:02}",
93 "S": lambda nanoseconds: f"{nanoseconds // 1_000_000_000 % 60:02}",
94 "L": lambda nanoseconds: f"{nanoseconds // 1_000_000 % 1_000:03}",
95 "U": lambda nanoseconds: f"{nanoseconds // 1_000 % 1_000_000:06}",
96 "N": lambda nanoseconds: f"{nanoseconds % 1_000_000_000:09}",
97 "s": lambda nanoseconds: f"{nanoseconds // 1_000_000_000}",
98 "m": lambda nanoseconds: f"{nanoseconds // 1_000_000}",
99 "u": lambda nanoseconds: f"{nanoseconds // 1_000}",
100 "n": lambda nanoseconds: f"{nanoseconds}",
101 } #: Functions rendering a format specifier from a non-negative duration in nanoseconds.
102
103 def __new__(
104 cls,
105 days: float = 0,
106 seconds: float = 0,
107 microseconds: float = 0,
108 milliseconds: float = 0,
109 minutes: float = 0,
110 hours: float = 0,
111 weeks: float = 0,
112 nanoseconds: float = 0
113 ) -> Self:
114 """
115 Initializes a duration from the sum of all given units.
116
117 The units of :class:`~datetime.timedelta` are converted by :class:`~datetime.timedelta`, so they resolve
118 microseconds; ``nanoseconds`` resolves nanoseconds. A float number of nanoseconds is rounded to the nearest
119 integer.
120
121 :param days: Optional, number of days. Default: ``0``.
122 :param seconds: Optional, number of seconds. Default: ``0``.
123 :param microseconds: Optional, number of microseconds. Default: ``0``.
124 :param milliseconds: Optional, number of milliseconds. Default: ``0``.
125 :param minutes: Optional, number of minutes. Default: ``0``.
126 :param hours: Optional, number of hours. Default: ``0``.
127 :param weeks: Optional, number of weeks. Default: ``0``.
128 :param nanoseconds: Optional, number of nanoseconds. Default: ``0``.
129 :returns: The duration.
130 :raises TypeError: If one of the parameters 'days' to 'weeks' is not a number (raised by
131 :class:`~datetime.timedelta`).
132 :raises ValueError: If parameter 'nanoseconds' is None.
133 :raises TypeError: If parameter 'nanoseconds' is not of type :class:`int` or :class:`float`.
134 """
...
156
157 @classmethod
158 def FromTimedelta(cls, value: timedelta) -> Self:
159 """
160 Convert a :class:`~datetime.timedelta` into a duration.
161
162 A duration given as ``value`` keeps its nanoseconds.
163
164 :param value: The time delta to convert.
165 :returns: A duration of the same length.
166 :raises ValueError: If parameter 'value' is None.
167 :raises TypeError: If parameter 'value' is not of type :class:`~datetime.timedelta`.
168 """
...
179
180 @readonly
181 def nanoseconds(self) -> int:
182 """
183 Read-only property to access the sub-second component in nanoseconds (:attr:`_nanoseconds`).
184
185 It is the counterpart of :attr:`~datetime.timedelta.microseconds` at nanosecond resolution.
186
187 :returns: The nanoseconds of the duration's last second, ``0`` to ``999999999``.
188 """
...
190
191 def total_nanoseconds(self) -> int:
192 """
193 Return the whole duration in nanoseconds.
194
195 It is the counterpart of :meth:`total_seconds` at nanosecond resolution.
196
197 :returns: The duration in nanoseconds.
198 """
...
200
201 def total_seconds(self) -> float:
202 """
203 Return the whole duration in seconds.
204
205 :returns: The duration in seconds, including the nanoseconds.
206 """
...
208
209 @staticmethod
210 def _TotalNanoseconds(value: timedelta) -> int:
211 """
212 Return a time delta in nanoseconds.
213
214 :param value: A :class:`~datetime.timedelta` or a duration.
215 :returns: The time delta in nanoseconds.
216 """
...
221
222 @staticmethod
223 def _DivideRounded(numerator: int, denominator: int) -> int:
224 """
225 Divide two integers, rounding half to even like :class:`~datetime.timedelta` does.
226
227 :param numerator: The dividend.
228 :param denominator: The divisor.
229 :returns: The quotient, rounded to the nearest integer; on a tie to the even one.
230 """
...
241
242 def __bool__(self) -> bool:
243 """
244 Check if the duration is not zero.
245
246 :returns: ``True``, if the duration is not zero.
247 """
...
249
250 def __eq__(self, other: Any) -> bool:
251 """
252 Compare two durations for equality, in nanoseconds.
253
254 :param other: A duration or a :class:`~datetime.timedelta` to compare with.
255 :returns: ``True``, if both durations are equal.
256 :raises TypeError: If parameter 'other' is not of type :class:`~datetime.timedelta`.
257 """
...
267
268 def __ne__(self, other: Any) -> bool:
269 """
270 Compare two durations for inequality, in nanoseconds.
271
272 :param other: A duration or a :class:`~datetime.timedelta` to compare with.
273 :returns: ``True``, if both durations are not equal.
274 :raises TypeError: If parameter 'other' is not of type :class:`~datetime.timedelta`.
275 """
...
283
284 def __lt__(self, other: Any) -> bool:
285 """
286 Compare two durations for less-than, in nanoseconds.
287
288 :param other: A duration or a :class:`~datetime.timedelta` to compare with.
289 :returns: ``True``, if this duration is shorter.
290 :raises TypeError: If parameter 'other' is not of type :class:`~datetime.timedelta`.
291 """
...
299
300 def __le__(self, other: Any) -> bool:
301 """
302 Compare two durations for less-than-or-equal, in nanoseconds.
303
304 :param other: A duration or a :class:`~datetime.timedelta` to compare with.
305 :returns: ``True``, if this duration is shorter or equal.
306 :raises TypeError: If parameter 'other' is not of type :class:`~datetime.timedelta`.
307 """
...
315
316 def __gt__(self, other: Any) -> bool:
317 """
318 Compare two durations for greater-than, in nanoseconds.
319
320 :param other: A duration or a :class:`~datetime.timedelta` to compare with.
321 :returns: ``True``, if this duration is longer.
322 :raises TypeError: If parameter 'other' is not of type :class:`~datetime.timedelta`.
323 """
...
331
332 def __ge__(self, other: Any) -> bool:
333 """
334 Compare two durations for greater-than-or-equal, in nanoseconds.
335
336 :param other: A duration or a :class:`~datetime.timedelta` to compare with.
337 :returns: ``True``, if this duration is longer or equal.
338 :raises TypeError: If parameter 'other' is not of type :class:`~datetime.timedelta`.
339 """
...
347
348 def __hash__(self) -> int:
349 """
350 Return a hash, which equals the hash of an equal :class:`~datetime.timedelta`.
351
352 :returns: The hash value.
353 """
...
358
359 def __neg__(self) -> Self:
360 """
361 Negate the duration.
362
363 :returns: The negated duration.
364 """
...
366
367 def __pos__(self) -> Self:
368 """
369 Return the duration unchanged.
370
371 :returns: This duration.
372 """
...
374
375 def __abs__(self) -> Self:
376 """
377 Return the absolute duration.
378
379 :returns: The duration without sign.
380 """
...
382
383 def __add__(self, other: Any) -> Any:
384 """
385 Add a duration or a :class:`~datetime.timedelta`.
386
387 Other operands are left to :class:`~datetime.timedelta`: ``duration + datetime`` is a
388 :class:`~datetime.datetime`.
389
390 :param other: The second operand.
391 :returns: The sum as a duration.
392 """
...
397
398 def __radd__(self, other: Any) -> Any:
399 """
400 Add this duration to a :class:`~datetime.timedelta`.
401
402 :param other: The first operand.
403 :returns: The sum as a duration.
404 """
...
409
410 def __sub__(self, other: Any) -> Any:
411 """
412 Subtract a duration or a :class:`~datetime.timedelta`.
413
414 :param other: The second operand.
415 :returns: The difference as a duration.
416 """
...
421
422 def __rsub__(self, other: Any) -> Any:
423 """
424 Subtract this duration from a :class:`~datetime.timedelta`.
425
426 :param other: The first operand.
427 :returns: The difference as a duration.
428 """
...
433
434 def __mul__(self, other: Any) -> Any:
435 """
436 Multiply the duration by a number.
437
438 A product with a :class:`float` is rounded to the nearest nanosecond, on a tie to the even one.
439
440 :param other: An :class:`int` or :class:`float` factor.
441 :returns: The product as a duration.
442 """
...
450
451 def __rmul__(self, other: Any) -> Any:
452 """
453 Multiply a number by the duration.
454
455 :param other: An :class:`int` or :class:`float` factor.
456 :returns: The product as a duration.
457 """
...
459
460 def __truediv__(self, other: Any) -> Any:
461 """
462 Divide the duration by a duration, a :class:`~datetime.timedelta` or a number.
463
464 A quotient of a number is rounded to the nearest nanosecond, on a tie to the even one.
465
466 :param other: The divisor.
467 :returns: The ratio as a :class:`float` for a time delta divisor, otherwise the quotient as a duration.
468 """
...
478
479 def __rtruediv__(self, other: Any) -> Any:
480 """
481 Divide a :class:`~datetime.timedelta` by the duration.
482
483 :param other: The dividend.
484 :returns: The ratio as a :class:`float`.
485 """
...
490
491 def __floordiv__(self, other: Any) -> Any:
492 """
493 Divide the duration by a duration, a :class:`~datetime.timedelta` or an integer, rounding down.
494
495 :param other: The divisor.
496 :returns: The ratio as an :class:`int` for a time delta divisor, otherwise the quotient as a duration.
497 """
...
504
505 def __rfloordiv__(self, other: Any) -> Any:
506 """
507 Divide a :class:`~datetime.timedelta` by the duration, rounding down.
508
509 :param other: The dividend.
510 :returns: The ratio as an :class:`int`.
511 """
...
516
517 def __mod__(self, other: Any) -> Any:
518 """
519 Return the remainder of dividing the duration by a duration or a :class:`~datetime.timedelta`.
520
521 :param other: The divisor.
522 :returns: The remainder as a duration.
523 """
...
528
529 def __rmod__(self, other: Any) -> Any:
530 """
531 Return the remainder of dividing a :class:`~datetime.timedelta` by the duration.
532
533 :param other: The dividend.
534 :returns: The remainder as a duration.
535 """
...
540
541 def __divmod__(self, other: Any) -> Any:
542 """
543 Divide the duration by a duration or a :class:`~datetime.timedelta`, returning quotient and remainder.
544
545 :param other: The divisor.
546 :returns: The quotient as an :class:`int` and the remainder as a duration.
547 """
...
553
554 def __rdivmod__(self, other: Any) -> Any:
555 """
556 Divide a :class:`~datetime.timedelta` by the duration, returning quotient and remainder.
557
558 :param other: The dividend.
559 :returns: The quotient as an :class:`int` and the remainder as a duration.
560 """
...
566
567 def __reduce__(self) -> tuple[type[Self], tuple[int, ...]]:
568 """
569 Return how to rebuild the duration, so :mod:`pickle` and :mod:`copy` keep its nanoseconds.
570
571 :returns: The class and its constructor arguments.
572 """
...
574
575 def __repr__(self) -> str:
576 """
577 Return the duration as a constructor call.
578
579 Only components other than zero are named. A duration resolving more than microseconds names ``nanoseconds``
580 instead of ``microseconds``. A zero duration is ``Duration(0)``.
581
582 :returns: The constructor call, e.g. ``Duration(seconds=1, nanoseconds=5)``.
583 """
...
600
601 def __str__(self) -> str:
602 """
603 Return the duration like :class:`~datetime.timedelta` does.
604
605 A duration resolving more than microseconds shows 9 fractional digits instead of 6.
606
607 :returns: The duration as ``[D day[s], ]H:MM:SS[.ffffff]``, or with 9 fractional digits.
608 """
...
616
617 def __format__(self, formatSpec: str) -> str:
618 """
619 Return the duration according to the format specification.
620
621 .. topic:: Format Specifiers
622
623 An **uppercase** specifier is a field of the duration as it would be displayed. A **lowercase** specifier is
624 the whole duration expressed in one unit, which is what a report or a comparison wants.
625
626 +-----------+--------------------------------------------------------+
627 | Specifier | Meaning |
628 +===========+========================================================+
629 | ``%H`` | hours, not capped - a 26 hour duration shows ``26`` |
630 +-----------+--------------------------------------------------------+
631 | ``%M`` | minutes, ``00`` to ``59`` |
632 +-----------+--------------------------------------------------------+
633 | ``%S`` | seconds, ``00`` to ``59`` |
634 +-----------+--------------------------------------------------------+
635 | ``%L`` | fractional seconds, 3 digits (milliseconds) |
636 +-----------+--------------------------------------------------------+
637 | ``%U`` | fractional seconds, 6 digits (microseconds) |
638 +-----------+--------------------------------------------------------+
639 | ``%N`` | fractional seconds, 9 digits (nanoseconds) |
640 +-----------+--------------------------------------------------------+
641 | ``%s`` | the whole duration in seconds |
642 +-----------+--------------------------------------------------------+
643 | ``%m`` | the whole duration in milliseconds |
644 +-----------+--------------------------------------------------------+
645 | ``%u`` | the whole duration in microseconds |
646 +-----------+--------------------------------------------------------+
647 | ``%n`` | the whole duration in nanoseconds |
648 +-----------+--------------------------------------------------------+
649
650 The fractional specifiers are truncations of the same fraction, so ``%S.%U`` renders ``04.123456`` without
651 having to be combined with anything. ``%H`` is not capped, so ``%H:%M:%S`` never silently drops a day.
652
653 A negative duration renders the fields of its absolute value, with a ``-`` in front of the first placeholder:
654 ``took %s s`` renders ``took -5 s``.
655
656 ``%%`` renders a literal percent sign. An empty format specification returns :meth:`__str__`.
657
658 :param formatSpec: The format specification, using ``%``-placeholders for the duration's parts.
659 :returns: The formatted duration.
660 :raises ValueError: If the format specification contains an unknown placeholder.
661 """
...
687
689@export
690class ExcludeContextManager:
691 """
692 A stopwatch context manager for excluding certain time spans from measurement.
693
694 While a normal stopwatch's embedded context manager (re)starts the stopwatch on every *enter* event and pauses the
695 stopwatch on every *exit* event, this context manager pauses on *enter* events and restarts on every *exit* event.
696 """
697 _stopwatch: Stopwatch #: Reference to the stopwatch.
698
699 def __init__(self, stopwatch: Stopwatch) -> None:
700 """
701 Initializes an excluding context manager.
702
703 :param stopwatch: Reference to the stopwatch.
704 """
...
706
707 def __enter__(self) -> Self:
708 """
709 Enter the context and pause the stopwatch.
710
711 :returns: Excluding stopwatch context manager instance.
712 """
...
716
717 def __exit__(
718 self,
719 exc_type: Nullable[type[BaseException]] = None,
720 exc_val: Nullable[BaseException] = None,
721 exc_tb: Nullable[TracebackType] = None
722 ) -> Nullable[bool]:
723 """
724 Exit the context and restart stopwatch.
725
726 :param exc_type: Optional, exception type.
727 :param exc_val: Optional, exception instance.
728 :param exc_tb: Optional, exception's traceback.
729 :returns: ``None``
730 """
...
732
734@export
735class Stopwatch(SlottedObject):
736 """
737 The stopwatch implements a solution to measure and collect timings.
738
739 The time measurement can be started, paused, resumed and stopped. More over, split times can be taken too. The
740 measurement is based on :func:`time.perf_counter_ns`. Additionally, starting and stopping is preserved as absolute
741 time via :meth:`datetime.datetime.now`.
742
743 Every split time taken is a time delta to the previous operation. These are preserved in an internal sequence of
744 splits. This sequence includes time deltas of activity and inactivity. Thus, a running stopwatch can be split as well
745 as a paused stopwatch.
746
747 The stopwatch can also be used in a :ref:`with-statement <with>`, because it implements the :ref:`context manager protocol <context-managers>`.
748
749 A stopwatch measures one time span: it is started once and stopped once. There is no restart or reset operation; a
750 new stopwatch measures again.
751 """
752
753 _name: Nullable[str] #: Optional name of the stopwatch.
754 _preferPause: bool #: If ``True``, the context manager pauses instead of stopping on exit.
755 _digits: int #: Number of fractional digits ``__str__`` renders the duration with.
756
757 _beginTime: Nullable[datetime] #: Absolute time when the stopwatch was started.
758 _endTime: Nullable[datetime] #: Absolute time when the stopwatch was stopped.
759 _startTime: Nullable[int] #: Performance counter in ns when the stopwatch was started.
760 _resumeTime: Nullable[int] #: Performance counter in ns of the latest resume operation.
761 _pauseTime: Nullable[int] #: Performance counter in ns of the latest pause operation.
762 _stopTime: Nullable[int] #: Performance counter in ns when the stopwatch was stopped.
763 _totalTime: Nullable[int] #: Duration in ns from starting to stopping, activity and inactivity.
764 _duration: Nullable[Duration] #: :attr:`Duration` of a stopped stopwatch, computed on first access.
765 _splits: list[tuple[float, bool]] #: Split times as (duration, is-active) pairs, in the order they were taken.
766
767 _excludeContextManager: ExcludeContextManager #: The nested context manager excluding time spans from measurement.
768
769 def __init__(
770 self,
771 name: Nullable[str] = None,
772 started: bool = False,
773 preferPause: bool = False,
774 digits: int = 3
775 ) -> None:
776 """
777 Initializes the fields of the stopwatch.
778
779 If parameter ``started`` is set to true, the stopwatch will immediately start.
780
781 :param name: Optional, name of the stopwatch.
782 :param started: Optional, if ``True``, start the stopwatch immediately.
783 :param preferPause: Optional, if ``True``, ``__exit__(...)`` prefers pause over stop behavior.
784 :param digits: Optional, number of fractional digits :meth:`__str__` renders the duration with.
785 :raises TypeError: If parameter 'digits' is not of type :class:`int`.
786 :raises ValueError: If parameter 'digits' is negative or greater than 9.
787 """
...
817
818 def Start(self) -> None:
819 """
820 Start the stopwatch.
821
822 A stopwatch can only be started once. There is no restart or reset operation provided; a new stopwatch measures
823 again.
824
825 :raises StopwatchError: If stopwatch was already started.
826 :raises StopwatchError: If stopwatch was already started and stopped. |br|
827 Create a new stopwatch to measure again.
828 """
...
838
839 def Split(self) -> float:
840 """
841 Take a split time and return the time delta to the previous stopwatch operation.
842
843 The stopwatch needs to be running to take a split time. See property :data:`IsRunning` to check if the stopwatch
844 is running and the split operation is possible. |br|
845 Depending on the previous operation, the time delta will be:
846
847 * the duration from start operation to the first split.
848 * the duration from last resume to this split.
849
850 :returns: Duration in seconds since last stopwatch operation
851 :raises StopwatchError: If stopwatch was not started or resumed.
852 """
...
863
864 def Pause(self) -> float:
865 """
866 Pause the stopwatch and return the time delta to the previous stopwatch operation.
867
868 The stopwatch needs to be running to pause it. See property :data:`IsRunning` to check if the stopwatch is running
869 and the pause operation is possible. |br|
870 Depending on the previous operation, the time delta will be:
871
872 * the duration from start operation to the first pause.
873 * the duration from last resume to this pause.
874
875 :returns: Duration in seconds since last stopwatch operation
876 :raises StopwatchError: If stopwatch was not started or resumed.
877 """
...
888
889 def Resume(self) -> float:
890 """
891 Resume the stopwatch and return the time delta to the previous pause operation.
892
893 The stopwatch needs to be paused to resume it. See property :data:`IsPaused` to check if the stopwatch is paused
894 and the resume operation is possible. |br|
895 The time delta will be the duration from last pause to this resume.
896
897 :returns: Duration in seconds since last pause operation
898 :raises StopwatchError: If stopwatch was not paused.
899 """
...
910
911 def Stop(self) -> float:
912 """
913 Stop the stopwatch and return the time delta to the previous stopwatch operation.
914
915 The stopwatch needs to be started to stop it. See property :data:`IsStarted` to check if the stopwatch was started
916 and the stop operation is possible. |br|
917 Depending on the previous operation, the time delta will be:
918
919 * the duration from start operation to the stop operation.
920 * the duration from last resume to the stop operation.
921
922 :returns: Duration in seconds since last stopwatch operation
923 :raises StopwatchError: If stopwatch was not started.
924 :raises StopwatchError: If stopwatch was already stopped.
925 """
...
955
956 @property
957 def Digits(self) -> int:
958 """
959 Property to get and set the number of fractional digits (:attr:`_digits`) used by :meth:`__str__`.
960
961 The measurement itself is unaffected - this only decides how many digits of the duration in seconds are
962 rendered. It defaults to ``3``, which is milliseconds.
963
964 :returns: Number of fractional digits.
965 :raises TypeError: If the assigned value is not of type :class:`int`.
966 :raises ValueError: If the assigned value is negative or greater than 9.
967 """
...
969
970 @Digits.setter
971 def Digits(self, digits: int) -> None:
...
982
983 @readonly
984 def Name(self) -> Nullable[str]:
985 """
986 Read-only property returning the name of the stopwatch.
987
988 :returns: Name of the stopwatch.
989 """
...
991
992 @readonly
993 def IsStarted(self) -> bool:
994 """
995 Read-only property returning the IsStarted state of the stopwatch.
996
997 :returns: True, if stopwatch was started.
998 """
...
1000
1001 @readonly
1002 def IsRunning(self) -> bool:
1003 """
1004 Read-only property returning the IsRunning state of the stopwatch.
1005
1006 :returns: True, if stopwatch was started and is currently not paused.
1007 """
...
1009
1010 @readonly
1011 def IsPaused(self) -> bool:
1012 """
1013 Read-only property returning the IsPaused state of the stopwatch.
1014
1015 :returns: True, if stopwatch was started and is currently paused.
1016 """
...
1018
1019 @readonly
1020 def IsStopped(self) -> bool:
1021 """
1022 Read-only property returning the IsStopped state of the stopwatch.
1023
1024 :returns: True, if stopwatch was stopped.
1025 """
...
1027
1028 @readonly
1029 def StartTime(self) -> Nullable[datetime]:
1030 """
1031 Read-only property returning the absolute time when the stopwatch was started.
1032
1033 :returns: The time when the stopwatch was started, otherwise None.
1034 """
...
1036
1037 @readonly
1038 def StopTime(self) -> Nullable[datetime]:
1039 """
1040 Read-only property returning the absolute time when the stopwatch was stopped.
1041
1042 :returns: The time when the stopwatch was stopped, otherwise None.
1043 """
...
1045
1046 @readonly
1047 def HasSplitTimes(self) -> bool:
1048 """
1049 Read-only property checking if split times have been taken.
1050
1051 :returns: True, if at least one split time has been taken.
1052 """
...
1054
1055 @readonly
1056 def SplitCount(self) -> int:
1057 """
1058 Read-only property returning the number of split times.
1059
1060 :returns: Number of split times.
1061 """
...
1063
1064 @readonly
1065 def ActiveCount(self) -> int:
1066 """
1067 Read-only property returning the number of active split times.
1068
1069 A running stopwatch is inside an active span that hasn't been recorded yet, and that span is counted here -
1070 the result is what the stopwatch would report if it were stopped right now. This matches
1071 :attr:`Activity`, which includes the running span's duration.
1072
1073 :returns: Number of active split times, including the one in progress.
1074 """
...
1079
1080 @readonly
1081 def InactiveCount(self) -> int:
1082 """
1083 Read-only property returning the number of inactive split times.
1084
1085 A paused stopwatch is inside an inactive span that hasn't been recorded yet, and that span is counted here -
1086 the result is what the stopwatch would report if it were stopped right now. This matches
1087 :attr:`Inactivity`, which includes the paused span's duration.
1088
1089 :returns: Number of inactive split times, including the one in progress.
1090 """
...
1095
1096 @readonly
1097 def Activity(self) -> Duration:
1098 """
1099 Read-only property returning the duration of all active split times.
1100
1101 If the stopwatch is currently running, the duration since start or last resume operation will be included. It is
1102 built from :attr:`ActivityInSeconds`, the same value as a float, so it resolves microseconds.
1103
1104 :returns: Duration of all active split times. If the stopwatch was never started, the return value is a zero
1105 duration.
1106 """
...
1108
1109 @readonly
1110 def ActivityInSeconds(self) -> float:
1111 """
1112 Read-only property returning the same duration as :attr:`Activity`, but in seconds.
1113
1114 :returns: Duration of all active split times in seconds. If the stopwatch was never started, the return value will
1115 be 0.0.
1116 """
...
1122
1123 @readonly
1124 def Inactivity(self) -> Duration:
1125 """
1126 Read-only property returning the duration of all inactive split times.
1127
1128 If the stopwatch is currently paused, the duration since last pause operation will be included. It is built from
1129 :attr:`InactivityInSeconds`, the same value as a float, so it resolves microseconds.
1130
1131 :returns: Duration of all inactive split times. If the stopwatch was never started, the return value is a zero
1132 duration.
1133 """
...
1135
1136 @readonly
1137 def InactivityInSeconds(self) -> float:
1138 """
1139 Read-only property returning the same duration as :attr:`Inactivity`, but in seconds.
1140
1141 :returns: Duration of all inactive split times in seconds. If the stopwatch was never started, the return value will
1142 be 0.0.
1143 """
...
1149
1150 @readonly
1151 def Duration(self) -> Duration:
1152 """
1153 Read-only property returning the duration from start operation to stop operation.
1154
1155 If the stopwatch is not yet stopped, the duration from start to now is returned. A
1156 :class:`~pyTooling.Stopwatch.Duration` prints as hours, minutes and seconds, accepts a format specification and
1157 resolves nanoseconds; :attr:`DurationInSeconds` and :attr:`DurationInNanoseconds` are the same measurement as
1158 numbers. The duration of a stopped stopwatch is computed once (:attr:`_duration`).
1159
1160 :returns: Duration since the stopwatch was started. If the stopwatch was never started, the return value is a zero
1161 duration.
1162 """
...
1171
1172 @readonly
1173 def DurationInSeconds(self) -> float:
1174 """
1175 Read-only property returning the same duration as :attr:`Duration`, but in seconds.
1176
1177 :returns: Duration since stopwatch was started in seconds. If the stopwatch was never started, the return value will
1178 be 0.0.
1179 """
...
1181
1182 @readonly
1183 def DurationInNanoseconds(self) -> int:
1184 """
1185 Read-only property returning the same duration as :attr:`Duration`, but in whole nanoseconds.
1186
1187 This is the measurement as the underlying :func:`time.perf_counter_ns` took it, so anything that divides a
1188 duration into parts - :meth:`__format__` does - works from an integer instead of converting a float back.
1189
1190 Precision is not the reason to prefer it. A float holds a duration in seconds exactly, to the nanosecond, up
1191 to :math:`2^{53}` ns - a little over 104 days - which no stopwatch will reach.
1192
1193 :returns: Duration since the stopwatch was started in nanoseconds. If the stopwatch was never started, the
1194 return value will be 0.
1195 """
...
1202
1203 @readonly
1204 def Exclude(self) -> ExcludeContextManager:
1205 """
1206 Return an *exclude* context manager for the stopwatch instance.
1207
1208 :returns: An excluding context manager.
1209 """
...
1214
1215 def __enter__(self) -> Self:
1216 """
1217 Enter the context and start or resume the stopwatch.
1218
1219 An unstarted stopwatch is started, a paused one is resumed.
1220
1221 :returns: The stopwatch itself.
1222 :raises StopwatchError: If the stopwatch is running.
1223 :raises StopwatchError: If the stopwatch was already stopped. |br|
1224 Create a new stopwatch to measure again, or create it with ``preferPause=True`` to
1225 measure several ``with``-blocks.
1226 """
...
1246
1247 def __exit__(
1248 self,
1249 exc_type: Nullable[type[BaseException]] = None,
1250 exc_val: Nullable[BaseException] = None,
1251 exc_tb: Nullable[TracebackType] = None
1252 ) -> Nullable[bool]:
1253 """
1254 Exit the context and pause or stop the stopwatch.
1255
1256 A running stopwatch is paused, if ``preferPause`` was set, otherwise it's stopped.
1257
1258 :param exc_type: Optional, exception type, otherwise None.
1259 :param exc_val: Optional, exception object, otherwise None.
1260 :param exc_tb: Optional, exception's traceback, otherwise None.
1261 :returns: ``None``
1262 :raises StopwatchError: If the stopwatch was never started.
1263 :raises StopwatchError: If the stopwatch was already stopped.
1264 :raises StopwatchError: If the stopwatch is paused, because it wasn't resumed.
1265 """
...
1288
1289 def __len__(self) -> int:
1290 """
1291 Implementation of ``len(...)`` to return the number of split times.
1292
1293 :returns: Number of split times.
1294 """
...
1296
1297 def __getitem__(self, index: int) -> tuple[float, bool]:
1298 """
1299 Implementation of ``split = object[i]`` to return the i-th split time.
1300
1301 :param index: Index to access the i-th split time.
1302 :returns: i-th split time as a tuple of: |br|
1303 (1) delta time to the previous stopwatch operation and |br|
1304 (2) a boolean indicating if the split was an activity (true) or inactivity (false).
1305 :raises IndexError: If index *i* doesn't exist.
1306 """
...
1308
1309 def __iter__(self) -> Iterator[tuple[float, bool]]:
1310 """
1311 Return an iterator of tuples to iterate all split times.
1312
1313 If the stopwatch is not stopped yet, the last split won't be included.
1314
1315 :returns: Iterator of split time tuples of: |br|
1316 (1) delta time to the previous stopwatch operation and |br|
1317 (2) a boolean indicating if the split was an activity (true) or inactivity (false).
1318 """
...
1320
1321 def __format__(self, formatSpec: str) -> str:
1322 """
1323 Return the measured duration according to the format specification.
1324
1325 .. topic:: Format Specifiers
1326
1327 An **uppercase** specifier is a field of the duration as it would be displayed. A **lowercase** specifier is
1328 the whole duration expressed in one unit, which is what a report or a comparison wants.
1329
1330 +-----------+--------------------------------------------------------+
1331 | Specifier | Meaning |
1332 +===========+========================================================+
1333 | ``%H`` | hours, not capped - a 26 hour measurement shows ``26`` |
1334 +-----------+--------------------------------------------------------+
1335 | ``%M`` | minutes, ``00`` to ``59`` |
1336 +-----------+--------------------------------------------------------+
1337 | ``%S`` | seconds, ``00`` to ``59`` |
1338 +-----------+--------------------------------------------------------+
1339 | ``%L`` | fractional seconds, 3 digits (milliseconds) |
1340 +-----------+--------------------------------------------------------+
1341 | ``%U`` | fractional seconds, 6 digits (microseconds) |
1342 +-----------+--------------------------------------------------------+
1343 | ``%N`` | fractional seconds, 9 digits (nanoseconds) |
1344 +-----------+--------------------------------------------------------+
1345 | ``%s`` | the whole duration in seconds |
1346 +-----------+--------------------------------------------------------+
1347 | ``%m`` | the whole duration in milliseconds |
1348 +-----------+--------------------------------------------------------+
1349 | ``%u`` | the whole duration in microseconds |
1350 +-----------+--------------------------------------------------------+
1351 | ``%n`` | the whole duration in nanoseconds |
1352 +-----------+--------------------------------------------------------+
1353
1354 The fractional specifiers are truncations of the same fraction, so ``%S.%U`` renders ``04.123456`` without
1355 having to be combined with anything. ``%H`` is not capped, so ``%H:%M:%S`` never silently drops a day.
1356
1357 ``%%`` renders a literal percent sign. An empty format specification returns :meth:`__str__`.
1358
1359 :param formatSpec: The format specification, using ``%``-placeholders for the duration's parts.
1360 :returns: The formatted duration.
1361 :raises ValueError: If the format specification contains an unknown placeholder.
1362 """
...
1367
1368 def __str__(self) -> str:
1369 """
1370 Returns the stopwatch's state and its measured time span.
1371
1372 The duration is rendered in seconds with :attr:`Digits` fractional digits, in every state - a running and a
1373 stopped stopwatch report the same unit at the same resolution.
1374
1375 :returns: The string equivalent of the stopwatch.
1376 """
...