dycw-utilities 0.174.10__tar.gz → 0.174.11__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 (103) hide show
  1. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/PKG-INFO +1 -1
  2. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/pyproject.toml +2 -2
  3. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/__init__.py +1 -1
  4. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/permissions.py +83 -59
  5. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/README.md +0 -0
  6. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/aeventkit.py +0 -0
  7. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/altair.py +0 -0
  8. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/asyncio.py +0 -0
  9. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/atomicwrites.py +0 -0
  10. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/atools.py +0 -0
  11. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/cachetools.py +0 -0
  12. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/click.py +0 -0
  13. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/concurrent.py +0 -0
  14. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/contextlib.py +0 -0
  15. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/contextvars.py +0 -0
  16. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/cryptography.py +0 -0
  17. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/cvxpy.py +0 -0
  18. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/dataclasses.py +0 -0
  19. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/docker.py +0 -0
  20. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/enum.py +0 -0
  21. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/errors.py +0 -0
  22. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/fastapi.py +0 -0
  23. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/fpdf2.py +0 -0
  24. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/functions.py +0 -0
  25. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/functools.py +0 -0
  26. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/getpass.py +0 -0
  27. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/git.py +0 -0
  28. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/grp.py +0 -0
  29. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/gzip.py +0 -0
  30. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/hashlib.py +0 -0
  31. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/http.py +0 -0
  32. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/hypothesis.py +0 -0
  33. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/importlib.py +0 -0
  34. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/inflect.py +0 -0
  35. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/ipython.py +0 -0
  36. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/iterables.py +0 -0
  37. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/jinja2.py +0 -0
  38. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/json.py +0 -0
  39. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/jupyter.py +0 -0
  40. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/libcst.py +0 -0
  41. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/lightweight_charts.py +0 -0
  42. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/logging.py +0 -0
  43. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/math.py +0 -0
  44. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/memory_profiler.py +0 -0
  45. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/modules.py +0 -0
  46. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/more_itertools.py +0 -0
  47. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/numpy.py +0 -0
  48. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/operator.py +0 -0
  49. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/optuna.py +0 -0
  50. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/orjson.py +0 -0
  51. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/os.py +0 -0
  52. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/parse.py +0 -0
  53. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/pathlib.py +0 -0
  54. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/pickle.py +0 -0
  55. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/platform.py +0 -0
  56. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/polars.py +0 -0
  57. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/polars_ols.py +0 -0
  58. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/postgres.py +0 -0
  59. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/pottery.py +0 -0
  60. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/pqdm.py +0 -0
  61. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/psutil.py +0 -0
  62. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/pwd.py +0 -0
  63. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/py.typed +0 -0
  64. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/pydantic.py +0 -0
  65. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/pydantic_settings.py +0 -0
  66. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/pydantic_settings_sops.py +0 -0
  67. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/pyinstrument.py +0 -0
  68. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/pytest.py +0 -0
  69. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/pytest_plugins/__init__.py +0 -0
  70. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/pytest_plugins/pytest_randomly.py +0 -0
  71. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/pytest_plugins/pytest_regressions.py +0 -0
  72. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/pytest_regressions.py +0 -0
  73. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/random.py +0 -0
  74. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/re.py +0 -0
  75. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/redis.py +0 -0
  76. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/reprlib.py +0 -0
  77. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/scipy.py +0 -0
  78. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/sentinel.py +0 -0
  79. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/shelve.py +0 -0
  80. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/shutil.py +0 -0
  81. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/slack_sdk.py +0 -0
  82. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/socket.py +0 -0
  83. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/sqlalchemy.py +0 -0
  84. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/sqlalchemy_polars.py +0 -0
  85. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/statsmodels.py +0 -0
  86. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/string.py +0 -0
  87. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/subprocess.py +0 -0
  88. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/tempfile.py +0 -0
  89. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/testbook.py +0 -0
  90. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/text.py +0 -0
  91. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/threading.py +0 -0
  92. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/timer.py +0 -0
  93. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/traceback.py +0 -0
  94. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/types.py +0 -0
  95. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/typing.py +0 -0
  96. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/tzdata.py +0 -0
  97. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/tzlocal.py +0 -0
  98. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/uuid.py +0 -0
  99. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/version.py +0 -0
  100. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/warnings.py +0 -0
  101. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/whenever.py +0 -0
  102. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/zipfile.py +0 -0
  103. {dycw_utilities-0.174.10 → dycw_utilities-0.174.11}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dycw-utilities
3
- Version: 0.174.10
3
+ Version: 0.174.11
4
4
  Author: Derek Wan
5
5
  Author-email: Derek Wan <d.wan@icloud.com>
6
6
  Requires-Dist: atomicwrites>=1.4.1,<1.5
@@ -101,7 +101,7 @@
101
101
  name = "dycw-utilities"
102
102
  readme = "README.md"
103
103
  requires-python = ">= 3.12"
104
- version = "0.174.10"
104
+ version = "0.174.11"
105
105
 
106
106
  [project.entry-points.pytest11]
107
107
  pytest-randomly = "utilities.pytest_plugins.pytest_randomly"
