dycw-utilities 0.125.3__py3-none-any.whl → 0.125.4__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.
- {dycw_utilities-0.125.3.dist-info → dycw_utilities-0.125.4.dist-info}/METADATA +3 -3
- {dycw_utilities-0.125.3.dist-info → dycw_utilities-0.125.4.dist-info}/RECORD +6 -5
- utilities/__init__.py +1 -1
- utilities/libcst.py +184 -0
- {dycw_utilities-0.125.3.dist-info → dycw_utilities-0.125.4.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.125.3.dist-info → dycw_utilities-0.125.4.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: dycw-utilities
|
3
|
-
Version: 0.125.
|
3
|
+
Version: 0.125.4
|
4
4
|
Author-email: Derek Wan <d.wan@icloud.com>
|
5
5
|
License-File: LICENSE
|
6
6
|
Requires-Python: >=3.12
|
@@ -10,7 +10,7 @@ Requires-Dist: hypothesis<6.132,>=6.131.27; extra == 'test'
|
|
10
10
|
Requires-Dist: pytest-asyncio<0.27,>=0.26.0; extra == 'test'
|
11
11
|
Requires-Dist: pytest-cov<6.2,>=6.1.1; extra == 'test'
|
12
12
|
Requires-Dist: pytest-instafail<0.6,>=0.5.0; extra == 'test'
|
13
|
-
Requires-Dist: pytest-lazy-fixtures<1.2,>=1.1.
|
13
|
+
Requires-Dist: pytest-lazy-fixtures<1.2,>=1.1.3; extra == 'test'
|
14
14
|
Requires-Dist: pytest-only<2.2,>=2.1.2; extra == 'test'
|
15
15
|
Requires-Dist: pytest-randomly<3.17,>=3.16.0; extra == 'test'
|
16
16
|
Requires-Dist: pytest-regressions<2.8,>=2.7.0; extra == 'test'
|
@@ -174,7 +174,7 @@ Requires-Dist: scipy<1.16,>=1.15.3; extra == 'zzz-test-scipy'
|
|
174
174
|
Provides-Extra: zzz-test-sentinel
|
175
175
|
Provides-Extra: zzz-test-shelve
|
176
176
|
Provides-Extra: zzz-test-slack-sdk
|
177
|
-
Requires-Dist: aiohttp<3.
|
177
|
+
Requires-Dist: aiohttp<3.13,>=3.12.0; extra == 'zzz-test-slack-sdk'
|
178
178
|
Requires-Dist: slack-sdk<3.36,>=3.35.0; extra == 'zzz-test-slack-sdk'
|
179
179
|
Provides-Extra: zzz-test-socket
|
180
180
|
Provides-Extra: zzz-test-sqlalchemy
|
@@ -1,4 +1,4 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=O9WCgPzIqylIlRxvuTI9h_FJzryWHiUMdYDZGV6Rhj8,60
|
2
2
|
utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
|
3
3
|
utilities/asyncio.py,sha256=gr2eUx0E6LiCup6VKgUGwh8lAUriGdX2TlK-PZdlvfo,28284
|
4
4
|
utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
|
@@ -28,6 +28,7 @@ utilities/importlib.py,sha256=mV1xT_O_zt_GnZZ36tl3xOmMaN_3jErDWY54fX39F6Y,429
|
|
28
28
|
utilities/ipython.py,sha256=V2oMYHvEKvlNBzxDXdLvKi48oUq2SclRg5xasjaXStw,763
|
29
29
|
utilities/iterables.py,sha256=prKXBdF5QfLTGC-q4567DwO8xzUng_Z-2a4wBkMqyDo,45360
|
30
30
|
utilities/jupyter.py,sha256=ft5JA7fBxXKzP-L9W8f2-wbF0QeYc_2uLQNFDVk4Z-M,2917
|
31
|
+
utilities/libcst.py,sha256=jGLm2vTwUFcCIghN66mDSUXesgZZ6Pw2BbixkUf-PHA,5062
|
31
32
|
utilities/lightweight_charts.py,sha256=vyVOzarYhBIOZj2xDhqdbP85qbSKUjdc6Au91rc1W4M,2814
|
32
33
|
utilities/logging.py,sha256=gwo3pusPjnWO1ollrtn1VKYyRAQJTue4SkCbMeNvec4,25715
|
33
34
|
utilities/loguru.py,sha256=MEMQVWrdECxk1e3FxGzmOf21vWT9j8CAir98SEXFKPA,3809
|
@@ -87,7 +88,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
|
|
87
88
|
utilities/whenever.py,sha256=jS31ZAY5OMxFxLja_Yo5Fidi87Pd-GoVZ7Vi_teqVDA,16743
|
88
89
|
utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
|
89
90
|
utilities/zoneinfo.py,sha256=-5j7IQ9nb7gR43rdgA7ms05im-XuqhAk9EJnQBXxCoQ,1874
|
90
|
-
dycw_utilities-0.125.
|
91
|
-
dycw_utilities-0.125.
|
92
|
-
dycw_utilities-0.125.
|
93
|
-
dycw_utilities-0.125.
|
91
|
+
dycw_utilities-0.125.4.dist-info/METADATA,sha256=dwOemW5k0Dz8lVM3bRKczDGMMkRNTW41Np8Bi_13dEU,12851
|
92
|
+
dycw_utilities-0.125.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
93
|
+
dycw_utilities-0.125.4.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
94
|
+
dycw_utilities-0.125.4.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/libcst.py
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from collections.abc import Sequence
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from subprocess import check_output
|
6
|
+
from typing import assert_never, override
|
7
|
+
|
8
|
+
from libcst import (
|
9
|
+
AsName,
|
10
|
+
Attribute,
|
11
|
+
BaseExpression,
|
12
|
+
FormattedString,
|
13
|
+
FormattedStringExpression,
|
14
|
+
FormattedStringText,
|
15
|
+
Import,
|
16
|
+
ImportAlias,
|
17
|
+
ImportFrom,
|
18
|
+
ImportStar,
|
19
|
+
Module,
|
20
|
+
Name,
|
21
|
+
)
|
22
|
+
|
23
|
+
|
24
|
+
def generate_from_import(
|
25
|
+
module: str, name: str, /, *, asname: str | None = None
|
26
|
+
) -> ImportFrom:
|
27
|
+
"""Generate an `ImportFrom` object."""
|
28
|
+
alias = ImportAlias(
|
29
|
+
name=Name(name), asname=AsName(Name(asname)) if asname else None
|
30
|
+
)
|
31
|
+
return ImportFrom(module=split_dotted_str(module), names=[alias])
|
32
|
+
|
33
|
+
|
34
|
+
def generate_f_string(var: str, suffix: str, /) -> FormattedString:
|
35
|
+
"""Generate an f-string."""
|
36
|
+
return FormattedString([
|
37
|
+
FormattedStringExpression(expression=Name(var)),
|
38
|
+
FormattedStringText(suffix),
|
39
|
+
])
|
40
|
+
|
41
|
+
|
42
|
+
def generate_import(module: str, /, *, asname: str | None = None) -> Import:
|
43
|
+
"""Generate an `Import` object."""
|
44
|
+
alias = ImportAlias(
|
45
|
+
name=split_dotted_str(module), asname=AsName(Name(asname)) if asname else None
|
46
|
+
)
|
47
|
+
return Import(names=[alias])
|
48
|
+
|
49
|
+
|
50
|
+
##
|
51
|
+
|
52
|
+
|
53
|
+
@dataclass(kw_only=True, slots=True)
|
54
|
+
class _ParseImportOutput:
|
55
|
+
module: str
|
56
|
+
name: str | None = None
|
57
|
+
|
58
|
+
|
59
|
+
def parse_import(import_: Import | ImportFrom, /) -> Sequence[_ParseImportOutput]:
|
60
|
+
"""Parse an import."""
|
61
|
+
match import_:
|
62
|
+
case Import():
|
63
|
+
return [_parse_import_one(n) for n in import_.names]
|
64
|
+
case ImportFrom():
|
65
|
+
if (attr_or_name := import_.module) is None:
|
66
|
+
raise _ParseImportEmptyModuleError(import_=import_)
|
67
|
+
module = join_dotted_str(attr_or_name)
|
68
|
+
match import_.names:
|
69
|
+
case Sequence() as names:
|
70
|
+
return [_parse_import_from_one(module, n) for n in names]
|
71
|
+
case ImportStar():
|
72
|
+
return [_ParseImportOutput(module=module, name="*")]
|
73
|
+
case _ as never:
|
74
|
+
assert_never(never)
|
75
|
+
case _ as never:
|
76
|
+
assert_never(never)
|
77
|
+
|
78
|
+
|
79
|
+
def _parse_import_one(alias: ImportAlias, /) -> _ParseImportOutput:
|
80
|
+
return _ParseImportOutput(module=join_dotted_str(alias.name))
|
81
|
+
|
82
|
+
|
83
|
+
def _parse_import_from_one(module: str, alias: ImportAlias, /) -> _ParseImportOutput:
|
84
|
+
match alias.name:
|
85
|
+
case Name(name):
|
86
|
+
return _ParseImportOutput(module=module, name=name)
|
87
|
+
case Attribute() as attr:
|
88
|
+
raise _ParseImportAliasError(module=module, attr=attr)
|
89
|
+
case _ as never:
|
90
|
+
assert_never(never)
|
91
|
+
|
92
|
+
|
93
|
+
@dataclass(kw_only=True, slots=True)
|
94
|
+
class ParseImportError(Exception): ...
|
95
|
+
|
96
|
+
|
97
|
+
@dataclass(kw_only=True, slots=True)
|
98
|
+
class _ParseImportEmptyModuleError(ParseImportError):
|
99
|
+
import_: ImportFrom
|
100
|
+
|
101
|
+
@override
|
102
|
+
def __str__(self) -> str:
|
103
|
+
return f"Module must not be None; got {self.import_}"
|
104
|
+
|
105
|
+
|
106
|
+
@dataclass(kw_only=True, slots=True)
|
107
|
+
class _ParseImportAliasError(ParseImportError):
|
108
|
+
module: str
|
109
|
+
attr: Attribute
|
110
|
+
|
111
|
+
@override
|
112
|
+
def __str__(self) -> str:
|
113
|
+
attr = self.attr
|
114
|
+
return f"Invalid alias name; got module {self.module!r} and attribute '{attr.value.value}.{attr.attr.value}'"
|
115
|
+
|
116
|
+
|
117
|
+
##
|
118
|
+
|
119
|
+
|
120
|
+
def split_dotted_str(dotted: str, /) -> Name | Attribute:
|
121
|
+
"""Split a dotted string into a name/attribute."""
|
122
|
+
parts = dotted.split(".")
|
123
|
+
node = Name(parts[0])
|
124
|
+
for part in parts[1:]:
|
125
|
+
node = Attribute(value=node, attr=Name(part))
|
126
|
+
return node
|
127
|
+
|
128
|
+
|
129
|
+
def join_dotted_str(name_or_attr: Name | Attribute, /) -> str:
|
130
|
+
"""Join a dotted from from a name/attribute."""
|
131
|
+
parts: Sequence[str] = []
|
132
|
+
curr: BaseExpression | Name | Attribute = name_or_attr
|
133
|
+
while True:
|
134
|
+
match curr:
|
135
|
+
case Name(value=value):
|
136
|
+
parts.append(value)
|
137
|
+
break
|
138
|
+
case Attribute(value=value, attr=Name(value=attr_value)):
|
139
|
+
parts.append(attr_value)
|
140
|
+
curr = value
|
141
|
+
case BaseExpression() as expr:
|
142
|
+
raise JoinDottedStrError(name_or_attr=name_or_attr, expr=expr)
|
143
|
+
case _ as never:
|
144
|
+
assert_never(never)
|
145
|
+
return ".".join(reversed(parts))
|
146
|
+
|
147
|
+
|
148
|
+
@dataclass(kw_only=True, slots=True)
|
149
|
+
class JoinDottedStrError(Exception):
|
150
|
+
name_or_attr: Name | Attribute
|
151
|
+
expr: BaseExpression
|
152
|
+
|
153
|
+
@override
|
154
|
+
def __str__(self) -> str:
|
155
|
+
return f"Only names & attributes allowed; got {self.expr}"
|
156
|
+
|
157
|
+
|
158
|
+
##
|
159
|
+
|
160
|
+
|
161
|
+
def render_module(source: str | Module, /) -> str:
|
162
|
+
"""Render a module."""
|
163
|
+
match source: # skipif-ci
|
164
|
+
case str() as text:
|
165
|
+
return check_output(["ruff", "format", "-"], input=text, text=True)
|
166
|
+
case Module() as module:
|
167
|
+
return render_module(module.code)
|
168
|
+
case _ as never:
|
169
|
+
assert_never(never)
|
170
|
+
|
171
|
+
|
172
|
+
##
|
173
|
+
|
174
|
+
|
175
|
+
__all__ = [
|
176
|
+
"ParseImportError",
|
177
|
+
"generate_f_string",
|
178
|
+
"generate_from_import",
|
179
|
+
"generate_import",
|
180
|
+
"join_dotted_str",
|
181
|
+
"parse_import",
|
182
|
+
"render_module",
|
183
|
+
"split_dotted_str",
|
184
|
+
]
|
File without changes
|
File without changes
|