Release Notes¶
0.1¶
Initial public beta release of Uvicore.
Used in production by a few internal companies, running a few dozen APIs and basic websites.
Not 100% feature complete. The web at large would have many requests for enhancement.
0.2¶
See Upgrade 0.1 to 0.2 for details.
Refactor of the "service" concept. The word service removed in favor or package, provider, and registers.
0.3¶
See the 0.3 Changelog and Upgrade 0.2 to 0.3 for details.
Removed the async encode/databases wrapper and upgraded SQLAlchemy from from 1.4 up to the new async SQLAlchemy 2.0.
Documentation Improvements¶
Added comprehensive documentation for HTTP web development: - New HTTP Web Guide covering web controllers, views, routing, and templates - Expanded Controllers documentation with real-world examples - Enhanced Views documentation with template patterns and best practices
0.4¶
See the 0.4 Changelog and Upgrade 0.3 to 0.4 for details.
A breaking release that modernizes the web stack and hardens the database and ORM layers.
- Modernized web stack - upgraded to Pydantic v2, FastAPI 0.137 and Starlette 1.3. This is breaking for application code: model
update_forward_refs()removal (forward refs now rebuilt centrally), explicit defaults on optional Pydantic fields,on_event→ lifespanStartup/Shutdownevents,@validator→@field_validator, and serialization renames (.dict()→.model_dump()). - Inline table definitions - define a model's schema directly on the model with
__connection__+__tablename__+ a raw__table__list, no separateTableclass required. - Expanded query operators -
ilike/!ilike,between/!between,<>,not in,not like, and explicitis/is not, all case-insensitive and whitespace tolerant. - Cross-database robustness - validated against real Postgres, MySQL and MariaDB, with portable auto-increment primary keys, type-safe
find(), andpostgres/postgresqldialect normalization. - ORM fixes - multiple
*Manyincludes no longer cartesian-multiply,HasManydelete()/set()now work, and the auto-API create endpoint is fixed. - HTTP client moved to httpx (0.4.2) - the bundled async HTTP Client switched from
aiohttptohttpxandaiohttpwas dropped entirely. This is breaking for code that callsuvicore.ioc.make('aiohttp'): requests are now awaited directly (noasync with) and the response usesr.status_code/r.text/r.json(). .envloading changed (0.4.2) -environswas upgraded to 15.x andread_env()no longer populatesos.environ, it loads only into the instance it is called on. Existing apps must changepackage/bootstrap.pyto read.envinto the sharedenvinstance (from uvicore.configuration import env; env.read_env(...)) instead of a throwawayEnv(), or their configs will silently see no.envvalues.- Composite (multi-column) relation & join keys (0.4.3) - relation
foreign_key/local_keyand the query builder's.join()now accept ordered column lists (or ansa.and_()expression) to build multi-column JOINONclauses, required for sharded backends like Vitess/PlanetScale. The same release also fixes eager-loading on non-primary-key natural keys and*Manyincludes combined with.limit()/.offset(). See the 0.4 Changelog. - Redis connection
options(0.4.5) - a Redis connection inconfig/package.pynow accepts an optionaloptionsdict whose keys are passed straight through as keyword arguments to the underlying redis-py async client (from_url()) - e.g.health_check_interval,socket_timeout,max_connections,decode_responses. This mirrors theoptionsdict on a database connection. Purely additive. See the 0.4 Changelog. - Database engine disposal at shutdown (0.4.6) - async database engines are now properly disposed when the CLI, HTTP server or pytest run ends, fixing the noisy
RuntimeError: Event loop is closedtracebacks from async driver (aiomysql/asyncpg) connection finalizers on exit. Addsawait uvicore.db.disconnect()(one connection, one metakey, orall_dbs=True) and makesuvicore.db.init()safe to call more than once - unchanged engines are reused, replaced engines are disposed instead of orphaned, and registeredMetaDatais preserved. Also backported to 0.3.18. See the 0.4 Changelog. - View composers documented (0.4.7) - the previously-undocumented view composers feature (inject shared context into every view matching a wildcard, registered with
register_http_view_composers()) now has a dedicated page. Also fixes the dict form of that method, which raisedAttributeErroron boot, and cleans up thegen composerhint and stub comments that referenced the removed pre-0.4self.composers()helper. Purely additive. See the 0.4 Changelog. - Web-stack bug fixes (0.4.8) - web error pages now return the correct HTTP status code (a rendered
errors/404.j2responds404, not200), and redirect-on-failed-authentication by route name no longer crashes with aURL is not iterableTypeError. Both surfaced while rebuilding theapp1reference web app to exercise the full web feature set. Purely additive. See the 0.4 Changelog. - Rich-powered console logging (0.4.9) -
uvicore.log's console (STDOUT/STDERR) output is now rendered with rich: full-width rule headers, glyph bullets for items, badges for notice/critical, and⚠/✖prefixes for warning/error. Theuvicore.loginterface is unchanged and the file handler stays plain/greppable.richis now a base dependency (not gated behind an extra). One behavior change:WARNING/ERROR/CRITICALnow print to STDERR (was STDOUT); setlogger.console.colors = Falsefor the previous plain STDOUT-only output. See the 0.4 Changelog. - Date-stamped log files and multiple logs per app (0.4.10) - put strftime tokens in your log path (
/var/log/acme.wiki/%Y-%m-%d_{channel}.log) and the date becomes part of the filename, rolling onto the next day's file by itself with nothing ever renamed - which finally makes it safe foruvicorn --workers Nor a CLI command to share one log with the server (rename-based rotation silently wedges every process but the one that wins the rename, and it never rotates again). Newretention(days) prunes old dated files. Separately, new channels give each feature of an app its own log file -uvicore.log.channel('Processor').info(...)writing2026-07-29_Processor.log- with the full logger interface, config inheritance, and no double-writes into the default log.name()'s one-shot scope is now task-local (ContextVar) so concurrent tasks can no longer misattribute each other's log lines. Fully backward compatible: a path with no strftime tokens keeps the legacy rotation behavior exactly. Logging also finally has a dedicated docs page. See the 0.4 Changelog. - Leaner OpenAPI / Swagger docs (0.4.3) - the API server now collapses FastAPI's duplicate
Foo-Input/Foo-Outputschemas into a singleFoo(newapi.openapi.separate_schemas, defaultFalse), and new Swagger knobs keep the docs responsive for apps with large model graphs:api.openapi.docs.models_expansion(default-1) hides the standalone Schemas section,docs.model_expansioncollapses each operation's nested model tree for instant expand, anddocs.parameterspasses arbitrary settings straight to Swagger UI. Together they roughly halveopenapi.jsonsize and keep the UI snappy. See the 0.4 Changelog. - Configurable engine pooling + Snowflake connections that outlive four hours (0.4.11) - a database connection now takes an optional
pooldict (pre_ping,recycle,size,max_overflow,timeout,use_lifo,reset_on_return) mapping onto SQLAlchemy'screate_engine()pool_*arguments;pre_pingwas previously hardcodedTrueand unchangeable, and was applied to sync engines only, so async connections silently had no stale-connection protection - both branches are now configured identically (async gainspre_ping; opt out with'pool': {'pre_ping': False}). Separately, Snowflake connections no longer die ~4 hours after boot with390114: Authentication token has expired: the connector never renews the ~4h master token, and becausesnowflake-sqlalchemydeclares nois_disconnect(), SQLAlchemy kept returning the dead connection to the pool and handing it back out until the process was restarted. Uvicore now defaults Snowflake connections toclient_session_keep_alive(a credit-free heartbeat that refreshes the token) and teaches each Snowflake engine that the terminal token codes are disconnects, so a token that dies anyway invalidates the pool and the next query re-authenticates. Both are defaults you can override per connection. See the 0.4 Changelog.