stdl 0.6.7__tar.gz → 0.6.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {stdl-0.6.7 → stdl-0.6.8}/PKG-INFO +5 -2
  2. {stdl-0.6.7 → stdl-0.6.8}/mkdocs.yml +0 -1
  3. {stdl-0.6.7 → stdl-0.6.8}/pyproject.toml +12 -13
  4. {stdl-0.6.7 → stdl-0.6.8}/stdl/dt.py +35 -0
  5. {stdl-0.6.7 → stdl-0.6.8}/stdl/fs.py +3 -0
  6. {stdl-0.6.7 → stdl-0.6.8}/stdl/log.py +9 -0
  7. {stdl-0.6.7 → stdl-0.6.8}/stdl/net.py +8 -0
  8. stdl-0.6.8/tests/test_dt.py +231 -0
  9. stdl-0.6.7/tests/test_dt.py +0 -109
  10. {stdl-0.6.7 → stdl-0.6.8}/.editorconfig +0 -0
  11. {stdl-0.6.7 → stdl-0.6.8}/.github/workflows/lint.yml +0 -0
  12. {stdl-0.6.7 → stdl-0.6.8}/.github/workflows/publish.yml +0 -0
  13. {stdl-0.6.7 → stdl-0.6.8}/.github/workflows/test.yml +0 -0
  14. {stdl-0.6.7 → stdl-0.6.8}/.gitignore +0 -0
  15. {stdl-0.6.7 → stdl-0.6.8}/.pre-commit-config.yaml +0 -0
  16. {stdl-0.6.7 → stdl-0.6.8}/.python-version +0 -0
  17. {stdl-0.6.7 → stdl-0.6.8}/.readthedocs.yml +0 -0
  18. {stdl-0.6.7 → stdl-0.6.8}/CONTRIBUTING.md +0 -0
  19. {stdl-0.6.7 → stdl-0.6.8}/Justfile +0 -0
  20. {stdl-0.6.7 → stdl-0.6.8}/LICENSE +0 -0
  21. {stdl-0.6.7 → stdl-0.6.8}/README.md +0 -0
  22. {stdl-0.6.7 → stdl-0.6.8}/docs/index.md +0 -0
  23. {stdl-0.6.7 → stdl-0.6.8}/docs/reference/color.md +0 -0
  24. {stdl-0.6.7 → stdl-0.6.8}/docs/reference/decorators.md +0 -0
  25. {stdl-0.6.7 → stdl-0.6.8}/docs/reference/dt.md +0 -0
  26. {stdl-0.6.7 → stdl-0.6.8}/docs/reference/fs.md +0 -0
  27. {stdl-0.6.7 → stdl-0.6.8}/docs/reference/import_lazy.md +0 -0
  28. {stdl-0.6.7 → stdl-0.6.8}/docs/reference/log.md +0 -0
  29. {stdl-0.6.7 → stdl-0.6.8}/docs/reference/net.md +0 -0
  30. {stdl-0.6.7 → stdl-0.6.8}/docs/reference/st.md +0 -0
  31. {stdl-0.6.7 → stdl-0.6.8}/docs/requirements.txt +0 -0
  32. {stdl-0.6.7 → stdl-0.6.8}/stdl/__init__.py +0 -0
  33. {stdl-0.6.7 → stdl-0.6.8}/stdl/color.py +0 -0
  34. {stdl-0.6.7 → stdl-0.6.8}/stdl/decorators.py +0 -0
  35. {stdl-0.6.7 → stdl-0.6.8}/stdl/import_lazy.py +0 -0
  36. {stdl-0.6.7 → stdl-0.6.8}/stdl/lst.py +0 -0
  37. {stdl-0.6.7 → stdl-0.6.8}/stdl/st.py +0 -0
  38. {stdl-0.6.7 → stdl-0.6.8}/tests/test_color.py +0 -0
  39. {stdl-0.6.7 → stdl-0.6.8}/tests/test_decorators.py +0 -0
  40. {stdl-0.6.7 → stdl-0.6.8}/tests/test_fs.py +0 -0
  41. {stdl-0.6.7 → stdl-0.6.8}/tests/test_lst.py +0 -0
  42. {stdl-0.6.7 → stdl-0.6.8}/tests/test_pydantic_integration.py +0 -0
  43. {stdl-0.6.7 → stdl-0.6.8}/tests/test_st.py +0 -0
  44. {stdl-0.6.7 → stdl-0.6.8}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stdl
