Skip to content

Commit

Permalink
remove side effect in Instance._load, add test for side effects on …
Browse files Browse the repository at this point in the history
…load (#1563)

Co-authored-by: erlendvollset <[email protected]>
  • Loading branch information
haakonvt and erlendvollset authored Dec 22, 2023
1 parent e442375 commit 7fb8fb6
Show file tree
Hide file tree
Showing 16 changed files with 277 additions and 227 deletions.
4 changes: 2 additions & 2 deletions cognite/client/_api/data_modeling/instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ def __call__(
instance_type: Literal["node", "edge"] = "node",
limit: int | None = None,
include_typing: bool = False,
sources: list[ViewId] | ViewId | None = None,
sources: list[ViewId] | ViewId | list[View] | View | None = None,
sort: list[InstanceSort | dict] | InstanceSort | dict | None = None,
filter: Filter | dict | None = None,
) -> Iterator[Edge] | Iterator[EdgeList] | Iterator[Node] | Iterator[NodeList]:
Expand All @@ -226,7 +226,7 @@ def __call__(
instance_type (Literal["node", "edge"]): Whether to query for nodes or edges.
limit (int | None): Maximum number of instances to return. Defaults to returning all items.
include_typing (bool): Whether to return property type information as part of the result.
sources (list[ViewId] | ViewId | None): Views to retrieve properties from.
sources (list[ViewId] | ViewId | list[View] | View | None): Views to retrieve properties from.
sort (list[InstanceSort | dict] | InstanceSort | dict | None): How you want the listed instances information ordered.
filter (Filter | dict | None): Advanced filtering of instances.
Expand Down
2 changes: 1 addition & 1 deletion cognite/client/data_classes/capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ class Scope:

@classmethod
def _load(cls, resource: dict, cognite_client: CogniteClient | None = None) -> Self:
project_scope_dct = {ProjectScope.name: resource.pop(ProjectScope.name)}
project_scope_dct = {ProjectScope.name: resource.get(ProjectScope.name)}
return cls(
capability=Capability.load(resource),
project_scope=ProjectScope.load(project_scope_dct),
Expand Down
2 changes: 0 additions & 2 deletions cognite/client/data_classes/data_modeling/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
EdgeList,
EdgeListWithCursor,
InstanceApply,
InstancesApply,
InstancesApplyResult,
InstancesDeleteResult,
InstanceSort,
Expand Down Expand Up @@ -175,6 +174,5 @@
"InstancesDeleteResult",
"InstancesResult",
"InstanceApply",
"InstancesApply",
"query",
]
74 changes: 38 additions & 36 deletions cognite/client/data_classes/data_modeling/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
CogniteResourceList,
)
from cognite.client.data_classes.data_modeling._validation import validate_data_modeling_identifier
from cognite.client.data_classes.data_modeling.core import DataModelingResource
from cognite.client.data_classes.data_modeling.core import DataModelingSchemaResource
from cognite.client.data_classes.data_modeling.data_types import (
DirectRelation,
PropertyType,
Expand All @@ -24,7 +24,7 @@
from cognite.client import CogniteClient


class ContainerCore(DataModelingResource):
class ContainerCore(DataModelingSchemaResource, ABC):
"""Represent the physical storage of data. This is the base class for the read and write version.
Args:
Expand All @@ -33,42 +33,24 @@ class ContainerCore(DataModelingResource):
properties (dict[str, ContainerProperty]): We index the property by a local unique identifier.
description (str | None): Textual description of the view
name (str | None): Human readable name for the view.
used_for (Literal["node", "edge", "all"] | None): Should this operation apply to nodes, edges or both.
constraints (dict[str, Constraint] | None): Set of constraints to apply to the container
indexes (dict[str, Index] | None): Set of indexes to apply to the container.
**_ (Any): No description.
"""

def __init__(
self,
space: str,
external_id: str,
properties: dict[str, ContainerProperty],
description: str | None = None,
name: str | None = None,
used_for: Literal["node", "edge", "all"] | None = None,
constraints: dict[str, Constraint] | None = None,
indexes: dict[str, Index] | None = None,
**_: Any,
description: str | None,
name: str | None,
constraints: dict[str, Constraint] | None,
indexes: dict[str, Index] | None,
) -> None:
self.space = space
self.external_id = external_id
self.description = description
self.name = name
self.used_for = used_for
super().__init__(space=space, external_id=external_id, description=description, name=name)
self.properties = properties
self.constraints = constraints
self.indexes = indexes

@classmethod
def _load(cls, resource: dict, cognite_client: CogniteClient | None = None) -> Self:
if "constraints" in resource:
resource["constraints"] = {k: Constraint.load(v) for k, v in resource["constraints"].items()} or None
if "indexes" in resource:
resource["indexes"] = {k: Index.load(v) for k, v in resource["indexes"].items()} or None
if "properties" in resource:
resource["properties"] = {k: ContainerProperty.load(v) for k, v in resource["properties"].items()} or None
return super()._load(resource, cognite_client)
self.constraints: dict[str, Constraint] = constraints or {}
self.indexes: dict[str, Index] = indexes or {}

def dump(self, camel_case: bool = True) -> dict[str, Any]:
output = super().dump(camel_case)
Expand Down Expand Up @@ -111,7 +93,8 @@ def __init__(
indexes: dict[str, Index] | None = None,
) -> None:
validate_data_modeling_identifier(space, external_id)
super().__init__(space, external_id, properties, description, name, used_for, constraints, indexes)
super().__init__(space, external_id, properties, description, name, constraints, indexes)
self.used_for = used_for

@classmethod
def _load(cls, resource: dict, cognite_client: CogniteClient | None = None) -> ContainerApply:
Expand Down Expand Up @@ -146,7 +129,6 @@ class Container(ContainerCore):
used_for (Literal["node", "edge", "all"]): Should this operation apply to nodes, edges or both.
constraints (dict[str, Constraint] | None): Set of constraints to apply to the container
indexes (dict[str, Index] | None): Set of indexes to apply to the container.
**_ (Any): No description.
"""

def __init__(
Expand All @@ -157,18 +139,38 @@ def __init__(
is_global: bool,
last_updated_time: int,
created_time: int,
description: str | None = None,
name: str | None = None,
used_for: Literal["node", "edge", "all"] = "node",
constraints: dict[str, Constraint] | None = None,
indexes: dict[str, Index] | None = None,
**_: Any,
description: str | None,
name: str | None,
used_for: Literal["node", "edge", "all"],
constraints: dict[str, Constraint] | None,
indexes: dict[str, Index] | None,
) -> None:
super().__init__(space, external_id, properties, description, name, used_for, constraints, indexes)
super().__init__(space, external_id, properties, description, name, constraints, indexes)
self.used_for = used_for
self.is_global = is_global
self.last_updated_time = last_updated_time
self.created_time = created_time

@classmethod
def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = None) -> Self:
constraints = (
{k: Constraint.load(v) for k, v in resource["constraints"].items()} if "constraints" in resource else None
)
indexes = {k: Index.load(v) for k, v in resource["indexes"].items()} if "indexes" in resource else None
return cls(
space=resource["space"],
external_id=resource["externalId"],
properties={k: ContainerProperty.load(v) for k, v in resource["properties"].items()},
is_global=resource["isGlobal"],
last_updated_time=resource["lastUpdatedTime"],
created_time=resource["createdTime"],
description=resource.get("description"),
name=resource.get("name"),
used_for=resource["usedFor"],
constraints=constraints,
indexes=indexes,
)

def as_apply(self) -> ContainerApply:
return ContainerApply(
space=self.space,
Expand Down
20 changes: 16 additions & 4 deletions cognite/client/data_classes/data_modeling/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
)
from cognite.client.utils._auxiliary import json_dump_default
from cognite.client.utils._importing import local_import
from cognite.client.utils._text import convert_all_keys_to_snake_case

if TYPE_CHECKING:
import pandas as pd
Expand All @@ -25,6 +24,9 @@


class DataModelingResource(CogniteResource, ABC):
def __init__(self, space: str):
self.space = space

def __repr__(self) -> str:
args = []
if hasattr(self, "space"):
Expand All @@ -39,9 +41,19 @@ def __repr__(self) -> str:

return f"<{type(self).__qualname__}({', '.join(args)}) at {id(self):#x}>"

@classmethod
def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = None) -> Self:
return cls(**convert_all_keys_to_snake_case(resource))

class DataModelingSchemaResource(DataModelingResource, ABC):
def __init__(
self,
space: str,
external_id: str,
name: str | None,
description: str | None,
) -> None:
super().__init__(space=space)
self.external_id = external_id
self.name = name
self.description = description


class DataModelingInstancesList(CogniteResourceList, Generic[T_CogniteResource]):
Expand Down
47 changes: 24 additions & 23 deletions cognite/client/data_classes/data_modeling/data_models.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
from __future__ import annotations

from abc import ABC
from operator import attrgetter
from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar, Union
from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar, Union, cast

from typing_extensions import Self

from cognite.client.data_classes._base import CogniteFilter, CogniteResourceList
from cognite.client.data_classes.data_modeling._validation import validate_data_modeling_identifier
from cognite.client.data_classes.data_modeling.core import DataModelingResource, DataModelingSort
from cognite.client.data_classes.data_modeling.core import DataModelingSchemaResource, DataModelingSort
from cognite.client.data_classes.data_modeling.ids import DataModelId, ViewId
from cognite.client.data_classes.data_modeling.views import View, ViewApply

if TYPE_CHECKING:
from cognite.client import CogniteClient


class DataModelCore(DataModelingResource):
class DataModelCore(DataModelingSchemaResource, ABC):
"""A group of views.
Args:
Expand All @@ -24,22 +25,17 @@ class DataModelCore(DataModelingResource):
version (str): DMS version.
description (str | None): Textual description of the data model
name (str | None): Human readable name for the data model.
**_ (Any): No description.
"""

def __init__(
self,
space: str,
external_id: str,
version: str,
description: str | None = None,
name: str | None = None,
**_: Any,
description: str | None,
name: str | None,
) -> None:
self.space = space
self.external_id = external_id
self.description = description
self.name = name
super().__init__(space, external_id, name, description)
self.version = version

def as_id(self) -> DataModelId:
Expand Down Expand Up @@ -114,7 +110,6 @@ class DataModel(DataModelCore, Generic[T_View]):
description (str | None): Textual description of the data model
name (str | None): Human readable name for the data model.
views (list[T_View] | None): List of views included in this data model.
**_ (Any): No description.
"""

def __init__(
Expand All @@ -125,10 +120,9 @@ def __init__(
is_global: bool,
last_updated_time: int,
created_time: int,
description: str | None = None,
name: str | None = None,
views: list[T_View] | None = None,
**_: Any,
description: str | None,
name: str | None,
views: list[T_View] | None,
) -> None:
super().__init__(space, external_id, version, description, name)
self.views: list[T_View] = views or []
Expand All @@ -137,18 +131,25 @@ def __init__(
self.created_time = created_time

@classmethod
def _load_view(cls, view_data: dict) -> ViewId | View:
def _load_view(cls, view_data: dict) -> T_View:
if "type" in view_data:
return ViewId.load(view_data)
return cast(T_View, ViewId.load(view_data))
else:
return View._load(view_data)
return cast(T_View, View._load(view_data))

@classmethod
def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = None) -> Self:
if "views" in resource:
resource["views"] = [cls._load_view(v) for v in resource["views"]] or None

return super()._load(resource)
return cls(
space=resource["space"],
external_id=resource["externalId"],
version=resource["version"],
name=resource.get("name"),
description=resource.get("description"),
is_global=resource["isGlobal"],
last_updated_time=resource["lastUpdatedTime"],
created_time=resource["createdTime"],
views=[cls._load_view(v) for v in resource.get("views", [])],
)

def dump(self, camel_case: bool = True) -> dict[str, Any]:
output = super().dump(camel_case)
Expand Down
Loading

0 comments on commit 7fb8fb6

Please sign in to comment.