fmtr.tools 1.2.6__tar.gz → 1.2.7__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 (89) hide show
  1. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/PKG-INFO +43 -43
  2. fmtr_tools-1.2.7/fmtr/tools/pattern_tools.py +232 -0
  3. fmtr_tools-1.2.7/fmtr/tools/version +1 -0
  4. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr.tools.egg-info/PKG-INFO +43 -43
  5. fmtr_tools-1.2.6/fmtr/tools/pattern_tools.py +0 -175
  6. fmtr_tools-1.2.6/fmtr/tools/version +0 -1
  7. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/LICENSE +0 -0
  8. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/README.md +0 -0
  9. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/__init__.py +0 -0
  10. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/ai_tools/__init__.py +0 -0
  11. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/ai_tools/agentic_tools.py +0 -0
  12. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/ai_tools/inference_tools.py +0 -0
  13. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/api_tools.py +0 -0
  14. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/async_tools.py +0 -0
  15. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/augmentation_tools.py +0 -0
  16. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/caching_tools.py +0 -0
  17. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/constants.py +0 -0
  18. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/data_modelling_tools.py +0 -0
  19. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/dataclass_tools.py +0 -0
  20. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/datatype_tools.py +0 -0
  21. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/debugging_tools.py +0 -0
  22. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/dns_tools/__init__.py +0 -0
  23. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/dns_tools/client.py +0 -0
  24. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/dns_tools/dm.py +0 -0
  25. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/dns_tools/server.py +0 -0
  26. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/docker_tools.py +0 -0
  27. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/entrypoints/__init__.py +0 -0
  28. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/entrypoints/cache_hfh.py +0 -0
  29. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/entrypoints/ep_test.py +0 -0
  30. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/entrypoints/remote_debug_test.py +0 -0
  31. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/entrypoints/shell_debug.py +0 -0
  32. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/environment_tools.py +0 -0
  33. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/function_tools.py +0 -0
  34. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/google_api_tools.py +0 -0
  35. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/hash_tools.py +0 -0
  36. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/hfh_tools.py +0 -0
  37. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/html_tools.py +0 -0
  38. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/http_tools.py +0 -0
  39. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/import_tools.py +0 -0
  40. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/inspection_tools.py +0 -0
  41. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/interface_tools.py +0 -0
  42. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/iterator_tools.py +0 -0
  43. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/json_fix_tools.py +0 -0
  44. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/json_tools.py +0 -0
  45. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/logging_tools.py +0 -0
  46. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/merging_tools.py +0 -0
  47. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/metric_tools.py +0 -0
  48. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/name_tools.py +0 -0
  49. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/netrc_tools.py +0 -0
  50. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/openai_tools.py +0 -0
  51. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/packaging_tools.py +0 -0
  52. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/parallel_tools.py +0 -0
  53. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/path_tools/__init__.py +0 -0
  54. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/path_tools/app_path_tools.py +0 -0
  55. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/path_tools/path_tools.py +0 -0
  56. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/path_tools/type_path_tools.py +0 -0
  57. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/pdf_tools.py +0 -0
  58. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/platform_tools.py +0 -0
  59. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/process_tools.py +0 -0
  60. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/profiling_tools.py +0 -0
  61. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/random_tools.py +0 -0
  62. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/semantic_tools.py +0 -0
  63. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/settings_tools.py +0 -0
  64. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/setup_tools/__init__.py +0 -0
  65. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/setup_tools/setup_tools.py +0 -0
  66. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/spaces_tools.py +0 -0
  67. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/string_tools.py +0 -0
  68. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/tabular_tools.py +0 -0
  69. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/tests/__init__.py +0 -0
  70. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/tests/conftest.py +0 -0
  71. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/tests/helpers.py +0 -0
  72. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/tests/test_datatype.py +0 -0
  73. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/tests/test_environment.py +0 -0
  74. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/tests/test_json.py +0 -0
  75. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/tests/test_path.py +0 -0
  76. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/tests/test_yaml.py +0 -0
  77. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/tokenization_tools.py +0 -0
  78. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/tools.py +0 -0
  79. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/unicode_tools.py +0 -0
  80. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/version_tools.py +0 -0
  81. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr/tools/yaml_tools.py +0 -0
  82. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr.tools.egg-info/SOURCES.txt +0 -0
  83. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr.tools.egg-info/dependency_links.txt +0 -0
  84. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr.tools.egg-info/entry_points.txt +0 -0
  85. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr.tools.egg-info/requires.txt +42 -42
  86. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/fmtr.tools.egg-info/top_level.txt +0 -0
  87. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/pyproject.toml +0 -0
  88. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/setup.cfg +0 -0
  89. {fmtr_tools-1.2.6 → fmtr_tools-1.2.7}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmtr.tools
