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.

Files changed (86) hide show
  1. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/PKG-INFO +44 -39
  2. fmtr_tools-1.2.4/fmtr/tools/dns_tools.py +221 -0
  3. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/pattern_tools.py +6 -3
  4. fmtr_tools-1.2.4/fmtr/tools/version +1 -0
  5. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr.tools.egg-info/PKG-INFO +44 -39
  6. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr.tools.egg-info/requires.txt +43 -38
  7. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/setup.py +1 -1
  8. fmtr_tools-1.2.2/fmtr/tools/dns_tools.py +0 -57
  9. fmtr_tools-1.2.2/fmtr/tools/version +0 -1
  10. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/LICENSE +0 -0
  11. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/README.md +0 -0
  12. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/__init__.py +0 -0
  13. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/ai_tools/__init__.py +0 -0
  14. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/ai_tools/agentic_tools.py +0 -0
  15. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/ai_tools/inference_tools.py +0 -0
  16. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/api_tools.py +0 -0
  17. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/async_tools.py +0 -0
  18. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/augmentation_tools.py +0 -0
  19. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/caching_tools.py +0 -0
  20. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/constants.py +0 -0
  21. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/data_modelling_tools.py +0 -0
  22. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/dataclass_tools.py +0 -0
  23. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/datatype_tools.py +0 -0
  24. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/debugging_tools.py +0 -0
  25. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/docker_tools.py +0 -0
  26. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/entrypoints/__init__.py +0 -0
  27. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/entrypoints/cache_hfh.py +0 -0
  28. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/entrypoints/ep_test.py +0 -0
  29. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/entrypoints/remote_debug_test.py +0 -0
  30. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/entrypoints/shell_debug.py +0 -0
  31. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/environment_tools.py +0 -0
  32. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/function_tools.py +0 -0
  33. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/google_api_tools.py +0 -0
  34. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/hash_tools.py +0 -0
  35. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/hfh_tools.py +0 -0
  36. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/html_tools.py +0 -0
  37. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/http_tools.py +0 -0
  38. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/import_tools.py +0 -0
  39. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/inspection_tools.py +0 -0
  40. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/interface_tools.py +0 -0
  41. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/iterator_tools.py +0 -0
  42. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/json_fix_tools.py +0 -0
  43. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/json_tools.py +0 -0
  44. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/logging_tools.py +0 -0
  45. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/merging_tools.py +0 -0
  46. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/metric_tools.py +0 -0
  47. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/name_tools.py +0 -0
  48. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/netrc_tools.py +0 -0
  49. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/openai_tools.py +0 -0
  50. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/packaging_tools.py +0 -0
  51. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/parallel_tools.py +0 -0
  52. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/path_tools/__init__.py +0 -0
  53. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/path_tools/app_path_tools.py +0 -0
  54. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/path_tools/path_tools.py +0 -0
  55. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/path_tools/type_path_tools.py +0 -0
  56. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/pdf_tools.py +0 -0
  57. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/platform_tools.py +0 -0
  58. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/process_tools.py +0 -0
  59. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/profiling_tools.py +0 -0
  60. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/random_tools.py +0 -0
  61. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/semantic_tools.py +0 -0
  62. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/settings_tools.py +0 -0
  63. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/setup_tools/__init__.py +0 -0
  64. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/setup_tools/setup_tools.py +0 -0
  65. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/spaces_tools.py +0 -0
  66. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/string_tools.py +0 -0
  67. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tabular_tools.py +0 -0
  68. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/__init__.py +0 -0
  69. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/conftest.py +0 -0
  70. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/helpers.py +0 -0
  71. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/test_datatype.py +0 -0
  72. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/test_environment.py +0 -0
  73. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/test_json.py +0 -0
  74. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/test_path.py +0 -0
  75. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tests/test_yaml.py +0 -0
  76. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tokenization_tools.py +0 -0
  77. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/tools.py +0 -0
  78. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/unicode_tools.py +0 -0
  79. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/version_tools.py +0 -0
  80. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr/tools/yaml_tools.py +0 -0
  81. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr.tools.egg-info/SOURCES.txt +0 -0
  82. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr.tools.egg-info/dependency_links.txt +0 -0
  83. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr.tools.egg-info/entry_points.txt +0 -0
  84. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/fmtr.tools.egg-info/top_level.txt +0 -0
  85. {fmtr_tools-1.2.2 → fmtr_tools-1.2.4}/pyproject.toml +0 -0
  86. {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.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: regex; extra == "all"
130
- Requires-Dist: logfire[fastapi]; extra == "all"
131
- Requires-Dist: dask[bag]; extra == "all"
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: yamlscript; extra == "all"
135
- Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
136
- Requires-Dist: ollama; extra == "all"
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: diskcache; extra == "all"
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: filetype; extra == "all"
150
+ Requires-Dist: diskcache; extra == "all"
151
+ Requires-Dist: json_repair; extra == "all"
145
152
  Requires-Dist: flet-video; extra == "all"
146
- Requires-Dist: logfire[httpx]; extra == "all"
147
- Requires-Dist: flet-webview; extra == "all"
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: uvicorn[standard]; extra == "all"
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: google-auth; extra == "all"
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: setuptools; extra == "all"
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: Unidecode; extra == "all"
170
- Requires-Dist: huggingface_hub; extra == "all"
171
- Requires-Dist: fastapi; extra == "all"
172
- Requires-Dist: bokeh; extra == "all"
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: pytest-cov; extra == "all"
176
- Requires-Dist: httpx_retries; extra == "all"
177
- Requires-Dist: google-api-python-client; extra == "all"
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
- logger.debug(f'Finished rewriting: {get_history_str()}')
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.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: regex; extra == "all"
130
- Requires-Dist: logfire[fastapi]; extra == "all"
131
- Requires-Dist: dask[bag]; extra == "all"
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: yamlscript; extra == "all"
135
- Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
136
- Requires-Dist: ollama; extra == "all"
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: diskcache; extra == "all"
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: filetype; extra == "all"
150
+ Requires-Dist: diskcache; extra == "all"
151
+ Requires-Dist: json_repair; extra == "all"
145
152
  Requires-Dist: flet-video; extra == "all"
146
- Requires-Dist: logfire[httpx]; extra == "all"
147
- Requires-Dist: flet-webview; extra == "all"
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: uvicorn[standard]; extra == "all"
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: google-auth; extra == "all"
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: setuptools; extra == "all"
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: Unidecode; extra == "all"
170
- Requires-Dist: huggingface_hub; extra == "all"
171
- Requires-Dist: fastapi; extra == "all"
172
- Requires-Dist: bokeh; extra == "all"
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: pytest-cov; extra == "all"
176
- Requires-Dist: httpx_retries; extra == "all"
177
- Requires-Dist: google-api-python-client; extra == "all"
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
- regex
22
- logfire[fastapi]
23
- dask[bag]
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
- yamlscript
27
- pydantic-ai[logfire,openai]
28
- ollama
30
+ contexttimer
31
+ flet-webview
32
+ pandas
29
33
  openai
30
- diskcache
31
- pydevd-pycharm
32
- sre_yield
34
+ pytest-cov
33
35
  distributed
34
- contexttimer
35
36
  html2text
36
- filetype
37
+ diskcache
38
+ json_repair
37
39
  flet-video
38
- logfire[httpx]
39
- flet-webview
40
+ pyyaml
41
+ openpyxl
42
+ filetype
43
+ google-api-python-client
44
+ google-auth
40
45
  pymupdf4llm
41
- uvicorn[standard]
46
+ dask[bag]
47
+ httpx_retries
48
+ torchvision
49
+ tabulate
50
+ logfire[httpx]
42
51
  tokenizers
43
- google-auth
52
+ logfire
53
+ pymupdf
44
54
  dnspython[doh]
55
+ yamlscript
45
56
  sentence_transformers
46
- setuptools
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
- Unidecode
62
- huggingface_hub
63
- fastapi
64
- bokeh
65
- torchvision
62
+ semver
63
+ logfire[fastapi]
64
+ torchaudio
65
+ ollama
66
66
  google-auth-httplib2
67
- pytest-cov
68
- httpx_retries
69
- google-api-python-client
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