@@ -135,7 +135,7 @@
135
135
  # bump-my-version
136
136
  [tool.bumpversion]
137
137
  allow_dirty = true
138
- current_version = "0.174.10"
138
+ current_version = "0.174.11"
139
139
 
140
140
  [[tool.bumpversion.files]]
141
141
  filename = "src/utilities/__init__.py"
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.174.10"
3
+ __version__ = "0.174.11"
@@ -14,15 +14,38 @@ from stat import (
14
14
  S_IXOTH,
15
15
  S_IXUSR,
16
16
  )
17
- from typing import Literal, Self, override
17
+ from typing import Literal, Self, assert_never, override
18
18
 
19
19
  from utilities.dataclasses import replace_non_sentinel
20
20
  from utilities.re import ExtractGroupsError, extract_groups
21
21
  from utilities.sentinel import Sentinel, sentinel
22
22
 
23
+ type PermissionsLike = Permissions | int | str
24
+
25
+
26
+ ##
27
+
28
+
29
+ def ensure_perms(perms: PermissionsLike, /) -> Permissions:
30
+ """Ensure a set of file permissions."""
31
+ match perms:
32
+ case Permissions():
33
+ return perms
34
+ case int():
35
+ return Permissions.from_int(perms)
36
+ case str():
37
+ return Permissions.from_text(perms)
38
+ case never:
39
+ assert_never(never)
40
+
41
+
42
+ ##
43
+
23
44
 
24
45
  @dataclass(order=True, unsafe_hash=True, kw_only=True, slots=True)
25
46
  class Permissions:
47
+ """A set of file permissions."""
48
+
26
49
  user_read: bool = False
27
50
  user_write: bool = False
28
51
  user_execute: bool = False
@@ -34,26 +57,18 @@ class Permissions:
34
57
  others_execute: bool = False
35
58
 
36
59
  def __int__(self) -> int:
37
- return (
38
- 100
39
- * self._int(
40
- read=self.user_read, write=self.user_write, execute=self.user_execute
41
- )
42
- + 10
43
- * self._int(
44
- read=self.group_read, write=self.group_write, execute=self.group_execute
45
- )
46
- + self._int(
47
- read=self.others_read,
48
- write=self.others_write,
49
- execute=self.others_execute,
50
- )
51
- )
52
-
53
- def _int(
54
- self, *, read: bool = False, write: bool = False, execute: bool = False
55
- ) -> int:
56
- return (4 if read else 0) + (2 if write else 0) + (1 if execute else 0)
60
+ flags: list[int] = [
61
+ S_IRUSR if self.user_read else 0,
62
+ S_IWUSR if self.user_write else 0,
63
+ S_IXUSR if self.user_execute else 0,
64
+ S_IRGRP if self.group_read else 0,
65
+ S_IWGRP if self.group_write else 0,
66
+ S_IXGRP if self.group_execute else 0,
67
+ S_IROTH if self.others_read else 0,
68
+ S_IWOTH if self.others_write else 0,
69
+ S_IXOTH if self.others_execute else 0,
70
+ ]
71
+ return reduce(or_, flags)
57
72
 
58
73
  @override
59
74
  def __repr__(self) -> str:
@@ -87,13 +102,11 @@ class Permissions:
87
102
  write: bool = False,
88
103
  execute: bool = False,
89
104
  ) -> str:
90
- parts: list[str] = []
91
- if read:
92
- parts.append("r")
93
- if write:
94
- parts.append("w")
95
- if execute:
96
- parts.append("x")
105
+ parts: list[str] = [
106
+ "r" if read else "",
107
+ "w" if write else "",
108
+ "x" if execute else "",
109
+ ]
97
110
  return f"{prefix}={''.join(parts)}"
98
111
 
99
112
  @override
@@ -101,12 +114,12 @@ class Permissions:
101
114
  return repr(self)
102
115
 
103
116
  @classmethod
104
- def from_int(cls, n: int, /) -> Self:
117
+ def from_human_int(cls, n: int, /) -> Self:
105
118
  if not (0 <= n <= 777):
