fmtr.tools 1.1.16__tar.gz → 1.1.18__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.

Potentially problematic release.


This version of fmtr.tools might be problematic. Click here for more details.

Files changed (78) hide show
  1. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/PKG-INFO +56 -38
  2. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/__init__.py +16 -1
  3. fmtr_tools-1.1.18/fmtr/tools/dns_tools.py +57 -0
  4. fmtr_tools-1.1.18/fmtr/tools/pattern_tools.py +237 -0
  5. fmtr_tools-1.1.18/fmtr/tools/setup_tools.py +273 -0
  6. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/string_tools.py +0 -6
  7. fmtr_tools-1.1.18/fmtr/tools/version +1 -0
  8. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr.tools.egg-info/PKG-INFO +56 -38
  9. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr.tools.egg-info/SOURCES.txt +3 -0
  10. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr.tools.egg-info/requires.txt +59 -37
  11. fmtr_tools-1.1.16/fmtr/tools/version +0 -1
  12. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/LICENSE +0 -0
  13. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/README.md +0 -0
  14. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/ai_tools/__init__.py +0 -0
  15. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/ai_tools/agentic_tools.py +0 -0
  16. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/ai_tools/inference_tools.py +0 -0
  17. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/api_tools.py +0 -0
  18. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/async_tools.py +0 -0
  19. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/augmentation_tools.py +0 -0
  20. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/caching_tools.py +0 -0
  21. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/console_script_tools.py +0 -0
  22. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/constants.py +0 -0
  23. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/data_modelling_tools.py +0 -0
  24. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/dataclass_tools.py +0 -0
  25. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/datatype_tools.py +0 -0
  26. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/debugging_tools.py +0 -0
  27. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/docker_tools.py +0 -0
  28. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/environment_tools.py +0 -0
  29. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/function_tools.py +0 -0
  30. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/google_api_tools.py +0 -0
  31. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/hash_tools.py +0 -0
  32. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/hfh_tools.py +0 -0
  33. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/html_tools.py +0 -0
  34. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/import_tools.py +0 -0
  35. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/inspection_tools.py +0 -0
  36. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/interface_tools.py +0 -0
  37. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/iterator_tools.py +0 -0
  38. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/json_fix_tools.py +0 -0
  39. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/json_tools.py +0 -0
  40. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/logging_tools.py +0 -0
  41. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/merging_tools.py +0 -0
  42. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/metric_tools.py +0 -0
  43. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/name_tools.py +0 -0
  44. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/netrc_tools.py +0 -0
  45. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/openai_tools.py +0 -0
  46. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/packaging_tools.py +0 -0
  47. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/parallel_tools.py +0 -0
  48. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/path_tools/__init__.py +0 -0
  49. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/path_tools/app_path_tools.py +0 -0
  50. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/path_tools/path_tools.py +0 -0
  51. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/path_tools/type_path_tools.py +0 -0
  52. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/pdf_tools.py +0 -0
  53. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/platform_tools.py +0 -0
  54. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/process_tools.py +0 -0
  55. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/profiling_tools.py +0 -0
  56. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/random_tools.py +0 -0
  57. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/semantic_tools.py +0 -0
  58. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/settings_tools.py +0 -0
  59. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/spaces_tools.py +0 -0
  60. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/tabular_tools.py +0 -0
  61. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/tests/__init__.py +0 -0
  62. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/tests/conftest.py +0 -0
  63. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/tests/helpers.py +0 -0
  64. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/tests/test_datatype.py +0 -0
  65. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/tests/test_environment.py +0 -0
  66. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/tests/test_json.py +0 -0
  67. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/tests/test_path.py +0 -0
  68. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/tests/test_yaml.py +0 -0
  69. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/tokenization_tools.py +0 -0
  70. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/tools.py +0 -0
  71. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/unicode_tools.py +0 -0
  72. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/version_tools.py +0 -0
  73. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr/tools/yaml_tools.py +0 -0
  74. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr.tools.egg-info/dependency_links.txt +0 -0
  75. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr.tools.egg-info/entry_points.txt +0 -0
  76. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/fmtr.tools.egg-info/top_level.txt +0 -0
  77. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/setup.cfg +0 -0
  78. {fmtr_tools-1.1.16 → fmtr_tools-1.1.18}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmtr.tools
3
- Version: 1.1.16
3
+ Version: 1.1.18
4
4
  Summary: Collection of high-level tools to simplify everyday development tasks, with a focus on AI/ML
5
5
  Home-page: https://github.com/fmtr/fmtr.tools
6
6
  Author: Frontmatter
@@ -9,54 +9,60 @@ License: Copyright © 2025 Frontmatter. All rights reserved.
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
11
  Provides-Extra: test
12
- Requires-Dist: distributed; extra == "test"
13
- Requires-Dist: uvicorn[standard]; extra == "test"
14
- Requires-Dist: google-auth; extra == "test"
15
- Requires-Dist: tokenizers; extra == "test"
16
- Requires-Dist: torchaudio; extra == "test"
17
- Requires-Dist: pymupdf; extra == "test"
18
- Requires-Dist: appdirs; extra == "test"
19
- Requires-Dist: huggingface_hub; extra == "test"
12
+ Requires-Dist: dask[bag]; extra == "test"
13
+ Requires-Dist: deepmerge; extra == "test"
14
+ Requires-Dist: contexttimer; extra == "test"
15
+ Requires-Dist: flet-webview; extra == "test"
16
+ Requires-Dist: regex; extra == "test"
20
17
  Requires-Dist: tabulate; extra == "test"
