Docstring Style#

pyCSAMT uses NumPy-style docstrings for public Python APIs. The goal is not only pretty generated reference pages. Good docstrings are part of the v2 API contract: they explain scientific assumptions, units, array shapes, optional dependencies, agent input/output schemas, and workflow reproducibility.

This page defines the expected style for public functions, classes, agents, pipeline steps, CLI helpers, inversion objects, and compatibility aliases.

Why docstrings matter#

pyCSAMT users often work across notebooks, scripts, CLI workflows, agents, and generated API pages. A docstring may be read in any of these places:

  • help(pycsamt.api.read_edis) in a Python shell;

  • an IDE hover card;

  • an autosummary-generated API page;

  • a tutorial that links to the API reference;

  • a developer review of a new public function;

  • an agent or pipeline page explaining structured outputs.

For this reason, public docstrings must be complete enough to stand alone, but short enough that the API reference remains scannable.

Relationship to the API policy#

The API Policy defines which names are public. This page defines how those public names should be documented.

In short:

  • public stable APIs need complete NumPy-style docstrings;

  • public experimental APIs need complete docstrings plus a stability note;

  • private helpers may have short implementation docstrings;

  • deprecated aliases must document the replacement and removal plan.

General rules#

Use these rules everywhere unless a more specific section below says otherwise.

Rule

Requirement

Start with one sentence

The first line should describe the object in plain language and fit on one line when reasonable.

Use NumPy sections

Prefer recognized sections such as Parameters, Returns, Attributes, Raises, Warns, Notes, Examples, and See Also.

Be explicit about science

Document units, coordinate systems, tensor conventions, frequency versus period, resistivity units, and array shapes.

Document optional dependencies

Name optional packages required at execution time, not only at install time.

Prefer keyword clarity

Public functions should document defaults and accepted strings.

Keep examples runnable

Examples should use public imports and realistic small workflows.

Avoid private import paths

Examples should not teach users to import from underscored modules.

No custom top-level sections

Do not add headings such as Input Keys or Output Data Keys at the same level as Parameters. Put those details inside Notes.

Recognized section order#

Use this order for most public docstrings.

 1One-line summary.
 2
 3Optional extended summary.  Explain the scientific purpose, not the
 4implementation line by line.
 5
 6Parameters
 7----------
 8name : type
 9    Description.
10
11Returns
12-------
13type
14    Description.
15
16Raises
17------
18ErrorType
19    When and why this is raised.
20
21Warns
22-----
23WarningType
24    When and why this warning is emitted.
25
26See Also
27--------
28related_function : Short description.
29
30Notes
31-----
32Scientific assumptions, units, shapes, algorithms, or structured schemas.
33
34Examples
35--------
36>>> from pycsamt.api import read_edis
37>>> survey = read_edis("data/edis")

Not every docstring needs every section. For a simple helper, a summary, Parameters, Returns, and a short example may be enough.

Sections to avoid#

numpydoc warns when it sees unknown sections. Avoid custom top-level section headings like these:

 1Input Keys
 2----------
 3...
 4
 5Output Data Keys
 6----------------
 7...
 8
 9Resolution Rules
10----------------
11...
12
13Recognised Variables
14--------------------
15...

Instead, put the same content under Notes using paragraphs, bullet lists, or simple tables.

Preferred:

 1Notes
 2-----
 3Input mapping keys:
 4
 5``sites`` : Sites
 6    Survey object to process.
 7``path`` : str or path-like
 8    Directory or file used when ``sites`` is not supplied.
 9
10Output data keys:
11
12``qc_table`` : pandas.DataFrame
13    Per-station quality metrics.
14``n_flagged`` : int
15    Number of stations that failed the configured thresholds.

Function docstrings#

A public function docstring must explain what the function does, what it accepts, what it returns, and what can fail.

Template:

 1def read_edis(
 2    sources,
 3    *,
 4    recursive=True,
 5    strict=False,
 6    on_dup="replace",
 7    progress="auto",
 8    verbose=0,
 9):