106
- raise PermissionsFromIntRangeError(n=n)
107
- user_read, user_write, user_execute = cls._from_int(n, (n // 100) % 10)
108
- group_read, group_write, group_execute = cls._from_int(n, (n // 10) % 10)
109
- others_read, others_write, others_execute = cls._from_int(n, n % 10)
119
+ raise PermissionsFromHumanIntRangeError(n=n)
120
+ user_read, user_write, user_execute = cls._from_human_int(n, (n // 100) % 10)
121
+ group_read, group_write, group_execute = cls._from_human_int(n, (n // 10) % 10)
122
+ others_read, others_write, others_execute = cls._from_human_int(n, n % 10)
110
123
  return cls(
111
124
  user_read=user_read,
112
125
  user_write=user_write,
@@ -120,13 +133,13 @@ class Permissions:
120
133
  )
121
134
 
122
135
  @classmethod
123
- def _from_int(cls, n: int, digit: int, /) -> tuple[bool, bool, bool]:
136
+ def _from_human_int(cls, n: int, digit: int, /) -> tuple[bool, bool, bool]:
124
137
  if not (0 <= digit <= 7):
125
- raise PermissionsFromIntDigitError(n=n, digit=digit)
138
+ raise PermissionsFromHumanIntDigitError(n=n, digit=digit)
126
139
  return bool(4 & digit), bool(2 & digit), bool(1 & digit)
127
140
 
128
141
  @classmethod
129
- def from_octal(cls, n: int, /) -> Self:
142
+ def from_int(cls, n: int, /) -> Self:
130
143
  if 0o0 <= n <= 0o777:
131
144
  return cls(
132
145
  user_read=bool(n & S_IRUSR),
@@ -139,7 +152,7 @@ class Permissions:
139
152
  others_write=bool(n & S_IWOTH),
140
153
  others_execute=bool(n & S_IXOTH),
141
154
  )
142
- raise PermissionsFromOctalError(n=n)
155
+ raise PermissionsFromIntError(n=n)
143
156
 
144
157
  @classmethod
145
158
  def from_text(cls, text: str, /) -> Self:
@@ -170,19 +183,27 @@ class Permissions:
170
183
  return read != "", write != "", execute != ""
171
184
 
172
185
  @property
173
- def octal(self) -> int:
174
- flags: list[int] = [
175
- S_IRUSR if self.user_read else 0,
176
- S_IWUSR if self.user_write else 0,
177
- S_IXUSR if self.user_execute else 0,
178
- S_IRGRP if self.group_read else 0,
179
- S_IWGRP if self.group_write else 0,
180
- S_IXGRP if self.group_execute else 0,
181
- S_IROTH if self.others_read else 0,
182
- S_IWOTH if self.others_write else 0,
183
- S_IXOTH if self.others_execute else 0,
184
- ]
185
- return reduce(or_, flags)
186
+ def human_int(self) -> int:
187
+ return (
188
+ 100
189
+ * self._human_int(
190
+ read=self.user_read, write=self.user_write, execute=self.user_execute
191
+ )
192
+ + 10
193
+ * self._human_int(
194
+ read=self.group_read, write=self.group_write, execute=self.group_execute
195
+ )
196
+ + self._human_int(
197
+ read=self.others_read,
198
+ write=self.others_write,
199
+ execute=self.others_execute,
200
+ )
201
+ )
202
+
203
+ def _human_int(
204
+ self, *, read: bool = False, write: bool = False, execute: bool = False
205
+ ) -> int:
206
+ return (4 if read else 0) + (2 if write else 0) + (1 if execute else 0)
186
207
 
187
208
  def replace(
188
209
  self,
@@ -216,33 +237,35 @@ class PermissionsError(Exception): ...
216
237
 
217
238
 
218
239
  @dataclass(kw_only=True, slots=True)
219
- class PermissionsFromIntError(PermissionsError):
240
+ class PermissionsFromHumanIntError(PermissionsError):
220
241
  n: int
221
242
 
222
243
 
223
244
  @dataclass(kw_only=True, slots=True)
224
- class PermissionsFromIntRangeError(PermissionsFromIntError):
245
+ class PermissionsFromHumanIntRangeError(PermissionsFromHumanIntError):
225
246
  @override
226
247
  def __str__(self) -> str:
227
- return f"Invalid integer for permissions; got {self.n}"
248
+ return f"Invalid human integer for permissions; got {self.n}"
228
249
 
229
250
 
230
251
  @dataclass(kw_only=True, slots=True)
231
- class PermissionsFromIntDigitError(PermissionsFromIntError):
252
+ class PermissionsFromHumanIntDigitError(PermissionsFromHumanIntError):
232
253
  digit: int
233
254
 
234
255
  @override
235
256
  def __str__(self) -> str:
236
- return f"Invalid integer for permissions; got digit {self.digit} in {self.n}"
257
+ return (
258
+ f"Invalid human integer for permissions; got digit {self.digit} in {self.n}"
259
+ )
237
260
 
238
261
 
239
262
  @dataclass(kw_only=True, slots=True)
240
- class PermissionsFromOctalError(PermissionsError):
263
+ class PermissionsFromIntError(PermissionsError):
241
264
  n: int
242
265
 
243
266
  @override
244
267
  def __str__(self) -> str:
245
- return f"Invalid octal for permissions; got {oct(self.n)}"
268
+ return f"Invalid integer for permissions; got {self.n} = {oct(self.n)}"
246
269
 
247
270
 
248
271
  @dataclass(kw_only=True, slots=True)
@@ -257,8 +280,9 @@ class PermissionsFromTextError(PermissionsError):
257
280
  __all__ = [
258
281
  "Permissions",
259
282
  "PermissionsError",
260
- "PermissionsFromIntDigitError",
283
+ "PermissionsFromHumanIntDigitError",
284
+ "PermissionsFromHumanIntError",
261
285
  "PermissionsFromIntError",
262
- "PermissionsFromOctalError",
263
286
  "PermissionsFromTextError",
287
+ "ensure_perms",
264
288
  ]