Skip to content

Commit bd2a4cf

Browse files
committed
Make res, printer etc. kw-only
Also add more run-time checks if provided arguments are valid
1 parent 7350b43 commit bd2a4cf

File tree

1 file changed

+84
-10
lines changed

1 file changed

+84
-10
lines changed

src/stopuhr/chrono.py

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,75 @@ def log(self, value: bool):
234234
raise ValueError("log must be a boolean value")
235235
self._log = value
236236

237+
def parse_printer(self, printer: Printer | None) -> Printer:
238+
"""Parse the printer argument.
239+
240+
If the printer is None, return the default printer.
241+
If the printer is a callable, return it.
242+
Otherwise, raise an error.
243+
244+
Args:
245+
printer (Printer | None): The printer to use.
246+
247+
Returns:
248+
Printer: The printer to use.
249+
250+
Raises:
251+
ValueError: If the printer is not a callable or None.
252+
253+
"""
254+
if printer is None:
255+
return self.printer
256+
if not callable(printer):
257+
raise ValueError("printer must be a callable function or None")
258+
return printer
259+
260+
def parse_res(self, res: int | None) -> int:
261+
"""Parse the res argument.
262+
263+
If res is None, return the default res.
264+
If res is an int, return it.
265+
Otherwise, raise an error.
266+
267+
Args:
268+
res (int | None): The number of decimal places to round to.
269+
270+
Returns:
271+
int: The number of decimal places to round to.
272+
273+
Raises:
274+
ValueError: If res is not an int or None.
275+
276+
"""
277+
if res is None:
278+
return self.res
279+
if not isinstance(res, int) or res < 0:
280+
raise ValueError("res must be a non-negative integer or None")
281+
return res
282+
283+
def parse_log(self, log: bool | None) -> bool:
284+
"""Parse the log argument.
285+
286+
If log is None, return the default log setting.
287+
If log is a bool, return it.
288+
Otherwise, raise an error.
289+
290+
Args:
291+
log (bool | None): Whether to log the duration at occurence.
292+
293+
Returns:
294+
bool: Whether to log the duration at occurence.
295+
296+
Raises:
297+
ValueError: If log is not a boolean or None.
298+
299+
"""
300+
if log is None:
301+
return self.log
302+
if not isinstance(log, bool):
303+
raise ValueError("log must be a boolean value or None")
304+
return log
305+
237306
def reset(self):
238307
"""Reset the durations."""
239308
self.durations: dict[str, list[float]] = defaultdict(list)
@@ -300,7 +369,7 @@ def combine(*timers: "Chronometer") -> "Chronometer":
300369

301370
return combined
302371

303-
def summary(self, res: int | None = None, printer: Printer | None = None):
372+
def summary(self, *, res: int | None = None, printer: Printer | None = None):
304373
"""Print a summary of the durations.
305374
306375
Args:
@@ -310,8 +379,8 @@ def summary(self, res: int | None = None, printer: Printer | None = None):
310379
Defaults to None.
311380
312381
"""
313-
res = res or self.res
314-
printer = printer or self.printer
382+
res = self.parse_res(res)
383+
printer = self.parse_printer(printer)
315384
for key, values in self.durations.items():
316385
if not values:
317386
printer(f"{key} has no durations recorded")
@@ -334,9 +403,10 @@ def start(self, key: str):
334403
key (str): The msg / key to store the start time under.
335404
336405
"""
406+
assert isinstance(key, str), "key must be a string"
337407
self.idling_starts[key].append(time.perf_counter())
338408

339-
def stop(self, key: str, res: int | None = None, log: bool | None = None, printer: Printer | None = None):
409+
def stop(self, key: str, *, res: int | None = None, log: bool | None = None, printer: Printer | None = None):
340410
"""Stop the timer for a key.
341411
342412
Args:
@@ -349,9 +419,10 @@ def stop(self, key: str, res: int | None = None, log: bool | None = None, printe
349419
Defaults to None.
350420
351421
"""
352-
res = res or self.res
353-
log = log if log is not None else self.log
354-
printer = printer or self.printer
422+
assert isinstance(key, str), "key must be a string"
423+
res = self.parse_res(res)
424+
log = self.parse_log(log)
425+
printer = self.parse_printer(printer)
355426
end = time.perf_counter()
356427
try:
357428
start = self.idling_starts[key].pop(0)
@@ -365,6 +436,7 @@ def stop(self, key: str, res: int | None = None, log: bool | None = None, printe
365436
def f(
366437
self,
367438
key: str,
439+
*,
368440
res: int | None = None,
369441
log: bool | None = None,
370442
printer: Printer | None = None,
@@ -396,9 +468,10 @@ def f(
396468
ValueError: If any of the print_kwargs are not in the functions signature.
397469
398470
"""
399-
res = res or self.res
400-
log = log if log is not None else self.log
401-
printer = printer or self.printer
471+
assert isinstance(key, str), "key must be a string"
472+
res = self.parse_res(res)
473+
log = self.parse_log(log)
474+
printer = self.parse_printer(printer)
402475

403476
def _decorator(func):
404477
func_signature = signature(func)
@@ -441,6 +514,7 @@ def _inner(*args, **kwargs):
441514
def __call__(
442515
self,
443516
key: str,
517+
*,
444518
res: int | None = None,
445519
log: bool | None = None,
446520
printer: Printer | None = None,

0 commit comments

Comments
 (0)