wsbuilder 1.0.0__tar.gz → 1.0.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.
Files changed (130) hide show
  1. {wsbuilder-1.0.0/src/wsbuilder.egg-info → wsbuilder-1.0.2}/PKG-INFO +1 -1
  2. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/getting-started.md +1 -0
  3. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/install.md +1 -0
  4. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/pyproject.toml +21 -1
  5. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/__init__.py +54 -54
  6. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/app.py +2 -2
  7. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/cookies.py +0 -1
  8. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/db_replicas.py +38 -39
  9. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/dns.py +1 -2
  10. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/headers.py +0 -1
  11. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/http.py +1 -2
  12. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/http3_server.py +2 -3
  13. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/http3_websocket.py +1 -2
  14. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/mdns.py +1 -2
  15. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/orm.py +5 -5
  16. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/pki.py +1 -1
  17. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/predicts.py +0 -1
  18. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/proxyi.py +1 -1
  19. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/quic/address.py +1 -1
  20. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/quic/connection.py +2 -5
  21. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/server.py +5 -5
  22. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/tasks.py +0 -1
  23. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/ws.py +1 -2
  24. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/ws_demo.py +9 -9
  25. {wsbuilder-1.0.0 → wsbuilder-1.0.2/src/wsbuilder.egg-info}/PKG-INFO +1 -1
  26. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_contributing_policy.py +0 -1
  27. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_dns.py +1 -1
  28. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_docs.py +10 -1
  29. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_documentation_files.py +0 -1
  30. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_hpack.py +0 -1
  31. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_http2.py +0 -1
  32. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_http3.py +0 -1
  33. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_http3_server.py +3 -4
  34. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_http3_streaming_udp.py +2 -4
  35. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_ia.py +8 -1
  36. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_orm_replicas.py +45 -45
  37. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_pki.py +3 -3
  38. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_pr_identity_policy.py +1 -2
  39. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_quic_crypto.py +4 -2
  40. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_tasks.py +9 -1
  41. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_ws.py +5 -6
  42. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/LICENSE +0 -0
  43. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/MANIFEST.in +0 -0
  44. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/README.md +0 -0
  45. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/404.html +0 -0
  46. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/CNAME +0 -0
  47. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/advanced.md +0 -0
  48. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/ai-predict.md +0 -0
  49. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/api-map.md +0 -0
  50. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/app-http.md +0 -0
  51. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/architecture.md +0 -0
  52. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/assets/wsbuilder-mark.svg +0 -0
  53. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/beginners.md +0 -0
  54. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/cache-security.md +0 -0
  55. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/cache.md +0 -0
  56. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/capabilities.md +0 -0
  57. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/data-orm.md +0 -0
  58. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/dns.md +0 -0
  59. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/full-project.md +0 -0
  60. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/help/index.md +0 -0
  61. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/http-versions.md +0 -0
  62. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/http.md +0 -0
  63. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/ia.md +0 -0
  64. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/index.md +0 -0
  65. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/intermediate.md +0 -0
  66. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/mdns.md +0 -0
  67. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/network-edge.md +0 -0
  68. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/observability-tasks.md +0 -0
  69. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/observability.md +0 -0
  70. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/persistence.md +0 -0
  71. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/proxy.md +0 -0
  72. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/reference/index.md +0 -0
  73. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/security.md +0 -0
  74. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/stylesheets/extra.css +0 -0
  75. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/tasks.md +0 -0
  76. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/tls-certificates.md +0 -0
  77. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/docs/websocket.md +0 -0
  78. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/examples/full-project/.gitignore +0 -0
  79. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/examples/full-project/README.md +0 -0
  80. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/examples/full-project/app.py +0 -0
  81. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/examples/full-project/pyproject.toml +0 -0
  82. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/examples/full-project/upstream.py +0 -0
  83. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/mkdocs.yml +0 -0
  84. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/setup.cfg +0 -0
  85. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/__main__.py +0 -0
  86. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/cache.py +0 -0
  87. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/caches.py +0 -0
  88. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/constants.py +0 -0
  89. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/framework.py +24 -24
  90. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/hpack.py +0 -0
  91. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/http1.py +0 -0
  92. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/http2.py +0 -0
  93. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/http3.py +0 -0
  94. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/ia.py +1 -1
  95. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/logs.py +0 -0
  96. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/metrics.py +0 -0
  97. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/qpack.py +0 -0
  98. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/quic/__init__.py +0 -0
  99. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/quic/crypto.py +0 -0
  100. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/quic/frames.py +0 -0
  101. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/quic/packet.py +0 -0
  102. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/quic/recovery.py +0 -0
  103. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/quic/tls.py +0 -0
  104. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/quic/varint.py +0 -0
  105. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder/security.py +0 -0
  106. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder.egg-info/SOURCES.txt +0 -0
  107. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder.egg-info/dependency_links.txt +0 -0
  108. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder.egg-info/entry_points.txt +0 -0
  109. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder.egg-info/requires.txt +0 -0
  110. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/src/wsbuilder.egg-info/top_level.txt +0 -0
  111. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_app_threads.py +0 -0
  112. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_cache.py +0 -0
  113. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_caches.py +0 -0
  114. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_demo_monitor.py +0 -0
  115. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_headers_cookies.py +0 -0
  116. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_http.py +0 -0
  117. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_http1.py +0 -0
  118. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_http3_streaming.py +0 -0
  119. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_logs.py +0 -0
  120. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_mdns.py +0 -0
  121. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_metrics.py +0 -0
  122. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_orm.py +0 -0
  123. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_predicts.py +0 -0
  124. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_proxyi.py +0 -0
  125. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_quic_address.py +0 -0
  126. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_quic_protocol.py +0 -0
  127. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_quic_recovery.py +0 -0
  128. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_quic_tls.py +0 -0
  129. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_security.py +0 -0
  130. {wsbuilder-1.0.0 → wsbuilder-1.0.2}/tests/test_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wsbuilder
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: Lightweight HTTP + WebSocket framework for Python.
5
5
  Author: wsbuilder Authors
