fmtr.tools 1.2.2__tar.gz → 1.2.3__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.3}/PKG-INFO +48 -43
  2. fmtr_tools-1.2.3/fmtr/tools/dns_tools.py +221 -0
  3. fmtr_tools-1.2.3/fmtr/tools/version +1 -0
  4. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr.tools.egg-info/PKG-INFO +48 -43
  5. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr.tools.egg-info/requires.txt +47 -42
  6. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/setup.py +1 -1
  7. fmtr_tools-1.2.2/fmtr/tools/dns_tools.py +0 -57
  8. fmtr_tools-1.2.2/fmtr/tools/version +0 -1
  9. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/LICENSE +0 -0
  10. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/README.md +0 -0
  11. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/__init__.py +0 -0
  12. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/ai_tools/__init__.py +0 -0
  13. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/ai_tools/agentic_tools.py +0 -0
  14. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/ai_tools/inference_tools.py +0 -0
  15. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/api_tools.py +0 -0
  16. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/async_tools.py +0 -0
  17. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/augmentation_tools.py +0 -0
  18. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/caching_tools.py +0 -0
  19. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/constants.py +0 -0
  20. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/data_modelling_tools.py +0 -0
  21. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/dataclass_tools.py +0 -0
  22. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/datatype_tools.py +0 -0
  23. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/debugging_tools.py +0 -0
  24. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/docker_tools.py +0 -0
  25. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/entrypoints/__init__.py +0 -0
  26. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/entrypoints/cache_hfh.py +0 -0
  27. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/entrypoints/ep_test.py +0 -0
  28. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/entrypoints/remote_debug_test.py +0 -0
  29. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/entrypoints/shell_debug.py +0 -0
  30. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/environment_tools.py +0 -0
  31. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/function_tools.py +0 -0
  32. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/google_api_tools.py +0 -0
  33. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/hash_tools.py +0 -0
  34. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/hfh_tools.py +0 -0
  35. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/html_tools.py +0 -0
  36. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/http_tools.py +0 -0
  37. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/import_tools.py +0 -0
  38. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/inspection_tools.py +0 -0
  39. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/interface_tools.py +0 -0
  40. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/iterator_tools.py +0 -0
  41. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/json_fix_tools.py +0 -0
  42. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/json_tools.py +0 -0
  43. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/logging_tools.py +0 -0
  44. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/merging_tools.py +0 -0
  45. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/metric_tools.py +0 -0
  46. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/name_tools.py +0 -0
  47. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/netrc_tools.py +0 -0
  48. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/openai_tools.py +0 -0
  49. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/packaging_tools.py +0 -0
  50. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/parallel_tools.py +0 -0
  51. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/path_tools/__init__.py +0 -0
  52. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/path_tools/app_path_tools.py +0 -0
  53. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/path_tools/path_tools.py +0 -0
  54. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/path_tools/type_path_tools.py +0 -0
  55. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/pattern_tools.py +0 -0
  56. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/pdf_tools.py +0 -0
  57. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/platform_tools.py +0 -0
  58. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/process_tools.py +0 -0
  59. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/profiling_tools.py +0 -0
  60. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/random_tools.py +0 -0
  61. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/semantic_tools.py +0 -0
  62. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/settings_tools.py +0 -0
  63. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/setup_tools/__init__.py +0 -0
  64. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/setup_tools/setup_tools.py +0 -0
  65. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/spaces_tools.py +0 -0
  66. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/string_tools.py +0 -0
  67. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/tabular_tools.py +0 -0
  68. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/tests/__init__.py +0 -0
  69. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/tests/conftest.py +0 -0
  70. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/tests/helpers.py +0 -0
  71. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/tests/test_datatype.py +0 -0
  72. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/tests/test_environment.py +0 -0
  73. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/tests/test_json.py +0 -0
  74. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/tests/test_path.py +0 -0
  75. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/tests/test_yaml.py +0 -0
  76. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/tokenization_tools.py +0 -0
  77. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/tools.py +0 -0
  78. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/unicode_tools.py +0 -0
  79. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/version_tools.py +0 -0
  80. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr/tools/yaml_tools.py +0 -0
  81. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr.tools.egg-info/SOURCES.txt +0 -0
  82. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr.tools.egg-info/dependency_links.txt +0 -0
  83. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr.tools.egg-info/entry_points.txt +0 -0
  84. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/fmtr.tools.egg-info/top_level.txt +0 -0
  85. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/pyproject.toml +0 -0
  86. {fmtr_tools-1.2.2 → fmtr_tools-1.2.3}/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.3
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
- 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: deepmerge; extra == "all"
131
+ Requires-Dist: logfire; extra == "all"
132
+ Requires-Dist: tokenizers; extra == "all"
133
+ Requires-Dist: appdirs; extra == "all"
133
134
  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"