3
- Version: 0.6.7
3
+ Version: 0.6.8
4
4
  Summary: Extended Python Standard Library
5
5
  Project-URL: Repository, https://github.com/zigai/stdl
6
6
  Project-URL: Issues, https://github.com/zigai/stdl/issues
@@ -28,7 +28,7 @@ License: MIT License
28
28
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
29
  SOFTWARE.
30
30
  License-File: LICENSE
31
- Keywords: ANSI colors,color formats,filesystem,lazy import,list utils,logging configuration,python lazy import,python utilities,standard library,stdl,string utils,terminal links,terminal output
31
+ Keywords: ansi-colors,color-formats,filesystem,lazy-import,list-utils,python-utilities,standard-library,stdl,terminal-links,text-processing
32
32
  Classifier: Intended Audience :: Developers
33
33
  Classifier: License :: OSI Approved :: MIT License
34
34
  Classifier: Operating System :: OS Independent
@@ -41,6 +41,9 @@ Classifier: Programming Language :: Python :: 3.12
41
41
  Classifier: Programming Language :: Python :: 3.13
42
42
  Classifier: Programming Language :: Python :: 3.14
43
43
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
44
+ Classifier: Topic :: System :: Filesystems
45
+ Classifier: Topic :: Text Processing :: General
46
+ Classifier: Topic :: Utilities
44
47
  Classifier: Typing :: Typed
45
48
  Requires-Python: >=3.10
46
49
  Requires-Dist: python-dateutil>=2.8.2
@@ -24,7 +24,6 @@ theme:
24
24
  - navigation.instant.prefetch
25
25
  - navigation.instant.preview
26
26
  - navigation.instant.progress
27
- - search.highlight
28
27
  - search.share
29
28
  - search.highlight
30
29
  - search.suggest
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "stdl"
3
- version = "0.6.7"
3
+ version = "0.6.8"
4
4
  description = "Extended Python Standard Library"
5
5
  authors = [{ name = "Žiga Ivanšek", email = "ziga.ivansek@gmail.com" }]
6
6
  license = { file = "LICENSE" }
