Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@
description: Issues fixed in Ansys Mechanical version 271
color: FFDD33

- name: fixed-in-next-release
description: Issues fixed in the upcoming Ansys Mechanical release
color: FFEE66

- name: 24R2
description: Bug observed in PyMechanical with Mechanical version 24R2
color: 804000
Expand Down
1 change: 1 addition & 0 deletions doc/changelog.d/1564.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add reuse flag to override gallery flag
3 changes: 2 additions & 1 deletion doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
import ansys.mechanical.core as pymechanical
from ansys.mechanical.core.embedding.initializer import SUPPORTED_MECHANICAL_EMBEDDING_VERSIONS

# necessary when building the sphinx gallery
# Documentation gallery: enables Sphinx-Gallery and embedded ``App`` instance
# reuse across gallery scripts. Per-constructor opt-out: ``App(..., reuse_instance=True)``.
pymechanical.BUILDING_GALLERY = True

# Ensure that offscreen rendering is used for docs generation
Expand Down
1 change: 1 addition & 0 deletions doc/source/user_guide/embedding/globals.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ not for the ``globals`` argument of the ``App`` class:

app = App()
app.update_globals(globals(), False)

14 changes: 13 additions & 1 deletion src/ansys/mechanical/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,19 @@
from ansys.mechanical.core.pool import LocalMechanicalPool

BUILDING_GALLERY = False
"""Whether or not to build gallery examples."""
"""Control documentation gallery behavior and embedded-app instance sharing.

When ``True``:

- Sphinx enables the example gallery during documentation builds (see ``doc/source/conf.py``).
- For the embedded :class:`~ansys.mechanical.core.embedding.app.App`, multiple
``App()`` constructions reuse one underlying Mechanical instance so examples can
run without exhausting resources.

To opt out of embedded instance sharing for a single ``App`` while leaving this
flag unchanged (for example when ``BUILDING_GALLERY`` is set globally), pass
``reuse_instance=True`` to the constructor.
"""

__all__ = [
"__version__",
Expand Down
16 changes: 14 additions & 2 deletions src/ansys/mechanical/core/embedding/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ class App:
feature_flags : list, optional
List of feature flag names to enable. Default is [].
Available flags include: 'ThermalShells', 'MultistageHarmonic', 'CPython'.
reuse_instance : bool, optional
When ``True``, gallery instance sharing is skipped for this constructor,
so initialization follows the same path as when the module-level
``BUILDING_GALLERY`` flag is ``False``. Use this to opt out of gallery
sharing without assigning ``pymechanical.BUILDING_GALLERY = False`` (for
example when the flag is set globally for documentation builds).
Default is ``False``.

Examples
--------
Expand Down Expand Up @@ -236,12 +243,16 @@ def __init__(
self,
db_file: str | None = None,
private_appdata: bool = False,
*,
reuse_instance: bool = False,
**kwargs: typing.Any,
) -> None:
"""Construct an instance of the mechanical Application."""
global INSTANCES
from ansys.mechanical.core import BUILDING_GALLERY

use_gallery_sharing = BUILDING_GALLERY and not reuse_instance

self._enable_logging = kwargs.get("enable_logging", True)
if self._enable_logging:
self._log = LOG
Expand Down Expand Up @@ -270,7 +281,7 @@ def __init__(

# If the building gallery flag is set, we need to share the instance
# This can apply to running the `make -C doc html` command
if BUILDING_GALLERY:
if use_gallery_sharing:
if len(INSTANCES) != 0:
# Get the first instance of the app
instance: App = INSTANCES[0]
Expand Down Expand Up @@ -668,7 +679,8 @@ def _share(self, other) -> None:
"""Shares the state of self with other.

Other is another instance of App.
This is used when the BUILDING_GALLERY flag is on.
This is used when the BUILDING_GALLERY flag is on and the constructor
was not called with ``reuse_instance=True``.
In that mode, multiple instance of App are used, but
they all point to the same underlying application
object. Because of that, special care needs to be
Expand Down
13 changes: 12 additions & 1 deletion tests/embedding/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,8 @@ def test_building_gallery(pytestconfig, run_subprocess, rootdir):
"""Test for building gallery check.

When building the gallery, each example file creates another instance of the app.
When the BUILDING_GALLERY flag is enabled, only one instance is kept.
When the BUILDING_GALLERY flag is enabled, only one instance is kept (unless a
constructor uses ``reuse_instance=True`` to opt out of sharing).
This is to test the bug fixed in https://github.com/ansys/pymechanical/pull/784
and will fail on PyMechanical version 0.11.0
"""
Expand All @@ -350,6 +351,16 @@ def test_building_gallery(pytestconfig, run_subprocess, rootdir):
assert "Multiple App launched with building gallery flag on" in stdout


@pytest.mark.embedding_scripts
def test_reuse_instance_bypasses_gallery_sharing(pytestconfig, run_subprocess, rootdir):
"""When BUILDING_GALLERY is True, ``reuse_instance=True`` skips the sharing path."""
version = pytestconfig.getoption("ansys_version")
script = Path(rootdir) / "tests" / "scripts" / "reuse_instance_test.py"
_process, stdout, stderr = run_subprocess([sys.executable, str(script), version])
stdout = stdout.decode()
assert "reuse_instance bypassed gallery sharing as expected" in stdout


@pytest.mark.embedding
def test_shims_import_material(embedded_app, assets):
"""Test deprecation warning for shims import material."""
Expand Down
1 change: 1 addition & 0 deletions tests/embedding/test_globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ def test_globals_kwarg_building_gallery(run_subprocess, pytestconfig, rootdir):

Test ViewOrientationType exists and messages are printed when
BUILDING_GALLERY is True and globals are updated during the app initialization.
(Gallery instance sharing can be skipped per ``App`` via ``reuse_instance=True``.)
"""
version = pytestconfig.getoption("ansys_version")
embedded_py = Path(rootdir) / "tests" / "scripts" / "run_embedded_app.py"
Expand Down
43 changes: 43 additions & 0 deletions tests/scripts/reuse_instance_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Copyright (C) 2022 - 2026 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

"""Exercise App(reuse_instance=...) when BUILDING_GALLERY is True."""

import sys

import ansys.mechanical.core as pymechanical


if __name__ == "__main__":
version = int(sys.argv[1])
pymechanical.BUILDING_GALLERY = True

_ = pymechanical.App(version=version)
try:
pymechanical.App(version=version, reuse_instance=True)
except RuntimeError as exc:
if "Cannot have more than one embedded mechanical instance" in str(exc):
print("reuse_instance bypassed gallery sharing as expected")
else:
raise
else:
raise AssertionError("Expected RuntimeError for second App with reuse_instance=True")
Loading