Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PYG-229] 🦖Generation Bug #311

Merged
merged 9 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cdf.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ version = "0.3.0b3"
[plugins]
run = false
pull = false
dump = false
dump = false

[feature_flags]
graphql = true
11 changes: 9 additions & 2 deletions cognite/pygen/_core/models/data_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,11 +291,11 @@ def pydantic_field(self) -> Literal["Field", "pydantic.Field"]:
def init_import(self) -> str:
"""The data class __init__ imports of this data class"""
import_classes = [self.read_name, self.graphql_name]
if self.is_writable:
if self.is_writable or self.is_interface:
import_classes.append(self.write_name)
import_classes.append(f"{self.read_name}Apply")
import_classes.append(self.read_list_name)
if self.is_writable:
if self.is_writable or self.is_interface:
import_classes.append(self.write_list_name)
import_classes.append(f"{self.read_name}ApplyList")
import_classes.append(self.field_names)
Expand Down Expand Up @@ -576,6 +576,13 @@ def is_edge_class(self) -> bool:
"""Check if the data class is an edge class."""
return False

@property
def has_writable_connection_fields(self) -> bool:
return any(
isinstance(field_, BaseConnectionField) and field_.destination_class and field_.is_write_field
for field_ in self
)

@property
def connections_docs_write(self) -> str:
"""Return a string with all connections that are write fields."""
Expand Down
2 changes: 1 addition & 1 deletion cognite/pygen/_core/templates/api_class_node.py.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class {{ api_class.name }}({% if data_class.is_writable %}NodeAPI{% else %}NodeR
replace: bool = False,
write_none: bool = False,
) -> ResourcesWriteResult:
"""Add or update (upsert) {{data_class.doc_list_name}}.{% if data_class.has_field_of_type(ft.OneToManyConnectionField) %}
"""Add or update (upsert) {{data_class.doc_list_name}}.{% if data_class.has_writable_connection_fields %}

