Skip to content

Commit ea4bea5

Browse files
fix: Raise AttributeError for non-existing meshing objects after switch_to solver. (#3949)
Raise AttributeError for non-existing meshing objects after switch_to solver. Earlier instead of raising an Attribute error (general python behaviour), it was returning None. This has been corrected now. There is an "is_active" method associated with session objects to check it's state. --------- Co-authored-by: pyansys-ci-bot <[email protected]>
1 parent 7c1590a commit ea4bea5

14 files changed

+133
-52
lines changed

doc/changelog.d/3949.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Raise AttributeError for non-existing meshing objects after switch_to solver.

src/ansys/fluent/core/session.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,10 @@ def __init__(self, _session):
265265
for obj in filter(None, (self._datamodel_events, self.transcript, self.events)):
266266
self._fluent_connection.register_finalizer_cb(obj.stop)
267267

268+
def is_active(self):
269+
"""Whether the current session is active."""
270+
return True if self._fluent_connection else False
271+
268272
@property
269273
def field_info(self):
270274
"""Provides access to Fluent field information."""
@@ -370,6 +374,7 @@ def exit(self, **kwargs) -> None:
370374
logger.debug("session.exit() called")
371375
if self._fluent_connection:
372376
self._fluent_connection.exit(**kwargs)
377+
self._fluent_connection = None
373378

374379
def force_exit(self) -> None:
375380
"""Forces the Fluent session to exit, losing unsaved progress and data."""
@@ -449,6 +454,8 @@ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any):
449454
self.exit()
450455

