fmtr.tools 1.3.15__tar.gz → 1.3.17__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.
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/PKG-INFO +46 -46
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/dns_tools/dm.py +40 -7
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/dns_tools/proxy.py +1 -1
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/dns_tools/server.py +44 -35
- fmtr_tools-1.3.17/fmtr/tools/version +1 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr.tools.egg-info/PKG-INFO +46 -46
- fmtr_tools-1.3.15/fmtr/tools/version +0 -1
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/LICENSE +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/README.md +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/__init__.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/ai_tools/__init__.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/ai_tools/agentic_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/ai_tools/inference_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/api_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/async_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/augmentation_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/caching_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/constants.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/data_modelling_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/dataclass_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/datatype_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/debugging_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/dns_tools/__init__.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/dns_tools/client.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/docker_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/entrypoints/__init__.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/entrypoints/cache_hfh.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/entrypoints/ep_test.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/entrypoints/install_yamlscript.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/entrypoints/remote_debug_test.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/entrypoints/shell_debug.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/environment_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/function_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/google_api_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/hash_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/hfh_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/html_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/http_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/import_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/inspection_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/interface_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/iterator_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/json_fix_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/json_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/logging_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/merging_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/metric_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/name_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/netrc_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/openai_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/packaging_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/parallel_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/path_tools/__init__.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/path_tools/app_path_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/path_tools/path_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/path_tools/type_path_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/pattern_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/pdf_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/platform_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/process_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/profiling_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/random_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/semantic_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/settings_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/setup_tools/__init__.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/setup_tools/setup_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/spaces_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/string_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/tabular_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/tests/__init__.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/tests/conftest.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/tests/helpers.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/tests/test_datatype.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/tests/test_environment.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/tests/test_json.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/tests/test_path.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/tests/test_yaml.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/tokenization_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/unicode_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/version_tools/__init__.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/version_tools/version_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr/tools/yaml_tools.py +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr.tools.egg-info/SOURCES.txt +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr.tools.egg-info/dependency_links.txt +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr.tools.egg-info/entry_points.txt +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr.tools.egg-info/requires.txt +45 -45
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/fmtr.tools.egg-info/top_level.txt +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/pyproject.toml +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/setup.cfg +0 -0
- {fmtr_tools-1.3.15 → fmtr_tools-1.3.17}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fmtr.tools
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.17
|
|
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
|
|
@@ -126,61 +126,61 @@ Requires-Dist: logfire[httpx]; extra == "http"
|
|
|
126
126
|
Provides-Extra: setup
|
|
127
127
|
Requires-Dist: setuptools; extra == "setup"
|
|
128
128
|
Provides-Extra: all
|
|
129
|
-
Requires-Dist: flet-video; extra == "all"
|
|
130
|
-
Requires-Dist: regex; extra == "all"
|
|
131
|
-
Requires-Dist: dnspython[doh]; extra == "all"
|
|
132
|
-
Requires-Dist: distributed; extra == "all"
|
|
133
|
-
Requires-Dist: tinynetrc; extra == "all"
|
|
134
|
-
Requires-Dist: pandas; extra == "all"
|
|
135
|
-
Requires-Dist: contexttimer; extra == "all"
|
|
136
|
-
Requires-Dist: peft; extra == "all"
|
|
137
|
-
Requires-Dist: pydantic; extra == "all"
|
|
138
|
-
Requires-Dist: google-auth; extra == "all"
|
|
139
|
-
Requires-Dist: tokenizers; extra == "all"
|
|
140
|
-
Requires-Dist: diskcache; extra == "all"
|
|
141
|
-
Requires-Dist: cachetools; extra == "all"
|
|
142
|
-
Requires-Dist: pymupdf; extra == "all"
|
|
143
|
-
Requires-Dist: bokeh; extra == "all"
|
|
144
|
-
Requires-Dist: tabulate; extra == "all"
|
|
145
|
-
Requires-Dist: ollama; extra == "all"
|
|
146
129
|
Requires-Dist: Unidecode; extra == "all"
|
|
147
|
-
Requires-Dist:
|
|
148
|
-
Requires-Dist:
|
|
149
|
-
Requires-Dist:
|
|
150
|
-
Requires-Dist:
|
|
151
|
-
Requires-Dist: huggingface_hub; extra == "all"
|
|
152
|
-
Requires-Dist: openai; extra == "all"
|
|
153
|
-
Requires-Dist: sentence_transformers; extra == "all"
|
|
154
|
-
Requires-Dist: openpyxl; extra == "all"
|
|
130
|
+
Requires-Dist: semver; extra == "all"
|
|
131
|
+
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
132
|
+
Requires-Dist: setuptools; extra == "all"
|
|
133
|
+
Requires-Dist: dask[bag]; extra == "all"
|
|
155
134
|
Requires-Dist: httpx_retries; extra == "all"
|
|
156
135
|
Requires-Dist: flet-webview; extra == "all"
|
|
157
|
-
Requires-Dist:
|
|
136
|
+
Requires-Dist: docker; extra == "all"
|
|
137
|
+
Requires-Dist: bokeh; extra == "all"
|
|
138
|
+
Requires-Dist: filetype; extra == "all"
|
|
158
139
|
Requires-Dist: torchvision; extra == "all"
|
|
159
|
-
Requires-Dist: torchaudio; extra == "all"
|
|
160
140
|
Requires-Dist: faker; extra == "all"
|
|
161
|
-
Requires-Dist:
|
|
162
|
-
Requires-Dist: flet[all]; extra == "all"
|
|
163
|
-
Requires-Dist: google-api-python-client; extra == "all"
|
|
164
|
-
Requires-Dist: json_repair; extra == "all"
|
|
165
|
-
Requires-Dist: google-auth-oauthlib; extra == "all"
|
|
141
|
+
Requires-Dist: uvicorn[standard]; extra == "all"
|
|
166
142
|
Requires-Dist: fastapi; extra == "all"
|
|
167
|
-
Requires-Dist:
|
|
168
|
-
Requires-Dist:
|
|
169
|
-
Requires-Dist: semver; extra == "all"
|
|
170
|
-
Requires-Dist: pymupdf4llm; extra == "all"
|
|
171
|
-
Requires-Dist: pydevd-pycharm~=251.25410.159; extra == "all"
|
|
143
|
+
Requires-Dist: regex; extra == "all"
|
|
144
|
+
Requires-Dist: openai; extra == "all"
|
|
172
145
|
Requires-Dist: appdirs; extra == "all"
|
|
173
|
-
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
174
|
-
Requires-Dist: pydantic-settings; extra == "all"
|
|
175
|
-
Requires-Dist: uvicorn[standard]; extra == "all"
|
|
176
146
|
Requires-Dist: httpx; extra == "all"
|
|
177
|
-
Requires-Dist:
|
|
178
|
-
Requires-Dist:
|
|
179
|
-
Requires-Dist:
|
|
180
|
-
Requires-Dist:
|
|
181
|
-
Requires-Dist:
|
|
147
|
+
Requires-Dist: distributed; extra == "all"
|
|
148
|
+
Requires-Dist: google-api-python-client; extra == "all"
|
|
149
|
+
Requires-Dist: peft; extra == "all"
|
|
150
|
+
Requires-Dist: google-auth-oauthlib; extra == "all"
|
|
151
|
+
Requires-Dist: pyyaml; extra == "all"
|
|
152
|
+
Requires-Dist: json_repair; extra == "all"
|
|
153
|
+
Requires-Dist: deepmerge; extra == "all"
|
|
154
|
+
Requires-Dist: cachetools; extra == "all"
|
|
155
|
+
Requires-Dist: flet[all]; extra == "all"
|
|
156
|
+
Requires-Dist: pydevd-pycharm~=251.25410.159; extra == "all"
|
|
182
157
|
Requires-Dist: html2text; extra == "all"
|
|
158
|
+
Requires-Dist: ollama; extra == "all"
|
|
159
|
+
Requires-Dist: pandas; extra == "all"
|
|
160
|
+
Requires-Dist: pymupdf4llm; extra == "all"
|
|
161
|
+
Requires-Dist: pydantic-settings; extra == "all"
|
|
162
|
+
Requires-Dist: flet-video; extra == "all"
|
|
163
|
+
Requires-Dist: sentence_transformers; extra == "all"
|
|
164
|
+
Requires-Dist: pymupdf; extra == "all"
|
|
165
|
+
Requires-Dist: logfire[httpx]; extra == "all"
|
|
166
|
+
Requires-Dist: huggingface_hub; extra == "all"
|
|
167
|
+
Requires-Dist: openpyxl; extra == "all"
|
|
168
|
+
Requires-Dist: contexttimer; extra == "all"
|
|
169
|
+
Requires-Dist: yamlscript; extra == "all"
|
|
170
|
+
Requires-Dist: pydantic; extra == "all"
|
|
171
|
+
Requires-Dist: sre_yield; extra == "all"
|
|
183
172
|
Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
|
|
173
|
+
Requires-Dist: tokenizers; extra == "all"
|
|
174
|
+
Requires-Dist: torchaudio; extra == "all"
|
|
175
|
+
Requires-Dist: logfire[fastapi]; extra == "all"
|
|
176
|
+
Requires-Dist: pytest-cov; extra == "all"
|
|
177
|
+
Requires-Dist: tinynetrc; extra == "all"
|
|
178
|
+
Requires-Dist: tabulate; extra == "all"
|
|
179
|
+
Requires-Dist: diskcache; extra == "all"
|
|
180
|
+
Requires-Dist: transformers[sentencepiece]; extra == "all"
|
|
181
|
+
Requires-Dist: google-auth; extra == "all"
|
|
182
|
+
Requires-Dist: logfire; extra == "all"
|
|
183
|
+
Requires-Dist: dnspython[doh]; extra == "all"
|
|
184
184
|
Dynamic: author
|
|
185
185
|
Dynamic: author-email
|
|
186
186
|
Dynamic: description
|
|
@@ -10,6 +10,19 @@ from typing import Self, Optional, List
|
|
|
10
10
|
|
|
11
11
|
from fmtr.tools.string_tools import join
|
|
12
12
|
|
|
13
|
+
TTL_CODE_DEFAULTS = {
|
|
14
|
+
dnsrcode.NOERROR: 300, # Successful query
|
|
15
|
+
dnsrcode.FORMERR: 60, # Format error
|
|
16
|
+
dnsrcode.SERVFAIL: 10, # Server failure
|
|
17
|
+
dnsrcode.NXDOMAIN: 60 * 60, # Non-existent domain
|
|
18
|
+
dnsrcode.NOTIMP: 60, # Not implemented
|
|
19
|
+
dnsrcode.REFUSED: 60, # Refused
|
|
20
|
+
dnsrcode.YXDOMAIN: 600, # Name exists when it should not
|
|
21
|
+
dnsrcode.YXRRSET: 600, # RR Set exists when it should not
|
|
22
|
+
dnsrcode.NXRRSET: 300, # RR Set that should exist does not
|
|
23
|
+
dnsrcode.NOTAUTH: 60, # Not authorized
|
|
24
|
+
dnsrcode.NOTZONE: 60 # Name not contained in zone
|
|
25
|
+
}
|
|
13
26
|
|
|
14
27
|
@dataclass
|
|
15
28
|
class BaseDNSData:
|
|
@@ -68,6 +81,24 @@ class Response(BaseDNSData):
|
|
|
68
81
|
def rcode_text(self) -> str:
|
|
69
82
|
return dnsrcode.to_text(self.rcode)
|
|
70
83
|
|
|
84
|
+
@property
|
|
85
|
+
def ttl(self) -> int:
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
Get median TTL from answers, falling back to authority, then to error-code defaults.
|
|
89
|
+
|
|
90
|
+
"""
|
|
91
|
+
answers = self.message.answer or self.message.authority
|
|
92
|
+
if answers:
|
|
93
|
+
ttls = [answer.ttl for answer in answers]
|
|
94
|
+
ttl = min(ttls)
|
|
95
|
+
return ttl
|
|
96
|
+
|
|
97
|
+
ttl = TTL_CODE_DEFAULTS.get(self.rcode, dnsrcode.NXDOMAIN)
|
|
98
|
+
return ttl
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
71
102
|
def __str__(self):
|
|
72
103
|
"""
|
|
73
104
|
|
|
@@ -148,6 +179,9 @@ class Exchange:
|
|
|
148
179
|
client_name: Optional[str] = None
|
|
149
180
|
is_complete: bool = False
|
|
150
181
|
|
|
182
|
+
@property
|
|
183
|
+
def addr(self):
|
|
184
|
+
return self.ip, self.port
|
|
151
185
|
|
|
152
186
|
@classmethod
|
|
153
187
|
def from_wire(cls, wire: bytes, **kwargs) -> Self:
|
|
@@ -163,26 +197,25 @@ class Exchange:
|
|
|
163
197
|
def question_last(self) -> RRset:
|
|
164
198
|
"""
|
|
165
199
|
|
|
166
|
-
|
|
200
|
+
Create an RRset surrogate representing the latest/current question. This can be the original question - or a hybrid one if we've injected our own answers into the Exchange.
|
|
167
201
|
|
|
168
202
|
"""
|
|
169
203
|
if self.answers_pre:
|
|
170
204
|
rrset = self.answers_pre[-1]
|
|
171
|
-
|
|
205
|
+
rdtype = self.request.type
|
|
172
206
|
ttl = self.request.question.ttl
|
|
173
207
|
rdclass = self.request.question.rdclass
|
|
174
208
|
name = next(iter(rrset.items.keys())).to_text()
|
|
175
|
-
|
|
176
|
-
rrset_contrived = dns.rrset.from_text(
|
|
209
|
+
rrset_surrogate = dns.rrset.from_text(
|
|
177
210
|
name=name,
|
|
178
211
|
ttl=ttl,
|
|
179
|
-
rdtype=
|
|
212
|
+
rdtype=rdtype,
|
|
180
213
|
rdclass=rdclass,
|
|
181
214
|
)
|
|
182
215
|
|
|
183
|
-
return
|
|
216
|
+
return rrset_surrogate
|
|
184
217
|
else:
|
|
185
|
-
return self.request.question
|
|
218
|
+
return self.request.question
|
|
186
219
|
|
|
187
220
|
@property
|
|
188
221
|
def query_last(self) -> QueryMessage:
|
|
@@ -39,7 +39,7 @@ class Proxy(server.Plain):
|
|
|
39
39
|
"""
|
|
40
40
|
exchange.is_complete = True
|
|
41
41
|
|
|
42
|
-
def resolve(self, exchange: Exchange) -> Exchange:
|
|
42
|
+
async def resolve(self, exchange: Exchange) -> Exchange:
|
|
43
43
|
"""
|
|
44
44
|
|
|
45
45
|
Resolve a request, processing each stage, initial question, upstream response etc.
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import asyncio
|
|
2
|
+
import dns.rcode
|
|
3
|
+
from dataclasses import dataclass, field
|
|
3
4
|
from datetime import timedelta
|
|
4
5
|
from functools import cached_property
|
|
6
|
+
from typing import Optional
|
|
5
7
|
|
|
6
8
|
from fmtr.tools import caching_tools as caching
|
|
7
9
|
from fmtr.tools.dns_tools.dm import Exchange
|
|
@@ -9,59 +11,61 @@ from fmtr.tools.logging_tools import logger
|
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
@dataclass(kw_only=True, eq=False)
|
|
12
|
-
class Plain:
|
|
14
|
+
class Plain(asyncio.DatagramProtocol):
|
|
13
15
|
"""
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
Async base class for a plain DNS server using asyncio DatagramProtocol.
|
|
17
18
|
"""
|
|
18
19
|
|
|
19
20
|
host: str
|
|
20
21
|
port: int
|
|
22
|
+
transport: Optional[asyncio.DatagramTransport] = field(default=None, init=False)
|
|
21
23
|
|
|
22
24
|
@cached_property
|
|
23
|
-
def
|
|
24
|
-
return
|
|
25
|
+
def loop(self):
|
|
26
|
+
return asyncio.get_event_loop()
|
|
27
|
+
|
|
25
28
|
|
|
26
29
|
@cached_property
|
|
27
30
|
def cache(self):
|
|
28
31
|
"""
|
|
29
32
|
|
|
30
33
|
Overridable cache.
|
|
31
|
-
|
|
32
34
|
"""
|
|
33
35
|
cache = caching.TLRU(maxsize=1_024, ttu_static=timedelta(hours=1), desc='DNS Request')
|
|
34
36
|
return cache
|
|
35
37
|
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
+
def connection_made(self, transport: asyncio.DatagramTransport):
|
|
39
|
+
self.transport = transport
|
|
40
|
+
logger.info(f'Listening on {self.host}:{self.port}')
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
def datagram_received(self, data: bytes, addr):
|
|
43
|
+
ip, port = addr
|
|
44
|
+
exchange = Exchange.from_wire(data, ip=ip, port=port)
|
|
45
|
+
asyncio.create_task(self.handle(exchange))
|
|
40
46
|
|
|
47
|
+
async def start(self):
|
|
41
48
|
"""
|
|
42
|
-
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
43
|
-
sock.bind((self.host, self.port))
|
|
44
|
-
logger.info(f'Listening on {self.host}:{self.port}')
|
|
45
|
-
while True:
|
|
46
|
-
data, (ip, port) = sock.recvfrom(512)
|
|
47
|
-
exchange = Exchange.from_wire(data, ip=ip, port=port)
|
|
48
|
-
self.handle(exchange)
|
|
49
|
-
sock.sendto(exchange.response.message.to_wire(), (ip, port))
|
|
50
49
|
|
|
51
|
-
|
|
50
|
+
Start the async UDP server.
|
|
52
51
|
"""
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
logger.info(f'Starting async DNS server on {self.host}:{self.port}...')
|
|
54
|
+
await self.loop.create_datagram_endpoint(
|
|
55
|
+
lambda: self,
|
|
56
|
+
local_addr=(self.host, self.port)
|
|
57
|
+
)
|
|
58
|
+
await asyncio.Future() # Prevent exit by blocking forever
|
|
55
59
|
|
|
60
|
+
async def resolve(self, exchange: Exchange) -> Exchange:
|
|
56
61
|
"""
|
|
57
|
-
raise NotImplemented
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
"""
|
|
61
|
-
|
|
62
|
-
Check cache, patch in in new ID and mark complete
|
|
63
|
+
To be defined in subclasses.
|
|
63
64
|
|
|
64
65
|
"""
|
|
66
|
+
raise NotImplementedError
|
|
67
|
+
|
|
68
|
+
def check_cache(self, exchange: Exchange):
|
|
65
69
|
if exchange.key in self.cache:
|
|
66
70
|
logger.info(f'Request found in cache.')
|
|
67
71
|
exchange.response = self.cache[exchange.key]
|
|
@@ -88,25 +92,30 @@ class Plain:
|
|
|
88
92
|
"""
|
|
89
93
|
request = exchange.request
|
|
90
94
|
response = exchange.response
|
|
91
|
-
|
|
92
95
|
logger.info(
|
|
93
96
|
f'Resolution complete {exchange.client_name=} {request.message.id=} {request.type_text} {request.name_text} {request.question=} {exchange.is_complete=} {response.rcode=} {response.rcode_text=} {response.answer=} {response.blocked_by=}...'
|
|
94
97
|
)
|
|
95
98
|
|
|
99
|
+
def log_dns_errors(self, exchange: Exchange):
|
|
100
|
+
"""
|
|
96
101
|
|
|
102
|
+
Warn about any errors
|
|
97
103
|
|
|
98
|
-
def handle(self, exchange: Exchange):
|
|
99
104
|
"""
|
|
105
|
+
if exchange.response.rcode != dns.rcode.NOERROR:
|
|
106
|
+
logger.warning(f'Error {exchange.response.rcode_text=}')
|
|
100
107
|
|
|
101
|
-
|
|
102
|
-
|
|
108
|
+
async def handle(self, exchange: Exchange):
|
|
103
109
|
"""
|
|
104
110
|
|
|
111
|
+
Warn about any errors
|
|
112
|
+
|
|
113
|
+
"""
|
|
105
114
|
if not exchange.request.is_valid:
|
|
106
115
|
raise ValueError(f'Only one question per request is supported. Got {len(exchange.request.question)} questions.')
|
|
107
116
|
|
|
108
117
|
if not exchange.is_internal:
|
|
109
|
-
self.handle(exchange.reverse)
|
|
118
|
+
await self.handle(exchange.reverse)
|
|
110
119
|
client_name = exchange.reverse.question_last.name.to_text()
|
|
111
120
|
if not exchange.reverse.response.answer:
|
|
112
121
|
logger.warning(f'Client name could not be resolved {client_name=}.')
|
|
@@ -117,10 +126,10 @@ class Plain:
|
|
|
117
126
|
self.check_cache(exchange)
|
|
118
127
|
|
|
119
128
|
if not exchange.is_complete:
|
|
120
|
-
exchange = self.resolve(exchange)
|
|
121
|
-
exchange.
|
|
129
|
+
exchange = await self.resolve(exchange)
|
|
130
|
+
self.cache[exchange.key] = exchange.response
|
|
122
131
|
|
|
123
|
-
self.
|
|
132
|
+
self.log_dns_errors(exchange)
|
|
124
133
|
self.log_response(exchange)
|
|
125
134
|
|
|
126
|
-
|
|
135
|
+
self.transport.sendto(exchange.response.message.to_wire(), exchange.addr)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.3.17
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fmtr.tools
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.17
|
|
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
|
|
@@ -126,61 +126,61 @@ Requires-Dist: logfire[httpx]; extra == "http"
|
|
|
126
126
|
Provides-Extra: setup
|
|
127
127
|
Requires-Dist: setuptools; extra == "setup"
|
|
128
128
|
Provides-Extra: all
|
|
129
|
-
Requires-Dist: flet-video; extra == "all"
|
|
130
|
-
Requires-Dist: regex; extra == "all"
|
|
131
|
-
Requires-Dist: dnspython[doh]; extra == "all"
|
|
132
|
-
Requires-Dist: distributed; extra == "all"
|
|
133
|
-
Requires-Dist: tinynetrc; extra == "all"
|
|
134
|
-
Requires-Dist: pandas; extra == "all"
|
|
135
|
-
Requires-Dist: contexttimer; extra == "all"
|
|
136
|
-
Requires-Dist: peft; extra == "all"
|
|
137
|
-
Requires-Dist: pydantic; extra == "all"
|
|
138
|
-
Requires-Dist: google-auth; extra == "all"
|
|
139
|
-
Requires-Dist: tokenizers; extra == "all"
|
|
140
|
-
Requires-Dist: diskcache; extra == "all"
|
|
141
|
-
Requires-Dist: cachetools; extra == "all"
|
|
142
|
-
Requires-Dist: pymupdf; extra == "all"
|
|
143
|
-
Requires-Dist: bokeh; extra == "all"
|
|
144
|
-
Requires-Dist: tabulate; extra == "all"
|
|
145
|
-
Requires-Dist: ollama; extra == "all"
|
|
146
129
|
Requires-Dist: Unidecode; extra == "all"
|
|
147
|
-
Requires-Dist:
|
|
148
|
-
Requires-Dist:
|
|
149
|
-
Requires-Dist:
|
|
150
|
-
Requires-Dist:
|
|
151
|
-
Requires-Dist: huggingface_hub; extra == "all"
|
|
152
|
-
Requires-Dist: openai; extra == "all"
|
|
153
|
-
Requires-Dist: sentence_transformers; extra == "all"
|
|
154
|
-
Requires-Dist: openpyxl; extra == "all"
|
|
130
|
+
Requires-Dist: semver; extra == "all"
|
|
131
|
+
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
132
|
+
Requires-Dist: setuptools; extra == "all"
|
|
133
|
+
Requires-Dist: dask[bag]; extra == "all"
|
|
155
134
|
Requires-Dist: httpx_retries; extra == "all"
|
|
156
135
|
Requires-Dist: flet-webview; extra == "all"
|
|
157
|
-
Requires-Dist:
|
|
136
|
+
Requires-Dist: docker; extra == "all"
|
|
137
|
+
Requires-Dist: bokeh; extra == "all"
|
|
138
|
+
Requires-Dist: filetype; extra == "all"
|
|
158
139
|
Requires-Dist: torchvision; extra == "all"
|
|
159
|
-
Requires-Dist: torchaudio; extra == "all"
|
|
160
140
|
Requires-Dist: faker; extra == "all"
|
|
161
|
-
Requires-Dist:
|
|
162
|
-
Requires-Dist: flet[all]; extra == "all"
|
|
163
|
-
Requires-Dist: google-api-python-client; extra == "all"
|
|
164
|
-
Requires-Dist: json_repair; extra == "all"
|
|
165
|
-
Requires-Dist: google-auth-oauthlib; extra == "all"
|
|
141
|
+
Requires-Dist: uvicorn[standard]; extra == "all"
|
|
166
142
|
Requires-Dist: fastapi; extra == "all"
|
|
167
|
-
Requires-Dist:
|
|
168
|
-
Requires-Dist:
|
|
169
|
-
Requires-Dist: semver; extra == "all"
|
|
170
|
-
Requires-Dist: pymupdf4llm; extra == "all"
|
|
171
|
-
Requires-Dist: pydevd-pycharm~=251.25410.159; extra == "all"
|
|
143
|
+
Requires-Dist: regex; extra == "all"
|
|
144
|
+
Requires-Dist: openai; extra == "all"
|
|
172
145
|
Requires-Dist: appdirs; extra == "all"
|
|
173
|
-
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
174
|
-
Requires-Dist: pydantic-settings; extra == "all"
|
|
175
|
-
Requires-Dist: uvicorn[standard]; extra == "all"
|
|
176
146
|
Requires-Dist: httpx; extra == "all"
|
|
177
|
-
Requires-Dist:
|
|
178
|
-
Requires-Dist:
|
|
179
|
-
Requires-Dist:
|
|
180
|
-
Requires-Dist:
|
|
181
|
-
Requires-Dist:
|
|
147
|
+
Requires-Dist: distributed; extra == "all"
|
|
148
|
+
Requires-Dist: google-api-python-client; extra == "all"
|
|
149
|
+
Requires-Dist: peft; extra == "all"
|
|
150
|
+
Requires-Dist: google-auth-oauthlib; extra == "all"
|
|
151
|
+
Requires-Dist: pyyaml; extra == "all"
|
|
152
|
+
Requires-Dist: json_repair; extra == "all"
|
|
153
|
+
Requires-Dist: deepmerge; extra == "all"
|
|
154
|
+
Requires-Dist: cachetools; extra == "all"
|
|
155
|
+
Requires-Dist: flet[all]; extra == "all"
|
|
156
|
+
Requires-Dist: pydevd-pycharm~=251.25410.159; extra == "all"
|
|
182
157
|
Requires-Dist: html2text; extra == "all"
|
|
158
|
+
Requires-Dist: ollama; extra == "all"
|
|
159
|
+
Requires-Dist: pandas; extra == "all"
|
|
160
|
+
Requires-Dist: pymupdf4llm; extra == "all"
|
|
161
|
+
Requires-Dist: pydantic-settings; extra == "all"
|
|
162
|
+
Requires-Dist: flet-video; extra == "all"
|
|
163
|
+
Requires-Dist: sentence_transformers; extra == "all"
|
|
164
|
+
Requires-Dist: pymupdf; extra == "all"
|
|
165
|
+
Requires-Dist: logfire[httpx]; extra == "all"
|
|
166
|
+
Requires-Dist: huggingface_hub; extra == "all"
|
|
167
|
+
Requires-Dist: openpyxl; extra == "all"
|
|
168
|
+
Requires-Dist: contexttimer; extra == "all"
|
|
169
|
+
Requires-Dist: yamlscript; extra == "all"
|
|
170
|
+
Requires-Dist: pydantic; extra == "all"
|
|
171
|
+
Requires-Dist: sre_yield; extra == "all"
|
|
183
172
|
Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
|
|
173
|
+
Requires-Dist: tokenizers; extra == "all"
|
|
174
|
+
Requires-Dist: torchaudio; extra == "all"
|
|
175
|
+
Requires-Dist: logfire[fastapi]; extra == "all"
|
|
176
|
+
Requires-Dist: pytest-cov; extra == "all"
|
|
177
|
+
Requires-Dist: tinynetrc; extra == "all"
|
|
178
|
+
Requires-Dist: tabulate; extra == "all"
|
|
179
|
+
Requires-Dist: diskcache; extra == "all"
|
|
180
|
+
Requires-Dist: transformers[sentencepiece]; extra == "all"
|
|
181
|
+
Requires-Dist: google-auth; extra == "all"
|
|
182
|
+
Requires-Dist: logfire; extra == "all"
|
|
183
|
+
Requires-Dist: dnspython[doh]; extra == "all"
|
|
184
184
|
Dynamic: author
|
|
185
185
|
Dynamic: author-email
|
|
186
186
|
Dynamic: description
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1.3.15
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -14,61 +14,61 @@ pydantic-ai[logfire,openai]
|
|
|
14
14
|
ollama
|
|
15
15
|
|
|
16
16
|
[all]
|
|
17
|
-
flet-video
|
|
18
|
-
regex
|
|
19
|
-
dnspython[doh]
|
|
20
|
-
distributed
|
|
21
|
-
tinynetrc
|
|
22
|
-
pandas
|
|
23
|
-
contexttimer
|
|
24
|
-
peft
|
|
25
|
-
pydantic
|
|
26
|
-
google-auth
|
|
27
|
-
tokenizers
|
|
28
|
-
diskcache
|
|
29
|
-
cachetools
|
|
30
|
-
pymupdf
|
|
31
|
-
bokeh
|
|
32
|
-
tabulate
|
|
33
|
-
ollama
|
|
34
17
|
Unidecode
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
huggingface_hub
|
|
40
|
-
openai
|
|
41
|
-
sentence_transformers
|
|
42
|
-
openpyxl
|
|
18
|
+
semver
|
|
19
|
+
google-auth-httplib2
|
|
20
|
+
setuptools
|
|
21
|
+
dask[bag]
|
|
43
22
|
httpx_retries
|
|
44
23
|
flet-webview
|
|
45
|
-
|
|
24
|
+
docker
|
|
25
|
+
bokeh
|
|
26
|
+
filetype
|
|
46
27
|
torchvision
|
|
47
|
-
torchaudio
|
|
48
28
|
faker
|
|
49
|
-
|
|
50
|
-
flet[all]
|
|
51
|
-
google-api-python-client
|
|
52
|
-
json_repair
|
|
53
|
-
google-auth-oauthlib
|
|
29
|
+
uvicorn[standard]
|
|
54
30
|
fastapi
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
semver
|
|
58
|
-
pymupdf4llm
|
|
59
|
-
pydevd-pycharm~=251.25410.159
|
|
31
|
+
regex
|
|
32
|
+
openai
|
|
60
33
|
appdirs
|
|
61
|
-
google-auth-httplib2
|
|
62
|
-
pydantic-settings
|
|
63
|
-
uvicorn[standard]
|
|
64
34
|
httpx
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
35
|
+
distributed
|
|
36
|
+
google-api-python-client
|
|
37
|
+
peft
|
|
38
|
+
google-auth-oauthlib
|
|
39
|
+
pyyaml
|
|
40
|
+
json_repair
|
|
41
|
+
deepmerge
|
|
42
|
+
cachetools
|
|
43
|
+
flet[all]
|
|
44
|
+
pydevd-pycharm~=251.25410.159
|
|
70
45
|
html2text
|
|
46
|
+
ollama
|
|
47
|
+
pandas
|
|
48
|
+
pymupdf4llm
|
|
49
|
+
pydantic-settings
|
|
50
|
+
flet-video
|
|
51
|
+
sentence_transformers
|
|
52
|
+
pymupdf
|
|
53
|
+
logfire[httpx]
|
|
54
|
+
huggingface_hub
|
|
55
|
+
openpyxl
|
|
56
|
+
contexttimer
|
|
57
|
+
yamlscript
|
|
58
|
+
pydantic
|
|
59
|
+
sre_yield
|
|
71
60
|
pydantic-ai[logfire,openai]
|
|
61
|
+
tokenizers
|
|
62
|
+
torchaudio
|
|
63
|
+
logfire[fastapi]
|
|
64
|
+
pytest-cov
|
|
65
|
+
tinynetrc
|
|
66
|
+
tabulate
|
|
67
|
+
diskcache
|
|
68
|
+
transformers[sentencepiece]
|
|
69
|
+
google-auth
|
|
70
|
+
logfire
|
|
71
|
+
dnspython[doh]
|
|
72
72
|
|
|
73
73
|
[api]
|
|
74
74
|
fastapi
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|