Release Notes
Latest Changes🔗
- feat: declare first-party adapters as installed entry points and remove eager bootstrap. PR #564 by @zschumacher.
- v1: adapters are now discovered through the standard
pydapper.adaptersentry-point group and load lazily. First-party and third-party adapters use one provider contract: an installed distribution declares an entry point per adapter name whose synchronous zero-argument callback registers exactly that name through the publicregister_adapter(). The eight stable first-party names (sqlite3,psycopg2,psycopg,aiopg,mysql,pymssql,oracledb,google) are unchanged and are now declared by thepydapperdistribution itself; the private eager first-party bootstrap was removed, so a plainimport pydapperno longer registers adapters, imports adapter command modules, or imports optional database drivers. DSN-basedconnect()/connect_async()and explicitusing(..., adapter=name)/using_async(..., adapter=name)selection load only the requested provider (explicit selection still bypasses connection predicates), while automaticusing()/using_async()selection loads every installed provider before evaluating predicates, so an unrelated broken provider can fail automatic selection but never exact-name selection. Precedence is deterministic: a direct runtimeregister_adapter()call wins for the process, the first-party provider wins over an external provider using the same name, and duplicate external providers fail before either loads with every conflicting distribution named — metadata enumeration order never chooses a winner. Provider failures identify the adapter and provider distribution, preserve the original exception as the cause, and roll back their registry mutations; successful callbacks run at most once per process and the installed catalog is cached per process. Runtime registration viaregister_adapter()remains fully supported, no public API is added or deprecated, and no new runtime dependency is introduced. See Adapter registration for the packaging contract for adapter authors. - feat: extract first-party adapter provider callbacks. PR #563 by @zschumacher.
- feat: load installed adapter providers before automatic selection. PR #562 by @zschumacher.
- feat: add private deterministic load-all-providers pass. PR #561 by @zschumacher.
- feat: resolve installed adapter providers from name-based public paths. PR #560 by @zschumacher.
- feat: add private exact-name adapter resolution and provider precedence. PR #559 by @zschumacher.
- feat: add private transactional loader for adapter provider entry points. PR #558 by @zschumacher.
- feat: replace dsnparse with an owned URL DSN parser. PR #557 by @zschumacher.
- v1: pydapper now owns its narrow URL-style DSN parser using the standard library's
urllib.parse. A direct implementation is smaller and lower risk than vendoring a generic parser API that pydapper does not consume. This removes the mandatorydsnparsedependency, so it is no longer installed with pydapper, while preserving default and exact explicit adapter routing, including third-party adapters. Parse-result fields now have accurate public types, and representations and parser-generated errors no longer expose credential-bearing DSNs or passwords. Decoded network hosts are revalidated so encoded controls and Unicode delimiter lookalikes cannot bypass authority parsing. - feat: add private lazy entry-point discovery catalog for pydapper.adapters. PR #556 by @zschumacher.
- feat: validate capability declarations and add command preparation hooks. PR #555 by @zschumacher.
- feat: validate adapter capability declarations and add command preparation hooks.
register_adapter()now validates each supplied command class'scapabilitiesdeclaration (afrozensetofAdapterCapabilitymembers) for both modes before the registry is touched, so an invalid declaration fails atomically. Every first-party command class explicitly declares its current — empty — capability set, andcommands.supports(AdapterCapability.X)reports declared support. New documented, compatibility-sensitive adapter-author preparation seams run inside the command-owned cursor lifecycle for every sync and async command family:_prepare_cursor*()once per acquired cursor and_prepare_command*()once per executed handler, both receiving a normalizedCommandOptionsinstance. No optional capability (transactions, timeouts, stored procedures, readonly, max rows, etc.) is implemented by this change. The*_or_defaulthelpers now forward the normalized options toquery_first/query_single; see Breaking Changes below for the subclass-override impact. - feat: add AdapterCapability vocabulary and command capability checks. PR #554 by @zschumacher.
- test: cover query_multiple runtime failures inside the command-owned cursor lifecycle. PR #553 by @zschumacher.
- fix: project query_single rows inside the command-owned cursor lifecycle. PR #551 by @zschumacher.
- fix: run mysql query_first inside the command-owned cursor lifecycle. PR #552 by @zschumacher.
- fix: project query_single rows inside the command-owned cursor lifecycle. PR #550 by @zschumacher.
- fix: validate and extract scalar results inside the command-owned cursor lifecycle. PR #549 by @zschumacher.
- fix: project query_first rows inside the command-owned cursor lifecycle. PR #548 by @zschumacher.
- fix: async command-owned cursor exception precedence. PR #547 by @zschumacher.
- fix: sync command-owned cursor exception precedence. PR #546 by @zschumacher.
- fix: validate complete query batches before DBAPI work. PR #545 by @zschumacher.
- fix: validate complete query batches before DBAPI work.
query_multipleandquery_multiple_asyncnow construct and validate every parameter handler before acquiring a cursor, so a missing or invalid parameter in any query of the tuple fails before any query executes or fetches. This is client-side prevalidation, not transactional execution; a validated batch can still fail partway through on a runtime database, fetch, column, or mapping error. - chore(ai): Add CLAUDE.md symlink to AGENTS.md. PR #544 by @zschumacher.
- fix: harden async context lifecycle. PR #540 by @zschumacher.
- fix: harden async context lifecycle. Async resources now resolve exactly once and
context-manager exception suppression propagates correctly; both
await connect_async()andasync with connect_async()usage remain unchanged. - feat: stabilize adapter registration. PR #539 by @zschumacher.
Breaking Changes🔗
- DSNs now follow the focused
<database>[+<adapter>]://...v1 grammar. Incidental inherited conveniences such asname=valuestrings, constructor default injection, dict-like mutation, tuple-style iteration or indexing, environment helpers, and multiple hosts are intentionally not reproduced. Multi-plus input such asdb+adapter+extra://...previously invented the adapter nameadapter_extra; it is now rejected. Query values are no longer implicitly coerced: for example,?code=001&enabled=truepreviously produced1andTruebut now preserves"001"and"true"; a bare query key such as?flagnow maps to{"flag": ""}instead of raising. Corrected DSN examples place ports after the host; literal and percent-encoded colons in passwords remain valid. SQLite slash counts are now explicit:sqlite:///relative/path.dbtargetsrelative/path.db; usesqlite:////absolute/path.dbfor/absolute/path.db(the three-slash form previously targeted/relative/path.db). - Command delegation:
query_first_or_default,query_single_or_default, and their async equivalents now forward the normalizedoptions=keyword toquery_first/query_single(and the async equivalents) instead of validating and discarding it. Subclass overrides of those four target methods must accept anoptions=keyword; overrides without it now raiseTypeErrorwhen called through the*_or_defaulthelpers. Add*, options=Noneto the override signature to migrate. - Adapter registration:
registerandregister_asyncwere removed. Useregister_adapterand explicitadapter=selection where needed.
Other Changes🔗
- feat: add command options model. PR #531 by @zschumacher.
- docs: define v1 compatibility policy. PR #530 by @zschumacher.
- fix: guarantee query cursor cleanup. PR #529 by @zschumacher.
- docs: clarify mapper references. PR #528 by @zschumacher.
- fix: harden mapper typing ux. PR #527 by @zschumacher.
- fix: handle callable default values safely. PR #526 by @zschumacher.
- fix: reject duplicate query columns. PR #524 by @zschumacher.
- fix: clean up mysql query_single unread results. PR #523 by @zschumacher.
- fix: bound query_single row fetching. PR #522 by @zschumacher.
- fix: harden parameter shapes and executemany behavior. PR #521 by @zschumacher.
- v1: replace placeholder regex with SQL-aware scanner. PR #509 by @zschumacher.
- v1: define public exceptions and cardinality semantics. PR #505 by @zschumacher.
- feat: add params alias across public command APIs. PR #503 by @zschumacher.
- docs: add AI development guidance. PR #504 by @zschumacher.
0.13.1🔗
Internal🔗
- chore(deps): update deps. PR #428 by @zschumacher.
0.13.0🔗
Internal🔗
- chore(deps): upgrade deps. PR #427 by @zschumacher.
- chore: update deps. PR #384 by @zschumacher.
- chore: migrate to use testcontainers. PR #383 by @zschumacher.
0.12.0🔗
Features🔗
Support psycopg async apis. PR #336 by @zschumacher.
Bug Fixes🔗
Internal🔗
upgrade poetry, actions, and use newer oracle image. PR #335 by @zschumacher.
0.11.1🔗
Features🔗
Add callable that returns a model as a supported model type. PR #333 by @zschumacher.
0.11.0🔗
Breaking changes🔗
- Python 3.8 support deprecated
- cx_oracle support deprecated
Internal🔗
update to latest dsnparse. PR #332 by @zschumacher.
support python 3.13; update deps; deprecate Cx_Oracle in favor or Oracledb. PR #331 by @zschumacher.
- Bump idna from 3.4 to 3.7. PR #262 by @dependabot[bot].
- Bump cryptography from 41.0.5 to 42.0.4. PR #255 by @dependabot[bot].
0.10.0🔗
Features🔗
-
- ✨ add support for
psycopg3. PR #214 by @idumancic.
- ✨ add support for
Internal🔗
- 🔧 fix step names in fmt.yml. PR #256 by @otosky.
- ⬆️ Support python 3.12. PR #199 by @zschumacher.
Docs🔗
- 📝 Remove broken badge in docs index. PR #198 by @zschumacher.
- 🔧 add readthedoc config file. PR #197 by @zschumacher.
0.9.0🔗
Bug fixes🔗
Internal🔗
- 🔧 update poetry to 1.7.1 and bump deps. PR #195 by @zschumacher.
- 🔧 use bigquery emulator for tests. PR #166 by @zschumacher.
- 🔧 bump deps and use markers for tests. PR #164 by @zschumacher.
0.8.0🔗
Features🔗
- ✨ Add support for
bigquery. PR #142 by @zschumacher.
Internal🔗
- 🔧 Remove python 3.7 support. PR #145 by @zschumacher.
- 🔧 update
poetryto1.4.0. PR #143 by @zschumacher. - 🔧 Remove irrelevant make command. PR #125 by @zschumacher.
- 🔧 Dependabot 2023-02-12. PR #124 by @zschumacher.
Docs🔗
- 📋 Add
aiopgto table in PostgreSQL docs section. PR #107 by @zschumacher.
0.7.0🔗
Features🔗
- 🔧 Improve typing. PR #101 by @zschumacher.
Internal🔗
- 🔧 Dependabot updates 2023-01-01. PR #106 by @zschumacher.
0.6.0🔗
Features🔗
- ⬆️ support python 3.11. PR #84 by @zschumacher.
0.5.3🔗
Internal🔗
- 🔧 Add variable length tuple typing annotation for query_multiple. PR #67 by @enewnham.
- 🔧 Dependabot updates 2022-10-28. PR #85 by @zschumacher.
- 🔧 Better developer support for arm chips. PR #52 by @zschumacher.
0.5.2🔗
Docs🔗
- 🔧 Add example for serializing one-to-many relationships to docs. PR #44 by @zschumacher.
Internal🔗
- 🔧 Address dependabot 2022-08-13. PR #51 by @zschumacher.
- 🔧 Add extra to install all optional deps. PR #50 by @zschumacher.
0.5.1🔗
Internal🔗
- 🔧 Address Dependabot PRs. PR #42 by @zschumacher.
- 🔧 Add Dependabot. PR #31 by @zschumacher.
0.5.0🔗
Features🔗
- ✨ Add
oracledbsupport. PR #25 by @troyswanson.
Internal🔗
- 🔧 Bump black to the stable release v22.3.0. PR #27 by @zschumacher.
- 🔧 use coro-context-manager. PR #23 by @zschumacher.
0.4.0🔗
Features🔗
- ✨ Add async support starting with
aiopg. PR #22 by @zschumacher.
0.3.0🔗
Features🔗
- ✨ support
PYDAPPER_DSNenvironment variable for connections. PR #21 by @zschumacher.
Internal🔗
- 🔧 Cache oracle-instantclient download in test workflow. PR #20 by @zschumacher.
0.2.0🔗
Features🔗
- ✨ Add oracle support via
cx_Oracle. PR #17 by @zschumacher.
0.1.2🔗
🚀 First stable release of pydapper!