137
- 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"
141
- Requires-Dist: distributed; extra == "all"
142
- Requires-Dist: contexttimer; extra == "all"
143
- Requires-Dist: html2text; extra == "all"
144
135
  Requires-Dist: filetype; extra == "all"
145
- Requires-Dist: flet-video; extra == "all"
146
- Requires-Dist: logfire[httpx]; extra == "all"
147
- Requires-Dist: flet-webview; extra == "all"
148
- Requires-Dist: pymupdf4llm; extra == "all"
149
- Requires-Dist: uvicorn[standard]; extra == "all"
150
- Requires-Dist: tokenizers; extra == "all"
151
- Requires-Dist: google-auth; extra == "all"
152
- Requires-Dist: dnspython[doh]; extra == "all"
153
- Requires-Dist: sentence_transformers; extra == "all"
154
- Requires-Dist: setuptools; extra == "all"
155
136
  Requires-Dist: pymupdf; extra == "all"
156
- Requires-Dist: google-auth-oauthlib; extra == "all"
157
- Requires-Dist: pydantic; extra == "all"
158
- Requires-Dist: docker; extra == "all"
159
137
  Requires-Dist: json_repair; extra == "all"
138
+ Requires-Dist: sentence_transformers; extra == "all"
139
+ Requires-Dist: fastapi; extra == "all"
160
140
  Requires-Dist: tabulate; extra == "all"
161
- Requires-Dist: appdirs; extra == "all"
141
+ Requires-Dist: distributed; extra == "all"
162
142
  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
143
  Requires-Dist: semver; extra == "all"
168
- Requires-Dist: flet[all]; extra == "all"
144
+ Requires-Dist: uvicorn[standard]; extra == "all"
145
+ Requires-Dist: html2text; extra == "all"
146
+ Requires-Dist: google-auth-httplib2; extra == "all"
147
+ Requires-Dist: bokeh; extra == "all"
148
+ Requires-Dist: flet-webview; extra == "all"
149
+ Requires-Dist: logfire[httpx]; extra == "all"
150
+ Requires-Dist: torchaudio; extra == "all"
169
151
  Requires-Dist: Unidecode; extra == "all"
152
+ Requires-Dist: flet-video; extra == "all"
153
+ Requires-Dist: google-auth; extra == "all"
154
+ Requires-Dist: flet[all]; extra == "all"
155
+ Requires-Dist: openai; extra == "all"
156
+ Requires-Dist: pymupdf4llm; extra == "all"
157
+ Requires-Dist: dnspython[doh]; extra == "all"
158
+ Requires-Dist: deepmerge; extra == "all"
159
+ Requires-Dist: contexttimer; extra == "all"
160
+ Requires-Dist: openpyxl; extra == "all"
161
+ Requires-Dist: tinynetrc; extra == "all"
162
+ Requires-Dist: ollama; extra == "all"
170
163
  Requires-Dist: huggingface_hub; extra == "all"
171
- Requires-Dist: fastapi; extra == "all"
172
- Requires-Dist: bokeh; extra == "all"
173
- Requires-Dist: torchvision; extra == "all"
174
- Requires-Dist: google-auth-httplib2; extra == "all"
175
- Requires-Dist: pytest-cov; extra == "all"
164
+ Requires-Dist: google-auth-oauthlib; extra == "all"
176
165
  Requires-Dist: httpx_retries; extra == "all"
166
+ Requires-Dist: docker; extra == "all"
167
+ Requires-Dist: setuptools; extra == "all"
168
+ Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
177
169
  Requires-Dist: google-api-python-client; extra == "all"
