grpc-server-kit 0.1.1__tar.gz → 0.2.1__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 (60) hide show
  1. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/CHANGELOG.md +18 -0
  2. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/PKG-INFO +2 -1
  3. grpc_server_kit-0.2.1/grpc_server_kit/__version__.py +1 -0
  4. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/interceptors/__init__.py +6 -2
  5. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/interceptors/exception_handler.py +8 -4
  6. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/settings.py +13 -0
  7. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/pyproject.toml +1 -0
  8. grpc_server_kit-0.1.1/grpc_server_kit/__version__.py +0 -1
  9. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/.gitignore +0 -0
  10. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/LICENSE +0 -0
  11. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/README.md +0 -0
  12. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/__init__.py +0 -0
  13. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/__init__.py +0 -0
  14. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/app.py +0 -0
  15. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/builder.py +0 -0
  16. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/dishka/__init__.py +0 -0
  17. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/dishka/bundle.py +0 -0
  18. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/dishka/interceptors.py +0 -0
  19. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/dishka/metrics.py +0 -0
  20. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/dishka/observability.py +0 -0
  21. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/dishka/server.py +0 -0
  22. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/health/__init__.py +0 -0
  23. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/health/cache.py +0 -0
  24. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/health/checkers/__init__.py +0 -0
  25. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/health/checkers/postgres.py +0 -0
  26. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/health/checkers/redis.py +0 -0
  27. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/health/checkers/utils.py +0 -0
  28. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/health/orchestrator.py +0 -0
  29. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/health/protocols.py +0 -0
  30. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/health/servicer.py +0 -0
  31. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/interceptors/base.py +0 -0
  32. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/interceptors/context.py +0 -0
  33. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/interceptors/metadata.py +0 -0
  34. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/interceptors/metrics.py +0 -0
  35. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/interceptors/protocols.py +0 -0
  36. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/interceptors/request_logger.py +0 -0
  37. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/interceptors/sentry.py +0 -0
  38. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/interceptors/tracing.py +0 -0
  39. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/observability/__init__.py +0 -0
  40. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/observability/tracing.py +0 -0
  41. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/runtime.py +0 -0
  42. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/aio/server.py +0 -0
  43. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/config.py +0 -0
  44. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/constants.py +0 -0
  45. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/credentials.py +0 -0
  46. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/dishka/__init__.py +0 -0
  47. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/dishka/settings.py +0 -0
  48. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/interceptors/__init__.py +0 -0
  49. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/interceptors/constants.py +0 -0
  50. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/interceptors/utils.py +0 -0
  51. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/observability/__init__.py +0 -0
  52. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/observability/metrics.py +0 -0
  53. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/observability/naming.py +0 -0
  54. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/observability/protocols.py +0 -0
  55. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/observability/sentry.py +0 -0
  56. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/options.py +0 -0
  57. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/protocols.py +0 -0
  58. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/py.typed +0 -0
  59. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/server.py +0 -0
  60. {grpc_server_kit-0.1.1 → grpc_server_kit-0.2.1}/grpc_server_kit/signals.py +0 -0
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.1](https://github.com/bedrock-python/grpc-server-kit/compare/grpc-server-kit-v0.2.0...grpc-server-kit-v0.2.1) (2026-10-16)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * map a failed outbound RPC instead of passing its status to the caller ([#50](https://github.com/bedrock-python/grpc-server-kit/issues/50)) ([232b190](https://github.com/bedrock-python/grpc-server-kit/commit/232b19076a083774dc52fbf01f1cc7809de04a98)), closes [#48](https://github.com/bedrock-python/grpc-server-kit/issues/48)
9
+
10
+ ## [0.2.0](https://github.com/bedrock-python/grpc-server-kit/compare/grpc-server-kit-v0.1.1...grpc-server-kit-v0.2.0) (2026-09-14)
11
+
12
+
13
+ ### ⚠ BREAKING CHANGES
14
+
15
+ * `BaseGrpcServerSettings(port=0)` now raises `ValidationError`; pass `allow_ephemeral_port=True` (or `GRPC__ALLOW_EPHEMERAL_PORT=true`) to bind an ephemeral port on purpose.
16
+
17
+ ### Features
18
+
19
+ * BaseGrpcServerSettings rejects port=0 unless allow_ephemeral_port=True ([4a450e4](https://github.com/bedrock-python/grpc-server-kit/commit/4a450e4dbc7ffe0a7d5fd107e9b2d0e7e2c2d279)), closes [#24](https://github.com/bedrock-python/grpc-server-kit/issues/24)
20
+
3
21
  ## [0.1.1](https://github.com/bedrock-python/grpc-server-kit/compare/grpc-server-kit-v0.1.0...grpc-server-kit-v0.1.1) (2026-09-06)
4
22
 
5
23
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: grpc-server-kit
3
- Version: 0.1.1
3
+ Version: 0.2.1
4
4
  Summary: Batteries-optional async gRPC server toolkit (app facade, builder, TLS, lifecycle, health, streaming-aware interceptors, observability)
5
5
  Project-URL: Repository, https://github.com/bedrock-python/grpc-server-kit
6
6
  Project-URL: Documentation, https://bedrock-python.github.io/grpc-server-kit/
@@ -217,6 +217,7 @@ Classifier: License :: OSI Approved :: Apache Software License
217
217
  Classifier: Programming Language :: Python :: 3
218
218
  Classifier: Programming Language :: Python :: 3.12
219
219
  Classifier: Programming Language :: Python :: 3.13
220
+ Classifier: Programming Language :: Python :: 3.14
220
221
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
221
222
  Classifier: Typing :: Typed
222
223
  Requires-Python: >=3.12
@@ -0,0 +1 @@
1
+ __version__ = "0.2.1"
@@ -10,8 +10,12 @@ Canonical execution order (outermost first):
10
10
  2. ``AsyncContextInterceptor`` (extract metadata into context vars)
11
11
  3. ``AsyncRequestLoggerInterceptor`` (request/response logging)
12
12
  4. ``AsyncTracingInterceptor`` (tracing span)
13
- 5. ``AsyncSentryInterceptor`` (error tracking)
14
- 6. ``AsyncExceptionHandlerInterceptor`` (exception → gRPC status mapping)
13
+ 5. ``AsyncExceptionHandlerInterceptor`` (exception → gRPC status mapping)
14
+ 6. ``AsyncSentryInterceptor`` (error tracking)
15
+
16
+ Sentry sits inside the exception handler so it sees the original exception;
17
+ outside, it would only see the ``grpc.aio.AbortError`` the handler turned it
18
+ into, and it never captures those.
15
19
  """
16
20
 
17
21
  from .base import AsyncServerInterceptor, RpcCall, split_method_name
@@ -97,13 +97,19 @@ class AsyncExceptionHandlerInterceptor(AsyncServerInterceptor):
97
97
  """Interceptor that catches exceptions and converts them to gRPC errors.
98
98
 
99
99
  Rules:
100
- - ``grpc.aio.AbortError`` (the handler already aborted with its own status) and
101
- ``grpc.RpcError`` are re-raised as-is.
100
+ - ``grpc.aio.AbortError`` (the handler already aborted with its own status) is
101
+ re-raised as-is.
102
102
  - ``CancelledError`` / ``KeyboardInterrupt`` / ``SystemExit`` are re-raised.
103
103
  - Any other exception is mapped to a ``grpc.StatusCode`` (via the MRO) and the request
104
104
  is aborted with details from the ``detail_factory``. Unmapped exceptions default to
105
105
  ``INTERNAL`` with a safe message (no internal details leaked).
106
106
 
107
+ A ``grpc.RpcError`` raised by an outbound call the handler made (``grpc.aio.AioRpcError``)
108
+ is one of those "other" exceptions: its status and details belong to the downstream
109
+ service, so it is mapped like anything else (``INTERNAL`` by default) instead of reaching
110
+ the caller as ``UNKNOWN`` with the downstream details. Map ``grpc.RpcError`` in
111
+ ``error_status_map`` to report downstream failures with another status.
112
+
107
113
  Covers streaming RPCs: exceptions raised while producing a response stream are
108
114
  mapped and aborted the same way as unary failures.
109
115
  """
@@ -142,8 +148,6 @@ class AsyncExceptionHandlerInterceptor(AsyncServerInterceptor):
142
148
  # The handler (or an inner interceptor) already aborted this RPC with
143
149
  # its own status — pass it through untouched.
144
150
  raise
145
- except grpc.RpcError:
146
- raise
147
151
  except Exception as exc:
148
152
  await self._abort_with_mapped_status(exc, call)
149
153
 
@@ -62,6 +62,10 @@ class BaseGrpcServerSettings(BaseModel):
62
62
  # Basic
63
63
  host: str = Field(default=DEFAULT_HOST, min_length=1, description="gRPC server host")
64
64
  port: int = Field(default=DEFAULT_PORT, ge=0, le=65535, description="gRPC server port (0 = ephemeral)")
65
+ allow_ephemeral_port: bool = Field(
66
+ default=False,
67
+ description="Accept port=0 (ephemeral); off by default, a Service targeting a fixed port cannot reach one",
68
+ )
65
69
 
66
70
  # TLS/SSL settings
67
71
  ssl_enabled: bool = Field(default=False, description="Enable TLS/SSL")
@@ -135,6 +139,15 @@ class BaseGrpcServerSettings(BaseModel):
135
139
  # Health (consumed by GrpcApp.enable_health as its defaults)
136
140
  health: BaseHealthSettings = Field(default_factory=BaseHealthSettings, description="Health check configuration")
137
141
 
142
+ @model_validator(mode="after")
143
+ def _validate_port(self) -> "BaseGrpcServerSettings":
144
+ if self.port == 0 and not self.allow_ephemeral_port:
145
+ raise ValueError(
146
+ "gRPC port=0 binds an ephemeral port and a Service targeting a fixed port will not reach this "
147
+ "process; set allow_ephemeral_port=True if that is intended"
148
+ )
149
+ return self
150
+
138
151
  @model_validator(mode="after")
139
152
  def _validate_ssl(self) -> "BaseGrpcServerSettings":
140
153
  if self.ssl_enabled:
@@ -15,6 +15,7 @@ classifiers = [
15
15
  "Programming Language :: Python :: 3",
16
16
  "Programming Language :: Python :: 3.12",
17
17
  "Programming Language :: Python :: 3.13",
18
+ "Programming Language :: Python :: 3.14",
18
19
  "Topic :: Software Development :: Libraries :: Python Modules",
19
20
  "Typing :: Typed",
20
21
  ]
@@ -1 +0,0 @@
1
- __version__ = "0.1.1"
File without changes