3
- Version: 1.2.6
3
+ Version: 1.2.7
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/tools
6
6
  Author: Frontmatter
@@ -130,60 +130,60 @@ Requires-Dist: logfire[httpx]; extra == "http"
130
130
  Provides-Extra: setup
131
131
  Requires-Dist: setuptools; extra == "setup"
132
132
  Provides-Extra: all
133
- Requires-Dist: google-api-python-client; extra == "all"
133
+ Requires-Dist: distributed; extra == "all"
134
+ Requires-Dist: diskcache; extra == "all"
135
+ Requires-Dist: sre_yield; extra == "all"
136
+ Requires-Dist: httpx_retries; extra == "all"
137
+ Requires-Dist: filetype; extra == "all"
138
+ Requires-Dist: logfire[httpx]; extra == "all"
139
+ Requires-Dist: pymupdf4llm; extra == "all"
134
140
  Requires-Dist: Unidecode; extra == "all"
135
141
  Requires-Dist: uvicorn[standard]; extra == "all"
136
- Requires-Dist: huggingface_hub; extra == "all"
137
- Requires-Dist: logfire[fastapi]; extra == "all"
138
- Requires-Dist: pymupdf4llm; extra == "all"
142
+ Requires-Dist: faker; extra == "all"
139
143
  Requires-Dist: semver; extra == "all"
140
- Requires-Dist: pymupdf; extra == "all"
141
- Requires-Dist: bokeh; extra == "all"
144
+ Requires-Dist: pyyaml; extra == "all"
145
+ Requires-Dist: regex; extra == "all"
142
146
  Requires-Dist: contexttimer; extra == "all"
143
- Requires-Dist: diskcache; extra == "all"
144
147
  Requires-Dist: peft; extra == "all"
145
- Requires-Dist: dask[bag]; extra == "all"
146
- Requires-Dist: pydevd-pycharm; extra == "all"
147
- Requires-Dist: transformers[sentencepiece]; extra == "all"
148
- Requires-Dist: regex; extra == "all"
149
- Requires-Dist: json_repair; extra == "all"
150
- Requires-Dist: html2text; extra == "all"
151
- Requires-Dist: faker; extra == "all"
148
+ Requires-Dist: flet-webview; extra == "all"
149
+ Requires-Dist: pydantic-settings; extra == "all"
150
+ Requires-Dist: deepmerge; extra == "all"
151
+ Requires-Dist: httpx; extra == "all"
152
+ Requires-Dist: pydantic; extra == "all"
153
+ Requires-Dist: pymupdf; extra == "all"
154
+ Requires-Dist: google-auth; extra == "all"
155
+ Requires-Dist: google-api-python-client; extra == "all"
156
+ Requires-Dist: logfire; extra == "all"
152
157
  Requires-Dist: openpyxl; extra == "all"
