Skip to content

fix(db): resolve DB URL without ConfigParser interpolation - #19

Merged
thecaffiend merged 2 commits into
mainfrom
18-envpy-fails-on-db-urls-containing-configparser-interpolation
Jul 22, 2026
Merged

thecaffiend merged 2 commits into
mainfrom
18-envpy-fails-on-db-urls-containing-configparser-interpolation

Conversation

@thecaffiend

Copy link
Copy Markdown
Member

Summary

Fixes #18. env.py routed the DB URL through Alembic's Config, backed by a ConfigParser using BasicInterpolation, so any % in a percent-encoded password raised InterpolationSyntaxError before a connection was attempted. Percent-encoding is unavoidable for passwords containing @ : / <space> etc., and a literal % (e.g. an AWS-rotated RDS password) encodes to %25, so this broke a whole class of valid passwords.

env.py / db_url.py

  • Resolve the URL via a new side-effect-free resolve_db_url helper in cape_cod_db/db_url.py; stop calling set_main_option and re-reading through the interpolating parser.
  • Online migrations build the engine with create_engine(db_url, ...); offline passes url=db_url directly to context.configure.

database.py

  • Import the resolved db_url from env.py instead of config.get_main_option; the existing DB_URL env fallback still works.

security

  • Mask the password in configuration logs via make_url(db_url).render_as_string(hide_password=True) in both env.py and database.py.

tests / docs

  • Add pytest and a regression test covering percent-encoded passwords (% @ : / <space> # ? plus a combined case), asserting make_url(url).password round-trips.
  • Add a "Running Tests" section to the README.

Verification

poetry run ruff check, poetry run pyright, and poetry run pytest all pass. Confirmed make_url("postgresql://user:p%25ss%40word@host/db").password == "p%ss@word" and that the config log renders user:***.

Related

Once released, cape-ph/cape-cod-env can drop its %% workaround in roles/common/tasks/resolve_db_url.yaml and pass the plain percent-encoded URL.

Routing the DB URL through Alembic's Config backed it with a ConfigParser
using BasicInterpolation, so any '%' in a percent-encoded password raised
InterpolationSyntaxError before a connection was attempted.

- add side-effect-free resolve_db_url helper in cape_cod_db/db_url.py and
  stop calling set_main_option; pass the resolved URL straight to the
  engine and context
- online migrations use create_engine; offline passes url= directly
- database.py imports the resolved db_url instead of get_main_option,
  preserving its DB_URL env fallback
- mask the password in configuration logs via
  render_as_string(hide_password=True)
- add pytest and regression tests covering percent-encoded passwords

Fixes #18
- DB URL resolution decoupled from ConfigParser interpolation
- DB password masked in env.py/database.py logs
@thecaffiend
thecaffiend requested a review from mehalter July 22, 2026 17:41
@thecaffiend thecaffiend linked an issue Jul 22, 2026 that may be closed by this pull request

@mehalter mehalter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

@thecaffiend
thecaffiend merged commit 5e68e62 into main Jul 22, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

env.py fails on DB URLs containing '%' (ConfigParser interpolation)

2 participants