dycw-utilities 0.176.3__py3-none-any.whl → 0.176.5__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dycw-utilities
3
- Version: 0.176.3
3
+ Version: 0.176.5
4
4
  Summary: Miscellaneous Python utilities
5
5
  Author: Derek Wan
6
6
  Author-email: Derek Wan <d.wan@icloud.com>
@@ -10,7 +10,7 @@ Requires-Dist: tzlocal>=5.3.1,<6
10
10
  Requires-Dist: whenever>=0.9.4,<1
11
11
  Requires-Dist: coloredlogs>=15.0.1,<16 ; extra == 'logging'
12
12
  Requires-Dist: dycw-pytest-only>=2.1.1,<3 ; extra == 'test'
13
- Requires-Dist: hypothesis>=6.148.13,<7 ; extra == 'test'
13
+ Requires-Dist: hypothesis>=6.149.1,<7 ; extra == 'test'
14
14
  Requires-Dist: pytest>=9.0.2,<10 ; extra == 'test'
15
15
  Requires-Dist: pytest-asyncio>=1.3.0,<2 ; extra == 'test'
16
16
  Requires-Dist: pytest-cov>=7.0.0,<8 ; extra == 'test'
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=dudRl7LB05YsPVxe-Pt7y8Ta-J6klGP0xDRX8eVCGnU,60
1
+ utilities/__init__.py,sha256=chOgc1NV48pH7zK_NN0RHPlWQSfdIFGjwBbOXK_Es2A,60
2
2
  utilities/altair.py,sha256=TLfRFbG9HwG7SLXoJ-v0r-t49ZaGgTQZD82cpjVi4vs,9085
3
3
  utilities/asyncio.py,sha256=aJySVxBY0gqsIYnoNmH7-1r8djKuf4vSsU69VCD08t8,16772
4
4
  utilities/atomicwrites.py,sha256=tPo6r-Rypd9u99u66B9z86YBPpnLrlHtwox_8Z7T34Y,5790
@@ -44,6 +44,7 @@ utilities/operator.py,sha256=C3NylZWGTVWRpwYHOPVhaLgRhw0DfpS4_XQ8KfPhBLQ,3613
44
44
  utilities/optuna.py,sha256=C-fhWYiXHVPo1l8QctYkFJ4DyhbSrGorzP1dJb_qvd8,1933
45
45
  utilities/orjson.py,sha256=T_0SlK811ysg46d3orvIPY3JpBa4FRMpP2wlPQo7-gU,41854
46
46
  utilities/os.py,sha256=kjKKSQfnRqFTTZ315iavaaGd3gGuYNoSWlxVLCJjyQs,4852
47
+ utilities/packaging.py,sha256=kuGenKbyAwxZWL_mbkMzkNXKjhyi9a9tfZQwitrYYlM,2401
47
48
  utilities/parse.py,sha256=g7Qm9eBOIeDId2tGA021CIaeF6jp1TI8rx4srdvlyoo,17937
48
49
  utilities/pathlib.py,sha256=N4Ip8R9eCM-6GfvxUJ3T9oQIle2C2P52F-13BCFRdTg,9345
49
50
  utilities/permissions.py,sha256=vLXlWztSVYffbrxptne7ksj6dU1HLekm4fEvS4ny_4Q,8944
@@ -80,7 +81,7 @@ utilities/sqlalchemy.py,sha256=HQYpd7LFxdTF5WYVWYtCJeEBI71EJm7ytvCGyAH9B-U,37163
80
81
  utilities/sqlalchemy_polars.py,sha256=JCGhB37raSR7fqeWV5dTsciRTMVzIdVT9YSqKT0piT0,13370
81
82
  utilities/statsmodels.py,sha256=koyiBHvpMcSiBfh99wFUfSggLNx7cuAw3rwyfAhoKpQ,3410
82
83
  utilities/string.py,sha256=shmBK87zZwzGyixuNuXCiUbqzfeZ9xlrFwz6JTaRvDk,582
83
- utilities/subprocess.py,sha256=AEJ5xxwNfdR_hZ-dGvHpf7Qsr-_C2iXhC-IA2dkVwV4,53827
84
+ utilities/subprocess.py,sha256=eGRRQF-OXDy2kMSy7dv4fUzcPizvRgGBxYvPh-QVqZo,53980
84
85
  utilities/tempfile.py,sha256=4kRGd4hyINDX4hpcYMtwzDcd-4IKjpTWT0MnjnKD4hE,4221
85
86
  utilities/testbook.py,sha256=j1KmaVbrX9VrbeMgtPh5gk55myAsn3dyRUn7jGbPbRk,1294
86
87
  utilities/text.py,sha256=UKW6xtF4bb0i-Gu1jctJvvQmeXk6U6UYNqoEnCLdaOo,14102
@@ -97,7 +98,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
97
98
  utilities/whenever.py,sha256=F4ek0-OBWxHYrZdmoZt76N2RnNyKY5KrEHt7rqO4AQE,60183
98
99
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
99
100
  utilities/zoneinfo.py,sha256=tdIScrTB2-B-LH0ukb1HUXKooLknOfJNwHk10MuMYvA,3619