153
- Requires-Dist: appdirs; extra == "all"
154
- Requires-Dist: logfire[httpx]; extra == "all"
155
- Requires-Dist: openai; extra == "all"
158
+ Requires-Dist: html2text; extra == "all"
159
+ Requires-Dist: pytest-cov; extra == "all"
160
+ Requires-Dist: docker; extra == "all"
161
+ Requires-Dist: google-auth-oauthlib; extra == "all"
162
+ Requires-Dist: json_repair; extra == "all"
163
+ Requires-Dist: ollama; extra == "all"
156
164
  Requires-Dist: sentence_transformers; extra == "all"
157
- Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
165
+ Requires-Dist: pandas; extra == "all"
166
+ Requires-Dist: setuptools; extra == "all"
167
+ Requires-Dist: logfire[fastapi]; extra == "all"
168
+ Requires-Dist: tabulate; extra == "all"
169
+ Requires-Dist: pydevd-pycharm; extra == "all"
170
+ Requires-Dist: huggingface_hub; extra == "all"
171
+ Requires-Dist: flet-video; extra == "all"
172
+ Requires-Dist: dask[bag]; extra == "all"
173
+ Requires-Dist: google-auth-httplib2; extra == "all"
174
+ Requires-Dist: bokeh; extra == "all"
158
175
  Requires-Dist: fastapi; extra == "all"
159
- Requires-Dist: torchvision; extra == "all"
160
- Requires-Dist: ollama; extra == "all"
161
- Requires-Dist: tokenizers; extra == "all"
162
176
  Requires-Dist: dnspython[doh]; extra == "all"
163
- Requires-Dist: flet-video; extra == "all"
164
- Requires-Dist: pandas; extra == "all"
177
+ Requires-Dist: transformers[sentencepiece]; extra == "all"
165
178
  Requires-Dist: yamlscript; extra == "all"
166
- Requires-Dist: filetype; extra == "all"
167
- Requires-Dist: logfire; extra == "all"
168
- Requires-Dist: tabulate; extra == "all"
169
- Requires-Dist: google-auth-oauthlib; extra == "all"
179
+ Requires-Dist: torchaudio; extra == "all"
180
+ Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
181
+ Requires-Dist: torchvision; extra == "all"
170
182
  Requires-Dist: flet[all]; extra == "all"
171
- Requires-Dist: pydantic-settings; extra == "all"
183
+ Requires-Dist: openai; extra == "all"
172
184
  Requires-Dist: tinynetrc; extra == "all"
173
- Requires-Dist: deepmerge; extra == "all"
174
- Requires-Dist: distributed; extra == "all"
175
- Requires-Dist: flet-webview; extra == "all"
176
- Requires-Dist: setuptools; extra == "all"
177
- Requires-Dist: pyyaml; extra == "all"
178
- Requires-Dist: sre_yield; extra == "all"
179
- Requires-Dist: google-auth; extra == "all"
180
- Requires-Dist: torchaudio; extra == "all"
181
- Requires-Dist: pydantic; extra == "all"
182
- Requires-Dist: google-auth-httplib2; extra == "all"
183
- Requires-Dist: docker; extra == "all"
184
- Requires-Dist: httpx; extra == "all"
185
- Requires-Dist: pytest-cov; extra == "all"
186
- Requires-Dist: httpx_retries; extra == "all"
185
+ Requires-Dist: appdirs; extra == "all"
186
+ Requires-Dist: tokenizers; extra == "all"
187
187
  Dynamic: author
188
188
  Dynamic: author-email
189
189
  Dynamic: description
