Migrating from v0.x to v1.x
Lightkube v1 keeps the public client and resource APIs familiar, but changes a
few implementation dependencies and fixes two streaming issues.
Use this guide to check code that uses Lightkube internals, custom resources,
httpx types, or retry handlers.
Lightkube v1 requires Python 3.10 or newer. This is also the minimum Python
version required by httpx2, which is used by Lightkube v1.
Migrate from httpx to httpx2
Lightkube v1 is now using httpx2 instead of
httpx. The change matters if your code passes httpx objects into Lightkube
or catches httpx exceptions raised by it.
For example, update the HTTP types passed when creating a Client:
# v0
import httpx
from lightkube import Client
transport: httpx.BaseTransport = ...
client = Client(timeout=httpx.Timeout(30.0), transport=transport)
# v1
import httpx2
from lightkube import Client
transport: httpx2.BaseTransport = ...
client = Client(timeout=httpx2.Timeout(30.0), transport=transport)
For AsyncClient, use httpx2.AsyncBaseTransport for the transport instead.
Update exception handlers separately:
# v0
import httpx
try:
...
except httpx.HTTPError:
...
# v1
import httpx2
try:
...
except httpx2.HTTPError:
...
Alternatively, you can alias httpx2 as httpx to reduce the number of code
changes:
import httpx2 as httpx
If your application uses httpx for another dependency, keep that dependency
as needed, but use httpx2 for objects and exceptions exchanged with
Lightkube.
Models use msgspec.Struct
Lightkube's generated models now use
msgspec.Struct instead of
standard-library dataclasses. Normal use of generated models is unchanged:
construct them, access their attributes, and use from_dict and to_dict as
before.
The benefit is that msgspec provides schema-aware, optimized encoding and decoding with low allocation overhead. It can convert directly between bytes and typed Struct instances, avoiding the intermediate standard Python object representation. This is faster and more memory efficient while retaining typed model fields.
In the benchmark of a 700-pod response, v1 decodes a Pod list into fully
materialized typed objects about 12x faster than v0 with lazy=False. It is
also about 2.2x faster than the v0 lazy=True measurement, although lazy
decoding deferred part of the conversion work until fields were accessed.
Custom resources
In preparation for the msgspec migration, the lightkube.core.schema
compatibility layer has been available since Lightkube v0.15.1. Its use is
required for custom resource models from v1 onward, so make this source change
when upgrading. You can also apply it before upgrading.
Change:
# v0.*
from dataclasses import dataclass, field
from lightkube.core.dataclasses_dict import DataclassDictMixIn
to:
# v1.*
from lightkube.core.schema import dataclass, field, DictMixin
Models need to subclass DictMixin instead of DataclassDictMixIn, as shown in
the custom resources guide. The dataclass decorator is
provided for source compatibility, but it is now a no-op. The field helper
translates dataclass-style field metadata for msgspec.
If your code imports DataclassDictMixIn or other helpers directly from
lightkube.core.dataclasses_dict, migrate those imports to the corresponding
exports from lightkube.core.schema. The dataclasses_dict implementation is
replaced by the msgspec-backed compatibility layer in v1.
Remove lazy decoding and custom dict factories
msgspec decodes Structs eagerly, so lazy model decoding is no longer
available. The lazy argument is retained for compatibility in some APIs,
but it has no effect. In particular, passing lazy=True to
DictMixin.from_dict emits a DeprecationWarning; use the default eager
decoding instead and remove lazy arguments from your code.
The non-default dict_factory argument to DictMixin.to_dict is also
deprecated. Call to_dict() to receive a normal dict, then transform that
dictionary separately if a custom mapping type is required.
Pod logs with follow
Client.log and AsyncClient.log now disable the HTTP read timeout when
follow=True. This allows a long-lived log stream to remain open while the
container is temporarily quiet. The configured timeout still applies to the
other timeout phases and to non-following log requests.
No call-site change is required, but code that expected an idle followed log stream to fail with a read-timeout exception should now close or cancel the stream explicitly.
Recovering watches after a 410 response
When Client.watch or AsyncClient.watch is configured to retry, a Kubernetes
410 Gone response means that the saved resourceVersion is too old. v1
clears that version before retrying the request. Kubernetes can then return a
fresh state and the watch can continue instead of retrying forever with the
same invalid version.
The default error handler still raises errors. Opt into retry as before:
from lightkube import Client
from lightkube.resources.core_v1 import Pod
from lightkube.types import on_error_retry
with Client() as client:
for event, pod in client.watch(Pod, on_error=on_error_retry):
print(event, pod.metadata.name)
Custom handlers that return OnErrorAction.RETRY receive the same 410
recovery behavior.
Upgrade checklist
- Change Lightkube-related
httpximports and exception handlers tohttpx2, or useimport httpx2 as httpx. - Change custom resource models to import
dataclass,fieldandDictMixinfromlightkube.core.schema. - Remove reliance on lazy decoding and non-default
dict_factoryvalues. - Review followed log streams and watch retry handlers if their previous failure behavior was part of your application logic.