451456
def __dir__(self):
457+
if self._fluent_connection is None:
458+
return ["is_active"]
452459
dir_list = set(list(self.__dict__.keys()) + dir(type(self))) - {
453460
"field_data",
454461
"field_info",

src/ansys/fluent/core/session_meshing.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ def __init__(
6464
transcript can be subsequently started and stopped
6565
using method calls on the ``Session`` object.
6666
"""
67-
self._switched = False
6867
super(Meshing, self).__init__(
6968
fluent_connection=fluent_connection,
7069
scheme_eval=scheme_eval,
@@ -89,15 +88,19 @@ def switch_to_solver(self) -> Any:
8988
scheme_eval=self.scheme_eval,
9089
file_transfer_service=self._file_transfer_service,
9190
)
92-
self._switched = True
91+
self._fluent_connection = None
9392
return solver_session
9493

9594
def __getattribute__(self, item: str):
96-
if item == "_switched":
97-
return super(Meshing, self).__getattribute__(item)
98-
99-
if self._switched and item != "exit":
100-
return None
95+
if super(Meshing, self).__getattribute__(
96+
"_fluent_connection"
97+
) is None and item not in [
98+
"is_active",
99+
"_fluent_connection",
100+
]:
101+
raise AttributeError(
102+
f"'{__class__.__name__}' object has no attribute '{item}'"
103+
)
101104

102105
return super(Meshing, self).__getattribute__(item)
103106

src/ansys/fluent/core/session_solver.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,8 @@ def __getattr__(self, attr):
366366
return getattr(self._settings_api_root, attr)
367367

368368
def __dir__(self):
369+
if self._fluent_connection is None:
370+
return ["is_active"]
369371
settings_dir = []
370372
if self.get_fluent_version() <= FluentVersion.v242:
371373
self._populate_settings_api_root()

tests/conftest.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,13 @@ def create_session(**kwargs):
257257
return pyfluent.launch_fluent(**kwargs)
258258

259259

260+
@pytest.fixture
261+
def new_meshing_session_wo_exit():
262+
meshing = create_session(mode=pyfluent.FluentMode.MESHING)
263+
yield meshing
264+
# Exit is intentionally avoided here. Please exit from the method using this.
265+
266+
260267
@pytest.fixture
261268
def new_meshing_session():
262269
meshing = create_session(mode=pyfluent.FluentMode.MESHING)
@@ -277,6 +284,14 @@ def watertight_workflow_session(new_meshing_session):
277284
return new_meshing_session
278285

279286

287+
@pytest.fixture
288+
def watertight_workflow_session_wo_exit(new_meshing_session_wo_exit):
289+
new_meshing_session_wo_exit.workflow.InitializeWorkflow(
290+
WorkflowType="Watertight Geometry"
291+
)
292+
return new_meshing_session_wo_exit
293+
294+
280295
@pytest.fixture
281296
def fault_tolerant_workflow_session(new_meshing_session):
282297
new_meshing_session.workflow.InitializeWorkflow(
@@ -285,6 +300,14 @@ def fault_tolerant_workflow_session(new_meshing_session):
285300
return new_meshing_session
286301

287302

303+
@pytest.fixture
304+
def fault_tolerant_workflow_session_wo_exit(new_meshing_session_wo_exit):
305+
new_meshing_session_wo_exit.workflow.InitializeWorkflow(
306+
WorkflowType="Fault-tolerant Meshing"
307+
)
308+
return new_meshing_session_wo_exit
309+
310+
288311
@pytest.fixture
289312
def mixing_elbow_watertight_pure_meshing_session(
290313
new_pure_meshing_session, mixing_elbow_geometry_filename

tests/test_cad_to_post_ftm.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@
4949
@pytest.mark.nightly
5050
@pytest.mark.codegen_required
5151
def test_exhaust_system(
52-
fault_tolerant_workflow_session, exhaust_system_geometry_filename
52+
fault_tolerant_workflow_session_wo_exit, exhaust_system_geometry_filename
5353
):
54-
meshing_session = fault_tolerant_workflow_session
54+
meshing_session = fault_tolerant_workflow_session_wo_exit
5555
workflow = meshing_session.workflow
5656

5757
_ = partial(assign_task_arguments, workflow=workflow, check_state=True)
@@ -545,3 +545,5 @@ def test_exhaust_system(
545545
)
546546

547547
###############################################################################
548+
549+
solver_session.exit()

tests/test_cad_to_post_wtm.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@
5050

5151
@pytest.mark.nightly
5252
@pytest.mark.codegen_required
53-
def test_mixing_elbow(watertight_workflow_session, mixing_elbow_geometry_filename):
54-
meshing_session = watertight_workflow_session
53+
def test_mixing_elbow(
54+
watertight_workflow_session_wo_exit, mixing_elbow_geometry_filename
55+
):
56+
meshing_session = watertight_workflow_session_wo_exit
5557
workflow = meshing_session.workflow
5658

5759
assign_task_args = partial(
@@ -270,3 +272,4 @@ def test_mixing_elbow(watertight_workflow_session, mixing_elbow_geometry_filenam
270272
)
271273

272274
###############################################################################
275+
solver_session.exit()

tests/test_fluent_session.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,13 +231,14 @@ def _freeze_fluent(s):
231231
tmp_thread = threading.Thread(target=_freeze_fluent, args=(session,), daemon=True)
232232
tmp_thread.start()
233233
tmp_thread.join(5)
234+
fl_connection = session._fluent_connection
234235
if tmp_thread.is_alive():
235236
session.exit(timeout=1, timeout_force=True)
236237
tmp_thread.join()
237238
else:
238239
raise Exception("Test should have temporarily frozen Fluent, but did not.")
239240

240-
assert session._fluent_connection.wait_process_finished(wait=5)
241+
assert fl_connection.wait_process_finished(wait=5)
241242

242243

243244
@pytest.mark.fluent_version(">=23.1")
@@ -276,16 +277,19 @@ def test_fluent_exit(monkeypatch: pytest.MonkeyPatch):
276277

277278
def test_fluent_exit_wait():
278279
session1 = pyfluent.launch_fluent()
280+
fl_connection1 = session1._fluent_connection
279281
session1.exit()
280-
assert not session1._fluent_connection.wait_process_finished(wait=0)
282+
assert not fl_connection1.wait_process_finished(wait=0)
281283

282284
session2 = pyfluent.launch_fluent()
285+
fl_connection2 = session2._fluent_connection
283286
session2.exit(wait=60)
284-
assert session2._fluent_connection.wait_process_finished(wait=0)
287+
assert fl_connection2.wait_process_finished(wait=0)
285288

286289
session3 = pyfluent.launch_fluent()
290+
fl_connection3 = session3._fluent_connection
287291
session3.exit(wait=True)
288-
assert session3._fluent_connection.wait_process_finished(wait=0)
292+
assert fl_connection3.wait_process_finished(wait=0)
289293

290294
with pytest.raises(WaitTypeError):
291295
session4 = pyfluent.launch_fluent()

tests/test_meshing_workflow.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,3 +441,22 @@ def test_setting_none_type_tasks(new_meshing_session):
441441
meshing.workflow.TaskObject["Describe Overset Features"].CommandName()
442442
== "DescribeOversetFeatures"
443443
)
444+
445+
446+
def test_inaccessible_meshing_attributes_after_switching_to_solver(
447+
new_meshing_session_wo_exit,
448+
):
449+
meshing = new_meshing_session_wo_exit
450+
assert meshing.is_active() is True
451+
solver = meshing.switch_to_solver()
452+
assert solver.is_active() is True
453+
assert meshing.is_active() is False
454+
with pytest.raises(AttributeError):
455+
# 'switched' attribute is not there in Meshing.
456+
assert meshing.switched
457+
assert dir(meshing) == ["is_active"]
458+
del meshing
459+
assert solver.is_active() is True
460+
solver.exit()
461+
assert solver.is_active() is False
462+
assert dir(solver) == ["is_active"]

tests/test_meshingmode/test_meshing_launch.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,20 @@ def test_launch_pure_meshing(mixing_elbow_watertight_pure_meshing_session):
102102

103103
@pytest.mark.fluent_version("latest")
104104
@pytest.mark.codegen_required
105-
def test_launch_meshing_and_switch(new_meshing_session):
106-
meshing = new_meshing_session
107-
assert not meshing._switched
108-
_ = meshing.switch_to_solver()
109-
assert meshing._switched
110-
assert not meshing.tui
111-
assert not meshing.meshing
112-
assert not meshing.workflow
113-
assert not meshing.watertight
105+
def test_launch_meshing_and_switch(new_meshing_session_wo_exit):
106+
meshing = new_meshing_session_wo_exit
107+
assert meshing.is_active() is True
108+
solver = meshing.switch_to_solver()
109+
assert meshing.is_active() is False
110+
for attr in ("tui", "meshing", "workflow", "watertight"):
111+
with pytest.raises(AttributeError):
112+
getattr(meshing, attr)
113+
solver.exit()
114114

115115

116116
@pytest.mark.fluent_version("latest")
117117
@pytest.mark.codegen_required
118-
def test_meshing_streaming_and_switch(new_meshing_session):
118+
def test_meshing_streaming_and_switch(new_meshing_session_wo_exit):
119119

120120
def on_case_loaded(session, event_info):
121121
on_case_loaded.loaded = True
@@ -127,7 +127,7 @@ def on_trancript(transcript):
127127

128128
on_trancript.called = False
129129

130-
meshing = new_meshing_session
130+
meshing = new_meshing_session_wo_exit
131131

132132
meshing.events.register_callback(MeshingEvent.CASE_LOADED, on_case_loaded)
133133
meshing.transcript.register_callback(on_trancript)
@@ -145,6 +145,7 @@ def on_trancript(transcript):
145145

146146
assert not on_trancript.called
147147
assert not on_case_loaded.loaded
148+
solver.exit()
148149

149150

150151
@pytest.mark.fluent_version("latest")

0 commit comments

Comments
 (0)