100
- dycw_utilities-0.176.3.dist-info/WHEEL,sha256=RRVLqVugUmFOqBedBFAmA4bsgFcROUBiSUKlERi0Hcg,79
101
- dycw_utilities-0.176.3.dist-info/entry_points.txt,sha256=cOGtKeJI0KXLSV7MJ8Dhc2G8jPgDcBDm53MVNJU4ycI,136
102
- dycw_utilities-0.176.3.dist-info/METADATA,sha256=lENsiNzcZ72rEXxx9bVVLq2Tdy-QSohppEgvMY0xkPs,1399
103
- dycw_utilities-0.176.3.dist-info/RECORD,,
101
+ dycw_utilities-0.176.5.dist-info/WHEEL,sha256=KSLUh82mDPEPk0Bx0ScXlWL64bc8KmzIPNcpQZFV-6E,79
102
+ dycw_utilities-0.176.5.dist-info/entry_points.txt,sha256=cOGtKeJI0KXLSV7MJ8Dhc2G8jPgDcBDm53MVNJU4ycI,136
103
+ dycw_utilities-0.176.5.dist-info/METADATA,sha256=g6FzLC0r4g8uwbM_zouJhCaaHM0IsQ1q33xQyS5tZp0,1398
104
+ dycw_utilities-0.176.5.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.21
2
+ Generator: uv 0.9.22
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.176.3"
3
+ __version__ = "0.176.5"
utilities/packaging.py ADDED
@@ -0,0 +1,87 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING, Self, override
5
+
6
+ import packaging._parser
7
+ import packaging.requirements
8
+ from packaging.requirements import _parse_requirement
9
+ from packaging.specifiers import Specifier, SpecifierSet
10
+
11
+ from utilities.iterables import OneEmptyError, one
12
+
13
+ if TYPE_CHECKING:
14
+ from packaging._parser import MarkerList
15
+
16
+
17
+ @dataclass(order=True, unsafe_hash=True, kw_only=True, slots=True)
18
+ class Requirement:
19
+ requirement: str
20
+ _parsed_req: packaging._parser.ParsedRequirement
21
+ _custom_req: _CustomRequirement
22
+
23
+ def __getitem__(self, key: str, /) -> str:
24
+ return self.specifier_set[key]
25
+
26
+ @override
27
+ def __str__(self) -> str:
28
+ return str(self._custom_req)
29
+
30
+ @classmethod
31
+ def new(cls, requirement: str, /) -> Self:
32
+ return cls(
33
+ requirement=requirement,
34
+ _parsed_req=_parse_requirement(requirement),
35
+ _custom_req=_CustomRequirement(requirement),
36
+ )
37
+
38
+ @property
39
+ def extras(self) -> list[str]:
40
+ return self._parsed_req.extras
41
+
42
+ @property
43
+ def marker(self) -> MarkerList | None:
44
+ return self._parsed_req.marker
45
+
46
+ @property
47
+ def name(self) -> str:
48
+ return self._parsed_req.name
49
+
50
+ @property
51
+ def specifier(self) -> str:
52
+ return self._parsed_req.specifier
53
+
54
+ @property
55
+ def specifier_set(self) -> _CustomSpecifierSet:
56
+ return _CustomSpecifierSet(_parse_requirement(self.requirement).specifier)
57
+
58
+ @property
59
+ def url(self) -> str:
60
+ return self._parsed_req.url
61
+
62
+
63
+ class _CustomRequirement(packaging.requirements.Requirement):
64
+ @override
65
+ def __init__(self, requirement_string: str) -> None:
66
+ super().__init__(requirement_string)
67
+ parsed = _parse_requirement(requirement_string)
68
+ self.specifier = _CustomSpecifierSet(parsed.specifier)
69
+
70
+
71
+ class _CustomSpecifierSet(SpecifierSet):
72
+ def __getitem__(self, key: str, /) -> str:
73
+ try:
74
+ return one(s.version for s in self if s.operator == key)
75
+ except OneEmptyError:
76
+ raise KeyError(key) from None
77
+
78
+ @override
79
+ def __str__(self) -> str:
80
+ specs = sorted(self._specs, key=self._key)
81
+ return ", ".join(map(str, specs))
82
+
83
+ def _key(self, spec: Specifier, /) -> int:
84
+ return [">=", "<"].index(spec.operator)
85
+
86
+
87
+ __all__ = ["Requirement"]
utilities/subprocess.py CHANGED
@@ -994,10 +994,15 @@ def _rsync_many_prepare(
994
994
  case Path():
995
995
  cp(src, temp_src / name)
996
996
  case str():
997
- if Path(src).exists():
998
- cp(src, temp_src / name)
999
- else:
997
+ try:
998
+ exists = Path(src).exists()
999
+ except OSError:
1000
1000
  tee(temp_src / name, src)
1001
+ else:
1002
+ if exists:
1003
+ cp(src, temp_src / name)
1004
+ else:
1005
+ tee(temp_src / name, src)
1001
1006
  case never:
1002
1007
  assert_never(never)
1003
1008
  cmds: list[list[str]] = [