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.
- {pgcommon-0.3.0 → pgcommon-0.3.1}/CHANGELOG.md +6 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/PKG-INFO +6 -6
- {pgcommon-0.3.0 → pgcommon-0.3.1}/README.md +5 -5
- {pgcommon-0.3.0 → pgcommon-0.3.1}/VERSIONING.md +2 -3
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/lld/platform-lld-pgcommon-py.md +1 -1
- {pgcommon-0.3.0 → pgcommon-0.3.1}/pyproject.toml +1 -1
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/config.py +19 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_config.py +13 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/uv.lock +1 -1
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.claude/CLAUDE.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.claude/api-reference.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.claude/architecture.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.claude/database-schema.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.claude/operations.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.claude/request-flows.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.claude/settings.json +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.dockerignore +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.env-example +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.githooks/pre-commit +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/CODEOWNERS +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/SECURITY.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/dependabot.yml +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/pull_request_template.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/api-compat.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/arch-lint.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/changelog-section.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/coverage-gate.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/create-github-release.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/detect-changes.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/detect-changes_test.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/docs-mermaid-sync.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/export-image-provenance.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/extract-release-notes.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/image-fingerprint.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/log-image-digest.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/pr-summary.js +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/prepare-release-dist.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/release-checksums.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/release-image-tags.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/release_guard.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/set-build-cache-targets.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/set-source-date-epoch.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/smoke-tests.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/verify-changelog-entry.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/scripts/verify-release-tag.sh +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/workflows/changelog-check.yml +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/workflows/ci.yml +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/workflows/docs.yml +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/workflows/release.yml +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/workflows/validate-quality.yml +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.github/workflows/validate-test.yml +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/.gitignore +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/ARCHITECTURE.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/CONTRIBUTING.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/Dockerfile +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/LICENSE +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/Makefile +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/SECURITY.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/build-constraints.txt +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docker-compose.yml +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/MigrationGuide.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/adr/ADR-001-contextvars-guc-propagation.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/adr/ADR-002-cancellation-and-rollback-safety.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/adr/ADR-003-guc-injection-timing.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/adr/ADR-004-migration-runner-design.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/README.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/consuming-service-wiring.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/data-model.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/deployment.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/documentation-assets.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/error-classification.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/failure-domains.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/layer-model.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/migration-runner.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/no-identity-flow.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/observability-stack.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/package-dependencies.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/pool-create-hook-composition.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/pool-lifecycle.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/query-flow.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/retry-flow.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/rls-guc-flow.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/runinsavepoint-lifecycle.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/runintx-lifecycle.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/withconn-pgbouncer-mode.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/withconn-standard-mode.mmd +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/observability/README.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/docs/observability/metrics-registry.md +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/migrations/0001_create_example_table.down.sql +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/migrations/0001_create_example_table.up.sql +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/__init__.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/__init__.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/dsn.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/duration.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/goquote.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/query_event.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/sql_verb.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/sqlstate.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/statement_scope.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/strconv.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/txshape.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/_internal/wrap_tx.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/errors.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/guc.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/integrations/__init__.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/integrations/fastapi.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/logger.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/metrics.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/metrics_registry.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/migrate/__init__.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/migrate/__main__.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/migrate/cli.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/migrate/errors.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/migrate/runner.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/pool.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/py.typed +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/retry.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/tracer.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/src/pgcommon/tx.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/__init__.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/conftest.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/e2e/__init__.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/e2e/conftest.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/e2e/test_core_invariants_e2e.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/e2e/test_pgbouncer_callback_escape_e2e.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/e2e/test_rls_pgbouncer_e2e.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/fixtures/__init__.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/fixtures/fake_asyncpg.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/fixtures/fake_migrate_conn.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/fixtures/fakes.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/fixtures/go_pgmetrics_vectors.go.txt +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/fixtures/go_pgmetrics_vectors.json +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/__init__.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/conftest.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_concurrency_stress.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_connection_loss.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_core_invariants_integration.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_error_helpers_real_violations.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_failover.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_migrate_invariants_integration.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_migrate_runner_integration.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_platform_metrics.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_pool_hardening.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_pool_lifecycle.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_pool_regressions.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_retry_real_serialization_failure.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_rls_guc_injection.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_tx_and_savepoint.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_tx_escape.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/__init__.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_config_parity.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_core_invariants.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_core_redaction_and_config.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_errors.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_fastapi_invariants.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_guc.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_integrations_fastapi.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_internal.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_internal_parity.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_logger.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_metrics.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_metrics_identity.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_metrics_invariants.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_metrics_inventory.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_metrics_lifecycle.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_metrics_standard.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_migrate_cli.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_migrate_cli_invariants.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_migrate_invariants.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_migrate_runner.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_pool.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_pool_hardening.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_pool_metrics.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_pool_parity.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_pool_resilience.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_public_api.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_retry.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_tracer.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_tx.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_txshape.py +0 -0
- {pgcommon-0.3.0 → pgcommon-0.3.1}/tests/unit/test_txshape_go_oracle.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
2044
|
-
uv add "pgcommon==0.3.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
2018
|
-
uv add "pgcommon==0.3.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 |
|
|
@@ -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)
|
|
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
|
|
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
|
|
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
|
{pgcommon-0.3.0 → pgcommon-0.3.1}/docs/architecture/mermaid/pool-create-hook-composition.mmd
RENAMED
|
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
|
|
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
|
|
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
|
{pgcommon-0.3.0 → pgcommon-0.3.1}/tests/integration/test_retry_real_serialization_failure.py
RENAMED
|
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
|
|
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
|