diff --git a/cognite/pygen/_core/templates/api_core.py.jinja b/cognite/pygen/_core/templates/api_core.py.jinja index 4d079211b..b9e98fb76 100644 --- a/cognite/pygen/_core/templates/api_core.py.jinja +++ b/cognite/pygen/_core/templates/api_core.py.jinja @@ -572,8 +572,10 @@ class GraphQLQueryResponse: elif "__typename" in data: try: item = self._data_class_by_type[data["__typename"]].model_validate(data) - except KeyError: - raise ValueError(f"Could not find class for type {data['__typename']}") from None + except KeyError as key_error: + if key_error.args[0] == data["__typename"]: + raise ValueError(f"Could not find class for type {data['__typename']}") from None + raise key_error else: self._output.append(item) else: diff --git a/cognite/pygen/_core/templates/data_classes_core_cdf_external.py.jinja b/cognite/pygen/_core/templates/data_classes_core_cdf_external.py.jinja index b8291bb4d..a01569ae3 100644 --- a/cognite/pygen/_core/templates/data_classes_core_cdf_external.py.jinja +++ b/cognite/pygen/_core/templates/data_classes_core_cdf_external.py.jinja @@ -1,11 +1,14 @@ from __future__ import annotations import datetime +from collections.abc import Callable from typing import ( Annotated, Optional, Any, no_type_check, + TypeVar, + Set, ) from cognite.client import data_modeling as dm @@ -24,6 +27,23 @@ from pydantic import BaseModel, BeforeValidator, model_validator, field_validato from pydantic.alias_generators import to_camel from pydantic.functional_serializers import PlainSerializer +T_CogniteResource = TypeVar("T_CogniteResource", bound=CogniteTimeSeries | CogniteSequence | CogniteFileMetadata) + +_MISSING_VALUE = -999 + +def _create_load_method(resource_cls: type[T_CogniteResource], required_fields: Set[str]) -> Callable[[Any], Any]: + def _load_if_dict(value: Any) -> Any: + if not isinstance(value, dict): + return value + if missing_values := set(required_fields) - set(value.keys()): + raise ValueError(f"Missing required fields: {', '.join(missing_values)}") + for key in ["createdTime", "lastUpdatedTime"]: + # GraphQL does not support returning these properties, while the read classes requires them. + value[key] = _MISSING_VALUE + return resource_cls.load(value) + + return _load_if_dict + TimeSeries = Annotated[ CogniteTimeSeries, @@ -32,7 +52,7 @@ TimeSeries = Annotated[ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteTimeSeries.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteTimeSeries, {"id", "isStep", "isString"})), ] @@ -43,7 +63,7 @@ SequenceRead = Annotated[ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteSequence.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteSequence, {"id", "columns"})), ] @@ -54,7 +74,7 @@ FileMetadata = Annotated[ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteFileMetadata.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteFileMetadata, {"id", "uploaded", "name"})), ] @@ -123,6 +143,15 @@ class TimeSeriesGraphQL(GraphQLExternal): datetime.datetime.fromisoformat(item["timestamp"].replace("Z", "+00:00")) ) data["datapoints"] = datapoints["items"] + if missing := [name for name in ["id", "isString", "isStep"] if data.get(name) is None]: + raise ValueError( + f"Cannot create datapoints, missing required fields: {', '.join(missing)}. " + "You need to include these in your query." + ) + if "type" not in data: + # Type is not supported in the timeseries you retrieve through GraphQL, but it is required + # for the Datapoints object. Luckily it can be inferred from the isString field, so we set it here. + data["type"] = "string" if data["isString"] else "numeric" data["data"] = Datapoints.load(data) if isinstance(data, dict) and "getLatestDataPoint" in data: latest = data.pop("getLatestDataPoint") @@ -146,6 +175,7 @@ class TimeSeriesGraphQL(GraphQLExternal): description=self.description, ) + @no_type_check def as_read(self) -> CogniteTimeSeries: return CogniteTimeSeries( id=self.id, @@ -160,6 +190,8 @@ class TimeSeriesGraphQL(GraphQLExternal): is_step=self.is_step, description=self.description, security_categories=self.security_categories, + created_time=self.created_time, + last_updated_time=self.last_updated_time, ) @@ -237,7 +269,7 @@ class SequenceColumnGraphQL(GraphQLExternal): @field_validator("value_type", mode="before") def title_value_type(cls, value: Any) -> Any: if isinstance(value, str): - return value.title() + return value.upper() return value @no_type_check diff --git a/cognite/pygen/_core/templates/data_classes_core_datapoints_api.py.jinja b/cognite/pygen/_core/templates/data_classes_core_datapoints_api.py.jinja index 0d42a43ff..2dad65c3d 100644 --- a/cognite/pygen/_core/templates/data_classes_core_datapoints_api.py.jinja +++ b/cognite/pygen/_core/templates/data_classes_core_datapoints_api.py.jinja @@ -150,7 +150,6 @@ class DataPointsAPI: uniform_index=uniform_index, include_aggregate_name=include_aggregate_name, include_granularity_name=include_granularity_name, - column_names="instance_id", ) def __getattr__(self, item: str) -> Any: diff --git a/cognite/pygen/_core/templates/data_classes_core_query_select.py.jinja b/cognite/pygen/_core/templates/data_classes_core_query_select.py.jinja index 243c9ed2a..07dc56fbf 100644 --- a/cognite/pygen/_core/templates/data_classes_core_query_select.py.jinja +++ b/cognite/pygen/_core/templates/data_classes_core_query_select.py.jinja @@ -225,7 +225,7 @@ class NodeQueryCore(QueryCore[T_DomainModelList, T_DomainListEnd]): step.connection_property = item._connection_property step.expression.from_ = from_ step.expression.filter = item._assemble_filter() - step.expression.sort = item._create_sort() + step.expression.sort = item._create_sort() or [] builder.append(step) elif isinstance(item, NodeQueryCore) and isinstance(item._expression, dm.query.EdgeResultSetExpression): # Edge without properties @@ -264,7 +264,7 @@ class NodeQueryCore(QueryCore[T_DomainModelList, T_DomainListEnd]): ) step.expression.from_ = from_ step.expression.filter = item._assemble_filter() - step.expression.sort = item._create_sort() + step.expression.sort = item._create_sort() or [] builder.append(step) else: raise TypeError(f"Unsupported query step type: {type(item._expression)}") diff --git a/cognite/pygen/_query/executor.py b/cognite/pygen/_query/executor.py index 327c9a6af..8b46c0e37 100644 --- a/cognite/pygen/_query/executor.py +++ b/cognite/pygen/_query/executor.py @@ -256,7 +256,7 @@ def _fetch_reverse_direct_relation_of_lists( is_selected = is_items if step.raw_filter is None else dm.filters.And(is_items, step.raw_filter) chunk_result = client.data_modeling.instances.search( - view_id, properties=None, filter=is_selected, limit=api_limit + view=view_id, query=None, properties=None, filter=is_selected, limit=api_limit ) for node in chunk_result: node_id = node.as_id() diff --git a/cognite/pygen/_query/step.py b/cognite/pygen/_query/step.py index 123f4b56f..0e5e3e94c 100644 --- a/cognite/pygen/_query/step.py +++ b/cognite/pygen/_query/step.py @@ -7,7 +7,7 @@ from cognite.client import CogniteClient from cognite.client.data_classes import data_modeling as dm -from cognite.client.data_classes._base import CogniteObject +from cognite.client.data_classes._base import CogniteResource from cognite.client.data_classes.data_modeling.instances import Instance from cognite.client.data_classes.data_modeling.views import ReverseDirectRelation, ViewProperty @@ -20,7 +20,7 @@ @dataclass(frozen=True) -class ViewPropertyId(CogniteObject): +class ViewPropertyId(CogniteResource): view: dm.ViewId property: str diff --git a/cognite/pygen/demo/_solar_apm.py b/cognite/pygen/demo/_solar_apm.py index 47b0ee071..240751312 100644 --- a/cognite/pygen/demo/_solar_apm.py +++ b/cognite/pygen/demo/_solar_apm.py @@ -5,7 +5,7 @@ from typing import Any, cast from cognite.client import CogniteClient -from cognite.client.data_classes import DataSet +from cognite.client.data_classes import DataSet, DataSetWrite from cognite.client.data_classes.data_modeling import ( DataModel, MappedProperty, @@ -146,7 +146,7 @@ def _data_set_id(self, client: CogniteClient) -> int | None: new_dataset = cast( DataSet, client.data_sets.create( - DataSet( + DataSetWrite( external_id=self._data_set_external_id, name=self._data_set_external_id, description="This data set was created by pygen for demo purposes.", diff --git a/cognite/pygen/utils/cdf.py b/cognite/pygen/utils/cdf.py index 35da8cfa3..cdb930a80 100644 --- a/cognite/pygen/utils/cdf.py +++ b/cognite/pygen/utils/cdf.py @@ -12,7 +12,7 @@ from cognite.client import CogniteClient from cognite.client import data_modeling as dm -from cognite.client._api.files import FilesAPI +from cognite.client._sync_api.files import SyncFilesAPI from cognite.client.data_classes import FileMetadata, FileMetadataList, TimeSeries, TimeSeriesList from cognite.client.data_classes._base import CogniteResource, T_CogniteResource, T_CogniteResourceList from cognite.client.data_classes.data_modeling import ( @@ -104,7 +104,7 @@ def delete( class _FileAPIAdapter(_CogniteCoreResourceAPI[FileMetadataList]): - def __init__(self, files_api: FilesAPI): + def __init__(self, files_api: SyncFilesAPI): self._files_api = files_api def retrieve_multiple( diff --git a/cognite/pygen/utils/mock_generator.py b/cognite/pygen/utils/mock_generator.py index c2e429386..5bd9422d6 100644 --- a/cognite/pygen/utils/mock_generator.py +++ b/cognite/pygen/utils/mock_generator.py @@ -107,7 +107,7 @@ def __init__( @property def _views(self) -> dm.ViewList: - return dm.ViewList(self._view_by_id.values()) + return dm.ViewList(list(self._view_by_id.values())) def __str__(self): args = [ @@ -390,6 +390,7 @@ def _only_null_values(count: int) -> list[None]: external.timeseries.extend( [ TimeSeries( + id=-1, external_id=ts, name=ts, data_set_id=self._data_set_id, @@ -398,6 +399,8 @@ def _only_null_values(count: int) -> list[None]: metadata={ "source": f"Pygen{type(self).__name__}", }, + created_time=0, + last_updated_time=1, ) for timeseries_set in values for ts in ( @@ -412,6 +415,7 @@ def _only_null_values(count: int) -> list[None]: external.file.extend( [ FileMetadata( + id=-1, external_id=file, name=file, source=self._instance_space, @@ -420,6 +424,9 @@ def _only_null_values(count: int) -> list[None]: metadata={ "source": f"Pygen{type(self).__name__}", }, + created_time=0, + last_updated_time=1, + uploaded=False, ) for file_set in values for file in (cast(list[str], file_set) if isinstance(file_set, list) else [cast(str, file_set)]) @@ -430,13 +437,14 @@ def _only_null_values(count: int) -> list[None]: external.sequence.extend( [ Sequence( + id=-1, external_id=seq, name=seq, data_set_id=self._data_set_id, columns=[ SequenceColumn( external_id="value", - value_type=cast(Literal["Double"], "DOUBLE"), + value_type="DOUBLE", metadata={ "source": f"Pygen{type(self).__name__}", }, @@ -445,6 +453,8 @@ def _only_null_values(count: int) -> list[None]: metadata={ "source": f"Pygen{type(self).__name__}", }, + created_time=0, + last_updated_time=1, ) for seq_set in values for seq in (cast(list[str], seq_set) if isinstance(seq_set, list) else [cast(str, seq_set)]) @@ -830,7 +840,7 @@ def deploy( if nodes or edges: # There is an 'edge' if there is an outward and inward edge on two views, we can get duplicated edges. # We should remove the duplicates. - edges = dm.EdgeApplyList({edge.as_id(): edge for edge in edges}.values()) + edges = dm.EdgeApplyList(list({edge.as_id(): edge for edge in edges}.values())) created = client.data_modeling.instances.apply( nodes, diff --git a/examples/cognite_core/_api/_core.py b/examples/cognite_core/_api/_core.py index dc808d62e..c60b48d1c 100644 --- a/examples/cognite_core/_api/_core.py +++ b/examples/cognite_core/_api/_core.py @@ -561,8 +561,10 @@ def _parse_item(self, data: dict[str, Any]) -> None: elif "__typename" in data: try: item = self._data_class_by_type[data["__typename"]].model_validate(data) - except KeyError: - raise ValueError(f"Could not find class for type {data['__typename']}") from None + except KeyError as key_error: + if key_error.args[0] == data["__typename"]: + raise ValueError(f"Could not find class for type {data['__typename']}") from None + raise key_error else: self._output.append(item) else: diff --git a/examples/cognite_core/_api_client.py b/examples/cognite_core/_api_client.py index 5344ab739..ac91e91ff 100644 --- a/examples/cognite_core/_api_client.py +++ b/examples/cognite_core/_api_client.py @@ -53,7 +53,7 @@ class CogniteCoreClient: Generated with: pygen = 0.0.0 - cognite-sdk = 7.92.0 + cognite-sdk = 8.0.5 pydantic = 2.12.5 Data Model: diff --git a/examples/cognite_core/data_classes/_core/cdf_external.py b/examples/cognite_core/data_classes/_core/cdf_external.py index 43d22a881..26564bb2f 100644 --- a/examples/cognite_core/data_classes/_core/cdf_external.py +++ b/examples/cognite_core/data_classes/_core/cdf_external.py @@ -1,11 +1,14 @@ from __future__ import annotations import datetime +from collections.abc import Callable from typing import ( Annotated, Optional, Any, no_type_check, + TypeVar, + Set, ) from cognite.client import data_modeling as dm @@ -24,6 +27,25 @@ from pydantic.alias_generators import to_camel from pydantic.functional_serializers import PlainSerializer +T_CogniteResource = TypeVar("T_CogniteResource", bound=CogniteTimeSeries | CogniteSequence | CogniteFileMetadata) + +_MISSING_VALUE = -999 + + +def _create_load_method(resource_cls: type[T_CogniteResource], required_fields: Set[str]) -> Callable[[Any], Any]: + def _load_if_dict(value: Any) -> Any: + if not isinstance(value, dict): + return value + if missing_values := set(required_fields) - set(value.keys()): + raise ValueError(f"Missing required fields: {', '.join(missing_values)}") + for key in ["createdTime", "lastUpdatedTime"]: + # GraphQL does not support returning these properties, while the read classes requires them. + value[key] = _MISSING_VALUE + return resource_cls.load(value) + + return _load_if_dict + + TimeSeries = Annotated[ CogniteTimeSeries, PlainSerializer( @@ -31,7 +53,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteTimeSeries.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteTimeSeries, {"id", "isStep", "isString"})), ] @@ -42,7 +64,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteSequence.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteSequence, {"id", "columns"})), ] @@ -53,7 +75,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteFileMetadata.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteFileMetadata, {"id", "uploaded", "name"})), ] @@ -122,6 +144,15 @@ def parse_datapoints(cls, data: Any) -> Any: datetime.datetime.fromisoformat(item["timestamp"].replace("Z", "+00:00")) ) data["datapoints"] = datapoints["items"] + if missing := [name for name in ["id", "isString", "isStep"] if data.get(name) is None]: + raise ValueError( + f"Cannot create datapoints, missing required fields: {', '.join(missing)}. " + "You need to include these in your query." + ) + if "type" not in data: + # Type is not supported in the timeseries you retrieve through GraphQL, but it is required + # for the Datapoints object. Luckily it can be inferred from the isString field, so we set it here. + data["type"] = "string" if data["isString"] else "numeric" data["data"] = Datapoints.load(data) if isinstance(data, dict) and "getLatestDataPoint" in data: latest = data.pop("getLatestDataPoint") @@ -145,6 +176,7 @@ def as_write(self) -> CogniteTimeSeriesWrite: description=self.description, ) + @no_type_check def as_read(self) -> CogniteTimeSeries: return CogniteTimeSeries( id=self.id, @@ -159,6 +191,8 @@ def as_read(self) -> CogniteTimeSeries: is_step=self.is_step, description=self.description, security_categories=self.security_categories, + created_time=self.created_time, + last_updated_time=self.last_updated_time, ) @@ -236,7 +270,7 @@ class SequenceColumnGraphQL(GraphQLExternal): @field_validator("value_type", mode="before") def title_value_type(cls, value: Any) -> Any: if isinstance(value, str): - return value.title() + return value.upper() return value @no_type_check diff --git a/examples/cognite_core/data_classes/_core/datapoints_api.py b/examples/cognite_core/data_classes/_core/datapoints_api.py index 0199917d8..0bdd2ebb6 100644 --- a/examples/cognite_core/data_classes/_core/datapoints_api.py +++ b/examples/cognite_core/data_classes/_core/datapoints_api.py @@ -150,7 +150,6 @@ def retrieve_dataframe( uniform_index=uniform_index, include_aggregate_name=include_aggregate_name, include_granularity_name=include_granularity_name, - column_names="instance_id", ) def __getattr__(self, item: str) -> Any: diff --git a/examples/cognite_core/data_classes/_core/query/executor.py b/examples/cognite_core/data_classes/_core/query/executor.py index f4b75ea9f..535287e6f 100644 --- a/examples/cognite_core/data_classes/_core/query/executor.py +++ b/examples/cognite_core/data_classes/_core/query/executor.py @@ -256,7 +256,7 @@ def _fetch_reverse_direct_relation_of_lists( is_selected = is_items if step.raw_filter is None else dm.filters.And(is_items, step.raw_filter) chunk_result = client.data_modeling.instances.search( - view_id, properties=None, filter=is_selected, limit=api_limit + view=view_id, query=None, properties=None, filter=is_selected, limit=api_limit ) for node in chunk_result: node_id = node.as_id() diff --git a/examples/cognite_core/data_classes/_core/query/select.py b/examples/cognite_core/data_classes/_core/query/select.py index 604375e04..9cd65d2c3 100644 --- a/examples/cognite_core/data_classes/_core/query/select.py +++ b/examples/cognite_core/data_classes/_core/query/select.py @@ -224,7 +224,7 @@ def _create_query( step.connection_property = item._connection_property step.expression.from_ = from_ step.expression.filter = item._assemble_filter() - step.expression.sort = item._create_sort() + step.expression.sort = item._create_sort() or [] builder.append(step) elif isinstance(item, NodeQueryCore) and isinstance(item._expression, dm.query.EdgeResultSetExpression): # Edge without properties @@ -263,7 +263,7 @@ def _create_query( ) step.expression.from_ = from_ step.expression.filter = item._assemble_filter() - step.expression.sort = item._create_sort() + step.expression.sort = item._create_sort() or [] builder.append(step) else: raise TypeError(f"Unsupported query step type: {type(item._expression)}") diff --git a/examples/cognite_core/data_classes/_core/query/step.py b/examples/cognite_core/data_classes/_core/query/step.py index 21f561746..32ce0a010 100644 --- a/examples/cognite_core/data_classes/_core/query/step.py +++ b/examples/cognite_core/data_classes/_core/query/step.py @@ -7,7 +7,7 @@ from cognite.client import CogniteClient from cognite.client.data_classes import data_modeling as dm -from cognite.client.data_classes._base import CogniteObject +from cognite.client.data_classes._base import CogniteResource from cognite.client.data_classes.data_modeling.instances import Instance from cognite.client.data_classes.data_modeling.views import ReverseDirectRelation, ViewProperty @@ -20,7 +20,7 @@ @dataclass(frozen=True) -class ViewPropertyId(CogniteObject): +class ViewPropertyId(CogniteResource): view: dm.ViewId property: str diff --git a/examples/omni/_api/_core.py b/examples/omni/_api/_core.py index 45b1c4e77..adc8b0d82 100644 --- a/examples/omni/_api/_core.py +++ b/examples/omni/_api/_core.py @@ -561,8 +561,10 @@ def _parse_item(self, data: dict[str, Any]) -> None: elif "__typename" in data: try: item = self._data_class_by_type[data["__typename"]].model_validate(data) - except KeyError: - raise ValueError(f"Could not find class for type {data['__typename']}") from None + except KeyError as key_error: + if key_error.args[0] == data["__typename"]: + raise ValueError(f"Could not find class for type {data['__typename']}") from None + raise key_error else: self._output.append(item) else: diff --git a/examples/omni/_api_client.py b/examples/omni/_api_client.py index f5d28ff6a..5397cdd4d 100644 --- a/examples/omni/_api_client.py +++ b/examples/omni/_api_client.py @@ -45,7 +45,7 @@ class OmniClient: Generated with: pygen = 0.0.0 - cognite-sdk = 7.92.0 + cognite-sdk = 8.0.5 pydantic = 2.12.5 Data Model: diff --git a/examples/omni/data_classes/_core/cdf_external.py b/examples/omni/data_classes/_core/cdf_external.py index b8291bb4d..26564bb2f 100644 --- a/examples/omni/data_classes/_core/cdf_external.py +++ b/examples/omni/data_classes/_core/cdf_external.py @@ -1,11 +1,14 @@ from __future__ import annotations import datetime +from collections.abc import Callable from typing import ( Annotated, Optional, Any, no_type_check, + TypeVar, + Set, ) from cognite.client import data_modeling as dm @@ -24,6 +27,24 @@ from pydantic.alias_generators import to_camel from pydantic.functional_serializers import PlainSerializer +T_CogniteResource = TypeVar("T_CogniteResource", bound=CogniteTimeSeries | CogniteSequence | CogniteFileMetadata) + +_MISSING_VALUE = -999 + + +def _create_load_method(resource_cls: type[T_CogniteResource], required_fields: Set[str]) -> Callable[[Any], Any]: + def _load_if_dict(value: Any) -> Any: + if not isinstance(value, dict): + return value + if missing_values := set(required_fields) - set(value.keys()): + raise ValueError(f"Missing required fields: {', '.join(missing_values)}") + for key in ["createdTime", "lastUpdatedTime"]: + # GraphQL does not support returning these properties, while the read classes requires them. + value[key] = _MISSING_VALUE + return resource_cls.load(value) + + return _load_if_dict + TimeSeries = Annotated[ CogniteTimeSeries, @@ -32,7 +53,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteTimeSeries.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteTimeSeries, {"id", "isStep", "isString"})), ] @@ -43,7 +64,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteSequence.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteSequence, {"id", "columns"})), ] @@ -54,7 +75,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteFileMetadata.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteFileMetadata, {"id", "uploaded", "name"})), ] @@ -123,6 +144,15 @@ def parse_datapoints(cls, data: Any) -> Any: datetime.datetime.fromisoformat(item["timestamp"].replace("Z", "+00:00")) ) data["datapoints"] = datapoints["items"] + if missing := [name for name in ["id", "isString", "isStep"] if data.get(name) is None]: + raise ValueError( + f"Cannot create datapoints, missing required fields: {', '.join(missing)}. " + "You need to include these in your query." + ) + if "type" not in data: + # Type is not supported in the timeseries you retrieve through GraphQL, but it is required + # for the Datapoints object. Luckily it can be inferred from the isString field, so we set it here. + data["type"] = "string" if data["isString"] else "numeric" data["data"] = Datapoints.load(data) if isinstance(data, dict) and "getLatestDataPoint" in data: latest = data.pop("getLatestDataPoint") @@ -146,6 +176,7 @@ def as_write(self) -> CogniteTimeSeriesWrite: description=self.description, ) + @no_type_check def as_read(self) -> CogniteTimeSeries: return CogniteTimeSeries( id=self.id, @@ -160,6 +191,8 @@ def as_read(self) -> CogniteTimeSeries: is_step=self.is_step, description=self.description, security_categories=self.security_categories, + created_time=self.created_time, + last_updated_time=self.last_updated_time, ) @@ -237,7 +270,7 @@ class SequenceColumnGraphQL(GraphQLExternal): @field_validator("value_type", mode="before") def title_value_type(cls, value: Any) -> Any: if isinstance(value, str): - return value.title() + return value.upper() return value @no_type_check diff --git a/examples/omni/data_classes/_core/datapoints_api.py b/examples/omni/data_classes/_core/datapoints_api.py index 45a0a5f51..656863d6b 100644 --- a/examples/omni/data_classes/_core/datapoints_api.py +++ b/examples/omni/data_classes/_core/datapoints_api.py @@ -150,7 +150,6 @@ def retrieve_dataframe( uniform_index=uniform_index, include_aggregate_name=include_aggregate_name, include_granularity_name=include_granularity_name, - column_names="instance_id", ) def __getattr__(self, item: str) -> Any: diff --git a/examples/omni/data_classes/_core/query/executor.py b/examples/omni/data_classes/_core/query/executor.py index f35e6eb2a..4b3825d85 100644 --- a/examples/omni/data_classes/_core/query/executor.py +++ b/examples/omni/data_classes/_core/query/executor.py @@ -256,7 +256,7 @@ def _fetch_reverse_direct_relation_of_lists( is_selected = is_items if step.raw_filter is None else dm.filters.And(is_items, step.raw_filter) chunk_result = client.data_modeling.instances.search( - view_id, properties=None, filter=is_selected, limit=api_limit + view=view_id, query=None, properties=None, filter=is_selected, limit=api_limit ) for node in chunk_result: node_id = node.as_id() diff --git a/examples/omni/data_classes/_core/query/filter_classes.py b/examples/omni/data_classes/_core/query/filter_classes.py index 40bfa8ad1..2d583f2cd 100644 --- a/examples/omni/data_classes/_core/query/filter_classes.py +++ b/examples/omni/data_classes/_core/query/filter_classes.py @@ -8,7 +8,6 @@ from omni.data_classes._core.helpers import as_instance_dict_id - T_QueryCore = TypeVar("T_QueryCore") diff --git a/examples/omni/data_classes/_core/query/select.py b/examples/omni/data_classes/_core/query/select.py index 07665b927..052aa0170 100644 --- a/examples/omni/data_classes/_core/query/select.py +++ b/examples/omni/data_classes/_core/query/select.py @@ -31,7 +31,6 @@ from omni.data_classes._core.query.processing import QueryUnpacker from omni.data_classes._core.query.step import QueryBuildStep, ViewPropertyId - T_DomainListEnd = TypeVar("T_DomainListEnd", bound=Union[DomainModelList, DomainRelationList], covariant=True) @@ -225,7 +224,7 @@ def _create_query( step.connection_property = item._connection_property step.expression.from_ = from_ step.expression.filter = item._assemble_filter() - step.expression.sort = item._create_sort() + step.expression.sort = item._create_sort() or [] builder.append(step) elif isinstance(item, NodeQueryCore) and isinstance(item._expression, dm.query.EdgeResultSetExpression): # Edge without properties @@ -264,7 +263,7 @@ def _create_query( ) step.expression.from_ = from_ step.expression.filter = item._assemble_filter() - step.expression.sort = item._create_sort() + step.expression.sort = item._create_sort() or [] builder.append(step) else: raise TypeError(f"Unsupported query step type: {type(item._expression)}") diff --git a/examples/omni/data_classes/_core/query/step.py b/examples/omni/data_classes/_core/query/step.py index 4576844a9..73ead33b9 100644 --- a/examples/omni/data_classes/_core/query/step.py +++ b/examples/omni/data_classes/_core/query/step.py @@ -7,7 +7,7 @@ from cognite.client import CogniteClient from cognite.client.data_classes import data_modeling as dm -from cognite.client.data_classes._base import CogniteObject +from cognite.client.data_classes._base import CogniteResource from cognite.client.data_classes.data_modeling.instances import Instance from cognite.client.data_classes.data_modeling.views import ReverseDirectRelation, ViewProperty @@ -20,7 +20,7 @@ @dataclass(frozen=True) -class ViewPropertyId(CogniteObject): +class ViewPropertyId(CogniteResource): view: dm.ViewId property: str diff --git a/examples/omni_multi/_api/_core.py b/examples/omni_multi/_api/_core.py index 80c7c23a7..9988f4d65 100644 --- a/examples/omni_multi/_api/_core.py +++ b/examples/omni_multi/_api/_core.py @@ -538,8 +538,10 @@ def _parse_item(self, data: dict[str, Any]) -> None: elif "__typename" in data: try: item = self._data_class_by_type[data["__typename"]].model_validate(data) - except KeyError: - raise ValueError(f"Could not find class for type {data['__typename']}") from None + except KeyError as key_error: + if key_error.args[0] == data["__typename"]: + raise ValueError(f"Could not find class for type {data['__typename']}") from None + raise key_error else: self._output.append(item) else: diff --git a/examples/omni_multi/_api_client.py b/examples/omni_multi/_api_client.py index fe3187bf1..9c82b1c82 100644 --- a/examples/omni_multi/_api_client.py +++ b/examples/omni_multi/_api_client.py @@ -120,7 +120,7 @@ class OmniMultiClient: Generated with: pygen = 0.0.0 - cognite-sdk = 7.92.0 + cognite-sdk = 8.0.5 pydantic = 2.12.5 """ diff --git a/examples/omni_multi/data_classes/_core/cdf_external.py b/examples/omni_multi/data_classes/_core/cdf_external.py index 43d22a881..26564bb2f 100644 --- a/examples/omni_multi/data_classes/_core/cdf_external.py +++ b/examples/omni_multi/data_classes/_core/cdf_external.py @@ -1,11 +1,14 @@ from __future__ import annotations import datetime +from collections.abc import Callable from typing import ( Annotated, Optional, Any, no_type_check, + TypeVar, + Set, ) from cognite.client import data_modeling as dm @@ -24,6 +27,25 @@ from pydantic.alias_generators import to_camel from pydantic.functional_serializers import PlainSerializer +T_CogniteResource = TypeVar("T_CogniteResource", bound=CogniteTimeSeries | CogniteSequence | CogniteFileMetadata) + +_MISSING_VALUE = -999 + + +def _create_load_method(resource_cls: type[T_CogniteResource], required_fields: Set[str]) -> Callable[[Any], Any]: + def _load_if_dict(value: Any) -> Any: + if not isinstance(value, dict): + return value + if missing_values := set(required_fields) - set(value.keys()): + raise ValueError(f"Missing required fields: {', '.join(missing_values)}") + for key in ["createdTime", "lastUpdatedTime"]: + # GraphQL does not support returning these properties, while the read classes requires them. + value[key] = _MISSING_VALUE + return resource_cls.load(value) + + return _load_if_dict + + TimeSeries = Annotated[ CogniteTimeSeries, PlainSerializer( @@ -31,7 +53,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteTimeSeries.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteTimeSeries, {"id", "isStep", "isString"})), ] @@ -42,7 +64,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteSequence.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteSequence, {"id", "columns"})), ] @@ -53,7 +75,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteFileMetadata.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteFileMetadata, {"id", "uploaded", "name"})), ] @@ -122,6 +144,15 @@ def parse_datapoints(cls, data: Any) -> Any: datetime.datetime.fromisoformat(item["timestamp"].replace("Z", "+00:00")) ) data["datapoints"] = datapoints["items"] + if missing := [name for name in ["id", "isString", "isStep"] if data.get(name) is None]: + raise ValueError( + f"Cannot create datapoints, missing required fields: {', '.join(missing)}. " + "You need to include these in your query." + ) + if "type" not in data: + # Type is not supported in the timeseries you retrieve through GraphQL, but it is required + # for the Datapoints object. Luckily it can be inferred from the isString field, so we set it here. + data["type"] = "string" if data["isString"] else "numeric" data["data"] = Datapoints.load(data) if isinstance(data, dict) and "getLatestDataPoint" in data: latest = data.pop("getLatestDataPoint") @@ -145,6 +176,7 @@ def as_write(self) -> CogniteTimeSeriesWrite: description=self.description, ) + @no_type_check def as_read(self) -> CogniteTimeSeries: return CogniteTimeSeries( id=self.id, @@ -159,6 +191,8 @@ def as_read(self) -> CogniteTimeSeries: is_step=self.is_step, description=self.description, security_categories=self.security_categories, + created_time=self.created_time, + last_updated_time=self.last_updated_time, ) @@ -236,7 +270,7 @@ class SequenceColumnGraphQL(GraphQLExternal): @field_validator("value_type", mode="before") def title_value_type(cls, value: Any) -> Any: if isinstance(value, str): - return value.title() + return value.upper() return value @no_type_check diff --git a/examples/omni_multi/data_classes/_core/datapoints_api.py b/examples/omni_multi/data_classes/_core/datapoints_api.py index fd37c59dc..7c4cc58fd 100644 --- a/examples/omni_multi/data_classes/_core/datapoints_api.py +++ b/examples/omni_multi/data_classes/_core/datapoints_api.py @@ -150,7 +150,6 @@ def retrieve_dataframe( uniform_index=uniform_index, include_aggregate_name=include_aggregate_name, include_granularity_name=include_granularity_name, - column_names="instance_id", ) def __getattr__(self, item: str) -> Any: diff --git a/examples/omni_multi/data_classes/_core/query/executor.py b/examples/omni_multi/data_classes/_core/query/executor.py index 57cb5ab66..eb9519904 100644 --- a/examples/omni_multi/data_classes/_core/query/executor.py +++ b/examples/omni_multi/data_classes/_core/query/executor.py @@ -256,7 +256,7 @@ def _fetch_reverse_direct_relation_of_lists( is_selected = is_items if step.raw_filter is None else dm.filters.And(is_items, step.raw_filter) chunk_result = client.data_modeling.instances.search( - view_id, properties=None, filter=is_selected, limit=api_limit + view=view_id, query=None, properties=None, filter=is_selected, limit=api_limit ) for node in chunk_result: node_id = node.as_id() diff --git a/examples/omni_multi/data_classes/_core/query/select.py b/examples/omni_multi/data_classes/_core/query/select.py index b05fa0a82..8ee229f64 100644 --- a/examples/omni_multi/data_classes/_core/query/select.py +++ b/examples/omni_multi/data_classes/_core/query/select.py @@ -224,7 +224,7 @@ def _create_query( step.connection_property = item._connection_property step.expression.from_ = from_ step.expression.filter = item._assemble_filter() - step.expression.sort = item._create_sort() + step.expression.sort = item._create_sort() or [] builder.append(step) elif isinstance(item, NodeQueryCore) and isinstance(item._expression, dm.query.EdgeResultSetExpression): # Edge without properties @@ -263,7 +263,7 @@ def _create_query( ) step.expression.from_ = from_ step.expression.filter = item._assemble_filter() - step.expression.sort = item._create_sort() + step.expression.sort = item._create_sort() or [] builder.append(step) else: raise TypeError(f"Unsupported query step type: {type(item._expression)}") diff --git a/examples/omni_multi/data_classes/_core/query/step.py b/examples/omni_multi/data_classes/_core/query/step.py index 20c0f394c..c872ca5d1 100644 --- a/examples/omni_multi/data_classes/_core/query/step.py +++ b/examples/omni_multi/data_classes/_core/query/step.py @@ -7,7 +7,7 @@ from cognite.client import CogniteClient from cognite.client.data_classes import data_modeling as dm -from cognite.client.data_classes._base import CogniteObject +from cognite.client.data_classes._base import CogniteResource from cognite.client.data_classes.data_modeling.instances import Instance from cognite.client.data_classes.data_modeling.views import ReverseDirectRelation, ViewProperty @@ -20,7 +20,7 @@ @dataclass(frozen=True) -class ViewPropertyId(CogniteObject): +class ViewPropertyId(CogniteResource): view: dm.ViewId property: str diff --git a/examples/omni_sub/_api/_core.py b/examples/omni_sub/_api/_core.py index a1eef4d49..a06fd4778 100644 --- a/examples/omni_sub/_api/_core.py +++ b/examples/omni_sub/_api/_core.py @@ -538,8 +538,10 @@ def _parse_item(self, data: dict[str, Any]) -> None: elif "__typename" in data: try: item = self._data_class_by_type[data["__typename"]].model_validate(data) - except KeyError: - raise ValueError(f"Could not find class for type {data['__typename']}") from None + except KeyError as key_error: + if key_error.args[0] == data["__typename"]: + raise ValueError(f"Could not find class for type {data['__typename']}") from None + raise key_error else: self._output.append(item) else: diff --git a/examples/omni_sub/_api_client.py b/examples/omni_sub/_api_client.py index bf1a5d471..a3df82b87 100644 --- a/examples/omni_sub/_api_client.py +++ b/examples/omni_sub/_api_client.py @@ -26,7 +26,7 @@ class OmniSubClient: Generated with: pygen = 0.0.0 - cognite-sdk = 7.92.0 + cognite-sdk = 8.0.5 pydantic = 2.12.5 Data Model: diff --git a/examples/omni_sub/data_classes/_core/cdf_external.py b/examples/omni_sub/data_classes/_core/cdf_external.py index 43d22a881..26564bb2f 100644 --- a/examples/omni_sub/data_classes/_core/cdf_external.py +++ b/examples/omni_sub/data_classes/_core/cdf_external.py @@ -1,11 +1,14 @@ from __future__ import annotations import datetime +from collections.abc import Callable from typing import ( Annotated, Optional, Any, no_type_check, + TypeVar, + Set, ) from cognite.client import data_modeling as dm @@ -24,6 +27,25 @@ from pydantic.alias_generators import to_camel from pydantic.functional_serializers import PlainSerializer +T_CogniteResource = TypeVar("T_CogniteResource", bound=CogniteTimeSeries | CogniteSequence | CogniteFileMetadata) + +_MISSING_VALUE = -999 + + +def _create_load_method(resource_cls: type[T_CogniteResource], required_fields: Set[str]) -> Callable[[Any], Any]: + def _load_if_dict(value: Any) -> Any: + if not isinstance(value, dict): + return value + if missing_values := set(required_fields) - set(value.keys()): + raise ValueError(f"Missing required fields: {', '.join(missing_values)}") + for key in ["createdTime", "lastUpdatedTime"]: + # GraphQL does not support returning these properties, while the read classes requires them. + value[key] = _MISSING_VALUE + return resource_cls.load(value) + + return _load_if_dict + + TimeSeries = Annotated[ CogniteTimeSeries, PlainSerializer( @@ -31,7 +53,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteTimeSeries.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteTimeSeries, {"id", "isStep", "isString"})), ] @@ -42,7 +64,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteSequence.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteSequence, {"id", "columns"})), ] @@ -53,7 +75,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteFileMetadata.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteFileMetadata, {"id", "uploaded", "name"})), ] @@ -122,6 +144,15 @@ def parse_datapoints(cls, data: Any) -> Any: datetime.datetime.fromisoformat(item["timestamp"].replace("Z", "+00:00")) ) data["datapoints"] = datapoints["items"] + if missing := [name for name in ["id", "isString", "isStep"] if data.get(name) is None]: + raise ValueError( + f"Cannot create datapoints, missing required fields: {', '.join(missing)}. " + "You need to include these in your query." + ) + if "type" not in data: + # Type is not supported in the timeseries you retrieve through GraphQL, but it is required + # for the Datapoints object. Luckily it can be inferred from the isString field, so we set it here. + data["type"] = "string" if data["isString"] else "numeric" data["data"] = Datapoints.load(data) if isinstance(data, dict) and "getLatestDataPoint" in data: latest = data.pop("getLatestDataPoint") @@ -145,6 +176,7 @@ def as_write(self) -> CogniteTimeSeriesWrite: description=self.description, ) + @no_type_check def as_read(self) -> CogniteTimeSeries: return CogniteTimeSeries( id=self.id, @@ -159,6 +191,8 @@ def as_read(self) -> CogniteTimeSeries: is_step=self.is_step, description=self.description, security_categories=self.security_categories, + created_time=self.created_time, + last_updated_time=self.last_updated_time, ) @@ -236,7 +270,7 @@ class SequenceColumnGraphQL(GraphQLExternal): @field_validator("value_type", mode="before") def title_value_type(cls, value: Any) -> Any: if isinstance(value, str): - return value.title() + return value.upper() return value @no_type_check diff --git a/examples/omni_sub/data_classes/_core/datapoints_api.py b/examples/omni_sub/data_classes/_core/datapoints_api.py index ea621cf8c..8bff2407d 100644 --- a/examples/omni_sub/data_classes/_core/datapoints_api.py +++ b/examples/omni_sub/data_classes/_core/datapoints_api.py @@ -150,7 +150,6 @@ def retrieve_dataframe( uniform_index=uniform_index, include_aggregate_name=include_aggregate_name, include_granularity_name=include_granularity_name, - column_names="instance_id", ) def __getattr__(self, item: str) -> Any: diff --git a/examples/omni_sub/data_classes/_core/query/executor.py b/examples/omni_sub/data_classes/_core/query/executor.py index 576a88b02..40341dc1a 100644 --- a/examples/omni_sub/data_classes/_core/query/executor.py +++ b/examples/omni_sub/data_classes/_core/query/executor.py @@ -256,7 +256,7 @@ def _fetch_reverse_direct_relation_of_lists( is_selected = is_items if step.raw_filter is None else dm.filters.And(is_items, step.raw_filter) chunk_result = client.data_modeling.instances.search( - view_id, properties=None, filter=is_selected, limit=api_limit + view=view_id, query=None, properties=None, filter=is_selected, limit=api_limit ) for node in chunk_result: node_id = node.as_id() diff --git a/examples/omni_sub/data_classes/_core/query/select.py b/examples/omni_sub/data_classes/_core/query/select.py index 28db08ea4..48b6ecbb1 100644 --- a/examples/omni_sub/data_classes/_core/query/select.py +++ b/examples/omni_sub/data_classes/_core/query/select.py @@ -224,7 +224,7 @@ def _create_query( step.connection_property = item._connection_property step.expression.from_ = from_ step.expression.filter = item._assemble_filter() - step.expression.sort = item._create_sort() + step.expression.sort = item._create_sort() or [] builder.append(step) elif isinstance(item, NodeQueryCore) and isinstance(item._expression, dm.query.EdgeResultSetExpression): # Edge without properties @@ -263,7 +263,7 @@ def _create_query( ) step.expression.from_ = from_ step.expression.filter = item._assemble_filter() - step.expression.sort = item._create_sort() + step.expression.sort = item._create_sort() or [] builder.append(step) else: raise TypeError(f"Unsupported query step type: {type(item._expression)}") diff --git a/examples/omni_sub/data_classes/_core/query/step.py b/examples/omni_sub/data_classes/_core/query/step.py index e6783bc8d..d76c5cdc3 100644 --- a/examples/omni_sub/data_classes/_core/query/step.py +++ b/examples/omni_sub/data_classes/_core/query/step.py @@ -7,7 +7,7 @@ from cognite.client import CogniteClient from cognite.client.data_classes import data_modeling as dm -from cognite.client.data_classes._base import CogniteObject +from cognite.client.data_classes._base import CogniteResource from cognite.client.data_classes.data_modeling.instances import Instance from cognite.client.data_classes.data_modeling.views import ReverseDirectRelation, ViewProperty @@ -20,7 +20,7 @@ @dataclass(frozen=True) -class ViewPropertyId(CogniteObject): +class ViewPropertyId(CogniteResource): view: dm.ViewId property: str diff --git a/examples/wind_turbine/_api/_core.py b/examples/wind_turbine/_api/_core.py index d464ff98b..c6f871fd0 100644 --- a/examples/wind_turbine/_api/_core.py +++ b/examples/wind_turbine/_api/_core.py @@ -561,8 +561,10 @@ def _parse_item(self, data: dict[str, Any]) -> None: elif "__typename" in data: try: item = self._data_class_by_type[data["__typename"]].model_validate(data) - except KeyError: - raise ValueError(f"Could not find class for type {data['__typename']}") from None + except KeyError as key_error: + if key_error.args[0] == data["__typename"]: + raise ValueError(f"Could not find class for type {data['__typename']}") from None + raise key_error else: self._output.append(item) else: diff --git a/examples/wind_turbine/_api_client.py b/examples/wind_turbine/_api_client.py index c93169dce..d06508bd8 100644 --- a/examples/wind_turbine/_api_client.py +++ b/examples/wind_turbine/_api_client.py @@ -38,7 +38,7 @@ class WindTurbineClient: Generated with: pygen = 0.0.0 - cognite-sdk = 7.92.0 + cognite-sdk = 8.0.5 pydantic = 2.12.5 Data Model: diff --git a/examples/wind_turbine/data_classes/_core/cdf_external.py b/examples/wind_turbine/data_classes/_core/cdf_external.py index 43d22a881..26564bb2f 100644 --- a/examples/wind_turbine/data_classes/_core/cdf_external.py +++ b/examples/wind_turbine/data_classes/_core/cdf_external.py @@ -1,11 +1,14 @@ from __future__ import annotations import datetime +from collections.abc import Callable from typing import ( Annotated, Optional, Any, no_type_check, + TypeVar, + Set, ) from cognite.client import data_modeling as dm @@ -24,6 +27,25 @@ from pydantic.alias_generators import to_camel from pydantic.functional_serializers import PlainSerializer +T_CogniteResource = TypeVar("T_CogniteResource", bound=CogniteTimeSeries | CogniteSequence | CogniteFileMetadata) + +_MISSING_VALUE = -999 + + +def _create_load_method(resource_cls: type[T_CogniteResource], required_fields: Set[str]) -> Callable[[Any], Any]: + def _load_if_dict(value: Any) -> Any: + if not isinstance(value, dict): + return value + if missing_values := set(required_fields) - set(value.keys()): + raise ValueError(f"Missing required fields: {', '.join(missing_values)}") + for key in ["createdTime", "lastUpdatedTime"]: + # GraphQL does not support returning these properties, while the read classes requires them. + value[key] = _MISSING_VALUE + return resource_cls.load(value) + + return _load_if_dict + + TimeSeries = Annotated[ CogniteTimeSeries, PlainSerializer( @@ -31,7 +53,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteTimeSeries.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteTimeSeries, {"id", "isStep", "isString"})), ] @@ -42,7 +64,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteSequence.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteSequence, {"id", "columns"})), ] @@ -53,7 +75,7 @@ return_type=dict, when_used="unless-none", ), - BeforeValidator(lambda v: CogniteFileMetadata.load(v) if isinstance(v, dict) else v), + BeforeValidator(_create_load_method(CogniteFileMetadata, {"id", "uploaded", "name"})), ] @@ -122,6 +144,15 @@ def parse_datapoints(cls, data: Any) -> Any: datetime.datetime.fromisoformat(item["timestamp"].replace("Z", "+00:00")) ) data["datapoints"] = datapoints["items"] + if missing := [name for name in ["id", "isString", "isStep"] if data.get(name) is None]: + raise ValueError( + f"Cannot create datapoints, missing required fields: {', '.join(missing)}. " + "You need to include these in your query." + ) + if "type" not in data: + # Type is not supported in the timeseries you retrieve through GraphQL, but it is required + # for the Datapoints object. Luckily it can be inferred from the isString field, so we set it here. + data["type"] = "string" if data["isString"] else "numeric" data["data"] = Datapoints.load(data) if isinstance(data, dict) and "getLatestDataPoint" in data: latest = data.pop("getLatestDataPoint") @@ -145,6 +176,7 @@ def as_write(self) -> CogniteTimeSeriesWrite: description=self.description, ) + @no_type_check def as_read(self) -> CogniteTimeSeries: return CogniteTimeSeries( id=self.id, @@ -159,6 +191,8 @@ def as_read(self) -> CogniteTimeSeries: is_step=self.is_step, description=self.description, security_categories=self.security_categories, + created_time=self.created_time, + last_updated_time=self.last_updated_time, ) @@ -236,7 +270,7 @@ class SequenceColumnGraphQL(GraphQLExternal): @field_validator("value_type", mode="before") def title_value_type(cls, value: Any) -> Any: if isinstance(value, str): - return value.title() + return value.upper() return value @no_type_check diff --git a/examples/wind_turbine/data_classes/_core/datapoints_api.py b/examples/wind_turbine/data_classes/_core/datapoints_api.py index dc3e6ba89..407bf055f 100644 --- a/examples/wind_turbine/data_classes/_core/datapoints_api.py +++ b/examples/wind_turbine/data_classes/_core/datapoints_api.py @@ -150,7 +150,6 @@ def retrieve_dataframe( uniform_index=uniform_index, include_aggregate_name=include_aggregate_name, include_granularity_name=include_granularity_name, - column_names="instance_id", ) def __getattr__(self, item: str) -> Any: diff --git a/examples/wind_turbine/data_classes/_core/query/executor.py b/examples/wind_turbine/data_classes/_core/query/executor.py index e9a7b61f5..f6d417908 100644 --- a/examples/wind_turbine/data_classes/_core/query/executor.py +++ b/examples/wind_turbine/data_classes/_core/query/executor.py @@ -256,7 +256,7 @@ def _fetch_reverse_direct_relation_of_lists( is_selected = is_items if step.raw_filter is None else dm.filters.And(is_items, step.raw_filter) chunk_result = client.data_modeling.instances.search( - view_id, properties=None, filter=is_selected, limit=api_limit + view=view_id, query=None, properties=None, filter=is_selected, limit=api_limit ) for node in chunk_result: node_id = node.as_id() diff --git a/examples/wind_turbine/data_classes/_core/query/select.py b/examples/wind_turbine/data_classes/_core/query/select.py index 53977f874..6dbe1014d 100644 --- a/examples/wind_turbine/data_classes/_core/query/select.py +++ b/examples/wind_turbine/data_classes/_core/query/select.py @@ -224,7 +224,7 @@ def _create_query( step.connection_property = item._connection_property step.expression.from_ = from_ step.expression.filter = item._assemble_filter() - step.expression.sort = item._create_sort() + step.expression.sort = item._create_sort() or [] builder.append(step) elif isinstance(item, NodeQueryCore) and isinstance(item._expression, dm.query.EdgeResultSetExpression): # Edge without properties @@ -263,7 +263,7 @@ def _create_query( ) step.expression.from_ = from_ step.expression.filter = item._assemble_filter() - step.expression.sort = item._create_sort() + step.expression.sort = item._create_sort() or [] builder.append(step) else: raise TypeError(f"Unsupported query step type: {type(item._expression)}") diff --git a/examples/wind_turbine/data_classes/_core/query/step.py b/examples/wind_turbine/data_classes/_core/query/step.py index f64dfe9f8..a201d3b65 100644 --- a/examples/wind_turbine/data_classes/_core/query/step.py +++ b/examples/wind_turbine/data_classes/_core/query/step.py @@ -7,7 +7,7 @@ from cognite.client import CogniteClient from cognite.client.data_classes import data_modeling as dm -from cognite.client.data_classes._base import CogniteObject +from cognite.client.data_classes._base import CogniteResource from cognite.client.data_classes.data_modeling.instances import Instance from cognite.client.data_classes.data_modeling.views import ReverseDirectRelation, ViewProperty @@ -20,7 +20,7 @@ @dataclass(frozen=True) -class ViewPropertyId(CogniteObject): +class ViewPropertyId(CogniteResource): view: dm.ViewId property: str diff --git a/pyproject.toml b/pyproject.toml index 1c999de7c..c81f2c04b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,8 +7,8 @@ readme="README.md" requires-python = ">=3.10,<4.0" license = "Apache-2.0" dependencies = [ - "cognite-sdk>=7.86.0, <8.0.0", - # pydantic 2.7.0 is the latest versoion supported by pyodide '0.26.2' which is what is used in the + "cognite-sdk>=8.0.1, <9.0.0", + # pydantic 2.7.0 is the latest version supported by pyodide '0.26.2' which is what is used in the # Cognite JupyterLab environment. # Thus, we pin it to ensure compatibility with the pyodide build. "pydantic>=2.7", @@ -61,7 +61,9 @@ dev = [ "Faker>=18.0.0", "hypothesis>=6.0.0", "jupyterlab>=3.0.0", - "cognite-toolkit==0.5.87", + # cognite-toolkit is not yet compatible with PySDK v8 and it is only used to deploy test data. + # Add back in when it is compatible with PySDK v8. +# "cognite-toolkit==0.5.87", "marko>=2.1.0, <3.0.0", ] diff --git a/tests/test_integration/test_graphql_query.py b/tests/test_integration/test_graphql_query.py index cc0e2bd6e..e0ac9a8b2 100644 --- a/tests/test_integration/test_graphql_query.py +++ b/tests/test_integration/test_graphql_query.py @@ -37,11 +37,15 @@ def test_query_cdf_external_timeseries_and_sequence(omni_client: OmniClient) -> timeseries { externalId name + id + isStep + isString } sequence{ externalId name + id columns{ externalId valueType @@ -78,11 +82,15 @@ def test_query_cdf_external_listed_timeseries_and_sequence(omni_client: OmniClie timeseries { externalId name + id + isStep + isString } sequences{ externalId name + id columns{ externalId valueType @@ -168,6 +176,9 @@ def test_query_with_datapoints(turbine_client: WindTurbineClient) -> None: temperature{ externalId name + id + isStep + isString getDataPoints(granularity: "1d", aggregates: SUM, first: 100){ items{ timestamp diff --git a/tests/test_integration/test_upsert.py b/tests/test_integration/test_upsert.py index debad6f15..3b5f2f000 100644 --- a/tests/test_integration/test_upsert.py +++ b/tests/test_integration/test_upsert.py @@ -74,10 +74,10 @@ def test_upsert_multiple_requests(omni_client: OmniClient, cognite_client: Cogni ], ) resources = new_item_a.to_instances_write() - - limit = omni_client.connection_item_a._client.data_modeling.instances._CREATE_LIMIT + async_client = omni_client.connection_item_a._client.get_async_client() + limit = async_client.data_modeling.instances._CREATE_LIMIT try: - omni_client.connection_item_a._client.data_modeling.instances._CREATE_LIMIT = 1 + async_client.data_modeling.instances._CREATE_LIMIT = 1 # Act created = omni_client.upsert(new_item_a) @@ -86,7 +86,7 @@ def test_upsert_multiple_requests(omni_client: OmniClient, cognite_client: Cogni assert len(created.nodes) == 4 assert len(created.edges) == 3 finally: - omni_client.connection_item_a._client.data_modeling.instances._CREATE_LIMIT = limit + async_client.data_modeling.instances._CREATE_LIMIT = limit cognite_client.data_modeling.instances.delete(resources.nodes.as_ids(), resources.edges.as_ids()) @@ -259,7 +259,7 @@ def test_upsert_with_cdf_external(omni_client: OmniClient, cognite_client: Cogni SequenceColumnWrite( external_id=f"{test_name}:Column1", name="Column1", - value_type="String", + value_type="STRING", ) ], ), @@ -309,7 +309,7 @@ def test_upsert_with_cdf_external_listed(omni_client: OmniClient, cognite_client SequenceColumnWrite( external_id=f"{test_name}:Column1", name="Column1", - value_type="String", + value_type="STRING", ) ], ) diff --git a/tests/test_unit/conftest.py b/tests/test_unit/conftest.py index fb062419a..fe1823445 100644 --- a/tests/test_unit/conftest.py +++ b/tests/test_unit/conftest.py @@ -1,7 +1,8 @@ from collections.abc import Iterable +from unittest.mock import MagicMock import pytest -from cognite.client import CogniteClient +from cognite.client import ClientConfig, CogniteClient from cognite.client import data_modeling as dm from cognite.client.testing import monkeypatch_cognite_client @@ -24,6 +25,8 @@ def pygen_config() -> PygenConfig: @pytest.fixture() def mock_cognite_client() -> Iterable[CogniteClient]: with monkeypatch_cognite_client() as m: + m.config = MagicMock(spec=ClientConfig) + m.config.client_name = "CognitePygen" yield m diff --git a/tests/test_unit/test_generator/test_generate_core.py b/tests/test_unit/test_generator/test_generate_core.py index 39fcf4ab2..d941659ed 100644 --- a/tests/test_unit/test_generator/test_generate_core.py +++ b/tests/test_unit/test_generator/test_generate_core.py @@ -60,24 +60,31 @@ def test_generate_data_class_core_query_init(omni_multi_api_generator: MultiAPIG assert actual == expected -def test_generate_data_class_core_query_filter_classes(omni_multi_api_generator: MultiAPIGenerator) -> None: +def test_generate_data_class_core_query_filter_classes( + omni_multi_api_generator: MultiAPIGenerator, code_formatter: CodeFormatter +) -> None: # Arrange expected = OmniFiles.data_core_query_filter_classes.read_text() # Act actual = omni_multi_api_generator.generate_data_class_core_query_filter_classes() + actual = code_formatter.format_code(actual) + # Assert assert actual == expected -def test_generate_data_class_core_query_select(omni_multi_api_generator: MultiAPIGenerator) -> None: +def test_generate_data_class_core_query_select( + omni_multi_api_generator: MultiAPIGenerator, code_formatter: CodeFormatter +) -> None: # Arrange expected = OmniFiles.data_core_query_select.read_text() # Act actual = omni_multi_api_generator.generate_data_class_core_query_select() + actual = code_formatter.format_code(actual) # Assert assert actual == expected @@ -92,13 +99,17 @@ def test_generate_data_class_query_files(omni_multi_api_generator: MultiAPIGener assert actual == expected, f"File: {filename}" -def test_generate_data_class_core_cdf_external(omni_multi_api_generator: MultiAPIGenerator) -> None: +def test_generate_data_class_core_cdf_external( + omni_multi_api_generator: MultiAPIGenerator, code_formatter: CodeFormatter +) -> None: # Arrange expected = OmniFiles.data_core_cdf_external.read_text() # Act actual = omni_multi_api_generator.generate_data_class_core_cdf_external_file() + actual = code_formatter.format_code(actual) + # Assert assert actual == expected diff --git a/tests/test_unit/test_query/test_interface.py b/tests/test_unit/test_query/test_interface.py index 8e27f3c40..992896827 100644 --- a/tests/test_unit/test_query/test_interface.py +++ b/tests/test_unit/test_query/test_interface.py @@ -104,6 +104,7 @@ def test_not_prefix_existing_client(self) -> None: client_name, project="test_project", credentials=Token("223ienie"), + cluster="api", ) ) diff --git a/tests/test_unit/test_resulting_sdk/test_core_api.py b/tests/test_unit/test_resulting_sdk/test_core_api.py index ca651a70d..2e880093f 100644 --- a/tests/test_unit/test_resulting_sdk/test_core_api.py +++ b/tests/test_unit/test_resulting_sdk/test_core_api.py @@ -1,6 +1,8 @@ from typing import Any +from unittest.mock import MagicMock import pytest +from cognite.client import ClientConfig from cognite.client import data_modeling as dm from cognite.client.data_classes.data_modeling.instances import Properties from cognite.client.testing import monkeypatch_cognite_client @@ -171,6 +173,8 @@ def test_parse_query_without_typename(self) -> None: class TestAPIClass: def test_skip_validation(self) -> None: with monkeypatch_cognite_client() as mock_client: + mock_client.config = MagicMock(spec=ClientConfig) + mock_client.config.client_name = "CognitePygen" mock_client.data_modeling.instances.list.return_value = dm.NodeList[dm.Node]( [ dm.Node( @@ -214,6 +218,8 @@ def test_skip_validation(self) -> None: def test_retrieve_extra_arguments(self) -> None: with monkeypatch_cognite_client() as mock_client: + mock_client.config = MagicMock(spec=ClientConfig) + mock_client.config.client_name = "CognitePygen" mock_client.data_modeling.instances.list.return_value = dm.NodeList[dm.Node]( [ dm.Node( @@ -250,6 +256,8 @@ def test_retrieve_extra_arguments(self) -> None: def test_retrieve_unknown_enum(self) -> None: with monkeypatch_cognite_client() as mock_client: + mock_client.config = MagicMock(spec=ClientConfig) + mock_client.config.client_name = "CognitePygen" mock_client.data_modeling.instances.list.return_value = dm.NodeList[dm.Node]( [ dm.Node( diff --git a/tests/test_unit/test_resulting_sdk/test_data_class.py b/tests/test_unit/test_resulting_sdk/test_data_class.py index 8705f5be0..88a1c78bc 100644 --- a/tests/test_unit/test_resulting_sdk/test_data_class.py +++ b/tests/test_unit/test_resulting_sdk/test_data_class.py @@ -286,6 +286,9 @@ def test_as_write_with_cdf_external_references(self) -> None: mime_type="application/json", directory="/tmp", name="my_file_external_id", + uploaded=True, + last_updated_time=1, + created_time=0, ), timeseries=TimeSeries( id=987654321, @@ -293,6 +296,8 @@ def test_as_write_with_cdf_external_references(self) -> None: name="my_timeseries_external_id", is_step=False, is_string=False, + last_updated_time=1, + created_time=0, ), sequence=Sequence( id=1122334455, @@ -302,9 +307,11 @@ def test_as_write_with_cdf_external_references(self) -> None: SequenceColumn( external_id="column1", name="column1", - value_type="String", + value_type="STRING", ) ], + last_updated_time=1, + created_time=0, ), ) diff --git a/tests/test_unit/test_resulting_sdk/test_import_sdks.py b/tests/test_unit/test_resulting_sdk/test_import_sdks.py index a9f0bbd0b..e143a4eb6 100644 --- a/tests/test_unit/test_resulting_sdk/test_import_sdks.py +++ b/tests/test_unit/test_resulting_sdk/test_import_sdks.py @@ -4,9 +4,11 @@ import importlib from collections.abc import Iterable +from unittest.mock import MagicMock import pytest -from cognite.client import CogniteClient +from cognite.client import ClientConfig, CogniteClient +from cognite.client.testing import CogniteClientMock from tests.constants import EXAMPLE_SDKS, ExampleSDK @@ -21,14 +23,18 @@ def example_sdk_generated(skip_typed: bool = False) -> Iterable[ExampleSDK]: @pytest.mark.parametrize("example_sdk", example_sdk_generated()) -def test_import_client(example_sdk: ExampleSDK, mock_cognite_client: CogniteClient) -> None: +def test_import_client(example_sdk: ExampleSDK) -> None: + client = CogniteClientMock() + client.config = MagicMock(spec=ClientConfig) + client.config.client_name = "CognitePygen" + # Act module = vars(importlib.import_module(example_sdk.top_level_package)) # Assert if not example_sdk.is_typed: assert example_sdk.client_name in module - assert module[example_sdk.client_name](mock_cognite_client) + assert module[example_sdk.client_name](client) @pytest.mark.parametrize("example_sdk", example_sdk_generated(skip_typed=True)) diff --git a/tests/test_unit/test_resulting_sdk/test_iterate.py b/tests/test_unit/test_resulting_sdk/test_iterate.py index 5a1a5a418..fa42f2255 100644 --- a/tests/test_unit/test_resulting_sdk/test_iterate.py +++ b/tests/test_unit/test_resulting_sdk/test_iterate.py @@ -1,3 +1,6 @@ +from unittest.mock import MagicMock + +from cognite.client import ClientConfig from cognite.client.data_classes.aggregations import AggregatedNumberedValue from cognite.client.data_classes.data_modeling import Node, NodeListWithCursor from cognite.client.data_classes.data_modeling.instances import Properties @@ -17,6 +20,8 @@ def query_call(query: Query) -> QueryResult: return self.single_item_a_result with monkeypatch_cognite_client() as client: + client.config = MagicMock(spec=ClientConfig) + client.config.client_name = "CognitePygen" client.data_modeling.instances.aggregate.return_value = AggregatedNumberedValue("externalId", 4) client.data_modeling.instances.query.side_effect = query_call pygen = OmniClient(client) diff --git a/tests/test_unit/test_resulting_sdk/test_select.py b/tests/test_unit/test_resulting_sdk/test_select.py index 7fbd2d23e..0bf099e89 100644 --- a/tests/test_unit/test_resulting_sdk/test_select.py +++ b/tests/test_unit/test_resulting_sdk/test_select.py @@ -1,4 +1,7 @@ +from unittest.mock import MagicMock + import pytest +from cognite.client import ClientConfig from cognite.client.testing import monkeypatch_cognite_client from omni import OmniClient @@ -9,6 +12,8 @@ class TestSelectMethod: ) def test_dump_yaml(self) -> None: with monkeypatch_cognite_client() as client: + client.config = MagicMock(spec=ClientConfig) + client.config.client_name = "CognitePygen" pygen = OmniClient(client) query_yaml = pygen.connection_item_a.select().other_direct._dump_yaml() @@ -20,5 +25,7 @@ def test_dump_yaml(self) -> None: ) def test_select_no_user_warning(self) -> None: with monkeypatch_cognite_client() as client: + client.config = MagicMock(spec=ClientConfig) + client.config.client_name = "CognitePygen" pygen = OmniClient(client) pygen.connection_item_a.select().name.equals("test").list_full() diff --git a/tests/utils.py b/tests/utils.py index 41f818cd3..fa3201a47 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -235,7 +235,6 @@ def get_client(self) -> CogniteClient: project=self.CDF_PROJECT, credentials=self.get_credentials(), base_url=self.cdf_url, - max_workers=self.CDF_MAX_WORKERS, timeout=self.CDF_TIMEOUT, ) return CogniteClient(config) diff --git a/uv.lock b/uv.lock index d5e0b8ccc..c26f2c774 100644 --- a/uv.lock +++ b/uv.lock @@ -110,18 +110,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797, upload-time = "2025-10-18T17:46:45.663Z" }, ] -[[package]] -name = "asgiref" -version = "3.11.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/40/f03da1264ae8f7cfdbf9146542e5e7e8100a4c66ab48e791df9a03d3f6c0/asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce", size = 38550, upload-time = "2026-02-03T13:30:14.33Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345, upload-time = "2026-02-03T13:30:13.039Z" }, -] - [[package]] name = "asttokens" version = "3.0.1" @@ -152,6 +140,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, ] +[[package]] +name = "authlib" +version = "1.6.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/98/00d3dd826d46959ad8e32af2dbb2398868fd9fd0683c26e56d0789bd0e68/authlib-1.6.9.tar.gz", hash = "sha256:d8f2421e7e5980cc1ddb4e32d3f5fa659cfaf60d8eaf3281ebed192e4ab74f04", size = 165134, upload-time = "2026-03-02T07:44:01.998Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/23/b65f568ed0c22f1efacb744d2db1a33c8068f384b8c9b482b52ebdbc3ef6/authlib-1.6.9-py2.py3-none-any.whl", hash = "sha256:f08b4c14e08f0861dc18a32357b33fbcfd2ea86cfe3fe149484b4d764c4a0ac3", size = 244197, upload-time = "2026-03-02T07:44:00.307Z" }, +] + [[package]] name = "babel" version = "2.18.0" @@ -500,7 +500,6 @@ format = [ [package.dev-dependencies] dev = [ - { name = "cognite-toolkit" }, { name = "faker" }, { name = "hypothesis" }, { name = "ipython", version = "8.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -537,7 +536,7 @@ dev = [ requires-dist = [ { name = "black", marker = "extra == 'format'", specifier = ">=24.4.2" }, { name = "build", marker = "extra == 'cli'", specifier = ">=1.1" }, - { name = "cognite-sdk", specifier = ">=7.86.0,<8.0.0" }, + { name = "cognite-sdk", specifier = ">=8.0.1,<9.0.0" }, { name = "inflect", specifier = ">=6.2" }, { name = "jinja2", specifier = ">=3.1" }, { name = "packaging", marker = "extra == 'cli'", specifier = ">=21.3" }, @@ -550,7 +549,6 @@ provides-extras = ["cli", "format"] [package.metadata.requires-dev] dev = [ - { name = "cognite-toolkit", specifier = "==0.5.87" }, { name = "faker", specifier = ">=18.0.0" }, { name = "hypothesis", specifier = ">=6.0.0" }, { name = "ipython", specifier = ">=8.0.0" }, @@ -582,46 +580,19 @@ dev = [ [[package]] name = "cognite-sdk" -version = "7.92.0" +version = "8.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "authlib" }, { name = "httpx" }, { name = "msal" }, { name = "packaging" }, { name = "protobuf" }, - { name = "requests" }, - { name = "requests-oauthlib" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1d/0e/ba2eeae2cb9cff5e153b093cb3b711fb7ccb280fd376eea5cdb3fb51a53b/cognite_sdk-7.92.0.tar.gz", hash = "sha256:36859bef8f4812be6d2bc4cba0224b7bb22d7e58b6bdc7929ca8f621dd0e8aa5", size = 539935, upload-time = "2026-03-02T12:38:53.23Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/2b/2b6476784c2da7fe86125da6eda4191fd523231e15e526c79bc578a8de6c/cognite_sdk-8.0.5.tar.gz", hash = "sha256:6232dd6c9e4896f40dc516cca958ee94333732c9a60796a20171dc7867115b5b", size = 693376, upload-time = "2026-03-27T11:34:08.747Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/b1/2e397a7b1062901ec157cd2fc30da98a7ea2342cd765e4ecc72a12d9f488/cognite_sdk-7.92.0-py3-none-any.whl", hash = "sha256:8cfb7e4903225b1b4ffe11e57ded17b418574cb5699676c4f4c8dc4574793eee", size = 687392, upload-time = "2026-03-02T12:38:51.399Z" }, -] - -[[package]] -name = "cognite-toolkit" -version = "0.5.87" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cognite-sdk" }, - { name = "mixpanel" }, - { name = "packaging" }, - { name = "pandas" }, - { name = "pip" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "pyyaml" }, - { name = "questionary" }, - { name = "rich" }, - { name = "sentry-sdk" }, - { name = "toml" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typer" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9b/0e/054b1cdaaf2a4e576b8ac920cd2f35c26a63b12a725be108113a40a3b542/cognite_toolkit-0.5.87.tar.gz", hash = "sha256:1eb1fb6e7f8206ae644eb969d082cdd214588f086671834ea421c8e18f1ef6e6", size = 931171, upload-time = "2025-07-24T05:02:18.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/38/2105da47828d62529ba3e489956cecf7eeb4a4d21712da4a90585ffebf67/cognite_toolkit-0.5.87-py3-none-any.whl", hash = "sha256:b3f572b85c18458da822e94db28b789a50bb99f52bb8c091c9d50b5ff400fdab", size = 787252, upload-time = "2025-07-24T05:02:17.228Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2f/e9eaff5693174b4277835c991ea23322ef3ce5e3cb8836cda4149dc752b3/cognite_sdk-8.0.5-py3-none-any.whl", hash = "sha256:d49cd5d3b2c90c8afcd46fa1c000debc4ee2a2e510920b4c38a4860fd73ed8d5", size = 921480, upload-time = "2026-03-27T11:34:07.067Z" }, ] [[package]] @@ -1056,7 +1027,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -1184,6 +1155,7 @@ wheels = [ name = "griffelib" version = "2.0.0" source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/06/eccbd311c9e2b3ca45dbc063b93134c57a1ccc7607c5e545264ad092c4a9/griffelib-2.0.0.tar.gz", hash = "sha256:e504d637a089f5cab9b5daf18f7645970509bf4f53eda8d79ed71cce8bd97934", size = 166312, upload-time = "2026-03-23T21:06:55.954Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl", hash = "sha256:01284878c966508b6d6f1dbff9b6fa607bc062d8261c5c7253cb285b06422a7f", size = 142004, upload-time = "2026-02-09T19:09:40.561Z" }, ] @@ -1282,7 +1254,7 @@ name = "importlib-metadata" version = "8.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp" }, + { name = "zipp", marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } wheels = [ @@ -2215,21 +2187,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl", hash = "sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1", size = 53598, upload-time = "2025-12-23T11:36:33.211Z" }, ] -[[package]] -name = "mixpanel" -version = "5.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asgiref" }, - { name = "httpx" }, - { name = "pydantic" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2c/1e/d2a733cb35b380942e0f1791427d23afa1bc1c37b8ce7089b2c691176d0f/mixpanel-5.0.0.tar.gz", hash = "sha256:ef78b7b36150fbf24796972bf44871515c6d1381a69be683eb26a65d44b84bb5", size = 21083, upload-time = "2025-11-04T22:10:23.502Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/05/248580e987b203c24e4cce53f5e5d60ea749550af5c756c770196d549f96/mixpanel-5.0.0-py3-none-any.whl", hash = "sha256:5c5fee75a304bd2940828a08a31f74fae7f02d5fe81914a851ed87e8884096ec", size = 23848, upload-time = "2025-11-04T22:10:22.233Z" }, -] - [[package]] name = "mkdocs" version = "1.6.1" @@ -2720,15 +2677,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/57/a7/b35835e278c18b85206834b3aa3abe68e77a98769c59233d1f6300284781/numpy-2.4.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:4b42639cdde6d24e732ff823a3fa5b701d8acad89c4142bc1d0bd6dc85200ba5", size = 12504685, upload-time = "2026-03-09T07:58:50.525Z" }, ] -[[package]] -name = "oauthlib" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, -] - [[package]] name = "overrides" version = "7.7.0" @@ -2988,15 +2936,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f2/26/c56ce33ca856e358d27fda9676c055395abddb82c35ac0f593877ed4562e/pillow-12.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:cb9bb857b2d057c6dfc72ac5f3b44836924ba15721882ef103cecb40d002d80e", size = 7029880, upload-time = "2026-02-11T04:23:04.783Z" }, ] -[[package]] -name = "pip" -version = "26.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/48/83/0d7d4e9efe3344b8e2fe25d93be44f64b65364d3c8d7bc6dc90198d5422e/pip-26.0.1.tar.gz", hash = "sha256:c4037d8a277c89b320abe636d59f91e6d0922d08a05b60e85e53b296613346d8", size = 1812747, upload-time = "2026-02-05T02:20:18.702Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl", hash = "sha256:bdb1b08f4274833d62c1aa29e20907365a2ceb950410df15fc9521bad440122b", size = 1787723, upload-time = "2026-02-05T02:20:16.416Z" }, -] - [[package]] name = "platformdirs" version = "4.9.4" @@ -3660,18 +3599,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/01/1b/5dbe84eefc86f48473947e2f41711aded97eecef1231f4558f1f02713c12/pyzmq-27.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355", size = 544862, upload-time = "2025-09-08T23:09:56.509Z" }, ] -[[package]] -name = "questionary" -version = "2.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "prompt-toolkit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f6/45/eafb0bba0f9988f6a2520f9ca2df2c82ddfa8d67c95d6625452e97b204a5/questionary-2.1.1.tar.gz", hash = "sha256:3d7e980292bb0107abaa79c68dd3eee3c561b83a0f89ae482860b181c8bd412d", size = 25845, upload-time = "2025-08-28T19:00:20.851Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl", hash = "sha256:a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59", size = 36753, upload-time = "2025-08-28T19:00:19.56Z" }, -] - [[package]] name = "readme-renderer" version = "44.0" @@ -3715,19 +3642,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] -[[package]] -name = "requests-oauthlib" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "oauthlib" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, -] - [[package]] name = "requests-toolbelt" version = "1.0.0" @@ -4001,19 +3915,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl", hash = "sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c", size = 17610, upload-time = "2026-01-14T06:27:35.218Z" }, ] -[[package]] -name = "sentry-sdk" -version = "2.54.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c8/e9/2e3a46c304e7fa21eaa70612f60354e32699c7102eb961f67448e222ad7c/sentry_sdk-2.54.0.tar.gz", hash = "sha256:2620c2575128d009b11b20f7feb81e4e4e8ae08ec1d36cbc845705060b45cc1b", size = 413813, upload-time = "2026-03-02T15:12:41.355Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/39/be412cc86bc6247b8f69e9383d7950711bd86f8d0a4a4b0fe8fad685bc21/sentry_sdk-2.54.0-py2.py3-none-any.whl", hash = "sha256:fd74e0e281dcda63afff095d23ebcd6e97006102cdc8e78a29f19ecdf796a0de", size = 439198, upload-time = "2026-03-02T15:12:39.546Z" }, -] - [[package]] name = "setuptools" version = "82.0.1"