18
+ Requires-Dist: bokeh; extra == "test"
19
+ Requires-Dist: google-auth-oauthlib; extra == "test"
20
+ Requires-Dist: faker; extra == "test"
21
+ Requires-Dist: google-api-python-client; extra == "test"
22
+ Requires-Dist: httpx; extra == "test"
23
+ Requires-Dist: transformers[sentencepiece]; extra == "test"
24
+ Requires-Dist: ollama; extra == "test"
25
+ Requires-Dist: tokenizers; extra == "test"
26
+ Requires-Dist: httpx_retries; extra == "test"
27
+ Requires-Dist: openpyxl; extra == "test"
28
+ Requires-Dist: sentence_transformers; extra == "test"
21
29
  Requires-Dist: pydantic; extra == "test"
22
- Requires-Dist: html2text; extra == "test"
30
+ Requires-Dist: google-auth; extra == "test"
31
+ Requires-Dist: semver; extra == "test"
32
+ Requires-Dist: pydevd-pycharm; extra == "test"
33
+ Requires-Dist: logfire; extra == "test"
34
+ Requires-Dist: pydantic-ai[logfire,openai]; extra == "test"
35
+ Requires-Dist: yamlscript; extra == "test"
36
+ Requires-Dist: pymupdf; extra == "test"
23
37
  Requires-Dist: docker; extra == "test"
24
- Requires-Dist: logfire[fastapi]; extra == "test"
25
- Requires-Dist: filetype; extra == "test"
26
38
  Requires-Dist: Unidecode; extra == "test"
27
- Requires-Dist: sentence_transformers; extra == "test"
39
+ Requires-Dist: torchaudio; extra == "test"
40
+ Requires-Dist: pymupdf4llm; extra == "test"
28
41
  Requires-Dist: fastapi; extra == "test"
29
- Requires-Dist: flet-webview; extra == "test"
30
- Requires-Dist: pydantic-settings; extra == "test"
31
- Requires-Dist: contexttimer; extra == "test"
32
- Requires-Dist: diskcache; extra == "test"
33
- Requires-Dist: ollama; extra == "test"
34
- Requires-Dist: logfire; extra == "test"
35
- Requires-Dist: openai; extra == "test"
36
- Requires-Dist: pydevd-pycharm; extra == "test"
42
+ Requires-Dist: distributed; extra == "test"
43
+ Requires-Dist: logfire[httpx]; extra == "test"
44
+ Requires-Dist: dnspython[doh]; extra == "test"
45
+ Requires-Dist: torchvision; extra == "test"
37
46
  Requires-Dist: pandas; extra == "test"
47
+ Requires-Dist: filetype; extra == "test"
38
48
  Requires-Dist: flet-video; extra == "test"
39
- Requires-Dist: yamlscript; extra == "test"
49
+ Requires-Dist: appdirs; extra == "test"
50
+ Requires-Dist: pydantic-settings; extra == "test"
51
+ Requires-Dist: pytest-cov; extra == "test"
52
+ Requires-Dist: logfire[fastapi]; extra == "test"
40
53
  Requires-Dist: sre_yield; extra == "test"
41
- Requires-Dist: google-auth-httplib2; extra == "test"
42
- Requires-Dist: google-api-python-client; extra == "test"
43
- Requires-Dist: dask[bag]; extra == "test"
44
- Requires-Dist: pydantic-ai[logfire,openai]; extra == "test"
45
- Requires-Dist: torchvision; extra == "test"
46
- Requires-Dist: openpyxl; extra == "test"
47
- Requires-Dist: flet[all]; extra == "test"
48
- Requires-Dist: bokeh; extra == "test"
49
- Requires-Dist: pyyaml; extra == "test"
50
54
  Requires-Dist: json_repair; extra == "test"
51
- Requires-Dist: pymupdf4llm; extra == "test"
52
55
  Requires-Dist: peft; extra == "test"
53
- Requires-Dist: deepmerge; extra == "test"
54
- Requires-Dist: pytest-cov; extra == "test"
55
- Requires-Dist: faker; extra == "test"
56
+ Requires-Dist: html2text; extra == "test"
57
+ Requires-Dist: openai; extra == "test"
58
+ Requires-Dist: google-auth-httplib2; extra == "test"
59
+ Requires-Dist: setuptools; extra == "test"
60
+ Requires-Dist: uvicorn[standard]; extra == "test"
61
+ Requires-Dist: pyyaml; extra == "test"
62
+ Requires-Dist: flet[all]; extra == "test"
63
+ Requires-Dist: huggingface_hub; extra == "test"
64
+ Requires-Dist: diskcache; extra == "test"
56
65
  Requires-Dist: tinynetrc; extra == "test"
57
- Requires-Dist: google-auth-oauthlib; extra == "test"
58
- Requires-Dist: semver; extra == "test"
59
- Requires-Dist: transformers[sentencepiece]; extra == "test"
60
66
  Provides-Extra: yaml
61
67
  Requires-Dist: yamlscript; extra == "yaml"
62
68
  Requires-Dist: pyyaml; extra == "yaml"
@@ -157,6 +163,18 @@ Provides-Extra: path-app
157
163
  Requires-Dist: appdirs; extra == "path-app"
158
164
  Provides-Extra: path-type
159
165
  Requires-Dist: filetype; extra == "path-type"
