Skip to content

Wrapper

The wrapper module provides low-level wrappers for external tools.

Base

Base Wrapper class for external package wrappers.

Wrappers are minimal shells around external binaries and CLIs. They are peer-level to Adapters: - Adapter: Keeps MolPy ↔ external data structures in sync - Wrapper: Encapsulates external package invocation (binaries, CLIs, scripts)

Wrappers MUST NOT contain high-level domain logic.

Wrapper dataclass

Wrapper(name, exe, workdir=None, env_vars=dict(), env=None, env_manager=None)

Bases: ABC

Minimal base class for external tool wrappers.

check

check()

Validate the wrapper configuration.

Returns:

Type Description
str

The resolved executable path.

Raises:

Type Description
FileNotFoundError

if the executable is not found.

Source code in src/molpy/wrapper/base.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
def check(self) -> str:
    """Validate the wrapper configuration.

    Returns:
        The resolved executable path.

    Raises:
        FileNotFoundError: if the executable is not found.
    """

    resolved = self.resolve_executable()
    if resolved is None:
        raise FileNotFoundError(
            f"Executable '{self.exe}' for {type(self).__name__} is not available. "
            "Install the tool and ensure it is on PATH, or configure env/env_manager, "
            "or set wrapper.exe to an absolute path."
        )
    return resolved

is_available

is_available()

Return True if the executable can be resolved on this machine.

Source code in src/molpy/wrapper/base.py
182
183
184
185
def is_available(self) -> bool:
    """Return True if the executable can be resolved on this machine."""

    return self.resolve_executable() is not None

resolve_executable

resolve_executable()

Resolve the configured executable to an absolute path if possible.

Returns:

Type Description
str | None

The resolved executable path, or None if it cannot be found.

Source code in src/molpy/wrapper/base.py
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
def resolve_executable(self) -> str | None:
    """Resolve the configured executable to an absolute path if possible.

    Returns:
        The resolved executable path, or None if it cannot be found.
    """

    exe_path = Path(self.exe)
    if exe_path.is_file():
        return str(exe_path)

    resolved = self._resolve_executable_via_env()
    if resolved is not None:
        return resolved

    # For venv/pip we already injected PATH in _merged_env(); use it here too.
    merged = self._merged_env()
    return shutil.which(self.exe, path=merged.get("PATH"))

Antechamber

Wrapper for the 'antechamber' CLI.

Higher-level workflow decisions belong in compute nodes.

AntechamberWrapper dataclass

AntechamberWrapper(name, exe='antechamber', workdir=None, env_vars=dict(), env=None, env_manager=None)

Bases: Wrapper

Wrapper for the 'antechamber' CLI.

read_antechamber_output

read_antechamber_output(path)

Read antechamber-generated output (.ac or .mol2) into a Frame.

Source code in src/molpy/wrapper/antechamber.py
88
89
90
91
92
93
94
95
96
97
98
def read_antechamber_output(path: Path) -> Frame:
    """Read antechamber-generated output (.ac or .mol2) into a Frame."""

    suffix = path.suffix.lower()

    if suffix == ".ac":
        return read_amber_ac(path)
    if suffix == ".mol2":
        return read_mol2(path)

    raise ValueError(f"Unsupported antechamber output format: {path}")

write_antechamber_input_pdb

write_antechamber_input_pdb(path, atomistic)

Write a PDB suitable as antechamber input.

This intentionally avoids using Atomistic.to_frame() which may enforce bond typing constraints that are irrelevant for PDB inputs.

Source code in src/molpy/wrapper/antechamber.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def write_antechamber_input_pdb(path: Path, atomistic: Atomistic) -> None:
    """Write a PDB suitable as antechamber input.

    This intentionally avoids using Atomistic.to_frame() which may enforce
    bond typing constraints that are irrelevant for PDB inputs.
    """

    ensure_parent_dir(path)

    atoms = list(atomistic.atoms)
    n_atoms = len(atoms)
    ids = np.arange(n_atoms, dtype=int) + 1

    def _get_str(key: str, default: str) -> list[str]:
        out: list[str] = []
        for a in atoms:
            v = a.get(key)
            out.append(default if v is None else str(v))
        return out

    def _get_int(key: str, default: int) -> np.ndarray:
        out: list[int] = []
        for a in atoms:
            v = a.get(key)
            out.append(default if v is None else int(v))
        return np.array(out, dtype=int)

    xs = np.array([float(a.get("x") or 0.0) for a in atoms], dtype=float)
    ys = np.array([float(a.get("y") or 0.0) for a in atoms], dtype=float)
    zs = np.array([float(a.get("z") or 0.0) for a in atoms], dtype=float)

    frame = Frame()
    frame["atoms"] = Block.from_dict(
        {
            "x": xs,
            "y": ys,
            "z": zs,
            "id": ids,
            "name": np.array(_get_str("name", "X"), dtype=object),
            "resName": np.array(_get_str("resName", "MOL"), dtype=object),
            "resSeq": _get_int("resSeq", 1),
            "chainID": np.array(_get_str("chainID", "A"), dtype=object),
            "element": np.array(_get_str("element", "X"), dtype=object),
        }
    )

    write_pdb(path, frame)

Prepgen

Wrapper for the 'prepgen' CLI.

Higher-level workflow decisions belong in compute nodes.

PrepgenWrapper dataclass

PrepgenWrapper(name, exe='prepgen', workdir=None, env_vars=dict(), env=None, env_manager=None)

Bases: Wrapper

Wrapper for the 'prepgen' CLI.

Tleap

Wrapper for the 'tleap' binary.

This wrapper runs tleap on a generated script file.