WUT_Computer_Science/Programming/PORR/code/time_measurement.py
Krzysztof kuhy Rudnicki 8798105aa1 Grade against 3.14 too, not just run on it
The pre-commit workflow already installs `python-version: "3.x"`, but ruff was
still grading the archive against py310 -- so it kept withholding the pyupgrade
and type-checking fixes that only become safe at the newer target.
`pin_ci_python.py` rewrites the knob.

The 17 findings that appeared are what the stale target was hiding:
`datetime.timezone.utc` where `datetime.UTC` is now correct (UP017), and 14
imports used only in annotations that belong under `TYPE_CHECKING` (TC002,
TC003) -- safe here precisely because 3.14's PEP 649 defers annotation
evaluation, which is the same reason ruff would not offer them at py310.

`ruff check` and `ruff format --check` are clean again, so the zero-findings
state this repo reached earlier today holds at the new target.

Still contradictory and deliberately untouched: the four
`Programming/ERSMS-project/*/Dockerfile` files say `FROM python:3.10-slim`.
They build an archived coursework service against its own era's pinned
dependencies, and nothing in this repo runs them; bumping the base image would
be a rewrite of that project, not a lint-target change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SfnJmxxsZ534AKk16rC2Gr
2026-08-29 11:55:43 +02:00

92 lines
2.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""Decorators that accumulate how long the measured calls take."""
import sys
import time
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from collections.abc import Callable
class TimeAccumulator:
"""Total time spent inside the decorated functions."""
def __init__(self) -> None:
"""Set up the object."""
self.total_time = 0
class ComplexTimeAcumulator:
"""Per-lap and longest times for the decorated functions."""
def __init__(self) -> None:
"""Set up the object."""
self.hard_reset()
def hard_reset(self) -> None:
"""Clear every recorded time."""
self.total_time = 0
self.reset()
def reset(self) -> None:
"""Clear the current lap."""
self.lap_time = 0
self.start = sys.float_info.max
self.end = 0
def save_lap_and_reset(self) -> None:
"""Store the current lap and start a new one."""
self.total_time += self.lap_time
self.reset()
time_accumulator = TimeAccumulator()
tests_time = TimeAccumulator()
longest_threads_time_accumulator = ComplexTimeAcumulator()
def time_measurement(accumulator: TimeAccumulator) -> Callable[..., object]:
"""Decorate a function so its runtime accumulates."""
def decorator(func: Callable[..., object]) -> Callable[..., object]:
"""Wrap the function so its runtime is recorded."""
def inner(*args: object, **kwargs: object) -> object:
"""Call the wrapped function and record how long it took."""
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
accumulator.total_time += end - start
return result
return inner
return decorator
def time_measurement_longest(
accumulator: ComplexTimeAcumulator,
) -> Callable[..., object]:
"""Decorate a function so its longest runtime is kept."""
def decorator(func: Callable[..., object]) -> Callable[..., object]:
"""Wrap the function so its runtime is recorded."""
def inner(*args: object, **kwargs: object) -> object:
"""Call the wrapped function and record how long it took."""
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
accumulator.start = min(accumulator.start, start)
accumulator.end = max(accumulator.end, end)
accumulator.lap_time = (
accumulator.end - accumulator.start
) # "=" instead of "+="
return result
return inner
return decorator