@@ -0,0 +1,232 @@
1
+ import regex as re
2
+ from dataclasses import dataclass, asdict
3
+ from functools import cached_property
4
+ from typing import List, Any
5
+
6
+ from fmtr.tools.logging_tools import logger
7
+ from fmtr.tools.string_tools import join
8
+
9
+
10
+ class RewriteCircularLoopError(Exception):
11
+ """
12
+
13
+ Circular loop error
14
+
15
+ """
16
+
17
+
18
+ MASK_GROUP = '(?:{pattern})'
19
+ MASK_NAMED = r"(?P<{key}>{pattern})"
20
+
21
+
22
+ def alt(*patterns):
23
+ patterns = sorted(patterns, key=len, reverse=True)
24
+ pattern = '|'.join(patterns)
25
+ pattern = MASK_GROUP.format(pattern=pattern)
26
+ return pattern
27
+
28
+
29
+
30
+
31
+
32
+
33
+ @dataclass
34
+ class Key:
35
+ RECORD_SEP = '␞'
36
+
37
+ def flatten(self, data):
38
+ """
39
+
40
+ Flatten/serialise dictionary data
41
+
42
+ """
43
+ pairs = [f'{value}' for key, value in data.items()]
44
+ string = self.RECORD_SEP.join(pairs)
45
+ return string
46
+
47
+ @cached_property
48
+ def pattern(self):
49
+ """
50
+
51
+ Serialise to pattern
52
+
53
+ """
54
+ data = {key: MASK_NAMED.format(key=key, pattern=value) for key, value in asdict(self).items()}
55
+ pattern = self.flatten(data)
56
+ return pattern
57
+
58
+ @cached_property
59
+ def rx(self):
60
+ """
61
+
62
+ Compile to Regular Expression
63
+
64
+ """
65
+ return re.compile(self.pattern)
66
+
67
+ @cached_property
68
+ def string(self):
69
+ """
70
+
71
+ Serialise to string
72
+
73
+ """
74
+ string = self.flatten(asdict(self))
75
+ return string
76
+
77
+ def transform(self, match: re.Match):
78
+ """
79
+
80
+ Transform match object into a new object of the same type.
81
+
82
+ """
83
+ groupdict = match.groupdict()
84
+ data = {key: value.format(**groupdict) for key, value in asdict(self).items()}
85
+ obj = self.__class__(**data)
86
+ return obj
87
+
88
+
89
+ @dataclass
90
+ class Item:
91
+ """
92
+
93
+ Key-value pair
94
+
95
+ """
96
+ key: Key
97
+ value: Key
98
+
99
+ @dataclass
100
+ class Mapper:
101
+ """
102
+
103
+ Pattern-based, dictionary-like mapper.
104
+ Compiles a single regex pattern from a list of rules, and determines which rule matched.
105
+ It supports initialization from structured rule data, execution of a single lookup pass, and
106
+ recursive lookups until a stable state is reached.
107
+
108
+ """
109
+ PREFIX_GROUP = '__'
110
+ items: List[Item]
111
+ default: Any = None
112
+ is_recursive: bool = False
113
+
114
+ @cached_property
115
+ def pattern(self):
116
+ """
117
+
118
+ Provides a dynamically generated regex pattern based on the rules provided.
119
+
120
+ """
121
+ patterns = [
122
+ MASK_NAMED.format(key=f'{self.PREFIX_GROUP}{i}', pattern=item.key.pattern)
123
+ for i, item in enumerate(self.items)
124
+ ]
125
+ pattern = alt(*patterns)
126
+ return pattern
127
+
128
+ @cached_property
129
+ def rx(self):
130
+ """
131
+
132
+ Regex object.
133
+
134
+ """
135
+ return re.compile(self.pattern)
136
+
137
+ def get_default(self, key: Key):
138
+ if self.is_recursive:
139
+ return key
140
+ else:
141
+ return self.default
142
+
143
+ def get(self, key: Key) -> Key:
144
+ """
145
+
146
+ Use recursive or single lookup pass, depending on whether recursive lookups have been specified.
147
+
148
+ """
149
+ if self.is_recursive:
150
+ return self.get_recursive(key)
151
+ else:
152
+ return self.get_one(key)
153
+
154
+ def get_one(self, key: Key):
155
+ """
156
+
157
+ Single lookup pass.
158
+ Lookup the source string based on the matching rule.
159
+
160
+ """
161
+
162
+ match = self.rx.fullmatch(key.string)
163
+
164
+ if not match:
165
+ value = self.get_default(key)
166
+ logger.debug(f'No match for {key=}.')
167
+ else:
168
+
169
+ match_ids = {name: v for name, v in match.groupdict().items() if v}
170
+ rule_ids = {
171
+ int(id.removeprefix(self.PREFIX_GROUP))
172
+ for id in match_ids.keys() if id.startswith(self.PREFIX_GROUP)
173
+ }
174
+
175
+ if len(rule_ids) != 1:
176
+ msg = f'Multiple group matches: {rule_ids}'
177
+ raise ValueError(msg)
178
+
179
+ rule_id = next(iter(rule_ids))
180
+ rule = self.items[rule_id]
181
+
182
+ if isinstance(rule.value, Key):
183
+ value = rule.value.transform(match)
184
+ else:
185
+ value = rule.value
186
+
187
+ logger.debug(f'Matched using {rule_id=}: {key=} → {value=}')
188
+
189
+ return value
190
+
191
+ def get_recursive(self, key: Key) -> Key:
192
+ """
193
+
194
+ Lookup the provided text by continuously applying lookup rules until no changes are made
195
+ or a circular loop is detected.
196
+
197
+ """
198
+ history = []
199
+ previous = key
200
+
201
+ def get_history_str():
202
+ return join(history, sep=' → ')
203
+
204
+ with logger.span(f'Matching {key=}...'):
205
+ while True:
206
+ if previous in history:
207
+ history.append(previous)
208
+ msg = f'Loop detected on node "{previous}": {get_history_str()}'
209
+ raise RewriteCircularLoopError(msg)
210
+
211
+ history.append(previous)
212
+
213
+ new = previous
214
+
215
+ new = self.get_one(new)
216
+
217
+ if new == previous:
218
+ break
219
+
220
+ previous = new
221
+
222
+ if len(history) == 1:
223
+ history_str = 'No matching performed.'
224
+ else:
225
+ history_str = get_history_str()
226
+ logger.debug(f'Finished matching: {history_str}')
227
+
228
+ return previous
229
+
230
+
231
+ if __name__ == '__main__':
232
+ ...
@@ -0,0 +1 @@
1
+ 1.2.7
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmtr.tools
3
- Version: 1.2.6
3
+ Version: 1.2.7
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/tools
6
6
  Author: Frontmatter
