Source code for pybmodes.workflows.examples

# Copyright 2024-2026 Jae Hoon Seo
# Marine Structural Mechanics and Integrity Lab (SMI Lab), Inha University
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""``pybmodes examples --copy`` workflow as a typed library function.

Vendors the bundled ``sample_inputs/`` and / or ``reference_decks/``
trees out of the installed :mod:`pybmodes._examples` package into a
user-supplied destination directory. Works for wheel installs (where
the bundles are package-data) and editable source installs (where
they're a literal ``src/pybmodes/_examples/`` directory) — the same
resolver locates ``pybmodes.__file__`` and reads from the sibling
``_examples`` folder.
"""
from __future__ import annotations

import pathlib
import shutil
from dataclasses import dataclass, field
from typing import Literal

from pybmodes.workflows._base import WorkflowResult

# bundle-name -> (sub-directory under pybmodes/_examples/, human description)
_EXAMPLE_BUNDLES: dict[str, tuple[str, str]] = {
    "samples": (
        "sample_inputs",
        "analytical-reference cases + 11 RWT samples",
    ),
    "decks": (
        "reference_decks",
        "6 patched ElastoDyn decks (land + monopile + floating)",
    ),
}

Kind = Literal["all", "samples", "decks"]


def _resolve_examples_root() -> pathlib.Path:
    """Locate ``pybmodes/_examples/`` on the installed package.

    Both wheel-installed and source-installed callers find the
    bundle tree alongside the imported :mod:`pybmodes` package.
    """
    import pybmodes

    pkg_dir = pathlib.Path(pybmodes.__file__).resolve().parent
    return pkg_dir / "_examples"


[docs] @dataclass class ExamplesResult(WorkflowResult): """Result of :func:`run_examples_copy`. Attributes ---------- dest : pathlib.Path | None The destination directory the bundles were copied into. ``None`` if the workflow short-circuited before copying anything (e.g. no requested bundles found in the installed package — exit code 2). copied : list[pathlib.Path] Absolute paths to each bundle successfully copied (typically ``dest / "sample_inputs"`` and / or ``dest / "reference_decks"``). skipped : list[str] Bundle names (``"samples"`` / ``"decks"``) that were requested but not found on disk inside the installed package. Each entry produces a warning line in ``messages`` / a ``WARN:`` prefix. """ dest: pathlib.Path | None = None copied: list[pathlib.Path] = field(default_factory=list) skipped: list[str] = field(default_factory=list)
[docs] def run_examples_copy( dest: str | pathlib.Path, *, kind: Kind = "all", force: bool = False, ) -> ExamplesResult: """Copy bundled example trees out of the installed pyBmodes package. Library entry point for :command:`pybmodes examples --copy DIR`. Parameters ---------- dest : str or pathlib.Path Destination directory. Created if missing. Existing sub-bundles in this directory are an error unless ``force=True``. kind : {"all", "samples", "decks"}, default "all" Which bundle(s) to copy. ``"all"`` copies both ``sample_inputs/`` and ``reference_decks/``; ``"samples"`` or ``"decks"`` selects only that one. force : bool, default False Overwrite existing destination sub-directories rather than erroring. Useful in CI scripts that re-vendor on every run. Returns ------- ExamplesResult Carries the destination, the list of copied bundle paths, and any skipped bundle names. Exit code is ``0`` on success, ``2`` if no requested bundles were found in the installed package or if a destination existed without ``force=True``. """ examples_root = _resolve_examples_root() requested: list[str] = ( ["samples", "decks"] if kind == "all" else [kind] ) selected: list[tuple[str, pathlib.Path]] = [] skipped: list[str] = [] for name in requested: subdir = _EXAMPLE_BUNDLES[name][0] src = examples_root / subdir if src.is_dir(): selected.append((name, src)) else: skipped.append(name) if not selected: return ExamplesResult( exit_code=2, errors=[ "error: example bundles not found inside the " "installed pybmodes package.", f" looked under: {examples_root}", " expected the wheel to ship " "`pybmodes/_examples/sample_inputs/` and " "`pybmodes/_examples/reference_decks/` as package " "data; if you installed from a wheel and the " "directories are absent, the wheel is malformed. " "From a source checkout run `pip install -e .` " "from the repo root and retry.", ], skipped=skipped, ) messages: list[str] = [] errors: list[str] = [] if skipped: errors.append( f"warning: skipping bundle(s) not found on disk: " f"{', '.join(skipped)}" ) dest_root = pathlib.Path(dest).resolve() dest_root.mkdir(parents=True, exist_ok=True) copied: list[pathlib.Path] = [] for name, src in selected: target = dest_root / src.name if target.exists(): if not force: return ExamplesResult( exit_code=2, dest=dest_root, copied=copied, skipped=skipped, messages=messages, errors=[ *errors, f"error: destination already exists: {target}", " pass force=True (CLI: --force) to " "overwrite, or pick an empty directory.", ], ) shutil.rmtree(target) shutil.copytree(src, target) copied.append(target) messages.append(f"copied {name}: {src} -> {target}") return ExamplesResult( exit_code=0, messages=messages, errors=errors, dest=dest_root, copied=copied, skipped=skipped, )