svc-infra 0.1.613__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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: svc-infra
3
- Version: 0.1.613
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
@@ -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.613.dist-info/METADATA,sha256=3sERXZccSqWjWt03Ceu0z12Q36BWFahfFlaiV6yom0U,8106
296
- svc_infra-0.1.613.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
297
- svc_infra-0.1.613.dist-info/entry_points.txt,sha256=6x_nZOsjvn6hRZsMgZLgTasaCSKCgAjsGhACe_CiP0U,48
298
- svc_infra-0.1.613.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,,