@@ -130,60 +130,60 @@ Requires-Dist: logfire[httpx]; extra == "http"
130
130
  Provides-Extra: setup
131
131
  Requires-Dist: setuptools; extra == "setup"
132
132
  Provides-Extra: all
133
- Requires-Dist: google-api-python-client; extra == "all"
133
+ Requires-Dist: distributed; extra == "all"
134
+ Requires-Dist: diskcache; extra == "all"
135
+ Requires-Dist: sre_yield; extra == "all"
136
+ Requires-Dist: httpx_retries; extra == "all"
137
+ Requires-Dist: filetype; extra == "all"
138
+ Requires-Dist: logfire[httpx]; extra == "all"
139
+ Requires-Dist: pymupdf4llm; extra == "all"
134
140
  Requires-Dist: Unidecode; extra == "all"
135
141
  Requires-Dist: uvicorn[standard]; extra == "all"
136
- Requires-Dist: huggingface_hub; extra == "all"
137
- Requires-Dist: logfire[fastapi]; extra == "all"
138
- Requires-Dist: pymupdf4llm; extra == "all"
142
+ Requires-Dist: faker; extra == "all"
139
143
  Requires-Dist: semver; extra == "all"
140
- Requires-Dist: pymupdf; extra == "all"
141
- Requires-Dist: bokeh; extra == "all"
144
+ Requires-Dist: pyyaml; extra == "all"
145
+ Requires-Dist: regex; extra == "all"
142
146
  Requires-Dist: contexttimer; extra == "all"
143
- Requires-Dist: diskcache; extra == "all"
144
147
  Requires-Dist: peft; extra == "all"
