Skip to content

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 → lifespan Startup/Shutdown events, @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 separate Table class required.
  • Expanded query operators - ilike/!ilike, between/!between, <>, not in, not like, and explicit is / 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(), and postgres/postgresql dialect normalization.
  • ORM fixes - multiple *Many includes no longer cartesian-multiply, HasMany delete()/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 aiohttp to httpx and aiohttp was dropped entirely. This is breaking for code that calls uvicore.ioc.make('aiohttp'): requests are now awaited directly (no async with) and the response uses r.status_code/r.text/r.json().
  • .env loading changed (0.4.2) - environs was upgraded to 15.x and read_env() no longer populates os.environ, it loads only into the instance it is called on. Existing apps must change package/bootstrap.py to read .env into the shared env instance (from uvicore.configuration import env; env.read_env(...)) instead of a throwaway Env(), or their configs will silently see no .env values.
  • Composite (multi-column) relation & join keys (0.4.3) - relation foreign_key/local_key and the query builder's .join() now accept ordered column lists (or an sa.and_() expression) to build multi-column JOIN ON clauses, required for sharded backends like Vitess/PlanetScale. The same release also fixes eager-loading on non-primary-key natural keys and *Many includes combined with .limit()/.offset(). See the 0.4 Changelog.
  • Redis connection options (0.4.5) - a Redis connection in config/package.py now accepts an optional options dict 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 the options dict 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 closed tracebacks from async driver (aiomysql/asyncpg) connection finalizers on exit. Adds await uvicore.db.disconnect() (one connection, one metakey, or all_dbs=True) and makes uvicore.db.init() safe to call more than once - unchanged engines are reused, replaced engines are disposed instead of orphaned, and registered MetaData is 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 raised AttributeError on boot, and cleans up the gen composer hint and stub comments that referenced the removed pre-0.4 self.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.j2 responds 404, not 200), and redirect-on-failed-authentication by route name no longer crashes with a URL is not iterable TypeError. Both surfaced while rebuilding the app1 reference 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. The uvicore.log interface is unchanged and the file handler stays plain/greppable. rich is now a base dependency (not gated behind an extra). One behavior change: WARNING/ERROR/CRITICAL now print to STDERR (was STDOUT); set logger.console.colors = False for 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 for uvicorn --workers N or 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). New retention (days) prunes old dated files. Separately, new channels give each feature of an app its own log file - uvicore.log.channel('Processor').info(...) writing 2026-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-Output schemas into a single Foo (new api.openapi.separate_schemas, default False), 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_expansion collapses each operation's nested model tree for instant expand, and docs.parameters passes arbitrary settings straight to Swagger UI. Together they roughly halve openapi.json size 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 pool dict (pre_ping, recycle, size, max_overflow, timeout, use_lifo, reset_on_return) mapping onto SQLAlchemy's create_engine() pool_* arguments; pre_ping was previously hardcoded True and unchangeable, and was applied to sync engines only, so async connections silently had no stale-connection protection - both branches are now configured identically (async gains pre_ping; opt out with 'pool': {'pre_ping': False}). Separately, Snowflake connections no longer die ~4 hours after boot with 390114: Authentication token has expired: the connector never renews the ~4h master token, and because snowflake-sqlalchemy declares no is_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 to client_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.