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 |
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 |
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: |
Accepted strings |
Use braces: |
Optional values |
Use |
Path-like values |
Prefer |
Arrays |
Include shape and units: |
Dictionaries |
Document important keys under the parameter description or in |
Forwarded kwargs |
Explain where they go: |
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_runreturns;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_dictor 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
PipelineandStepfrompycsamt.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 |
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 |
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 |
|---|---|
|
Move the content under |
|
Move the content under |
|
Move the content under |
|
Move the variable list under |
|
Make the underline exactly as long as the heading text, or remove the custom heading. |
|
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.