Note: This method iterates through all nodes and timeseries linked to {{ data_class.variable }} and creates them including the edges
between the nodes. For example, if any of {{ data_class.connections_docs_write }} are set, then these
Expand Down
8 changes: 5 additions & 3 deletions cognite/pygen/_core/templates/data_class_edge.py.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ class {{ data_class.read_name }}(DomainRelation{% if is_pydantic_v2 and data_cla
space: str{% if has_default_instance_space %} = DEFAULT_INSTANCE_SPACE{% endif %}{% for field in data_class.read_fields %}
{{ field.name }}: {{ field.as_read_type_hint() }}{% endfor %}


{% if data_class.is_writable or data_class.is_interface %}
def as_write(self) -> {{ data_class.write_name }}:
"""Convert this read version of {{ data_class.doc_name }} to the writing version."""
return {{ data_class.write_name }}(
Expand Down Expand Up @@ -207,12 +209,12 @@ class {{ data_class.read_name }}Apply({{ data_class.write_name }}):
)
return super().__new__(cls)


{% endif %}
class {{ data_class.read_list_name }}(DomainRelationList[{{ data_class.read_name }}]):
"""List of {{ data_class.doc_list_name }} in the reading version."""

_INSTANCE = {{ data_class.read_name }}

{% if data_class.is_writable or data_class.is_interface %}
def as_write(self) -> {{ data_class.write_list_name }}:
"""Convert this read version of {{ data_class.doc_name }} list to the writing version."""
return {{ data_class.write_list_name }}([edge.as_write() for edge in self])
Expand All @@ -235,7 +237,7 @@ class {{ data_class.write_list_name }}(DomainRelationWriteList[{{ data_class.wri

class {{ data_class.read_name }}ApplyList({{ data_class.write_list_name }}): ...


{% endif %}
def {{ data_class.filter_name }}(
edge_type: dm.DirectRelationReference,
view_id: dm.ViewId,{% if has_default_instance_space %}
Expand Down
2 changes: 1 addition & 1 deletion cognite/pygen/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.99.34"
__version__ = "0.99.35"
8 changes: 7 additions & 1 deletion docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@ Changes are grouped as follows
- `Fixed` for any bug fixes.
- `Security` in case of vulnerabilities.

## TBD
## [0.99.35] - 24-09-30
### Added
- Support for `Enum` in `MockGenerator`.

### Fixed
- Generating an SDK from a Data Model with a view with only primitive properties and reverse direct relations no
longer raises a `ValueError`.
- Generating an SDK from a Data Model with a view for edges that is not writable but used in an implements, no longer
raises an `ImportError`.

### Removed
- Support for Python `3.9` is dropped.

Expand Down
4 changes: 2 additions & 2 deletions docs/quickstart/cdf_streamlit.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ adding `cognite-pygen` to the installed packages under `settings`.

```text
pyodide-http==0.2.1
cognite-sdk==7.60.0
cognite-sdk==7.62.4
pydantic==1.10.7
cognite-pygen==0.99.34
cognite-pygen==0.99.35
```

Note that we also set `pydantic` to a specific version. This is because `pygen` supports both `pydantic` `v1` and `v2`, but
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class EquipmentUnitClient:
EquipmentUnitClient

Generated with:
pygen = 0.99.34
pygen = 0.99.35
cognite-sdk = 7.62.4
pydantic = 1.10.7

Expand All @@ -41,7 +41,7 @@ def __init__(self, config_or_client: CogniteClient | ClientConfig):
else:
raise ValueError(f"Expected CogniteClient or ClientConfig, got {type(config_or_client)}")
# The client name is used for aggregated logging of Pygen Usage
client.config.client_name = "CognitePygen:0.99.34"
client.config.client_name = "CognitePygen:0.99.35"

self._client = client

Expand Down
4 changes: 2 additions & 2 deletions examples-pydantic-v1/omni_multi_pydantic_v1/_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class OmniMultiClient:
OmniMultiClient

Generated with:
pygen = 0.99.34
pygen = 0.99.35
cognite-sdk = 7.62.4
pydantic = 1.10.7

Expand All @@ -125,7 +125,7 @@ def __init__(self, config_or_client: CogniteClient | ClientConfig):
else:
raise ValueError(f"Expected CogniteClient or ClientConfig, got {type(config_or_client)}")
# The client name is used for aggregated logging of Pygen Usage
client.config.client_name = "CognitePygen:0.99.34"
client.config.client_name = "CognitePygen:0.99.35"

self.omni_multi_a = OmniMultiAAPIs(client)
self.omni_multi_b = OmniMultiBAPIs(client)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ def apply(
) -> ResourcesWriteResult:
"""Add or update (upsert) connection item ds.

Note: This method iterates through all nodes and timeseries linked to connection_item_d and creates them including the edges
between the nodes. For example, if any of `direct_multi`, `direct_single` or `outwards_single` are set, then these
nodes as well as any nodes linked to them, and all the edges linking these nodes will be created.

Args:
connection_item_d: Connection item d or sequence of connection item ds to upsert.
replace (bool): How do we behave when a property value exists? Do we replace all matching and existing values with the supplied values (true)?
Expand Down
4 changes: 2 additions & 2 deletions examples-pydantic-v1/omni_pydantic_v1/_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class OmniClient:
OmniClient

Generated with:
pygen = 0.99.34
pygen = 0.99.35
cognite-sdk = 7.62.4
pydantic = 1.10.7

Expand All @@ -59,7 +59,7 @@ def __init__(self, config_or_client: CogniteClient | ClientConfig):
else:
raise ValueError(f"Expected CogniteClient or ClientConfig, got {type(config_or_client)}")
# The client name is used for aggregated logging of Pygen Usage
client.config.client_name = "CognitePygen:0.99.34"
client.config.client_name = "CognitePygen:0.99.35"

self._client = client

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,148 +112,12 @@ class ConnectionItemCEdge(DomainRelation):
default=None, repr=False, alias="connectionItemB"
)

def as_write(self) -> ConnectionItemCEdgeWrite:
"""Convert this read version of connection item c edge to the writing version."""
return ConnectionItemCEdgeWrite(
space=self.space,
external_id=self.external_id,
data_record=DataRecordWrite(existing_version=self.data_record.version),
end_node=self.end_node,
connection_item_a=[
connection_item_a.as_write() if isinstance(connection_item_a, DomainModel) else connection_item_a
for connection_item_a in self.connection_item_a or []
],
connection_item_b=[
connection_item_b.as_write() if isinstance(connection_item_b, DomainModel) else connection_item_b
for connection_item_b in self.connection_item_b or []
],
)

def as_apply(self) -> ConnectionItemCEdgeWrite:
"""Convert this read version of connection item c edge to the writing version."""
warnings.warn(
"as_apply is deprecated and will be removed in v1.0. Use as_write instead.",
UserWarning,
stacklevel=2,
)
return self.as_write()


class ConnectionItemCEdgeWrite(DomainRelationWrite):
"""This represents the writing version of connection item c edge.

It is used to when data is sent to CDF.

Args:
space: The space where the node is located.
external_id: The external id of the connection item c edge.
data_record: The data record of the connection item c edge edge.
end_node: The end node of this edge.
connection_item_a: The connection item a field.
connection_item_b: The connection item b field.
"""

_view_id: ClassVar[dm.ViewId] = dm.ViewId("pygen-models", "ConnectionItemC", "1")
space: str = DEFAULT_INSTANCE_SPACE
end_node: Union[str, dm.NodeId]
connection_item_a: Optional[list[Union[ConnectionItemAWrite, str, dm.NodeId]]] = Field(
default=None, repr=False, alias="connectionItemA"
)
connection_item_b: Optional[list[Union[ConnectionItemBWrite, str, dm.NodeId]]] = Field(
default=None, repr=False, alias="connectionItemB"
)

def _to_instances_write(
self,
cache: set[tuple[str, str]],
start_node: DomainModelWrite,
edge_type: dm.DirectRelationReference,
write_none: bool = False,
allow_version_increase: bool = False,
) -> ResourcesWrite:
resources = ResourcesWrite()
if self.external_id and (self.space, self.external_id) in cache:
return resources

_validate_end_node(start_node, self.end_node)

if isinstance(self.end_node, DomainModelWrite):
end_node = self.end_node.as_direct_reference()
elif isinstance(self.end_node, str):
end_node = dm.DirectRelationReference(self.space, self.end_node)
elif isinstance(self.end_node, dm.NodeId):
end_node = dm.DirectRelationReference(self.end_node.space, self.end_node.external_id)
else:
raise ValueError(f"Invalid type for equipment_module: {type(self.end_node)}")

external_id = self.external_id or DomainRelationWrite.external_id_factory(start_node, self.end_node, edge_type)

properties: dict[str, Any] = {}

if properties:
this_edge = dm.EdgeApply(
space=self.space,
external_id=external_id,
type=edge_type,
start_node=start_node.as_direct_reference(),
end_node=end_node,
existing_version=None if allow_version_increase else self.data_record.existing_version,
sources=[
dm.NodeOrEdgeData(
source=self._view_id,
properties=properties,
)
],
)
resources.edges.append(this_edge)
cache.add((self.space, external_id))

if isinstance(self.end_node, DomainModelWrite):
other_resources = self.end_node._to_instances_write(cache)
resources.extend(other_resources)

return resources


class ConnectionItemCEdgeApply(ConnectionItemCEdgeWrite):
def __new__(cls, *args, **kwargs) -> ConnectionItemCEdgeApply:
warnings.warn(
"ConnectionItemCEdgeApply is deprecated and will be removed in v1.0. Use ConnectionItemCEdgeWrite instead."
"The motivation for this change is that Write is a more descriptive name for the writing version of the"
"ConnectionItemCEdge.",
UserWarning,
stacklevel=2,
)
return super().__new__(cls)


class ConnectionItemCEdgeList(DomainRelationList[ConnectionItemCEdge]):
"""List of connection item c edges in the reading version."""

_INSTANCE = ConnectionItemCEdge

def as_write(self) -> ConnectionItemCEdgeWriteList:
"""Convert this read version of connection item c edge list to the writing version."""
return ConnectionItemCEdgeWriteList([edge.as_write() for edge in self])

def as_apply(self) -> ConnectionItemCEdgeWriteList:
"""Convert these read versions of connection item c edge list to the writing versions."""
warnings.warn(
"as_apply is deprecated and will be removed in v1.0. Use as_write instead.",
UserWarning,
stacklevel=2,
)
return self.as_write()


class ConnectionItemCEdgeWriteList(DomainRelationWriteList[ConnectionItemCEdgeWrite]):
"""List of connection item c edges in the writing version."""

_INSTANCE = ConnectionItemCEdgeWrite


class ConnectionItemCEdgeApplyList(ConnectionItemCEdgeWriteList): ...


def _create_connection_item_c_edge_filter(
edge_type: dm.DirectRelationReference,
Expand Down
4 changes: 2 additions & 2 deletions examples-pydantic-v1/omni_sub_pydantic_v1/_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class OmniSubClient:
OmniSubClient

Generated with:
pygen = 0.99.34
pygen = 0.99.35
cognite-sdk = 7.62.4
pydantic = 1.10.7

Expand All @@ -41,7 +41,7 @@ def __init__(self, config_or_client: CogniteClient | ClientConfig):
else:
raise ValueError(f"Expected CogniteClient or ClientConfig, got {type(config_or_client)}")
# The client name is used for aggregated logging of Pygen Usage
client.config.client_name = "CognitePygen:0.99.34"
client.config.client_name = "CognitePygen:0.99.35"

self._client = client

Expand Down
Loading