svc-infra 0.1.612__py3-none-any.whl → 0.1.614__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.

Potentially problematic release.


This version of svc-infra might be problematic. Click here for more details.

@@ -46,6 +46,7 @@ def problem_response(
46
46
  code: str | None = None,
47
47
  errors: list[dict] | None = None,
48
48
  trace_id: str | None = None,
49
+ headers: dict[str, str] | None = None,
49
50
  ) -> Response:
50
51
  body: Dict[str, Any] = {
51
52
  "type": type_uri,
@@ -62,7 +63,7 @@ def problem_response(
62
63
  body["errors"] = errors
63
64
  if trace_id:
64
65
  body["trace_id"] = trace_id
65
- return JSONResponse(status_code=status, content=body, media_type=PROBLEM_MT)
66
+ return JSONResponse(status_code=status, content=body, media_type=PROBLEM_MT, headers=headers)
66
67
 
67
68
 
68
69
  def register_error_handlers(app):
@@ -104,14 +105,25 @@ def register_error_handlers(app):
104
105
  @app.exception_handler(HTTPException)
105
106
  async def handle_http_exception(request: Request, exc: HTTPException):
106
107
  trace_id = _trace_id_from_request(request)
107
- title = {401: "Unauthorized", 403: "Forbidden", 404: "Not Found"}.get(
108
- exc.status_code, "Error"
109
- )
108
+ title = {
109
+ 401: "Unauthorized",
110
+ 403: "Forbidden",
111
+ 404: "Not Found",
112
+ 429: "Too Many Requests",
113
+ }.get(exc.status_code, "Error")
110
114
  detail = (
111
115
  exc.detail
112
116
  if not IS_PROD or exc.status_code < 500
113
117
  else "Something went wrong. Please contact support."
114
118
  )
119
+ # Preserve headers set on the exception (e.g., Retry-After for rate limits)
120
+ hdrs: dict[str, str] | None = None
121
+ try:
122
+ if getattr(exc, "headers", None):
123
+ # FastAPI/Starlette exceptions store headers as a dict[str, str]
124
+ hdrs = dict(getattr(exc, "headers")) # type: ignore[arg-type]
125
+ except Exception:
126
+ hdrs = None
115
127
  return problem_response(
116
128
  status=exc.status_code,
117
129
  title=title,
@@ -119,19 +131,29 @@ def register_error_handlers(app):
119
131
  code=title.replace(" ", "_").upper(),
120
132
  instance=str(request.url),
121
133
  trace_id=trace_id,
134
+ headers=hdrs,
122
135
  )
123
136
 
124
137
  @app.exception_handler(StarletteHTTPException)
125
138
  async def handle_starlette_http_exception(request: Request, exc: StarletteHTTPException):
126
139
  trace_id = _trace_id_from_request(request)
127
- title = {401: "Unauthorized", 403: "Forbidden", 404: "Not Found"}.get(
128
- exc.status_code, "Error"
129
- )
140
+ title = {
141
+ 401: "Unauthorized",
142
+ 403: "Forbidden",
143
+ 404: "Not Found",
144
+ 429: "Too Many Requests",
145
+ }.get(exc.status_code, "Error")
130
146
  detail = (
131
147
  exc.detail
132
148
  if not IS_PROD or exc.status_code < 500
133
149
  else "Something went wrong. Please contact support."
134
150
  )
151
+ hdrs: dict[str, str] | None = None
152
+ try:
153
+ if getattr(exc, "headers", None):
154
+ hdrs = dict(getattr(exc, "headers")) # type: ignore[arg-type]
155
+ except Exception:
156
+ hdrs = None
135
157
  return problem_response(
136
158
  status=exc.status_code,
137
159
  title=title,
@@ -139,6 +161,7 @@ def register_error_handlers(app):
139
161
  code=title.replace(" ", "_").upper(),
140
162
  instance=str(request.url),
141
163
  trace_id=trace_id,
164
+ headers=hdrs,
142
165
  )
143
166
 
144
167
  @app.exception_handler(IntegrityError)
@@ -177,8 +177,16 @@ def _collect_metadata() -> list[object]:
177
177
  if name not in pkgs:
178
178
  pkgs.append(name)
179
179
 
180
+ # Only attempt bare 'models' import if it is discoverable to avoid noisy tracebacks
180
181
  if "models" not in pkgs:
181
- pkgs.append("models")
182
+ try:
183
+ spec = getattr(importlib, "util", None)
184
+ if spec is not None and getattr(spec, "find_spec", None) is not None:
185
+ if spec.find_spec("models") is not None:
186
+ pkgs.append("models")
187
+ except Exception:
188
+ # Best-effort; if discovery fails, skip adding bare 'models'
189
+ pass
182
190
 
183
191
  def _import_and_collect(modname: str):
184
192
  try:
@@ -191,9 +191,16 @@ def _collect_metadata() -> list[object]:
191
191
  if name not in pkgs:
192
192
  pkgs.append(name)
193
193
 
194
- # Always also try a bare 'models'
194
+ # Only attempt a bare 'models' import if discoverable to avoid noisy tracebacks
195
195
  if "models" not in pkgs:
196
- pkgs.append("models")
196
+ try:
197
+ spec = getattr(importlib, "util", None)
198
+ if spec is not None and getattr(spec, "find_spec", None) is not None:
199
+ if spec.find_spec("models") is not None:
200
+ pkgs.append("models")
201
+ except Exception:
202
+ # If discovery fails, skip adding bare 'models'
203
+ pass
197
204
 
198
205
  def _import_and_collect(modname: str):
199
206
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: svc-infra
3
- Version: 0.1.612
3
+ Version: 0.1.614
4
4
  Summary: Infrastructure for building and deploying prod-ready services
5
5
  License: MIT
6
6
  Keywords: fastapi,sqlalchemy,alembic,auth,infra,async,pydantic
@@ -76,7 +76,7 @@ svc_infra/api/fastapi/middleware/debug.py,sha256=H3jBKvdPkr2KHUEMGnqWBPZ0tG6Fgw-
76
76
  svc_infra/api/fastapi/middleware/errors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
77
  svc_infra/api/fastapi/middleware/errors/catchall.py,sha256=TG0W71UCDbfgLNdIaIv6mBSwZA_etMp5GquwKcAwYbI,1842
78
78
  svc_infra/api/fastapi/middleware/errors/exceptions.py,sha256=857_bdMgQugf8rb7U6ZaTZV3aiFTfBzFaUg80YUfAYE,475
79
- svc_infra/api/fastapi/middleware/errors/handlers.py,sha256=9Em_Z6PXTm9gM9ulEYwY02DqRuNxz6LLh8z5CMheruY,7128
79
+ svc_infra/api/fastapi/middleware/errors/handlers.py,sha256=pQMVs5n627vcKkDFEaUzx5wCYeUcU-h6acWzh27RIEQ,7993
80
80
  svc_infra/api/fastapi/middleware/idempotency.py,sha256=vnBQgMWzJVaF8oWgfw2ATjEKCyQifDeGPUc9z1N7ebE,5051
81
81
  svc_infra/api/fastapi/middleware/idempotency_store.py,sha256=BQN_Cq_jf_cuZRhze4EF5v0lOMQXpUWoRo7CsSTprug,5528
82
82
  svc_infra/api/fastapi/middleware/optimistic_lock.py,sha256=9lOMBI4VNIVndXnrMmgSq4qeR7xPjNR1H9d1F71M5S8,1271
@@ -202,8 +202,8 @@ svc_infra/db/sql/templates/models_schemas/entity/models.py.tmpl,sha256=cSUAfYgAT
202
202
  svc_infra/db/sql/templates/models_schemas/entity/schemas.py.tmpl,sha256=xHTXk8u2uiqmQe8XpXjGAPg8pDpdr0rLtsSOC_fo9vo,1032
203
203
  svc_infra/db/sql/templates/setup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
204
204
  svc_infra/db/sql/templates/setup/alembic.ini.tmpl,sha256=7SHSOyUq9S7XqVdKpWLJW7gIWadv58t4AIooa306gDo,771
205
- svc_infra/db/sql/templates/setup/env_async.py.tmpl,sha256=inKnexIhdnvwQ9gy-ODBgK1mdSKpzw2Q3pdTS_9AN0A,12781
206
- svc_infra/db/sql/templates/setup/env_sync.py.tmpl,sha256=SP6OocHQIfkZl9g9iV0qVgkKOgQyViURQmTVqIIicPw,14195
205
+ svc_infra/db/sql/templates/setup/env_async.py.tmpl,sha256=XZ5jcZXaYdBca5cRaY_m4Zuhz5IGekO2cqLuwiVSK8Y,13201
206
+ svc_infra/db/sql/templates/setup/env_sync.py.tmpl,sha256=Q0GbhtHj45Kt3hep27V_pFqeZ53y08lamOZiWa7p0GU,14560
207
207
  svc_infra/db/sql/templates/setup/script.py.mako.tmpl,sha256=RiEMqF6dTN0S_lSRr90w5K2VtyK0_C81kBPZ02qQDZU,588
208
208
  svc_infra/db/sql/tenant.py,sha256=erCB_TjZ62NXXIiOjsTOV_u3mM8kj0-R7gy4RFc-NrE,2779
209
209
  svc_infra/db/sql/types.py,sha256=aDcYS-lEb3Aw9nlc8D67wyS5rmE9ZOkblxPjPFbMM_0,863
@@ -292,7 +292,7 @@ svc_infra/webhooks/fastapi.py,sha256=BCNvGNxukf6dC2a4i-6en-PrjBGV19YvCWOot5lXWsA
292
292
  svc_infra/webhooks/router.py,sha256=6JvAVPMEth_xxHX-IsIOcyMgHX7g1H0OVxVXKLuMp9w,1596
293
293
  svc_infra/webhooks/service.py,sha256=hWgiJRXKBwKunJOx91C7EcLUkotDtD3Xp0RT6vj2IC0,1797
294
294
  svc_infra/webhooks/signing.py,sha256=NCwdZzmravUe7HVIK_uXK0qqf12FG-_MVsgPvOw6lsM,784
295
- svc_infra-0.1.612.dist-info/METADATA,sha256=lsaRaSaDXhaN7O1GaR_f6VkV6CTHXHU7oI9FGDnpLeo,8106
296
- svc_infra-0.1.612.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
297
- svc_infra-0.1.612.dist-info/entry_points.txt,sha256=6x_nZOsjvn6hRZsMgZLgTasaCSKCgAjsGhACe_CiP0U,48
298
- svc_infra-0.1.612.dist-info/RECORD,,
295
+ svc_infra-0.1.614.dist-info/METADATA,sha256=zY3M6rLvXJH2tE7oHwD07QN7zEMfD4yjVP0AbYrMV48,8106
296
+ svc_infra-0.1.614.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
297
+ svc_infra-0.1.614.dist-info/entry_points.txt,sha256=6x_nZOsjvn6hRZsMgZLgTasaCSKCgAjsGhACe_CiP0U,48
298
+ svc_infra-0.1.614.dist-info/RECORD,,