10    """Read EDI files and return a public survey view.
11
12    Parameters
13    ----------
14    sources : path-like, sequence of path-like, or file-like
15        EDI file, directory, glob-compatible collection, or explicit
16        sequence of EDI paths.
17    recursive : bool, default=True
18        If True, search directories recursively.
19    strict : bool, default=False
20        If True, raise when a file cannot be parsed.  If False, keep
21        successful files and record parse errors on the parser metadata.
22    on_dup : {"replace", "keep"}, default="replace"
23        Policy used when several files resolve to the same station name.
24    progress : bool or {"auto"}, default="auto"
25        Whether to display progress while reading many files.
26    verbose : int, default=0
27        Verbosity level passed to the low-level parser.
28
29    Returns
30    -------
31    APISurvey
32        Public survey wrapper containing an EDI collection and parser
33        metadata.
34
35    Raises
36    ------
37    OSError
38        If an input path cannot be read.
39    ValueError
40        If ``on_dup`` is not a recognized duplicate policy.
41
42    Notes
43    -----
44    The returned object is the public view layer used by notebooks, CLI
45    commands, and higher-level workflows.  Low-level parser details remain
46    available through metadata when needed.
47
48    Examples
49    --------
50    >>> from pycsamt.api import read_edis
51    >>> survey = read_edis("data/willy/edis")
52    >>> survey.name
53    'edi_survey'
54    """

Parameter style#

Follow these conventions in Parameters.

Situation

Style

Default values

Put the default in the type line: verbose : int, default=0.

Accepted strings

Use braces: method : {"composite", "snr"}, default="composite".

Optional values

Use type or None or optional consistently.

Path-like values

Prefer path-like unless the function truly requires pathlib.Path.

Arrays

Include shape and units: rho : array-like of shape (n_freq, n_site).

Dictionaries

Document important keys under the parameter description or in Notes.

Forwarded kwargs

Explain where they go: **kwargs : dict followed by the callee.

Good examples:

1period_range : tuple of float, optional
2    Two-element ``(min_period, max_period)`` window in seconds.
3components : {"xy", "yx", "det", "ssq"}, default="det"
4    Impedance component or invariant used to compute apparent resistivity.
5station_spacing : float, optional
6    Station spacing in metres.  Required when station coordinates are not
7    available.

Return style#

Prefer structured return documentation. Avoid vague lines such as Returns result.

Examples:

1Returns
2-------
3APISurvey
4    Survey wrapper containing parsed EDI files, station metadata, and parser
5    diagnostics.
1Returns
2-------
3rho : ndarray of shape (n_depth, n_station)
4    Estimated resistivity section in ohm-m.
5depth : ndarray of shape (n_depth,)
6    Cell-centre depths in metres.

For multiple named return values, either return a dataclass/result object or document each output name. Do not return long positional tuples from new public APIs unless there is a compatibility reason.

Units, shapes, and conventions#

Scientific docstrings must record the assumptions that affect interpretation.

Document:

  • frequency in hertz or period in seconds;

  • apparent resistivity in ohm-m;

  • phase in degrees or radians;

  • distance, elevation, and depth in metres unless otherwise stated;

  • longitude/latitude in decimal degrees;

  • projected coordinates and EPSG/UTM assumptions when applicable;

  • impedance tensor component order;

  • shape names such as (n_freq, n_station) or (n_depth, n_x);

  • whether arrays are linear scale, log10 scale, or normalized.

Example:

1Notes
2-----
3``rho`` is expected in ohm-m on linear scale with shape
4``(n_frequency, n_station)``.  ``phase`` is expected in degrees with the
5same shape.  Frequencies are sorted from high to low by the reader, but
6plotting functions may display period increasing downward.

Class docstrings#

Class docstrings should document construction and user-facing state. Do not list every private attribute.

Template:

 1class ForwardConfig:
 2    """Configuration for forward electromagnetic modelling.
 3
 4    Parameters
 5    ----------
 6    dimensionality : {1, 2, 3}, default=2
 7        Model dimensionality.
 8    frequencies : array-like of float, optional
 9        Frequencies in hertz.
10    resistivity_background : float, default=100.0
11        Background resistivity in ohm-m.
12
13    Attributes
14    ----------
15    dimensionality : int
16        Validated model dimensionality.
17    frequencies : ndarray or None
18        Frequencies used by the forward solver.
19
20    Notes
21    -----
22    This object stores configuration only.  It does not run a solver or
23    import optional backend packages.
24    """

Use Attributes for values users are expected to inspect or modify. Use Notes for implementation details, algorithms, and scientific assumptions.

Dataclass docstrings#

For dataclasses, document constructor fields in Parameters when users instantiate the class directly. Document computed or post-init fields in Attributes.

Example:

 1@dataclass
 2class StepResult:
 3    """Record produced after one pipeline step runs.
 4
 5    Parameters
 6    ----------
 7    step_idx : int
 8        One-based position of the step in the pipeline.
 9    step_name : str
10        User-supplied step label.
11    step_code : str
12        Stable pipeline registry code, such as ``"NR001"``.
13
14    Attributes
15    ----------
16    error : Exception or None
17        Exception captured when the pipeline continued after a failed step.
18    """

