Skip to content
Draft
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
1 change: 1 addition & 0 deletions .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,7 @@ jobs:
NUM_CORES: 1
ANSYS_WORKBENCH_LOGGING_FILTER_LEVEL: 0
BUILD_CHEATSHEET: true
PYMECHANICAL_GALLERY_PARALLEL: "4"
CONTAINER_STABLE_EXIT: ${{ needs.container-stability-check.outputs.container_stable_exit }}
run: |
. /env/bin/activate
Expand Down
1 change: 1 addition & 0 deletions doc/changelog.d/1572.test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Parallel example test
23 changes: 20 additions & 3 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@
from pathlib import Path
import warnings

# Sphinx-Gallery parallel workers are separate processes that never import this
# file. PyVista reads PYVISTA_* from the environment at package import time, so
# set these before importing pyvista; child processes inherit os.environ.
os.environ.setdefault("PYVISTA_BUILDING_GALLERY", "true")
os.environ.setdefault("PYVISTA_OFF_SCREEN", "true")
# Parallel gallery workers may reuse a process; embedded App() then needs
# BUILDING_GALLERY so a later example can attach via _share instead of erroring.
os.environ.setdefault("PYMECHANICAL_BUILDING_GALLERY", "true")

from ansys_sphinx_theme import ansys_favicon, get_version_match
import pyvista
from pyvista.plotting.utilities.sphinx_gallery import DynamicScraper
Expand All @@ -22,9 +31,6 @@
import ansys.mechanical.core as pymechanical
from ansys.mechanical.core.embedding.initializer import SUPPORTED_MECHANICAL_EMBEDDING_VERSIONS

# necessary when building the sphinx gallery
pymechanical.BUILDING_GALLERY = True

# Ensure that offscreen rendering is used for docs generation
pyvista.OFF_SCREEN = True

Expand Down Expand Up @@ -182,11 +188,21 @@
copybutton_prompt_is_regexp = True

# -- Sphinx Gallery Options ---------------------------------------------------
# Parallel example execution (separate Python subprocess per running example).
# Each subprocess can host one embedded Mechanical App, so N workers means up
# to N concurrent Mechanical processes—only use N>1 when licenses and RAM allow.
# https://sphinx-gallery.github.io/stable/configuration.html#parallel-gallery-builds
try:
_gallery_parallel = max(1, int(os.environ.get("PYMECHANICAL_GALLERY_PARALLEL", "1")))
except ValueError:
_gallery_parallel = 1

sphinx_gallery_conf = {
# convert rst to md for ipynb
"pypandoc": True,
# path to your examples scripts
"examples_dirs": ["../../examples/"],
"abort_on_example_error": True,
# path where to save gallery generated examples
"gallery_dirs": ["examples/gallery_examples"], # Pattern to search for example files
"filename_pattern": r"\.py",
Expand All @@ -202,6 +218,7 @@
# Files to ignore
"ignore_pattern": "flycheck*", # noqa: E501
"thumbnail_size": (350, 350),
"parallel": _gallery_parallel,
}

# -- Options for HTML output -------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ tests = [

doc = [
"sphinx>=8.2.3,<9",
"joblib>=1.4.0,<2",
"ansys-sphinx-theme[autoapi,changelog]>=1.7.0,<2",
"imageio-ffmpeg>=0.6.0,<1",
"imageio>=2.37.2,<3",
Expand Down
9 changes: 7 additions & 2 deletions src/ansys/mechanical/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,10 @@

from ansys.mechanical.core.pool import LocalMechanicalPool

BUILDING_GALLERY = False
"""Whether or not to build gallery examples."""
_gallery_env = os.environ.get("PYMECHANICAL_BUILDING_GALLERY", "").strip().lower()
BUILDING_GALLERY = _gallery_env in ("1", "true", "yes")
"""Gallery mode for embedded ``App`` (shared instance when a process runs multiple examples).

Parallel Sphinx-Gallery workers inherit ``PYMECHANICAL_BUILDING_GALLERY=true`` from the
environment; they do not execute ``doc/source/conf.py``.
"""
44 changes: 37 additions & 7 deletions src/ansys/mechanical/core/embedding/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,52 @@
starting in version 23.1 and on Linux starting in version 23.2
"""

from pathlib import Path
import sys


def _sys_path_has_shadow_ansys_dir(path_entry: str) -> bool:
"""Check whether *path_entry* contains a subdirectory named exactly ``Ansys``.

Such a folder is imported as a namespace package and shadows the CLR
``Ansys`` module from ``clr.AddReference``, causing
``module 'Ansys' has no attribute 'Mechanical'``.

Only the exact spelling ``Ansys`` is treated as a shadow so
``site-packages/ansys`` is not removed from ``sys.path``.
"""
base = Path(path_entry) if path_entry else Path.cwd()
try:
if not base.is_dir():
return False
return any(p.is_dir() and p.name == "Ansys" for p in base.iterdir())
except OSError:
return False


def resolve(version):
"""Resolve function for all versions of Ansys Mechanical."""
import clr # isort: skip
import System # isort: skip

clr.AddReference("Ansys.Mechanical.Embedding")
import Ansys # isort: skip

_original_sys_path = sys.path[:]
try:
sys.path[:] = [p for p in sys.path if not _sys_path_has_shadow_ansys_dir(p)]
import Ansys # isort: skip
finally:
sys.path[:] = _original_sys_path

try:
assembly_resolver = Ansys.Mechanical.Embedding.AssemblyResolver
resolve_handler = assembly_resolver.MechanicalResolveEventHandler
System.AppDomain.CurrentDomain.AssemblyResolve += resolve_handler
except AttributeError:
error_msg = """Unable to resolve Mechanical assemblies. Please ensure the following:
1. Mechanical is installed.
2. A folder with the name "Ansys" does not exist in the same directory as the script being run.
"""
raise AttributeError(error_msg)
except AttributeError as err:
error_msg = (
"Unable to resolve Mechanical assemblies. Please ensure the following:\n"
" 1. Mechanical is installed.\n"
' 2. A subdirectory named exactly "Ansys" does not exist on sys.path '
"(e.g. next to an example script), which shadows the CLR Ansys module.\n"
)
raise AttributeError(error_msg) from err
Loading