145
- Requires-Dist: dask[bag]; extra == "all"
146
- Requires-Dist: pydevd-pycharm; extra == "all"
147
- Requires-Dist: transformers[sentencepiece]; extra == "all"
148
- Requires-Dist: regex; extra == "all"
149
- Requires-Dist: json_repair; extra == "all"
150
- Requires-Dist: html2text; extra == "all"
151
- Requires-Dist: faker; extra == "all"
148
+ Requires-Dist: flet-webview; extra == "all"
149
+ Requires-Dist: pydantic-settings; extra == "all"
150
+ Requires-Dist: deepmerge; extra == "all"
151
+ Requires-Dist: httpx; extra == "all"
152
+ Requires-Dist: pydantic; extra == "all"
153
+ Requires-Dist: pymupdf; extra == "all"
154
+ Requires-Dist: google-auth; extra == "all"
155
+ Requires-Dist: google-api-python-client; extra == "all"
156
+ Requires-Dist: logfire; extra == "all"
152
157
  Requires-Dist: openpyxl; extra == "all"
153
- Requires-Dist: appdirs; extra == "all"
154
- Requires-Dist: logfire[httpx]; extra == "all"
155
- Requires-Dist: openai; extra == "all"
158
+ Requires-Dist: html2text; extra == "all"
159
+ Requires-Dist: pytest-cov; extra == "all"
160
+ Requires-Dist: docker; extra == "all"
161
+ Requires-Dist: google-auth-oauthlib; extra == "all"
162
+ Requires-Dist: json_repair; extra == "all"
163
+ Requires-Dist: ollama; extra == "all"
156
164
  Requires-Dist: sentence_transformers; extra == "all"
157
- Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
165
+ Requires-Dist: pandas; extra == "all"
166
+ Requires-Dist: setuptools; extra == "all"
167
+ Requires-Dist: logfire[fastapi]; extra == "all"
168
+ Requires-Dist: tabulate; extra == "all"
169
+ Requires-Dist: pydevd-pycharm; extra == "all"
170
+ Requires-Dist: huggingface_hub; extra == "all"
171
+ Requires-Dist: flet-video; extra == "all"
172
+ Requires-Dist: dask[bag]; extra == "all"
173
+ Requires-Dist: google-auth-httplib2; extra == "all"
174
+ Requires-Dist: bokeh; extra == "all"
158
175
  Requires-Dist: fastapi; extra == "all"
159
- Requires-Dist: torchvision; extra == "all"
160
- Requires-Dist: ollama; extra == "all"
161
- Requires-Dist: tokenizers; extra == "all"
162
176
  Requires-Dist: dnspython[doh]; extra == "all"
163
- Requires-Dist: flet-video; extra == "all"
164
- Requires-Dist: pandas; extra == "all"
177
+ Requires-Dist: transformers[sentencepiece]; extra == "all"
165
178
  Requires-Dist: yamlscript; extra == "all"
166
- Requires-Dist: filetype; extra == "all"
167
- Requires-Dist: logfire; extra == "all"
168
- Requires-Dist: tabulate; extra == "all"
169
- Requires-Dist: google-auth-oauthlib; extra == "all"
179
+ Requires-Dist: torchaudio; extra == "all"
180
+ Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
181
+ Requires-Dist: torchvision; extra == "all"
170
182
  Requires-Dist: flet[all]; extra == "all"
171
- Requires-Dist: pydantic-settings; extra == "all"
183
+ Requires-Dist: openai; extra == "all"
172
184
  Requires-Dist: tinynetrc; extra == "all"