Agent docstrings#

Agents need richer docstrings because they are both Python objects and workflow building blocks. They should document constructor parameters, execution input mapping, output mapping, LLM behavior, and examples.

Use recognized numpydoc sections. Put agent schemas inside Notes rather than custom top-level Input Keys sections.

Template:

 1class DataQCAgent(BaseAgent):
 2    """Run data quality control on an MT/AMT/CSAMT survey.
 3
 4    Parameters
 5    ----------
 6    api_key : str, optional
 7        Provider API key used for optional LLM interpretation.
 8    model : str, optional
 9        Provider model name.  If omitted, the global agent configuration is
10        used.
11    llm_provider : {"claude", "openai", "gemini"}, default="claude"
12        LLM provider used when interpretation is enabled.
13    method : {"composite", "presence", "snr", "spatial"}, default="composite"
14        Confidence scoring method.
15    min_frac_ok : float, default=0.6
16        Minimum fraction of acceptable frequencies required for a station to
17        pass QC.
18
19    Returns
20    -------
21    AgentResult
22        Structured result.  The ``data`` mapping contains QC tables,
23        flagged stations, figures, and saved figure paths.
24
25    Notes
26    -----
27    Execution input mapping:
28
29    ``sites`` : Sites, optional
30        Survey object to evaluate.
31    ``path`` : path-like, optional
32        EDI directory or file used when ``sites`` is not supplied.
33    ``output_dir`` : path-like, optional
34        Directory where figures are written.
35    ``period_range`` : tuple of float, optional
36        Period window in seconds.
37
38    Output ``data`` mapping:
39
40    ``qc_table`` : pandas.DataFrame
41        Per-station metrics.
42    ``flags`` : pandas.DataFrame
43        Pass/fail flags per station.
44    ``n_flagged`` : int
45        Number of stations that failed configured thresholds.
46    ``figures`` : dict
47        Matplotlib figures keyed by plot name.
48
49    LLM interpretation is optional.  When no provider key is configured,
50    the agent still runs deterministic QC and leaves
51    ``llm_interpretation`` empty.
52
53    Examples
54    --------
55    >>> from pycsamt.agents import DataQCAgent
56    >>> agent = DataQCAgent(method="composite")
57    >>> result = agent.execute({
58    ...     "path": "data/willy/edis",
59    ...     "output_dir": "results/willy_qc",
60    ... })
61    >>> result.success
62    True
63    """

Agent examples should use public imports:

1from pycsamt.agents import DataQCAgent
2
3result = DataQCAgent().execute({
4    "path": "data/willy/edis",
5    "output_dir": "results/qc",
6})
7
8if result.success:
9    print(result.data["n_flagged"])

Coordinator and orchestrator docstrings#

Coordinator and orchestrator docstrings should describe workflow behavior, not only constructor arguments.

Document:

  • how steps are selected or registered;

  • what dry_run returns;

  • how failed steps are represented;

  • how LLM routing behaves when no key is configured;

  • which keys are required in the input mapping;

  • whether output is deterministic.

Example Notes content:

1Notes
2-----
3``dry_run=True`` returns a plan without executing workflow steps.  The plan
4includes the selected workflow name, step order, and input keys expected by
5each step.  No LLM call is required for dry-run routing when
6``workflow_type`` is supplied explicitly.

Pipeline docstrings#

Pipeline docstrings should make serialization and reproducibility clear.

For Step:

  • document accepted registry identifiers;

  • mention that parameter overrides are merged over registry defaults;

  • document serialization with to_dict or pipeline config files.

For Pipeline:

  • document accepted step declarations;

  • document input survey type;

  • document output directory behavior;

  • document error policy;

  • document generated plots/reports;

  • include a short example using Pipeline and Step from pycsamt.pipeline.

Example:

1from pycsamt.pipeline import Pipeline, Step
2
3pipe = Pipeline([
4    ("notch", Step("NR001", mains_hz=50)),
5    ("band", Step("FREQ001", fmin=1e-3, fmax=1.0)),
6])
7result = pipe.run(sites, outdir="results/willy")

Pipeline registry docstrings#

Registry objects such as step specifications should document user-facing fields. Step codes are public once documented.

Preferred style:

 1Parameters
 2----------
 3code : str
 4    Stable registry code, for example ``"NR001"``.
 5name : str
 6    Snake-case registry name.
 7category : str
 8    Processing category used by discovery helpers.
 9defaults : dict
