Repository: gabrieldemarmiesse/python-on-whales (language: python)

Issue:
Migrate to pydantic v2
Hi, and thanks for an awesome library. It has really helped my current project with automating integration- and end-to-end tests using docker compose. 

We are looking to migrate to pydantic v2, which python-on-whales does not yet support. This is of course understandable since the first stable pydantic v2 released june 30 (two days when writing this).

If you would like me to submit a PR for this migration i would gladly help :) 


Discussion / hints:
I would be open to such a PR :) python-on-whales should be compatible with both versions
So if i understand correctly, in `requirements.txt` it would look like e.g `pydantic>=1.5,<3
`?
Most changes from pydantic v1 to v2 involves renames, which will work but give `DeprecationWarnings` when using v2. 

I found there are mainly two things in the repo that would need to be changed to be able to support both pydantic v1 and v2: 
* the [`@all_fields_optional` ](https://github.com/gabrieldemarmiesse/python-on-whales/blob/af04c5de231f586f8a108e55e482a7235c80639d/python_on_whales/utils.py#L297-L302) decorator can no longer be used, because of changes to the `FieldInfo`-class. To solve this, it looks like I have to change all pydantic-classes from e.g
  ```python
  @all_fields_optional
  class ImageGraphDriver(DockerCamelModel):
      name: str
      data: Any
  ```
  to

  ```python
  class ImageGraphDriver(DockerCamelModel):
      name: Optional[str] = None
      data: Optional[Any] = None
  ```
  This also seems to be the direction pydantic will go in the future, as [stated in the migration guide](https://docs.pydantic.dev/latest/migration/#required-optional-and-nullable-fields)
* The `pydantic.error_wrappers` are removed in v2. I believe importing it as `pydantic.ValidationError` can be changed [here](https://github.com/gabrieldemarmiesse/python-on-whales/blob/af04c5de231f586f8a108e55e482a7235c80639d/python_on_whales/client_config.py#L245)

>it would look like e.g pydantic>=1.5,<3 ?

Yes

> the [@all_fields_optional ](https://github.com/gabrieldemarmiesse/python-on-whales/blob/af04c5de231f586f8a108e55e482a7235c80639d/python_on_whales/utils.py#L297-L302)decorator can no longer be used

That makes me really sad. Isn't there really any way to recreate it? That would be a massive change to put all field optional.

>The pydantic.error_wrappers are removed in v2. I believe importing it as pydantic.ValidationError can be changed [here](https://github.com/gabrieldemarmiesse/python-on-whales/blob/af04c5de231f586f8a108e55e482a7235c80639d/python_on_whales/client_config.py#L245)

That's fine by me :) 

> > the [@all_fields_optional ](https://github.com/gabrieldemarmiesse/python-on-whales/blob/af04c5de231f586f8a108e55e482a7235c80639d/python_on_whales/utils.py#L297-L302)decorator can no longer be used
> 
> That makes me really sad. Isn't there really any way to recreate it? That would be a massive change to put all field optional.

Yeah, me too. In my current project we have several pydantic models where we want all the different attributes to be optional, and we went ahead and did it the way i described above. Pydantic v2 is still young, and a lot of the docs has not been updated for the new version yet, so there hopefully might be some way of accomplishing something similar in v2. But if one wants to support both v1 and v2 it seems like there is some breaking changes to the `FieldInfo`, e.g the attributes used by the decorator, `field.required` and `field.allow_none` no longer exists

With my limited experience with pydantic, I can see some benefit from "explicit over implicit" (this did previously confuse me on a previous contribution to this project).
A first step: https://github.com/gabrieldemarmiesse/python-on-whales/pull/454

Fix patch:
diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml
index aeedef68..6fade89a 100644
--- a/.github/workflows/python-package.yml
+++ b/.github/workflows/python-package.yml
@@ -61,6 +61,9 @@ jobs:
           - test_system.py
           - test_task.py
           - test_volume.py
+        pydantic-version:
+          - pydantic==1.*
+          - pydantic==2.*
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
@@ -70,6 +73,7 @@ jobs:
       - name: Run test
         run: |
           bash tests/ci-setup.sh
+          pip install --upgrade ${{ matrix.pydantic-version }}
           python -m pytest -v --durations=10 tests/python_on_whales/components/${{ matrix.component }}
 
   build-linux-test-without-any-binary:
diff --git a/python_on_whales/client_config.py b/python_on_whales/client_config.py
index 9cc553e0..aaca6488 100644
--- a/python_on_whales/client_config.py
+++ b/python_on_whales/client_config.py
@@ -242,7 +242,7 @@ def _fetch_and_parse_inspect_result(self, reference: str):
         json_object = json.loads(json_str)[0]
         try:
             return self._parse_json_object(json_object)
-        except pydantic.error_wrappers.ValidationError as err:
+        except pydantic.ValidationError as err:
             fd, json_response_file = tempfile.mkstemp(suffix=".json", text=True)
             with open(json_response_file, "w") as f:
                 f.write(json_str)
diff --git a/python_on_whales/components/buildx/imagetools/models.py b/python_on_whales/components/buildx/imagetools/models.py
index 14df659e..4db4ece7 100644
--- a/python_on_whales/components/buildx/imagetools/models.py
+++ b/python_on_whales/components/buildx/imagetools/models.py
@@ -3,37 +3,38 @@
 from typing import List, Optional
 
 import pydantic
+from typing_extensions import Annotated
 
 
 class ManifestConfig(pydantic.BaseModel):
-    media_type: Optional[str] = pydantic.Field(alias="mediaType")
-    digest: Optional[str]
-    size: Optional[int]
+    media_type: Annotated[Optional[str], pydantic.Field(alias="mediaType")] = None
+    digest: Optional[str] = None
+    size: Optional[int] = None
 
 
 class ManifestLayer(pydantic.BaseModel):
-    media_type: Optional[str] = pydantic.Field(alias="mediaType")
-    digest: Optional[str]
-    size: Optional[int]
+    media_type: Annotated[Optional[str], pydantic.Field(alias="mediaType")] = None
+    digest: Optional[str] = None
+    size: Optional[int] = None
 
 
 class ManifestPlatform(pydantic.BaseModel):
-    architecture: Optional[str]
-    os: Optional[str]
-    os_version: Optional[str]
-    variant: Optional[str]
+    architecture: Optional[str] = None
+    os: Optional[str] = None
+    os_version: Optional[str] = None
+    variant: Optional[str] = None
 
 
 class ImageVariantManifest(pydantic.BaseModel):
-    media_type: Optional[str] = pydantic.Field(alias="mediaType")
-    size: Optional[int]
-    digest: Optional[str]
-    platform: Optional[ManifestPlatform]
+    media_type: Annotated[Optional[str], pydantic.Field(alias="mediaType")] = None
+    size: Optional[int] = None
+    digest: Optional[str] = None
+    platform: Optional[ManifestPlatform] = None
 
 
 class Manifest(pydantic.BaseModel):
-    media_type: Optional[str] = pydantic.Field(alias="mediaType")
-    schema_version: Optional[int] = pydantic.Field(alias="schemaVersion")
-    layers: Optional[List[ManifestLayer]]
-    manifests: Optional[List[ImageVariantManifest]]
-    config: Optional[ManifestConfig]
+    media_type: Annotated[Optional[str], pydantic.Field(alias="mediaType")]
+    schema_version: Annotated[Optional[int], pydantic.Field(alias="schemaVersion")]
+    layers: Optional[List[ManifestLayer]] = None
+    manifests: Optional[List[ImageVariantManifest]] = None
+    config: Optional[ManifestConfig] = None
diff --git a/python_on_whales/components/compose/models.py b/python_on_whales/components/compose/models.py
index 548e0717..5d0323f9 100644
--- a/python_on_whales/components/compose/models.py
+++ b/python_on_whales/components/compose/models.py
@@ -2,120 +2,110 @@
 from typing import Any, Dict, List, Optional, Union
 
 from pydantic import BaseModel, Field
+from typing_extensions import Annotated
 
-from python_on_whales.utils import all_fields_optional
 
-
-@all_fields_optional
 class ServicePlacement(BaseModel):
     constraints: Optional[List[str]] = None
 
 
-@all_fields_optional
 class ResourcesLimits(BaseModel):
-    cpus: Optional[float]
-    memory: Optional[int]
+    cpus: Optional[float] = None
+    memory: Optional[int] = None
 
 
-@all_fields_optional
 class ResourcesReservation(BaseModel):
-    cpus: Union[float, str, None]
-    memory: Optional[int]
+    cpus: Union[float, int, None] = None
+    memory: Optional[int] = None
 
 
-@all_fields_optional
 class ServiceResources(BaseModel):
-    limits: Optional[ResourcesLimits]
-    reservations: Optional[ResourcesReservation]
+    limits: Optional[ResourcesLimits] = None
+    reservations: Optional[ResourcesReservation] = None
 
 
-@all_fields_optional
 class ServiceDeployConfig(BaseModel):
-    labels: Optional[Dict[str, str]]
-    resources: Optional[ServiceResources]
-    placement: Optional[ServicePlacement]
-    replicas: Optional[int]
+    labels: Optional[Dict[str, str]] = None
+    resources: Optional[ServiceResources] = None
+    placement: Optional[ServicePlacement] = None
+    replicas: Optional[int] = None
 
 
-@all_fields_optional
 class DependencyCondition(BaseModel):
-    condition: Optional[str]
+    condition: Optional[str] = None
 
 
-@all_fields_optional
 class ComposeServiceBuild(BaseModel):
-    context: Optional[Path]
+    context: Optional[Path] = None
 
 
-@all_fields_optional
 class ComposeServicePort(BaseModel):
-    mode: Optional[str]
-    protocol: Optional[str]
-    published: Optional[int]
-    target: Optional[int]
+    mode: Optional[str] = None
+    protocol: Optional[str] = None
+    published: Optional[int] = None
+    target: Optional[int] = None
 
 
-@all_fields_optional
 class ComposeServiceVolume(BaseModel):
-    bind: Optional[dict]
-    source: Optional[str]
-    target: Optional[str]
-    type: Optional[str]
+    bind: Optional[dict] = None
+    source: Optional[str] = None
+    target: Optional[str] = None
+    type: Optional[str] = None
 
 
-@all_fields_optional
 class ComposeConfigService(BaseModel):
-    deploy: Optional[ServiceDeployConfig]
-    blkio_config: Optional[Any]
-    cpu_count: Optional[float]
-    cpu_percent: Optional[float]
-    cpu_shares: Optional[int]
-    cpuset: Optional[str]
-    build: Optional[ComposeServiceBuild]
-    cap_add: Optional[List[str]] = Field(default_factory=list)
-    cap_drop: Optional[List[str]] = Field(default_factory=list)
-    cgroup_parent: Optional[str]
-    command: Optional[List[str]]
+    deploy: Optional[ServiceDeployConfig] = None
+    blkio_config: Optional[Any] = None
+    cpu_count: Optional[float] = None
+    cpu_percent: Optional[float] = None
+    cpu_shares: Optional[int] = None
+    cpuset: Optional[str] = None
+    build: Optional[ComposeServiceBuild] = None
+    cap_add: Annotated[Optional[List[str]], Field(default_factory=list)]
+    cap_drop: Annotated[Optional[List[str]], Field(default_factory=list)]
+    cgroup_parent: Optional[str] = None
+    command: Optional[List[str]] = None
     configs: Any = None
-    container_name: Optional[str]
-    depends_on: Dict[str, DependencyCondition] = Field(default_factory=dict)
-    device_cgroup_rules: List[str] = Field(default_factory=list)
+    container_name: Optional[str] = None
+    depends_on: Annotated[Dict[str, DependencyCondition], Field(default_factory=dict)]
+    device_cgroup_rules: Annotated[List[str], Field(default_factory=list)]
     devices: Any = None
-    environment: Optional[Dict[str, Optional[str]]]
-    entrypoint: Optional[List[str]]
-    image: Optional[str]
-    labels: Optional[Dict[str, str]] = Field(default_factory=dict)
-    ports: Optional[List[ComposeServicePort]]
-    volumes: Optional[List[ComposeServiceVolume]]
+    environment: Optional[Dict[str, Optional[str]]] = None
+    entrypoint: Optional[List[str]] = None
+    image: Optional[str] = None
+    labels: Annotated[Optional[Dict[str, str]], Field(default_factory=dict)]
+    ports: Optional[List[ComposeServicePort]] = None
+    volumes: Optional[List[ComposeServiceVolume]] = None
 
 
-@all_fields_optional
 class ComposeConfigNetwork(BaseModel):
-    driver: Optional[str]
-    name: Optional[str]
+    driver: Optional[str] = None
+    name: Optional[str] = None
     external: Optional[bool] = False
-    driver_opts: Optional[Dict[str, Any]]
-    attachable: Optional[bool]
-    enable_ipv6: Optional[bool]
+    driver_opts: Optional[Dict[str, Any]] = None
+    attachable: Optional[bool] = None
+    enable_ipv6: Optional[bool] = None
     ipam: Any = None
-    internal: Optional[bool]
-    labels: Dict[str, str] = Field(default_factory=dict)
+    internal: Optional[bool] = None
+    labels: Annotated[Dict[str, str], Field(default_factory=dict)]
 
 
-@all_fields_optional
 class ComposeConfigVolume(BaseModel):
-    driver: Optional[str]
-    driver_opts: Optional[Dict[str, Any]]
-    external: Optional[bool]
-    labels: Optional[Dict[str, str]] = Field(default_factory=dict)
-    name: Optional[str]
+    driver: Optional[str] = None
+    driver_opts: Optional[Dict[str, Any]] = None
+    external: Optional[bool] = None
+    labels: Annotated[Optional[Dict[str, str]], Field(default_factory=dict)]
+    name: Optional[str] = None
 
 
-@all_fields_optional
 class ComposeConfig(BaseModel):
-    services: Optional[Dict[str, ComposeConfigService]]
-    networks: Optional[Dict[str, ComposeConfigNetwork]] = Field(default_factory=dict)
-    volumes: Optional[Dict[str, ComposeConfigVolume]] = Field(default_factory=dict)
+    services: Optional[Dict[str, ComposeConfigService]] = None
+    networks: Annotated[
+        Optional[Dict[str, ComposeConfigNetwork]], Field(default_factory=dict)
+    ]
+    volumes: Annotated[
+        Optional[Dict[str, ComposeConfigVolume]], Field(default_factory=dict)
+    ]
     configs: Any = None
     secrets: Any = None
 
@@ -128,4 +118,4 @@ class ComposeProject(BaseModel):
     exited: Optional[int] = 0
     paused: Optional[int] = 0
     dead: Optional[int] = 0
-    config_files: Optional[List[Path]]
+    config_files: Optional[List[Path]] = None
diff --git a/python_on_whales/components/config/models.py b/python_on_whales/components/config/models.py
index f910c093..d40a6b5c 100644
--- a/python_on_whales/components/config/models.py
+++ b/python_on_whales/components/config/models.py
@@ -2,6 +2,7 @@
 from typing import Any, Dict, Optional
 
 import pydantic
+from typing_extensions import Annotated
 
 from python_on_whales.utils import DockerCamelModel
 
@@ -16,15 +17,15 @@ class ConfigSpecDriver(DockerCamelModel):
 
 
 class ConfigSpec(DockerCamelModel):
-    name: Optional[str]
-    labels: Optional[Dict[str, str]]
-    data: Optional[str]
-    templating: Optional[ConfigSpecDriver]
+    name: Optional[str] = None
+    labels: Optional[Dict[str, str]] = None
+    data: Optional[str] = None
+    templating: Optional[ConfigSpecDriver] = None
 
 
 class ConfigInspectResult(DockerCamelModel):
-    id: Optional[str] = pydantic.Field(None, alias="ID")
-    version: Optional[DockerObjectVersion]
-    created_at: Optional[datetime]
-    updated_at: Optional[datetime]
-    spec: Optional[ConfigSpec]
+    id: Annotated[Optional[str], pydantic.Field(alias="ID")] = None
+    version: Optional[DockerObjectVersion] = None
+    created_at: Optional[datetime] = None
+    updated_at: Optional[datetime] = None
+    spec: Optional[ConfigSpec] = None
diff --git a/python_on_whales/components/container/models.py b/python_on_whales/components/container/models.py
index ebc51268..58250512 100644
--- a/python_on_whales/components/container/models.py
+++ b/python_on_whales/components/container/models.py
@@ -3,336 +3,311 @@
 from typing import Any, Dict, List, Optional, Tuple, Union
 
 import pydantic
+from typing_extensions import Annotated
 
-from python_on_whales.utils import DockerCamelModel, all_fields_optional
+from python_on_whales.utils import DockerCamelModel
 
 
-@all_fields_optional
 class ContainerHealthcheckResult(DockerCamelModel):
-    start: Optional[datetime]
-    end: Optional[datetime]
-    exit_code: Optional[int]
-    output: Optional[str]
+    start: Optional[datetime] = None
+    end: Optional[datetime] = None
+    exit_code: Optional[int] = None
+    output: Optional[str] = None
 
 
-@all_fields_optional
 class ContainerHealth(DockerCamelModel):
-    status: Optional[str]
-    failing_streak: Optional[int]
-    log: Optional[List[ContainerHealthcheckResult]]
+    status: Optional[str] = None
+    failing_streak: Optional[int] = None
+    log: Optional[List[ContainerHealthcheckResult]] = None
 
 
-@all_fields_optional
 class ContainerState(DockerCamelModel):
-    status: Optional[str]
-    running: Optional[bool]
-    paused: Optional[bool]
-    restarting: Optional[bool]
-    oom_killed: Optional[bool]
-    dead: Optional[bool]
-    pid: Optional[int]
-    exit_code: Optional[int]
-    error: Optional[str]
-    started_at: Optional[datetime]
-    finished_at: Optional[datetime]
-    health: Optional[ContainerHealth]
-
-
-@all_fields_optional
+    status: Optional[str] = None
+    running: Optional[bool] = None
+    paused: Optional[bool] = None
+    restarting: Optional[bool] = None
+    oom_killed: Optional[bool] = None
+    dead: Optional[bool] = None
+    pid: Optional[int] = None
+    exit_code: Optional[int] = None
+    error: Optional[str] = None
+    started_at: Optional[datetime] = None
+    finished_at: Optional[datetime] = None
+    health: Optional[ContainerHealth] = None
+
+
 class ContainerWeightDevice(DockerCamelModel):
-    path: Optional[Path]
-    weight: Optional[int]
+    path: Optional[Path] = None
+    weight: Optional[int] = None
 
 
-@all_fields_optional
 class ContainerThrottleDevice(DockerCamelModel):
-    path: Optional[Path]
-    rate: Optional[int]
+    path: Optional[Path] = None
+    rate: Optional[int] = None
 
 
-@all_fields_optional
 class ContainerDevice(DockerCamelModel):
-    path_on_host: Optional[Path]
-    path_in_container: Optional[Path]
-    cgroup_permissions: Optional[str]
+    path_on_host: Optional[Path] = None
+    path_in_container: Optional[Path] = None
+    cgroup_permissions: Optional[str] = None
 
 
-@all_fields_optional
 class ContainerDeviceRequest(DockerCamelModel):
-    driver: Optional[str]
-    count: Optional[int]
-    device_ids: Optional[List[str]] = pydantic.Field(None, alias="DeviceIDs")
-    capabilities: Optional[List[Any]]
-    options: Optional[Dict[str, str]]
+    driver: Optional[str] = None
+    count: Optional[int] = None
+    device_ids: Annotated[Optional[List[str]], pydantic.Field(alias="DeviceIDs")] = None
+    capabilities: Optional[List[Any]] = None
+    options: Optional[Dict[str, str]] = None
 
 
-@all_fields_optional
 class ContainerUlimit(DockerCamelModel):
-    name: Optional[str]
-    soft: Optional[int]
-    hard: Optional[int]
+    name: Optional[str] = None
+    soft: Optional[int] = None
+    hard: Optional[int] = None
 
 
-@all_fields_optional
 class ContainerLogConfig(DockerCamelModel):
-    type: Optional[str]
-    config: Optional[Any]
+    type: Optional[str] = None
+    config: Optional[Any] = None
 
 
-@all_fields_optional
 class ContainerRestartPolicy(DockerCamelModel):
-    name: Optional[str]
-    maximum_retry_count: Optional[int]
+    name: Optional[str] = None
+    maximum_retry_count: Optional[int] = None
 
 
-@all_fields_optional
 class PortBinding(DockerCamelModel):
-    host_ip: Optional[str]
-    host_port: Optional[str]
+    host_ip: Optional[str] = None
+    host_port: Optional[str] = None
 
 
-@all_fields_optional
 class ContainerMountBindOption(DockerCamelModel):
-    propagation: Optional[str]
-    non_recursive: Optional[bool]
+    propagation: Optional[str] = None
+    non_recursive: Optional[bool] = None
 
 
-@all_fields_optional
 class ContainerVolumeDriverConfig(DockerCamelModel):
-    name: Optional[str]
-    options: Optional[Dict[str, Any]]
+    name: Optional[str] = None
+    options: Optional[Dict[str, Any]] = None
 
 
-@all_fields_optional
 class ContainerVolumeOptions(DockerCamelModel):
-    no_copy: Optional[bool]
-    labels: Optional[Dict[str, str]]
+    no_copy: Optional[bool] = None
+    labels: Optional[Dict[str, str]] = None
 
 
-@all_fields_optional
 class ContainerTmpfsOptions(DockerCamelModel):
-    size_bytes: Optional[int]
-    mode: Optional[int]
+    size_bytes: Optional[int] = None
+    mode: Optional[int] = None
 
 
-@all_fields_optional
 class ContainerMount(DockerCamelModel):
-    target: Optional[Path]
-    source: Optional[str]
-    type: Optional[str]
-    read_only: Optional[bool]
-    consistency: Optional[str]
-    bind_options: Optional[ContainerMountBindOption]
-    volume_options: Optional[ContainerVolumeOptions]
-    tmpfs_options: Optional[ContainerTmpfsOptions]
+    target: Optional[Path] = None
+    source: Optional[str] = None
+    type: Optional[str] = None
+    read_only: Optional[bool] = None
+    consistency: Optional[str] = None
+    bind_options: Optional[ContainerMountBindOption] = None
+    volume_options: Optional[ContainerVolumeOptions] = None
+    tmpfs_options: Optional[ContainerTmpfsOptions] = None
 
 
-@all_fields_optional
 class ContainerHostConfig(DockerCamelModel):
-    cpu_shares: Optional[int]
-    memory: Optional[int]
-    cgroup_parent: Optional[Path]
-    blkio_weight: Optional[int]
-    blkio_weight_device: Optional[List[ContainerWeightDevice]]
-    blkio_device_read_bps: Optional[List[ContainerThrottleDevice]]
-    blkio_device_write_bps: Optional[List[ContainerThrottleDevice]]
-    blkio_device_read_iops: Optional[List[ContainerThrottleDevice]]
-    blkio_device_write_iops: Optional[List[ContainerThrottleDevice]]
-    cpu_period: Optional[int]
-    cpu_quota: Optional[int]
-    cpu_realtime_period: Optional[int]
-    cpu_realtime_runtime: Optional[int]
-    cpuset_cpus: Optional[str]
-    cpuset_mems: Optional[str]
-    devices: Optional[List[ContainerDevice]]
-    device_cgroup_rules: Optional[List[str]]
-    device_requests: Optional[List[ContainerDeviceRequest]]
-    kernel_memory: Optional[int]
-    kernel_memory_tcp: Optional[int]
-    memory_reservation: Optional[int]
-    memory_swap: Optional[int]
-    memory_swappiness: Optional[int]
-    nano_cpus: Optional[int]
-    oom_kill_disable: Optional[bool]
-    init: Optional[bool]
-    pids_limit: Optional[int]
-    ulimits: Optional[List[ContainerUlimit]]
-    cpu_count: Optional[int]
-    cpu_percent: Optional[int]
-    binds: Optional[List[str]]
-    container_id_file: Optional[Path]
-    log_config: Optional[ContainerLogConfig]
-    network_mode: Optional[str]
-    port_bindings: Optional[Dict[str, Optional[List[PortBinding]]]]
-    restart_policy: Optional[ContainerRestartPolicy]
-    auto_remove: Optional[bool]
-    volume_driver: Optional[str]
-    volumes_from: Optional[List[str]]
-    mounts: Optional[List[ContainerMount]]
-    capabilities: Optional[List[str]]
-    cap_add: Optional[List[str]]
-    cap_drop: Optional[List[str]]
-    cgroupns_mode: Optional[str]
-    dns: Optional[List[str]]
-    dns_options: Optional[List[str]]
-    dns_search: Optional[List[str]]
-    extra_hosts: Optional[List[str]]
-    group_add: Optional[List[str]]
-    ipc_mode: Optional[str]
-    cgroup: Optional[str]
-    links: Optional[List[str]]
-    oom_score_adj: Optional[int]
-    pid_mode: Optional[str]
-    privileged: Optional[bool]
-    publish_all_ports: Optional[bool]
-    readonly_rootfs: Optional[bool]
-    security_opt: Optional[List[str]]
+    cpu_shares: Optional[int] = None
+    memory: Optional[int] = None
+    cgroup_parent: Optional[Path] = None
+    blkio_weight: Optional[int] = None
+    blkio_weight_device: Optional[List[ContainerWeightDevice]] = None
+    blkio_device_read_bps: Optional[List[ContainerThrottleDevice]] = None
+    blkio_device_write_bps: Optional[List[ContainerThrottleDevice]] = None
+    blkio_device_read_iops: Optional[List[ContainerThrottleDevice]] = None
+    blkio_device_write_iops: Optional[List[ContainerThrottleDevice]] = None
+    cpu_period: Optional[int] = None
+    cpu_quota: Optional[int] = None
+    cpu_realtime_period: Optional[int] = None
+    cpu_realtime_runtime: Optional[int] = None
+    cpuset_cpus: Optional[str] = None
+    cpuset_mems: Optional[str] = None
+    devices: Optional[List[ContainerDevice]] = None
+    device_cgroup_rules: Optional[List[str]] = None
+    device_requests: Optional[List[ContainerDeviceRequest]] = None
+    kernel_memory: Optional[int] = None
+    kernel_memory_tcp: Optional[int] = None
+    memory_reservation: Optional[int] = None
+    memory_swap: Optional[int] = None
+    memory_swappiness: Optional[int] = None
+    nano_cpus: Optional[int] = None
+    oom_kill_disable: Optional[bool] = None
+    init: Optional[bool] = None
+    pids_limit: Optional[int] = None
+    ulimits: Optional[List[ContainerUlimit]] = None
+    cpu_count: Optional[int] = None
+    cpu_percent: Optional[int] = None
+    binds: Optional[List[str]] = None
+    container_id_file: Optional[Path] = None
+    log_config: Optional[ContainerLogConfig] = None
+    network_mode: Optional[str] = None
+    port_bindings: Optional[Dict[str, Optional[List[PortBinding]]]] = None
+    restart_policy: Optional[ContainerRestartPolicy] = None
+    auto_remove: Optional[bool] = None
+    volume_driver: Optional[str] = None
+    volumes_from: Optional[List[str]] = None
+    mounts: Optional[List[ContainerMount]] = None
+    capabilities: Optional[List[str]] = None
+    cap_add: Optional[List[str]] = None
+    cap_drop: Optional[List[str]] = None
+    cgroupns_mode: Optional[str] = None
+    dns: Optional[List[str]] = None
+    dns_options: Optional[List[str]] = None
+    dns_search: Optional[List[str]] = None
+    extra_hosts: Optional[List[str]] = None
+    group_add: Optional[List[str]] = None
+    ipc_mode: Optional[str] = None
+    cgroup: Optional[str] = None
+    links: Optional[List[str]] = None
+    oom_score_adj: Optional[int] = None
+    pid_mode: Optional[str] = None
+    privileged: Optional[bool] = None
+    publish_all_ports: Optional[bool] = None
+    readonly_rootfs: Optional[bool] = None
+    security_opt: Optional[List[str]] = None
     storage_opt: Any = None
-    tmpfs: Optional[Dict[Path, str]]
-    uts_mode: Optional[str]
-    userns_mode: Optional[str]
-    shm_size: Optional[int]
-    sysctls: Optional[Dict[str, Any]]
-    runtime: Optional[str]
-    console_size: Optional[Tuple[int, int]]
-    isolation: Optional[str]
-    masked_paths: Optional[List[Path]]
-    readonly_paths: Optional[List[Path]]
-
-
-@all_fields_optional
+    tmpfs: Optional[Dict[Path, str]] = None
+    uts_mode: Optional[str] = None
+    userns_mode: Optional[str] = None
+    shm_size: Optional[int] = None
+    sysctls: Optional[Dict[str, Any]] = None
+    runtime: Optional[str] = None
+    console_size: Optional[Tuple[int, int]] = None
+    isolation: Optional[str] = None
+    masked_paths: Optional[List[Path]] = None
+    readonly_paths: Optional[List[Path]] = None
+
+
 class ContainerHealthCheck(DockerCamelModel):
-    test: Optional[List[str]]
-    interval: Optional[int]
-    timeout: Optional[int]
-    retries: Optional[int]
-    start_period: Optional[int]
+    test: Optional[List[str]] = None
+    interval: Optional[int] = None
+    timeout: Optional[int] = None
+    retries: Optional[int] = None
+    start_period: Optional[int] = None
 
 
-@all_fields_optional
 class ContainerConfig(DockerCamelModel):
-    hostname: Optional[str]
-    domainname: Optional[str]
-    user: Optional[str]
-    attach_stdin: Optional[bool]
-    attach_stdout: Optional[bool]
-    attach_stderr: Optional[bool]
-    exposed_ports: Optional[dict]
-    tty: Optional[bool]
-    open_stdin: Optional[bool]
-    stdin_once: Optional[bool]
-    env: Optional[List[str]]
-    cmd: Optional[List[str]]
-    healthcheck: Optional[ContainerHealthCheck]
-    args_escaped: Optional[bool]
-    image: Optional[str]
-    volumes: Optional[dict]
-    working_dir: Optional[Path]
+    hostname: Optional[str] = None
+    domainname: Optional[str] = None
+    user: Optional[str] = None
+    attach_stdin: Optional[bool] = None
+    attach_stdout: Optional[bool] = None
+    attach_stderr: Optional[bool] = None
+    exposed_ports: Optional[dict] = None
+    tty: Optional[bool] = None
+    open_stdin: Optional[bool] = None
+    stdin_once: Optional[bool] = None
+    env: Optional[List[str]] = None
+    cmd: Optional[List[str]] = None
+    healthcheck: Optional[ContainerHealthCheck] = None
+    args_escaped: Optional[bool] = None
+    image: Optional[str] = None
+    volumes: Optional[dict] = None
+    working_dir: Optional[Path] = None
     entrypoint: Union[List[str], str, None] = None
-    network_disabled: Optional[bool]
-    mac_address: Optional[str]
-    on_build: Optional[List[str]]
-    labels: Optional[Dict[str, str]]
-    stop_signal: Optional[str]
-    stop_timeout: Optional[int]
-    shell: Optional[List[str]]
+    network_disabled: Optional[bool] = None
+    mac_address: Optional[str] = None
+    on_build: Optional[List[str]] = None
+    labels: Optional[Dict[str, str]] = None
+    stop_signal: Optional[str] = None
+    stop_timeout: Optional[int] = None
+    shell: Optional[List[str]] = None
 
 
-@all_fields_optional
 class Mount(DockerCamelModel):
-    type: Optional[str]
-    name: Optional[str]
-    source: Optional[str]
-    destination: Optional[str]
-    driver: Optional[str]
-    mode: Optional[str]
-    rw: Optional[bool]
-    propagation: Optional[str]
+    type: Optional[str] = None
+    name: Optional[str] = None
+    source: Optional[str] = None
+    destination: Optional[str] = None
+    driver: Optional[str] = None
+    mode: Optional[str] = None
+    rw: Optional[bool] = None
+    propagation: Optional[str] = None
 
 
-@all_fields_optional
 class ContainerEndpointIPAMConfig(DockerCamelModel):
-    ipv4_address: Optional[str]
-    ipv6_address: Optional[str]
-    link_local_ips: Optional[List[str]]
+    ipv4_address: Optional[str] = None
+    ipv6_address: Optional[str] = None
+    link_local_ips: Optional[List[str]] = None
 
 
-@all_fields_optional
 class NetworkInspectResult(DockerCamelModel):
-    ipam_config: Optional[ContainerEndpointIPAMConfig]
-    links: Optional[List[str]]
-    aliases: Optional[List[str]]
-    network_id: Optional[str]
-    endpoint_id: Optional[str]
-    gateway: Optional[str]
-    ip_address: Optional[str]
-    ip_prefix_length: Optional[int]
-    ipv6_gateway: Optional[str]
-    global_ipv6_address: Optional[str]
-    global_ipv6_prefix_length: Optional[int]
-    mac_address: Optional[str]
-    driver_options: Optional[dict]
-
-
-@all_fields_optional
+    ipam_config: Optional[ContainerEndpointIPAMConfig] = None
+    links: Optional[List[str]] = None
+    aliases: Optional[List[str]] = None
+    network_id: Optional[str] = None
+    endpoint_id: Optional[str] = None
+    gateway: Optional[str] = None
+    ip_address: Optional[str] = None
+    ip_prefix_length: Optional[int] = None
+    ipv6_gateway: Optional[str] = None
+    global_ipv6_address: Optional[str] = None
+    global_ipv6_prefix_length: Optional[int] = None
+    mac_address: Optional[str] = None
+    driver_options: Optional[dict] = None
+
+
 class ContainerNetworkAddress(DockerCamelModel):
-    addr: Optional[str]
-    prefix_len: Optional[int]
+    addr: Optional[str] = None
+    prefix_len: Optional[int] = None
 
 
-@all_fields_optional
 class NetworkSettings(DockerCamelModel):
-    bridge: Optional[str]
-    sandbox_id: Optional[str]
-    hairpin_mode: Optional[bool]
-    link_local_ipv6_address: Optional[str]
-    link_local_ipv6_prefix_length: Optional[int]
-    ports: Optional[dict]  # to rework
-    sandbox_key: Optional[Path]
-    secondary_ip_addresses: Optional[List[ContainerNetworkAddress]]
-    secondary_ipv6_addresses: Optional[List[ContainerNetworkAddress]]
-    endpoint_id: Optional[str]
-    gateway: Optional[str]
-    global_ipv6_address: Optional[str]
-    global_ipv6_prefix_length: Optional[int]
-    ip_address: Optional[str]
-    ip_prefix_length: Optional[int]
-    ipv6_gateway: Optional[str]
-    mac_address: Optional[str]
-    networks: Optional[Dict[str, NetworkInspectResult]]
-
-
-@all_fields_optional
+    bridge: Optional[str] = None
+    sandbox_id: Optional[str] = None
+    hairpin_mode: Optional[bool] = None
+    link_local_ipv6_address: Optional[str] = None
+    link_local_ipv6_prefix_length: Optional[int] = None
+    ports: Optional[dict] = None  # to rework
+    sandbox_key: Optional[Path] = None
+    secondary_ip_addresses: Optional[List[ContainerNetworkAddress]] = None
+    secondary_ipv6_addresses: Optional[List[ContainerNetworkAddress]] = None
+    endpoint_id: Optional[str] = None
+    gateway: Optional[str] = None
+    global_ipv6_address: Optional[str] = None
+    global_ipv6_prefix_length: Optional[int] = None
+    ip_address: Optional[str] = None
+    ip_prefix_length: Optional[int] = None
+    ipv6_gateway: Optional[str] = None
+    mac_address: Optional[str] = None
+    networks: Optional[Dict[str, NetworkInspectResult]] = None
+
+
 class ContainerGraphDriver(DockerCamelModel):
-    name: Optional[str]
-    data: Optional[Dict[str, Any]]
+    name: Optional[str] = None
+    data: Optional[Dict[str, Any]] = None
 
 
-@all_fields_optional
 class ContainerInspectResult(DockerCamelModel):
-    id: Optional[str]
-    created: Optional[datetime]
-    path: Optional[str]
-    args: Optional[List[str]]
-    state: Optional[ContainerState]
-    image: Optional[str]
-    resolv_conf_path: Optional[str]
-    hostname_path: Optional[Path]
-    hosts_path: Optional[Path]
-    log_path: Optional[Path]
+    id: Optional[str] = None
+    created: Optional[datetime] = None
+    path: Optional[str] = None
+    args: Optional[List[str]] = None
+    state: Optional[ContainerState] = None
+    image: Optional[str] = None
+    resolv_conf_path: Optional[str] = None
+    hostname_path: Optional[Path] = None
+    hosts_path: Optional[Path] = None
+    log_path: Optional[Path] = None
     node: Any = None
-    name: Optional[str]
-    restart_count: Optional[int]
-    driver: Optional[str]
-    platform: Optional[str]
-    mount_label: Optional[str]
-    process_label: Optional[str]
-    app_armor_profile: Optional[str]
-    exec_ids: Optional[List[str]]
-    host_config: Optional[ContainerHostConfig]
-    graph_driver: Optional[ContainerGraphDriver]
-    size_rw: Optional[int]
-    size_root_fs: Optional[int]
-    mounts: Optional[List[Mount]]
-    config: Optional[ContainerConfig]
-    network_settings: Optional[NetworkSettings]
+    name: Optional[str] = None
+    restart_count: Optional[int] = None
+    driver: Optional[str] = None
+    platform: Optional[str] = None
+    mount_label: Optional[str] = None
+    process_label: Optional[str] = None
+    app_armor_profile: Optional[str] = None
+    exec_ids: Optional[List[str]] = None
+    host_config: Optional[ContainerHostConfig] = None
+    graph_driver: Optional[ContainerGraphDriver] = None
+    size_rw: Optional[int] = None
+    size_root_fs: Optional[int] = None
+    mounts: Optional[List[Mount]] = None
+    config: Optional[ContainerConfig] = None
+    network_settings: Optional[NetworkSettings] = None
diff --git a/python_on_whales/components/context/models.py b/python_on_whales/components/context/models.py
index 14883b71..ab6363ef 100644
--- a/python_on_whales/components/context/models.py
+++ b/python_on_whales/components/context/models.py
@@ -1,23 +1,26 @@
 from typing import Dict, Optional
 
 import pydantic
+from typing_extensions import Annotated
 
 from python_on_whales.utils import DockerCamelModel
 
 
 class ContextEndpoint(DockerCamelModel):
-    host: Optional[str]
-    skip_tls_verify: Optional[bool] = pydantic.Field(alias="SkipTLSVerify")
+    host: Optional[str] = None
+    skip_tls_verify: Annotated[
+        Optional[bool], pydantic.Field(alias="SkipTLSVerify")
+    ] = None
 
 
 class ContextStorage(DockerCamelModel):
-    metadata_path: Optional[str]
-    tls_path: Optional[str] = pydantic.Field(None, alias="TLSPath")
+    metadata_path: Optional[str] = None
+    tls_path: Annotated[Optional[str], pydantic.Field(alias="TLSPath")] = None
 
 
 class ContextInspectResult(DockerCamelModel):
-    name: Optional[str]
-    metadata: Optional[Dict[str, str]]
-    endpoints: Optional[Dict[str, ContextEndpoint]]
-    tls_material: Optional[dict] = pydantic.Field(None, alias="TLSMaterial")
-    storage: Optional[ContextStorage]
+    name: Optional[str] = None
+    metadata: Optional[Dict[str, str]] = None
+    endpoints: Optional[Dict[str, ContextEndpoint]] = None
+    tls_material: Annotated[Optional[dict], pydantic.Field(alias="TLSMaterial")] = None
+    storage: Optional[ContextStorage] = None
diff --git a/python_on_whales/components/image/cli_wrapper.py b/python_on_whales/components/image/cli_wrapper.py
index 9768b6a2..f095b2a8 100644
--- a/python_on_whales/components/image/cli_wrapper.py
+++ b/python_on_whales/components/image/cli_wrapper.py
@@ -24,6 +24,7 @@
 )
 from python_on_whales.exceptions import DockerException, NoSuchImage
 from python_on_whales.utils import (
+    PYDANTIC_V2,
     ValidPath,
     format_dict_for_cli,
     run,
@@ -48,7 +49,10 @@ def _fetch_inspect_result_json(self, reference):
         return run(self.docker_cmd + ["image", "inspect", reference])
 
     def _parse_json_object(self, json_object: Dict[str, Any]) -> ImageInspectResult:
-        return ImageInspectResult.parse_obj(json_object)
+        if PYDANTIC_V2:
+            return ImageInspectResult.model_validate(json_object)
+        else:
+            return ImageInspectResult.parse_obj(json_object)
 
     def _get_inspect_result(self) -> ImageInspectResult:
         """Only there to allow tools to know the return type"""
diff --git a/python_on_whales/components/image/models.py b/python_on_whales/components/image/models.py
index 12eb6304..7e7be968 100644
--- a/python_on_whales/components/image/models.py
+++ b/python_on_whales/components/image/models.py
@@ -2,51 +2,49 @@
 from typing import Any, Dict, List, Optional
 
 import python_on_whales.components.container.models
-from python_on_whales.utils import DockerCamelModel, all_fields_optional
+from python_on_whales.utils import DockerCamelModel
 
 
-@all_fields_optional
 class ImageHealthcheck(DockerCamelModel):
-    test: Optional[List[str]]
-    interval: Optional[int]
-    timeout: Optional[int]
-    retries: Optional[int]
-    start_period: Optional[int]
+    test: Optional[List[str]] = None
+    interval: Optional[int] = None
+    timeout: Optional[int] = None
+    retries: Optional[int] = None
+    start_period: Optional[int] = None
 
 
-@all_fields_optional
 class ImageGraphDriver(DockerCamelModel):
-    name: Optional[str]
+    name: Optional[str] = None
     data: Any = None
 
 
-@all_fields_optional
 class ImageRootFS(DockerCamelModel):
-    type: Optional[str]
-    layers: Optional[List[str]]
-    base_layer: Optional[str]
+    type: Optional[str] = None
+    layers: Optional[List[str]] = None
+    base_layer: Optional[str] = None
 
 
-@all_fields_optional
 class ImageInspectResult(DockerCamelModel):
-    id: Optional[str]
-    repo_tags: Optional[List[str]]
-    repo_digests: Optional[List[str]]
-    parent: Optional[str]
-    comment: Optional[str]
-    created: Optional[datetime]
-    container: Optional[str]
+    id: Optional[str] = None
+    repo_tags: Optional[List[str]] = None
+    repo_digests: Optional[List[str]] = None
+    parent: Optional[str] = None
+    comment: Optional[str] = None
+    created: Optional[datetime] = None
+    container: Optional[str] = None
     container_config: Optional[
         python_on_whales.components.container.models.ContainerConfig
-    ]
-    docker_version: Optional[str]
-    author: Optional[str]
-    config: Optional[python_on_whales.components.container.models.ContainerConfig]
-    architecture: Optional[str]
-    os: Optional[str]
-    os_version: Optional[str]
-    size: Optional[int]
-    virtual_size: Optional[int]
-    graph_driver: Optional[ImageGraphDriver]
-    root_fs: Optional[ImageRootFS]
-    metadata: Optional[Dict[str, str]]
+    ] = None
+    docker_version: Optional[str] = None
+    author: Optional[str] = None
+    config: Optional[
+        python_on_whales.components.container.models.ContainerConfig
+    ] = None
+    architecture: Optional[str] = None
+    os: Optional[str] = None
+    os_version: Optional[str] = None
+    size: Optional[int] = None
+    virtual_size: Optional[int] = None
+    graph_driver: Optional[ImageGraphDriver] = None
+    root_fs: Optional[ImageRootFS] = None
+    metadata: Optional[Dict[str, str]] = None
diff --git a/python_on_whales/components/manifest/models.py b/python_on_whales/components/manifest/models.py
index 2fafcaf2..bd6fb5f1 100644
--- a/python_on_whales/components/manifest/models.py
+++ b/python_on_whales/components/manifest/models.py
@@ -1,12 +1,11 @@
 from typing import List, Optional
 
 from python_on_whales.components.buildx.imagetools.models import ImageVariantManifest
-from python_on_whales.utils import DockerCamelModel, all_fields_optional
+from python_on_whales.utils import DockerCamelModel
 
 
-@all_fields_optional
 class ManifestListInspectResult(DockerCamelModel):
-    name: Optional[str]
-    schema_version: Optional[int]
-    media_type: Optional[str]
-    manifests: Optional[List[ImageVariantManifest]]
+    name: Optional[str] = None
+    schema_version: Optional[int] = None
+    media_type: Optional[str] = None
+    manifests: Optional[List[ImageVariantManifest]] = None
diff --git a/python_on_whales/components/network/models.py b/python_on_whales/components/network/models.py
index 501115f0..f53caf94 100644
--- a/python_on_whales/components/network/models.py
+++ b/python_on_whales/components/network/models.py
@@ -1,39 +1,36 @@
 from datetime import datetime
 from typing import Any, Dict, List, Optional
 
-from python_on_whales.utils import DockerCamelModel, all_fields_optional
+from python_on_whales.utils import DockerCamelModel
 
 
-@all_fields_optional
 class NetworkIPAM(DockerCamelModel):
-    driver: Optional[str]
-    config: Optional[List[Dict[str, Any]]]
-    options: Optional[Dict[str, Any]]
+    driver: Optional[str] = None
+    config: Optional[List[Dict[str, Any]]] = None
+    options: Optional[Dict[str, Any]] = None
 
 
-@all_fields_optional
 class NetworkContainer(DockerCamelModel):
-    name: Optional[str]
-    endpoint_id: Optional[str]
-    mac_address: Optional[str]
-    ipv4_address: Optional[str]
-    ipv6_address: Optional[str]
+    name: Optional[str] = None
+    endpoint_id: Optional[str] = None
+    mac_address: Optional[str] = None
+    ipv4_address: Optional[str] = None
+    ipv6_address: Optional[str] = None
 
 
-@all_fields_optional
 class NetworkInspectResult(DockerCamelModel):
-    name: Optional[str]
-    id: Optional[str]
-    created: Optional[datetime]
-    scope: Optional[str]
-    driver: Optional[str]
-    enable_ipv6: Optional[bool]
-    ipam: Optional[NetworkIPAM]
-    internal: Optional[bool]
-    attachable: Optional[bool]
-    ingress: Optional[bool]
-    containers: Optional[Dict[str, NetworkContainer]]
-    options: Optional[Dict[str, Any]]
-    labels: Optional[Dict[str, str]]
-    config_from: Optional[dict]
-    config_only: Optional[bool]
+    name: Optional[str] = None
+    id: Optional[str] = None
+    created: Optional[datetime] = None
+    scope: Optional[str] = None
+    driver: Optional[str] = None
+    enable_ipv6: Optional[bool] = None
+    ipam: Optional[NetworkIPAM] = None
+    internal: Optional[bool] = None
+    attachable: Optional[bool] = None
+    ingress: Optional[bool] = None
+    containers: Optional[Dict[str, NetworkContainer]] = None
+    options: Optional[Dict[str, Any]] = None
+    labels: Optional[Dict[str, str]] = None
+    config_from: Optional[dict] = None
+    config_only: Optional[bool] = None
diff --git a/python_on_whales/components/node/models.py b/python_on_whales/components/node/models.py
index 9e57ee8f..42537fbc 100644
--- a/python_on_whales/components/node/models.py
+++ b/python_on_whales/components/node/models.py
@@ -2,104 +2,91 @@
 from typing import Dict, List, Optional
 
 from pydantic import Field
+from typing_extensions import Annotated
 
-from python_on_whales.utils import DockerCamelModel, all_fields_optional
+from python_on_whales.utils import DockerCamelModel
 
 
-@all_fields_optional
 class NodeVersion(DockerCamelModel):
-    index: Optional[int]
+    index: Optional[int] = None
 
 
-@all_fields_optional
 class NodeSpec(DockerCamelModel):
-    name: Optional[str]
-    labels: Optional[Dict[str, str]]
-    role: Optional[str]
-    availability: Optional[str]
+    name: Optional[str] = None
+    labels: Optional[Dict[str, str]] = None
+    role: Optional[str] = None
+    availability: Optional[str] = None
 
 
-@all_fields_optional
 class NodePlatform(DockerCamelModel):
-    architecture: Optional[str]
-    os: Optional[str] = Field(None, alias="OS")
+    architecture: Optional[str] = None
+    os: Annotated[Optional[str], Field(alias="OS")] = None
 
 
-@all_fields_optional
 class NodeNamedResourceSpec(DockerCamelModel):
-    kind: Optional[str]
-    value: Optional[str]
+    kind: Optional[str] = None
+    value: Optional[str] = None
 
 
-@all_fields_optional
 class NodeDiscreteResourceSpec(DockerCamelModel):
-    kind: Optional[str]
-    value: Optional[int]
+    kind: Optional[str] = None
+    value: Optional[int] = None
 
 
-@all_fields_optional
 class NodeGenericResource(DockerCamelModel):
-    named_resource_spec: Optional[NodeNamedResourceSpec]
-    discrete_resource_spec: Optional[NodeDiscreteResourceSpec]
+    named_resource_spec: Optional[NodeNamedResourceSpec] = None
+    discrete_resource_spec: Optional[NodeDiscreteResourceSpec] = None
 
 
-@all_fields_optional
 class NodeResource(DockerCamelModel):
-    nano_cpus: Optional[int] = Field(None, alias="NanoCPUs")
-    memory_bytes: Optional[int]
-    generic_resources: Optional[List[NodeGenericResource]]
+    nano_cpus: Annotated[Optional[int], Field(alias="NanoCPUs")] = None
+    memory_bytes: Optional[int] = None
+    generic_resources: Optional[List[NodeGenericResource]] = None
 
 
-@all_fields_optional
 class EnginePlugin(DockerCamelModel):
-    type: Optional[str]
-    name: Optional[str]
+    type: Optional[str] = None
+    name: Optional[str] = None
 
 
-@all_fields_optional
 class NodeEngine(DockerCamelModel):
-    engine_version: Optional[str]
-    labels: Optional[Dict[str, str]]
-    plugins: Optional[List[EnginePlugin]]
+    engine_version: Optional[str] = None
+    labels: Optional[Dict[str, str]] = None
+    plugins: Optional[List[EnginePlugin]] = None
 
 
-@all_fields_optional
 class NodeTLSInfo(DockerCamelModel):
-    trust_root: Optional[str]
-    cert_issuer_subject: Optional[str]
-    cert_issuer_public_key: Optional[str]
+    trust_root: Optional[str] = None
+    cert_issuer_subject: Optional[str] = None
+    cert_issuer_public_key: Optional[str] = None
 
 
-@all_fields_optional
 class NodeDescription(DockerCamelModel):
-    hostname: Optional[str]
-    platform: Optional[NodePlatform]
-    resources: Optional[NodeResource]
-    engine: Optional[NodeEngine]
-    tls_info: Optional[NodeTLSInfo]
+    hostname: Optional[str] = None
+    platform: Optional[NodePlatform] = None
+    resources: Optional[NodeResource] = None
+    engine: Optional[NodeEngine] = None
+    tls_info: Optional[NodeTLSInfo] = None
 
 
-@all_fields_optional
 class NodeStatus(DockerCamelModel):
-    state: Optional[str]
-    message: Optional[str]
-    addr: Optional[str]
+    state: Optional[str] = None
+    message: Optional[str] = None
+    addr: Optional[str] = None
 
 
-@all_fields_optional
 class NodeManagerStatus(DockerCamelModel):
-    leader: Optional[bool]
-    reachability: Optional[str]
-    addr: Optional[str]
+    leader: Optional[bool] = None
+    reachability: Optional[str] = None
+    addr: Optional[str] = None
 
 
-@all_fields_optional
 class NodeInspectResult(DockerCamelModel):
-    id: Optional[str] = Field(None, alias="ID")
-    version: Optional[NodeVersion]
-    created_at: Optional[datetime]
-    updated_at: Optional[datetime]
-    spec: Optional[NodeSpec]
-    description: Optional[NodeDescription]
-    status: Optional[NodeStatus]
-    manager_status: Optional[NodeManagerStatus]
+    id: Annotated[Optional[str], Field(alias="ID")] = None
+    version: Optional[NodeVersion] = None
+    created_at: Optional[datetime] = None
+    updated_at: Optional[datetime] = None
+    spec: Optional[NodeSpec] = None
+    description: Optional[NodeDescription] = None
+    status: Optional[NodeStatus] = None
+    manager_status: Optional[NodeManagerStatus] = None
diff --git a/python_on_whales/components/plugin/models.py b/python_on_whales/components/plugin/models.py
index e8af95e0..7509676c 100644
--- a/python_on_whales/components/plugin/models.py
+++ b/python_on_whales/components/plugin/models.py
@@ -1,47 +1,41 @@
 from typing import List, Optional
 
-from python_on_whales.utils import DockerCamelModel, all_fields_optional
+from python_on_whales.utils import DockerCamelModel
 
 
-@all_fields_optional
 class PluginMount(DockerCamelModel):
     pass
 
 
-@all_fields_optional
 class PluginDevice(DockerCamelModel):
     pass
 
 
-@all_fields_optional
 class PluginSettings(DockerCamelModel):
-    mounts: Optional[List[PluginMount]]
-    env: Optional[List[str]]
-    args: Optional[List[str]]
-    devices: Optional[List[PluginDevice]]
+    mounts: Optional[List[PluginMount]] = None
+    env: Optional[List[str]] = None
+    args: Optional[List[str]] = None
+    devices: Optional[List[PluginDevice]] = None
 
 
-@all_fields_optional
 class Interface(DockerCamelModel):
     pass
 
 
-@all_fields_optional
 class PluginConfig(DockerCamelModel):
-    docker_version: Optional[str]
-    description: Optional[str]
-    documentation: Optional[str]
-    interface: Optional[Interface]
-    entrypoint: Optional[List[str]]
-    work_dir: Optional[str]
+    docker_version: Optional[str] = None
+    description: Optional[str] = None
+    documentation: Optional[str] = None
+    interface: Optional[Interface] = None
+    entrypoint: Optional[List[str]] = None
+    work_dir: Optional[str] = None
     # TODO: add missing attributes
 
 
-@all_fields_optional
 class PluginInspectResult(DockerCamelModel):
-    id: Optional[str]
-    name: Optional[str]
-    enabled: Optional[bool]
-    settings: Optional[PluginSettings]
-    plugin_reference: Optional[str]
-    config: Optional[PluginConfig]
+    id: Optional[str] = None
+    name: Optional[str] = None
+    enabled: Optional[bool] = None
+    settings: Optional[PluginSettings] = None
+    plugin_reference: Optional[str] = None
+    config: Optional[PluginConfig] = None
diff --git a/python_on_whales/components/secret/models.py b/python_on_whales/components/secret/models.py
index b50ce324..08ec79e5 100644
--- a/python_on_whales/components/secret/models.py
+++ b/python_on_whales/components/secret/models.py
@@ -1,13 +1,12 @@
 from datetime import datetime
 from typing import Optional
 
-from python_on_whales.utils import DockerCamelModel, all_fields_optional
+from python_on_whales.utils import DockerCamelModel
 
 
-@all_fields_optional
 class SecretInspectResult(DockerCamelModel):
-    id: Optional[str]
-    version: Optional[dict]
-    created_at: Optional[datetime]
-    updated_at: Optional[datetime]
-    spec: Optional[dict]
+    id: Optional[str] = None
+    version: Optional[dict] = None
+    created_at: Optional[datetime] = None
+    updated_at: Optional[datetime] = None
+    spec: Optional[dict] = None
diff --git a/python_on_whales/components/service/models.py b/python_on_whales/components/service/models.py
index 2618750f..84b267b8 100644
--- a/python_on_whales/components/service/models.py
+++ b/python_on_whales/components/service/models.py
@@ -2,105 +2,93 @@
 from typing import Any, Dict, List, Optional
 
 from pydantic import Field
+from typing_extensions import Annotated
 
-from python_on_whales.utils import DockerCamelModel, all_fields_optional
+from python_on_whales.utils import DockerCamelModel
 
 
-@all_fields_optional
 class CPUMemoryQuotas(DockerCamelModel):
-    nano_cpus: Optional[int] = Field(None, alias="NanoCPUs")
-    memory_bytes: Optional[int]
+    nano_cpus: Annotated[Optional[int], Field(alias="NanoCPUs")] = None
+    memory_bytes: Optional[int] = None
 
 
-@all_fields_optional
 class Resources(DockerCamelModel):
-    limits: Optional[CPUMemoryQuotas]
-    reservations: Optional[CPUMemoryQuotas]
+    limits: Optional[CPUMemoryQuotas] = None
+    reservations: Optional[CPUMemoryQuotas] = None
 
 
-@all_fields_optional
 class ContainerSpec(DockerCamelModel):
-    image: Optional[str]
-    labels: Optional[Dict[str, str]]
-    privileges: Optional[Dict[str, Optional[str]]]
-    stop_grace_period: Optional[int]
-    isolation: Optional[str]
-    env: Optional[List[str]]
+    image: Optional[str] = None
+    labels: Optional[Dict[str, str]] = None
+    privileges: Optional[Dict[str, Optional[str]]] = None
+    stop_grace_period: Optional[int] = None
+    isolation: Optional[str] = None
+    env: Optional[List[str]] = None
 
 
-@all_fields_optional
 class TaskTemplate(DockerCamelModel):
-    container_spec: Optional[ContainerSpec]
-    resources: Optional[Resources]
+    container_spec: Optional[ContainerSpec] = None
+    resources: Optional[Resources] = None
 
 
-@all_fields_optional
 class ChangeConfig(DockerCamelModel):
-    parallelism: Optional[int]
-    failure_action: Optional[str]
-    monitor: Optional[int]
-    max_failure_ratio: Optional[int]
-    order: Optional[str]
+    parallelism: Optional[int] = None
+    failure_action: Optional[str] = None
+    monitor: Optional[int] = None
+    max_failure_ratio: Optional[float] = None
+    order: Optional[str] = None
 
 
-@all_fields_optional
 class ServiceSpec(DockerCamelModel):
-    name: Optional[str]
-    labels: Optional[Dict[str, str]]
-    mode: Optional[Dict[str, Any]]
-    update_config: Optional[ChangeConfig]
-    rollback_config: Optional[ChangeConfig]
-    task_template: Optional[TaskTemplate]
+    name: Optional[str] = None
+    labels: Optional[Dict[str, str]] = None
+    mode: Optional[Dict[str, Any]] = None
+    update_config: Optional[ChangeConfig] = None
+    rollback_config: Optional[ChangeConfig] = None
+    task_template: Optional[TaskTemplate] = None
 
 
-@all_fields_optional
 class ServiceVersion(DockerCamelModel):
-    index: Optional[int]
+    index: Optional[int] = None
 
 
-@all_fields_optional
 class EndpointPortConfig(DockerCamelModel):
-    name: Optional[str]
-    protocol: Optional[str]
-    target_port: Optional[int]
-    published_port: Optional[int]
-    publish_mode: Optional[str]
+    name: Optional[str] = None
+    protocol: Optional[str] = None
+    target_port: Optional[int] = None
+    published_port: Optional[int] = None
+    publish_mode: Optional[str] = None
 
 
-@all_fields_optional
 class ServiceEndpointSpec(DockerCamelModel):
-    mode: Optional[str]
-    ports: Optional[List[EndpointPortConfig]]
+    mode: Optional[str] = None
+    ports: Optional[List[EndpointPortConfig]] = None
 
 
-@all_fields_optional
 class VirtualIP(DockerCamelModel):
-    network_id: Optional[str]
-    addr: Optional[str]
+    network_id: Optional[str] = None
+    addr: Optional[str] = None
 
 
-@all_fields_optional
 class ServiceEndpoint(DockerCamelModel):
-    spec: Optional[ServiceEndpointSpec]
-    ports: Optional[List[EndpointPortConfig]]
-    virtual_ips: Optional[List[VirtualIP]]
+    spec: Optional[ServiceEndpointSpec] = None
+    ports: Optional[List[EndpointPortConfig]] = None
+    virtual_ips: Optional[List[VirtualIP]] = None
 
 
-@all_fields_optional
 class ServiceUpdateStatus(DockerCamelModel):
-    state: Optional[str]
-    started_at: Optional[str]
-    completed_at: Optional[str]
-    message: Optional[str]
+    state: Optional[str] = None
+    started_at: Optional[str] = None
+    completed_at: Optional[str] = None
+    message: Optional[str] = None
 
 
-@all_fields_optional
 class ServiceInspectResult(DockerCamelModel):
-    id: Optional[str] = Field(None, alias="ID")
-    version: Optional[ServiceVersion]
-    created_at: Optional[datetime]
-    updated_at: Optional[datetime]
-    spec: Optional[ServiceSpec]
-    previous_spec: Optional[ServiceSpec]
-    endpoint: Optional[ServiceEndpoint]
-    update_status: Optional[ServiceUpdateStatus]
+    id: Annotated[Optional[str], Field(alias="ID")] = None
+    version: Optional[ServiceVersion] = None
+    created_at: Optional[datetime] = None
+    updated_at: Optional[datetime] = None
+    spec: Optional[ServiceSpec] = None
+    previous_spec: Optional[ServiceSpec] = None
+    endpoint: Optional[ServiceEndpoint] = None
+    update_status: Optional[ServiceUpdateStatus] = None
diff --git a/python_on_whales/components/system/models.py b/python_on_whales/components/system/models.py
index f67cad1f..d5ac5885 100644
--- a/python_on_whales/components/system/models.py
+++ b/python_on_whales/components/system/models.py
@@ -3,227 +3,219 @@
 from typing import Any, Dict, List, Optional
 
 import pydantic
+from typing_extensions import Annotated
 
 import python_on_whales.components.node.models
-from python_on_whales.utils import DockerCamelModel, all_fields_optional
+from python_on_whales.utils import DockerCamelModel
 
 
-@all_fields_optional
 class DockerEventActor(DockerCamelModel):
-    id: Optional[str] = pydantic.Field(None, alias="ID")
-    attributes: Optional[Dict[str, Any]] = pydantic.Field(None, alias="Attributes")
+    id: Annotated[Optional[str], pydantic.Field(alias="ID")] = None
+    attributes: Annotated[
+        Optional[Dict[str, Any]], pydantic.Field(alias="Attributes")
+    ] = None
 
 
 class DockerEvent(DockerCamelModel):
-    type: Optional[str] = pydantic.Field(None, alias="Type")
-    action: Optional[str] = pydantic.Field(None, alias="Action")
-    actor: Optional[DockerEventActor] = pydantic.Field(None, alias="Actor")
-    time: Optional[datetime] = pydantic.Field(None, alias="time")
-    time_nano: Optional[int] = pydantic.Field(None, alias="timeNano")
+    type: Annotated[Optional[str], pydantic.Field(alias="Type")] = None
+    action: Annotated[Optional[str], pydantic.Field(alias="Action")] = None
+    actor: Annotated[Optional[DockerEventActor], pydantic.Field(alias="Actor")] = None
+    time: Annotated[Optional[datetime], pydantic.Field(alias="time")] = None
+    time_nano: Annotated[Optional[int], pydantic.Field(alias="timeNano")] = None
 
 
-@all_fields_optional
 class DockerItemsSummary(DockerCamelModel):
-    active: Optional[int]
-    reclaimable: Optional[pydantic.ByteSize]
-    reclaimable_percent: Optional[float]
-    size: Optional[pydantic.ByteSize]
-    total_count: Optional[int]
+    active: Optional[int] = None
+    reclaimable: Optional[pydantic.ByteSize] = None
+    reclaimable_percent: Optional[float] = None
+    size: Optional[pydantic.ByteSize] = None
+    total_count: Optional[int] = None
 
 
-@all_fields_optional
 class Plugins(DockerCamelModel):
-    volume: Optional[List[str]]
-    network: Optional[List[str]]
+    volume: Optional[List[str]] = None
+    network: Optional[List[str]] = None
     authorization: Any = None
-    log: Optional[List[str]]
+    log: Optional[List[str]] = None
 
 
-@all_fields_optional
 class Runtime(DockerCamelModel):
-    path: Optional[str]
-    runtime_args: Optional[List[str]]
+    path: Optional[str] = None
+    runtime_args: Optional[List[str]] = None
 
 
-@all_fields_optional
 class Commit(DockerCamelModel):
-    id: Optional[str] = pydantic.Field(None, alias="ID")
-    expected: Optional[str]
+    id: Annotated[Optional[str], pydantic.Field(alias="ID")] = None
+    expected: Optional[str] = None
 
 
-@all_fields_optional
 class RemoteManager(DockerCamelModel):
-    node_id: Optional[str] = pydantic.Field(None, alias="NodeID")
-    addr: Optional[str]
+    node_id: Annotated[Optional[str], pydantic.Field(alias="NodeID")] = None
+    addr: Optional[str] = None
 
 
-@all_fields_optional
 class Orchestration(DockerCamelModel):
-    task_history_retention_limit: Optional[int]
+    task_history_retention_limit: Optional[int] = None
 
 
-@all_fields_optional
 class Raft(DockerCamelModel):
-    snapshot_interval: Optional[int]
-    keep_old_snapshots: Optional[int]
-    log_entries_for_slow_followers: Optional[int]
-    election_tick: Optional[int]
-    heartbeat_tick: Optional[int]
+    snapshot_interval: Optional[int] = None
+    keep_old_snapshots: Optional[int] = None
+    log_entries_for_slow_followers: Optional[int] = None
+    election_tick: Optional[int] = None
+    heartbeat_tick: Optional[int] = None
 
 
-@all_fields_optional
 class SwarmDispatcher(DockerCamelModel):
-    heartbeat_period: Optional[int]
+    heartbeat_period: Optional[int] = None
 
 
-@all_fields_optional
 class SwarmCAConfig(DockerCamelModel):
-    node_cert_expiry: Optional[int]
-    external_ca: Optional[List[Any]] = pydantic.Field(
-        None, alias="ExternalCA"
-    )  # TODO: set type
-    signing_ca_cert: Optional[str] = pydantic.Field(None, alias="SigningCACert")
-    signing_ca_key: Optional[str] = pydantic.Field(None, alias="SigningCAKey")
-    force_rotate: Optional[int]
+    node_cert_expiry: Optional[int] = None
+    external_ca: Annotated[
+        Optional[List[Any]], pydantic.Field(alias="ExternalCA")
+    ] = None  # TODO: set type
+    signing_ca_cert: Annotated[
+        Optional[str], pydantic.Field(alias="SigningCACert")
+    ] = None
+    signing_ca_key: Annotated[
+        Optional[str], pydantic.Field(alias="SigningCAKey")
+    ] = None
+    force_rotate: Optional[int] = None
 
 
-@all_fields_optional
 class SwarmEncryptionConfig(DockerCamelModel):
-    auto_lock_managers: Optional[bool]
+    auto_lock_managers: Optional[bool] = None
 
 
-@all_fields_optional
 class Driver(DockerCamelModel):
-    name: Optional[str]
-    options: Optional[Dict[str, Any]]
+    name: Optional[str] = None
+    options: Optional[Dict[str, Any]] = None
 
 
-@all_fields_optional
 class SwarmTasksDefault(DockerCamelModel):
-    log_driver: Optional[Driver]
+    log_driver: Optional[Driver] = None
 
 
-@all_fields_optional
 class SwarmSpec(DockerCamelModel):
-    name: Optional[str]
-    labels: Optional[Dict[str, str]]
-    orchestration: Optional[Orchestration]
-    raft: Optional[Raft]
-    dispatcher: Optional[SwarmDispatcher]
-    ca_config: Optional[SwarmCAConfig] = pydantic.Field(None, alias="CAConfig")
-    encryption_config: Optional[SwarmEncryptionConfig]
-    task_defaults: Optional[SwarmTasksDefault]
+    name: Optional[str] = None
+    labels: Optional[Dict[str, str]] = None
+    orchestration: Optional[Orchestration] = None
+    raft: Optional[Raft] = None
+    dispatcher: Optional[SwarmDispatcher] = None
+    ca_config: Annotated[
+        Optional[SwarmCAConfig], pydantic.Field(alias="CAConfig")
+    ] = None
+    encryption_config: Optional[SwarmEncryptionConfig] = None
+    task_defaults: Optional[SwarmTasksDefault] = None
 
 
-@all_fields_optional
 class ClusterInfo(DockerCamelModel):
-    id: Optional[str] = pydantic.Field(None, alias="ID")
-    version: Optional[python_on_whales.components.node.models.NodeVersion]
-    created_at: Optional[datetime]
-    updated_at: Optional[datetime]
-    spec: Optional[SwarmSpec]
-    tls_info: Optional[python_on_whales.components.node.models.NodeTLSInfo]
-    root_rotation_in_progress: Optional[bool]
-    data_path_port: Optional[int]
-    default_addr_pool: Optional[List[str]]
-    subnet_size: Optional[int]
-
-
-@all_fields_optional
+    id: Annotated[Optional[str], pydantic.Field(alias="ID")]
+    version: Optional[python_on_whales.components.node.models.NodeVersion] = None
+    created_at: Optional[datetime] = None
+    updated_at: Optional[datetime] = None
+    spec: Optional[SwarmSpec] = None
+    tls_info: Optional[python_on_whales.components.node.models.NodeTLSInfo] = None
+    root_rotation_in_progress: Optional[bool] = None
+    data_path_port: Optional[int] = None
+    default_addr_pool: Optional[List[str]] = None
+    subnet_size: Optional[int] = None
+
+
 class SwarmInfo(DockerCamelModel):
-    node_id: Optional[str] = pydantic.Field(None, alias="NodeID")
-    node_addr: Optional[str]
-    local_node_state: Optional[str]
-    control_available: Optional[bool]
-    error: Optional[str]
-    remote_managers: Optional[List[RemoteManager]]
-    nodes: Optional[int]
-    managers: Optional[int]
-    cluster: Optional[ClusterInfo]
-
-
-@all_fields_optional
+    node_id: Annotated[Optional[str], pydantic.Field(alias="NodeID")] = None
+    node_addr: Optional[str] = None
+    local_node_state: Optional[str] = None
+    control_available: Optional[bool] = None
+    error: Optional[str] = None
+    remote_managers: Optional[List[RemoteManager]] = None
+    nodes: Optional[int] = None
+    managers: Optional[int] = None
+    cluster: Optional[ClusterInfo] = None
+
+
 class ClientPlugin(DockerCamelModel):
-    schema_version: Optional[str]
-    vendor: Optional[str]
-    version: Optional[str]
-    short_description: Optional[str]
-    name: Optional[str]
-    path: Optional[Path]
-    shadowed_paths: Optional[List[Path]]
+    schema_version: Optional[str] = None
+    vendor: Optional[str] = None
+    version: Optional[str] = None
+    short_description: Optional[str] = None
+    name: Optional[str] = None
+    path: Optional[Path] = None
+    shadowed_paths: Optional[List[Path]] = None
 
 
-@all_fields_optional
 class ClientInfo(DockerCamelModel):
-    debug: Optional[bool]
-    plugins: Optional[List[ClientPlugin]]
-    warnings: Optional[List[str]]
+    debug: Optional[bool] = None
+    plugins: Optional[List[ClientPlugin]] = None
+    warnings: Optional[List[str]] = None
 
 
-@all_fields_optional
 class SystemInfo(DockerCamelModel):
-    id: Optional[str] = pydantic.Field(None, alias="ID")
-    containers: Optional[int]
-    containers_running: Optional[int]
-    containers_paused: Optional[int]
-    containers_stopped: Optional[int]
-    images: Optional[int]
-    driver: Optional[str]
-    driver_status: Optional[List[List[str]]]
-    docker_root_dir: Optional[Path]
-    system_status: Optional[List[str]]
-    plugins: Optional[Plugins]
-    memory_limit: Optional[bool]
-    swap_limit: Optional[bool]
-    kernel_memory: Optional[bool]
-    cpu_cfs_period: Optional[bool]
-    cpu_cfs_quota: Optional[bool]
-    cpu_shares: Optional[bool] = pydantic.Field(None, alias="CPUShares")
-    cpu_set: Optional[bool] = pydantic.Field(None, alias="CPUSet")
-    pids_limit: Optional[bool]
-    oom_kill_disable: Optional[bool]
-    ipv4_forwarding: Optional[bool] = pydantic.Field(None, alias="IPv4Forwarding")
-    bridge_nf_iptables: Optional[bool]
-    bridge_nf_ip6tables: Optional[bool] = pydantic.Field(
-        None, alias="BridgeNfIp6tables"
-    )
-    debug: Optional[bool]
-    nfd: Optional[int] = pydantic.Field(None, alias="NFd")
-    n_goroutines: Optional[int]
-    system_time: Optional[str]
-    logging_driver: Optional[str]
-    cgroup_driver: Optional[str]
-    n_events_listener: Optional[int]
-    kernel_version: Optional[str]
-    operating_system: Optional[str]
-    os_type: Optional[str] = pydantic.Field(None, alias="OSType")
-    architecture: Optional[str]
-    n_cpu: Optional[int] = pydantic.Field(None, alias="NCPU")
-    mem_total: Optional[int]
-    index_server_address: Optional[str]
-    registry_config: Optional[Dict[str, Any]]
+    id: Annotated[Optional[str], pydantic.Field(alias="ID")] = None
+    containers: Optional[int] = None
+    containers_running: Optional[int] = None
+    containers_paused: Optional[int] = None
+    containers_stopped: Optional[int] = None
+    images: Optional[int] = None
+    driver: Optional[str] = None
+    driver_status: Optional[List[List[str]]] = None
+    docker_root_dir: Optional[Path] = None
+    system_status: Optional[List[str]] = None
+    plugins: Optional[Plugins] = None
+    memory_limit: Optional[bool] = None
+    swap_limit: Optional[bool] = None
+    kernel_memory: Optional[bool] = None
+    cpu_cfs_period: Optional[bool] = None
+    cpu_cfs_quota: Optional[bool] = None
+    cpu_shares: Annotated[Optional[bool], pydantic.Field(alias="CPUShares")] = None
+    cpu_set: Annotated[Optional[bool], pydantic.Field(alias="CPUSet")] = None
+    pids_limit: Optional[bool] = None
+    oom_kill_disable: Optional[bool] = None
+    ipv4_forwarding: Annotated[
+        Optional[bool], pydantic.Field(alias="IPv4Forwarding")
+    ] = None
+    bridge_nf_iptables: Optional[bool] = None
+    bridge_nf_ip6tables: Annotated[
+        Optional[bool], pydantic.Field(alias="BridgeNfIp6tables")
+    ] = None
+    debug: Optional[bool] = None
+    nfd: Annotated[Optional[int], pydantic.Field(alias="NFd")] = None
+    n_goroutines: Optional[int] = None
+    system_time: Optional[str] = None
+    logging_driver: Optional[str] = None
+    cgroup_driver: Optional[str] = None
+    n_events_listener: Optional[int] = None
+    kernel_version: Optional[str] = None
+    operating_system: Optional[str] = None
+    os_type: Annotated[Optional[str], pydantic.Field(alias="OSType")] = None
+    architecture: Optional[str] = None
+    n_cpu: Annotated[Optional[int], pydantic.Field(alias="NCPU")] = None
+    mem_total: Optional[int] = None
+    index_server_address: Optional[str] = None
+    registry_config: Optional[Dict[str, Any]] = None
     generic_resources: Optional[
         List[python_on_whales.components.node.models.NodeGenericResource]
-    ]
-
-    http_proxy: Optional[str]
-    https_proxy: Optional[str]
-    no_proxy: Optional[str]
-    name: Optional[str]
-    labels: Optional[Dict[str, str]]
-    experimental_build: Optional[bool]
-    server_version: Optional[str]
-    cluster_store: Optional[str]
-    runtimes: Optional[Dict[str, Runtime]]
-    default_runtime: Optional[str]
-    swarm: Optional[SwarmInfo]
-    live_restore_enabled: Optional[bool]
-    isolation: Optional[str]
-    init_binary: Optional[str]
-    containerd_commit: Optional[Commit]
-    runc_commit: Optional[Commit]
-    init_commit: Optional[Commit]
-    security_options: Optional[List[str]]
-    product_license: Optional[str]
-    warnings: Optional[List[str]]
-    client_info: Optional[ClientInfo]
+    ] = None
+
+    http_proxy: Optional[str] = None
+    https_proxy: Optional[str] = None
+    no_proxy: Optional[str] = None
+    name: Optional[str] = None
+    labels: Optional[List[str]] = None
+    experimental_build: Optional[bool] = None
+    server_version: Optional[str] = None
+    cluster_store: Optional[str] = None
+    runtimes: Optional[Dict[str, Runtime]] = None
+    default_runtime: Optional[str] = None
+    swarm: Optional[SwarmInfo] = None
+    live_restore_enabled: Optional[bool] = None
+    isolation: Optional[str] = None
+    init_binary: Optional[str] = None
+    containerd_commit: Optional[Commit] = None
+    runc_commit: Optional[Commit] = None
+    init_commit: Optional[Commit] = None
+    security_options: Optional[List[str]] = None
+    product_license: Optional[str] = None
+    warnings: Optional[List[str]] = None
+    client_info: Optional[ClientInfo] = None
diff --git a/python_on_whales/components/task/models.py b/python_on_whales/components/task/models.py
index 1a9e6f90..6995ed79 100644
--- a/python_on_whales/components/task/models.py
+++ b/python_on_whales/components/task/models.py
@@ -2,177 +2,158 @@
 from typing import Any, Dict, List, Optional
 
 import pydantic
+from typing_extensions import Annotated
 
-from python_on_whales.utils import DockerCamelModel, all_fields_optional
+from python_on_whales.utils import DockerCamelModel
 
 
-@all_fields_optional
 class ObjectVersion(DockerCamelModel):
-    index: Optional[int]
+    index: Optional[int] = None
 
 
-@all_fields_optional
 class NamedResourceSpec(DockerCamelModel):
-    kind: Optional[str]
-    value: Optional[str]
+    kind: Optional[str] = None
+    value: Optional[str] = None
 
 
-@all_fields_optional
 class DiscreteResourceSpec(DockerCamelModel):
-    kind: Optional[str]
-    value: Optional[int]
+    kind: Optional[str] = None
+    value: Optional[int] = None
 
 
-@all_fields_optional
 class AssignedGenericResources(DockerCamelModel):
-    named_resource_spec: Optional[NamedResourceSpec]
-    discrete_resource_spec: Optional[DiscreteResourceSpec]
+    named_resource_spec: Optional[NamedResourceSpec] = None
+    discrete_resource_spec: Optional[DiscreteResourceSpec] = None
 
 
-@all_fields_optional
 class ContainerStatus(DockerCamelModel):
-    container_id: Optional[str] = pydantic.Field(None, alias="ContainerID")
-    pid: Optional[int] = pydantic.Field(None, alias="PID")
-    exit_code: Optional[int]
+    container_id: Annotated[Optional[str], pydantic.Field(alias="ContainerID")] = None
+    pid: Annotated[Optional[int], pydantic.Field(alias="PID")] = None
+    exit_code: Optional[int] = None
 
 
-@all_fields_optional
 class TaskStatus(DockerCamelModel):
-    timestamp: Optional[datetime]
-    state: Optional[str]
-    message: Optional[str]
-    err: Optional[str]
-    container_status: Optional[ContainerStatus]
+    timestamp: Optional[datetime] = None
+    state: Optional[str] = None
+    message: Optional[str] = None
+    err: Optional[str] = None
+    container_status: Optional[ContainerStatus] = None
 
 
-@all_fields_optional
 class LogDriver(DockerCamelModel):
-    name: Optional[str]
-    options: Optional[Dict[str, str]]
+    name: Optional[str] = None
+    options: Optional[Dict[str, str]] = None
 
 
-@all_fields_optional
 class NetworkAttachmentConfig(DockerCamelModel):
-    target: Optional[str]
-    aliases: Optional[List[str]]
-    driver_opts: Optional[Dict[str, str]]
+    target: Optional[str] = None
+    aliases: Optional[List[str]] = None
+    driver_opts: Optional[Dict[str, str]] = None
 
 
-@all_fields_optional
 class Platform(DockerCamelModel):
-    architecture: Optional[str]
-    os: Optional[str] = pydantic.Field(None, alias="OS")
+    architecture: Optional[str] = None
+    os: Annotated[Optional[str], pydantic.Field(alias="OS")] = None
 
 
-@all_fields_optional
 class Spread(DockerCamelModel):
-    spread_descriptor: Optional[str]
+    spread_descriptor: Optional[str] = None
 
 
-@all_fields_optional
 class Placement(DockerCamelModel):
-    constraints: Optional[List[str]]
-    preferences: Optional[List[Spread]]
-    max_replicas: Optional[int]
-    platforms: Optional[List[Platform]]
+    constraints: Optional[List[str]] = None
+    preferences: Optional[List[Spread]] = None
+    max_replicas: Optional[int] = None
+    platforms: Optional[List[Platform]] = None
 
 
-@all_fields_optional
 class RestartPolicy(DockerCamelModel):
-    condition: Optional[str]
-    delay: Optional[int]
-    max_attempts: Optional[int]
-    window: Optional[int]
+    condition: Optional[str] = None
+    delay: Optional[int] = None
+    max_attempts: Optional[int] = None
+    window: Optional[int] = None
 
 
-@all_fields_optional
 class PluginPrivilege(DockerCamelModel):
-    name: Optional[str]
-    description: Optional[str]
-    value: Optional[List[str]]
+    name: Optional[str] = None
+    description: Optional[str] = None
+    value: Optional[List[str]] = None
 
 
-@all_fields_optional
 class PluginSpec(DockerCamelModel):
-    name: Optional[str]
-    remote: Optional[str]
-    disabled: Optional[bool]
-    plugin_privilege: Optional[List[PluginPrivilege]]
+    name: Optional[str] = None
+    remote: Optional[str] = None
+    disabled: Optional[bool] = None
+    plugin_privilege: Optional[List[PluginPrivilege]] = None
 
 
-@all_fields_optional
 class ContainerSpec(DockerCamelModel):
-    image: Optional[str]
-    labels: Optional[Dict[str, str]]
-    command: Optional[List[str]]
-    args: Optional[List[str]]
-    hostname: Optional[str]
-    env: Optional[List[str]]
-    dir: Optional[str]
-    user: Optional[str]
-    groups: Optional[List[str]]
-    privileges: Optional[Any]
-    tty: Optional[bool] = pydantic.Field(None, alias="TTY")
-    open_stdin: Optional[bool]
-    read_only: Optional[bool]
-    mounts: Optional[List[Any]]
-    stop_signal: Optional[str]
-    stop_grace_period: Optional[int]
+    image: Optional[str] = None
+    labels: Optional[Dict[str, str]] = None
+    command: Optional[List[str]] = None
+    args: Optional[List[str]] = None
+    hostname: Optional[str] = None
+    env: Optional[List[str]] = None
+    dir: Optional[str] = None
+    user: Optional[str] = None
+    groups: Optional[List[str]] = None
+    privileges: Any = None
+    tty: Annotated[Optional[bool], pydantic.Field(alias="TTY")] = None
+    open_stdin: Optional[bool] = None
+    read_only: Optional[bool] = None
+    mounts: Optional[List[Any]] = None
+    stop_signal: Optional[str] = None
+    stop_grace_period: Optional[int] = None
     health_check: Any = None
-    hosts: Optional[List[str]]
+    hosts: Optional[List[str]] = None
     dns_config: Any = None
-    secrets: Optional[List[Any]]
-    configs: Optional[List[Any]]
-    isolation: Optional[str]
-    init: Optional[bool]
-    sysctls: Optional[Any]
+    secrets: Optional[List[Any]] = None
+    configs: Optional[List[Any]] = None
+    isolation: Optional[str] = None
+    init: Optional[bool] = None
+    sysctls: Optional[Any] = None
 
 
-@all_fields_optional
 class NetworkAttachmentSpec(DockerCamelModel):
-    container_id: Optional[str] = pydantic.Field(None, alias="ContainerID")
+    container_id: Annotated[Optional[str], pydantic.Field(alias="ContainerID")] = None
 
 
-@all_fields_optional
 class ResourceObject(DockerCamelModel):
-    nano_cpus: Optional[int] = pydantic.Field(None, alias="NanoCPUs")
-    memory_bytes: Optional[int]
-    generic_resources: Optional[List[AssignedGenericResources]]
+    nano_cpus: Annotated[Optional[int], pydantic.Field(alias="NanoCPUs")] = None
+    memory_bytes: Optional[int] = None
+    generic_resources: Optional[List[AssignedGenericResources]] = None
 
 
-@all_fields_optional
 class Resources(DockerCamelModel):
-    limits: Optional[ResourceObject]
-    reservation: Optional[ResourceObject]
+    limits: Optional[ResourceObject] = None
+    reservation: Optional[ResourceObject] = None
 
 
-@all_fields_optional
 class TaskSpec(DockerCamelModel):
     # TODO: set types for Any
-    plugin_spec: Optional[PluginSpec]
-    container_spec: Optional[ContainerSpec]
-    network_attachment_spec: Optional[NetworkAttachmentSpec]
-    resources: Optional[Resources]
+    plugin_spec: Optional[PluginSpec] = None
+    container_spec: Optional[ContainerSpec] = None
+    network_attachment_spec: Optional[NetworkAttachmentSpec] = None
+    resources: Optional[Resources] = None
     restart_policy: Any = None
-    placement: Optional[Placement]
-    force_update: Optional[int]
-    runtime: Optional[str]
-    networks: Optional[List[NetworkAttachmentConfig]]
-    log_driver: Optional[LogDriver]
+    placement: Optional[Placement] = None
+    force_update: Optional[int] = None
+    runtime: Optional[str] = None
+    networks: Optional[List[NetworkAttachmentConfig]] = None
+    log_driver: Optional[LogDriver] = None
 
 
-@all_fields_optional
 class TaskInspectResult(DockerCamelModel):
-    id: Optional[str] = pydantic.Field(None, alias="ID")
-    version: Optional[ObjectVersion]
-    created_at: Optional[datetime]
-    updated_at: Optional[datetime]
-    name: Optional[str]
-    labels: Optional[Dict[str, str]]
-    spec: Optional[TaskSpec]
-    service_id: Optional[str] = pydantic.Field(None, alias="ServiceID")
-    slot: Optional[int]
-    node_id: Optional[str] = pydantic.Field(None, alias="NodeID")
-    assigned_generic_resources: Optional[List[AssignedGenericResources]]
-    status: Optional[TaskStatus]
-    desired_state: Optional[str]
+    id: Annotated[Optional[str], pydantic.Field(alias="ID")] = None
+    version: Optional[ObjectVersion] = None
+    created_at: Optional[datetime] = None
+    updated_at: Optional[datetime] = None
+    name: Optional[str] = None
+    labels: Optional[Dict[str, str]] = None
+    spec: Optional[TaskSpec] = None
+    service_id: Annotated[Optional[str], pydantic.Field(alias="ServiceID")] = None
+    slot: Optional[int] = None
+    node_id: Annotated[Optional[str], pydantic.Field(alias="NodeID")] = None
+    assigned_generic_resources: Optional[List[AssignedGenericResources]] = None
+    status: Optional[TaskStatus] = None
+    desired_state: Optional[str] = None
diff --git a/python_on_whales/components/volume/models.py b/python_on_whales/components/volume/models.py
index aba45d4f..6b8f0dae 100644
--- a/python_on_whales/components/volume/models.py
+++ b/python_on_whales/components/volume/models.py
@@ -2,16 +2,15 @@
 from pathlib import Path
 from typing import Any, Dict, Optional
 
-from python_on_whales.utils import DockerCamelModel, all_fields_optional
+from python_on_whales.utils import DockerCamelModel
 
 
-@all_fields_optional
 class VolumeInspectResult(DockerCamelModel):
-    name: Optional[str]
-    driver: Optional[str]
-    mountpoint: Optional[Path]
-    created_at: Optional[datetime]
-    status: Optional[Dict[str, Any]]
-    labels: Optional[Dict[str, str]]
-    scope: Optional[str]
-    options: Optional[Dict[str, str]]
+    name: Optional[str] = None
+    driver: Optional[str] = None
+    mountpoint: Optional[Path] = None
+    created_at: Optional[datetime] = None
+    status: Optional[Dict[str, Any]] = None
+    labels: Optional[Dict[str, str]] = None
+    scope: Optional[str] = None
+    options: Optional[Dict[str, str]] = None
diff --git a/python_on_whales/utils.py b/python_on_whales/utils.py
index a662c21c..c5d63e6f 100644
--- a/python_on_whales/utils.py
+++ b/python_on_whales/utils.py
@@ -300,15 +300,6 @@ def read_env_files(env_files: List[Path]) -> Dict[str, str]:
     return result_dict
 
 
-def all_fields_optional(cls):
-    """Decorator function used to modify a pydantic model's fields to all be optional."""
-    if not PYDANTIC_V2:
-        for field in cls.__fields__.values():
-            field.required = False
-            field.allow_none = True
-    return cls
-
-
 def format_time_arg(time_object):
     if time_object is None:
         return None
diff --git a/requirements.txt b/requirements.txt
index 3713ebfd..ac9b0f8d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-pydantic>=1.5,<2
+pydantic>=1.5,<3,!=2.0.*
 requests
 tqdm
 typer>=0.4.1


Test patch:
diff --git a/tests/python_on_whales/components/strange_compose_config_rc1.json b/tests/python_on_whales/components/strange_compose_config_rc1.json
deleted file mode 100644
index 97410583..00000000
--- a/tests/python_on_whales/components/strange_compose_config_rc1.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-  "services": {
-    "myservice": {
-      "deploy": {
-        "resources": {
-          "reservations": {
-            "cpus": "'0.25'"
-          }
-        },
-        "placement": {}
-      },
-      "image": "alpine",
-      "networks": {
-        "default": null
-      }
-    }
-  },
-  "networks": {
-    "default": {
-      "name": "tmp_default",
-      "ipam": {},
-      "external": false
-    }
-  }
-}
\ No newline at end of file
diff --git a/tests/python_on_whales/components/test_compose.py b/tests/python_on_whales/components/test_compose.py
index 3ef67f1a..a5be1a98 100644
--- a/tests/python_on_whales/components/test_compose.py
+++ b/tests/python_on_whales/components/test_compose.py
@@ -505,14 +505,6 @@ def test_compose_down_volumes():
     assert not docker.volume.exists("components_dodo")
 
 
-def test_compose_config_from_rc1():
-    config = ComposeConfig.parse_file(
-        Path(__file__).parent / "strange_compose_config_rc1.json"
-    )
-
-    assert config.services["myservice"].deploy.resources.reservations.cpus == "'0.25'"
-
-
 @pytest.mark.parametrize("json_file", get_all_jsons("compose"))
 def test_load_json(json_file):
     json_as_txt = json_file.read_text()