Coverage for src/git_dag/utils.py: 93%
25 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-08 12:49 +0200
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-08 12:49 +0200
1"""Misc utils."""
3import codecs
4import re
5from datetime import datetime, timedelta
8def escape_decode(text: str) -> str:
9 """Decode escapes of escapes (e.g., ``\\\\n -> \\n``).
11 Note
12 -----
13 The approach in https://stackoverflow.com/a/37059682 is used because it handles
14 unicode characters. FIXME: unfortunately, it relies on the internal function
15 ``codecs.escape_decode`` (https://github.com/python/cpython/issues/74773).
17 """
18 return codecs.escape_decode(text.encode())[0].decode() # type: ignore
21def transform_ascii_control_chars(text: str) -> str:
22 """Transform ascii control characters.
24 Note
25 -----
26 This is necessary because SVGs exported from graphviz cannot be displayed when they
27 contain certain ascii control characters.
29 """
31 def ascii_to_caret_notation(match: re.Match[str]) -> str:
32 char = match.group(0)
33 return f"^{chr(ord(char) + 64)}"
35 # do not transform \a \b \t \n \v \f \r (which correspond to ^G-^M)
36 # https://en.wikipedia.org/wiki/ASCII#Control_code_table
37 return re.sub(r"[\x01-\x06\x0E-\x1A]", ascii_to_caret_notation, text)
40def creator_timestamp_format(
41 data: str, fmt: str = "%a %b %d %H:%M:%S %Y"
42) -> tuple[str, str, str]:
43 """Format a creator (author/committer) and timestamp.
45 Note
46 -----
47 The default format (``fmt``) is the same as the default format used by git.
49 """
51 def formatter(timestamp_timezone: str) -> str:
52 """Convert a string containing a timestamp and maybe a timezone."""
53 split = timestamp_timezone.split()
54 date_time = datetime.fromtimestamp(int(split[0])).strftime(fmt)
55 return f"{date_time} {split[1]}" if len(split) == 2 else date_time
57 match = re.search("(?P<name>.*) (?P<email><.*>) (?P<date>.*)", data)
58 if match:
59 creator = match.group("name")
60 email = match.group("email")
61 date = formatter(match.group("date"))
62 return creator, email, date
64 raise ValueError("Creator pattern not matched.")
67def increase_date(
68 date: str,
69 hours: int = 1,
70 date_format: str = "%d/%m/%y %H:%M %z",
71) -> str:
72 """Increase a date by a given number of hours."""
73 date_obj = datetime.strptime(date, date_format)
74 return (date_obj + timedelta(hours=hours)).strftime(date_format)