Skip to content

Commit

Permalink
Allow marking 'supported_since' on class level (#604)
Browse files Browse the repository at this point in the history
Add a `_SUPPORTED_SINCE` class attribute to the `GrpcObjectBase` class, which has two effects:
- upon storing an object, check the server version and raise an appropriate exception if the server version is too low
- in the `mark_grpc_properties` class decorator, add a line at the end of the class docstring indicating the server version at which the class was introduced

On all existing classes, the `_SUPPORTED_SINCE` attribute is set to `"24.2"`.
  • Loading branch information
greschd authored Oct 16, 2024
1 parent bc6aa18 commit 0a03f69
Show file tree
Hide file tree
Showing 46 changed files with 107 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/ansys/acp/core/_server/direct.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import dataclasses
import os
import pathlib
import subprocess
from typing import TextIO

Expand Down Expand Up @@ -103,6 +104,10 @@ def start(self) -> None:
stdout_file = self._config.stdout_file
stderr_file = self._config.stderr_file

binary = pathlib.Path(self._config.binary_path)
if not binary.exists():
raise FileNotFoundError(f"Binary not found: '{binary}'")

port = find_free_ports()[0]
self._url = f"localhost:{port}"
self._stdout = open(stdout_file, mode="w", encoding="utf-8")
Expand Down
9 changes: 9 additions & 0 deletions src/ansys/acp/core/_tree_objects/_grpc_helpers/mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from typing import Any, Concatenate, Generic, TypeVar

from grpc import Channel
from packaging.version import parse as parse_version
from typing_extensions import ParamSpec, Self

from ansys.api.acp.v0.base_pb2 import CollectionPath, DeleteRequest, ListRequest
Expand Down Expand Up @@ -302,6 +303,14 @@ def define_mutable_mapping(
"""Define a mutable mapping of child tree objects."""

def collection_property(self: ParentT) -> MutableMapping[CreatableValueT]:
if self._server_version is not None:
if self._server_version < parse_version(object_class._SUPPORTED_SINCE):
raise RuntimeError(
f"The '{object_class.__name__}' object is only supported since version "
f"{object_class._SUPPORTED_SINCE} of the ACP gRPC server. The current server version is "
f"{self._server_version}."
)

return MutableMapping._initialize_with_cache(
server_wrapper=self._server_wrapper,
collection_path=CollectionPath(
Expand Down
22 changes: 22 additions & 0 deletions src/ansys/acp/core/_tree_objects/_grpc_helpers/property_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

from collections.abc import Callable
from functools import reduce
import sys
from typing import TYPE_CHECKING, Any, TypeVar

from google.protobuf.message import Message
Expand Down Expand Up @@ -91,6 +92,27 @@ def mark_grpc_properties(cls: T) -> T:
if name not in props_unique:
props_unique.append(name)
cls._GRPC_PROPERTIES = tuple(props_unique)

# The 'mark_grpc_properties' decorator is also used on intermediate base
# classes which do not have the '_SUPPORTED_SINCE' attribute. We only want
# to add the version information to the final class.
if hasattr(cls, "_SUPPORTED_SINCE"):
if isinstance(cls.__doc__, str):
# When adding to the docstring, we need to match the existing
# indentation of the docstring (except the first line).
# See PEP 257 'Handling Docstring Indentation'.
# Alternatively, we could strip the common indentation from the
# docstring.
indent = sys.maxsize
for line in cls.__doc__.splitlines()[1:]:
stripped = line.lstrip()
if stripped: # ignore empty lines
indent = min(indent, len(line) - len(stripped))
if indent == sys.maxsize:
indent = 0
cls.__doc__ += (
f"\n\n{indent * ' '}*Added in ACP server version {cls._SUPPORTED_SINCE}.*\n"
)
return cls


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ class GrpcObjectBase(Protocol):

__slots__: Iterable[str] = tuple()
_GRPC_PROPERTIES: tuple[str, ...] = tuple()
_SUPPORTED_SINCE: str

def __str__(self) -> str:
string_items = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ def supported_since(
Parameters
----------
version : Optional[str]
version :
The server version since which the method is supported. If ``None``, the
decorator does nothing.
err_msg_tpl : Optional[str]
err_msg_tpl :
A custom error message template. If ``None``, a default error message is used.
"""
if version is None:
Expand All @@ -69,7 +69,7 @@ def inner(self: T, /, *args: P.args, **kwargs: P.kwargs) -> R:
if server_version < required_version:
if err_msg_tpl is None:
err_msg = (
f"The method '{func.__name__}' is only supported since version {version} "
f"The '{func.__name__}' method is only supported since version {version} "
f"of the ACP gRPC server. The current server version is {server_version}."
)
else:
Expand Down
1 change: 1 addition & 0 deletions src/ansys/acp/core/_tree_objects/analysis_ply.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class AnalysisPly(ReadOnlyTreeObject, IdTreeObject):

_COLLECTION_LABEL = "analysis_plies"
_OBJECT_INFO_TYPE = analysis_ply_pb2.ObjectInfo
_SUPPORTED_SINCE = "24.2"

def _create_stub(self) -> analysis_ply_pb2_grpc.ObjectServiceStub:
return analysis_ply_pb2_grpc.ObjectServiceStub(self._channel)
Expand Down
8 changes: 8 additions & 0 deletions src/ansys/acp/core/_tree_objects/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class TreeObjectBase(ObjectCacheMixin, GrpcObjectBase):

_COLLECTION_LABEL: str
_OBJECT_INFO_TYPE: type[ObjectInfo]
_SUPPORTED_SINCE: str

_pb_object: ObjectInfo
name: ReadOnlyProperty[str]
Expand Down Expand Up @@ -328,6 +329,13 @@ def store(self: Self, parent: TreeObject) -> None:
Parent object to store the object under.
"""
self._server_wrapper_store = parent._server_wrapper
assert self._server_version is not None
if self._server_version < parse_version(self._SUPPORTED_SINCE):
raise RuntimeError(
f"The '{type(self).__name__}' object is only supported since version "
f"{self._SUPPORTED_SINCE} of the ACP gRPC server. The current server version is "
f"{self._server_version}."
)

collection_path = CollectionPath(
value=_rp_join(parent._resource_path.value, self._COLLECTION_LABEL)
Expand Down
1 change: 1 addition & 0 deletions src/ansys/acp/core/_tree_objects/boolean_selection_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class BooleanSelectionRule(CreatableTreeObject, IdTreeObject):
_COLLECTION_LABEL = "boolean_selection_rules"
_OBJECT_INFO_TYPE = boolean_selection_rule_pb2.ObjectInfo
_CREATE_REQUEST_TYPE = boolean_selection_rule_pb2.CreateRequest
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down
1 change: 1 addition & 0 deletions src/ansys/acp/core/_tree_objects/cad_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class CADComponent(ReadOnlyTreeObject, IdTreeObject):
__slots__: Iterable[str] = tuple()
_COLLECTION_LABEL = "cad_components"
_OBJECT_INFO_TYPE = cad_component_pb2.ObjectInfo
_SUPPORTED_SINCE = "24.2"

def _create_stub(self) -> cad_component_pb2_grpc.ObjectServiceStub:
return cad_component_pb2_grpc.ObjectServiceStub(self._channel)
Expand Down
1 change: 1 addition & 0 deletions src/ansys/acp/core/_tree_objects/cad_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class CADGeometry(CreatableTreeObject, IdTreeObject):
_COLLECTION_LABEL = "cad_geometries"
_OBJECT_INFO_TYPE = cad_geometry_pb2.ObjectInfo
_CREATE_REQUEST_TYPE = cad_geometry_pb2.CreateRequest
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down
1 change: 1 addition & 0 deletions src/ansys/acp/core/_tree_objects/cutoff_selection_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ class CutoffSelectionRule(CreatableTreeObject, IdTreeObject):
_COLLECTION_LABEL = "cutoff_selection_rules"
_OBJECT_INFO_TYPE = cutoff_selection_rule_pb2.ObjectInfo
_CREATE_REQUEST_TYPE = cutoff_selection_rule_pb2.CreateRequest
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class CylindricalSelectionRule(CreatableTreeObject, IdTreeObject):
_COLLECTION_LABEL = "cylindrical_selection_rules"
_OBJECT_INFO_TYPE = cylindrical_selection_rule_pb2.ObjectInfo
_CREATE_REQUEST_TYPE = cylindrical_selection_rule_pb2.CreateRequest
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down
1 change: 1 addition & 0 deletions src/ansys/acp/core/_tree_objects/edge_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class EdgeSet(CreatableTreeObject, IdTreeObject):
_COLLECTION_LABEL = "edge_sets"
_OBJECT_INFO_TYPE = edge_set_pb2.ObjectInfo
_CREATE_REQUEST_TYPE = edge_set_pb2.CreateRequest
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down
1 change: 1 addition & 0 deletions src/ansys/acp/core/_tree_objects/element_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class ElementSet(CreatableTreeObject, IdTreeObject):
_COLLECTION_LABEL = "element_sets"
_OBJECT_INFO_TYPE = element_set_pb2.ObjectInfo
_CREATE_REQUEST_TYPE = element_set_pb2.CreateRequest
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down
1 change: 1 addition & 0 deletions src/ansys/acp/core/_tree_objects/fabric.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class Fabric(CreatableTreeObject, IdTreeObject):
_COLLECTION_LABEL = "fabrics"
_OBJECT_INFO_TYPE = fabric_pb2.ObjectInfo
_CREATE_REQUEST_TYPE = fabric_pb2.CreateRequest
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class GeometricalSelectionRule(CreatableTreeObject, IdTreeObject):
_COLLECTION_LABEL = "geometrical_selection_rules"
_OBJECT_INFO_TYPE = geometrical_selection_rule_pb2.ObjectInfo
_CREATE_REQUEST_TYPE = geometrical_selection_rule_pb2.CreateRequest
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down
2 changes: 2 additions & 0 deletions src/ansys/acp/core/_tree_objects/linked_selection_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ class LinkedSelectionRule(GenericEdgePropertyType):
a Boolean Selection Rule, only to a Modeling Ply.
"""

_SUPPORTED_SINCE = "24.2"

def __init__(
self,
selection_rule: _LINKABLE_SELECTION_RULE_TYPES,
Expand Down
1 change: 1 addition & 0 deletions src/ansys/acp/core/_tree_objects/lookup_table_1d.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class LookUpTable1D(CreatableTreeObject, IdTreeObject):
_COLLECTION_LABEL = "lookup_tables_1d"
_OBJECT_INFO_TYPE = lookup_table_1d_pb2.ObjectInfo
_CREATE_REQUEST_TYPE = lookup_table_1d_pb2.CreateRequest
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down
1 change: 1 addition & 0 deletions src/ansys/acp/core/_tree_objects/lookup_table_1d_column.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class LookUpTable1DColumn(LookUpTableColumnBase):
_COLLECTION_LABEL = "lookup_table_1d_columns"
_OBJECT_INFO_TYPE = lookup_table_1d_column_pb2.ObjectInfo
_CREATE_REQUEST_TYPE = lookup_table_1d_column_pb2.CreateRequest
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down
1 change: 1 addition & 0 deletions src/ansys/acp/core/_tree_objects/lookup_table_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class LookUpTable3D(CreatableTreeObject, IdTreeObject):
_COLLECTION_LABEL = "lookup_tables_3d"
_OBJECT_INFO_TYPE = lookup_table_3d_pb2.ObjectInfo
_CREATE_REQUEST_TYPE = lookup_table_3d_pb2.CreateRequest
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down
1 change: 1 addition & 0 deletions src/ansys/acp/core/_tree_objects/lookup_table_3d_column.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class LookUpTable3DColumn(LookUpTableColumnBase):
_COLLECTION_LABEL = "lookup_table_3d_columns"
_OBJECT_INFO_TYPE = lookup_table_3d_column_pb2.ObjectInfo
_CREATE_REQUEST_TYPE = lookup_table_3d_column_pb2.CreateRequest
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down
1 change: 1 addition & 0 deletions src/ansys/acp/core/_tree_objects/material/material.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ class Material(CreatableTreeObject, IdTreeObject):
_COLLECTION_LABEL = "materials"
_OBJECT_INFO_TYPE = material_pb2.ObjectInfo
_CREATE_REQUEST_TYPE = material_pb2.CreateRequest
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class ConstantDensity(_DensityMixin, _ConstantPropertySet):
"""Constant density material property set."""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand All @@ -65,5 +66,6 @@ class VariableDensity(_DensityMixin, _VariablePropertySet):
"""Variable density material property set."""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

rho = variable_material_grpc_data_property("rho")
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class ConstantEngineeringConstants(_EngineeringConstantsMixin, _ConstantProperty
"""Constant engineering constants material property set."""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

@classmethod
def from_isotropic_constants(
Expand Down Expand Up @@ -173,6 +174,7 @@ class VariableEngineeringConstants(_EngineeringConstantsMixin, _VariableProperty
"""Variable engineering constants material property set."""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

E = variable_material_grpc_data_property("E", **_ISOTROPIC_KWARGS)
nu = variable_material_grpc_data_property("nu", **_ISOTROPIC_KWARGS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class ConstantFabricFiberAngle(_FabricFiberAngleMixin, _ConstantPropertySet):
"""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand All @@ -73,5 +74,6 @@ class VariableFabricFiberAngle(_FabricFiberAngleMixin, _VariablePropertySet):
"""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

fabric_fiber_angle = variable_material_grpc_data_property("fabric_fiber_angle")
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class ConstantLaRCConstants(_LaRCConstantsMixin, _ConstantPropertySet):
"""Constant LaRC failure criterion properties."""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down Expand Up @@ -79,6 +80,7 @@ class VariableLaRCConstants(_LaRCConstantsMixin, _VariablePropertySet):
"""Variable LaRC failure criterion properties."""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

fracture_angle_under_compression = variable_material_grpc_data_property(
"fracture_angle_under_compression"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class ConstantPuckConstants(_PuckConstantsMixin, _ConstantPropertySet):
"""Constant Puck constants material property set."""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down Expand Up @@ -125,6 +126,7 @@ class VariablePuckConstants(_PuckConstantsMixin, _VariablePropertySet):
"""Variable Puck constants material property set."""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

p_21_pos = variable_material_grpc_data_property("p_21_pos")
p_21_neg = variable_material_grpc_data_property("p_21_neg")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class ConstantStrainLimits(_StrainLimitsMixin, _ConstantPropertySet):
"""Constant strain limits material property set."""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

@classmethod
def from_isotropic_constants(
Expand Down Expand Up @@ -172,6 +173,7 @@ class VariableStrainLimits(_StrainLimitsMixin, _VariablePropertySet):
"""Variable strain limits material property set."""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

effective_strain = variable_material_grpc_data_property("effective_strain", **_ISOTROPIC_KWARGS)
eXc = variable_material_grpc_data_property("eXc", **_ORTHOTROPIC_KWARGS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class ConstantStressLimits(_StressLimitsMixin, _ConstantPropertySet):
"""Constant stress limits material property set."""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

@classmethod
def from_isotropic_constants(
Expand Down Expand Up @@ -172,6 +173,7 @@ class VariableStressLimits(_StressLimitsMixin, _VariablePropertySet):
"""Variable stress limits material property set."""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

effective_stress = variable_material_grpc_data_property("effective_stress", **_ISOTROPIC_KWARGS)
Xc = variable_material_grpc_data_property("Xc", **_ORTHOTROPIC_KWARGS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class ConstantTsaiWuConstants(_TsaiWuConstantsMixin, _ConstantPropertySet):
"""Constant Tsai-Wu constants material property set."""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

def __init__(
self,
Expand Down Expand Up @@ -71,6 +72,7 @@ class VariableTsaiWuConstants(_TsaiWuConstantsMixin, _VariablePropertySet):
"""Variable Tsai-Wu constants material property set."""

_GRPC_PROPERTIES = tuple()
_SUPPORTED_SINCE = "24.2"

XY = variable_material_grpc_data_property("XY")
XZ = variable_material_grpc_data_property("XZ")
Expand Down
Loading

0 comments on commit 0a03f69

Please sign in to comment.