v0.4¶
The 0.4 line modernizes Uvicore's web stack (Pydantic v2, the latest FastAPI and Starlette) and sharpens its two most foundational pieces, the database layer and the ORM, making them more expressive, more portable across database engines, and more robust.
This page tracks every release in the 0.4 line, newest first.
Python 3.12.x, 3.13.x and 3.14.x validated working with Uvicore 0.4.x versions!
0.4.2¶
Consolidates Uvicore's async HTTP client onto a single library.
Breaking: aiohttp removed
The bundled HTTP Client switched from aiohttp to
httpx, and aiohttp is no longer a dependency. Code that calls
uvicore.ioc.make('aiohttp') must migrate. See
Upgrade 0.3 to 0.4 → HTTP Client.
HTTP Client: aiohttp → httpx¶
Uvicore previously bundled two async HTTP libraries: aiohttp for the runtime client (the
http_client IoC service and the Mailgun mail backend) and httpx for the in‑process ASGI test
client. Since httpx is already part of the FastAPI/Starlette stack — and is the only one of the
two that can drive an ASGI app in‑process for tests — 0.4.2 drops aiohttp and standardizes
everything on httpx.
- Shared client is now
httpx.AsyncClient. Resolve it withuvicore.ioc.make('http_client')oruvicore.ioc.make('httpx')(the old'aiohttp'alias is gone). - Mailgun mail backend re‑implemented on httpx (
data=/files=multipart,(user, pass)basic auth). - Requests are awaited directly —
r = await http.get(url)— with noasync with, and the response is read synchronously (r.status_code,r.text,r.json()). - httpx unpinned from
0.26.*to0.28.*: the test client now uses the modernAsyncClient(transport=ASGITransport(app=...))API instead of the removedapp=shortcut.
The HTTP Client guide was rewritten around httpx, with a full aiohttp → httpx migration table.
0.4.0¶
The opening 0.4 release. The headline change is a modernized web stack (Pydantic v1 → v2, FastAPI 0.137, Starlette 1.3), alongside inline table definitions, a greatly expanded query operator set, cross-database hardening and ORM fixes.
Breaking release
The Pydantic v2 upgrade makes 0.4.0 breaking for application code, model update_forward_refs(), optional-field defaults, on_event hooks, custom validators, and serialization method names all change. Read Upgrade 0.3 to 0.4 before upgrading.
Modernized Web Stack (Pydantic v2, FastAPI, Starlette)¶
0.4.0 brings the web stack fully up to date:
- Pydantic v1 → v2. The ORM, models, responses and typing layer were ported to Pydantic v2. FastAPI dropped Pydantic v1 in 0.126, so this had to move together with the framework.
- FastAPI 0.115 → 0.137 and Starlette 0.45 → 1.3.
- Central model rebuild. Uvicore now resolves model forward references (relations) for every registered model centrally at boot, so you no longer add
update_forward_refs()/model_rebuild()to each model file (keepfrom __future__ import annotationsand the bottom-of-file relation imports). - Lifespan startup/shutdown. The HTTP server now uses Starlette's
lifespan(Starlette 1.0 removedon_event); theuvicore.http.events.server.Startup/Shutdownevents fire exactly as before.
This is the source of 0.4.0's breaking changes. The framework absorbs most of the churn, your Field() definitions, relations, tables, query builder and configs are unchanged, but a few app-side updates are required. See Upgrade 0.3 to 0.4.
Inline Table Definitions¶
ORM models can now define their schema fully inline, no separate Table class required. Set __connection__, __tablename__ and a raw __table__ list of SQLAlchemy columns, and Uvicore builds the real sa.Table for you, associating it with the connection's metadata and applying any table prefix, exactly like a Table class would.
@uvicore.model()
class Post(Model['Post'], metaclass=ModelMetaclass):
__connection__ = 'wiki'
__tablename__ = 'posts'
__table__ = [
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('unique_slug', sa.String(length=100), unique=True),
sa.Column('title', sa.String(length=100)),
]
__table_kwargs__ = {'sqlite_autoincrement': True} # optional
id: Optional[int] = Field('id', primary=True, read_only=True)
slug: str = Field('unique_slug')
title: str = Field('title')
You now have three ways to attach a table to a model, a Table class in a separate file, a Table class in the same file, or fully inline, and all three produce an identical SQLAlchemy table. See DB Tables.
Expanded Query Operators¶
The .where(), .or_where(), .filter() and .or_filter() clauses (in both the DB Query Builder and the ORM Query Builder) now support a much larger, case-insensitive, whitespace-tolerant operator set:
<>as an alias for!=<=(previously undocumented/missing)not inandnot likeas natural-language aliases of!inand!likeilike/!ilikefor case-insensitive matching that behaves the same on every databasebetween/!between(value is a[low, high]list)- explicit
is/is nullandis not/is not null
Unknown operators (and unknown columns) now raise a clear, actionable error instead of failing cryptically.
Tip
like is case-sensitive on Postgres but case-insensitive on SQLite/MySQL. Use the new ilike operator whenever you want portable, case-insensitive matching.
Cross-Database Robustness¶
0.4.0 was validated end to end against real Postgres, MySQL and MariaDB (in addition to SQLite), which surfaced and fixed several portability issues:
- Postgres just works. The
postgresdialect alias is normalized topostgresql(SQLAlchemy dropped the old alias), and additional server dialects (mariadb,mssql,oracle,cockroachdb) are recognized. - Portable auto-increment primary keys. Inserts no longer send an explicit
NULLprimary key, which SQLite tolerated but Postgres/MySQL rejected. The database now generates the key on every engine. - Type-safe
find().Model.query().find(id)coerces the value to the primary key's column type, so a string id (for example from a URL path) no longer errors withinteger = varcharon Postgres.
ORM Fixes¶
- Multiple
*Manyincludes no longer multiply. Eager-loading severalHasMany/BelongsToMany/MorphMany/MorphToManyrelations in one query previously returned a cartesian product of rows. Each relation now returns its correct cardinality. delete()andset()now work forHasMany. Previously these raised for one-to-many relations; they now delete (or replace) the related children like the other relation types.- Auto-API create fixed. The automatic Model Router
POST /{model}endpoint now returns the created record with a real primary key (it previously failed response validation).