170
+ Requires-Dist: torchvision; extra == "all"
171
+ Requires-Dist: logfire[fastapi]; extra == "all"
172
+ Requires-Dist: pandas; extra == "all"
173
+ Requires-Dist: regex; extra == "all"
174
+ Requires-Dist: httpx; extra == "all"
175
+ Requires-Dist: sre_yield; extra == "all"
176
+ Requires-Dist: pyyaml; extra == "all"
178
177
  Requires-Dist: peft; extra == "all"
179
- Requires-Dist: logfire; extra == "all"
178
+ Requires-Dist: pydantic; extra == "all"
179
+ Requires-Dist: pytest-cov; extra == "all"
180
+ Requires-Dist: yamlscript; extra == "all"
181
+ Requires-Dist: transformers[sentencepiece]; extra == "all"
182
+ Requires-Dist: diskcache; extra == "all"
183
+ Requires-Dist: dask[bag]; extra == "all"
184
+ Requires-Dist: pydevd-pycharm; 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
@@ -0,0 +1 @@
1
+ 1.2.3
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmtr.tools
3
- Version: 1.2.2
3
+ Version: 1.2.3
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
- 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: deepmerge; extra == "all"
131
+ Requires-Dist: logfire; extra == "all"
132
+ Requires-Dist: tokenizers; extra == "all"
133
+ Requires-Dist: appdirs; extra == "all"
133
134
  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"
137
- 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"
141
- Requires-Dist: distributed; extra == "all"
142
- Requires-Dist: contexttimer; extra == "all"
143
- Requires-Dist: html2text; extra == "all"
144
135
  Requires-Dist: filetype; extra == "all"
145
- Requires-Dist: flet-video; extra == "all"
146
- Requires-Dist: logfire[httpx]; extra == "all"
147
- Requires-Dist: flet-webview; extra == "all"
148
- Requires-Dist: pymupdf4llm; extra == "all"
149
- Requires-Dist: uvicorn[standard]; extra == "all"
150
- Requires-Dist: tokenizers; extra == "all"
151
- Requires-Dist: google-auth; extra == "all"
152
- Requires-Dist: dnspython[doh]; extra == "all"
153
- Requires-Dist: sentence_transformers; extra == "all"
154
- Requires-Dist: setuptools; extra == "all"
155
136
  Requires-Dist: pymupdf; extra == "all"
156
- Requires-Dist: google-auth-oauthlib; extra == "all"
157
- Requires-Dist: pydantic; extra == "all"
158
- Requires-Dist: docker; extra == "all"
159
137
  Requires-Dist: json_repair; extra == "all"
138
+ Requires-Dist: sentence_transformers; extra == "all"
139
+ Requires-Dist: fastapi; extra == "all"
160
140
  Requires-Dist: tabulate; extra == "all"
161
- Requires-Dist: appdirs; extra == "all"
141
+ Requires-Dist: distributed; extra == "all"
162
142
  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
143
  Requires-Dist: semver; extra == "all"
168
- Requires-Dist: flet[all]; extra == "all"
144
+ Requires-Dist: uvicorn[standard]; extra == "all"
145
+ Requires-Dist: html2text; extra == "all"
146
+ Requires-Dist: google-auth-httplib2; extra == "all"
147
+ Requires-Dist: bokeh; extra == "all"
148
+ Requires-Dist: flet-webview; extra == "all"
149
+ Requires-Dist: logfire[httpx]; extra == "all"
150
+ Requires-Dist: torchaudio; extra == "all"
169
151
  Requires-Dist: Unidecode; extra == "all"
152
+ Requires-Dist: flet-video; extra == "all"
153
+ Requires-Dist: google-auth; extra == "all"
154
+ Requires-Dist: flet[all]; extra == "all"
155
+ Requires-Dist: openai; extra == "all"
156
+ Requires-Dist: pymupdf4llm; extra == "all"
157
+ Requires-Dist: dnspython[doh]; extra == "all"
158
+ Requires-Dist: deepmerge; extra == "all"
159
+ Requires-Dist: contexttimer; extra == "all"
160
+ Requires-Dist: openpyxl; extra == "all"
161
+ Requires-Dist: tinynetrc; extra == "all"
162
+ Requires-Dist: ollama; extra == "all"
170
163
  Requires-Dist: huggingface_hub; extra == "all"
171
- Requires-Dist: fastapi; extra == "all"
172
- Requires-Dist: bokeh; extra == "all"
173
- Requires-Dist: torchvision; extra == "all"
174
- Requires-Dist: google-auth-httplib2; extra == "all"
175
- Requires-Dist: pytest-cov; extra == "all"
164
+ Requires-Dist: google-auth-oauthlib; extra == "all"
176
165
  Requires-Dist: httpx_retries; extra == "all"
166
+ Requires-Dist: docker; extra == "all"
167
+ Requires-Dist: setuptools; extra == "all"
168
+ Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
177
169
  Requires-Dist: google-api-python-client; extra == "all"
170
+ Requires-Dist: torchvision; extra == "all"
171
+ Requires-Dist: logfire[fastapi]; extra == "all"
172
+ Requires-Dist: pandas; extra == "all"
173
+ Requires-Dist: regex; extra == "all"
174
+ Requires-Dist: httpx; extra == "all"
175
+ Requires-Dist: sre_yield; extra == "all"
176
+ Requires-Dist: pyyaml; extra == "all"
178
177
  Requires-Dist: peft; extra == "all"
179
- Requires-Dist: logfire; extra == "all"
178
+ Requires-Dist: pydantic; extra == "all"
179
+ Requires-Dist: pytest-cov; extra == "all"
180
+ Requires-Dist: yamlscript; extra == "all"
181
+ Requires-Dist: transformers[sentencepiece]; extra == "all"
182
+ Requires-Dist: diskcache; extra == "all"
183
+ Requires-Dist: dask[bag]; extra == "all"
184
+ Requires-Dist: pydevd-pycharm; 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
- httpx
21
- regex
22
- logfire[fastapi]
23
- dask[bag]
24
- deepmerge
18
+ logfire
19
+ tokenizers
20
+ appdirs
25
21
  pydantic-settings
26
- yamlscript
27
- pydantic-ai[logfire,openai]
28
- ollama
29
- openai
30
- diskcache
31
- pydevd-pycharm
32
- sre_yield
33
- distributed
34
- contexttimer
35
- html2text
36
22
  filetype
37
- flet-video
38
- logfire[httpx]
39
- flet-webview
40
- pymupdf4llm
41
- uvicorn[standard]
42
- tokenizers
43
- google-auth
44
- dnspython[doh]
45
- sentence_transformers
46
- setuptools
47
23
  pymupdf
48
- google-auth-oauthlib
49
- pydantic
50
- docker
51
24
  json_repair
25
+ sentence_transformers
26
+ fastapi
52
27
  tabulate
53
- appdirs
28
+ distributed
54
29
  faker
55
- torchaudio
56
- pandas
57
- transformers[sentencepiece]
58
- pyyaml
59
30
  semver
60
- flet[all]
31
+ uvicorn[standard]
32
+ html2text
33
+ google-auth-httplib2
34
+ bokeh
35
+ flet-webview
36
+ logfire[httpx]
37
+ torchaudio
61
38
  Unidecode
39
+ flet-video
40
+ google-auth
41
+ flet[all]
42
+ openai
43
+ pymupdf4llm
44
+ dnspython[doh]
45
+ deepmerge
46
+ contexttimer
47
+ openpyxl
48
+ tinynetrc
49
+ ollama
62
50
  huggingface_hub
63
- fastapi
64
- bokeh
65
- torchvision
66
- google-auth-httplib2
67
- pytest-cov
51
+ google-auth-oauthlib
68
52
  httpx_retries
53
+ docker
54
+ setuptools
55
+ pydantic-ai[logfire,openai]
69
56
  google-api-python-client
57
+ torchvision
58
+ logfire[fastapi]
59
+ pandas
60
+ regex
61
+ httpx
62
+ sre_yield
63
+ pyyaml
70
64
  peft
71
- logfire
65
+ pydantic
66
+ pytest-cov
67
+ yamlscript
68
+ transformers[sentencepiece]
69
+ diskcache
70
+ dask[bag]
71
+ pydevd-pycharm
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