Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/whats_new.rst
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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