fmtr.tools 1.2.2__tar.gz → 1.2.4__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.2.2 → fmtr_tools-1.2.4}/PKG-INFO +44 -39
- fmtr_tools-1.2.4/fmtr/tools/dns_tools.py +221 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/pattern_tools.py +6 -3
- fmtr_tools-1.2.4/fmtr/tools/version +1 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr.tools.egg-info/PKG-INFO +44 -39
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr.tools.egg-info/requires.txt +43 -38
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/setup.py +1 -1
- fmtr_tools-1.2.2/fmtr/tools/dns_tools.py +0 -57
- fmtr_tools-1.2.2/fmtr/tools/version +0 -1
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/LICENSE +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/README.md +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/__init__.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/ai_tools/__init__.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/ai_tools/agentic_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/ai_tools/inference_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/api_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/async_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/augmentation_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/caching_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/constants.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/data_modelling_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/dataclass_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/datatype_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/debugging_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/docker_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/entrypoints/__init__.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/entrypoints/cache_hfh.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/entrypoints/ep_test.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/entrypoints/remote_debug_test.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/entrypoints/shell_debug.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/environment_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/function_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/google_api_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/hash_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/hfh_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/html_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/http_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/import_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/inspection_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/interface_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/iterator_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/json_fix_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/json_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/logging_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/merging_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/metric_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/name_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/netrc_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/openai_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/packaging_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/parallel_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/path_tools/__init__.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/path_tools/app_path_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/path_tools/path_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/path_tools/type_path_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/pdf_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/platform_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/process_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/profiling_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/random_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/semantic_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/settings_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/setup_tools/__init__.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/setup_tools/setup_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/spaces_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/string_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tabular_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/__init__.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/conftest.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/helpers.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/test_datatype.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/test_environment.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/test_json.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/test_path.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/test_yaml.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tokenization_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/unicode_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/version_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/yaml_tools.py +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr.tools.egg-info/SOURCES.txt +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr.tools.egg-info/dependency_links.txt +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr.tools.egg-info/entry_points.txt +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr.tools.egg-info/top_level.txt +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/pyproject.toml +0 -0
- {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fmtr.tools
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.4
|
|
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
|
|
@@ -112,6 +112,11 @@ Provides-Extra: path-type
|
|
|
112
112
|
Requires-Dist: filetype; extra == "path-type"
|
|
113
113
|
Provides-Extra: dns
|
|
114
114
|
Requires-Dist: dnspython[doh]; extra == "dns"
|
|
115
|
+
Requires-Dist: httpx; extra == "dns"
|
|
116
|
+
Requires-Dist: httpx_retries; extra == "dns"
|
|
117
|
+
Requires-Dist: logfire; extra == "dns"
|
|
118
|
+
Requires-Dist: semver; extra == "dns"
|
|
119
|
+
Requires-Dist: logfire[httpx]; extra == "dns"
|
|
115
120
|
Provides-Extra: patterns
|
|
116
121
|
Requires-Dist: regex; extra == "patterns"
|
|
117
122
|
Provides-Extra: http
|
|
@@ -123,60 +128,60 @@ Requires-Dist: logfire[httpx]; extra == "http"
|
|
|
123
128
|
Provides-Extra: setup
|
|
124
129
|
Requires-Dist: setuptools; extra == "setup"
|
|
125
130
|
Provides-Extra: all
|
|
126
|
-
Requires-Dist: tinynetrc; extra == "all"
|
|
127
|
-
Requires-Dist: openpyxl; extra == "all"
|
|
128
131
|
Requires-Dist: httpx; extra == "all"
|
|
129
|
-
Requires-Dist:
|
|
130
|
-
Requires-Dist:
|
|
131
|
-
Requires-Dist:
|
|
132
|
+
Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
|
|
133
|
+
Requires-Dist: Unidecode; extra == "all"
|
|
134
|
+
Requires-Dist: huggingface_hub; extra == "all"
|
|
135
|
+
Requires-Dist: faker; extra == "all"
|
|
136
|
+
Requires-Dist: bokeh; extra == "all"
|
|
137
|
+
Requires-Dist: docker; extra == "all"
|
|
132
138
|
Requires-Dist: deepmerge; extra == "all"
|
|
139
|
+
Requires-Dist: uvicorn[standard]; extra == "all"
|
|
140
|
+
Requires-Dist: regex; extra == "all"
|
|
141
|
+
Requires-Dist: tinynetrc; extra == "all"
|
|
133
142
|
Requires-Dist: pydantic-settings; extra == "all"
|
|
134
|
-
Requires-Dist:
|
|
135
|
-
Requires-Dist:
|
|
136
|
-
Requires-Dist:
|
|
143
|
+
Requires-Dist: contexttimer; extra == "all"
|
|
144
|
+
Requires-Dist: flet-webview; extra == "all"
|
|
145
|
+
Requires-Dist: pandas; extra == "all"
|
|
137
146
|
Requires-Dist: openai; extra == "all"
|
|
138
|
-
Requires-Dist:
|
|
139
|
-
Requires-Dist: pydevd-pycharm; extra == "all"
|
|
140
|
-
Requires-Dist: sre_yield; extra == "all"
|
|
147
|
+
Requires-Dist: pytest-cov; extra == "all"
|
|
141
148
|
Requires-Dist: distributed; extra == "all"
|
|
142
|
-
Requires-Dist: contexttimer; extra == "all"
|
|
143
149
|
Requires-Dist: html2text; extra == "all"
|
|
144
|
-
Requires-Dist:
|
|
150
|
+
Requires-Dist: diskcache; extra == "all"
|
|
151
|
+
Requires-Dist: json_repair; extra == "all"
|
|
145
152
|
Requires-Dist: flet-video; extra == "all"
|
|
146
|
-
Requires-Dist:
|
|
147
|
-
Requires-Dist:
|
|
153
|
+
Requires-Dist: pyyaml; extra == "all"
|
|
154
|
+
Requires-Dist: openpyxl; extra == "all"
|
|
155
|
+
Requires-Dist: filetype; extra == "all"
|
|
156
|
+
Requires-Dist: google-api-python-client; extra == "all"
|
|
157
|
+
Requires-Dist: google-auth; extra == "all"
|
|
148
158
|
Requires-Dist: pymupdf4llm; extra == "all"
|
|
149
|
-
Requires-Dist:
|
|
159
|
+
Requires-Dist: dask[bag]; extra == "all"
|
|
160
|
+
Requires-Dist: httpx_retries; extra == "all"
|
|
161
|
+
Requires-Dist: torchvision; extra == "all"
|
|
162
|
+
Requires-Dist: tabulate; extra == "all"
|
|
163
|
+
Requires-Dist: logfire[httpx]; extra == "all"
|
|
150
164
|
Requires-Dist: tokenizers; extra == "all"
|
|
151
|
-
Requires-Dist:
|
|
165
|
+
Requires-Dist: logfire; extra == "all"
|
|
166
|
+
Requires-Dist: pymupdf; extra == "all"
|
|
152
167
|
Requires-Dist: dnspython[doh]; extra == "all"
|
|
168
|
+
Requires-Dist: yamlscript; extra == "all"
|
|
153
169
|
Requires-Dist: sentence_transformers; extra == "all"
|
|
154
|
-
Requires-Dist:
|
|
155
|
-
Requires-Dist: pymupdf; extra == "all"
|
|
170
|
+
Requires-Dist: fastapi; extra == "all"
|
|
156
171
|
Requires-Dist: google-auth-oauthlib; extra == "all"
|
|
172
|
+
Requires-Dist: pydevd-pycharm; extra == "all"
|
|
157
173
|
Requires-Dist: pydantic; extra == "all"
|
|
158
|
-
Requires-Dist: docker; extra == "all"
|
|
159
|
-
Requires-Dist: json_repair; extra == "all"
|
|
160
|
-
Requires-Dist: tabulate; extra == "all"
|
|
161
|
-
Requires-Dist: appdirs; extra == "all"
|
|
162
|
-
Requires-Dist: faker; extra == "all"
|
|
163
|
-
Requires-Dist: torchaudio; extra == "all"
|
|
164
|
-
Requires-Dist: pandas; extra == "all"
|
|
165
|
-
Requires-Dist: transformers[sentencepiece]; extra == "all"
|
|
166
|
-
Requires-Dist: pyyaml; extra == "all"
|
|
167
|
-
Requires-Dist: semver; extra == "all"
|
|
168
174
|
Requires-Dist: flet[all]; extra == "all"
|
|
169
|
-
Requires-Dist:
|
|
170
|
-
Requires-Dist:
|
|
171
|
-
Requires-Dist:
|
|
172
|
-
Requires-Dist:
|
|
173
|
-
Requires-Dist: torchvision; extra == "all"
|
|
175
|
+
Requires-Dist: semver; extra == "all"
|
|
176
|
+
Requires-Dist: logfire[fastapi]; extra == "all"
|
|
177
|
+
Requires-Dist: torchaudio; extra == "all"
|
|
178
|
+
Requires-Dist: ollama; extra == "all"
|
|
174
179
|
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
175
|
-
Requires-Dist:
|
|
176
|
-
Requires-Dist:
|
|
177
|
-
Requires-Dist:
|
|
180
|
+
Requires-Dist: appdirs; extra == "all"
|
|
181
|
+
Requires-Dist: transformers[sentencepiece]; extra == "all"
|
|
182
|
+
Requires-Dist: setuptools; extra == "all"
|
|
183
|
+
Requires-Dist: sre_yield; extra == "all"
|
|
178
184
|
Requires-Dist: peft; extra == "all"
|
|
179
|
-
Requires-Dist: logfire; extra == "all"
|
|
180
185
|
Dynamic: author
|
|
181
186
|
Dynamic: author-email
|
|
182
187
|
Dynamic: description
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import dns
|
|
2
|
+
import httpx
|
|
3
|
+
import socket
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from dns.message import Message
|
|
6
|
+
from functools import cached_property
|
|
7
|
+
from httpx_retries import Retry, RetryTransport
|
|
8
|
+
from typing import Optional, Self
|
|
9
|
+
|
|
10
|
+
from fmtr.tools import Client, logger
|
|
11
|
+
|
|
12
|
+
RETRY_STRATEGY = Retry(
|
|
13
|
+
total=2, # initial + 1 retry
|
|
14
|
+
allowed_methods={"GET", "POST"},
|
|
15
|
+
status_forcelist={502, 503, 504},
|
|
16
|
+
retry_on_exceptions=None, # defaults to httpx.TransportError etc.
|
|
17
|
+
backoff_factor=0.25, # short backoff (e.g. 0.25s, 0.5s)
|
|
18
|
+
max_backoff_wait=0.75, # max total delay before giving up
|
|
19
|
+
backoff_jitter=0.1, # small jitter to avoid retry bursts
|
|
20
|
+
respect_retry_after_header=False, # DoH resolvers probably won't set this
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class HTTPClientDoH(Client):
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
Base HTTP client for DoH-appropriate retry strategy.
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
TRANSPORT = RetryTransport(retry=RETRY_STRATEGY)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class BaseDNSData:
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
DNS response object.
|
|
38
|
+
|
|
39
|
+
"""
|
|
40
|
+
wire: bytes
|
|
41
|
+
|
|
42
|
+
@cached_property
|
|
43
|
+
def message(self) -> Message:
|
|
44
|
+
return dns.message.from_wire(self.wire)
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def from_message(cls, message: Message) -> Self:
|
|
48
|
+
return cls(message.to_wire())
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class Response(BaseDNSData):
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
DNS response object.
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
http: Optional[httpx.Response] = None
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def from_http(cls, response: httpx.Response) -> Self:
|
|
63
|
+
self = cls(response.content, http=response)
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class Request(BaseDNSData):
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
DNS request object.
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
wire: bytes
|
|
75
|
+
|
|
76
|
+
@cached_property
|
|
77
|
+
def question(self):
|
|
78
|
+
return self.message.question[0]
|
|
79
|
+
|
|
80
|
+
@cached_property
|
|
81
|
+
def is_valid(self):
|
|
82
|
+
return len(self.message.question) != 0
|
|
83
|
+
|
|
84
|
+
@cached_property
|
|
85
|
+
def type(self):
|
|
86
|
+
return self.question.rdtype
|
|
87
|
+
|
|
88
|
+
@cached_property
|
|
89
|
+
def type_text(self):
|
|
90
|
+
return dns.rdatatype.to_text(self.type)
|
|
91
|
+
|
|
92
|
+
@cached_property
|
|
93
|
+
def name(self):
|
|
94
|
+
return self.question.name
|
|
95
|
+
|
|
96
|
+
@cached_property
|
|
97
|
+
def name_text(self):
|
|
98
|
+
return self.name.to_text()
|
|
99
|
+
|
|
100
|
+
@cached_property
|
|
101
|
+
def blackhole(self) -> Response:
|
|
102
|
+
blackhole = dns.message.make_response(self.message)
|
|
103
|
+
blackhole.flags |= dns.flags.RA
|
|
104
|
+
blackhole.set_rcode(dns.rcode.NXDOMAIN)
|
|
105
|
+
response = Response.from_message(blackhole)
|
|
106
|
+
return response
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@dataclass
|
|
110
|
+
class Exchange:
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
Entire DNS exchange for a DNS Proxy: request -> upstream response -> response
|
|
114
|
+
|
|
115
|
+
"""
|
|
116
|
+
ip: str
|
|
117
|
+
port: int
|
|
118
|
+
|
|
119
|
+
request: Request
|
|
120
|
+
response: Optional[Response] = None
|
|
121
|
+
response_upstream: Optional[Response] = None
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def from_wire(cls, wire: bytes, ip: str, port: int) -> Self:
|
|
125
|
+
request = Request(wire)
|
|
126
|
+
return cls(request=request, ip=ip, port=port)
|
|
127
|
+
|
|
128
|
+
@cached_property
|
|
129
|
+
def client(self):
|
|
130
|
+
return f'{self.ip}:{self.port}'
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class BasePlain:
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
Base for starting a plain DNS server
|
|
137
|
+
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
def __init__(self, host, port):
|
|
141
|
+
self.host = host
|
|
142
|
+
self.port = port
|
|
143
|
+
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
144
|
+
|
|
145
|
+
def resolve(self, exchange: Exchange):
|
|
146
|
+
raise NotImplemented
|
|
147
|
+
|
|
148
|
+
def start(self):
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
Listen and resolve via overridden resolve method.
|
|
152
|
+
|
|
153
|
+
"""
|
|
154
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
155
|
+
sock.bind((self.host, self.port))
|
|
156
|
+
print(f"Listening on {self.host}:{self.port}")
|
|
157
|
+
while True:
|
|
158
|
+
data, (ip, port) = sock.recvfrom(512)
|
|
159
|
+
exchange = Exchange.from_wire(data, ip=ip, port=port)
|
|
160
|
+
self.resolve(exchange)
|
|
161
|
+
sock.sendto(exchange.response.wire, (ip, port))
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class BaseDoHProxy(BasePlain):
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
Base for a DNS Proxy server
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
URL = None
|
|
172
|
+
HEADERS = {"Content-Type": "application/dns-message"}
|
|
173
|
+
client = HTTPClientDoH()
|
|
174
|
+
|
|
175
|
+
def process_question(self, exchange: Exchange):
|
|
176
|
+
return
|
|
177
|
+
|
|
178
|
+
def process_upstream(self, exchange: Exchange):
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
def from_upstream(self, exchange: Exchange) -> Exchange:
|
|
182
|
+
|
|
183
|
+
request = exchange.request
|
|
184
|
+
response_doh = self.client.post(self.URL, headers=self.HEADERS, content=request.wire)
|
|
185
|
+
response_doh.raise_for_status()
|
|
186
|
+
response = Response.from_http(response_doh)
|
|
187
|
+
exchange.response_upstream = response
|
|
188
|
+
|
|
189
|
+
return exchange
|
|
190
|
+
|
|
191
|
+
def resolve(self, exchange: Exchange):
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
Resolve a request, processing each stage, initial question, upstream response etc.
|
|
195
|
+
Subclasses can override the relevant processing methods to implement custom behaviour.
|
|
196
|
+
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
request = exchange.request
|
|
200
|
+
|
|
201
|
+
with logger.span(f'Handling request for {request.name_text} from {exchange.client}...'):
|
|
202
|
+
|
|
203
|
+
if not request.is_valid:
|
|
204
|
+
raise ValueError(f'Only one question per request is supported. Got {len(request.question)} questions.')
|
|
205
|
+
|
|
206
|
+
with logger.span(f'Processing question...'):
|
|
207
|
+
self.process_question(exchange)
|
|
208
|
+
if exchange.response:
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
with logger.span(f'Making upstream request for {request.name_text}...'):
|
|
212
|
+
self.from_upstream(exchange)
|
|
213
|
+
|
|
214
|
+
with logger.span(f'Processing upstream response...'):
|
|
215
|
+
self.process_upstream(exchange)
|
|
216
|
+
|
|
217
|
+
if exchange.response:
|
|
218
|
+
return
|
|
219
|
+
|
|
220
|
+
exchange.response = exchange.response_upstream
|
|
221
|
+
return
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
import regex as re
|
|
1
2
|
from dataclasses import dataclass
|
|
2
3
|
from functools import cached_property
|
|
3
4
|
from typing import List
|
|
4
5
|
|
|
5
|
-
import regex as re
|
|
6
|
-
|
|
7
6
|
from fmtr.tools.logging_tools import logger
|
|
8
7
|
|
|
9
8
|
|
|
@@ -158,7 +157,11 @@ class Rewriter:
|
|
|
158
157
|
|
|
159
158
|
previous = new
|
|
160
159
|
|
|
161
|
-
|
|
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}')
|
|
162
165
|
|
|
163
166
|
return previous
|
|
164
167
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.2.4
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fmtr.tools
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.4
|
|
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
|
|
@@ -112,6 +112,11 @@ Provides-Extra: path-type
|
|
|
112
112
|
Requires-Dist: filetype; extra == "path-type"
|
|
113
113
|
Provides-Extra: dns
|
|
114
114
|
Requires-Dist: dnspython[doh]; extra == "dns"
|
|
115
|
+
Requires-Dist: httpx; extra == "dns"
|
|
116
|
+
Requires-Dist: httpx_retries; extra == "dns"
|
|
117
|
+
Requires-Dist: logfire; extra == "dns"
|
|
118
|
+
Requires-Dist: semver; extra == "dns"
|
|
119
|
+
Requires-Dist: logfire[httpx]; extra == "dns"
|
|
115
120
|
Provides-Extra: patterns
|
|
116
121
|
Requires-Dist: regex; extra == "patterns"
|
|
117
122
|
Provides-Extra: http
|
|
@@ -123,60 +128,60 @@ Requires-Dist: logfire[httpx]; extra == "http"
|
|
|
123
128
|
Provides-Extra: setup
|
|
124
129
|
Requires-Dist: setuptools; extra == "setup"
|
|
125
130
|
Provides-Extra: all
|
|
126
|
-
Requires-Dist: tinynetrc; extra == "all"
|
|
127
|
-
Requires-Dist: openpyxl; extra == "all"
|
|
128
131
|
Requires-Dist: httpx; extra == "all"
|
|
129
|
-
Requires-Dist:
|
|
130
|
-
Requires-Dist:
|
|
131
|
-
Requires-Dist:
|
|
132
|
+
Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
|
|
133
|
+
Requires-Dist: Unidecode; extra == "all"
|
|
134
|
+
Requires-Dist: huggingface_hub; extra == "all"
|
|
135
|
+
Requires-Dist: faker; extra == "all"
|
|
136
|
+
Requires-Dist: bokeh; extra == "all"
|
|
137
|
+
Requires-Dist: docker; extra == "all"
|
|
132
138
|
Requires-Dist: deepmerge; extra == "all"
|
|
139
|
+
Requires-Dist: uvicorn[standard]; extra == "all"
|
|
140
|
+
Requires-Dist: regex; extra == "all"
|
|
141
|
+
Requires-Dist: tinynetrc; extra == "all"
|
|
133
142
|
Requires-Dist: pydantic-settings; extra == "all"
|
|
134
|
-
Requires-Dist:
|
|
135
|
-
Requires-Dist:
|
|
136
|
-
Requires-Dist:
|
|
143
|
+
Requires-Dist: contexttimer; extra == "all"
|
|
144
|
+
Requires-Dist: flet-webview; extra == "all"
|
|
145
|
+
Requires-Dist: pandas; extra == "all"
|
|
137
146
|
Requires-Dist: openai; extra == "all"
|
|
138
|
-
Requires-Dist:
|
|
139
|
-
Requires-Dist: pydevd-pycharm; extra == "all"
|
|
140
|
-
Requires-Dist: sre_yield; extra == "all"
|
|
147
|
+
Requires-Dist: pytest-cov; extra == "all"
|
|
141
148
|
Requires-Dist: distributed; extra == "all"
|
|
142
|
-
Requires-Dist: contexttimer; extra == "all"
|
|
143
149
|
Requires-Dist: html2text; extra == "all"
|
|
144
|
-
Requires-Dist:
|
|
150
|
+
Requires-Dist: diskcache; extra == "all"
|
|
151
|
+
Requires-Dist: json_repair; extra == "all"
|
|
145
152
|
Requires-Dist: flet-video; extra == "all"
|
|
146
|
-
Requires-Dist:
|
|
147
|
-
Requires-Dist:
|
|
153
|
+
Requires-Dist: pyyaml; extra == "all"
|
|
154
|
+
Requires-Dist: openpyxl; extra == "all"
|
|
155
|
+
Requires-Dist: filetype; extra == "all"
|
|
156
|
+
Requires-Dist: google-api-python-client; extra == "all"
|
|
157
|
+
Requires-Dist: google-auth; extra == "all"
|
|
148
158
|
Requires-Dist: pymupdf4llm; extra == "all"
|
|
149
|
-
Requires-Dist:
|
|
159
|
+
Requires-Dist: dask[bag]; extra == "all"
|
|
160
|
+
Requires-Dist: httpx_retries; extra == "all"
|
|
161
|
+
Requires-Dist: torchvision; extra == "all"
|
|
162
|
+
Requires-Dist: tabulate; extra == "all"
|
|
163
|
+
Requires-Dist: logfire[httpx]; extra == "all"
|
|
150
164
|
Requires-Dist: tokenizers; extra == "all"
|
|
151
|
-
Requires-Dist:
|
|
165
|
+
Requires-Dist: logfire; extra == "all"
|
|
166
|
+
Requires-Dist: pymupdf; extra == "all"
|
|
152
167
|
Requires-Dist: dnspython[doh]; extra == "all"
|
|
168
|
+
Requires-Dist: yamlscript; extra == "all"
|
|
153
169
|
Requires-Dist: sentence_transformers; extra == "all"
|
|
154
|
-
Requires-Dist:
|
|
155
|
-
Requires-Dist: pymupdf; extra == "all"
|
|
170
|
+
Requires-Dist: fastapi; extra == "all"
|
|
156
171
|
Requires-Dist: google-auth-oauthlib; extra == "all"
|
|
172
|
+
Requires-Dist: pydevd-pycharm; extra == "all"
|
|
157
173
|
Requires-Dist: pydantic; extra == "all"
|
|
158
|
-
Requires-Dist: docker; extra == "all"
|
|
159
|
-
Requires-Dist: json_repair; extra == "all"
|
|
160
|
-
Requires-Dist: tabulate; extra == "all"
|
|
161
|
-
Requires-Dist: appdirs; extra == "all"
|
|
162
|
-
Requires-Dist: faker; extra == "all"
|
|
163
|
-
Requires-Dist: torchaudio; extra == "all"
|
|
164
|
-
Requires-Dist: pandas; extra == "all"
|
|
165
|
-
Requires-Dist: transformers[sentencepiece]; extra == "all"
|
|
166
|
-
Requires-Dist: pyyaml; extra == "all"
|
|
167
|
-
Requires-Dist: semver; extra == "all"
|
|
168
174
|
Requires-Dist: flet[all]; extra == "all"
|
|
169
|
-
Requires-Dist:
|
|
170
|
-
Requires-Dist:
|
|
171
|
-
Requires-Dist:
|
|
172
|
-
Requires-Dist:
|
|
173
|
-
Requires-Dist: torchvision; extra == "all"
|
|
175
|
+
Requires-Dist: semver; extra == "all"
|
|
176
|
+
Requires-Dist: logfire[fastapi]; extra == "all"
|
|
177
|
+
Requires-Dist: torchaudio; extra == "all"
|
|
178
|
+
Requires-Dist: ollama; extra == "all"
|
|
174
179
|
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
175
|
-
Requires-Dist:
|
|
176
|
-
Requires-Dist:
|
|
177
|
-
Requires-Dist:
|
|
180
|
+
Requires-Dist: appdirs; extra == "all"
|
|
181
|
+
Requires-Dist: transformers[sentencepiece]; extra == "all"
|
|
182
|
+
Requires-Dist: setuptools; extra == "all"
|
|
183
|
+
Requires-Dist: sre_yield; extra == "all"
|
|
178
184
|
Requires-Dist: peft; extra == "all"
|
|
179
|
-
Requires-Dist: logfire; extra == "all"
|
|
180
185
|
Dynamic: author
|
|
181
186
|
Dynamic: author-email
|
|
182
187
|
Dynamic: description
|
|
@@ -15,60 +15,60 @@ pydantic-ai[logfire,openai]
|
|
|
15
15
|
ollama
|
|
16
16
|
|
|
17
17
|
[all]
|
|
18
|
-
tinynetrc
|
|
19
|
-
openpyxl
|
|
20
18
|
httpx
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
pydantic-ai[logfire,openai]
|
|
20
|
+
Unidecode
|
|
21
|
+
huggingface_hub
|
|
22
|
+
faker
|
|
23
|
+
bokeh
|
|
24
|
+
docker
|
|
24
25
|
deepmerge
|
|
26
|
+
uvicorn[standard]
|
|
27
|
+
regex
|
|
28
|
+
tinynetrc
|
|
25
29
|
pydantic-settings
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
contexttimer
|
|
31
|
+
flet-webview
|
|
32
|
+
pandas
|
|
29
33
|
openai
|
|
30
|
-
|
|
31
|
-
pydevd-pycharm
|
|
32
|
-
sre_yield
|
|
34
|
+
pytest-cov
|
|
33
35
|
distributed
|
|
34
|
-
contexttimer
|
|
35
36
|
html2text
|
|
36
|
-
|
|
37
|
+
diskcache
|
|
38
|
+
json_repair
|
|
37
39
|
flet-video
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
pyyaml
|
|
41
|
+
openpyxl
|
|
42
|
+
filetype
|
|
43
|
+
google-api-python-client
|
|
44
|
+
google-auth
|
|
40
45
|
pymupdf4llm
|
|
41
|
-
|
|
46
|
+
dask[bag]
|
|
47
|
+
httpx_retries
|
|
48
|
+
torchvision
|
|
49
|
+
tabulate
|
|
50
|
+
logfire[httpx]
|
|
42
51
|
tokenizers
|
|
43
|
-
|
|
52
|
+
logfire
|
|
53
|
+
pymupdf
|
|
44
54
|
dnspython[doh]
|
|
55
|
+
yamlscript
|
|
45
56
|
sentence_transformers
|
|
46
|
-
|
|
47
|
-
pymupdf
|
|
57
|
+
fastapi
|
|
48
58
|
google-auth-oauthlib
|
|
59
|
+
pydevd-pycharm
|
|
49
60
|
pydantic
|
|
50
|
-
docker
|
|
51
|
-
json_repair
|
|
52
|
-
tabulate
|
|
53
|
-
appdirs
|
|
54
|
-
faker
|
|
55
|
-
torchaudio
|
|
56
|
-
pandas
|
|
57
|
-
transformers[sentencepiece]
|
|
58
|
-
pyyaml
|
|
59
|
-
semver
|
|
60
61
|
flet[all]
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
torchvision
|
|
62
|
+
semver
|
|
63
|
+
logfire[fastapi]
|
|
64
|
+
torchaudio
|
|
65
|
+
ollama
|
|
66
66
|
google-auth-httplib2
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
appdirs
|
|
68
|
+
transformers[sentencepiece]
|
|
69
|
+
setuptools
|
|
70
|
+
sre_yield
|
|
70
71
|
peft
|
|
71
|
-
logfire
|
|
72
72
|
|
|
73
73
|
[api]
|
|
74
74
|
fastapi
|
|
@@ -93,6 +93,11 @@ pydantic
|
|
|
93
93
|
|
|
94
94
|
[dns]
|
|
95
95
|
dnspython[doh]
|
|
96
|
+
httpx
|
|
97
|
+
httpx_retries
|
|
98
|
+
logfire
|
|
99
|
+
semver
|
|
100
|
+
logfire[httpx]
|
|
96
101
|
|
|
97
102
|
[docker.api]
|
|
98
103
|
docker
|
|
@@ -35,7 +35,7 @@ DEPENDENCIES = {
|
|
|
35
35
|
'sets': ['pydantic-settings', 'dm'],
|
|
36
36
|
'path.app': ['appdirs'],
|
|
37
37
|
'path.type': ['filetype'],
|
|
38
|
-
'dns': ['dnspython[doh]'],
|
|
38
|
+
'dns': ['dnspython[doh]', 'http'],
|
|
39
39
|
'patterns': ['regex'],
|
|
40
40
|
'http': ['httpx', 'httpx_retries', 'logging', 'logfire[httpx]'],
|
|
41
41
|
'setup': ['setuptools']
|
|
@@ -1,57 +0,0 @@
|
|
|
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()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1.2.2
|
|
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
|