@@ -19,6 +19,9 @@ classifiers = [
19
19
  "Operating System :: OS Independent",
20
20
  "Intended Audience :: Developers",
21
21
  "Topic :: Software Development :: Libraries :: Python Modules",
22
+ "Topic :: Utilities",
23
+ "Topic :: System :: Filesystems",
24
+ "Topic :: Text Processing :: General",
22
25
  "Programming Language :: Python",
23
26
  "Programming Language :: Python :: 3",
24
27
  "Programming Language :: Python :: 3 :: Only",
@@ -31,21 +34,17 @@ classifiers = [
31
34
 
32
35
  keywords = [
33
36
  "stdl",
34
- "standard library",
35
- "python utilities",
37
+ "standard-library",
38
+ "python-utilities",
36
39
  "filesystem",
37
- "list utils",
38
- "string utils",
39
- "logging configuration",
40
- "ANSI colors",
41
- "color formats",
42
- "terminal output",
43
- "terminal links",
44
- "lazy import",
45
- "python lazy import",
40
+ "list-utils",
41
+ "ansi-colors",
42
+ "color-formats",
43
+ "terminal-links",
44
+ "lazy-import",
45
+ "text-processing",
46
46
  ]
47
47
 
48
-
49
48
  [project.optional-dependencies]
50
49
  full = ["tqdm>=4.67.1"]
51
50
  dev = [
@@ -233,6 +233,40 @@ def date_range(start: date, end: date) -> Generator[date, None, None]:
233
233
  yield start + timedelta(n)
234
234
 
235
235
 
236
+ def datetime_range(
237
+ start: datetime,
238
+ end: datetime,
239
+ step: timedelta,
240
+ ) -> Generator[datetime, None, None]:
241
+ """
242
+ Returns a generator for datetimes between ``start`` and ``end`` with a given ``step``.
243
+
244
+ Example:
245
+ ```python
246
+ >>> for dt in datetime_range(
247
+ ... datetime(2026, 1, 27, 0, 0),
248
+ ... datetime(2026, 1, 27, 3, 0),
249
+ ... timedelta(hours=1)
250
+ ... ):
251
+ ... print(dt)
252
+ 2026-01-27 00:00:00
253
+ 2026-01-27 01:00:00
254
+ 2026-01-27 02:00:00
255
+ ```
256
+ """
257
+ current = start
258
+ if step.total_seconds() == 0:
259
+ raise ValueError("step cannot be zero")
260
+ if step.total_seconds() > 0:
261
+ while current < end:
262
+ yield current
263
+ current += step
264
+ else:
265
+ while current > end:
266
+ yield current
267
+ current += step
268
+
269
+
236
270
  def sleep(lo: float, hi: float | None = None) -> float:
237
271
  """
238
272
  Sleeps for a random duration within a specified range.
@@ -346,6 +380,7 @@ __all__ = [
346
380
  "time_fmt",
347
381
  "date_fmt",
348
382
  "date_range",
383
+ "datetime_range",
349
384
  "sleep",
350
385
  "local_timezone",
351
386
  "timezone",
@@ -1059,6 +1059,7 @@ class File(PathBase):
1059
1059
 
1060
1060
  @property
1061
1061
  def exists(self) -> bool:
1062
+ """Whether the file exists."""
1062
1063
  return os.path.isfile(self.path)
1063
1064
 
1064
1065
  @property
@@ -1409,6 +1410,7 @@ class Directory(PathBase):
1409
1410
 
1410
1411
  @property
1411
1412
  def size(self) -> int:
1413
+ """Total size of the directory and all its contents in bytes."""
1412
1414
  return get_dir_size(self.path, readable=False) # type:ignore
1413
1415
 
1414
1416
  @property
@@ -1423,6 +1425,7 @@ class Directory(PathBase):
1423
1425
 
1424
1426
  @property
1425
1427
  def parent(self) -> Directory:
1428
+ """The parent directory."""
1426
1429
  return Directory(os.path.dirname(self.path))
1427
1430
 
1428
1431
  @classmethod
@@ -130,6 +130,15 @@ def br(
130
130
  *,
131
131
  newline: bool = False,
132
132
  ) -> None:
133
+ """
134
+ Print a horizontal line break across the terminal.
135
+
136
+ Args:
137
+ c (str): Character to repeat. Defaults to "_".
138
+ length (int | None): Line length. Defaults to terminal width.
139
+ handler (Callable[[str], None]): Function to output the line. Defaults to print.
140
+ newline (bool): Whether to append a newline. Defaults to False.
141
+ """
133
142
  if IGNORE_BREAKS:
134
143
  return
135
144
  length = length or get_terminal_size().columns
@@ -22,6 +22,14 @@ else:
22
22
 
23
23
 
24
24
  class DownloadSizeExceededError(Exception):
25
+ """
26
+ Exception raised when a file download exceeds the maximum allowed size.
27
+
28
+ Attributes:
29
+ filesize (int): The actual file size in bytes.
30
+ maxsize (int): The maximum allowed size in bytes.
31
+ """
32
+
25
33
  def __init__(self, filesize: int, maxsize: int):
26
34
  self.filesize = filesize
27
35
  self.maxsize = maxsize
@@ -0,0 +1,231 @@
1
+ from datetime import date, datetime, timedelta
2
+ from typing import Type
3
+
4
+ import pytest
5
+
6
+ from stdl.dt import date_fmt, datetime_fmt, datetime_range, hms_to_seconds, seconds_to_hms, time_fmt
7
+
8
+
9
+ @pytest.mark.parametrize(
10
+ "time_input, ms, expected_output",
11
+ [
12
+ (123.456, False, "00:02:03"),
13
+ (123.456, True, "00:02:03.456"),
14
+ (123, False, "00:02:03"),
15
+ (123.0, True, "00:02:03.000"),
16
+ (-123.456, False, "-00:02:03"),
17
+ (-123.456, True, "-00:02:03.456"),
18
+ (-0.5, True, "-00:00:00.500"),
19
+ (0, False, "00:00:00"),
20
+ (0.0, True, "00:00:00.000"),
21
+ (3600, False, "01:00:00"),
22
+ (3661.5, True, "01:01:01.500"),
23
+ (90321.789, False, "25:05:21"),
24
+ (90321.789, True, "25:05:21.789"),
25
+ ],
26
+ )
27
+ def test_seconds_to_hms(time_input: float, ms: bool, expected_output: Type[Exception]):
28
+ assert seconds_to_hms(time_input, ms) == expected_output
29
+
30
+
31
+ @pytest.mark.parametrize(
32
+ "time_input, ms, expected_output, expected_exception",
33
+ [
34
+ ("00:02:03", False, 123.0, None),
35
+ ("1:02:03", False, 3723.0, None),
36
+ ("2:3", False, 123.0, None),
37
+ ("0:0:0", False, 0.0, None),
38
+ ("123", False, 123.0, None),
39
+ ("-1:02:03", False, -3723.0, None), # Negative time
40
+ ("00:02:03.456", True, 123.456, None),
41
+ ("00:02:03.4", True, 123.4, None),
42
+ ("00:02:03.45", True, 123.45, None),
43
+ ("00:02:03.045", True, 123.045, None),
44
+ ("23:59:59.999", True, 23 * 3600 + 59 * 60 + 59.999, None),
45
+ ("12:34.567", True, 12 * 60 + 34.567, None), # Valid m:s.ms format
46
+ ("00:00:00", False, 0.0, None),
47
+ ("0:0", False, 0.0, None),
48
+ ("1:60", False, None, ValueError), # Invalid seconds
49
+ ("1:02:60", False, None, ValueError), # Invalid seconds
50
+ ("invalid_time", False, None, ValueError),
51
+ ("1:2:3:4", False, None, ValueError), # Too many parts
52
+ ("12:34:56.789x", True, None, ValueError), # Invalid characters in ms
53
+ ],
54
+ )
55
+ def test_hms_to_seconds(
56
+ time_input: str,
57
+ ms: bool,
58
+ expected_output: str,
59
+ expected_exception: Type[Exception],
60
+ ):
61
+ if expected_exception:
62
+ with pytest.raises(expected_exception):
63
+ x = hms_to_seconds(time_input, ms)
64
+ print(time_input, ms, x)
65
+ else:
66
+ result = hms_to_seconds(time_input, ms)
67
+ assert result == pytest.approx(expected_output, abs=1e-9)
68
+
69
+
70
+ def test_documentation_examples():
71
+ assert seconds_to_hms(123.456) == "00:02:03"
72
+ assert seconds_to_hms(123.456, ms=True) == "00:02:03.456"
73
+ assert hms_to_seconds("00:02:03.456", ms=True) == pytest.approx(123.456)
74
+ assert hms_to_seconds("2:3", ms=True) == pytest.approx(123.0)
75
+ assert hms_to_seconds("-00:02:03", ms=False) == -123.0
76
+ assert hms_to_seconds("00:02:03", ms=False) == 123.0
77
+ assert hms_to_seconds("-1:02:03", ms=False) == pytest.approx(-3723.0)
78
+
79
+
80
+ def test_millisecond_padding():
81
+ assert hms_to_seconds("00:00:00.1", ms=True) == pytest.approx(0.1)
82
+ assert hms_to_seconds("00:00:00.01", ms=True) == pytest.approx(0.01)
83
+ assert hms_to_seconds("00:00:00.001", ms=True) == pytest.approx(0.001)
84
+
85
+
86
+ def test_negative_time_roundtrip():
87
+ original_seconds = -123.456
88
+ time_str = seconds_to_hms(original_seconds, ms=True)
89
+ parsed_seconds = hms_to_seconds(time_str, ms=True)
90
+ assert parsed_seconds == original_seconds
91
+
92
+
93
+ def test_time_fmt():
94
+ assert time_fmt(0) == "00:00:00"
95
+ assert time_fmt(0, ms=True) == "00:00:00.000"
96
+ assert time_fmt(1, ms=True) == "00:00:01.000"
97
+
98
+
99
+ def test_datetime_fmt():
100
+ assert datetime_fmt(0.0, utc=True) == "1970-01-01 00:00:00"
101
+ assert datetime_fmt(0) == "1970-01-01 00:00:00"
102
+ assert datetime_fmt(0, fmt="dmY") == "01-01-1970 00:00:00"
103
+ assert datetime_fmt(0, fmt="dmY", dsep="/") == "01/01/1970 00:00:00"
104
+ assert datetime_fmt(0, fmt="dmY", dsep="/", tsep=".") == "01/01/1970 00.00.00"
105
+
106
+
107
+ def test_date_fmt():
108
+ assert date_fmt(date(1970, 1, 1)) == "1970-01-01"
109
+ assert date_fmt(0) == "1970-01-01"
110
+
111
+
112
+ class TestDatetimeRange:
113
+ def test_basic_positive_step(self):
114
+ """Test datetime_range with positive step."""
115
+ start = datetime(2026, 1, 27, 0, 0)
116
+ end = datetime(2026, 1, 27, 3, 0)
117
+ step = timedelta(hours=1)
118
+ result = list(datetime_range(start, end, step))
119
+ expected = [
120
+ datetime(2026, 1, 27, 0, 0),
121
+ datetime(2026, 1, 27, 1, 0),
122
+ datetime(2026, 1, 27, 2, 0),
123
+ ]
124
+ assert result == expected
125
+
126
+ def test_basic_negative_step(self):
127
+ """Test datetime_range with negative step (going backwards)."""
128
+ start = datetime(2026, 1, 27, 3, 0)
129
+ end = datetime(2026, 1, 27, 0, 0)
130
+ step = timedelta(hours=-1)
131
+ result = list(datetime_range(start, end, step))
132
+ expected = [
133
+ datetime(2026, 1, 27, 3, 0),
134
+ datetime(2026, 1, 27, 2, 0),
135
+ datetime(2026, 1, 27, 1, 0),
136
+ ]
137
+ assert result == expected
138
+
139
+ def test_zero_step_raises(self):
140
+ """Test that zero step raises ValueError."""
141
+ start = datetime(2026, 1, 27, 0, 0)
142
+ end = datetime(2026, 1, 27, 3, 0)
143
+ step = timedelta(0)
144
+ with pytest.raises(ValueError, match="step cannot be zero"):
145
+ list(datetime_range(start, end, step))
146
+
147
+ def test_empty_range_positive_step(self):
148
+ """Test when start >= end with positive step."""
149
+ start = datetime(2026, 1, 27, 3, 0)
150
+ end = datetime(2026, 1, 27, 0, 0)
151
+ step = timedelta(hours=1)
152
+ result = list(datetime_range(start, end, step))
153
+ assert result == []
154
+
155
+ def test_empty_range_negative_step(self):
156
+ """Test when start <= end with negative step."""
157
+ start = datetime(2026, 1, 27, 0, 0)
158
+ end = datetime(2026, 1, 27, 3, 0)
159
+ step = timedelta(hours=-1)
160
+ result = list(datetime_range(start, end, step))
161
+ assert result == []
162
+
163
+ def test_minute_step(self):
164
+ """Test with minute-level precision."""
165
+ start = datetime(2026, 1, 27, 0, 0)
166
+ end = datetime(2026, 1, 27, 0, 5)
167
+ step = timedelta(minutes=1)
168
+ result = list(datetime_range(start, end, step))
169
+ expected = [
170
+ datetime(2026, 1, 27, 0, 0),
171
+ datetime(2026, 1, 27, 0, 1),
172
+ datetime(2026, 1, 27, 0, 2),
173
+ datetime(2026, 1, 27, 0, 3),
174
+ datetime(2026, 1, 27, 0, 4),
175
+ ]
176
+ assert result == expected
177
+
178
+ def test_second_step(self):
179
+ """Test with second-level precision."""
180
+ start = datetime(2026, 1, 27, 0, 0, 0)
181
+ end = datetime(2026, 1, 27, 0, 0, 3)
182
+ step = timedelta(seconds=1)
183
+ result = list(datetime_range(start, end, step))
184
+ expected = [
185
+ datetime(2026, 1, 27, 0, 0, 0),
186
+ datetime(2026, 1, 27, 0, 0, 1),
187
+ datetime(2026, 1, 27, 0, 0, 2),
188
+ ]
189
+ assert result == expected
190
+
191
+ def test_day_step(self):
192
+ """Test with day-level step across multiple days."""
193
+ start = datetime(2026, 1, 27)
194
+ end = datetime(2026, 1, 30)
195
+ step = timedelta(days=1)
196
+ result = list(datetime_range(start, end, step))
197
+ expected = [
198
+ datetime(2026, 1, 27),
199
+ datetime(2026, 1, 28),
200
+ datetime(2026, 1, 29),
201
+ ]
202
+ assert result == expected
203
+
204
+ def test_partial_step(self):
205
+ """Test when end is not exactly reachable by step."""
206
+ start = datetime(2026, 1, 27, 0, 0)
207
+ end = datetime(2026, 1, 27, 2, 30)
208
+ step = timedelta(hours=1)
209
+ result = list(datetime_range(start, end, step))
210
+ expected = [
211
+ datetime(2026, 1, 27, 0, 0),
212
+ datetime(2026, 1, 27, 1, 0),
213
+ datetime(2026, 1, 27, 2, 0),
214
+ ]
215
+ assert result == expected
216
+
217
+ def test_documentation_example(self):
218
+ """Test the exact example from the docstring."""
219
+ result = list(
220
+ datetime_range(
221
+ datetime(2026, 1, 27, 0, 0),
222
+ datetime(2026, 1, 27, 3, 0),
223
+ timedelta(hours=1),
224
+ )
225
+ )
226
+ expected = [
227
+ datetime(2026, 1, 27, 0, 0),
228
+ datetime(2026, 1, 27, 1, 0),
229
+ datetime(2026, 1, 27, 2, 0),
230
+ ]
231
+ assert result == expected
@@ -1,109 +0,0 @@
1
- from datetime import date
2
- from typing import Type
3
-
4
- import pytest
5
-
6
- from stdl.dt import date_fmt, datetime_fmt, hms_to_seconds, seconds_to_hms, time_fmt
7
-
8
-
9
- @pytest.mark.parametrize(
10
- "time_input, ms, expected_output",
11
- [
12
- (123.456, False, "00:02:03"),
13
- (123.456, True, "00:02:03.456"),
14
- (123, False, "00:02:03"),
15
- (123.0, True, "00:02:03.000"),
16
- (-123.456, False, "-00:02:03"),
17
- (-123.456, True, "-00:02:03.456"),
18
- (-0.5, True, "-00:00:00.500"),
19
- (0, False, "00:00:00"),
20
- (0.0, True, "00:00:00.000"),
21
- (3600, False, "01:00:00"),
22
- (3661.5, True, "01:01:01.500"),
23
- (90321.789, False, "25:05:21"),
24
- (90321.789, True, "25:05:21.789"),
25
- ],
26
- )
27
- def test_seconds_to_hms(time_input: float, ms: bool, expected_output: Type[Exception]):
28
- assert seconds_to_hms(time_input, ms) == expected_output
29
-
30
-
31
- @pytest.mark.parametrize(
32
- "time_input, ms, expected_output, expected_exception",
33
- [
34
- ("00:02:03", False, 123.0, None),
35
- ("1:02:03", False, 3723.0, None),
36
- ("2:3", False, 123.0, None),
37
- ("0:0:0", False, 0.0, None),
38
- ("123", False, 123.0, None),
39
- ("-1:02:03", False, -3723.0, None), # Negative time
40
- ("00:02:03.456", True, 123.456, None),
41
- ("00:02:03.4", True, 123.4, None),
42
- ("00:02:03.45", True, 123.45, None),
43
- ("00:02:03.045", True, 123.045, None),
44
- ("23:59:59.999", True, 23 * 3600 + 59 * 60 + 59.999, None),
45
- ("12:34.567", True, 12 * 60 + 34.567, None), # Valid m:s.ms format
46
- ("00:00:00", False, 0.0, None),
47
- ("0:0", False, 0.0, None),
48
- ("1:60", False, None, ValueError), # Invalid seconds
49
- ("1:02:60", False, None, ValueError), # Invalid seconds
50
- ("invalid_time", False, None, ValueError),
51
- ("1:2:3:4", False, None, ValueError), # Too many parts
52
- ("12:34:56.789x", True, None, ValueError), # Invalid characters in ms
53
- ],
54
- )
55
- def test_hms_to_seconds(
56
- time_input: str,
57
- ms: bool,
58
- expected_output: str,
59
- expected_exception: Type[Exception],
60
- ):
61
- if expected_exception:
62
- with pytest.raises(expected_exception):
63
- x = hms_to_seconds(time_input, ms)
64
- print(time_input, ms, x)
65
- else:
66
- result = hms_to_seconds(time_input, ms)
67
- assert result == pytest.approx(expected_output, abs=1e-9)
68
-
69
-
70
- def test_documentation_examples():
71
- assert seconds_to_hms(123.456) == "00:02:03"
72
- assert seconds_to_hms(123.456, ms=True) == "00:02:03.456"
73
- assert hms_to_seconds("00:02:03.456", ms=True) == pytest.approx(123.456)
74
- assert hms_to_seconds("2:3", ms=True) == pytest.approx(123.0)
75
- assert hms_to_seconds("-00:02:03", ms=False) == -123.0
76
- assert hms_to_seconds("00:02:03", ms=False) == 123.0
77
- assert hms_to_seconds("-1:02:03", ms=False) == pytest.approx(-3723.0)
78
-
79
-
80
- def test_millisecond_padding():
81
- assert hms_to_seconds("00:00:00.1", ms=True) == pytest.approx(0.1)
82
- assert hms_to_seconds("00:00:00.01", ms=True) == pytest.approx(0.01)
83
- assert hms_to_seconds("00:00:00.001", ms=True) == pytest.approx(0.001)
84
-
85
-
86
- def test_negative_time_roundtrip():
87
- original_seconds = -123.456
88
- time_str = seconds_to_hms(original_seconds, ms=True)
89
- parsed_seconds = hms_to_seconds(time_str, ms=True)
90
- assert parsed_seconds == original_seconds
91
-
92
-
93
- def test_time_fmt():
94
- assert time_fmt(0) == "00:00:00"
95
- assert time_fmt(0, ms=True) == "00:00:00.000"
96
- assert time_fmt(1, ms=True) == "00:00:01.000"
97
-
98
-
99
- def test_datetime_fmt():
100
- assert datetime_fmt(0.0, utc=True) == "1970-01-01 00:00:00"
101
- assert datetime_fmt(0) == "1970-01-01 00:00:00"
102
- assert datetime_fmt(0, fmt="dmY") == "01-01-1970 00:00:00"
103
- assert datetime_fmt(0, fmt="dmY", dsep="/") == "01/01/1970 00:00:00"
104
- assert datetime_fmt(0, fmt="dmY", dsep="/", tsep=".") == "01/01/1970 00.00.00"
105
-
106
-
107
- def test_date_fmt():
108
- assert date_fmt(date(1970, 1, 1)) == "1970-01-01"
109
- assert date_fmt(0) == "1970-01-01"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes