Skip to content

Commit

Permalink
Improve autocompletion for grpc properties (#351)
Browse files Browse the repository at this point in the history
Improves the autocompletion for properties defined via either
'`rpc_data_property` or `grpc_data_property_read_only` by defining
protocols for read-only and read-write properties.

Add type annotations to the properties where they cannot be inferred
from the `from_protobuf` or `to_protobuf` methods.

Note: Some properties may still have type annotations which are
too broad, if the `from_protobuf` or `to_protobuf` type annotations
are too broad.
  • Loading branch information
greschd authored Jan 24, 2024
1 parent 0e6e5e2 commit 22ec26b
Show file tree
Hide file tree
Showing 31 changed files with 289 additions and 135 deletions.
44 changes: 30 additions & 14 deletions src/ansys/acp/core/_tree_objects/_grpc_helpers/property_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,24 @@
from functools import reduce
from typing import Any, Callable, TypeVar

from google.protobuf.message import Message

from ansys.api.acp.v0.base_pb2 import ResourcePath

from ..._utils.property_protocols import ReadOnlyProperty, ReadWriteProperty
from .polymorphic_from_pb import CreatableFromResourcePath, tree_object_from_resource_path
from .protocols import Editable, GrpcObjectBase, ObjectInfo, Readable

_TO_PROTOBUF_T = Callable[[Any], Any]
_FROM_PROTOBUF_T = Callable[[Any], Any]
# Note: The typing of the protobuf objects is fairly loose, maybe it could
# be improved. The main challenge is that we do not encode the structure of
# messages in the type system, and they can contain different fundamental
# or protobuf types.
_PROTOBUF_T = Any
_GET_T = TypeVar("_GET_T")
_SET_T = TypeVar("_SET_T")

_TO_PROTOBUF_T = Callable[[_SET_T], _PROTOBUF_T]
_FROM_PROTOBUF_T = Callable[[_PROTOBUF_T], _GET_T]


class _exposed_grpc_property(property):
Expand Down Expand Up @@ -64,8 +75,8 @@ def inner(self: Readable) -> CreatableFromResourcePath | None:


def grpc_data_getter(
name: str, from_protobuf: _FROM_PROTOBUF_T, check_optional: bool = False
) -> Callable[[Readable], Any]:
name: str, from_protobuf: _FROM_PROTOBUF_T[_GET_T], check_optional: bool = False
) -> Callable[[Readable], _GET_T]:
"""
Creates a getter method which obtains the server object via the gRPC
Get endpoint.
Expand All @@ -91,13 +102,15 @@ def inner(self: Readable) -> Any:
return inner


def grpc_data_setter(name: str, to_protobuf: _TO_PROTOBUF_T) -> Callable[[Editable, Any], None]:
def grpc_data_setter(
name: str, to_protobuf: _TO_PROTOBUF_T[_SET_T]
) -> Callable[[Editable, _SET_T], None]:
"""
Creates a setter method which updates the server object via the gRPC
Put endpoint.
"""

def inner(self: Editable, value: Any) -> None:
def inner(self: Editable, value: _SET_T) -> None:
self._get_if_stored()
current_value = _get_data_attribute(self._pb_object, name)
value_pb = to_protobuf(value)
Expand All @@ -112,7 +125,7 @@ def inner(self: Editable, value: Any) -> None:
return inner


def _get_data_attribute(pb_obj: ObjectInfo, name: str, check_optional: bool = False) -> Any:
def _get_data_attribute(pb_obj: Message, name: str, check_optional: bool = False) -> _PROTOBUF_T:
name_parts = name.split(".")
if check_optional:
parent_obj = reduce(getattr, name_parts[:-1], pb_obj)
Expand All @@ -121,7 +134,7 @@ def _get_data_attribute(pb_obj: ObjectInfo, name: str, check_optional: bool = Fa
return reduce(getattr, name_parts, pb_obj)


def _set_data_attribute(pb_obj: ObjectInfo, name: str, value: Any) -> None:
def _set_data_attribute(pb_obj: ObjectInfo, name: str, value: _PROTOBUF_T) -> None:
name_parts = name.split(".")

try:
Expand All @@ -140,19 +153,22 @@ def _set_data_attribute(pb_obj: ObjectInfo, name: str, value: Any) -> None:
target_object.add().CopyFrom(item)


def _wrap_doc(obj: Any, doc: str | None) -> Any:
AnyT = TypeVar("AnyT")


def _wrap_doc(obj: AnyT, doc: str | None) -> AnyT:
if doc is not None:
obj.__doc__ = doc
return obj


def grpc_data_property(
name: str,
to_protobuf: _TO_PROTOBUF_T = lambda x: x,
from_protobuf: _FROM_PROTOBUF_T = lambda x: x,
to_protobuf: _TO_PROTOBUF_T[_SET_T] = lambda x: x,
from_protobuf: _FROM_PROTOBUF_T[_GET_T] = lambda x: x,
check_optional: bool = False,
doc: str | None = None,
) -> Any:
) -> ReadWriteProperty[_GET_T, _SET_T]:
"""
Helper for defining properties accessed via gRPC. The property getter
and setter make calls to the gRPC Get and Put endpoints to synchronize
Expand Down Expand Up @@ -191,10 +207,10 @@ def grpc_data_property(

def grpc_data_property_read_only(
name: str,
from_protobuf: _FROM_PROTOBUF_T = lambda x: x,
from_protobuf: _FROM_PROTOBUF_T[_GET_T] = lambda x: x,
check_optional: bool = False,
doc: str | None = None,
) -> Any:
) -> ReadOnlyProperty[_GET_T]:
"""
Helper for defining properties accessed via gRPC. The property getter
makes call to the gRPC Get endpoints to synchronize
Expand Down
7 changes: 5 additions & 2 deletions src/ansys/acp/core/_tree_objects/analysis_ply.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from ansys.api.acp.v0 import analysis_ply_pb2, analysis_ply_pb2_grpc

from .._utils.property_protocols import ReadOnlyProperty
from ._grpc_helpers.property_helper import (
grpc_data_property_read_only,
grpc_link_property_read_only,
Expand Down Expand Up @@ -82,7 +83,9 @@ def _create_stub(self) -> analysis_ply_pb2_grpc.ObjectServiceStub:

status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb)
material = grpc_link_property_read_only("properties.material")
angle = grpc_data_property_read_only("properties.angle")
active_in_post_mode = grpc_data_property_read_only("properties.active_in_post_mode")
angle: ReadOnlyProperty[float] = grpc_data_property_read_only("properties.angle")
active_in_post_mode: ReadOnlyProperty[bool] = grpc_data_property_read_only(
"properties.active_in_post_mode"
)
elemental_data = elemental_data_property(AnalysisPlyElementalData)
nodal_data = nodal_data_property(AnalysisPlyNodalData)
6 changes: 4 additions & 2 deletions src/ansys/acp/core/_tree_objects/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from ansys.api.acp.v0.base_pb2 import CollectionPath, DeleteRequest, GetRequest, ResourcePath

from .._utils.property_protocols import ReadOnlyProperty, ReadWriteProperty
from .._utils.resource_paths import common_path
from .._utils.resource_paths import join as _rp_join
from .._utils.resource_paths import to_parts
Expand Down Expand Up @@ -50,6 +51,7 @@ class TreeObjectBase(GrpcObjectBase):
OBJECT_INFO_TYPE: type[ObjectInfo]

_pb_object: ObjectInfo
name: ReadWriteProperty[str, str]

def __init__(self: TreeObjectBase, name: str = "") -> None:
self._channel_store: Channel | None = None
Expand Down Expand Up @@ -134,7 +136,7 @@ class NamedTreeObject(GrpcObjectBase):

"""Implements the 'name' attribute for tree objects."""

name = grpc_data_property("info.name")
name: ReadWriteProperty[str, str] = grpc_data_property("info.name")
"""The name of the object."""

def __repr__(self) -> str:
Expand Down Expand Up @@ -255,7 +257,7 @@ class IdTreeObject(TreeObjectBase):

__slots__: Iterable[str] = tuple()

id = grpc_data_property_read_only("info.id")
id: ReadOnlyProperty[str] = grpc_data_property_read_only("info.id")

def __repr__(self) -> str:
return f"<{type(self).__name__} with id '{self.id}'>"
Expand Down
5 changes: 4 additions & 1 deletion src/ansys/acp/core/_tree_objects/boolean_selection_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from ansys.api.acp.v0 import boolean_selection_rule_pb2, boolean_selection_rule_pb2_grpc

from .._utils.property_protocols import ReadWriteProperty
from ._grpc_helpers.edge_property_list import define_edge_property_list
from ._grpc_helpers.property_helper import (
grpc_data_property,
Expand Down Expand Up @@ -77,7 +78,9 @@ def _create_stub(self) -> boolean_selection_rule_pb2_grpc.ObjectServiceStub:
status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb)

selection_rules = define_edge_property_list("properties.selection_rules", LinkedSelectionRule)
include_rule_type = grpc_data_property("properties.include_rule_type")
include_rule_type: ReadWriteProperty[bool, bool] = grpc_data_property(
"properties.include_rule_type"
)

elemental_data = elemental_data_property(BooleanSelectionRuleElementalData)
nodal_data = nodal_data_property(BooleanSelectionRuleNodalData)
3 changes: 2 additions & 1 deletion src/ansys/acp/core/_tree_objects/cad_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from ansys.api.acp.v0 import cad_component_pb2, cad_component_pb2_grpc

from .._utils.property_protocols import ReadOnlyProperty
from ._grpc_helpers.property_helper import grpc_data_property_read_only, mark_grpc_properties
from .base import IdTreeObject, ReadOnlyTreeObject
from .enums import status_type_from_pb
Expand Down Expand Up @@ -31,4 +32,4 @@ def _create_stub(self) -> cad_component_pb2_grpc.ObjectServiceStub:
return cad_component_pb2_grpc.ObjectServiceStub(self._channel)

status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb)
path = grpc_data_property_read_only("properties.path")
path: ReadOnlyProperty[str] = grpc_data_property_read_only("properties.path")
24 changes: 16 additions & 8 deletions src/ansys/acp/core/_tree_objects/cad_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
cad_geometry_pb2_grpc,
)

from .._typing_helper import PATH
from .._utils.array_conversions import to_numpy
from .._utils.path_to_str import path_to_str_checked
from .._utils.property_protocols import ReadOnlyProperty, ReadWriteProperty
from ._grpc_helpers.exceptions import wrap_grpc_errors
from ._grpc_helpers.mapping import get_read_only_collection_property
from ._grpc_helpers.property_helper import (
Expand Down Expand Up @@ -109,14 +111,20 @@ def _create_stub(self) -> cad_geometry_pb2_grpc.ObjectServiceStub:
return cad_geometry_pb2_grpc.ObjectServiceStub(self._channel)

status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb)
locked = grpc_data_property_read_only("properties.locked")

external_path = grpc_data_property("properties.external_path", to_protobuf=path_to_str_checked)
scale_factor = grpc_data_property("properties.scale_factor")
use_default_precision = grpc_data_property("properties.use_default_precision")
precision = grpc_data_property("properties.precision")
use_default_offset = grpc_data_property("properties.use_default_offset")
offset = grpc_data_property("properties.offset")
locked: ReadOnlyProperty[bool] = grpc_data_property_read_only("properties.locked")

external_path: ReadWriteProperty[PATH, PATH] = grpc_data_property(
"properties.external_path", to_protobuf=path_to_str_checked
)
scale_factor: ReadWriteProperty[float, float] = grpc_data_property("properties.scale_factor")
use_default_precision: ReadWriteProperty[bool, bool] = grpc_data_property(
"properties.use_default_precision"
)
precision: ReadWriteProperty[float, float] = grpc_data_property("properties.precision")
use_default_offset: ReadWriteProperty[bool, bool] = grpc_data_property(
"properties.use_default_offset"
)
offset: ReadWriteProperty[float, float] = grpc_data_property("properties.offset")

@property
def visualization_mesh(self) -> TriangleMesh:
Expand Down
7 changes: 4 additions & 3 deletions src/ansys/acp/core/_tree_objects/cutoff_selection_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from ansys.api.acp.v0 import cutoff_selection_rule_pb2, cutoff_selection_rule_pb2_grpc

from .._utils.property_protocols import ReadWriteProperty
from ._grpc_helpers.property_helper import (
grpc_data_property,
grpc_data_property_read_only,
Expand Down Expand Up @@ -116,14 +117,14 @@ def _create_stub(self) -> cutoff_selection_rule_pb2_grpc.ObjectServiceStub:
)
cutoff_geometry = grpc_link_property("properties.cutoff_geometry")
taper_edge_set = grpc_link_property("properties.taper_edge_set")
offset = grpc_data_property("properties.offset")
angle = grpc_data_property("properties.angle")
offset: ReadWriteProperty[float, float] = grpc_data_property("properties.offset")
angle: ReadWriteProperty[float, float] = grpc_data_property("properties.angle")
ply_cutoff_type = grpc_data_property(
"properties.ply_cutoff_type",
from_protobuf=ply_cutoff_type_from_pb,
to_protobuf=ply_cutoff_type_to_pb,
)
ply_tapering = grpc_data_property("properties.ply_tapering")
ply_tapering: ReadWriteProperty[bool, bool] = grpc_data_property("properties.ply_tapering")

elemental_data = elemental_data_property(CutoffSelectionRuleElementalData)
nodal_data = nodal_data_property(CutoffSelectionRuleNodalData)
19 changes: 13 additions & 6 deletions src/ansys/acp/core/_tree_objects/cylindrical_selection_rule.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from collections.abc import Iterable
from collections.abc import Collection, Iterable
import dataclasses

import numpy as np
Expand All @@ -9,6 +9,7 @@
from ansys.api.acp.v0 import cylindrical_selection_rule_pb2, cylindrical_selection_rule_pb2_grpc

from .._utils.array_conversions import to_1D_double_array, to_tuple_from_1D_array
from .._utils.property_protocols import ReadWriteProperty
from ._grpc_helpers.property_helper import (
grpc_data_property,
grpc_data_property_read_only,
Expand Down Expand Up @@ -98,17 +99,23 @@ def _create_stub(self) -> cylindrical_selection_rule_pb2_grpc.ObjectServiceStub:

status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb)

use_global_coordinate_system = grpc_data_property("properties.use_global_coordinate_system")
use_global_coordinate_system: ReadWriteProperty[bool, bool] = grpc_data_property(
"properties.use_global_coordinate_system"
)
rosette = grpc_link_property("properties.rosette")
origin = grpc_data_property(
origin: ReadWriteProperty[tuple[float, float, float], Collection[float]] = grpc_data_property(
"properties.origin", from_protobuf=to_tuple_from_1D_array, to_protobuf=to_1D_double_array
)
direction = grpc_data_property(
"properties.direction", from_protobuf=to_tuple_from_1D_array, to_protobuf=to_1D_double_array
)
radius = grpc_data_property("properties.radius")
relative_rule_type = grpc_data_property("properties.relative_rule_type")
include_rule_type = grpc_data_property("properties.include_rule_type")
radius: ReadWriteProperty[float, float] = grpc_data_property("properties.radius")
relative_rule_type: ReadWriteProperty[bool, bool] = grpc_data_property(
"properties.relative_rule_type"
)
include_rule_type: ReadWriteProperty[bool, bool] = grpc_data_property(
"properties.include_rule_type"
)

elemental_data = elemental_data_property(CylindricalSelectionRuleElementalData)
nodal_data = nodal_data_property(CylindricalSelectionRuleNodalData)
9 changes: 5 additions & 4 deletions src/ansys/acp/core/_tree_objects/edge_set.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import annotations

from collections.abc import Iterable
from collections.abc import Collection, Iterable

from ansys.api.acp.v0 import edge_set_pb2, edge_set_pb2_grpc

from .._utils.array_conversions import to_1D_double_array, to_1D_int_array, to_tuple_from_1D_array
from .._utils.property_protocols import ReadOnlyProperty, ReadWriteProperty
from ._grpc_helpers.property_helper import (
grpc_data_property,
grpc_data_property_read_only,
Expand Down Expand Up @@ -57,7 +58,7 @@ def __init__(
self,
name: str = "EdgeSet",
edge_set_type: EdgeSetType = EdgeSetType.BY_REFERENCE,
defining_node_labels: Iterable[int] = tuple(),
defining_node_labels: Collection[int] = tuple(),
element_set: ElementSet | None = None,
limit_angle: float = -1.0,
origin: tuple[float, float, float] = (0.0, 0.0, 0.0),
Expand All @@ -75,7 +76,7 @@ def _create_stub(self) -> edge_set_pb2_grpc.ObjectServiceStub:
return edge_set_pb2_grpc.ObjectServiceStub(self._channel)

status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb)
locked = grpc_data_property_read_only("properties.locked")
locked: ReadOnlyProperty[bool] = grpc_data_property_read_only("properties.locked")

edge_set_type = grpc_data_property(
"properties.edge_set_type",
Expand All @@ -88,7 +89,7 @@ def _create_stub(self) -> edge_set_pb2_grpc.ObjectServiceStub:
to_protobuf=to_1D_int_array,
)
element_set = grpc_link_property("properties.element_set")
limit_angle = grpc_data_property("properties.limit_angle")
limit_angle: ReadWriteProperty[float, float] = grpc_data_property("properties.limit_angle")
origin = grpc_data_property(
"properties.origin", from_protobuf=to_tuple_from_1D_array, to_protobuf=to_1D_double_array
)
11 changes: 6 additions & 5 deletions src/ansys/acp/core/_tree_objects/element_set.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from collections.abc import Container, Iterable
from collections.abc import Collection, Iterable
import dataclasses

import numpy as np
Expand All @@ -9,6 +9,7 @@
from ansys.api.acp.v0 import element_set_pb2, element_set_pb2_grpc

from .._utils.array_conversions import to_1D_int_array, to_tuple_from_1D_array
from .._utils.property_protocols import ReadOnlyProperty, ReadWriteProperty
from ._grpc_helpers.property_helper import (
grpc_data_property,
grpc_data_property_read_only,
Expand Down Expand Up @@ -50,7 +51,7 @@ def __init__(
self,
name: str = "ElementSet",
middle_offset: bool = False,
element_labels: Container[int] = tuple(),
element_labels: Collection[int] = tuple(),
):
"""Instantiate an Element Set.
Expand All @@ -71,9 +72,9 @@ def _create_stub(self) -> element_set_pb2_grpc.ObjectServiceStub:
return element_set_pb2_grpc.ObjectServiceStub(self._channel)

status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb)
locked = grpc_data_property_read_only("properties.locked")
middle_offset = grpc_data_property("properties.middle_offset")
element_labels = grpc_data_property(
locked: ReadOnlyProperty[bool] = grpc_data_property_read_only("properties.locked")
middle_offset: ReadWriteProperty[bool, bool] = grpc_data_property("properties.middle_offset")
element_labels: ReadWriteProperty[tuple[int, ...], Collection[int]] = grpc_data_property(
"properties.element_labels",
from_protobuf=to_tuple_from_1D_array,
to_protobuf=to_1D_int_array,
Expand Down
Loading

0 comments on commit 22ec26b

Please sign in to comment.