Skip to content

Commit

Permalink
Improve auto-generated docstrings of process variables (#130)
Browse files Browse the repository at this point in the history
* improve var details formatting

* update release notes
  • Loading branch information
benbovy committed Apr 14, 2020
1 parent ee17b8c commit f83796f
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 46 deletions.
2 changes: 2 additions & 0 deletions doc/whats_new.rst
Expand Up @@ -16,6 +16,8 @@ Enhancements
from model variables' metadata (:issue:`126`).
- Single-model parallelism now supports Dask's multi-processes or distributed
schedulers, although this is still limited and rarely optimal (:issue:`127`).
- Improved auto-generated docstrings of variables declared in process classes
(:issue:`130`).

Bug fixes
~~~~~~~~~
Expand Down
58 changes: 45 additions & 13 deletions xsimlab/formatting.py
@@ -1,6 +1,8 @@
"""Formatting utils and functions."""
import textwrap

import attr

from .utils import variables_dict
from .variable import VarIntent, VarType

Expand Down Expand Up @@ -85,23 +87,53 @@ def _summarize_var(var, process, col_width):


def var_details(var, max_line_length=70):
var_metadata = var.metadata.copy()
meta = var.metadata
subsections = []

if meta["description"]:
wrapped_descr = textwrap.fill(
meta["description"].capitalize(), width=max_line_length
)
subsections.append(wrapped_descr)
else:
subsections.append("No description given")

description = textwrap.fill(
var_metadata.pop("description").capitalize(), width=max_line_length
)
if not description:
description = "(no description given)"
info = [f"- type : ``{meta['var_type'].value}``"]

detail_items = [
("type", var_metadata.pop("var_type").value),
("intent", var_metadata.pop("intent").value),
]
detail_items += list(var_metadata.items())
if meta["var_type"] is VarType.FOREIGN:
ref_cls = meta["other_process_cls"]
ref_var = meta["var_name"]
info.append(f"- reference variable : :attr:`{ref_cls.__qualname__}.{ref_var}`")

info.append(f"- intent : ``{meta['intent'].value}``")

if meta.get("dims", False):
info.append("- dimensions : " + " or ".join(f"{d!r}" for d in meta["dims"]))

if meta.get("groups", False):
info.append("- groups : " + ", ".join(meta["groups"]))

if var.default != attr.NOTHING:
info.append(f"- default value : {var.default}")

if meta.get("static", False):
info.append("- static : ``True``")

subsections.append("Variable properties:\n\n" + "\n".join(info))

if meta.get("attrs", False):
subsections.append(
"Other attributes:\n\n"
+ "\n".join(f"- {k} : {v}" for k, v in meta["attrs"].items())
)

details = "\n".join([f"- {k} : {v}" for k, v in detail_items])
if meta.get("encoding", False):
subsections.append(
"Encoding options:\n\n"
+ "\n".join(f"- {k} : {v}" for k, v in meta["encoding"].items())
)

return description + "\n\n" + details + "\n"
return "\n\n".join(subsections) + "\n"


def add_attribute_section(process, placeholder="{{attributes}}"):
Expand Down
12 changes: 5 additions & 7 deletions xsimlab/tests/fixture_process.py
Expand Up @@ -85,13 +85,11 @@ def in_var_details():
"""\
Input variable
- type : variable
- intent : in
- dims : (('x',), ('x', 'y'))
- groups : ()
- static : False
- attrs : {}
- encoding : {}
Variable properties:
- type : ``variable``
- intent : ``in``
- dimensions : ('x',) or ('x', 'y')
"""
)

Expand Down
96 changes: 73 additions & 23 deletions xsimlab/tests/test_formatting.py
@@ -1,5 +1,7 @@
from textwrap import dedent

import attr

import xsimlab as xs
from xsimlab.formatting import (
add_attribute_section,
Expand Down Expand Up @@ -32,15 +34,67 @@ def test_wrap_indent():
assert wrap_indent(text, length=1) == expected


def test_var_details(example_process_obj):
var = xs.variable(dims="x", description="a variable")
def test_var_details():
@xs.process
class P:
var = xs.variable(
dims=[(), "x"],
description="a variable",
default=0,
groups=["g1", "g2"],
static=True,
attrs={"units": "m"},
encoding={"fill_value": -1},
)
var2 = xs.variable()

var_details_str = var_details(attr.fields(P).var)

expected = dedent(
"""\
A variable
Variable properties:
var_details_str = var_details(var)
- type : ``variable``
- intent : ``in``
- dimensions : () or ('x',)
- groups : g1, g2
- default value : 0
- static : ``True``
assert var_details_str.strip().startswith("A variable")
assert "- type : variable" in var_details_str
assert "- intent : in" in var_details_str
assert "- dims : (('x',),)" in var_details_str
Other attributes:
- units : m
Encoding options:
- fill_value : -1
"""
)

assert var_details_str == expected

@xs.process
class PP:
var = xs.foreign(P, "var2")

var_details_str = var_details(attr.fields(PP).var)

expected = dedent(
"""\
No description given
Variable properties:
- type : ``foreign``
- reference variable : :attr:`test_var_details.<locals>.P.var2`
- intent : ``in``
- dimensions : ()
"""
)

assert var_details_str == expected


@xs.process(autodoc=False)
Expand Down Expand Up @@ -72,24 +126,20 @@ def test_add_attribute_section():
var1 : :class:`attr.Attribute`
A variable
- type : variable
- intent : in
- dims : (('x',),)
- groups : ()
- static : False
- attrs : {}
- encoding : {}
Variable properties:
- type : ``variable``
- intent : ``in``
- dimensions : ('x',)
var2 : :class:`attr.Attribute`
(no description given)
- type : variable
- intent : in
- dims : ((),)
- groups : ()
- static : False
- attrs : {}
- encoding : {}
No description given
Variable properties:
- type : ``variable``
- intent : ``in``
- dimensions : ()
"""

assert add_attribute_section(WithoutPlaceHolder).strip() == expected.strip()
Expand Down
8 changes: 5 additions & 3 deletions xsimlab/variable.py
Expand Up @@ -407,11 +407,13 @@ def foreign(other_process_cls, var_name, intent="in"):
"other_process_cls": other_process_cls,
"var_name": var_name,
"intent": VarIntent(intent),
"description": ref_var.metadata["description"],
"attrs": ref_var.metadata.get("attrs", {}),
"encoding": ref_var.metadata.get("encoding", {}),
}

for meta_key in ["description", "dims", "attrs", "encoding"]:
ref_value = ref_var.metadata.get(meta_key)
if ref_value is not None:
metadata[meta_key] = ref_value

if VarIntent(intent) == VarIntent.OUT:
_init = False
_repr = False
Expand Down

0 comments on commit f83796f

Please sign in to comment.