10    JSON/YAML-serializable default parameters.
11
12Notes
13-----
14Step codes are part of the public pipeline API.  Do not rename a documented
15code without adding a deprecated alias.

Inversion and model docstrings#

Inversion APIs must document scientific and runtime assumptions carefully.

Include:

  • dimensionality;

  • supported survey type;

  • required input object or file format;

  • units and shapes;

  • mesh/model assumptions;

  • external executable or backend requirements;

  • output files;

  • reproducibility metadata.

Example:

1Notes
2-----
3The model grid uses metres in projected coordinates.  Resistivity values are
4stored in ohm-m on linear scale unless ``log10=True`` is passed.  The
5Occam2D executable is not invoked by this builder; it only writes validated
6input files.

AI model docstrings#

AI model and training docstrings should be precise about tensors and backend requirements.

Document:

  • backend: PyTorch, TensorFlow, or backend-agnostic;

  • input tensor shape and channel order;

  • target tensor shape;

  • normalization/scaling;

  • stochastic behavior and seeds;

  • checkpoint format;

  • GPU/CPU assumptions when relevant.

Example:

 1Parameters
 2----------
 3X : array-like of shape (n_sample, n_frequency, n_station, n_channel)
 4    Normalized input features.  Channel order is ``rho_xy``, ``phase_xy``,
 5    ``rho_yx``, ``phase_yx``.
 6y : array-like of shape (n_sample, n_depth, n_station)
 7    Target resistivity sections in log10 ohm-m.
 8
 9Notes
10-----
11This class imports PyTorch lazily when the model is constructed.  Importing
12:mod:`pycsamt.ai` does not require PyTorch.

CLI helper docstrings#

CLI command functions often appear in generated docs and tests. Their docstrings should describe user behavior, not Click internals.

Good:

1def run(output_dir, verbose):
2    """Run the configured inversion workflow from the command line."""

For reusable Click option decorators and parameter types, document:

  • accepted CLI spelling;

  • Python value returned by Click;

  • validation behavior;

  • examples if parsing is non-trivial.

Optional dependency docstrings#

When a public API uses optional packages, state when the dependency is needed.

Example:

1Notes
2-----
3This function imports ``geopandas`` only when GeoPackage export is
4requested.  CSV and JSON export do not require GIS extras.

Do not claim that an optional dependency is required to import pyCSAMT unless that is actually true.

Warnings, errors, and deprecations#

Use Raises for exceptions and Warns for runtime warnings.

Example:

 1Raises
 2------
 3ImportError
 4    If the selected backend requires an optional package that is not
 5    installed.
 6ValueError
 7    If ``method`` is not one of the supported correction methods.
 8
 9Warns
10-----
11FutureWarning
12    If a deprecated argument alias is used.

Deprecated APIs must include the replacement and planned removal in the docstring:

1def old_reader(path):
2    """Read EDI data using the legacy reader.
3
4    .. deprecated:: 2.0.0
5       Use :func:`pycsamt.api.read_edis` instead.  This alias is planned
6       for removal in pyCSAMT 2.2.0.
7    """

Examples style#

Examples should be short, public, and realistic.

Use doctest prompts for small expressions:

1Examples
2--------
3>>> from pycsamt.pipeline import Step
4>>> Step("NR001", mains_hz=50).to_dict()["code"]
5'NR001'

Use narrative examples in user guides when setup is too heavy for doctest. Inside docstrings, avoid examples that require large data downloads, API keys, or long-running inversion jobs.

For LLM examples, show both configured and no-LLM behavior when relevant:

1Examples
2--------
3>>> from pycsamt.agents import ContextInputAgent
4>>> agent = ContextInputAgent()  # no API key: deterministic fallback
5>>> result = agent.execute({"request": "Load EDIs and run QC"})
6>>> "workflow" in result.data["config"]
7True

Cross-references#

Use Sphinx roles for public names when the target is stable:

1See Also
2--------
3pycsamt.api.read_edis : Read EDI files into an API survey view.
4pycsamt.pipeline.Pipeline : Declarative processing workflow.
5pycsamt.agents.WorkflowOrchestratorAgent : Route natural-language requests
6    to agent workflows.

In prose, prefer:

1The returned object is compatible with
2:class:`pycsamt.pipeline.Pipeline`.

Avoid fragile references to private helpers unless the docstring is for that private helper.

Module docstrings#

