x-formula-utils 0.1.1__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.
- x_formula_utils/__init__.py +23 -0
- x_formula_utils/cli.py +30 -0
- x_formula_utils/resolver.py +316 -0
- x_formula_utils-0.1.1.dist-info/METADATA +63 -0
- x_formula_utils-0.1.1.dist-info/RECORD +8 -0
- x_formula_utils-0.1.1.dist-info/WHEEL +5 -0
- x_formula_utils-0.1.1.dist-info/entry_points.txt +2 -0
- x_formula_utils-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Public API for :mod:`x_formula_utils`.
|
|
2
|
+
|
|
3
|
+
The molecular-formula optimization logic was provided by zyh. x only
|
|
4
|
+
packages and exposes that logic as an installable Python distribution.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .resolver import (
|
|
8
|
+
FormulaMetadata,
|
|
9
|
+
formula_to_html,
|
|
10
|
+
parse_formula,
|
|
11
|
+
resolve_formula_metadata,
|
|
12
|
+
resolve_hill_formula_metadata,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"FormulaMetadata",
|
|
17
|
+
"formula_to_html",
|
|
18
|
+
"parse_formula",
|
|
19
|
+
"resolve_formula_metadata",
|
|
20
|
+
"resolve_hill_formula_metadata",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
__version__ = "0.1.0"
|
x_formula_utils/cli.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Command-line entry point for x-formula-utils."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
from typing import Sequence
|
|
8
|
+
|
|
9
|
+
from .resolver import resolve_hill_formula_metadata
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main(argv: Sequence[str] | None = None) -> None:
|
|
13
|
+
"""Print formula metadata as UTF-8 JSON."""
|
|
14
|
+
|
|
15
|
+
parser = argparse.ArgumentParser(
|
|
16
|
+
description="Resolve a Hill formula into search and display metadata."
|
|
17
|
+
)
|
|
18
|
+
parser.add_argument("hill_formula", help="For example: CuSO4 or H10CuO9S")
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
"--no-overrides",
|
|
21
|
+
action="store_true",
|
|
22
|
+
help="Disable built-in display override and special-carbon rules.",
|
|
23
|
+
)
|
|
24
|
+
parser.add_argument("--pretty", action="store_true", help="Indent JSON output.")
|
|
25
|
+
args = parser.parse_args(argv)
|
|
26
|
+
result = resolve_hill_formula_metadata(
|
|
27
|
+
args.hill_formula,
|
|
28
|
+
use_overrides=not args.no_overrides,
|
|
29
|
+
)
|
|
30
|
+
print(json.dumps(result, ensure_ascii=False, indent=2 if args.pretty else None))
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"""Hill-formula parsing and display normalization.
|
|
2
|
+
|
|
3
|
+
Original molecular-formula optimization logic: zyh.
|
|
4
|
+
Packaging, public API, and distribution integration: x.
|
|
5
|
+
|
|
6
|
+
``formula_key`` is deliberately different from Hill ordering: it sorts element
|
|
7
|
+
symbols alphabetically, includes every atom count (including ``1``), and is
|
|
8
|
+
therefore suitable for equality checks, deduplication, and exact search.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from collections import Counter
|
|
15
|
+
from typing import Mapping, TypedDict
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FormulaMetadata(TypedDict):
|
|
19
|
+
"""Stable metadata returned by :func:`resolve_hill_formula_metadata`."""
|
|
20
|
+
|
|
21
|
+
formula_key: str
|
|
22
|
+
formula_key_dict: dict[str, int]
|
|
23
|
+
human_formula: str
|
|
24
|
+
display_formula: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
_TOKEN_RE = re.compile(r"([A-Z][a-z]?|\d+|\(|\)|\[|\])")
|
|
28
|
+
_DOT_RE = re.compile(r"[.·]")
|
|
29
|
+
_SUBSCRIPT_TRANSLATION = str.maketrans("₀₁₂₃₄₅₆₇₈₉", "0123456789")
|
|
30
|
+
|
|
31
|
+
# Display-oriented rules from the original formula optimization code. They are
|
|
32
|
+
# intentionally presentation rules, not identity rules; formula_key remains the
|
|
33
|
+
# canonical identifier for retrieval.
|
|
34
|
+
_GROUP_REWRITES: tuple[tuple[str, str], ...] = (
|
|
35
|
+
("H4N", "NH4"),
|
|
36
|
+
("H2O4P", "H2PO4"),
|
|
37
|
+
("HO4P", "HPO4"),
|
|
38
|
+
("O4S", "SO4"),
|
|
39
|
+
("O3S", "SO3"),
|
|
40
|
+
("O3N", "NO3"),
|
|
41
|
+
("O2N", "NO2"),
|
|
42
|
+
("O4Cl", "ClO4"),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# This compact, embedded table keeps common human-facing inorganic notation
|
|
46
|
+
# deterministic while avoiding a runtime data-file dependency. Applications may
|
|
47
|
+
# extend or replace it through the ``display_overrides`` argument.
|
|
48
|
+
_DEFAULT_DISPLAY_OVERRIDES: Mapping[str, str] = {
|
|
49
|
+
"Al:1|H:3|O:3": "Al(OH)3",
|
|
50
|
+
"Ca:1|H:2|O:2": "Ca(OH)2",
|
|
51
|
+
"Ca:3|O:8|P:2": "Ca3(PO4)2",
|
|
52
|
+
"Cu:1|O:4|S:1": "CuSO4",
|
|
53
|
+
"Fe:2|O:12|S:3": "Fe2(SO4)3",
|
|
54
|
+
"H:4|N:2|O:3": "NH4NO3",
|
|
55
|
+
"H:8|N:2|O:4|S:1": "(NH4)2SO4",
|
|
56
|
+
"Cr:2|K:2|O:7": "K2Cr2O7",
|
|
57
|
+
"H:2|Mg:1|O:2": "Mg(OH)2",
|
|
58
|
+
"Na:2|O:4|S:1": "Na2SO4",
|
|
59
|
+
"Na:2|O:3|S:2": "Na2S2O3",
|
|
60
|
+
"Na:3|O:4|P:1": "Na3PO4",
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Carbon salt handling retained from the original algorithm. These are the
|
|
64
|
+
# cases where alphabetical element ordering is especially poor for display.
|
|
65
|
+
_SPECIAL_CARBON_ANIONS: Mapping[str, str] = {
|
|
66
|
+
"C:2|O:4": "C2O4",
|
|
67
|
+
"C:2|H:1|O:4": "HC2O4",
|
|
68
|
+
"C:6|H:5|O:7": "C6H5O7",
|
|
69
|
+
"C:6|H:6|O:7": "HC6H5O7",
|
|
70
|
+
"C:6|H:7|O:7": "H2C6H5O7",
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def parse_formula(formula: str) -> dict[str, int]:
|
|
75
|
+
"""Parse a formula into atom counts.
|
|
76
|
+
|
|
77
|
+
Parentheses, square brackets, hydrate separators (``.``/``·``), leading
|
|
78
|
+
hydrate multipliers, whitespace, and Unicode numeric subscripts are
|
|
79
|
+
supported. Charges and isotope labels are intentionally outside the
|
|
80
|
+
supported Hill-formula input format and raise :class:`ValueError`.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
if not isinstance(formula, str):
|
|
84
|
+
raise TypeError("formula must be a string")
|
|
85
|
+
|
|
86
|
+
normalized = formula.translate(_SUBSCRIPT_TRANSLATION).replace(" ", "")
|
|
87
|
+
if not normalized:
|
|
88
|
+
raise ValueError("formula is empty")
|
|
89
|
+
|
|
90
|
+
result: Counter[str] = Counter()
|
|
91
|
+
for part in _DOT_RE.split(normalized):
|
|
92
|
+
if not part:
|
|
93
|
+
raise ValueError(f"empty hydrate fragment in formula: {formula!r}")
|
|
94
|
+
multiplier, fragment = _split_leading_multiplier(part)
|
|
95
|
+
if not fragment:
|
|
96
|
+
raise ValueError(f"missing formula after multiplier in: {formula!r}")
|
|
97
|
+
for element, count in _parse_fragment(fragment).items():
|
|
98
|
+
result[element] += count * multiplier
|
|
99
|
+
|
|
100
|
+
return {element: int(result[element]) for element in sorted(result)}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _split_leading_multiplier(part: str) -> tuple[int, str]:
|
|
104
|
+
match = re.match(r"^(\d+)(.*)$", part)
|
|
105
|
+
if match is None:
|
|
106
|
+
return 1, part
|
|
107
|
+
multiplier = int(match.group(1))
|
|
108
|
+
if multiplier <= 0:
|
|
109
|
+
raise ValueError("formula multipliers must be positive")
|
|
110
|
+
return multiplier, match.group(2)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _parse_fragment(fragment: str) -> Counter[str]:
|
|
114
|
+
tokens = _TOKEN_RE.findall(fragment)
|
|
115
|
+
if "".join(tokens) != fragment:
|
|
116
|
+
raise ValueError(f"unsupported token sequence in formula fragment: {fragment!r}")
|
|
117
|
+
|
|
118
|
+
opening = {"(": ")", "[": "]"}
|
|
119
|
+
stack: list[tuple[str | None, Counter[str]]] = [(None, Counter())]
|
|
120
|
+
index = 0
|
|
121
|
+
while index < len(tokens):
|
|
122
|
+
token = tokens[index]
|
|
123
|
+
if token in opening:
|
|
124
|
+
stack.append((opening[token], Counter()))
|
|
125
|
+
index += 1
|
|
126
|
+
continue
|
|
127
|
+
if token in {")", "]"}:
|
|
128
|
+
if len(stack) == 1 or stack[-1][0] != token:
|
|
129
|
+
raise ValueError(f"unmatched {token!r} in formula fragment: {fragment!r}")
|
|
130
|
+
_, group = stack.pop()
|
|
131
|
+
index += 1
|
|
132
|
+
multiplier = 1
|
|
133
|
+
if index < len(tokens) and tokens[index].isdigit():
|
|
134
|
+
multiplier = _positive_count(tokens[index])
|
|
135
|
+
index += 1
|
|
136
|
+
for element, count in group.items():
|
|
137
|
+
stack[-1][1][element] += count * multiplier
|
|
138
|
+
continue
|
|
139
|
+
if token.isdigit():
|
|
140
|
+
raise ValueError(f"unexpected standalone multiplier in: {fragment!r}")
|
|
141
|
+
|
|
142
|
+
count = 1
|
|
143
|
+
index += 1
|
|
144
|
+
if index < len(tokens) and tokens[index].isdigit():
|
|
145
|
+
count = _positive_count(tokens[index])
|
|
146
|
+
index += 1
|
|
147
|
+
stack[-1][1][token] += count
|
|
148
|
+
|
|
149
|
+
if len(stack) != 1:
|
|
150
|
+
raise ValueError(f"unmatched opening bracket in formula fragment: {fragment!r}")
|
|
151
|
+
return stack[0][1]
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _positive_count(value: str) -> int:
|
|
155
|
+
count = int(value)
|
|
156
|
+
if count <= 0:
|
|
157
|
+
raise ValueError("element counts must be positive")
|
|
158
|
+
return count
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _counts_to_formula_key(counts: Mapping[str, int]) -> str:
|
|
162
|
+
return "|".join(f"{element}:{int(counts[element])}" for element in sorted(counts))
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _counts_to_formula(counts: Mapping[str, int]) -> str:
|
|
166
|
+
pieces: list[str] = []
|
|
167
|
+
for element in sorted(counts):
|
|
168
|
+
count = int(counts[element])
|
|
169
|
+
if count:
|
|
170
|
+
pieces.append(element)
|
|
171
|
+
if count != 1:
|
|
172
|
+
pieces.append(str(count))
|
|
173
|
+
return "".join(pieces)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _recover_special_carbon_salt_formula(counts: Mapping[str, int]) -> str | None:
|
|
177
|
+
if "C" not in counts or "O" not in counts:
|
|
178
|
+
return None
|
|
179
|
+
anion_counts = {key: value for key, value in counts.items() if key in {"C", "H", "O"}}
|
|
180
|
+
anion = _SPECIAL_CARBON_ANIONS.get(_counts_to_formula_key(anion_counts))
|
|
181
|
+
if anion is None:
|
|
182
|
+
return None
|
|
183
|
+
cations = {key: value for key, value in counts.items() if key not in {"C", "H", "O"}}
|
|
184
|
+
return _counts_to_formula(cations) + anion if cations else None
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _should_recover_human_formula(counts: Mapping[str, int]) -> bool:
|
|
188
|
+
"""Follow the original guard that avoids reformating ordinary organics."""
|
|
189
|
+
|
|
190
|
+
if "C" not in counts:
|
|
191
|
+
return True
|
|
192
|
+
if _recover_special_carbon_salt_formula(counts) is not None:
|
|
193
|
+
return True
|
|
194
|
+
return _counts_to_formula_key(counts) in {"C:1|Si:1", "C:2|Ca:1"}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _recover_with_pymatgen(formula: str) -> str:
|
|
198
|
+
"""Return pymatgen's reduced, human-readable formula.
|
|
199
|
+
|
|
200
|
+
Importing lazily keeps basic parsing utilities usable in minimal build
|
|
201
|
+
environments. A normal ``pip install x-formula-utils`` installs pymatgen
|
|
202
|
+
automatically through the package metadata.
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
from pymatgen.core import Composition
|
|
207
|
+
except ImportError as exc: # pragma: no cover - exercised by installation
|
|
208
|
+
raise RuntimeError(
|
|
209
|
+
"pymatgen is required to resolve display metadata. "
|
|
210
|
+
"Install with: pip install x-formula-utils"
|
|
211
|
+
) from exc
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
human_formula, _factor = Composition(formula).get_reduced_formula_and_factor()
|
|
215
|
+
except Exception as exc:
|
|
216
|
+
raise ValueError(f"pymatgen could not recover formula {formula!r}") from exc
|
|
217
|
+
return str(human_formula)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _apply_group_rewrites(formula: str) -> str:
|
|
221
|
+
rewritten = formula
|
|
222
|
+
for source, target in _GROUP_REWRITES:
|
|
223
|
+
rewritten = rewritten.replace(source, target)
|
|
224
|
+
return rewritten
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def formula_to_html(formula: str) -> str:
|
|
228
|
+
"""Convert formula counts to HTML subscripts.
|
|
229
|
+
|
|
230
|
+
A leading count after a hydrate separator remains a coefficient, e.g.
|
|
231
|
+
``CuSO4·5H2O`` becomes ``CuSO<sub>4</sub>·5H<sub>2</sub>O``.
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
if "<sub>" in formula:
|
|
235
|
+
return formula
|
|
236
|
+
|
|
237
|
+
def subscript_digits(text: str) -> str:
|
|
238
|
+
return re.sub(r"\d+", lambda match: f"<sub>{match.group()}</sub>", text)
|
|
239
|
+
|
|
240
|
+
parts = re.split(r"([.·])", formula)
|
|
241
|
+
output: list[str] = []
|
|
242
|
+
for index, part in enumerate(parts):
|
|
243
|
+
if part in {".", "·"}:
|
|
244
|
+
output.append(part)
|
|
245
|
+
elif index > 0 and parts[index - 1] in {".", "·"}:
|
|
246
|
+
match = re.match(r"^(\d+)(.*)$", part)
|
|
247
|
+
output.append(match.group(1) + subscript_digits(match.group(2)) if match else subscript_digits(part))
|
|
248
|
+
else:
|
|
249
|
+
output.append(subscript_digits(part))
|
|
250
|
+
return "".join(output)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def resolve_hill_formula_metadata(
|
|
254
|
+
hill_formula: str,
|
|
255
|
+
*,
|
|
256
|
+
use_overrides: bool = True,
|
|
257
|
+
display_overrides: Mapping[str, str] | None = None,
|
|
258
|
+
) -> FormulaMetadata:
|
|
259
|
+
"""Resolve a Hill formula into canonical search and display metadata.
|
|
260
|
+
|
|
261
|
+
Parameters
|
|
262
|
+
----------
|
|
263
|
+
hill_formula:
|
|
264
|
+
Formula to parse, such as ``"CuSO4"`` or ``"H10CuO9S"``.
|
|
265
|
+
use_overrides:
|
|
266
|
+
Apply the built-in common-formula display table and carbonate rules.
|
|
267
|
+
display_overrides:
|
|
268
|
+
Optional formula-key-to-display-formula mapping. These entries take
|
|
269
|
+
precedence over the built-in table and allow applications to add rules
|
|
270
|
+
without changing package files.
|
|
271
|
+
|
|
272
|
+
Raises
|
|
273
|
+
------
|
|
274
|
+
ValueError
|
|
275
|
+
If the formula is syntactically invalid or pymatgen cannot recover it.
|
|
276
|
+
RuntimeError
|
|
277
|
+
If the package dependency pymatgen is unavailable.
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
counts = parse_formula(hill_formula)
|
|
281
|
+
formula_key = _counts_to_formula_key(counts)
|
|
282
|
+
|
|
283
|
+
special_formula = _recover_special_carbon_salt_formula(counts) if use_overrides else None
|
|
284
|
+
if special_formula is not None:
|
|
285
|
+
human_formula = special_formula
|
|
286
|
+
elif _should_recover_human_formula(counts):
|
|
287
|
+
human_formula = _recover_with_pymatgen(hill_formula)
|
|
288
|
+
else:
|
|
289
|
+
human_formula = hill_formula
|
|
290
|
+
|
|
291
|
+
overrides: dict[str, str] = dict(_DEFAULT_DISPLAY_OVERRIDES) if use_overrides else {}
|
|
292
|
+
if display_overrides:
|
|
293
|
+
overrides.update(display_overrides)
|
|
294
|
+
|
|
295
|
+
display_plain = overrides.get(formula_key, special_formula or _apply_group_rewrites(human_formula))
|
|
296
|
+
return {
|
|
297
|
+
"formula_key": formula_key,
|
|
298
|
+
"formula_key_dict": dict(counts),
|
|
299
|
+
"human_formula": human_formula,
|
|
300
|
+
"display_formula": formula_to_html(display_plain),
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def resolve_formula_metadata(
|
|
305
|
+
formula: str,
|
|
306
|
+
*,
|
|
307
|
+
use_overrides: bool = True,
|
|
308
|
+
display_overrides: Mapping[str, str] | None = None,
|
|
309
|
+
) -> FormulaMetadata:
|
|
310
|
+
"""Alias of :func:`resolve_hill_formula_metadata` for concise use."""
|
|
311
|
+
|
|
312
|
+
return resolve_hill_formula_metadata(
|
|
313
|
+
formula,
|
|
314
|
+
use_overrides=use_overrides,
|
|
315
|
+
display_overrides=display_overrides,
|
|
316
|
+
)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: x-formula-utils
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Resolve Hill formulas into canonical search keys and display-ready metadata.
|
|
5
|
+
Author: zyh, x
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Project-URL: Homepage, https://pypi.org/project/x-formula-utils/
|
|
8
|
+
Keywords: chemistry,formula,Hill formula,pymatgen
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: License :: Other/Proprietary License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Requires-Dist: pymatgen==2025.10.7
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
23
|
+
|
|
24
|
+
# x-formula-utils
|
|
25
|
+
|
|
26
|
+
Resolve Hill formulas into canonical search keys and display-ready metadata.
|
|
27
|
+
|
|
28
|
+
## Environment setup
|
|
29
|
+
|
|
30
|
+
Python 3.10 or newer is required. Create and activate a virtual environment before installing the package:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
python -m venv .venv
|
|
34
|
+
|
|
35
|
+
# Windows PowerShell
|
|
36
|
+
.venv\Scripts\Activate.ps1
|
|
37
|
+
|
|
38
|
+
# macOS/Linux
|
|
39
|
+
source .venv/bin/activate
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Conda
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
conda create -n x-formula-utils python=3.12
|
|
46
|
+
conda activate x-formula-utils
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
python -m pip install --upgrade pip
|
|
53
|
+
pip install x-formula-utils
|
|
54
|
+
|
|
55
|
+
# Upgrade to the latest release
|
|
56
|
+
pip install --upgrade x-formula-utils
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`x-formula-utils` runtime dependencies: `pymatgen`.
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
Refer to the package API documentation for available imports and examples.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
x_formula_utils/__init__.py,sha256=_iIq6w5kuHyggIuMfru2hOVNROQ1yN6EJEthn-wDUeo,515
|
|
2
|
+
x_formula_utils/cli.py,sha256=2mIGaNUwc3A3o1GmvSUc1LR7ctt6XFnlUlljt1mUp0k,993
|
|
3
|
+
x_formula_utils/resolver.py,sha256=A3OHRXuygfQRM6kheMBJ0czNeLd4XVt_zlBl3ZESKXw,10950
|
|
4
|
+
x_formula_utils-0.1.1.dist-info/METADATA,sha256=JnXV9oj0ayPabIa6cF6hXAE2TVdylJN8jQRYzZj1JZo,1725
|
|
5
|
+
x_formula_utils-0.1.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
6
|
+
x_formula_utils-0.1.1.dist-info/entry_points.txt,sha256=Y3qVSIrPx5l_q9AnULymoxWBbGRjSg8C8OOykIa4cDU,55
|
|
7
|
+
x_formula_utils-0.1.1.dist-info/top_level.txt,sha256=mglDXs77kLRxGEkpWvEqCorqtYjNmJWNlU8xac1RN28,16
|
|
8
|
+
x_formula_utils-0.1.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
x_formula_utils
|