166
+ Provides-Extra: dns
167
+ Requires-Dist: dnspython[doh]; extra == "dns"
168
+ Provides-Extra: patterns
169
+ Requires-Dist: regex; extra == "patterns"
170
+ Provides-Extra: http
171
+ Requires-Dist: httpx; extra == "http"
172
+ Requires-Dist: httpx_retries; extra == "http"
173
+ Requires-Dist: logfire; extra == "http"
174
+ Requires-Dist: semver; extra == "http"
175
+ Requires-Dist: logfire[httpx]; extra == "http"
176
+ Provides-Extra: setup
177
+ Requires-Dist: setuptools; extra == "setup"
160
178
  Dynamic: author
161
179
  Dynamic: author-email
162
180
  Dynamic: description
@@ -163,4 +163,19 @@ except ImportError as exception:
163
163
  try:
164
164
  from fmtr.tools import settings_tools as sets
165
165
  except ImportError as exception:
166
- sets = MissingExtraMockModule('sets', exception)
166
+ sets = MissingExtraMockModule('sets', exception)
167
+
168
+ try:
169
+ from fmtr.tools import pattern_tools as patterns
170
+ except ImportError as exception:
171
+ patterns = MissingExtraMockModule('patterns', exception)
172
+
173
+ try:
174
+ from fmtr.tools import dns_tools as dns
175
+ except ImportError as exception:
176
+ dns = MissingExtraMockModule('dns', exception)
177
+
178
+ try:
179
+ from fmtr.tools import setup_tools as setup
180
+ except ImportError as exception:
181
+ setup = MissingExtraMockModule('setup', exception)
@@ -0,0 +1,57 @@
1
+ import dns
2
+ import httpx
3
+ import socket
4
+ from dns.message import Message, QueryMessage
5
+
6
+
7
+ class Server:
8
+
9
+ def __init__(self, host, port):
10
+ self.host = host
11
+ self.port = port
12
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
13
+
14
+ def resolve(self, message: Message) -> QueryMessage:
15
+ raise NotImplemented
16
+
17
+ def start(self):
18
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
19
+ sock.bind((self.host, self.port))
20
+ print(f"Listening on {self.host}:{self.port}")
21
+ while True:
22
+ data, addr = sock.recvfrom(512)
23
+ question = dns.message.from_wire(data)
24
+ answer = self.resolve(question)
25
+ sock.sendto(answer.to_wire(), addr)
26
+
27
+
28
+ class Proxy(Server):
29
+ url = "https://cloudflare-dns.com/dns-query"
30
+ headers = {"accept": "application/dns-json"}
31
+
32
+ def resolve(self, question: Message) -> Message:
33
+ qname = question.question[0].name.to_text()
34
+ qtype = dns.rdatatype.to_text(question.question[0].rdtype)
35
+ params = {"name": qname, "type": qtype}
36
+
37
+ response = httpx.get(self.url, headers=self.headers, params=params)
38
+ response.raise_for_status()
39
+
40
+ answer = dns.message.make_response(question)
41
+ answer.flags |= dns.flags.RA
42
+
43
+ data = response.json()
44
+ for answer_rr in data.get("Answer", []):
45
+ name = dns.name.from_text(answer_rr["name"])
46
+ rtype = answer_rr["type"]
47
+ ttl = answer_rr["TTL"]
48
+ rdata = dns.rdata.from_text(dns.rdataclass.IN, rtype, answer_rr["data"])
49
+ rrset = dns.rrset.from_rdata(name, ttl, rdata)
50
+ answer.answer.append(rrset)
51
+
52
+ return answer
53
+
54
+
55
+ if __name__ == "__main__":
56
+ proxy = Proxy("127.0.0.1", 5354)
57
+ proxy.start()
@@ -0,0 +1,237 @@
1
+ import regex as re
2
+ from dataclasses import dataclass
3
+ from functools import cached_property
4
+ from typing import List
5
+
6
+ from fmtr.tools.logging_tools import logger
7
+
8
+
9
+ class RewriteCircularLoopError(Exception):
10
+ """
11
+
12
+ Circular loop error
13
+
14
+ """
15
+
16
+
17
+ @dataclass
18
+ class Rule:
19
+ """
20
+ Represents a single rule for pattern matching and target string replacement.
21
+
22
+ This class is used to define a rule with a pattern and a target string.
23
+ The `pattern` is a regular expression used to identify matches in input text.
24
+ The `target` allows rewriting the identified matches with a formatted string.
25
+ It provides properties for generating a unique identifier for use as a regex group name and compiling the provided pattern into a regular expression object.
26
+
27
+ """
28
+ pattern: str
29
+ target: str
30
+
31
+ @cached_property
32
+ def id(self):
33
+ """
34
+
35
+ Regex group name.
36
+
37
+ """
38
+ return f'id{abs(hash(self.pattern))}'
39
+
40
+ @cached_property
41
+ def rx(self):
42
+ """
43
+
44
+ Regex object.
45
+
46
+ """
47
+ return re.compile(self.pattern)
48
+
49
+ def apply(self, match: re.Match):
50
+ """
51
+
52
+ Rewrite using the target string and match groups.
53
+
54
+ """
55
+ target = self.target.format(**match.groupdict())
56
+ return target
57
+
58
+
59
+ @dataclass
60
+ class Rewriter:
61
+ """
62
+
63
+ Represents a Rewriter class that handles pattern matching, rule application, and text rewriting.
64
+ Compiles a single regex pattern from a list of rules, and determines which rule matched.
65
+ It supports initialization from structured rule data, execution of a single rewrite pass, and
66
+ recursive rewriting until a stable state is reached.
67
+
68
+ """
69
+ rules: List[Rule]
70
+
71
+ @cached_property
72
+ def pattern(self):
73
+ """
74
+
75
+ Provides a dynamically generated regex pattern based on the rules provided.
76
+
77
+ """
78
+ patterns = [fr"(?P<{rule.id}>{rule.pattern})" for rule in self.rules]
79
+ sorted(patterns, key=len, reverse=True)
80
+ pattern = '|'.join(patterns)
81
+ return pattern
82
+
83
+ @cached_property
84
+ def rule_lookup(self):
85
+ """
86
+
87
+ Dictionary mapping rule identifiers to their corresponding rules.
88
+ """
89
+
90
+ return {rule.id: rule for rule in self.rules}
91
+
92
+ @cached_property
93
+ def rx(self):
94
+ """
95
+
96
+ Regex object.
97
+
98
+ """
99
+ return re.compile(self.pattern)
100
+
101
+ def rewrite_pass(self, source: str):
102
+ """
103
+
104
+ Single rewrite pass.
105
+ Rewrites the provided source string based on the matching rule.
106
+
107
+ """
108
+
109
+ match = self.rx.match(source)
110
+
111
+ if not match:
112
+ return source
113
+
114
+ match_ids = {k: v for k, v in match.groupdict().items() if v}
115
+ match_id = match_ids & self.rule_lookup.keys()
116
+
117
+ if len(match_id) != 1:
118
+ msg = f'Multiple group matches: {match_id}'
119
+ raise ValueError(msg)
120
+
121
+ match_id = next(iter(match_id))
122
+ rule = self.rule_lookup[match_id]
123
+ target = rule.apply(match)
124
+
125
+ logger.debug(f'Rewrote using {match_id=}: {source=} -> {target=}')
126
+
127
+ return target
128
+
129
+ def rewrite(self, source: str) -> str:
130
+ """
131
+
132
+ Rewrites the provided text by continuously applying rewrite rules until no changes are made
133
+ or a circular loop is detected.
134
+
135
+ """
136
+ history = []
137
+ previous = source
138
+
139
+ def get_history_str():
140
+ return ' -> '.join(history)
141
+
142
+ with logger.span(f'Rewriting "{source}"...'):
143
+ while True:
144
+ if previous in history:
145
+ history.append(previous)
146
+ msg = f'Loop detected on node "{previous}": {get_history_str()}'
147
+ raise RewriteCircularLoopError(msg)
148
+
149
+ history.append(previous)
150
+
151
+ new = previous
152
+
153
+ new = self.rewrite_pass(new)
154
+
155
+ if new == previous:
156
+ break
157
+
158
+ previous = new
159
+
160
+ logger.debug(f'Finished rewriting: {get_history_str()}')
161
+
162
+ return previous
163
+
164
+ @classmethod
165
+ def from_data(cls, data):
166
+ rules = [Rule(*pair) for pair in data.items()]
167
+ self = cls(rules=rules)
168
+ return self
169
+
170
+
171
+ if __name__ == '__main__':
172
+ data = {
173
+
174
+ r'(?P<name>[a-z]+)\.dev\.example\.com': '{name}.test.example.com',
175
+ r'img\d+\.static\.cdn\.example\.com': 'images.cdn.example.com',
176
+ r'service\.(?P<env>dev|staging|prod)\.example\.org': '{env}-service.example.org',
177
+ r'legacy\.(?P<region>[a-z]+)\.oldsite\.com': '{region}.newsitenow.com',
178
+ r'shop\.(?P<country_code>[a-z]{2})\.example\.net': 'store.{country_code}.example.net',
179
+ r'(?P<user>[a-z]+)\.mail\.example\.com': '{user}.email.example.com',
180
+ r'app1\.cluster(?P<num>[0-9]+)\.example\.cloud': 'service{num}.example.cloud',
181
+ r'(?P<project>[a-z]+)\.research\.corp\.com': '{project}.lab.corp.com',
182
+ r'cdn\.(?P<version>v[0-9]+)\.content\.net': 'static.{version}.content.net',
183
+ # Literal rule without named group
184
+ r'corp\.secureaccess\.com': 'access.corp.com',
185
+ # Literal rule without named group
186
+ r'redirect\.oldsite\.org': 'homepage.newsite.org',
187
+ # # Recursive rules
188
+
189
+ r'archive\.(?P<year>\d{4})\.oldsite\.net': 'legacy.{year}.oldsite.net', # Recursive matching
190
+ r'legacy\.(?P<year>\d{4})\.oldsite\.net': 'archive-backup.{year}.net', # Continuation
191
+
192
+ # r'(?P<subd>[a-zA-Z]+)\.vpn': '{subd}.loop.ts.net',
193
+ # r'(?P<subd>[a-zA-Z]+)\.loop\.ts\.net': '{subd}.vpn', # Recursive loop back to .vpn
194
+ }
195
+
196
+ rewriter = Rewriter.from_data(data)
197
+
198
+ tests = [
199
+ "sales.vpn",
200
+ "marketing.dev.example.com",
201
+ "img01.static.cdn.example.com",
202
+ "service.dev.example.org",
203
+ "legacy.eu.oldsite.com",
204
+ "shop.us.example.net",
205
+ "alice.mail.example.com",
206
+ "app1.cluster1.example.cloud",
207
+ "genetics.research.corp.com",
208
+ "cdn.v2.content.net",
209
+ "corp.secureaccess.com",
210
+ "redirect.oldsite.org",
211
+ "support.bbb.ts.net",
212
+ "support.ccc.ts.net",
213
+ "archive.2022.oldsite.net",
214
+ "legacy.2022.oldsite.net",
215
+ "finance.vpn",
216
+ "engineering.dev.example.com",
217
+ "img02.static.cdn.example.com",
218
+ "service.staging.example.org",
219
+ "legacy.apac.oldsite.com",
220
+ "shop.uk.example.net",
221
+ "bob.mail.example.com",
222
+ "app1.cluster2.example.cloud",
223
+ "astrophysics.research.corp.com",
224
+ "cdn.v3.content.net",
225
+ "archive.2021.oldsite.net",
226
+ "legacy.2021.oldsite.net",
227
+ "quality.bbb.ts.net",
228
+ "quality.ccc.ts.net"
229
+ ]
230
+
231
+ logger.warning('hello?')
232
+ for test in tests:
233
+ print(test)
234
+ text = rewriter.rewrite(test)
235
+ text
236
+
237
+ tests
@@ -0,0 +1,273 @@
1
+ from datetime import datetime
2
+ from functools import cached_property
3
+ from itertools import chain
4
+ from typing import List, Dict
5
+
6
+ from setuptools import find_namespace_packages, find_packages, setup
7
+
8
+ from fmtr.tools.constants import Constants
9
+ from fmtr.tools.path_tools import Path
10
+ from fmtr.tools.path_tools.path_tools import PathsBase
11
+
12
+
13
+ class SetupPaths(PathsBase):
14
+ """
15
+
16
+ Canonical paths for a package.
17
+
18
+ """
19
+
20
+ SKIP_DIRS = {'data'}
21
+
22
+ def __init__(self, path=None):
23
+
24
+ """
25
+
26
+ Use calling module path as default path, if not otherwise specified.
27
+
28
+ """
29
+ if not path:
30
+ path = self.from_caller()
31
+
32
+ self.repo = Path(path)
33
+
34
+ @property
35
+ def path(self):
36
+ if self.is_namespace:
37
+ return self.repo / self.org / self.name
38
+ else:
39
+ return self.repo / self.name
40
+
41
+ @property
42
+ def readme(self):
43
+ return self.repo / 'README.md'
44
+
45
+ @property
46
+ def version(self):
47
+ return self.path / Constants.FILENAME_VERSION
48
+
49
+ @cached_property
50
+ def layout(self):
51
+
52
+ directories = [
53
+ dir for dir in self.repo.iterdir()
54
+ if dir.is_dir() and not dir.name.startswith('.') and dir.name not in self.SKIP_DIRS
55
+ ]
56
+
57
+ if len(directories) != 1:
58
+ dirs_str = ', '.join([str(dir) for dir in directories])
59
+ raise ValueError(f'Expected exactly one directory in "{self.repo}", found {dirs_str}')
60
+
61
+ target = next(iter(directories))
62
+
63
+ contents = list(target.iterdir())
64
+ if len(contents) == 1 and (item := next(iter(contents))).is_dir():
65
+ return True, target.name, item.name
66
+
67
+ else:
68
+ return False, None, target.name
69
+
70
+ @property
71
+ def is_namespace(self) -> str:
72
+ is_namespace, org, name = self.layout
73
+ return is_namespace
74
+
75
+ @property
76
+ def org(self) -> str:
77
+ is_namespace, org, name = self.layout
78
+ return org
79
+
80
+ @property
81
+ def name(self) -> str:
82
+ is_namespace, org, name = self.layout
83
+ return name
84
+
85
+
86
+ class Setup:
87
+ AUTHOR = 'Frontmatter'
88
+ AUTHOR_EMAIL = 'innovative.fowler@mask.pro.fmtr.dev'
89
+
90
+ def __init__(self, paths, dependencies, console_scripts=None, client=None, **kwargs):
91
+
92
+
93
+ self.kwargs = kwargs
94
+ self.dependencies = dependencies
95
+ self.paths = paths
96
+
97
+ self.client = client
98
+ self.console_scripts = console_scripts
99
+
100
+ def get_entrypoint_path(self, key, value):
101
+ if value:
102
+ return f'{self.name}.{value}:{key}'
103
+ else:
104
+ return f'{self.name}:{key}'
105
+
106
+ @property
107
+ def entrypoints(self):
108
+ if self.console_scripts:
109
+ return dict(
110
+ console_scripts=[f'{key} = {self.get_entrypoint_path(key, value)}' for key, value in self.console_scripts.items()],
111
+ )
112
+ else:
113
+ return dict()
114
+
115
+ @property
116
+ def name(self):
117
+ if self.paths.is_namespace:
118
+ return f'{self.paths.org}.{self.paths.name}'
119
+ return self.paths.name
120
+
121
+ @property
122
+ def author(self):
123
+ if self.client:
124
+ return f'{self.AUTHOR} on behalf of {self.client}'
125
+ return self.AUTHOR
126
+
127
+ @property
128
+ def copyright(self):
129
+ if self.client:
130
+ return self.client
131
+ return self.AUTHOR
132
+
133
+ @property
134
+ def long_description(self):
135
+
136
+ return self.paths.readme.read_text()
137
+
138
+ @property
139
+ def version(self):
140
+ return self.paths.version.read_text().strip()
141
+
142
+ @property
143
+ def packages(self):
144
+
145
+ excludes = list(SetupPaths.SKIP_DIRS) + [f'{name}.*' for name in SetupPaths.SKIP_DIRS]
146
+
147
+ if self.paths.is_namespace:
148
+ return find_namespace_packages(where=str(self.paths.repo), exclude=excludes)
149
+ else:
150
+ return find_packages(where=str(self.paths.repo), exclude=excludes)
151
+
152
+ @property
153
+ def package_dir(self):
154
+ if self.paths.is_namespace:
155
+ return {'': str(self.paths.repo)}
156
+ else:
157
+ return None
158
+
159
+ @property
160
+ def package_data(self):
161
+ return {self.name: [Constants.FILENAME_VERSION]}
162
+
163
+ @property
164
+ def url(self):
165
+ return f'https://github.com/{self.paths.org}/{self.paths.name}'
166
+
167
+ def get_data_setup(self):
168
+ return dict(
169
+ name=self.name,
170
+ version=self.version,
171
+ author=self.author,
172
+ author_email=self.AUTHOR_EMAIL,
173
+ url=self.url,
174
+ license=f'Copyright © {datetime.now().year} {self.copyright}. All rights reserved.',
175
+ long_description=self.long_description,
176
+ long_description_content_type='text/markdown',
177
+ packages=self.packages,
178
+ package_dir=self.package_dir,
179
+ package_data=self.package_data,
180
+ entry_points=self.entrypoints,
181
+ install_requires=self.dependencies.install,
182
+ extras_require=self.dependencies.extras,
183
+ ) | self.kwargs
184
+
185
+ def setup(self):
186
+ return setup(**self.get_data_setup())
187
+
188
+ class Entrypoints:
189
+ ALL = 'all'
190
+ INSTALL = 'install'
191
+
192
+ def __init__(self, console_scripts=None, **kwargs):
193
+ self.kwargs = kwargs
194
+ self._console_scripts = console_scripts
195
+
196
+ @property
197
+ def console_scripts(self):
198
+ return [f'{key} = {value}:{key}' for key, value in self._console_scripts.items()]
199
+
200
+ @property
201
+ def data(self):
202
+ return dict(
203
+ console_scripts=self.console_scripts,
204
+ ) | self.kwargs
205
+
206
+
207
+ class Dependencies:
208
+ ALL = 'all'
209
+ INSTALL = 'install'
210
+
211
+ def __init__(self, **kwargs):
212
+ self.dependencies = kwargs
213
+
214
+ def resolve_values(self, key) -> List[str]:
215
+ """
216
+
217
+ Flatten a list of values.
218
+
219
+ """
220
+ values_resolved = []
221
+ values = self.dependencies[key]
222
+
223
+ for value in values:
224
+ if value == key or value not in self.dependencies:
225
+ # Add the value directly if it references itself or is not a dependency key.
226
+ values_resolved.append(value)
227
+ else:
228
+ # Recurse into nested dependencies.
229
+ values_resolved += self.resolve_values(value)
230
+
231
+ return values_resolved
232
+
233
+ @property
234
+ def extras(self) -> Dict[str, List[str]]:
235
+ """
236
+
237
+ Flatten dependencies.
238
+
239
+ """
240
+ resolved = {key: self.resolve_values(key) for key in self.dependencies.keys()}
241
+ resolved.pop(self.INSTALL, None)
242
+ resolved[self.ALL] = list(set(chain.from_iterable(resolved.values())))
243
+ return resolved
244
+
245
+ @property
246
+ def install(self):
247
+ return self.resolve_values(self.INSTALL)
248
+
249
+
250
+ if __name__ == '__main__':
251
+ ds = Dependencies(
252
+ install=['version', 'yaml'],
253
+
254
+ yaml=['yamlscript', 'pyyaml'],
255
+ logging=['logfire', 'version'],
256
+ version=['semver', 'av'],
257
+ av=['av']
258
+ # Add the rest of your dependencies...
259
+ )
260
+
261
+ ds
262
+
263
+ setup = Setup(
264
+ # client='Acme',
265
+ dependencies=ds,
266
+ description='some tools test',
267
+ console_scripts=dict(
268
+ cache_hfh='console_script_tools',
269
+ test=None,
270
+ )
271
+ )
272
+ data = setup.get_data_setup()
273
+ data
@@ -171,9 +171,3 @@ def trim(text: str) -> str:
171
171
 
