# Apps and manifests A PlayMolecule "app" is a container plus a JSON manifest. The container holds the science (the actual `proteinprepare` Python code, for example). The manifest declares everything PlayMolecule needs to expose that container as a typed Python function: parameters, defaults, expected outputs, resource requirements, and built-in tests. This page explains what's in a manifest and how PlayMolecule turns it into the Python surface you import. ## Anatomy of a manifest A manifest is a JSON document with these top-level keys. Annotated with `…` for omitted parts (not real JSON): ```text { "container_config": { "name": "ProteinPrepare", "version": "1", "condaenvs": [...] }, "meta_keywords": ["preparation", "protein", ...], "citations": [...], "files": { "tests/3ptb.pdb": "/app/files/tests/3ptb.pdb", ... }, "functions": [ { "function": "proteinprepare.apps.proteinprepare.app.main", "env": "base", "resources": { "ncpu": 1, "ngpu": 0 }, "outputs": { "output.pdb": "...", "details.csv": "...", "pka_plot.png": "..." }, "params": [ { "name": "outdir", "type": "Path", "mandatory": true, ... }, { "name": "pdbid", "type": "str", "mandatory": false, ... }, { "name": "pH", "type": "float", "value": 7.2, ... }, ... ], "tests": { "simple": { "description": "Prepare 3PTB structure from RCSB", "arguments": { "pdbid": "3ptb" }, "expected_outputs": ["output.pdb", "details.csv", "pka_plot.png"] }, ... }, "examples": ["proteinprepare(outdir='./test', pdbid='3ptb').run()"], "description": "ProteinPrepare prepares proteins (and nucleic acids)" } ] } ``` Each entry in `functions` becomes one callable on the app module. ## How a manifest becomes a Python function When a manifest is loaded, PlayMolecule assembles a Python callable for each entry in `functions`, plus a set of module-level attributes on the app's version submodule: **On the dynamic function itself:** 1. **Signature** — `params` is converted into an `inspect.Signature`. Each param's `type`, `nargs`, `mandatory`, and default `value` map onto `inspect.Parameter` attributes. The function then *binds* incoming kwargs against that signature on every call, which is how PlayMolecule raises a typed `TypeError` when you mistype `pdbi` for `pdbid`. 2. **Docstring** — `description`, `params`, `outputs`, and `examples` are formatted into a NumPy-style docstring so `help(app)` and `app?` work without any extra wiring. 3. **Tests** — each entry under `tests` becomes a callable test attribute accessible as `app.tests..run()`. 4. **Manifest metadata** — the per-function manifest entry is attached as `app.__manifest__`. **On the version submodule (shared by every function in that version):** 5. **Artifacts / files** — entries under `artifacts` (or the older synonym `datasets`) and the `files` block become attributes on the submodule itself: `someapp.v1.artifacts.`, `someapp.v1.files`. The full app manifest is also attached at the submodule level as `someapp.v1.__manifest__`. The end result: at the version submodule `playmolecule.apps.proteinprepare.v1` you get a callable `proteinprepare` (the function), plus `artifacts`, `datasets`, `files`, and `__manifest__` — everything derived from one JSON file. ## Versions A given app can ship multiple manifests, one per version. They appear as parallel submodules: ```text playmolecule.apps.proteinprepare # alias for latest playmolecule.apps.proteinprepare.v1 # explicit playmolecule.apps.proteinprepare.v2 # explicit ``` The unqualified symbol is set at import time by natural-sort over the version strings (`v10` sorts after `v9`). ## Multi-function apps `functions` is a list. A single manifest can expose several entry points — for example, an app that does both "prepare" and "validate". Each one shows up as its own attribute: ```python from playmolecule.apps import someapp someapp.prepare(outdir="out", ...) someapp.validate(outdir="out", ...) ``` A function literally named `main` is exposed under the app name instead, so you can write `someapp(...)` rather than `someapp.main(...)`. That's why `proteinprepare(...)` works — the manifest's actual function is named `main`. ## Where to put a manifest - **Docker registry** — embedded as an image label. - **HTTP backend** — served by the backend's `/apps/manifests` endpoint. - **Local registry** *(developer use)* — `/apps///.json` (where `` is the path after `local:` in `PM_REGISTRIES`), alongside a `run.sh` and any payload files. Useful when you're authoring your own app and want to iterate on the manifest without publishing. The three discovery paths produce the same `app_versions` data shape (see [Architecture](architecture.md)), so the resulting Python surface is identical regardless of source. ## See also - [Architecture](architecture.md) - [List and inspect apps](../howto/list-and-inspect-apps.md) - [Run built-in app tests](../howto/run-built-in-app-tests.md) - {py:func}`~playmolecule.describe_apps`