6
6
  License-Expression: MIT
@@ -73,6 +73,7 @@ app.enable_docs()
73
73
 
74
74
  ```bash
75
75
  PYTHONPATH=src pytest -q
76
+ ruff check src tests examples
76
77
  mkdocs build --strict
77
78
  python -m wsbuilder --host 0.0.0.0 --port 8765
78
79
  ```
@@ -155,6 +155,7 @@ Validar el paquete:
155
155
 
156
156
  ```bash
157
157
  PYTHONPATH=src pytest -q
158
+ ruff check src tests examples
158
159
  ```
159
160
 
160
161
  Validar la documentacion:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "wsbuilder"
7
- version = "1.0.0"
7
+ version = "1.0.2"
8
8
  description = "Lightweight HTTP + WebSocket framework for Python."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -49,3 +49,23 @@ package-dir = {"" = "src"}
49
49
  [tool.setuptools.packages.find]
50
50
  where = ["src"]
51
51
  include = ["wsbuilder*"]
52
+
53
+ [tool.ruff]
54
+ target-version = "py311"
55
+ line-length = 100
56
+ src = ["src", "tests"]
57
+
58
+ [tool.ruff.lint]
59
+ select = ["E", "F", "W", "I", "B", "UP"]
60
+ ignore = [
61
+ "E501", # long literals and protocol tables read better unwrapped
62
+ "B905", # zip() over equal-length vectors; strict= would change behaviour
63
+ "UP031", # printf-style formatting is used deliberately in log templates
64
+ ]
65
+
66
+ [tool.ruff.lint.per-file-ignores]
67
+ # Test closures defined in loops run and are joined within the same iteration.
68
+ "tests/*" = ["B023"]
69
+
70
+ [tool.ruff.lint.isort]
71
+ known-first-party = ["wsbuilder"]
@@ -1,19 +1,48 @@
1
1
  """wsbuilder: mini framework HTTP + WebSocket."""