Public modules should start with a short module docstring explaining the module purpose and main entry points.

Good module docstring:

1"""Public readers for EDI files and site collections.
2
3This module provides lightweight facade functions used by notebooks, CLI
4commands, and application views.  Low-level parser classes remain in the
5format-specific packages.
6"""

Private implementation modules may use shorter module docstrings, but should still explain non-obvious architecture.

Private helper docstrings#

Private helpers do not need full NumPy-style documentation. A short docstring is enough when the function is local and simple.

Example:

1def _to_figure(obj):
2    """Return a matplotlib Figure from a Figure-like or Axes-like object."""

Use fuller documentation for private helpers only when the algorithm is complex or the helper is widely used internally.

Style details#

Follow these formatting details to keep generated docs consistent.

Item

Convention

Line length

Keep source lines readable, usually below 88-96 characters.

Markup

Use double backticks for literals and Sphinx roles for references.

Bullets

Use bullets in Notes for readable lists.

Tables

Avoid complex reST tables inside docstrings. They are fragile in autosummary output.

Unicode

Prefer ASCII in new code docstrings unless the symbol is scientifically important or already used nearby.

Ellipses

Use ... in examples, not the single Unicode ellipsis.

External links

Put long links in user guides or references pages rather than API docstrings.

Common fixes for current warnings#

When Sphinx emits numpydoc warnings, the fix is usually one of these.

Warning pattern

Fix

Unknown section Input Keys

Move the content under Notes as Execution input mapping.

Unknown section Output Data Keys

Move the content under Notes as Output data mapping.

Unknown section Resolution Rules

Move the content under Notes or Examples.

Unknown section Recognised Variables

Move the variable list under Notes.

potentially wrong underline length

Make the underline exactly as long as the heading text, or remove the custom heading.

Citation is not referenced

Move citations to the project references page or cite them explicitly in prose.

Review checklist#

Use this checklist before exposing a new public API.

 1[ ] First line is a clear one-sentence summary.
 2[ ] Parameters include types, defaults, accepted strings, units, and shapes.
 3[ ] Returns section names the structured result and important fields.
 4[ ] Raises and Warns sections describe user-visible failures.
 5[ ] Notes document scientific assumptions and optional dependencies.
 6[ ] Agent input/output mappings are under Notes, not custom sections.
 7[ ] Examples use public imports and small realistic workflows.
 8[ ] Deprecated APIs include replacement and planned removal.
 9[ ] Cross-references point to stable public names.
10[ ] Sphinx builds without new numpydoc warnings from this docstring.

Minimal examples by API type#

Function:

 1def estimate_skin_depth(rho, frequency):
 2    """Estimate electromagnetic skin depth.
 3
 4    Parameters
 5    ----------
 6    rho : float or array-like
 7        Resistivity in ohm-m.
 8    frequency : float or array-like
 9        Frequency in hertz.
10
11    Returns
12    -------
13    float or ndarray
14        Skin depth in metres.
15    """

Class:

 1class SurveyMeta:
 2    """Metadata describing a survey acquisition.
 3
 4    Parameters
 5    ----------
 6    name : str
 7        Survey name.
 8    crs : str, optional
 9        Coordinate reference system, such as ``"EPSG:32629"``.
10    """

Agent:

 1class ReportAgent(BaseAgent):
 2    """Assemble workflow outputs into Markdown, HTML, or PDF reports.
 3
 4    Parameters
 5    ----------
 6    format : {"markdown", "html", "pdf"}, default="html"
 7        Report format to generate.
 8
 9    Returns
10    -------
11    AgentResult
12        Structured result containing report paths and collected warnings.
13
14    Notes
15    -----
16    Execution input mapping:
17
18    ``results`` : mapping
19        Workflow results to summarize.
20    ``output_dir`` : path-like
21        Directory where report files are written.
22    """

Pipeline step:

 1class Step:
 2    """Configured pipeline step.
 3
 4    Parameters
 5    ----------
 6    code_or_name : str
 7        Stable registry code or snake-case registry name.
 8    **params : dict
 9        Parameter overrides merged over registry defaults.
10
11    Returns
12    -------
13    Step
14        Configured step ready to place in a pipeline.
15    """

In short#

A pyCSAMT docstring should answer four questions quickly:

  • What does this object do?

  • What inputs does it require, with which units and shapes?

  • What structured result does it return?

  • What assumptions, optional dependencies, warnings, or reproducibility details must the user know?

When those answers are clear, the generated API reference becomes useful documentation rather than a mechanical listing of names.