173
- Requires-Dist: deepmerge; extra == "all"
174
- Requires-Dist: distributed; extra == "all"
175
- Requires-Dist: flet-webview; extra == "all"
176
- Requires-Dist: setuptools; extra == "all"
177
- Requires-Dist: pyyaml; extra == "all"
178
- Requires-Dist: sre_yield; extra == "all"
179
- Requires-Dist: google-auth; extra == "all"
180
- Requires-Dist: torchaudio; extra == "all"
181
- Requires-Dist: pydantic; extra == "all"
182
- Requires-Dist: google-auth-httplib2; extra == "all"
183
- Requires-Dist: docker; extra == "all"
184
- Requires-Dist: httpx; extra == "all"
185
- Requires-Dist: pytest-cov; extra == "all"
186
- Requires-Dist: httpx_retries; extra == "all"
185
+ Requires-Dist: appdirs; extra == "all"
186
+ Requires-Dist: tokenizers; extra == "all"
187
187
  Dynamic: author
188
188
  Dynamic: author-email
189
189
  Dynamic: description
@@ -1,175 +0,0 @@
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 Rewrite:
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[Rewrite]
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.fullmatch(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
- if len(history) == 1:
161
- history_str = 'No rewrites performed.'
162
- else:
163
- history_str = get_history_str()
164
- logger.debug(f'Finished rewriting: {history_str}')
165
-
166
- return previous
167
-
168
- @classmethod
169
- def from_data(cls, data):
170
- rules = [Rewrite(*pair) for pair in data.items()]
171
- self = cls(rules=rules)
172
- return self
173
-
174
-
175
-
@@ -1 +0,0 @@
1
- 1.2.6
File without changes
File without changes
@@ -15,60 +15,60 @@ pydantic-ai[logfire,openai]
15
15
  ollama
16
16
 
17
17
  [all]
18
- google-api-python-client
18
+ distributed
19
+ diskcache
20
+ sre_yield
21
+ httpx_retries
22
+ filetype
23
+ logfire[httpx]
24
+ pymupdf4llm
19
25
  Unidecode
20
26
  uvicorn[standard]
21
- huggingface_hub
22
- logfire[fastapi]
23
- pymupdf4llm
27
+ faker
24
28
  semver
25
- pymupdf
26
- bokeh
29
+ pyyaml
30
+ regex
27
31
  contexttimer
28
- diskcache
29
32
  peft
30
- dask[bag]
31
- pydevd-pycharm
32
- transformers[sentencepiece]
33
- regex
34
- json_repair
35
- html2text
36
- faker
33
+ flet-webview
34
+ pydantic-settings
35
+ deepmerge
36
+ httpx
37
+ pydantic
38
+ pymupdf
39
+ google-auth
40
+ google-api-python-client
41
+ logfire
37
42
  openpyxl
38
- appdirs
39
- logfire[httpx]
40
- openai
43
+ html2text
44
+ pytest-cov
45
+ docker
46
+ google-auth-oauthlib
47
+ json_repair
48
+ ollama
41
49
  sentence_transformers
42
- pydantic-ai[logfire,openai]
50
+ pandas
51
+ setuptools
52
+ logfire[fastapi]
53
+ tabulate
54
+ pydevd-pycharm
55
+ huggingface_hub
56
+ flet-video
57
+ dask[bag]
58
+ google-auth-httplib2
59
+ bokeh
43
60
  fastapi
44
- torchvision
45
- ollama
46
- tokenizers
47
61
  dnspython[doh]
48
- flet-video
49
- pandas
62
+ transformers[sentencepiece]
50
63
  yamlscript
51
- filetype
52
- logfire
53
- tabulate
54
- google-auth-oauthlib
64
+ torchaudio
65
+ pydantic-ai[logfire,openai]
66
+ torchvision
55
67
  flet[all]
56
- pydantic-settings
68
+ openai
57
69
  tinynetrc
58
- deepmerge
59
- distributed
60
- flet-webview
61
- setuptools
62
- pyyaml
63
- sre_yield
64
- google-auth
65
- torchaudio
66
- pydantic
67
- google-auth-httplib2
68
- docker
69
- httpx
70
- pytest-cov
71
- httpx_retries
70
+ appdirs
71
+ tokenizers
72
72
 
73
73
  [api]
74
74
  fastapi
File without changes
File without changes
File without changes