fmtr.tools 1.3.1__tar.gz → 1.3.2__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 (90) hide show
  1. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/PKG-INFO +45 -45
  2. fmtr_tools-1.3.2/fmtr/tools/dns_tools/__init__.py +6 -0
  3. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/dns_tools/client.py +28 -19
  4. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/dns_tools/dm.py +59 -10
  5. fmtr_tools-1.3.1/fmtr/tools/dns_tools/server.py → fmtr_tools-1.3.2/fmtr/tools/dns_tools/proxy.py +17 -38
  6. fmtr_tools-1.3.2/fmtr/tools/dns_tools/server.py +38 -0
  7. fmtr_tools-1.3.2/fmtr/tools/version +1 -0
  8. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr.tools.egg-info/PKG-INFO +45 -45
  9. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr.tools.egg-info/SOURCES.txt +2 -0
  10. fmtr_tools-1.3.1/fmtr/tools/dns_tools/__init__.py +0 -6
  11. fmtr_tools-1.3.1/fmtr/tools/version +0 -1
  12. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/LICENSE +0 -0
  13. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/README.md +0 -0
  14. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/__init__.py +0 -0
  15. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/ai_tools/__init__.py +0 -0
  16. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/ai_tools/agentic_tools.py +0 -0
  17. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/ai_tools/inference_tools.py +0 -0
  18. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/api_tools.py +0 -0
  19. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/async_tools.py +0 -0
  20. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/augmentation_tools.py +0 -0
  21. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/caching_tools.py +0 -0
  22. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/constants.py +0 -0
  23. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/data_modelling_tools.py +0 -0
  24. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/dataclass_tools.py +0 -0
  25. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/datatype_tools.py +0 -0
  26. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/debugging_tools.py +0 -0
  27. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/docker_tools.py +0 -0
  28. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/entrypoints/__init__.py +0 -0
  29. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/entrypoints/cache_hfh.py +0 -0
  30. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/entrypoints/ep_test.py +0 -0
  31. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/entrypoints/remote_debug_test.py +0 -0
  32. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/entrypoints/shell_debug.py +0 -0
  33. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/environment_tools.py +0 -0
  34. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/function_tools.py +0 -0
  35. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/google_api_tools.py +0 -0
  36. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/hash_tools.py +0 -0
  37. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/hfh_tools.py +0 -0
  38. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/html_tools.py +0 -0
  39. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/http_tools.py +0 -0
  40. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/import_tools.py +0 -0
  41. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/inspection_tools.py +0 -0
  42. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/interface_tools.py +0 -0
  43. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/iterator_tools.py +0 -0
  44. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/json_fix_tools.py +0 -0
  45. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/json_tools.py +0 -0
  46. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/logging_tools.py +0 -0
  47. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/merging_tools.py +0 -0
  48. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/metric_tools.py +0 -0
  49. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/name_tools.py +0 -0
  50. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/netrc_tools.py +0 -0
  51. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/openai_tools.py +0 -0
  52. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/packaging_tools.py +0 -0
  53. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/parallel_tools.py +0 -0
  54. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/path_tools/__init__.py +0 -0
  55. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/path_tools/app_path_tools.py +0 -0
  56. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/path_tools/path_tools.py +0 -0
  57. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/path_tools/type_path_tools.py +0 -0
  58. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/pattern_tools.py +0 -0
  59. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/pdf_tools.py +0 -0
  60. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/platform_tools.py +0 -0
  61. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/process_tools.py +0 -0
  62. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/profiling_tools.py +0 -0
  63. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/random_tools.py +0 -0
  64. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/semantic_tools.py +0 -0
  65. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/settings_tools.py +0 -0
  66. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/setup_tools/__init__.py +0 -0
  67. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/setup_tools/setup_tools.py +0 -0
  68. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/spaces_tools.py +0 -0
  69. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/string_tools.py +0 -0
  70. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/tabular_tools.py +0 -0
  71. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/tests/__init__.py +0 -0
  72. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/tests/conftest.py +0 -0
  73. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/tests/helpers.py +0 -0
  74. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/tests/test_datatype.py +0 -0
  75. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/tests/test_environment.py +0 -0
  76. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/tests/test_json.py +0 -0
  77. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/tests/test_path.py +0 -0
  78. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/tests/test_yaml.py +0 -0
  79. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/tokenization_tools.py +0 -0
  80. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/tools.py +0 -0
  81. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/unicode_tools.py +0 -0
  82. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/version_tools.py +0 -0
  83. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr/tools/yaml_tools.py +0 -0
  84. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr.tools.egg-info/dependency_links.txt +0 -0
  85. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr.tools.egg-info/entry_points.txt +0 -0
  86. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr.tools.egg-info/requires.txt +44 -44
  87. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/fmtr.tools.egg-info/top_level.txt +0 -0
  88. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/pyproject.toml +0 -0
  89. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/setup.cfg +0 -0
  90. {fmtr_tools-1.3.1 → fmtr_tools-1.3.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmtr.tools
3
- Version: 1.3.1
3
+ Version: 1.3.2
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
@@ -130,60 +130,60 @@ Requires-Dist: logfire[httpx]; extra == "http"
130
130
  Provides-Extra: setup
131
131
  Requires-Dist: setuptools; extra == "setup"
132
132
  Provides-Extra: all
133
- Requires-Dist: deepmerge; extra == "all"
134
- Requires-Dist: bokeh; extra == "all"
135
- Requires-Dist: distributed; extra == "all"
136
- Requires-Dist: pydantic; extra == "all"
137
- Requires-Dist: appdirs; extra == "all"
133
+ Requires-Dist: uvicorn[standard]; extra == "all"
134
+ Requires-Dist: openpyxl; extra == "all"
135
+ Requires-Dist: sentence_transformers; extra == "all"
136
+ Requires-Dist: peft; extra == "all"
137
+ Requires-Dist: httpx_retries; extra == "all"
138
138
  Requires-Dist: sre_yield; extra == "all"
139
- Requires-Dist: logfire; extra == "all"
139
+ Requires-Dist: faker; extra == "all"
140
140
  Requires-Dist: ollama; extra == "all"
141
- Requires-Dist: Unidecode; extra == "all"
142
- Requires-Dist: flet[all]; extra == "all"
143
- Requires-Dist: google-auth-httplib2; extra == "all"
144
- Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
145
- Requires-Dist: pydevd-pycharm; extra == "all"
146
- Requires-Dist: torchvision; extra == "all"
147
- Requires-Dist: pyyaml; extra == "all"
148
- Requires-Dist: tabulate; extra == "all"
149
- Requires-Dist: pymupdf; extra == "all"
150
- Requires-Dist: docker; extra == "all"
151
- Requires-Dist: openai; extra == "all"
152
- Requires-Dist: sentence_transformers; extra == "all"
153
- Requires-Dist: dnspython[doh]; extra == "all"
154
- Requires-Dist: regex; extra == "all"
155
- Requires-Dist: semver; extra == "all"
141
+ Requires-Dist: logfire; extra == "all"
142
+ Requires-Dist: bokeh; extra == "all"
156
143
  Requires-Dist: setuptools; extra == "all"
157
- Requires-Dist: google-auth; extra == "all"
158
- Requires-Dist: openpyxl; extra == "all"
159
- Requires-Dist: google-api-python-client; extra == "all"
144
+ Requires-Dist: dask[bag]; extra == "all"
145
+ Requires-Dist: diskcache; extra == "all"
160
146
  Requires-Dist: torchaudio; extra == "all"
161
- Requires-Dist: tinynetrc; extra == "all"
162
- Requires-Dist: httpx_retries; extra == "all"
163
- Requires-Dist: pymupdf4llm; extra == "all"
147
+ Requires-Dist: tokenizers; extra == "all"
148
+ Requires-Dist: pydantic; extra == "all"
149
+ Requires-Dist: pandas; extra == "all"
150
+ Requires-Dist: flet-video; extra == "all"
151
+ Requires-Dist: Unidecode; extra == "all"
152
+ Requires-Dist: pydantic-settings; extra == "all"
153
+ Requires-Dist: dnspython[doh]; extra == "all"
154
+ Requires-Dist: logfire[httpx]; extra == "all"
164
155
  Requires-Dist: yamlscript; extra == "all"
156
+ Requires-Dist: fastapi; extra == "all"
157
+ Requires-Dist: openai; extra == "all"
158
+ Requires-Dist: deepmerge; extra == "all"
159
+ Requires-Dist: html2text; extra == "all"
160
+ Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
161
+ Requires-Dist: contexttimer; extra == "all"
162
+ Requires-Dist: flet[all]; extra == "all"
163
+ Requires-Dist: logfire[fastapi]; extra == "all"
164
+ Requires-Dist: regex; extra == "all"
165
+ Requires-Dist: docker; extra == "all"
165
166
  Requires-Dist: filetype; extra == "all"
166
- Requires-Dist: pydantic-settings; extra == "all"
167
- Requires-Dist: flet-webview; extra == "all"
168
167
  Requires-Dist: google-auth-oauthlib; extra == "all"
168
+ Requires-Dist: tabulate; extra == "all"
169
169
  Requires-Dist: pytest-cov; extra == "all"
170
- Requires-Dist: fastapi; extra == "all"
171
- Requires-Dist: logfire[fastapi]; extra == "all"
172
- Requires-Dist: pandas; extra == "all"
173
- Requires-Dist: html2text; extra == "all"
174
- Requires-Dist: dask[bag]; extra == "all"
175
- Requires-Dist: uvicorn[standard]; extra == "all"
176
- Requires-Dist: json_repair; extra == "all"
177
- Requires-Dist: flet-video; extra == "all"
178
- Requires-Dist: httpx; extra == "all"
179
- Requires-Dist: faker; extra == "all"
180
- Requires-Dist: peft; extra == "all"
181
- Requires-Dist: tokenizers; extra == "all"
170
+ Requires-Dist: pyyaml; extra == "all"
182
171
  Requires-Dist: transformers[sentencepiece]; extra == "all"
183
- Requires-Dist: logfire[httpx]; extra == "all"
184
- Requires-Dist: contexttimer; extra == "all"
172
+ Requires-Dist: pymupdf; extra == "all"
173
+ Requires-Dist: pydevd-pycharm; extra == "all"
174
+ Requires-Dist: torchvision; extra == "all"
175
+ Requires-Dist: google-auth; extra == "all"
176
+ Requires-Dist: flet-webview; extra == "all"
185
177
  Requires-Dist: huggingface_hub; extra == "all"
186
- Requires-Dist: diskcache; extra == "all"
178
+ Requires-Dist: httpx; extra == "all"
179
+ Requires-Dist: semver; extra == "all"
180
+ Requires-Dist: json_repair; extra == "all"
181
+ Requires-Dist: tinynetrc; extra == "all"
182
+ Requires-Dist: google-auth-httplib2; extra == "all"
183
+ Requires-Dist: appdirs; extra == "all"
184
+ Requires-Dist: pymupdf4llm; extra == "all"
185
+ Requires-Dist: distributed; extra == "all"
186
+ Requires-Dist: google-api-python-client; extra == "all"
187
187
  Dynamic: author
188
188
  Dynamic: author-email
189
189
  Dynamic: description
@@ -0,0 +1,6 @@
1
+ from fmtr.tools.import_tools import MissingExtraMockModule
2
+
3
+ try:
4
+ from fmtr.tools.dns_tools import server, client, dm, proxy
5
+ except ImportError as exception:
6
+ server = client = dm = proxy = MissingExtraMockModule('dns', exception)
@@ -1,11 +1,12 @@
1
- import dns
2
1
  from dataclasses import dataclass
3
- from dns import query
4
2
  from functools import cached_property
3
+
4
+ import dns as dnspython
5
+ from dns import query
5
6
  from httpx_retries import Retry, RetryTransport
6
7
 
7
8
  from fmtr.tools import http_tools as http
8
- from fmtr.tools.dns_tools.dm import Exchange, Response, Request
9
+ from fmtr.tools.dns_tools.dm import Exchange, Response
9
10
  from fmtr.tools.logging_tools import logger
10
11
 
11
12
  RETRY_STRATEGY = Retry(
@@ -29,28 +30,36 @@ class HTTPClientDoH(http.Client):
29
30
  TRANSPORT = RetryTransport(retry=RETRY_STRATEGY)
30
31
 
31
32
 
32
- class ClientBasePlain:
33
- def __init__(self, host, port=53):
34
- self.host = host
35
- self.port = port
33
+ @dataclass
34
+ class Plain:
35
+ """
36
+
37
+ Plain DNS
38
+
39
+ """
40
+ host: str
41
+ port: int = 53
36
42
 
37
43
  def resolve(self, exchange: Exchange):
44
+
38
45
  with logger.span(f'UDP {self.host}:{self.port}'):
39
- response = query.udp(q=exchange.request.message, where=self.host, port=self.port)
40
- exchange.response_upstream = Response.from_message(response)
46
+ response_plain = query.udp(q=exchange.query_last, where=self.host, port=self.port)
47
+ response = Response.from_message(response_plain)
48
+
49
+ exchange.response.message.answer += response.message.answer
41
50
 
42
51
 
43
52
  @dataclass
44
- class ClientDoH:
53
+ class HTTP:
45
54
  """
46
55
 
47
- Base DoH client.
56
+ DNS over HTTP
48
57
 
49
58
  """
50
59
 
51
60
  HEADERS = {"Content-Type": "application/dns-message"}
52
61
  CLIENT = HTTPClientDoH()
53
- BOOTSTRAP = ClientBasePlain('8.8.8.8')
62
+ BOOTSTRAP = Plain('8.8.8.8')
54
63
 
55
64
  host: str
56
65
  url: str
@@ -58,11 +67,10 @@ class ClientDoH:
58
67
 
59
68
  @cached_property
60
69
  def ip(self):
61
- message = dns.message.make_query(self.host, dns.rdatatype.A, flags=0)
62
- request = Request.from_message(message)
63
- exchange = Exchange(request=request, ip=None, port=None)
70
+ message = dnspython.message.make_query(self.host, dnspython.rdatatype.A, flags=0)
71
+ exchange = Exchange.from_wire(message.to_wire(), ip=None, port=None)
64
72
  self.BOOTSTRAP.resolve(exchange)
65
- ip = next(iter(exchange.response_upstream.answer.items.keys())).address
73
+ ip = next(iter(exchange.response.answer.items.keys())).address
66
74
  return ip
67
75
 
68
76
  def resolve(self, exchange: Exchange):
@@ -71,10 +79,11 @@ class ClientDoH:
71
79
  Resolve via DoH
72
80
 
73
81
  """
74
- request = exchange.request
82
+
75
83
  headers = self.HEADERS | dict(Host=self.host)
76
84
  url = self.url.format(host=self.ip)
77
- response_doh = self.CLIENT.post(url, headers=headers, content=request.wire)
85
+ response_doh = self.CLIENT.post(url, headers=headers, content=exchange.query_last.to_wire())
78
86
  response_doh.raise_for_status()
79
87
  response = Response.from_http(response_doh)
80
- exchange.response_upstream = response
88
+
89
+ exchange.response.message.answer += response.message.answer
@@ -1,10 +1,12 @@
1
- import dns
2
- import httpx
3
1
  from dataclasses import dataclass
4
- from dns.message import Message
5
2
  from functools import cached_property
6
3
  from typing import Self, Optional
7
4
 
5
+ import dns
6
+ import httpx
7
+ from dns.message import Message, QueryMessage
8
+ from dns.rrset import RRset
9
+
8
10
 
9
11
  @dataclass
10
12
  class BaseDNSData:
@@ -33,14 +35,17 @@ class Response(BaseDNSData):
33
35
  """
34
36
 
35
37
  http: Optional[httpx.Response] = None
38
+ is_complete: bool = False
36
39
 
37
40
  @classmethod
38
41
  def from_http(cls, response: httpx.Response) -> Self:
39
42
  self = cls(response.content, http=response)
40
43
  return self
41
44
 
42
- @cached_property
43
- def answer(self):
45
+ @property
46
+ def answer(self) -> Optional[RRset]:
47
+ if not self.message.answer:
48
+ return None
44
49
  return self.message.answer[-1]
45
50
 
46
51
 
@@ -54,7 +59,7 @@ class Request(BaseDNSData):
54
59
  wire: bytes
55
60
 
56
61
  @cached_property
57
- def question(self):
62
+ def question(self) -> RRset:
58
63
  return self.message.question[0]
59
64
 
60
65
  @cached_property
@@ -77,10 +82,14 @@ class Request(BaseDNSData):
77
82
  def name_text(self):
78
83
  return self.name.to_text()
79
84
 
85
+ def get_response_template(self):
86
+ message = dns.message.make_response(self.message)
87
+ message.flags |= dns.flags.RA
88
+ return message
89
+
80
90
  @cached_property
81
91
  def blackhole(self) -> Response:
82
- blackhole = dns.message.make_response(self.message)
83
- blackhole.flags |= dns.flags.RA
92
+ blackhole = self.get_response_template()
84
93
  blackhole.set_rcode(dns.rcode.NXDOMAIN)
85
94
  response = Response.from_message(blackhole)
86
95
  return response
@@ -98,13 +107,53 @@ class Exchange:
98
107
 
99
108
  request: Request
100
109
  response: Optional[Response] = None
101
- response_upstream: Optional[Response] = None
110
+
102
111
 
103
112
  @classmethod
104
113
  def from_wire(cls, wire: bytes, ip: str, port: int) -> Self:
105
114
  request = Request(wire)
106
- return cls(request=request, ip=ip, port=port)
115
+ response = Response.from_message(request.get_response_template())
116
+
117
+ return cls(request=request, response=response, ip=ip, port=port)
107
118
 
108
119
  @cached_property
109
120
  def client(self):
110
121
  return f'{self.ip}:{self.port}'
122
+
123
+ @property
124
+ def question_last(self) -> RRset:
125
+ """
126
+
127
+ Contrive an RRset 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.
128
+
129
+ """
130
+ if self.response.answer:
131
+ rrset = self.response.answer
132
+ ty = self.request.type
133
+ ttl = self.request.question.ttl
134
+ rdclass = self.request.question.rdclass
135
+ name = next(iter(rrset.items.keys())).to_text()
136
+
137
+ rrset_contrived = dns.rrset.from_text(
138
+ name=name,
139
+ ttl=ttl,
140
+ rdtype=ty,
141
+ rdclass=rdclass,
142
+
143
+ )
144
+
145
+ return rrset_contrived
146
+ else:
147
+ return self.request.question # Solves the issue of digging out the name.
148
+
149
+ @property
150
+ def query_last(self) -> QueryMessage:
151
+ """
152
+
153
+ Create a query (e.g. for use by upstream) based on the last question.
154
+
155
+ """
156
+
157
+ question_last = self.question_last
158
+ query = dns.message.make_query(qname=question_last.name, rdclass=question_last.rdclass, rdtype=question_last.rdtype)
159
+ return query
@@ -1,59 +1,34 @@
1
- import socket
2
1
  from dataclasses import dataclass
3
2
 
4
3
  from fmtr.tools import logger
5
- from fmtr.tools.dns_tools.client import ClientDoH
4
+ from fmtr.tools.dns_tools import server, client
6
5
  from fmtr.tools.dns_tools.dm import Exchange
7
6
 
8
7
 
9
8
  @dataclass
10
- class ServerBasePlain:
9
+ class Proxy(server.Plain):
11
10
  """
12
11
 
13
- Base for starting a plain DNS server
12
+ Base for a DNS Proxy server (plain server) TODO: Allow subclassing of any server type.
14
13
 
15
14
  """
16
15
 
17
- host: str
18
- port: int
16
+ client: client.HTTP
19
17
 
20
- def __post_init__(self):
21
-
22
- self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
23
-
24
- def resolve(self, exchange: Exchange):
25
- raise NotImplemented
26
-
27
- def start(self):
18
+ def process_question(self, exchange: Exchange):
28
19
  """
29
20
 
30
- Listen and resolve via overridden resolve method.
21
+ Modify exchange based on initial question.
31
22
 
32
23
  """
33
- sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
34
- sock.bind((self.host, self.port))
35
- print(f"Listening on {self.host}:{self.port}")
36
- while True:
37
- data, (ip, port) = sock.recvfrom(512)
38
- exchange = Exchange.from_wire(data, ip=ip, port=port)
39
- self.resolve(exchange)
40
- sock.sendto(exchange.response.wire, (ip, port))
41
-
42
-
43
- @dataclass
44
- class ServerBaseDoHProxy(ServerBasePlain):
45
- """
46
-
47
- Base for a DNS Proxy server
48
-
49
- """
50
-
51
- client: ClientDoH
52
-
53
- def process_question(self, exchange: Exchange):
54
24
  return
55
25
 
56
26
  def process_upstream(self, exchange: Exchange):
27
+ """
28
+
29
+ Modify exchange after upstream response.
30
+
31
+ """
57
32
  return
58
33
 
59
34
  def resolve(self, exchange: Exchange):
@@ -73,17 +48,21 @@ class ServerBaseDoHProxy(ServerBasePlain):
73
48
 
74
49
  with logger.span(f'Processing question...'):
75
50
  self.process_question(exchange)
76
- if exchange.response:
51
+ if exchange.response.is_complete:
77
52
  return
78
53
 
79
54
  with logger.span(f'Making upstream request for {request.name_text}...'):
80
55
  self.client.resolve(exchange)
56
+ if exchange.response.is_complete:
57
+ return
81
58
 
82
59
  with logger.span(f'Processing upstream response...'):
83
60
  self.process_upstream(exchange)
61
+ if exchange.response.is_complete:
62
+ return
84
63
 
85
64
  if exchange.response:
86
65
  return
87
66
 
88
- exchange.response = exchange.response_upstream
67
+ exchange.response.is_complete = True
89
68
  return
@@ -0,0 +1,38 @@
1
+ import socket
2
+ from dataclasses import dataclass
3
+
4
+ from fmtr.tools.dns_tools.dm import Exchange
5
+
6
+
7
+ @dataclass
8
+ class Plain:
9
+ """
10
+
11
+ Base for starting a plain DNS server
12
+
13
+ """
14
+
15
+ host: str
16
+ port: int
17
+
18
+ def __post_init__(self):
19
+
20
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
21
+
22
+ def resolve(self, exchange: Exchange):
23
+ raise NotImplemented
24
+
25
+ def start(self):
26
+ """
27
+
28
+ Listen and resolve via overridden resolve method.
29
+
30
+ """
31
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
32
+ sock.bind((self.host, self.port))
33
+ print(f"Listening on {self.host}:{self.port}")
34
+ while True:
35
+ data, (ip, port) = sock.recvfrom(512)
36
+ exchange = Exchange.from_wire(data, ip=ip, port=port)
37
+ self.resolve(exchange)
38
+ sock.sendto(exchange.response.message.to_wire(), (ip, port))
@@ -0,0 +1 @@
1
+ 1.3.2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmtr.tools
3
- Version: 1.3.1
3
+ Version: 1.3.2
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
@@ -130,60 +130,60 @@ Requires-Dist: logfire[httpx]; extra == "http"
130
130
  Provides-Extra: setup
131
131
  Requires-Dist: setuptools; extra == "setup"
132
132
  Provides-Extra: all
133
- Requires-Dist: deepmerge; extra == "all"
134
- Requires-Dist: bokeh; extra == "all"
135
- Requires-Dist: distributed; extra == "all"
136
- Requires-Dist: pydantic; extra == "all"
137
- Requires-Dist: appdirs; extra == "all"
133
+ Requires-Dist: uvicorn[standard]; extra == "all"
134
+ Requires-Dist: openpyxl; extra == "all"
135
+ Requires-Dist: sentence_transformers; extra == "all"
136
+ Requires-Dist: peft; extra == "all"
137
+ Requires-Dist: httpx_retries; extra == "all"
138
138
  Requires-Dist: sre_yield; extra == "all"
139
- Requires-Dist: logfire; extra == "all"
139
+ Requires-Dist: faker; extra == "all"
140
140
  Requires-Dist: ollama; extra == "all"
141
- Requires-Dist: Unidecode; extra == "all"
142
- Requires-Dist: flet[all]; extra == "all"
143
- Requires-Dist: google-auth-httplib2; extra == "all"
144
- Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
145
- Requires-Dist: pydevd-pycharm; extra == "all"
146
- Requires-Dist: torchvision; extra == "all"
147
- Requires-Dist: pyyaml; extra == "all"
148
- Requires-Dist: tabulate; extra == "all"
149
- Requires-Dist: pymupdf; extra == "all"
150
- Requires-Dist: docker; extra == "all"
151
- Requires-Dist: openai; extra == "all"
152
- Requires-Dist: sentence_transformers; extra == "all"
153
- Requires-Dist: dnspython[doh]; extra == "all"
154
- Requires-Dist: regex; extra == "all"
155
- Requires-Dist: semver; extra == "all"
141
+ Requires-Dist: logfire; extra == "all"
142
+ Requires-Dist: bokeh; extra == "all"
156
143
  Requires-Dist: setuptools; extra == "all"
157
- Requires-Dist: google-auth; extra == "all"
158
- Requires-Dist: openpyxl; extra == "all"
159
- Requires-Dist: google-api-python-client; extra == "all"
144
+ Requires-Dist: dask[bag]; extra == "all"
145
+ Requires-Dist: diskcache; extra == "all"
160
146
  Requires-Dist: torchaudio; extra == "all"
161
- Requires-Dist: tinynetrc; extra == "all"
162
- Requires-Dist: httpx_retries; extra == "all"
163
- Requires-Dist: pymupdf4llm; extra == "all"
147
+ Requires-Dist: tokenizers; extra == "all"
148
+ Requires-Dist: pydantic; extra == "all"
149
+ Requires-Dist: pandas; extra == "all"
150
+ Requires-Dist: flet-video; extra == "all"
151
+ Requires-Dist: Unidecode; extra == "all"
152
+ Requires-Dist: pydantic-settings; extra == "all"
153
+ Requires-Dist: dnspython[doh]; extra == "all"
154
+ Requires-Dist: logfire[httpx]; extra == "all"
164
155
  Requires-Dist: yamlscript; extra == "all"
156
+ Requires-Dist: fastapi; extra == "all"
157
+ Requires-Dist: openai; extra == "all"
158
+ Requires-Dist: deepmerge; extra == "all"
159
+ Requires-Dist: html2text; extra == "all"
160
+ Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
161
+ Requires-Dist: contexttimer; extra == "all"
162
+ Requires-Dist: flet[all]; extra == "all"
163
+ Requires-Dist: logfire[fastapi]; extra == "all"
164
+ Requires-Dist: regex; extra == "all"
165
+ Requires-Dist: docker; extra == "all"
165
166
  Requires-Dist: filetype; extra == "all"
166
- Requires-Dist: pydantic-settings; extra == "all"
167
- Requires-Dist: flet-webview; extra == "all"
168
167
  Requires-Dist: google-auth-oauthlib; extra == "all"
168
+ Requires-Dist: tabulate; extra == "all"
169
169
  Requires-Dist: pytest-cov; extra == "all"
170
- Requires-Dist: fastapi; extra == "all"
171
- Requires-Dist: logfire[fastapi]; extra == "all"
172
- Requires-Dist: pandas; extra == "all"
173
- Requires-Dist: html2text; extra == "all"
174
- Requires-Dist: dask[bag]; extra == "all"
175
- Requires-Dist: uvicorn[standard]; extra == "all"
176
- Requires-Dist: json_repair; extra == "all"
177
- Requires-Dist: flet-video; extra == "all"
178
- Requires-Dist: httpx; extra == "all"
179
- Requires-Dist: faker; extra == "all"
180
- Requires-Dist: peft; extra == "all"
181
- Requires-Dist: tokenizers; extra == "all"
170
+ Requires-Dist: pyyaml; extra == "all"
182
171
  Requires-Dist: transformers[sentencepiece]; extra == "all"
183
- Requires-Dist: logfire[httpx]; extra == "all"
184
- Requires-Dist: contexttimer; extra == "all"
172
+ Requires-Dist: pymupdf; extra == "all"
173
+ Requires-Dist: pydevd-pycharm; extra == "all"
174
+ Requires-Dist: torchvision; extra == "all"
175
+ Requires-Dist: google-auth; extra == "all"
176
+ Requires-Dist: flet-webview; extra == "all"
185
177
  Requires-Dist: huggingface_hub; extra == "all"
186
- Requires-Dist: diskcache; extra == "all"
178
+ Requires-Dist: httpx; extra == "all"
179
+ Requires-Dist: semver; extra == "all"
180
+ Requires-Dist: json_repair; extra == "all"
181
+ Requires-Dist: tinynetrc; extra == "all"
182
+ Requires-Dist: google-auth-httplib2; extra == "all"
183
+ Requires-Dist: appdirs; extra == "all"
184
+ Requires-Dist: pymupdf4llm; extra == "all"
185
+ Requires-Dist: distributed; extra == "all"
186
+ Requires-Dist: google-api-python-client; extra == "all"
187
187
  Dynamic: author
188
188
  Dynamic: author-email
189
189
  Dynamic: description
@@ -57,6 +57,7 @@ setup.py
57
57
  ./fmtr/tools/dns_tools/__init__.py
58
58
  ./fmtr/tools/dns_tools/client.py
59
59
  ./fmtr/tools/dns_tools/dm.py
60
+ ./fmtr/tools/dns_tools/proxy.py
60
61
  ./fmtr/tools/dns_tools/server.py
61
62
  ./fmtr/tools/entrypoints/__init__.py
62
63
  ./fmtr/tools/entrypoints/cache_hfh.py
@@ -138,6 +139,7 @@ fmtr/tools/ai_tools/inference_tools.py
138
139
  fmtr/tools/dns_tools/__init__.py
139
140
  fmtr/tools/dns_tools/client.py
140
141
  fmtr/tools/dns_tools/dm.py
142
+ fmtr/tools/dns_tools/proxy.py
141
143
  fmtr/tools/dns_tools/server.py
142
144
  fmtr/tools/entrypoints/__init__.py
143
145
  fmtr/tools/entrypoints/cache_hfh.py
@@ -1,6 +0,0 @@
1
- from fmtr.tools.import_tools import MissingExtraMockModule
2
-
3
- try:
4
- from fmtr.tools.dns_tools import server, client, dm
5
- except ImportError as exception:
6
- server = client = dm = MissingExtraMockModule('dns', exception)
@@ -1 +0,0 @@
1
- 1.3.1
File without changes
File without changes
@@ -15,60 +15,60 @@ pydantic-ai[logfire,openai]
15
15
  ollama
16
16
 
17
17
  [all]
18
- deepmerge
19
- bokeh
20
- distributed
21
- pydantic
22
- appdirs
18
+ uvicorn[standard]
19
+ openpyxl
20
+ sentence_transformers
21
+ peft
22
+ httpx_retries
23
23
  sre_yield
24
- logfire
24
+ faker
25
25
  ollama
26
- Unidecode
27
- flet[all]
28
- google-auth-httplib2
29
- pydantic-ai[logfire,openai]
30
- pydevd-pycharm
31
- torchvision
32
- pyyaml
33
- tabulate
34
- pymupdf
35
- docker
36
- openai
37
- sentence_transformers
38
- dnspython[doh]
39
- regex
40
- semver
26
+ logfire
27
+ bokeh
41
28
  setuptools
42
- google-auth
43
- openpyxl
44
- google-api-python-client
29
+ dask[bag]
30
+ diskcache
45
31
  torchaudio
46
- tinynetrc
47
- httpx_retries
48
- pymupdf4llm
32
+ tokenizers
33
+ pydantic
34
+ pandas
35
+ flet-video
36
+ Unidecode
37
+ pydantic-settings
38
+ dnspython[doh]
39
+ logfire[httpx]
49
40
  yamlscript
41
+ fastapi
42
+ openai
43
+ deepmerge
44
+ html2text
45
+ pydantic-ai[logfire,openai]
46
+ contexttimer
47
+ flet[all]
48
+ logfire[fastapi]
49
+ regex
50
+ docker
50
51
  filetype
51
- pydantic-settings
52
- flet-webview
53
52
  google-auth-oauthlib
53
+ tabulate
54
54
  pytest-cov
55
- fastapi
56
- logfire[fastapi]
57
- pandas
58
- html2text
59
- dask[bag]
60
- uvicorn[standard]
61
- json_repair
62
- flet-video
63
- httpx
64
- faker
65
- peft
66
- tokenizers
55
+ pyyaml
67
56
  transformers[sentencepiece]
68
- logfire[httpx]
69
- contexttimer
57
+ pymupdf
58
+ pydevd-pycharm
59
+ torchvision
60
+ google-auth
61
+ flet-webview
70
62
  huggingface_hub
71
- diskcache
63
+ httpx
64
+ semver
65
+ json_repair
66
+ tinynetrc
67
+ google-auth-httplib2
68
+ appdirs
69
+ pymupdf4llm
70
+ distributed
71
+ google-api-python-client
72
72
 
73
73
  [api]
74
74
  fastapi
File without changes
File without changes
File without changes