By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

 _utils.py

View raw Download
text/x-script.python • 3.73 kiB
Python script text executable Python script, ASCII text executable
        
            
1
import os
2
import shutil
3
import contextlib
4
import typing
5
from datetime import datetime
6
7
__all__ = [
8
"_no_date_constructor",
9
"_in_directory",
10
"_delete_directory_contents",
11
"_parse_date_string",
12
]
13
14
15
def _no_date_constructor(loader, node):
16
"""Function to prevent the YAML loader from converting dates, keeping them as strings,
17
so they can be parsed in a more lenient way.
18
"""
19
value = loader.construct_scalar(node)
20
return value
21
22
23
@contextlib.contextmanager
24
def _in_directory(directory):
25
"""Execute a block of code in a different directory.
26
27
:param directory: The directory to change to.
28
"""
29
cwd = os.getcwd()
30
os.chdir(directory)
31
try:
32
yield
33
finally:
34
os.chdir(cwd)
35
36
37
def _delete_directory_contents(directory, dont_delete: typing.Optional[list[str]] = None):
38
"""Delete all files and directories in a directory recursively,
39
but not the directory itself.
40
41
:param directory: The directory to clear.
42
:param dont_delete: A list of files and directories to not delete.
43
"""
44
for root, dirs, files in os.walk(directory):
45
for file in files:
46
if file not in dont_delete:
47
os.remove(os.path.join(root, file))
48
for dir in dirs:
49
if dir not in dont_delete:
50
shutil.rmtree(os.path.join(root, dir))
51
52
53
def _parse_date_string(date_string):
54
"""Parse a date/time string into a datetime object. Supports multiple unambiguous formats.
55
56
:param date_string: The date/time string to parse.
57
:return: A datetime object representing the date/time string.
58
"""
59
def split_date_and_time(date_string):
60
"""Split a date/time string into a date string and a time string.
61
62
:param date_string: The date/time string to split.
63
:return: A tuple containing the date and time strings.
64
"""
65
if ":" not in date_string:
66
return date_string, "00:00:00"
67
68
elements = date_string.partition(":")
69
partition_character = " "
70
if " " not in date_string:
71
partition_character = "-"
72
if "-" not in date_string:
73
partition_character = "T"
74
75
date = elements[0].rpartition(partition_character)[0].strip()
76
time = elements[0].rpartition(partition_character)[2].strip() + elements[1] + elements[2].strip()
77
time = time.removeprefix("T").removesuffix("Z")
78
79
return date, time
80
81
time_formats = [
82
# 24-hour ISO
83
"%H:%M:%S",
84
"%H:%M",
85
"%H",
86
# Single digit hour
87
"-%H:%M:%S",
88
"-%H:%M",
89
"-%H",
90
# 12-hour (AM/PM)
91
"%I:%M:%S %p",
92
"%I:%M %p",
93
"%I %p",
94
# Single digit 12-hour
95
"-%I:%M:%S %p",
96
"-%I:%M %p",
97
"-%I %p",
98
]
99
100
date_formats = [
101
# ISO formats
102
"%Y-%m-%d",
103
"%y-%m-%d",
104
# European formats
105
"%d.%m.%Y",
106
"%d.%m.%y",
107
# American formats
108
"%m/%d/%Y",
109
"%m/%d/%y",
110
# Text-based European formats
111
"%d %B %Y",
112
"%d %b %Y",
113
"%d %B, %Y",
114
"%d %b, %Y",
115
# Text-based American formats
116
"%B %d %Y",
117
"%b %d %Y",
118
"%B %d, %Y",
119
"%b %d, %Y",
120
# ISO weekly calendar
121
"%G-W%V-%u",
122
]
123
124
date, time = split_date_and_time(date_string)
125
126
time_object = datetime.min.time()
127
date_object = datetime.min.date()
128
129
for time_format in time_formats:
130
try:
131
time_object = datetime.strptime(time, time_format)
132
except ValueError:
133
pass
134
for date_format in date_formats:
135
try:
136
date_object = datetime.strptime(date, date_format)
137
except ValueError:
138
pass
139
140
return datetime.combine(date_object, time_object.time())
141