socketmap-sql 0.2.0__py3-none-any.whl → 0.2.1__py3-none-any.whl

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.
@@ -1,18 +1,19 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: socketmap-sql
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: An implementation of the sendmail socketmap protocol to allow an SQL database
5
5
  Author-email: Keith Gaughan <k@stereochro.me>
6
- Requires-Python: >=3.8
6
+ Requires-Python: >=3.12
7
7
  Description-Content-Type: text/x-rst
8
+ License-Expression: MIT
8
9
  Classifier: Development Status :: 4 - Beta
9
10
  Classifier: Intended Audience :: System Administrators
10
- Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Operating System :: POSIX
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Programming Language :: Python :: 3 :: Only
14
14
  Classifier: Topic :: Communications :: Email :: Mail Transport Agents
15
15
  Classifier: Topic :: Database
16
+ License-File: LICENSE
16
17
  Project-URL: Source, https://github.com/kgaughan/socketmap-sql
17
18
 
18
19
  =============
@@ -0,0 +1,6 @@
1
+ socketmapsql.py,sha256=ZcApgWGIGUZ1a81uCyhMUm-evcmrzxyHAlwjs_tpNB0,7316
2
+ socketmap_sql-0.2.1.dist-info/entry_points.txt,sha256=1N1u8xbsj9ryvD0v-HWBMxNfiCarn1Dq7pXXwjn7-8E,51
3
+ socketmap_sql-0.2.1.dist-info/licenses/LICENSE,sha256=Du-PK9kF9TBcjQfRGArxGPH9HVDUxifjzF5yFex2NKE,1070
4
+ socketmap_sql-0.2.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
5
+ socketmap_sql-0.2.1.dist-info/METADATA,sha256=iN1lE_1ACmwCwW3i1xaoczFdFsLa7lXNtVcZlvlKKOs,3229
6
+ socketmap_sql-0.2.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: flit 3.8.0
2
+ Generator: flit 3.12.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
socketmapsql.py CHANGED
@@ -9,19 +9,17 @@ import configparser
9
9
  import contextlib
10
10
  import dataclasses
11
11
  import importlib
12
- import io
13
12
  import os.path
14
13
  import re
15
14
  import select
16
15
  import subprocess
17
16
  import sys
18
- from typing import Callable, Dict, List, Mapping, Optional, Tuple, TypedDict
17
+ import typing as t
19
18
 
19
+ __version__: str = "0.2.1"
20
20
 
21
- __version__ = "0.2.0"
22
21
 
23
-
24
- Transform = Callable[[str, Mapping[str, str]], List[str]]
22
+ Transform = t.Callable[[str, t.Mapping[str, str]], list[str]]
25
23
 
26
24
 
27
25
  @dataclasses.dataclass
@@ -30,10 +28,10 @@ class TableCfg:
30
28
  transform: Transform
31
29
 
32
30
 
33
- class Cfg(TypedDict):
34
- database: Dict[str, str]
35
- misc: Dict[str, str]
36
- tables: Dict[str, TableCfg]
31
+ class Cfg(t.TypedDict):
32
+ database: dict[str, str]
33
+ misc: dict[str, str]
34
+ tables: dict[str, TableCfg]
37
35
 
38
36
 