172
172
  """
173
173
  return dedent(text).strip()
174
-
175
- if __name__ == '__main__':
176
- import numpy as np
177
-
178
- st = join([1, None, 'test', np.nan, 0, '', 'yeah'])
179
- st
@@ -0,0 +1 @@
1
+ 1.1.18
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmtr.tools
3
- Version: 1.1.16
3
+ Version: 1.1.18
4
4
  Summary: Collection of high-level tools to simplify everyday development tasks, with a focus on AI/ML
5
5
  Home-page: https://github.com/fmtr/fmtr.tools
6
6
  Author: Frontmatter
@@ -9,54 +9,60 @@ License: Copyright © 2025 Frontmatter. All rights reserved.
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
11
  Provides-Extra: test
12
- Requires-Dist: distributed; extra == "test"
13
- Requires-Dist: uvicorn[standard]; extra == "test"
14
- Requires-Dist: google-auth; extra == "test"
15
- Requires-Dist: tokenizers; extra == "test"
16
- Requires-Dist: torchaudio; extra == "test"
17
- Requires-Dist: pymupdf; extra == "test"
18
- Requires-Dist: appdirs; extra == "test"
19
- Requires-Dist: huggingface_hub; extra == "test"
12
+ Requires-Dist: dask[bag]; extra == "test"
13
+ Requires-Dist: deepmerge; extra == "test"
14
+ Requires-Dist: contexttimer; extra == "test"
15
+ Requires-Dist: flet-webview; extra == "test"
16
+ Requires-Dist: regex; extra == "test"
20
17
  Requires-Dist: tabulate; extra == "test"
18
+ Requires-Dist: bokeh; extra == "test"
19
+ Requires-Dist: google-auth-oauthlib; extra == "test"
20
+ Requires-Dist: faker; extra == "test"
21
+ Requires-Dist: google-api-python-client; extra == "test"
22
+ Requires-Dist: httpx; extra == "test"
23
+ Requires-Dist: transformers[sentencepiece]; extra == "test"
24
+ Requires-Dist: ollama; extra == "test"
25
+ Requires-Dist: tokenizers; extra == "test"
26
+ Requires-Dist: httpx_retries; extra == "test"
27
+ Requires-Dist: openpyxl; extra == "test"
28
+ Requires-Dist: sentence_transformers; extra == "test"
21
29
  Requires-Dist: pydantic; extra == "test"
22
- Requires-Dist: html2text; extra == "test"
30
+ Requires-Dist: google-auth; extra == "test"
31
+ Requires-Dist: semver; extra == "test"
32
+ Requires-Dist: pydevd-pycharm; extra == "test"
33
+ Requires-Dist: logfire; extra == "test"
34
+ Requires-Dist: pydantic-ai[logfire,openai]; extra == "test"
35
+ Requires-Dist: yamlscript; extra == "test"
36
+ Requires-Dist: pymupdf; extra == "test"
23
37
  Requires-Dist: docker; extra == "test"
24
- Requires-Dist: logfire[fastapi]; extra == "test"
25
- Requires-Dist: filetype; extra == "test"
26
38
  Requires-Dist: Unidecode; extra == "test"
27
- Requires-Dist: sentence_transformers; extra == "test"
39
+ Requires-Dist: torchaudio; extra == "test"
40
+ Requires-Dist: pymupdf4llm; extra == "test"
28
41
  Requires-Dist: fastapi; extra == "test"
29
- Requires-Dist: flet-webview; extra == "test"
30
- Requires-Dist: pydantic-settings; extra == "test"
31
- Requires-Dist: contexttimer; extra == "test"
32
- Requires-Dist: diskcache; extra == "test"
33
- Requires-Dist: ollama; extra == "test"
34
- Requires-Dist: logfire; extra == "test"
35
- Requires-Dist: openai; extra == "test"
36
- Requires-Dist: pydevd-pycharm; extra == "test"
42
+ Requires-Dist: distributed; extra == "test"
43
+ Requires-Dist: logfire[httpx]; extra == "test"
44
+ Requires-Dist: dnspython[doh]; extra == "test"
45
+ Requires-Dist: torchvision; extra == "test"
37
46
  Requires-Dist: pandas; extra == "test"
47
+ Requires-Dist: filetype; extra == "test"
38
48
  Requires-Dist: flet-video; extra == "test"
39
- Requires-Dist: yamlscript; extra == "test"
49
+ Requires-Dist: appdirs; extra == "test"
50
+ Requires-Dist: pydantic-settings; extra == "test"
51
+ Requires-Dist: pytest-cov; extra == "test"
52
+ Requires-Dist: logfire[fastapi]; extra == "test"
40
53
  Requires-Dist: sre_yield; extra == "test"
41
- Requires-Dist: google-auth-httplib2; extra == "test"
42
- Requires-Dist: google-api-python-client; extra == "test"
43
- Requires-Dist: dask[bag]; extra == "test"
44
- Requires-Dist: pydantic-ai[logfire,openai]; extra == "test"
45
- Requires-Dist: torchvision; extra == "test"
46
- Requires-Dist: openpyxl; extra == "test"
47
- Requires-Dist: flet[all]; extra == "test"
48
- Requires-Dist: bokeh; extra == "test"
49
- Requires-Dist: pyyaml; extra == "test"
50
54
  Requires-Dist: json_repair; extra == "test"
51
- Requires-Dist: pymupdf4llm; extra == "test"
52
55
  Requires-Dist: peft; extra == "test"
53
- Requires-Dist: deepmerge; extra == "test"
54
- Requires-Dist: pytest-cov; extra == "test"
55
- Requires-Dist: faker; extra == "test"
56
+ Requires-Dist: html2text; extra == "test"
57
+ Requires-Dist: openai; extra == "test"
58
+ Requires-Dist: google-auth-httplib2; extra == "test"
59
+ Requires-Dist: setuptools; extra == "test"
60
+ Requires-Dist: uvicorn[standard]; extra == "test"
61
+ Requires-Dist: pyyaml; extra == "test"
62
+ Requires-Dist: flet[all]; extra == "test"
63
+ Requires-Dist: huggingface_hub; extra == "test"
64
+ Requires-Dist: diskcache; extra == "test"
56
65
  Requires-Dist: tinynetrc; extra == "test"
57
- Requires-Dist: google-auth-oauthlib; extra == "test"
58
- Requires-Dist: semver; extra == "test"
59
- Requires-Dist: transformers[sentencepiece]; extra == "test"
60
66
  Provides-Extra: yaml
61
67
  Requires-Dist: yamlscript; extra == "yaml"
62
68
  Requires-Dist: pyyaml; extra == "yaml"
@@ -157,6 +163,18 @@ Provides-Extra: path-app
157
163
  Requires-Dist: appdirs; extra == "path-app"
158
164
  Provides-Extra: path-type
159
165
  Requires-Dist: filetype; extra == "path-type"
166
+ Provides-Extra: dns
167
+ Requires-Dist: dnspython[doh]; extra == "dns"
168
+ Provides-Extra: patterns
169
+ Requires-Dist: regex; extra == "patterns"
170
+ Provides-Extra: http
171
+ Requires-Dist: httpx; extra == "http"
172
+ Requires-Dist: httpx_retries; extra == "http"
173
+ Requires-Dist: logfire; extra == "http"
174
+ Requires-Dist: semver; extra == "http"
175
+ Requires-Dist: logfire[httpx]; extra == "http"
176
+ Provides-Extra: setup
177
+ Requires-Dist: setuptools; extra == "setup"
160
178
  Dynamic: author
161
179
  Dynamic: author-email
162
180
  Dynamic: description
@@ -12,6 +12,7 @@ setup.py
12
12
  ./fmtr/tools/dataclass_tools.py
13
13
  ./fmtr/tools/datatype_tools.py
14
14
  ./fmtr/tools/debugging_tools.py
15
+ ./fmtr/tools/dns_tools.py
15
16
  ./fmtr/tools/docker_tools.py
16
17
  ./fmtr/tools/environment_tools.py
17
18
  ./fmtr/tools/function_tools.py
@@ -33,6 +34,7 @@ setup.py
33
34
  ./fmtr/tools/openai_tools.py
34
35
  ./fmtr/tools/packaging_tools.py
35
36
  ./fmtr/tools/parallel_tools.py
37
+ ./fmtr/tools/pattern_tools.py
36
38
  ./fmtr/tools/pdf_tools.py
37
39
  ./fmtr/tools/platform_tools.py
38
40
  ./fmtr/tools/process_tools.py
@@ -40,6 +42,7 @@ setup.py
40
42
  ./fmtr/tools/random_tools.py
41
43
  ./fmtr/tools/semantic_tools.py
42
44
  ./fmtr/tools/settings_tools.py
45
+ ./fmtr/tools/setup_tools.py
43
46
  ./fmtr/tools/spaces_tools.py
44
47
  ./fmtr/tools/string_tools.py
45
48
  ./fmtr/tools/tabular_tools.py
@@ -35,6 +35,9 @@ pydevd-pycharm
35
35
  [dm]
36
36
  pydantic
37
37
 
38
+ [dns]
39
+ dnspython[doh]
40
+
38
41
  [docker.api]
39
42
  docker
40
43
 
@@ -50,6 +53,13 @@ huggingface_hub
50
53
  [html]
51
54
  html2text
52
55
 
56
+ [http]
57
+ httpx
58
+ httpx_retries
59
+ logfire
60
+ semver
61
+ logfire[httpx]
62
+
53
63
  [interface]
54
64
  flet[all]
55
65
  flet-video
@@ -88,6 +98,9 @@ appdirs
88
98
  [path.type]
89
99
  filetype
90
100
 
101
+ [patterns]
102
+ regex
103
+
91
104
  [pdf]
92
105
  pymupdf
93
106
  pydantic
@@ -110,6 +123,9 @@ openpyxl
110
123
  pydantic-settings
111
124
  pydantic
112
125
 
126
+ [setup]
127
+ setuptools
128
+
113
129
  [spaces]
114
130
  tinynetrc
115
131
 
@@ -119,54 +135,60 @@ tabulate
119
135
  openpyxl
120
136
 
121
137
  [test]
122
- distributed
123
- uvicorn[standard]
124
- google-auth
125
- tokenizers
126
- torchaudio
127
- pymupdf
128
- appdirs
129
- huggingface_hub
138
+ dask[bag]
139
+ deepmerge
140
+ contexttimer
141
+ flet-webview
142
+ regex
130
143
  tabulate
144
+ bokeh
145
+ google-auth-oauthlib
146
+ faker
147
+ google-api-python-client
148
+ httpx
149
+ transformers[sentencepiece]
150
+ ollama
151
+ tokenizers
152
+ httpx_retries
153
+ openpyxl
154
+ sentence_transformers
131
155
  pydantic
132
- html2text
156
+ google-auth
157
+ semver
158
+ pydevd-pycharm
159
+ logfire
160
+ pydantic-ai[logfire,openai]
161
+ yamlscript
162
+ pymupdf
133
163
  docker
134
- logfire[fastapi]
135
- filetype
136
164
  Unidecode
137
- sentence_transformers
165
+ torchaudio
166
+ pymupdf4llm
138
167
  fastapi
139
- flet-webview
140
- pydantic-settings
141
- contexttimer
142
- diskcache
143
- ollama
144
- logfire
145
- openai
146
- pydevd-pycharm
168
+ distributed
169
+ logfire[httpx]
170
+ dnspython[doh]
171
+ torchvision
147
172
  pandas
173
+ filetype
148
174
  flet-video
149
- yamlscript
175
+ appdirs
176
+ pydantic-settings
177
+ pytest-cov
178
+ logfire[fastapi]
150
179
  sre_yield
151
- google-auth-httplib2
152
- google-api-python-client
153
- dask[bag]
154
- pydantic-ai[logfire,openai]
155
- torchvision
156
- openpyxl
157
- flet[all]
158
- bokeh
159
- pyyaml
160
180
  json_repair
161
- pymupdf4llm
162
181
  peft
163
- deepmerge
164
- pytest-cov
165
- faker
182
+ html2text
183
+ openai
184
+ google-auth-httplib2
185
+ setuptools
186
+ uvicorn[standard]
187
+ pyyaml
188
+ flet[all]
189
+ huggingface_hub
190
+ diskcache
166
191
  tinynetrc
167
- google-auth-oauthlib
168
- semver
169
- transformers[sentencepiece]
170
192
 
171
193
  [tokenization]
172
194
  tokenizers
@@ -1 +0,0 @@
1
- 1.1.16
File without changes
File without changes
File without changes
File without changes