Skip to content

Commit

Permalink
Fix/obj performance (#340)
Browse files Browse the repository at this point in the history
Co-authored-by: Mario Ostieri <[email protected]>
  • Loading branch information
randallfrank and mariostieriansys authored Dec 7, 2023
1 parent 3f6404a commit 045557b
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 113 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ jobs:
runs-on: ubuntu-latest
needs: doc-style
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Login in Github Container registry
uses: docker/login-action@v2
Expand Down Expand Up @@ -185,7 +185,7 @@ jobs:
needs: tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Microsoft Teams Notification
uses: jdcargile/[email protected]
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/nightly-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-20.04

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Login in Github Container registry
uses: docker/login-action@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ jobs:
needs: [ nightly_test, nightly_build ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Microsoft Teams Notification
uses: jdcargile/[email protected]
with:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ tests = [
"docker>=6.1.0",
]
doc = [
"Sphinx==7.0.1",
"Sphinx==7.2.6",
"numpydoc==1.5.0",
"ansys-sphinx-theme==0.9.9",
"sphinx-copybutton==0.5.2",
Expand Down
21 changes: 20 additions & 1 deletion src/ansys/pyensight/core/ensight_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def __init__(self, host: str = "127.0.0.1", port: int = 12345, secret_key: str =
self._stub = None
self._dsg_stub = None
self._security_token = secret_key
self._session_name: str = ""
# Streaming APIs
# Event (strings)
self._event_stream = None
Expand Down Expand Up @@ -71,6 +72,19 @@ def security_token(self) -> str:
def security_token(self, name: str) -> None:
self._security_token = name

@property
def session_name(self) -> str:
"""The gRPC server session name
EnSight gRPC calls can include the session name via 'session_name' metadata.
A client session may provide a session name via this property.
"""
return self._session_name

@session_name.setter
def session_name(self, name: str) -> None:
self._session_name = name

def shutdown(self, stop_ensight: bool = False, force: bool = False) -> None:
"""Close down the gRPC connection
Expand Down Expand Up @@ -148,7 +162,9 @@ def connect(self, timeout: float = 15.0) -> None:
def _metadata(self) -> List[Tuple[bytes, Union[str, bytes]]]:
"""Compute the gRPC stream metadata
Compute the list to be passed to the gRPC calls for things like security.
Compute the list to be passed to the gRPC calls for things like security
and the session name.
"""
ret: List[Tuple[bytes, Union[str, bytes]]] = list()
s: Union[str, bytes]
Expand All @@ -157,6 +173,9 @@ def _metadata(self) -> List[Tuple[bytes, Union[str, bytes]]]:
if type(s) == str:
s = s.encode("utf-8")
ret.append((b"shared_secret", s))
if self.session_name:
s = self.session_name.encode("utf-8")
ret.append((b"session_name", s))
return ret

def render(
Expand Down
71 changes: 45 additions & 26 deletions src/ansys/pyensight/core/ensobj.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
The ensobj module provides the base class to all EnSight proxy objects
"""
from typing import TYPE_CHECKING, Any, List, Optional, no_type_check
from typing import TYPE_CHECKING, Any, Optional, no_type_check

if TYPE_CHECKING:
from ansys.pyensight.core import Session
Expand All @@ -29,9 +29,10 @@ class ENSOBJ(object):
classes are ENS_TOOL, ENS_PART and ENS_ANNOT.
attr_value :
The attribute value associated with any specified attr_id.
Returns
-------
owned : bool
If True, the object is assumed to be "owned" by this interpreter.
This means that the lifecycle of the ENSOBJ instance in EnSight is
dictated by the lifecycle of this proxy object.
"""

Expand All @@ -41,13 +42,20 @@ def __init__(
objid: int,
attr_id: Optional[int] = None,
attr_value: Optional[int] = None,
owned: Optional[bool] = None,
) -> None:
self._session = session
self._objid = objid
self._attr_id = attr_id
self._attr_value = attr_value
self._session.add_ensobj_instance(self)
self.attr_list = self.populate_attr_list()

# True if this Python instance "owns" the ENSOBJ instance (via EnSight proxy cache)
if owned:
self._is_owned = True
else:
self._is_owned = False
# do not put this object in the cache if it is owned, allow gc
self._session.add_ensobj_instance(self)

def __eq__(self, obj):
return self._objid == obj._objid
Expand All @@ -58,11 +66,28 @@ def __lt__(self, obj):
def __hash__(self):
return self._objid

def __del__(self):
# release the session to allow for garbage collection
tmp_session = self._session
self._session = None
if self._is_owned:
try:
cmd = f"ensight.objs.release_id('{tmp_session.name}', {self.__OBJID__})"
tmp_session.cmd(cmd, do_eval=False)
except Exception: # pragma: no cover
# This could happen at any time, including outside
# the scope of the session, so we need to be
# ready for any error.
pass

@property
def __OBJID__(self) -> int: # noqa: N802
return self._objid

def _remote_obj(self) -> str:
"""Convert the object into a string appropriate for use in the
remote EnSight session. Usually, this is some form of
ensight.objs.wrap_id()."""
return self._session.remote_obj(self._objid)

def getattr(self, attrid: Any) -> Any:
Expand Down Expand Up @@ -222,16 +247,6 @@ def attrinfo(self, attrid: Optional[Any] = None) -> dict:
return self._session.cmd(f"{self._remote_obj()}.attrinfo()")
return self._session.cmd(f"{self._remote_obj()}.attrinfo({attrid.__repr__()})")

def populate_attr_list(self) -> List[str]:
"""Populates a list with attributes.
Returns
-------
List[str]
The list with the attributes.
"""
return [k for k, _ in self.attrinfo().items()]

def attrissensitive(self, attrid: Any) -> bool:
"""Check to see if a given attribute is 'sensitive'
Expand Down Expand Up @@ -437,17 +452,21 @@ def destroy(self) -> None:

def __str__(self) -> str:
desc = ""
if self._session.ensight.objs.enums.DESCRIPTION in self.attr_list:
try:
if hasattr(self, "DESCRIPTION"):
desc_text = self.DESCRIPTION
else:
if hasattr(self.__class__, "attr_list"):
if self._session.ensight.objs.enums.DESCRIPTION in self.__class__.attr_list:
try:
if hasattr(self, "DESCRIPTION"):
desc_text = self.DESCRIPTION
else:
desc_text = ""
except RuntimeError:
# self.DESCRIPTION is a gRPC call that can fail for default objects
desc_text = ""
except RuntimeError:
# self.DESCRIPTION is a gRPC call that can fail for default objects
desc_text = ""
desc = f", desc: '{desc_text}'"
return f"Class: {self.__class__.__name__}{desc}, CvfObjID: {self._objid}, cached:no"
desc = f", desc: '{desc_text}'"
owned = ""
if self._is_owned:
owned = ", Owned"
return f"Class: {self.__class__.__name__}{desc}{owned}, CvfObjID: {self._objid}, cached:no"

def __repr__(self) -> str:
"""Custom __repr__ method used by the stub API.
Expand Down
32 changes: 26 additions & 6 deletions src/ansys/pyensight/core/listobj.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,19 @@
"""
from collections.abc import Iterable
import fnmatch
from typing import Any, List, Optional, SupportsIndex, TypeVar, no_type_check, overload
from typing import (
TYPE_CHECKING,
Any,
List,
Optional,
SupportsIndex,
TypeVar,
no_type_check,
overload,
)

if TYPE_CHECKING:
from ansys.pyensight.core import Session

from ansys.pyensight.core.ensobj import ENSOBJ

Expand Down Expand Up @@ -44,8 +56,9 @@ class ensobjlist(List[T]): # noqa: N801
"""

def __init__(self, *args, **kwargs) -> None:
def __init__(self, *args, session: Optional["Session"] = None, **kwargs) -> None:
super().__init__(*args, **kwargs)
self._session = session

@staticmethod
def _is_iterable(arg: Any) -> bool:
Expand Down Expand Up @@ -92,7 +105,7 @@ def find(
value_list = value
if not self._is_iterable(value):
value_list = [value]
out_list: ensobjlist[Any] = ensobjlist()
out_list: ensobjlist[Any] = ensobjlist(session=self._session)
for item in self:
if isinstance(item, ENSOBJ):
try:
Expand All @@ -112,7 +125,16 @@ def find(
break
except RuntimeError:
pass
# TODO: handle group
if group:
# This is a bit of a hack, but the find() method generates a local list of
# proxy objects. We want to put that in a group. We do that by running
# a script in EnSight that creates an empty group and then adds those
# children to the group. The output becomes the remote referenced ENS_GROUP.
if self._session is not None:
ens_group_cmd = "ensight.objs.core.VPORTS.find('__unknown__', group=1)"
ens_group = self._session.cmd(ens_group_cmd)
ens_group.addchild(out_list)
out_list = ens_group
return out_list

def set_attr(self, attr: Any, value: Any) -> int:
Expand All @@ -139,7 +161,6 @@ def set_attr(self, attr: Any, value: Any) -> int:
>>> session.ensight.objs.core.PARTS.set_attr("VISIBLE", True)
"""
objid_list = []
session = None
objid_list = [x.__OBJID__ for x in self if isinstance(x, ENSOBJ)]
for item in self:
Expand Down Expand Up @@ -176,7 +197,6 @@ def get_attr(self, attr: Any, default: Optional[Any] = None):
>>> state = session.ensight.core.PARTS.get_attr(session.ensight.objs.enums.VISIBLE)
"""
objid_list = []
session = None
objid_list = [x.__OBJID__ for x in self if isinstance(x, ENSOBJ)]
for item in self:
Expand Down
Loading

0 comments on commit 045557b

Please sign in to comment.