Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 43 additions & 36 deletions src/rapidata/api_client/lazy_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from __future__ import annotations

from typing import Any, Dict, Optional
from typing import Any, Dict, Optional, TYPE_CHECKING

from pydantic import BaseModel, ConfigDict, ValidationError

Expand Down Expand Up @@ -122,38 +122,45 @@ def _lazy_construct(
# ------------------------------------------------------------------
# Access-time guard – raise only when a bad field is actually read
# ------------------------------------------------------------------
def __getattribute__(self, name: str) -> Any:
# Only intercept known model fields; everything else takes the
# fast super() path (Pydantic internals, dunder attrs, methods).
# Use getattr (MRO-aware) rather than type(self).__dict__.get():
# in Pydantic v2, fields are exposed via the `model_fields`
# class-property and are NOT present in the class's own __dict__,
# so a __dict__ lookup always misses and the guard never fires.
model_fields = getattr(type(self), "model_fields", None)
if model_fields is not None and name in model_fields:
# model_construct() bypasses __init__, so `_field_validation_errors`
# may not be set on every instance. Treat missing as "no errors".
errors: Optional[Dict[str, dict]]
try:
errors = object.__getattribute__(
self, "_field_validation_errors"
)
except AttributeError:
errors = None
if errors and name in errors:
err = errors[name]
message = (
f"Field '{name}' on {type(self).__name__} has an unexpected "
f"type from the backend: {err.get('msg', err)}"
)
# Imported lazily to avoid pulling the higher SDK layer at
# module import time (this base class is imported by every
# generated model).
from rapidata.rapidata_client.api.rapidata_api_client import (
format_outdated_sdk_note,
)
note = format_outdated_sdk_note()
if note:
message = f"{message}\n{note}"
raise TypeError(message)
return super().__getattribute__(name)
# Guarded against TYPE_CHECKING for the same reason Pydantic guards its own
# __getattr__/__setattr__ (pydantic/main.py): a class-visible __getattribute__
# returning Any makes type checkers treat *every* attribute access as a valid
# Any, which silently hides renamed/removed backend fields (e.g. .status after
# it became .state). Hiding it from pyright restores per-field access checking
# against the declared model annotations while leaving runtime behaviour intact.
if not TYPE_CHECKING:

def __getattribute__(self, name: str) -> Any:
# Only intercept known model fields; everything else takes the
# fast super() path (Pydantic internals, dunder attrs, methods).
# Use getattr (MRO-aware) rather than type(self).__dict__.get():
# in Pydantic v2, fields are exposed via the `model_fields`
# class-property and are NOT present in the class's own __dict__,
# so a __dict__ lookup always misses and the guard never fires.
model_fields = getattr(type(self), "model_fields", None)
if model_fields is not None and name in model_fields:
# model_construct() bypasses __init__, so `_field_validation_errors`
# may not be set on every instance. Treat missing as "no errors".
errors: Optional[Dict[str, dict]]
try:
errors = object.__getattribute__(self, "_field_validation_errors")
except AttributeError:
errors = None
if errors and name in errors:
err = errors[name]
message = (
f"Field '{name}' on {type(self).__name__} has an unexpected "
f"type from the backend: {err.get('msg', err)}"
)
# Imported lazily to avoid pulling the higher SDK layer at
# module import time (this base class is imported by every
# generated model).
from rapidata.rapidata_client.api.rapidata_api_client import (
format_outdated_sdk_note,
)

note = format_outdated_sdk_note()
if note:
message = f"{message}\n{note}"
raise TypeError(message)
return super().__getattribute__(name)
4 changes: 3 additions & 1 deletion src/rapidata/rapidata_client/job/rapidata_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@ def get_status(self) -> str:
The current status of the job as a string.
"""
with tracer.start_as_current_span("RapidataJob.get_status"):
return self._openapi_service.order.job_api.job_job_id_get(self.id).status
return self._openapi_service.order.job_api.job_job_id_get(
self.id
).state.value

def get_results(self) -> RapidataResults:
"""
Expand Down
Loading