pgcommon 0.3.0__tar.gz → 0.3.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 (184) hide show
  1. {pgcommon-0.3.0 → pgcommon-0.3.1}/CHANGELOG.md +6 -0
  2. {pgcommon-0.3.0 → pgcommon-0.3.1}/PKG-INFO +6 -6
  3. {pgcommon-0.3.0 → pgcommon-0.3.1}/README.md +5 -5
  4. {pgcommon-0.3.0 → pgcommon-0.3.1}/VERSIONING.md +2 -3
  5. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/lld/platform-lld-pgcommon-py.md +1 -1
  6. {pgcommon-0.3.0 → pgcommon-0.3.1}/pyproject.toml +1 -1
  7. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/config.py +19 -0
  8. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_config.py +13 -0
  9. {pgcommon-0.3.0 → pgcommon-0.3.1}/uv.lock +1 -1
  10. {pgcommon-0.3.0 → pgcommon-0.3.1}/.claude/CLAUDE.md +0 -0
  11. {pgcommon-0.3.0 → pgcommon-0.3.1}/.claude/api-reference.md +0 -0
  12. {pgcommon-0.3.0 → pgcommon-0.3.1}/.claude/architecture.md +0 -0
  13. {pgcommon-0.3.0 → pgcommon-0.3.1}/.claude/database-schema.md +0 -0
  14. {pgcommon-0.3.0 → pgcommon-0.3.1}/.claude/operations.md +0 -0
  15. {pgcommon-0.3.0 → pgcommon-0.3.1}/.claude/request-flows.md +0 -0
  16. {pgcommon-0.3.0 → pgcommon-0.3.1}/.claude/settings.json +0 -0
  17. {pgcommon-0.3.0 → pgcommon-0.3.1}/.dockerignore +0 -0
  18. {pgcommon-0.3.0 → pgcommon-0.3.1}/.env-example +0 -0
  19. {pgcommon-0.3.0 → pgcommon-0.3.1}/.githooks/pre-commit +0 -0
  20. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/CODEOWNERS +0 -0
  21. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  22. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  23. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/SECURITY.md +0 -0
  24. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/dependabot.yml +0 -0
  25. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/pull_request_template.md +0 -0
  26. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/api-compat.sh +0 -0
  27. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/arch-lint.sh +0 -0
  28. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/changelog-section.sh +0 -0
  29. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/coverage-gate.sh +0 -0
  30. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/create-github-release.sh +0 -0
  31. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/detect-changes.sh +0 -0
  32. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/detect-changes_test.sh +0 -0
  33. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/docs-mermaid-sync.sh +0 -0
  34. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/export-image-provenance.sh +0 -0
  35. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/extract-release-notes.sh +0 -0
  36. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/image-fingerprint.sh +0 -0
  37. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/log-image-digest.sh +0 -0
  38. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/pr-summary.js +0 -0
  39. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/prepare-release-dist.sh +0 -0
  40. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/release-checksums.sh +0 -0
  41. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/release-image-tags.sh +0 -0
  42. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/release_guard.py +0 -0
  43. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/set-build-cache-targets.sh +0 -0
  44. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/set-source-date-epoch.sh +0 -0
  45. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/smoke-tests.sh +0 -0
  46. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/verify-changelog-entry.sh +0 -0
  47. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/verify-release-tag.sh +0 -0
  48. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/workflows/changelog-check.yml +0 -0
  49. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/workflows/ci.yml +0 -0
  50. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/workflows/docs.yml +0 -0
  51. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/workflows/release.yml +0 -0
  52. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/workflows/validate-quality.yml +0 -0
  53. {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/workflows/validate-test.yml +0 -0
  54. {pgcommon-0.3.0 → pgcommon-0.3.1}/.gitignore +0 -0
  55. {pgcommon-0.3.0 → pgcommon-0.3.1}/ARCHITECTURE.md +0 -0
  56. {pgcommon-0.3.0 → pgcommon-0.3.1}/CONTRIBUTING.md +0 -0
  57. {pgcommon-0.3.0 → pgcommon-0.3.1}/Dockerfile +0 -0
  58. {pgcommon-0.3.0 → pgcommon-0.3.1}/LICENSE +0 -0
  59. {pgcommon-0.3.0 → pgcommon-0.3.1}/Makefile +0 -0
  60. {pgcommon-0.3.0 → pgcommon-0.3.1}/SECURITY.md +0 -0
  61. {pgcommon-0.3.0 → pgcommon-0.3.1}/build-constraints.txt +0 -0
  62. {pgcommon-0.3.0 → pgcommon-0.3.1}/docker-compose.yml +0 -0
  63. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/MigrationGuide.md +0 -0
  64. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/adr/ADR-001-contextvars-guc-propagation.md +0 -0
  65. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/adr/ADR-002-cancellation-and-rollback-safety.md +0 -0
  66. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/adr/ADR-003-guc-injection-timing.md +0 -0
  67. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/adr/ADR-004-migration-runner-design.md +0 -0
  68. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/README.md +0 -0
  69. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/consuming-service-wiring.mmd +0 -0
  70. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/data-model.mmd +0 -0
  71. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/deployment.mmd +0 -0
  72. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/documentation-assets.mmd +0 -0
  73. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/error-classification.mmd +0 -0
  74. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/failure-domains.mmd +0 -0
  75. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/layer-model.mmd +0 -0
  76. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/migration-runner.mmd +0 -0
  77. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/no-identity-flow.mmd +0 -0
  78. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/observability-stack.mmd +0 -0
  79. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/package-dependencies.mmd +0 -0
  80. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/pool-create-hook-composition.mmd +0 -0
  81. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/pool-lifecycle.mmd +0 -0
  82. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/query-flow.mmd +0 -0
  83. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/retry-flow.mmd +0 -0
  84. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/rls-guc-flow.mmd +0 -0
  85. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/runinsavepoint-lifecycle.mmd +0 -0
  86. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/runintx-lifecycle.mmd +0 -0
  87. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/withconn-pgbouncer-mode.mmd +0 -0
  88. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/withconn-standard-mode.mmd +0 -0
  89. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/observability/README.md +0 -0
  90. {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/observability/metrics-registry.md +0 -0
  91. {pgcommon-0.3.0 → pgcommon-0.3.1}/migrations/0001_create_example_table.down.sql +0 -0
  92. {pgcommon-0.3.0 → pgcommon-0.3.1}/migrations/0001_create_example_table.up.sql +0 -0
  93. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/__init__.py +0 -0
  94. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/__init__.py +0 -0
  95. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/dsn.py +0 -0
  96. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/duration.py +0 -0
  97. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/goquote.py +0 -0
  98. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/query_event.py +0 -0
  99. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/sql_verb.py +0 -0
  100. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/sqlstate.py +0 -0
  101. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/statement_scope.py +0 -0
  102. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/strconv.py +0 -0
  103. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/txshape.py +0 -0
  104. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/wrap_tx.py +0 -0
  105. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/errors.py +0 -0
  106. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/guc.py +0 -0
  107. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/integrations/__init__.py +0 -0
  108. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/integrations/fastapi.py +0 -0
  109. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/logger.py +0 -0
  110. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/metrics.py +0 -0
  111. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/metrics_registry.py +0 -0
  112. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/migrate/__init__.py +0 -0
  113. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/migrate/__main__.py +0 -0
  114. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/migrate/cli.py +0 -0
  115. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/migrate/errors.py +0 -0
  116. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/migrate/runner.py +0 -0
  117. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/pool.py +0 -0
  118. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/py.typed +0 -0
  119. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/retry.py +0 -0
  120. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/tracer.py +0 -0
  121. {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/tx.py +0 -0
  122. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/__init__.py +0 -0
  123. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/conftest.py +0 -0
  124. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/e2e/__init__.py +0 -0
  125. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/e2e/conftest.py +0 -0
  126. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/e2e/test_core_invariants_e2e.py +0 -0
  127. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/e2e/test_pgbouncer_callback_escape_e2e.py +0 -0
  128. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/e2e/test_rls_pgbouncer_e2e.py +0 -0
  129. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/fixtures/__init__.py +0 -0
  130. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/fixtures/fake_asyncpg.py +0 -0
  131. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/fixtures/fake_migrate_conn.py +0 -0
  132. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/fixtures/fakes.py +0 -0
  133. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/fixtures/go_pgmetrics_vectors.go.txt +0 -0
  134. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/fixtures/go_pgmetrics_vectors.json +0 -0
  135. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/__init__.py +0 -0
  136. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/conftest.py +0 -0
  137. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_concurrency_stress.py +0 -0
  138. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_connection_loss.py +0 -0
  139. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_core_invariants_integration.py +0 -0
  140. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_error_helpers_real_violations.py +0 -0
  141. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_failover.py +0 -0
  142. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_migrate_invariants_integration.py +0 -0
  143. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_migrate_runner_integration.py +0 -0
  144. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_platform_metrics.py +0 -0
  145. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_pool_hardening.py +0 -0
  146. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_pool_lifecycle.py +0 -0
  147. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_pool_regressions.py +0 -0
  148. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_retry_real_serialization_failure.py +0 -0
  149. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_rls_guc_injection.py +0 -0
  150. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_tx_and_savepoint.py +0 -0
  151. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_tx_escape.py +0 -0
  152. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/__init__.py +0 -0
  153. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_config_parity.py +0 -0
  154. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_core_invariants.py +0 -0
  155. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_core_redaction_and_config.py +0 -0
  156. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_errors.py +0 -0
  157. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_fastapi_invariants.py +0 -0
  158. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_guc.py +0 -0
  159. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_integrations_fastapi.py +0 -0
  160. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_internal.py +0 -0
  161. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_internal_parity.py +0 -0
  162. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_logger.py +0 -0
  163. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_metrics.py +0 -0
  164. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_metrics_identity.py +0 -0
  165. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_metrics_invariants.py +0 -0
  166. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_metrics_inventory.py +0 -0
  167. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_metrics_lifecycle.py +0 -0
  168. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_metrics_standard.py +0 -0
  169. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_migrate_cli.py +0 -0
  170. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_migrate_cli_invariants.py +0 -0
  171. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_migrate_invariants.py +0 -0
  172. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_migrate_runner.py +0 -0
  173. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_pool.py +0 -0
  174. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_pool_hardening.py +0 -0
  175. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_pool_metrics.py +0 -0
  176. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_pool_parity.py +0 -0
  177. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_pool_resilience.py +0 -0
  178. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_public_api.py +0 -0
  179. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_retry.py +0 -0
  180. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_tracer.py +0 -0
  181. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_tx.py +0 -0
  182. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_txshape.py +0 -0
  183. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_txshape_go_oracle.py +0 -0
  184. {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_wrap_tx.py +0 -0
@@ -11,6 +11,12 @@ public API.
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [0.3.1] - 2026-10-15
15
+
16
+ ### Security
17
+
18
+ - `Config` and `ResolvedConfig` no longer print the DSN's password in their `repr` (and so in `str`, tracebacks, debuggers and log lines that format the object): the generated dataclass repr showed the DSN as is. The `dsn` field is now shown through `mask_dsn`; the field value itself is unchanged. Found by platform-eventcommon's review (its `OutboxEnv.pool_config()` hands the `Config` to callers).
19
+
14
20
  ## [0.3.0] - 2026-10-15
15
21
 
16
22
  Brings the port in line with Go `platform-pgcommon` **v2.0.1**: the Tier 1 metrics below, plus
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pgcommon
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Opinionated async PostgreSQL access layer for platform services — RLS tenancy, retries, and observability by default.
5
5
  Project-URL: Homepage, https://github.com/BCBP-SOLUTIONS-FZC-LLC/platform-pgcommon-py
6
6
  Project-URL: Repository, https://github.com/BCBP-SOLUTIONS-FZC-LLC/platform-pgcommon-py
@@ -32,7 +32,7 @@ The platform's **PostgreSQL access layer for Python services**: an `asyncio` lib
32
32
 
33
33
  **Repository:** `github.com/BCBP-SOLUTIONS-FZC-LLC/platform-pgcommon-py` (private)
34
34
 
35
- **Package:** `pgcommon` 0.3.0 ([release status](#versioning-and-releases): released 2026-10-15, on [PyPI](https://pypi.org/project/pgcommon/)) · Apache-2.0 · Python ≥ 3.12 (CI runs 3.12, 3.13 and 3.14) · consumed as a dependency, never deployed on its own. It also ships one artefact, the `pgcommon-migrate` CLI, as a container image (`ghcr.io/bcbp-solutions-fzc-llc/platform-pgcommon-py`).
35
+ **Package:** `pgcommon` 0.3.1 ([release status](#versioning-and-releases): released 2026-10-15, on [PyPI](https://pypi.org/project/pgcommon/)) · Apache-2.0 · Python ≥ 3.12 (CI runs 3.12, 3.13 and 3.14) · consumed as a dependency, never deployed on its own. It also ships one artefact, the `pgcommon-migrate` CLI, as a container image (`ghcr.io/bcbp-solutions-fzc-llc/platform-pgcommon-py`).
36
36
 
37
37
  **Design:** one `src/pgcommon` package — public modules on top of a private `_internal/` layer, with the layer rules enforced by import-linter in CI. See [ARCHITECTURE.md](ARCHITECTURE.md) for design decisions, layer diagrams, and — importantly — how this port's `asyncio`/`contextvars` model differs from the Go original's `context.Context` model. This README is how to **use** it.
38
38
 
@@ -2040,8 +2040,8 @@ pgcommon itself is released to PyPI, and services adopt it by bumping their pin;
2040
2040
  Pin an exact version in production ([VERSIONING.md § Consume a release](VERSIONING.md#consume-a-release) has the pin styles):
2041
2041
 
2042
2042
  ```bash
2043
- pip install "pgcommon==0.3.0" # or "pgcommon[fastapi]==0.3.0" / "pgcommon[structlog]==0.3.0"
2044
- uv add "pgcommon==0.3.0"
2043
+ pip install "pgcommon==0.3.1" # or "pgcommon[fastapi]==0.3.1" / "pgcommon[structlog]==0.3.1"
2044
+ uv add "pgcommon==0.3.1"
2045
2045
  ```
2046
2046
 
2047
2047
  The files on PyPI are byte-identical to the GitHub Release's wheel and sdist: the release builds them once and uploads the same files to both.
@@ -2059,7 +2059,7 @@ Two-stage `Dockerfile`. The build stage (`python:3.14-slim` with uv `0.11.32`, b
2059
2059
  Needs [cosign](https://docs.sigstore.dev/cosign/system_config/installation/) ≥ 2.4. A tag-triggered release is signed by `release.yml@refs/tags/<tag>`; a re-run dispatched from `main` by `release.yml@refs/heads/main`, which the regular expression below also accepts.
2060
2060
 
2061
2061
  ```bash
2062
- TAG=v0.3.0 # the release to verify
2062
+ TAG=v0.3.1 # the release to verify
2063
2063
  REPO=BCBP-SOLUTIONS-FZC-LLC/platform-pgcommon-py
2064
2064
  IDENTITY="^https://github\.com/${REPO}/\.github/workflows/release\.yml@refs/(tags/${TAG}|heads/main)$"
2065
2065
 
@@ -2208,7 +2208,7 @@ MIGRATIONS_DIR=/migrations
2208
2208
 
2209
2209
  | Consumer | pgcommon pin | Uses |
2210
2210
  |---|---|---|
2211
- | `platform-eventcommon` (Python port of `platform-events`) | `>=0.2.0,<0.3.0` — moving to 0.3.0 needs a coordinated change ([Upgrading from 0.2.0](#upgrading-from-020)) | `Pool` and `run_in_tx` for its outbox, inbox and DLQ stores; `with_guc_set` so message handlers run under RLS; `MigrationDirtyError` for its own schema bookkeeping |
2211
+ | `platform-eventcommon` (Python port of `platform-events`) | `>=0.2.0,<0.4.0` on its `main` (its next release, 0.3.0, requires `>=0.3.0,<0.4.0`) | `Pool` and `run_in_tx` for its outbox, inbox and DLQ stores; `with_guc_set` so message handlers run under RLS; `MigrationDirtyError` for its own schema bookkeeping |
2212
2212
  | `platform-interop-tests` (Python probe) | — | Cross-language compatibility with the Go library (CI's `interop` job) |
2213
2213
  | FastAPI platform services | their own | Pool, RLS injection, transactions, error helpers, metrics, `pgcommon.integrations.fastapi` |
2214
2214
 
@@ -6,7 +6,7 @@ The platform's **PostgreSQL access layer for Python services**: an `asyncio` lib
6
6
 
7
7
  **Repository:** `github.com/BCBP-SOLUTIONS-FZC-LLC/platform-pgcommon-py` (private)
8
8
 
9
- **Package:** `pgcommon` 0.3.0 ([release status](#versioning-and-releases): released 2026-10-15, on [PyPI](https://pypi.org/project/pgcommon/)) · Apache-2.0 · Python ≥ 3.12 (CI runs 3.12, 3.13 and 3.14) · consumed as a dependency, never deployed on its own. It also ships one artefact, the `pgcommon-migrate` CLI, as a container image (`ghcr.io/bcbp-solutions-fzc-llc/platform-pgcommon-py`).
9
+ **Package:** `pgcommon` 0.3.1 ([release status](#versioning-and-releases): released 2026-10-15, on [PyPI](https://pypi.org/project/pgcommon/)) · Apache-2.0 · Python ≥ 3.12 (CI runs 3.12, 3.13 and 3.14) · consumed as a dependency, never deployed on its own. It also ships one artefact, the `pgcommon-migrate` CLI, as a container image (`ghcr.io/bcbp-solutions-fzc-llc/platform-pgcommon-py`).
10
10
 
11
11
  **Design:** one `src/pgcommon` package — public modules on top of a private `_internal/` layer, with the layer rules enforced by import-linter in CI. See [ARCHITECTURE.md](ARCHITECTURE.md) for design decisions, layer diagrams, and — importantly — how this port's `asyncio`/`contextvars` model differs from the Go original's `context.Context` model. This README is how to **use** it.
12
12
 
@@ -2014,8 +2014,8 @@ pgcommon itself is released to PyPI, and services adopt it by bumping their pin;
2014
2014
  Pin an exact version in production ([VERSIONING.md § Consume a release](VERSIONING.md#consume-a-release) has the pin styles):
2015
2015
 
2016
2016
  ```bash
2017
- pip install "pgcommon==0.3.0" # or "pgcommon[fastapi]==0.3.0" / "pgcommon[structlog]==0.3.0"
2018
- uv add "pgcommon==0.3.0"
2017
+ pip install "pgcommon==0.3.1" # or "pgcommon[fastapi]==0.3.1" / "pgcommon[structlog]==0.3.1"
2018
+ uv add "pgcommon==0.3.1"
2019
2019
  ```
2020
2020
 
2021
2021
  The files on PyPI are byte-identical to the GitHub Release's wheel and sdist: the release builds them once and uploads the same files to both.
@@ -2033,7 +2033,7 @@ Two-stage `Dockerfile`. The build stage (`python:3.14-slim` with uv `0.11.32`, b
2033
2033
  Needs [cosign](https://docs.sigstore.dev/cosign/system_config/installation/) ≥ 2.4. A tag-triggered release is signed by `release.yml@refs/tags/<tag>`; a re-run dispatched from `main` by `release.yml@refs/heads/main`, which the regular expression below also accepts.
2034
2034
 
2035
2035
  ```bash
2036
- TAG=v0.3.0 # the release to verify
2036
+ TAG=v0.3.1 # the release to verify
2037
2037
  REPO=BCBP-SOLUTIONS-FZC-LLC/platform-pgcommon-py
2038
2038
  IDENTITY="^https://github\.com/${REPO}/\.github/workflows/release\.yml@refs/(tags/${TAG}|heads/main)$"
2039
2039
 
@@ -2182,7 +2182,7 @@ MIGRATIONS_DIR=/migrations
2182
2182
 
2183
2183
  | Consumer | pgcommon pin | Uses |
2184
2184
  |---|---|---|
2185
- | `platform-eventcommon` (Python port of `platform-events`) | `>=0.2.0,<0.3.0` — moving to 0.3.0 needs a coordinated change ([Upgrading from 0.2.0](#upgrading-from-020)) | `Pool` and `run_in_tx` for its outbox, inbox and DLQ stores; `with_guc_set` so message handlers run under RLS; `MigrationDirtyError` for its own schema bookkeeping |
2185
+ | `platform-eventcommon` (Python port of `platform-events`) | `>=0.2.0,<0.4.0` on its `main` (its next release, 0.3.0, requires `>=0.3.0,<0.4.0`) | `Pool` and `run_in_tx` for its outbox, inbox and DLQ stores; `with_guc_set` so message handlers run under RLS; `MigrationDirtyError` for its own schema bookkeeping |
2186
2186
  | `platform-interop-tests` (Python probe) | — | Cross-language compatibility with the Go library (CI's `interop` job) |
2187
2187
  | FastAPI platform services | their own | Pool, RLS injection, transactions, error helpers, metrics, `pgcommon.integrations.fastapi` |
2188
2188
 
@@ -251,9 +251,7 @@ and push the tag.
251
251
  image's `provenance.slsa.json` and `sbom.cyclonedx.json` attached.
252
252
 
253
253
  The cross-language **interop** check gates `push`, exactly as in the Go repo: while it is
254
- red, no image, no PyPI upload and no GitHub Release happen. (At the time of writing it fails
255
- for `0.3.0` because `platform-eventcommon` pins `pgcommon<0.3.0`; relax that pin, or re-run
256
- the release once it is relaxed.)
254
+ red, no image, no PyPI upload and no GitHub Release happen.
257
255
 
258
256
  8. **Notify consumers** (Slack/ADR) with upgrade notes if MINOR or MAJOR.
259
257
 
@@ -331,6 +329,7 @@ which `release_guard.py` checks).
331
329
 
332
330
  | pgcommon | Go `platform-pgcommon` parity | Python | asyncpg | Tested in CI |
333
331
  |----------|-------------------------------|--------|---------|--------------|
332
+ | `0.3.1` (2026-10-15) | `v2.0.1` | `>=3.12` | `>=0.30,<0.32` | as `0.3.0` (patch: `Config` / `ResolvedConfig` repr masks the DSN password) |
334
333
  | `0.3.0` (2026-10-15) | `v2.0.1` | `>=3.12` | `>=0.30,<0.32` | Python 3.12, 3.13, 3.14 on the locked asyncpg (`0.31.0`), plus asyncpg `0.30.0` on 3.12 (`.github/workflows/validate-test.yml`: the `test` job on 3.12, the `python` matrix on 3.13 / 3.14, the `asyncpg-compat` job) |
335
334
  | `0.2.0` | not recorded (predates per-release Go tracking) | `>=3.12` | `>=0.29` | Python 3.12, 3.13, 3.14 |
336
335
  | `0.1.0` | not recorded (predates per-release Go tracking) | `>=3.12` | `>=0.29` | Python 3.12, 3.13, 3.14 |
@@ -6,7 +6,7 @@
6
6
  |---|---|
7
7
  | Document type | Low-Level Design (LLD) |
8
8
  | Component | `platform-pgcommon-py`: the `pgcommon` Python package (asyncio PostgreSQL access library) and the `pgcommon-migrate` CLI |
9
- | Python package | `pgcommon` **0.3.0** (`pyproject.toml`; `requires-python >=3.12`; runtime dependencies `asyncpg>=0.30,<0.32`, `prometheus-client>=0.20`, `opentelemetry-api>=1.24`; extras `fastapi`, `structlog`) |
9
+ | Python package | `pgcommon` **0.3.1** (`pyproject.toml`; `requires-python >=3.12`; runtime dependencies `asyncpg>=0.30,<0.32`, `prometheus-client>=0.20`, `opentelemetry-api>=1.24`; extras `fastapi`, `structlog`) |
10
10
  | Go sibling | `platform-pgcommon` **v2.0.1** (`github.com/BCBP-SOLUTIONS-FZC-LLC/platform-pgcommon/v2`). This port tracks it; the deliberate differences are listed in §18.5 and `ARCHITECTURE.md` § Divergences from Go |
11
11
  | Library status | **0.3.0 released 2026-10-15** (`CHANGELOG.md` `[0.3.0]`); before it 0.2.0 (2026-07-27). 0.3.0 contains breaking changes (§19.2). `platform-eventcommon` supports it (`pgcommon>=0.2.0,<0.4.0`; §16 A1) and the interop harness checks the pair at `platform-interop-tests` `9bd44ca`. Test suite: 2375 passed, 1 skipped, 100% line + branch coverage (§14) |
12
12
  | LLD revision | 1.1 |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pgcommon"
3
- version = "0.3.0"
3
+ version = "0.3.1"
4
4
  description = "Opinionated async PostgreSQL access layer for platform services — RLS tenancy, retries, and observability by default."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -16,6 +16,7 @@ so `?pool_max_conns=20` in the DSN is used unless `Config.max_conns` is set.
16
16
  from __future__ import annotations
17
17
 
18
18
  import configparser
19
+ import dataclasses
19
20
  import os
20
21
  import re
21
22
  from collections.abc import Callable
@@ -31,6 +32,7 @@ from pgcommon._internal.dsn import (
31
32
  dsn_settings,
32
33
  has_bad_percent_escape,
33
34
  is_url_dsn,
35
+ mask_dsn,
34
36
  normalize_dsn,
35
37
  redact,
36
38
  resolve_connect_timeout,
@@ -117,6 +119,17 @@ def default_min_conns_for(pgbouncer_mode: bool, max_conns: int) -> int:
117
119
  return min(DEFAULT_MIN_CONNS, max_conns)
118
120
 
119
121
 
122
+ def _repr_masking_dsn(obj: object) -> str:
123
+ """The dataclass repr with `dsn`'s password masked (`mask_dsn`): a `Config` reaches logs,
124
+ tracebacks and debuggers, and the generated repr printed the DSN as is."""
125
+ parts = []
126
+ for f in dataclasses.fields(obj): # type: ignore[arg-type]
127
+ value = getattr(obj, f.name)
128
+ shown = repr(mask_dsn(value)) if f.name == "dsn" and isinstance(value, str) else repr(value)
129
+ parts.append(f"{f.name}={shown}")
130
+ return f"{type(obj).__name__}({', '.join(parts)})"
131
+
132
+
120
133
  @dataclass(slots=True)
121
134
  class Config:
122
135
  """Pool configuration. `dsn` is the only required field.
@@ -199,6 +212,9 @@ class Config:
199
212
  # callback made in the TxEndedInCallbackError, to be resolved by hand.
200
213
  prepared_xact_guard: bool = True
201
214
 
215
+ def __repr__(self) -> str:
216
+ return _repr_masking_dsn(self)
217
+
202
218
  @property
203
219
  def resolved_pool_name(self) -> str:
204
220
  """`pool_name`, or `"default"` when empty — the value used as the `pool` metric label."""
@@ -278,6 +294,9 @@ class ResolvedConfig:
278
294
  saturated_alive_window: timedelta = DEFAULT_SATURATED_ALIVE_WINDOW # 0: always ping
279
295
  prepared_xact_guard: bool = True
280
296
 
297
+ def __repr__(self) -> str:
298
+ return _repr_masking_dsn(self)
299
+
281
300
 
282
301
  def _unset(d: timedelta | None) -> bool:
283
302
  return d is None or d == timedelta(0)
@@ -451,3 +451,16 @@ def test_config_from_env_reads_pg_acquire_timeout(
451
451
  cfg, warnings = config_from_env()
452
452
  assert cfg.acquire_timeout == want
453
453
  assert [w.key for w in warnings] == (["PG_ACQUIRE_TIMEOUT"] if warns else [])
454
+
455
+
456
+ def test_repr_masks_the_dsn_password() -> None:
457
+ # The generated dataclass repr printed the DSN as is, so a Config in a log line, a
458
+ # traceback or a debugger showed the password.
459
+ from pgcommon.config import Config, resolve
460
+
461
+ cfg = Config(dsn="postgres://app:s3cr%40t%2Fpw@db:5432/app?sslmode=require")
462
+ for shown in (repr(cfg), str(cfg), repr(resolve(cfg))):
463
+ assert "s3cr" not in shown
464
+ assert "postgres://app:" in shown
465
+ keyword = Config(dsn="host=db user=app password='s3cr@t pw' dbname=app")
466
+ assert "s3cr" not in repr(keyword)
@@ -820,7 +820,7 @@ wheels = [
820
820
 
821
821
  [[package]]
822
822
  name = "pgcommon"
823
- version = "0.3.0"
823
+ version = "0.3.1"
824
824
  source = { editable = "." }
825
825
  dependencies = [
826
826
  { name = "asyncpg" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes