Skip to content

Commit

Permalink
Fix autocompletion of 'create_*' methods (#340)
Browse files Browse the repository at this point in the history
Split the `define_mutable_mapping` functionality into two
functions. This allows typing the function which produces the
`create_*` methods with a ParamSpec, which in turn allows the
autocompletion to work (tested on VSCode).

Other changes:
- Improve the type hints on class decorators, to enable autocompletion
  on the constructors.
- Improve autocompletion for the `define_mutable_mapping` return value
  by introducing a protocol that describes read-only properties.
  • Loading branch information
greschd authored Jan 24, 2024
1 parent 88b1b7a commit 0e6e5e2
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 51 deletions.
39 changes: 26 additions & 13 deletions src/ansys/acp/core/_tree_objects/_grpc_helpers/mapping.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
from __future__ import annotations

from collections.abc import Iterator
from functools import wraps
from typing import TYPE_CHECKING, Any, Callable, Generic, TypeVar

if TYPE_CHECKING:
from mypy_extensions import KwArg, Arg
from typing import Callable, Generic, TypeVar

from grpc import Channel
from typing_extensions import Concatenate, ParamSpec

from ansys.api.acp.v0.base_pb2 import CollectionPath, DeleteRequest, ListRequest

from ..._utils.property_protocols import ReadOnlyProperty
from ..._utils.resource_paths import join as _rp_join
from ..base import CreatableTreeObject, TreeObject
from .exceptions import wrap_grpc_errors
from .protocols import EditableAndReadableResourceStub, ObjectInfo, ReadableResourceStub

ValueT = TypeVar("ValueT", bound=CreatableTreeObject)

__all__ = ["Mapping", "MutableMapping", "define_mutable_mapping"]
__all__ = ["Mapping", "MutableMapping", "define_mutable_mapping", "define_create_method"]


class Mapping(Generic[ValueT]):
Expand Down Expand Up @@ -168,15 +166,30 @@ def collection_property(self: ParentT) -> Mapping[ValueT]:
return collection_property


def define_mutable_mapping(
object_class: type[ValueT], stub_class: type[EditableAndReadableResourceStub]
) -> tuple[Callable[[Arg(ParentT, "self"), KwArg(Any)], ValueT], property]:
@wraps(object_class.__init__)
def create_method(self: ParentT, **kwargs: Any) -> ValueT:
obj = object_class(**kwargs)
P = ParamSpec("P")


def define_create_method(
object_class: Callable[P, ValueT], func_name: str, parent_class_name: str, module_name: str
) -> Callable[Concatenate[ParentT, P], ValueT]:
def inner(self: ParentT, /, *args: P.args, **kwargs: P.kwargs) -> ValueT:
obj = object_class(*args, **kwargs)
obj.store(parent=self)
return obj

# NOTE: This relies on our convention to document the tree object classes
# on the class itself, instead of the __init__ method.
inner.__doc__ = object_class.__doc__

inner.__name__ = func_name
inner.__qualname__ = f"{parent_class_name}.{func_name}"
inner.__module__ = module_name
return inner


def define_mutable_mapping(
object_class: type[ValueT], stub_class: type[EditableAndReadableResourceStub]
) -> ReadOnlyProperty[MutableMapping[ValueT]]:
def collection_property(self: ParentT) -> MutableMapping[ValueT]:
return MutableMapping(
channel=self._channel,
Expand All @@ -187,4 +200,4 @@ def collection_property(self: ParentT) -> MutableMapping[ValueT]:
stub=stub_class(channel=self._channel),
)

return create_method, property(collection_property)
return property(collection_property)
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from __future__ import annotations

from functools import reduce
from typing import Any, Callable
from typing import Any, Callable, TypeVar

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

Expand All @@ -25,7 +25,10 @@ class _exposed_grpc_property(property):
pass


def mark_grpc_properties(cls: type[GrpcObjectBase]) -> type[GrpcObjectBase]:
T = TypeVar("T", bound=type[GrpcObjectBase])


def mark_grpc_properties(cls: T) -> T:
props: list[str] = []
# Loop is needed because we otherwise get only the _GRPC_PROPERTIES of one of the base classes.
for base_cls in reversed(cls.__bases__):
Expand Down
10 changes: 8 additions & 2 deletions src/ansys/acp/core/_tree_objects/lookup_table_1d.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
)

from .._utils.array_conversions import to_1D_double_array, to_tuple_from_1D_array
from ._grpc_helpers.mapping import define_mutable_mapping
from ._grpc_helpers.mapping import define_create_method, define_mutable_mapping
from ._grpc_helpers.property_helper import (
grpc_data_property,
grpc_data_property_read_only,
Expand Down Expand Up @@ -80,6 +80,12 @@ def _create_stub(self) -> lookup_table_1d_pb2_grpc.ObjectServiceStub:
"properties.direction", from_protobuf=to_tuple_from_1D_array, to_protobuf=to_1D_double_array
)

create_column, columns = define_mutable_mapping(
create_column = define_create_method(
LookUpTable1DColumn,
func_name="create_column",
parent_class_name="LookUpTable1D",
module_name=__module__,
)
columns = define_mutable_mapping(
LookUpTable1DColumn, lookup_table_1d_column_pb2_grpc.ObjectServiceStub
)
10 changes: 8 additions & 2 deletions src/ansys/acp/core/_tree_objects/lookup_table_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
lookup_table_3d_pb2_grpc,
)

from ._grpc_helpers.mapping import define_mutable_mapping
from ._grpc_helpers.mapping import define_create_method, define_mutable_mapping
from ._grpc_helpers.property_helper import (
grpc_data_property,
grpc_data_property_read_only,
Expand Down Expand Up @@ -93,6 +93,12 @@ def _create_stub(self) -> lookup_table_3d_pb2_grpc.ObjectServiceStub:
search_radius = grpc_data_property("properties.search_radius")
num_min_neighbors = grpc_data_property("properties.num_min_neighbors")

create_column, columns = define_mutable_mapping(
create_column = define_create_method(
LookUpTable3DColumn,
func_name="create_column",
parent_class_name="LookUpTable3D",
module_name=__module__,
)
columns = define_mutable_mapping(
LookUpTable3DColumn, lookup_table_3d_column_pb2_grpc.ObjectServiceStub
)
175 changes: 147 additions & 28 deletions src/ansys/acp/core/_tree_objects/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
from .._utils.visualization import to_pyvista_faces, to_pyvista_types
from ._grpc_helpers.enum_wrapper import wrap_to_string_enum
from ._grpc_helpers.exceptions import wrap_grpc_errors
from ._grpc_helpers.mapping import define_mutable_mapping
from ._grpc_helpers.mapping import define_create_method, define_mutable_mapping
from ._grpc_helpers.property_helper import (
grpc_data_property,
grpc_data_property_read_only,
Expand Down Expand Up @@ -309,69 +309,188 @@ def export_materials(self, path: _PATH) -> None:
)
)

create_material, materials = define_mutable_mapping(
Material, material_pb2_grpc.ObjectServiceStub
create_material = define_create_method(
Material, func_name="create_material", parent_class_name="Model", module_name=__module__
)
create_fabric, fabrics = define_mutable_mapping(Fabric, fabric_pb2_grpc.ObjectServiceStub)
create_stackup, stackups = define_mutable_mapping(Stackup, stackup_pb2_grpc.ObjectServiceStub)
create_sublaminate, sublaminates = define_mutable_mapping(
SubLaminate, sublaminate_pb2_grpc.ObjectServiceStub
materials = define_mutable_mapping(Material, material_pb2_grpc.ObjectServiceStub)

create_fabric = define_create_method(
Fabric, func_name="create_fabric", parent_class_name="Model", module_name=__module__
)
fabrics = define_mutable_mapping(Fabric, fabric_pb2_grpc.ObjectServiceStub)

create_stackup = define_create_method(
Stackup, func_name="create_stackup", parent_class_name="Model", module_name=__module__
)
stackups = define_mutable_mapping(Stackup, stackup_pb2_grpc.ObjectServiceStub)

create_sublaminate = define_create_method(
SubLaminate,
func_name="create_sublaminate",
parent_class_name="Model",
module_name=__module__,
)
sublaminates = define_mutable_mapping(SubLaminate, sublaminate_pb2_grpc.ObjectServiceStub)

create_element_set = define_create_method(
ElementSet,
func_name="create_element_set",
parent_class_name="Model",
module_name=__module__,
)
create_element_set, element_sets = define_mutable_mapping(
ElementSet, element_set_pb2_grpc.ObjectServiceStub
element_sets = define_mutable_mapping(ElementSet, element_set_pb2_grpc.ObjectServiceStub)

create_edge_set = define_create_method(
EdgeSet, func_name="create_edge_set", parent_class_name="Model", module_name=__module__
)
create_edge_set, edge_sets = define_mutable_mapping(
EdgeSet, edge_set_pb2_grpc.ObjectServiceStub
edge_sets = define_mutable_mapping(EdgeSet, edge_set_pb2_grpc.ObjectServiceStub)

create_cad_geometry = define_create_method(
CADGeometry,
func_name="create_cad_geometry",
parent_class_name="Model",
module_name=__module__,
)
create_cad_geometry, cad_geometries = define_mutable_mapping(
CADGeometry, cad_geometry_pb2_grpc.ObjectServiceStub
cad_geometries = define_mutable_mapping(CADGeometry, cad_geometry_pb2_grpc.ObjectServiceStub)

create_virtual_geometry = define_create_method(
VirtualGeometry,
func_name="create_virtual_geometry",
parent_class_name="Model",
module_name=__module__,
)
create_virtual_geometry, virtual_geometries = define_mutable_mapping(
virtual_geometries = define_mutable_mapping(
VirtualGeometry, virtual_geometry_pb2_grpc.ObjectServiceStub
)
create_rosette, rosettes = define_mutable_mapping(Rosette, rosette_pb2_grpc.ObjectServiceStub)

create_lookup_table_1d, lookup_tables_1d = define_mutable_mapping(
create_rosette = define_create_method(
Rosette, func_name="create_rosette", parent_class_name="Model", module_name=__module__
)
rosettes = define_mutable_mapping(Rosette, rosette_pb2_grpc.ObjectServiceStub)

create_lookup_table_1d = define_create_method(
LookUpTable1D,
func_name="create_lookup_table_1d",
parent_class_name="Model",
module_name=__module__,
)
lookup_tables_1d = define_mutable_mapping(
LookUpTable1D, lookup_table_1d_pb2_grpc.ObjectServiceStub
)

create_lookup_table_3d, lookup_tables_3d = define_mutable_mapping(
create_lookup_table_3d = define_create_method(
LookUpTable3D,
func_name="create_lookup_table_3d",
parent_class_name="Model",
module_name=__module__,
)
lookup_tables_3d = define_mutable_mapping(
LookUpTable3D, lookup_table_3d_pb2_grpc.ObjectServiceStub
)

create_parallel_selection_rule, parallel_selection_rules = define_mutable_mapping(
create_parallel_selection_rule = define_create_method(
ParallelSelectionRule,
func_name="create_parallel_selection_rule",
parent_class_name="Model",
module_name=__module__,
)
parallel_selection_rules = define_mutable_mapping(
ParallelSelectionRule, parallel_selection_rule_pb2_grpc.ObjectServiceStub
)
create_cylindrical_selection_rule, cylindrical_selection_rules = define_mutable_mapping(

create_cylindrical_selection_rule = define_create_method(
CylindricalSelectionRule,
func_name="create_cylindrical_selection_rule",
parent_class_name="Model",
module_name=__module__,
)
cylindrical_selection_rules = define_mutable_mapping(
CylindricalSelectionRule, cylindrical_selection_rule_pb2_grpc.ObjectServiceStub
)
create_spherical_selection_rule, spherical_selection_rules = define_mutable_mapping(

create_spherical_selection_rule = define_create_method(
SphericalSelectionRule,
func_name="create_spherical_selection_rule",
parent_class_name="Model",
module_name=__module__,
)
spherical_selection_rules = define_mutable_mapping(
SphericalSelectionRule, spherical_selection_rule_pb2_grpc.ObjectServiceStub
)
create_tube_selection_rule, tube_selection_rules = define_mutable_mapping(

create_tube_selection_rule = define_create_method(
TubeSelectionRule,
func_name="create_tube_selection_rule",
parent_class_name="Model",
module_name=__module__,
)
tube_selection_rules = define_mutable_mapping(
TubeSelectionRule, tube_selection_rule_pb2_grpc.ObjectServiceStub
)
create_cutoff_selection_rule, cutoff_selection_rules = define_mutable_mapping(

create_cutoff_selection_rule = define_create_method(
CutoffSelectionRule,
func_name="create_cutoff_selection_rule",
parent_class_name="Model",
module_name=__module__,
)
cutoff_selection_rules = define_mutable_mapping(
CutoffSelectionRule, cutoff_selection_rule_pb2_grpc.ObjectServiceStub
)
create_geometrical_selection_rule, geometrical_selection_rules = define_mutable_mapping(

create_geometrical_selection_rule = define_create_method(
GeometricalSelectionRule,
func_name="create_geometrical_selection_rule",
parent_class_name="Model",
module_name=__module__,
)
geometrical_selection_rules = define_mutable_mapping(
GeometricalSelectionRule, geometrical_selection_rule_pb2_grpc.ObjectServiceStub
)
create_variable_offset_selection_rule, variable_offset_selection_rules = define_mutable_mapping(

create_variable_offset_selection_rule = define_create_method(
VariableOffsetSelectionRule,
func_name="create_variable_offset_selection_rule",
parent_class_name="Model",
module_name=__module__,
)
variable_offset_selection_rules = define_mutable_mapping(
VariableOffsetSelectionRule, variable_offset_selection_rule_pb2_grpc.ObjectServiceStub
)
create_boolean_selection_rule, boolean_selection_rules = define_mutable_mapping(

create_boolean_selection_rule = define_create_method(
BooleanSelectionRule,
func_name="create_boolean_selection_rule",
parent_class_name="Model",
module_name=__module__,
)
boolean_selection_rules = define_mutable_mapping(
BooleanSelectionRule, boolean_selection_rule_pb2_grpc.ObjectServiceStub
)

create_oriented_selection_set, oriented_selection_sets = define_mutable_mapping(
create_oriented_selection_set = define_create_method(
OrientedSelectionSet,
func_name="create_oriented_selection_set",
parent_class_name="Model",
module_name=__module__,
)
oriented_selection_sets = define_mutable_mapping(
OrientedSelectionSet, oriented_selection_set_pb2_grpc.ObjectServiceStub
)
create_modeling_group, modeling_groups = define_mutable_mapping(
create_modeling_group = define_create_method(
ModelingGroup,
func_name="create_modeling_group",
parent_class_name="Model",
module_name=__module__,
)
modeling_groups = define_mutable_mapping(
ModelingGroup, modeling_group_pb2_grpc.ObjectServiceStub
)

create_sensor, sensors = define_mutable_mapping(Sensor, sensor_pb2_grpc.ObjectServiceStub)
create_sensor = define_create_method(
Sensor, func_name="create_sensor", parent_class_name="Model", module_name=__module__
)
sensors = define_mutable_mapping(Sensor, sensor_pb2_grpc.ObjectServiceStub)

@property
def mesh(self) -> MeshData:
Expand Down
10 changes: 7 additions & 3 deletions src/ansys/acp/core/_tree_objects/modeling_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from ansys.api.acp.v0 import modeling_group_pb2, modeling_group_pb2_grpc, modeling_ply_pb2_grpc

from ._grpc_helpers.mapping import define_mutable_mapping
from ._grpc_helpers.mapping import define_create_method, define_mutable_mapping
from ._grpc_helpers.property_helper import mark_grpc_properties
from ._mesh_data import ElementalData, NodalData, elemental_data_property, nodal_data_property
from .base import CreatableTreeObject, IdTreeObject
Expand Down Expand Up @@ -49,9 +49,13 @@ def __init__(self, name: str = "ModelingGroup", **kwargs: Any):
def _create_stub(self) -> modeling_group_pb2_grpc.ObjectServiceStub:
return modeling_group_pb2_grpc.ObjectServiceStub(self._channel)

create_modeling_ply, modeling_plies = define_mutable_mapping(
ModelingPly, modeling_ply_pb2_grpc.ObjectServiceStub
create_modeling_ply = define_create_method(
ModelingPly,
func_name="create_modeling_ply",
parent_class_name="ModelingGroup",
module_name=__module__,
)
modeling_plies = define_mutable_mapping(ModelingPly, modeling_ply_pb2_grpc.ObjectServiceStub)

elemental_data = elemental_data_property(ModelingGroupElementalData)
nodal_data = nodal_data_property(ModelingGroupNodalData)
Loading

0 comments on commit 0e6e5e2

Please sign in to comment.