39
37
  FUNC_REF_PATTERN = re.compile(
@@ -44,18 +42,18 @@ FUNC_REF_PATTERN = re.compile(
44
42
  (?P<object>[a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)*)
45
43
  $
46
44
  """,
47
- re.I | re.X,
45
+ re.IGNORECASE | re.VERBOSE,
48
46
  )
49
47
 
50
48
 
51
- def match(name: str) -> Tuple[str, str]:
49
+ def match(name: str) -> tuple[str, str]:
52
50
  matches = FUNC_REF_PATTERN.match(name)
53
51
  if not matches:
54
52
  raise ValueError(f"Malformed callable '{name}'")
55
53
  return matches.group("module"), matches.group("object")
56
54
 
57
55
 
58
- def resolve(module_name: str, obj_name: str) -> Callable:
56
+ def resolve(module_name: str, obj_name: str) -> t.Callable:
59
57
  """
60
58
  Resolve a named object in a module.
61
59
  """
@@ -66,10 +64,8 @@ class MalformedNetstringError(Exception):
66
64
  pass
67
65
 
68
66
 
69
- def read_netstring(fp: io.TextIOWrapper) -> Optional[str]:
70
- """
71
- Reads a single netstring.
72
- """
67
+ def read_netstring(fp: t.IO[str]) -> str | None:
68
+ """Reads a single netstring."""
73
69
  ns = ""
74
70
  while True:
75
71
  c = fp.read(1)
@@ -77,7 +73,7 @@ def read_netstring(fp: io.TextIOWrapper) -> Optional[str]:
77
73
  return None
78
74
  if c == ":":
79
75
  break
80
- if len(ns) > 10:
76
+ if len(ns) > 10: # noqa: PLR2004
81
77
  raise MalformedNetstringError
82
78
  if c == "0" and ns == "":
83
79
  # We can't allow leading zeros.
@@ -99,31 +95,31 @@ def read_netstring(fp: io.TextIOWrapper) -> Optional[str]:
99
95
  return result
100
96
 
101
97
 
102
- def write_netstring(fp: io.TextIOWrapper, response: str):
98
+ def write_netstring(fp: t.IO[str], response: str) -> None:
103
99
  fp.write(f"{len(response)}:{response},")
104
100
  fp.flush()
105
101
 
106
102
 
107
- def process_local(local_part: str, cfg: Mapping[str, str]) -> str:
103
+ def process_local(local_part: str, cfg: t.Mapping[str, str]) -> str:
108
104
  delimiter = cfg.get("recipient_delimiter", "").strip()
109
105
  if delimiter != "":
110
106
  local_part = local_part.split(delimiter, 1)[0]
111
107
  return local_part.lower()
112
108
 
113
109
 
114
- def split(arg: str, cfg: Mapping[str, str]) -> List[str]:
110
+ def split(arg: str, cfg: t.Mapping[str, str]) -> list[str]:
115
111
  parts = arg.split("@", 1)
116
112
  parts[0] = process_local(parts[0], cfg)
117
113
  parts[1] = parts[1].lower()
118
114
  return parts
119
115
 
120
116
 
121
- def parse_config(fp: io.TextIOWrapper) -> Cfg:
117
+ def parse_config(fp: t.IO[str]) -> Cfg:
122
118
  transforms = {
123
- "all": lambda arg, cfg: [arg],
124
- "lowercase": lambda arg, cfg: [arg.lower()],
119
+ "all": lambda arg, _: [arg],
120
+ "lowercase": lambda arg, _: [arg.lower()],
125
121
  "local": lambda arg, cfg: [process_local(arg.split("@", 1)[0], cfg)],
126
- "domain": lambda arg, cfg: [arg.split("@", 1)[1].lower()],
122
+ "domain": lambda arg, _: [arg.split("@", 1)[1].lower()],
127
123
  "split": split,
128
124
  }
129
125
 
@@ -157,18 +153,18 @@ def parse_config(fp: io.TextIOWrapper) -> Cfg:
157
153
  )
158
154
 
159
155
 
160
- def get_int(cfg: Mapping[str, str], key: str) -> Optional[int]:
156
+ def get_int(cfg: t.Mapping[str, str], key: str) -> int | None:
161
157
  return int(cfg[key]) if key in cfg else None
162
158
 
163
159
 
164
160
  def serve_client(
165
- fh_in: io.TextIOWrapper,
166
- fh_out: io.TextIOWrapper,
161
+ fh_in: t.IO[str],
162
+ fh_out: t.IO[str],
167
163
  conn,
168
164
  timeout: int,
169
- tables: Mapping[str, TableCfg],
170
- cfg: Mapping[str, str],
171
- ):
165
+ tables: t.Mapping[str, TableCfg],
166
+ cfg: t.Mapping[str, str],
167
+ ) -> None:
172
168
  max_requests = get_int(cfg, "max_requests")
173
169
  try:
174
170
  while True:
@@ -203,17 +199,15 @@ def serve_client(
203
199
  if result is None:
204
200
  write_netstring(fh_out, "NOTFOUND ")
205
201
  else:
206
- write_netstring(fh_out, f"OK {str(result[0])}")
202
+ write_netstring(fh_out, f"OK {result[0]!s}")
207
203
  except MalformedNetstringError:
208
204
  write_netstring(fh_out, "PERM malformed netstring")
209
205
  except Exception as exc:
210
- write_netstring(fh_out, f"PERM {str(exc)}")
206
+ write_netstring(fh_out, f"PERM {exc!s}")
211
207
 
212
208
 
213
- def connect(settings: Dict[str, str]):
214
- """
215
- Connect to a database.
216
- """
209
+ def connect(settings: dict[str, str]):
210
+ """Connect to a database."""
217
211
  driver = importlib.import_module(settings.pop("driver", "sqlite3"))
218
212
  return driver.connect(**settings)
219
213
 
@@ -240,7 +234,7 @@ def make_parser() -> argparse.ArgumentParser:
240
234
  return parser
241
235
 
242
236
 
243
- def main():
237
+ def main() -> int:
244
238
  args = make_parser().parse_args()
245
239
 
246
240
  with contextlib.closing(args.config):
@@ -261,8 +255,9 @@ def main():
261
255
  if req == ".exit":
262
256
  proc.terminate()
263
257
  break
264
- write_netstring(proc.stdin, req)
265
- print(read_netstring(proc.stdout))
258
+ if proc.stdin is not None and proc.stdout is not None:
259
+ write_netstring(proc.stdin, req)
260
+ print(read_netstring(proc.stdout))
266
261
  else:
267
262
  with contextlib.closing(connect(cfg["database"])) as conn:
268
263
  serve_client(
@@ -1,6 +0,0 @@
1
- socketmapsql.py,sha256=VJVawDQ4HcxUAAhKl4OIsxxJ3n2xVJPVYJVWr2l56-c,7305
2
- socketmap_sql-0.2.0.dist-info/entry_points.txt,sha256=1N1u8xbsj9ryvD0v-HWBMxNfiCarn1Dq7pXXwjn7-8E,51
3
- socketmap_sql-0.2.0.dist-info/LICENSE,sha256=Du-PK9kF9TBcjQfRGArxGPH9HVDUxifjzF5yFex2NKE,1070
4
- socketmap_sql-0.2.0.dist-info/WHEEL,sha256=rSgq_JpHF9fHR1lx53qwg_1-2LypZE_qmcuXbVUq948,81
5
- socketmap_sql-0.2.0.dist-info/METADATA,sha256=0HIm4bfejhQEUU40eKRe7XtGY5Z5SB6YsmD0vWLds5E,3233
6
- socketmap_sql-0.2.0.dist-info/RECORD,,