2
2
 
3
+ from .cache import Cache, SQLiteMemoryCache, install_cache
4
+ from .caches import GlobalCacheRule, ViewResponseCache, install_caches
5
+ from .cookies import build_set_cookie, get_cookie, parse_cookie_header
6
+ from .db_replicas import (
7
+ DatabaseReplica,
8
+ DatabaseReplicaPool,
9
+ OptimizedDatabase,
10
+ SQLite3OptimizationConfig,
11
+ )
12
+ from .dns import LocalDNSServer
3
13
  from .framework import (
4
- App,
5
- HTTPServer,
6
- Request,
7
- Response,
8
- Route,
9
- Router,
10
- WebSocket,
14
+ BALANCING_BEST,
15
+ BALANCING_CONSISTENT_HASH,
16
+ BALANCING_FIRST_AVAILABLE,
17
+ BALANCING_IP_HASH,
18
+ BALANCING_LEAST_BYTES_IN,
19
+ BALANCING_LEAST_BYTES_OUT,
20
+ BALANCING_LEAST_CONNECTIONS,
21
+ BALANCING_LEAST_REQUESTS,
22
+ BALANCING_LEAST_RESPONSE_TIME,
23
+ BALANCING_POWER_OF_TWO_CHOICES,
24
+ BALANCING_RANDOM,
25
+ BALANCING_ROUND_ROBIN,
26
+ BALANCING_WEIGHTED_ROUND_ROBIN,
27
+ SUPPORTED_BALANCING_STRATEGIES,
11
28
  TASK_CANCELLED,
12
29
  TASK_COMPLETED,
13
30
  TASK_FAILED,
14
31
  TASK_PENDING,
15
32
  TASK_REJECTED,
16
33
  TASK_RUNNING,
34
+ App,
35
+ HTTPServer,
36
+ ProxyI,
37
+ ProxyMetricsBucket,
38
+ ProxyRouteBuilder,
39
+ ProxyRule,
40
+ ProxyTarget,
41
+ Request,
42
+ Response,
43
+ Route,
44
+ Router,
45
+ RunningStats,
17
46
  TaskCancelledError,
18
47
  TaskClosedError,
19
48
  TaskContext,
@@ -21,44 +50,31 @@ from .framework import (
21
50
  TaskHandle,
22
51
  TaskManager,
23
52
  TaskRejectedError,
24
- ProxyI,
25
- proxyi,
26
- ProxyRule,
27
- ProxyRouteBuilder,
28
- ProxyTarget,
29
- ProxyMetricsBucket,
30
- RunningStats,
53
+ WebSocket,
54
+ handshake_websocket_with_options,
31
55
  install_proxyi,
32
56
  normalize_balance_mode,
33
57
  normalize_target,
34
- SUPPORTED_BALANCING_STRATEGIES,
35
- BALANCING_ROUND_ROBIN,
36
- BALANCING_WEIGHTED_ROUND_ROBIN,
37
- BALANCING_RANDOM,
38
- BALANCING_LEAST_CONNECTIONS,
39
- BALANCING_LEAST_RESPONSE_TIME,
40
- BALANCING_LEAST_REQUESTS,
41
- BALANCING_LEAST_BYTES_IN,
42
- BALANCING_LEAST_BYTES_OUT,
43
- BALANCING_IP_HASH,
44
- BALANCING_CONSISTENT_HASH,
45
- BALANCING_FIRST_AVAILABLE,
46
- BALANCING_POWER_OF_TWO_CHOICES,
47
- BALANCING_BEST,
48
58
  parse_close_payload,
49
59
  parse_query_string,
50
- handshake_websocket_with_options,
60
+ proxyi,
61
+ )
62
+ from .headers import get_header, has_header, normalize_header_name, set_header
63
+ from .ia import (
64
+ DataSet,
65
+ DataSummary,
66
+ DenseLayer,
67
+ ErrorSummary,
68
+ NeuralNetwork,
69
+ describe_data,
70
+ evaluate_errors,
71
+ submit_training_task,
51
72
  )
52
- from .metrics import AppMetrics, install_metrics
53
73
  from .logs import NDJSONLog, install_logs
54
- from .cache import Cache, SQLiteMemoryCache, install_cache
55
- from .caches import GlobalCacheRule, ViewResponseCache, install_caches
56
- from .security import ACLRule, SecurityDecision, SecurityPolicy, install_security
57
- from .dns import LocalDNSServer
58
74
  from .mdns import LocalMDNSServer, MDNSService, discover_mdns_services
59
- from .cookies import build_set_cookie, get_cookie, parse_cookie_header
60
- from .headers import get_header, has_header, normalize_header_name, set_header
75
+ from .metrics import AppMetrics, install_metrics
61
76
  from .orm import (
77
+ SQL,
62
78
  BlobField,
63
79
  BooleanField,
64
80
  Database,
@@ -69,7 +85,6 @@ from .orm import (
69
85
  Model,
70
86
  QuerySet,
71
87
  RealField,
72
- SQL,
73
88
  TextField,
74
89
  Transaction,
75
90
  create_tables,
@@ -77,23 +92,8 @@ from .orm import (
77
92
  quote_identifier,
78
93
  validate_identifier,
79
94
  )
80
- from .db_replicas import (
81
- DatabaseReplica,
82
- DatabaseReplicaPool,
83
- OptimizedDatabase,
84
- SQLite3OptimizationConfig,
85
- )
86
- from .ia import (
87
- DataSet,
88
- DataSummary,
89
- DenseLayer,
90
- ErrorSummary,
91
- NeuralNetwork,
92
- describe_data,
93
- evaluate_errors,
94
- submit_training_task,
95
- )
96
95
  from .predicts import Predictor
96
+ from .security import ACLRule, SecurityDecision, SecurityPolicy, install_security
97
97
 
98
98
  #: Names served from wsbuilder.pki, which needs the optional `tls` extra.
99
99
  _PKI_EXPORTS = frozenset(
@@ -126,7 +126,7 @@ def __getattr__(name):
126
126
  def __dir__():
127
127
  return sorted(set(globals()) | _PKI_EXPORTS)
128
128
 
129
- __version__ = "1.0.0"
129
+ __version__ = "1.0.2"
130
130
 
131
131
  __all__ = [
132
132
  "App",
@@ -431,7 +431,7 @@ class App:
431
431
 
432
432
  def _sign_thread_cookie(self, route_path, thread_id, issued_at=None):
433
433
  issued = int(time.time() if issued_at is None else issued_at)
434
- payload = f"{route_path}|{thread_id}|{issued}".encode("utf-8")
434
+ payload = f"{route_path}|{thread_id}|{issued}".encode()
435
435
  sig = hmac.new(
436
436
  self._thread_cookie_secret,
437
437
  payload,
@@ -467,7 +467,7 @@ class App:
467
467
  ttl = max(0.0, float(ttl_seconds or 0.0))
468
468
  if issued > now + 60 or (ttl > 0 and now - issued > ttl):
469
469
  return ""
470
- payload = f"{route_path}|{normalized}|{issued}".encode("utf-8")
470
+ payload = f"{route_path}|{normalized}|{issued}".encode()
471
471
  expected = hmac.new(
472
472
  self._thread_cookie_secret,
473
473
  payload,
@@ -2,7 +2,6 @@ import re
2
2
 
3
3
  from .headers import get_header
4
4
 
5
-
6
5
  _COOKIE_NAME_RE = re.compile(r"^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$")
7
6
  _COOKIE_VALUE_RE = re.compile(r'^[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]*$')
8
7
  _SAME_SITE_VALUES = {
@@ -4,47 +4,46 @@ from __future__ import annotations
4
4
 
5
5
  import sqlite3
6
6
  import threading
7
- from typing import Optional
8
7
  from dataclasses import dataclass
9
8
 
10
9
 
11
10
  @dataclass
12
11
  class SQLite3OptimizationConfig:
13
12
  """Configuration for SQLite3 optimizations."""
14
-
13
+
15
14
  # WAL mode - enables multiple readers
16
15
  journal_mode: str = "WAL"
17
-
16
+
18
17
  # Memory budget in KiB used with PRAGMA cache_size < 0.
19
18
  cache_size: int = 10000
20
-
19
+
21
20
  # Synchronization level (FULL=safer, NORMAL=faster)
22
21
  synchronous: str = "NORMAL"
23
-
22
+
24
23
  # Temp store location
25
24
  temp_store: str = "MEMORY"
26
-
25
+
27
26
  # Query timeout
28
27
  busy_timeout: float = 10.0
29
-
28
+
30
29
  # Enable memory-mapped I/O
31
30
  mmap_size: int = 30000000 # 30MB
32
-
31
+
33
32
  # Optimization settings
34
33
  optimize: bool = True
35
34
  automatic_indexing: bool = True
36
35
  query_only: bool = False
37
-
36
+
38
37
  # Foreign keys enforcement
39
38
  foreign_keys: bool = True
40
-
39
+
41
40
  # Checkpoint settings for WAL
42
41
  wal_autocheckpoint: int = 1000 # Pages before auto-checkpoint
43
42
 
44
43
 
45
44
  class DatabaseReplica:
46
45
  """Represents a read-only replica of the database."""
47
-
46
+
48
47
  def __init__(
49
48
  self,
50
49
  db_path: str,
@@ -55,9 +54,9 @@ class DatabaseReplica:
55
54
  self.db_path = db_path
56
55
  self.timeout = timeout
57
56
  self.detect_types = detect_types
58
- self._conn: Optional[sqlite3.Connection] = None
57
+ self._conn: sqlite3.Connection | None = None
59
58
  self._lock = threading.RLock()
60
-
59
+
61
60
  def connect(self) -> sqlite3.Connection:
62
61
  """Get or create read-only connection."""
63
62
  with self._lock:
@@ -73,14 +72,14 @@ class DatabaseReplica:
73
72
  # Read-only optimization
74
73
  self._conn.execute("PRAGMA query_only = ON")
75
74
  return self._conn
76
-
75
+
77
76
  def close(self):
78
77
  """Close the replica connection."""
79
78
  with self._lock:
80
79
  if self._conn:
81
80
  self._conn.close()
82
81
  self._conn = None
83
-
82
+
84
83
  def execute(self, sql: str, params=None):
85
84
  """Execute a read-only query."""
86
85
  conn = self.connect()
@@ -88,17 +87,17 @@ class DatabaseReplica:
88
87
  params = ()
89
88
  with self._lock:
90
89
  return conn.execute(sql, tuple(params))
91
-
90
+
92
91
  def fetchone(self, sql: str, params=None):
93
92
  """Fetch one result."""
94
93
  with self._lock:
95
94
  return self.execute(sql, params).fetchone()
96
-
95
+
97
96
  def fetchall(self, sql: str, params=None):
98
97
  """Fetch all results."""
99
98
  with self._lock:
100
99
  return self.execute(sql, params).fetchall()
101
-
100
+
102
101
  def scalar(self, sql: str, params=None, default=None):
103
102
  """Fetch a scalar value."""
104
103
  with self._lock:
@@ -110,7 +109,7 @@ class DatabaseReplica:
110
109
 
111
110
  class DatabaseReplicaPool:
112
111
  """Pool of read-only database replicas with round-robin distribution."""
113
-
112
+
114
113
  def __init__(
115
114
  self,
116
115
  db_path: str,
@@ -125,7 +124,7 @@ class DatabaseReplicaPool:
125
124
  self.replica_count = replica_count
126
125
  self.timeout = timeout
127
126
  self.detect_types = detect_types
128
-
127
+
129
128
  self._replicas = [
130
129
  DatabaseReplica(
131
130
  db_path,
@@ -136,34 +135,34 @@ class DatabaseReplicaPool:
136
135
  ]
137
136
  self._current_index = 0
138
137
  self._lock = threading.RLock()
139
-
138
+
140
139
  def get_replica(self) -> DatabaseReplica:
141
140
  """Get the next replica in round-robin fashion."""
142
141
  with self._lock:
143
142
  replica = self._replicas[self._current_index]
144
143
  self._current_index = (self._current_index + 1) % len(self._replicas)
145
144
  return replica
146
-
145
+
147
146
  def execute(self, sql: str, params=None):
148
147
  """Execute query on a replica."""
149
148
  replica = self.get_replica()
150
149
  return replica.execute(sql, params)
151
-
150
+
152
151
  def fetchone(self, sql: str, params=None):
153
152
  """Fetch one result from a replica."""
154
153
  replica = self.get_replica()
155
154
  return replica.fetchone(sql, params)
156
-
155
+
157
156
  def fetchall(self, sql: str, params=None):
158
157
  """Fetch all results from a replica."""
159
158
  replica = self.get_replica()
160
159
  return replica.fetchall(sql, params)
161
-
160
+
162
161
  def scalar(self, sql: str, params=None, default=None):
163
162
  """Fetch scalar from a replica."""
164
163
  replica = self.get_replica()
165
164
  return replica.scalar(sql, params, default)
166
-
165
+
167
166
  def close_all(self):
168
167
  """Close all replica connections."""
169
168
  for replica in self._replicas:
@@ -179,7 +178,7 @@ def _pragma_cache_size_from_kib(size_kib: int) -> int:
179
178
 
180
179
  class OptimizedDatabase:
181
180
  """SQLite3 database with built-in optimizations."""
182
-
181
+
183
182
  def __init__(
184
183
  self,
185
184
  path: str = ":memory:",
@@ -198,7 +197,7 @@ class OptimizedDatabase:
198
197
  self._tx_depth = threading.local()
199
198
  self._savepoint_sequence = 0
200
199
  self.optimization_config = optimization_config or SQLite3OptimizationConfig()
201
-
200
+
202
201
  # Connection setup
203
202
  if shared_cache:
204
203
  dsn = "file::memory:?cache=shared" if path == ":memory:" else path
@@ -216,14 +215,14 @@ class OptimizedDatabase:
216
215
  timeout=timeout,
217
216
  detect_types=detect_types,
218
217
  )
219
-
218
+
220
219
  self._conn.row_factory = sqlite3.Row
221
-
220
+
222
221
  # Apply SQLite3 optimizations
223
222
  self._apply_optimizations(timeout, pragmas)
224
-
223
+
225
224
  # Setup read replicas if enabled
226
- self._replicas: Optional[DatabaseReplicaPool] = None
225
+ self._replicas: DatabaseReplicaPool | None = None
227
226
  if enable_replicas and path != ":memory:":
228
227
  self._replicas = DatabaseReplicaPool(
229
228
  path,
@@ -231,11 +230,11 @@ class OptimizedDatabase:
231
230
  timeout=timeout,
232
231
  detect_types=detect_types,
233
232
  )
234
-
233
+
235
234
  def _apply_optimizations(self, timeout: float, custom_pragmas: dict | None):
236
235
  """Apply SQLite3 optimization pragmas."""
237
236
  config = self.optimization_config
238
-
237
+
239
238
  base_pragmas = {
240
239
  "journal_mode": config.journal_mode,
241
240
  "cache_size": _pragma_cache_size_from_kib(config.cache_size),
@@ -247,25 +246,25 @@ class OptimizedDatabase:
247
246
  "query_only": 1 if config.query_only else 0,
248
247
  "automatic_index": 1 if config.automatic_indexing else 0,
249
248
  }
250
-
249
+
251
250
  if custom_pragmas:
252
251
  base_pragmas.update(custom_pragmas)
253
-
252
+
254
253
  with self._lock:
255
254
  for key, value in base_pragmas.items():
256
255
  self._log(f"PRAGMA {key}={value}")
257
256
  self._conn.execute(f"PRAGMA {key}={value}")
258
-
257
+
259
258
  # WAL checkpoint settings
260
259
  if config.journal_mode.upper() == "WAL":
261
260
  self._log(f"PRAGMA wal_autocheckpoint={config.wal_autocheckpoint}")
262
261
  self._conn.execute(f"PRAGMA wal_autocheckpoint={config.wal_autocheckpoint}")
263
-
262
+
264
263
  # Run optimization if enabled
265
264
  if config.optimize:
266
265
  self._log("PRAGMA optimize")
267
266
  self._conn.execute("PRAGMA optimize")
268
-
267
+
269
268
  def _log(self, *parts):
270
269
  if self.debug:
271
270
  print("[DB]", *parts)
@@ -2,7 +2,6 @@ import base64
2
2
  import socket
3
3
  import threading
4
4
 
5
-
6
5
  QTYPE_A = 1
7
6
  QTYPE_NS = 2
8
7
  QTYPE_CNAME = 5
@@ -1028,7 +1027,7 @@ class LocalDNSServer:
1028
1027
  while self._running.is_set():
1029
1028
  try:
1030
1029
  data, addr = self._sock.recvfrom(4096)
1031
- except socket.timeout:
1030
+ except TimeoutError:
1032
1031
  continue
1033
1032
  except OSError:
1034
1033
  if self._running.is_set():
@@ -1,6 +1,5 @@
1
1
  import re
2
2
 
3
-
4
3
  _HEADER_NAME_RE = re.compile(r"^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$")
5
4
 
6
5
 
@@ -5,7 +5,6 @@ from urllib.parse import parse_qsl
5
5
  from .constants import STATUS_MESSAGES
6
6
  from .headers import validate_header_name, validate_header_value
7
7
 
8
-
9
8
  MAX_QUERY_FIELDS = 1024
10
9
  HTTP_0_9 = "HTTP/0.9"
11
10
  HTTP_1_1 = "HTTP/1.1"
@@ -281,7 +280,7 @@ def send_http_response(conn, response, *, send_body=True, keep_alive=False, vers
281
280
  use_chunked = "chunked" in lowermap.get("transfer-encoding", "").lower()
282
281
  for chunk in _iter_stream_chunks(response.stream):
283
282
  if use_chunked:
284
- conn.sendall(f"{len(chunk):X}\r\n".encode("utf-8"))
283
+ conn.sendall(f"{len(chunk):X}\r\n".encode())
285
284
  conn.sendall(chunk)
286
285
  conn.sendall(b"\r\n")
287
286
  else:
@@ -19,7 +19,7 @@ from cryptography.hazmat.primitives import serialization
19
19
  from . import http3
20
20
  from .http3_websocket import Http3WebSocketStream
21
21
  from .quic.address import AddressValidator
22
- from .quic.connection import MAX_DATAGRAM_SIZE, QuicConnection
22
+ from .quic.connection import QuicConnection
23
23
  from .quic.crypto import retry_integrity_tag
24
24
  from .quic.packet import (
25
25
  PACKET_INITIAL,
@@ -28,7 +28,6 @@ from .quic.packet import (
28
28
  new_connection_id,
29
29
  parse_long_header,
30
30
  )
31
- from .quic.varint import decode_varint
32
31
 
33
32
  DEFAULT_MAX_CONNECTIONS = 256
34
33
 
@@ -106,7 +105,7 @@ class Http3Server:
106
105
  while not self._stop.is_set():
107
106
  try:
108
107
  datagram, address = sock.recvfrom(65535)
109
- except socket.timeout:
108
+ except TimeoutError:
110
109
  # The idle gap is where loss timers get a chance to fire;
111
110
  # without this a probe would wait for the next datagram,
112
111
  # which on a lossy path may never come.
@@ -8,7 +8,6 @@ those. Bytes the peer sends are pushed in by the thread that owns the UDP
8
8
  socket; bytes this side sends are queued for that same thread to packetize.
9
9
  """
10
10
 
11
- import socket
12
11
  import threading
13
12
  import time
14
13
 
@@ -60,7 +59,7 @@ class Http3WebSocketStream:
60
59
  if deadline is not None:
61
60
  remaining = deadline - time.monotonic()
62
61
  if remaining <= 0:
63
- raise socket.timeout("timed out")
62
+ raise TimeoutError("timed out")
64
63
  self._cond.wait(remaining)
65
64
  if self._buffer:
66
65
  chunk = bytes(self._buffer[:n])
@@ -7,10 +7,9 @@ import re
7
7
  import socket
8
8
  import threading
9
9
  import time
10
+ from collections.abc import Mapping
10
11
  from dataclasses import dataclass
11
12
  from types import MappingProxyType
12
- from typing import Mapping
13
-
14
13
 
15
14
  DEFAULT_SERVICE_TYPE = "_http._tcp.local."
16
15
 
@@ -316,29 +316,29 @@ class Database:
316
316
  )
317
317
 
318
318
  self._conn.row_factory = sqlite3.Row
319
-
319
+
320
320
  # Default optimized pragmas
321
321
  base_pragmas = {
322
322
  "foreign_keys": 1,
323
323
  "busy_timeout": int(timeout * 1000),
324
324
  }
325
-
325
+
326
326
  # Add SQLite3 optimizations
327
327
  if enable_wal:
328
328
  base_pragmas["journal_mode"] = "WAL"
329
329
  base_pragmas["wal_autocheckpoint"] = 1000
330
-
330
+
331
331
  base_pragmas["cache_size"] = _pragma_cache_size_from_mb(cache_size_mb)
332
332
  base_pragmas["synchronous"] = "NORMAL"
333
333
  base_pragmas["temp_store"] = "MEMORY"
334
334
  base_pragmas["mmap_size"] = 30000000
335
335
  base_pragmas["automatic_index"] = 1
336
-
336
+
337
337
  if pragmas:
338
338
  base_pragmas.update(pragmas)
339
339
  for key, value in base_pragmas.items():
340
340
  self.set_pragma(key, value)
341
-
341
+
342
342
  # Enable read replicas if needed
343
343
  if enable_replicas and path != ":memory:":
344
344
  from . import db_replicas
@@ -32,7 +32,7 @@ from cryptography.hazmat.primitives import hashes, serialization
32
32
  from cryptography.hazmat.primitives.asymmetric import ec, rsa
33
33
  from cryptography.x509.oid import ExtendedKeyUsageOID, NameOID
34
34
 
35
- UTC = _datetime.timezone.utc
35
+ UTC = _datetime.UTC
36
36
 
37
37
  DEFAULT_CA_VALID_DAYS = 3650
38
38
  DEFAULT_LEAF_VALID_DAYS = 397
@@ -18,7 +18,6 @@ from __future__ import annotations
18
18
 
19
19
  import math
20
20
 
21
-
22
21
  _RANK_TOLERANCE = 1e-12
23
22
 
24
23
 
@@ -839,7 +839,7 @@ class ProxyRule:
839
839
  return rows[0][4]
840
840
 
841
841
  def _hash_score(self, key, target):
842
- raw = f"{key}|{target.url}|{target.name}".encode("utf-8")
842
+ raw = f"{key}|{target.url}|{target.name}".encode()
843
843
  digest = hashlib.sha256(raw).digest()
844
844
  value = int.from_bytes(digest[:8], byteorder="big", signed=False)
845
845
  weight = max(1, int(getattr(target, "weight", 1) or 1))
@@ -44,7 +44,7 @@ class AddressValidator:
44
44
  @staticmethod
45
45
  def _address_bytes(address):
46
46
  host, port = address
47
- return f"{host}|{int(port)}".encode("utf-8")
47
+ return f"{host}|{int(port)}".encode()
48
48
 
49
49
  def issue(self, address, original_destination_cid):
50
50
  """Mint a token tied to this address and the client's first id."""