# Docments


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

A docment is a comment beside a parameter in a function signature. It
keeps the parameter’s description next to its name, type and default.
`docments(f)` reads these comments for documentation tools and CLIs such
as `fastcore.script`:

## Why?

Without docments, if you want to document your parameters, you have to
repeat param names in docstrings, since they’re already in the function
signature. The parameters have to be kept synchronized in the two places
as you change your code. Readers of your code have to look back and
forth between two places to understand what’s happening. So it’s more
work for you, and for your users.

NumPy-style docstrings have formatting rules, including [where to put
spaces before
colons](https://stackoverflow.com/questions/62167540/why-do-definitions-have-a-space-before-the-colon-in-numpy-docstring-sections).
That formatting is a pain to write and maintain, and awkward to read in
code. Here’s an example using [NumPy-style
documentation](https://numpydoc.readthedocs.io/en/latest/format.html):

``` python
def add_np(a:int, b:int=0)->int:
    """The sum of two numbers.
    
    Used to demonstrate numpy-style docstrings.

Parameters
----------
a : int
    the 1st number to add
b : int
    the 2nd number to add (default: 0)

Returns
-------
int
    the result of adding `a` to `b`"""
    return a+b
```

By comparison, here’s the same thing using docments:

``` python
def add(
    a:int, # the 1st number to add
    b=0,   # the 2nd number to add
)->int:    # the result of adding `a` to `b`
    "The sum of two numbers."
    return a+b
```

## Numpy docstring helper functions

[`docments`](https://fastcore.fast.ai/docments.html#docments) also reads
NumPy-style docstrings. You can combine them with parameter comments.
The helpers below read and parse the docstrings.

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L41"
target="_blank" style="float:right; font-size:smaller">source</a>

### docstring

``` python
def docstring(
    sym
):
```

*Get documentation from an object, its constructor, or its callable
implementation*

[`docstring`](https://fastcore.fast.ai/docments.html#docstring) reads
the function’s prose independently of its parameter comments.

``` python
test_eq(docstring(add), "The sum of two numbers.")
```

Classes without their own prose can use a documented parent constructor.
An undocumented class does not inherit the generic `object.__init__`
description.

``` python
class Named:
    def __init__(self, name):
        "Create a named object."
        self.name = name

class NamedChild(Named): pass
class Undocumented: pass

test_eq(docstring(NamedChild), "Create a named object.")
test_eq(docstring(Undocumented), '')
docstring(NamedChild)
```

    'Create a named object.'

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L55"
target="_blank" style="float:right; font-size:smaller">source</a>

### parse_docstring

``` python
def parse_docstring(
    sym
):
```

*Parse a numpy-style docstring in `sym`*

NumPy parameter sections become named records with a type and
description. The summary remains separate from those parameter
descriptions.

``` python
npdocs = parse_docstring(add_np)
test_eq(npdocs.Parameters['a'].type, 'int')
npdocs.Parameters
```

    {'a': Parameter(name='a', type='int', desc=['the 1st number to add']),
     'b': Parameter(name='b', type='int', desc=['the 2nd number to add (default: 0)'])}

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L68"
target="_blank" style="float:right; font-size:smaller">source</a>

### get_source

``` python
def get_source(
    s
):
```

*Get source for a function, callable implementation, dataclass, or
source string*

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L64"
target="_blank" style="float:right; font-size:smaller">source</a>

### get_dataclass_source

``` python
def get_dataclass_source(
    s
):
```

*Get source code for dataclass `s`*

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L60"
target="_blank" style="float:right; font-size:smaller">source</a>

### isdataclass

``` python
def isdataclass(
    s
):
```

*Check if `s` is a dataclass but not a dataclass’ instance*

[`get_source`](https://fastcore.fast.ai/docments.html#get_source)
unwraps decorated functions and partials to find their implementation.
Passing source text leaves it unchanged.

``` python
source = get_source(add)
test_eq(get_source(partial(add, 1)), source)
test_eq(get_source(source), source)
PrettyString(source)
```

    def add(
        a:int, # the 1st number to add
        b=0,   # the 2nd number to add
    )->int:    # the result of adding `a` to `b`
        "The sum of two numbers."
        return a+b

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L82"
target="_blank" style="float:right; font-size:smaller">source</a>

### get_name

``` python
def get_name(
    obj
):
```

*Get the name of `obj`*

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L97"
target="_blank" style="float:right; font-size:smaller">source</a>

### qual_name

``` python
def qual_name(
    obj
):
```

*Get the qualified name of `obj`*

[`get_name`](https://fastcore.fast.ai/docments.html#get_name) uses the
short function or method name.
[`qual_name`](https://fastcore.fast.ai/docments.html#qual_name) retains
its enclosing class or module name.

``` python
test_eq(get_name(L.map), 'map')
test_eq(qual_name(L.map), 'L.map')
test_eq(qual_name(docscrape), 'fastcore.docscrape')
get_name(L.map), qual_name(L.map)
```

    ('map', 'L.map')

## Extracting parameter documentation

Follow one delegated function through comment extraction. `_d` supplies
its own documentation for `b`; `a` and `z` come from the functions it
delegates to:

``` python
def _b(
    z:str='b', # Last
):
    return b, a

@delegates(_b)
def _c(
    b:str, # Ignore
    a:int=2
): return b, a

@delegates(_c)
def _d(
    c:int, # First
    b:str, # Second
    **kwargs
)->int: # Return an int
    return c, _c(b, **kwargs)
```

Python’s tokenizer retains the comments that the syntax tree omits.
First collect each comment by source line:

``` python
s = _d
comments = {o.start[0]:_clean_comment(o.string) for o in _tokens(s) if o.type==COMMENT}
comments
```

    {3: ' First', 4: ' Second', 6: ' Return an int'}

Parameter locations use one-based source line numbers. The return
annotation also has a location when present.

``` python
parms = _param_locs(s, returns=True, args_kwargs=True) or {}
parms
```

    {3: 'c', 4: 'b', 5: 'kwargs', 6: 'return'}

A parameter uses its trailing comment, or consecutive comment lines
immediately above it. Comments belonging to another parameter are not
included.

``` python
docs = {arg:_get_comment(line, arg, comments, parms) for line,arg in parms.items()}
test_eq(docs['c'], 'First')
docs
```

    {'c': 'First', 'b': 'Second', 'kwargs': None, 'return': 'Return an int'}

Full docments retain the signature’s annotation and default alongside
the comment.

``` python
sig = signature(s, eval_str=True)
res = {name:_get_full(p, docs) for name,p in sig.parameters.items()}
test_is(res['c'].anno, int)
res
```

    {'c': {'docment': 'First',
      'anno': int,
      'default': inspect._empty,
      'kind': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>},
     'b': {'docment': 'Second',
      'anno': str,
      'default': inspect._empty,
      'kind': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>},
     'a': {'docment': None,
      'anno': int,
      'default': 2,
      'kind': <_ParameterKind.KEYWORD_ONLY: 3>},
     'z': {'docment': None,
      'anno': str,
      'default': 'b',
      'kind': <_ParameterKind.KEYWORD_ONLY: 3>}}

Add the return annotation and its comment to `res`. `_d.__delwrap__`
points to `_c`, where the next pass looks for descriptions of delegated
parameters.

``` python
res['return'] = AttrDict(docment=docs.get('return'), anno=sig.return_annotation, default=empty)
test_eq(res['return'].docment, 'Return an int')
res['return']
```

``` python
{ 'anno': <class 'int'>,
  'default': <class 'inspect._empty'>,
  'docment': 'Return an int'}
```

``` python
_d.__delwrap__
```

    <function __main__._c(b: str, a: int = 2, *, z: str = 'b')>

## Docments

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L164"
target="_blank" style="float:right; font-size:smaller">source</a>

### ann_parts

``` python
def ann_parts(
    anno
):
```

*The underlying type and metadata tuple of an `Annotated`, else
`(anno, ())`*

[`ann_parts`](https://fastcore.fast.ai/docments.html#ann_parts) splits
an annotation into its underlying type and its `Annotated` metadata,
returning an empty tuple for plain annotations. An
`Annotated[None, ...]` reports its type as `None` (used for CLI params
whose argparse action takes no value).

``` python
test_eq(ann_parts(int), (int, ()))
test_eq(ann_parts(Annotated[int, "doc", dict(nargs='+')]), (int, ("doc", dict(nargs='+'))))
test_eq(ann_parts(Annotated[None, "ver"]), (None, ("ver",)))
```

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L179"
target="_blank" style="float:right; font-size:smaller">source</a>

### docments

``` python
def docments(
    s, full:bool=False, eval_str:bool=False, returns:bool=True, args_kwargs:bool=False
):
```

*Get parameter and return documentation from comments, `Annotated`
metadata, and NumPy docstrings*

``` python
def add(
    a:int,  # The first operand
    b=1,    # The second operand
) -> int:   # The sum
    "Add `a` to `b`"
    return a + b
test_eq(docments(add), {'a': 'The first operand', 'b': 'The second operand', 'return': 'The sum'})
```

For longer descriptions, put comment lines above the parameter.
[`docments`](https://fastcore.fast.ai/docments.html#docments) also reads
`typing.Annotated` string metadata and NumPy-style docstrings. Pass
`full=True` to retrieve each parameter’s default, type and docment
together.

``` python
docs = docments(_d)
test_eq(docs, {'c':'First', 'b':'Second', 'a':None, 'z':'Last', 'return':'Return an int'})
docs
```

``` python
{'a': None, 'b': 'Second', 'c': 'First', 'return': 'Return an int', 'z': 'Last'}
```

``` python
full = docments(_d, full=True)
test_eq({k:v.docment for k,v in full.items()}, docs)
test_eq(full.a.default, 2)
full
```

``` python
{ 'a': { 'anno': <class 'int'>,
         'default': 2,
         'docment': None,
         'kind': <_ParameterKind.KEYWORD_ONLY: 3>},
  'b': { 'anno': <class 'str'>,
         'default': <class 'inspect._empty'>,
         'docment': 'Second',
         'kind': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>},
  'c': { 'anno': <class 'int'>,
         'default': <class 'inspect._empty'>,
         'docment': 'First',
         'kind': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>},
  'return': { 'anno': <class 'int'>,
              'default': <class 'inspect._empty'>,
              'docment': 'Return an int'},
  'z': { 'anno': <class 'str'>,
         'default': 'b',
         'docment': 'Last',
         'kind': <_ParameterKind.KEYWORD_ONLY: 3>}}
```

Editing a module on disk can leave a live object’s source locations out
of date. If that source no longer parses,
[`docments`](https://fastcore.fast.ai/docments.html#docments) returns
signature information without comments. Restart the interpreter to load
the edited module. Invalid source text passed directly to
[`docments`](https://fastcore.fast.ai/docments.html#docments) still
raises a syntax error:

``` python
def _fn(a:int): return a
_real = get_source
def get_source(s): return '    "stale"\nx = 1'
try: test_eq(docments(_fn), {'a': None, 'return': None})
finally: get_source = _real
test_fail(lambda: _parses('    "stale"\nx = 1'), contains='indent')
```

Instead of a comment, you can document a parameter or return value with
`typing.Annotated` metadata. The first string in the metadata is used as
the docment. A trailing comment wins if both are present. Generated APIs
can carry these descriptions without Python source:

``` python
def _g(
    a:Annotated[int, "the first"],
    b:Annotated[str, "ignored"]='x' # the second
) -> Annotated[list[int], "the results"]: ...

test_eq(docments(_g), {'a': 'the first', 'b': 'the second', 'return': 'the results'})
```

[`docments`](https://fastcore.fast.ai/docments.html#docments) returns a
dict mapping parameter names to descriptions. The `return` key holds the
return description. Pass `returns=False` to omit that entry. Here it is
for `add`:

``` python
def add(
    a:int, # the 1st number to add
    b=0,   # the 2nd number to add
)->int:    # the result of adding `a` to `b`
    "The sum of two numbers."
    return a+b
```

``` python
docments(add)
```

``` python
{ 'a': 'the 1st number to add',
  'b': 'the 2nd number to add',
  'return': 'the result of adding `a` to `b`'}
```

`args_kwargs=True` adds args and kwargs docs too:

``` python
def add(
    a:int, # the 1st number to add
    *args, # some args
    b=0,   # the 2nd number to add
    **kwargs, # Passed to the `example` function
)->int:    # the result of adding `a` to `b`
    "The sum of two numbers."
    return a+b
```

``` python
docments(add, args_kwargs=True)
```

``` python
{ 'a': 'the 1st number to add',
  'args': 'some args',
  'b': 'the 2nd number to add',
  'kwargs': 'Passed to the `example` function',
  'return': 'the result of adding `a` to `b`'}
```

If you pass `full=True`, the values are `dict` of defaults, types, and
docments as values. Note that the type annotation is inferred from the
default value, if the annotation is empty and a default is supplied.
(Note that for `full`, `args_kwargs=True` is always set too.)

``` python
docments(add, full=True)
```

``` python
{ 'a': { 'anno': <class 'int'>,
         'default': <class 'inspect._empty'>,
         'docment': 'the 1st number to add',
         'kind': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>},
  'args': { 'anno': <class 'inspect._empty'>,
            'default': <class 'inspect._empty'>,
            'docment': 'some args',
            'kind': <_ParameterKind.VAR_POSITIONAL: 2>},
  'b': { 'anno': <class 'int'>,
         'default': 0,
         'docment': 'the 2nd number to add',
         'kind': <_ParameterKind.KEYWORD_ONLY: 3>},
  'kwargs': { 'anno': <class 'inspect._empty'>,
              'default': <class 'inspect._empty'>,
              'docment': None,
              'kind': <_ParameterKind.VAR_KEYWORD: 4>},
  'return': { 'anno': <class 'int'>,
              'default': <class 'inspect._empty'>,
              'docment': 'the result of adding `a` to `b`'}}
```

To evaluate stringified annotations (from python 3.10), use `eval_str`:

``` python
docments(add, full=True, eval_str=True)['a']
```

``` python
{ 'anno': <class 'int'>,
  'default': <class 'inspect._empty'>,
  'docment': 'the 1st number to add',
  'kind': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>}
```

``` python
docments(add, full=True)['a']
```

``` python
{ 'anno': <class 'int'>,
  'default': <class 'inspect._empty'>,
  'docment': 'the 1st number to add',
  'kind': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>}
```

If you need more space to document a parameter, place one or more lines
of comments above the parameter, or above the return type. You can
mix-and-match these docment styles:

``` python
def add(
    # The first operand
    a:int,
    # This is the second of the operands to the *addition* operator.
    # Note that passing a negative value here is the equivalent of the *subtraction* operator.
    b:int,
)->int: # The result is calculated using Python's builtin `+` operator.
    "Add `a` to `b`"
    return a+b
```

``` python
docments(add)
```

``` python
{ 'a': 'The first operand',
  'b': 'This is the second of the operands to the *addition* operator.\n'
       'Note that passing a negative value here is the equivalent of the '
       '*subtraction* operator.',
  'return': "The result is calculated using Python's builtin `+` operator."}
```

Docments works with async functions, too:

``` python
async def add_async(
    # The first operand
    a:int,
    # This is the second of the operands to the *addition* operator.
    # Note that passing a negative value here is the equivalent of the *subtraction* operator.
    b:int,
)->int: # The result is calculated using Python's builtin `+` operator.
    "Add `a` to `b`"
    return a+b
```

``` python
test_eq(docments(add_async), docments(add))
```

You can also use docments with classes and methods:

``` python
class Adder:
    "An addition calculator"
    def __init__(self,
        a:int, # First operand
        b:int, # 2nd operand
    ): self.a,self.b = a,b
    
    def calculate(self
                 )->int: # Integral result of addition operator
        "Add `a` to `b`"
        return a+b
```

``` python
docments(Adder)
```

``` python
{'a': 'First operand', 'b': '2nd operand', 'return': None, 'self': None}
```

``` python
docments(Adder.calculate)
```

``` python
{'return': 'Integral result of addition operator', 'self': None}
```

docments can also be extracted from numpy-style docstrings:

``` python
print(add_np.__doc__)
```

    The sum of two numbers.

        Used to demonstrate numpy-style docstrings.

    Parameters
    ----------
    a : int
        the 1st number to add
    b : int
        the 2nd number to add (default: 0)

    Returns
    -------
    int
        the result of adding `a` to `b`

``` python
docments(add_np)
```

``` python
{ 'a': 'the 1st number to add',
  'b': 'the 2nd number to add (default: 0)',
  'return': 'the result of adding `a` to `b`'}
```

NumPy descriptions fill gaps in the parameter comments. When both
document the same parameter, the comment takes precedence:

``` python
def add_mixed(
    a:int, # the first number to add
    b
)->int: # the result
    """The sum of two numbers.

Parameters
----------
a : int
    description overridden by the parameter comment
b : int
    the 2nd number to add (default: 0)"""
    return a+b
```

``` python
mixed = docments(add_mixed, full=True)
test_eq(mixed.a.docment, 'the first number to add')
test_eq(mixed.b.docment, 'the 2nd number to add (default: 0)')
mixed
```

``` python
{ 'a': { 'anno': <class 'int'>,
         'default': <class 'inspect._empty'>,
         'docment': 'the first number to add',
         'kind': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>},
  'b': { 'anno': <class 'inspect._empty'>,
         'default': <class 'inspect._empty'>,
         'docment': 'the 2nd number to add (default: 0)',
         'kind': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>},
  'return': { 'anno': <class 'int'>,
              'default': <class 'inspect._empty'>,
              'docment': 'the result'}}
```

[`docments`](https://fastcore.fast.ai/docments.html#docments) reads
dataclass field comments from the class’s source file. For a dataclass
defined in a notebook, export it to a module and import the class from
there.

Builtins just return an empty dict:

``` python
docments(str)
```

``` python
{'args': None, 'kwargs': None, 'return': None, 'self': None}
```

## Extract docstrings

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L201"
target="_blank" style="float:right; font-size:smaller">source</a>

### sig_source

``` python
def sig_source(
    obj
):
```

*Full source of signature line(s) for a function or class.*

[`sig_source`](https://fastcore.fast.ai/docments.html#sig_source)
preserves the original source lines through the signature. A body
written on the same line is included; a body on subsequent lines is
omitted.

``` python
print(sig_source(flexiclass))
```

    def flexiclass(
        cls # The class to convert
    ) -> dataclass:

``` python
def simple(x: dict[str, int]): return x
print(sig_source(simple))
```

    def simple(x: dict[str, int]): return x

``` python
def multi(a, b=1,
          c=2,
          d=3):
    return a
source = sig_source(multi)
assert '          c=2,' in source
assert 'return a' not in source
PrettyString(source)
```

    def multi(a, b=1,
              c=2,
              d=3):

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L248"
target="_blank" style="float:right; font-size:smaller">source</a>

### extract_docstrings

``` python
def extract_docstrings(
    code
):
```

*Create a dict from function/class/method names to tuples of docstrings
and param lists*

[`extract_docstrings`](https://fastcore.fast.ai/docments.html#extract_docstrings)
reads source without executing it. It records public functions and
methods, module prose, and a class’s constructor parameters. A
constructor docstring supplies class documentation when the class has
none.

``` python
sample_code = """
"This is a module."

def top_func(a, b, *args, **kw):
    "This is top-level."
    pass

class SampleClass:
    "This is a class."

    def __init__(self, x, y):
        "Constructor for SampleClass."
        pass

    def method1(self, param1):
        "This is method1."
        pass

    def _private_method(self):
        "This should not be included."
        pass

class AnotherClass:
    def __init__(self, a, b):
        "This class has no separate docstring."
        pass"""

exp = {'_module': ('This is a module.', ''), 'top_func': ('This is top-level.', 'a, b, *args, **kw'),
    'SampleClass': ('This is a class.', 'self, x, y'), 'SampleClass.method1': ('This is method1.', 'self, param1'),
    'AnotherClass': ('This class has no separate docstring.', 'self, a, b')}
test_eq(extract_docstrings(sample_code), exp)
```

## Rendering docment Tables

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L279"
target="_blank" style="float:right; font-size:smaller">source</a>

### DocmentTbl

``` python
def DocmentTbl(
    obj, verbose:bool=True, returns:bool=True
):
```

*Compute the docment table string*

[`DocmentTbl`](https://fastcore.fast.ai/docments.html#docmenttbl)
renders parameter documentation as a Markdown table. String annotations
can be displayed without resolving their types:

``` python
def _f(
    a,      # description of param a
    b=True, # description of param b
    c:'Result'=None
) -> int: ...

_dm = DocmentTbl(_f)
assert 'description of param a' in str(_dm)
assert 'Returns' in str(_dm)
assert 'Result' in _dm.params_str
_dm
```

<table>
<thead>
<tr>
<th></th>
<th><strong>Type</strong></th>
<th><strong>Default</strong></th>
<th><strong>Details</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>a</td>
<td></td>
<td></td>
<td>description of param a</td>
</tr>
<tr>
<td>b</td>
<td>bool</td>
<td>True</td>
<td>description of param b</td>
</tr>
<tr>
<td>c</td>
<td>Result</td>
<td>None</td>
<td></td>
</tr>
<tr>
<td><strong>Returns</strong></td>
<td><strong>int</strong></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>

Markdown table cells escape pipes and footnote markers. Existing escapes
are preserved, and line breaks become `<br>`.

``` python
def compile_pattern(
    # Alternatives: a | b
    # Escaped form: a \| b
    # See ^[syntax]
    pattern:str
): return re.compile(pattern)

table = DocmentTbl(compile_pattern)
assert r'Alternatives: a \| b<br>Escaped form: a \| b<br>See \^[syntax]' in str(table)
table
```

<table>
<colgroup>
<col style="width: 33%" />
<col style="width: 33%" />
<col style="width: 33%" />
</colgroup>
<thead>
<tr>
<th></th>
<th><strong>Type</strong></th>
<th><strong>Details</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>pattern</td>
<td>str</td>
<td>Alternatives: a | b<br>Escaped form: a | b<br>See ^[syntax]</td>
</tr>
</tbody>
</table>

Columns without any values are omitted. This function has no defaults or
documented return value, so neither the Default column nor the Returns
row appears.

``` python
def _f(
    a,
    b:int, #param b
    c:str  #param c
):
    "Do a thing"
    ...
```

``` python
_dm2 = DocmentTbl(_f)
assert 'Default' not in str(_dm2)
assert 'Returns' not in str(_dm2)
assert 'param b' in str(_dm2)
_dm2
```

<table>
<thead>
<tr>
<th></th>
<th><strong>Type</strong></th>
<th><strong>Details</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>a</td>
<td></td>
<td></td>
</tr>
<tr>
<td>b</td>
<td>int</td>
<td>param b</td>
</tr>
<tr>
<td>c</td>
<td>str</td>
<td>param c</td>
</tr>
</tbody>
</table>

By default, passing a class to
[`DocmentTbl`](https://fastcore.fast.ai/docments.html#docmenttbl) shows
its constructor parameters:

``` python
class _Test:
    def __init__(
        self,
        a,      # description of param a
        b=True, # description of param b
        c:str=None
    ): ...

    def foo(
        self,
        c:int,      # description of param c
        d=True, # description of param d
    ): ...
```

``` python
DocmentTbl(_Test)
```

<table>
<thead>
<tr>
<th></th>
<th><strong>Type</strong></th>
<th><strong>Default</strong></th>
<th><strong>Details</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>a</td>
<td></td>
<td></td>
<td>description of param a</td>
</tr>
<tr>
<td>b</td>
<td>bool</td>
<td>True</td>
<td>description of param b</td>
</tr>
<tr>
<td>c</td>
<td>str</td>
<td>None</td>
<td></td>
</tr>
</tbody>
</table>

You can also pass a method to be rendered as well:

``` python
method_table = DocmentTbl(_Test.foo)
assert 'description of param c' in str(method_table)
assert 'self' not in method_table.dm
method_table
```

<table>
<thead>
<tr>
<th></th>
<th><strong>Type</strong></th>
<th><strong>Default</strong></th>
<th><strong>Details</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>c</td>
<td>int</td>
<td></td>
<td>description of param c</td>
</tr>
<tr>
<td>d</td>
<td>bool</td>
<td>True</td>
<td>description of param d</td>
</tr>
</tbody>
</table>

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L330"
target="_blank" style="float:right; font-size:smaller">source</a>

### DocmentList

``` python
def DocmentList(
    obj
):
```

[`DocmentList`](https://fastcore.fast.ai/docments.html#docmentlist)
renders each parameter as a bullet containing its type, default, and
description.

``` python
listing = DocmentList(_f)
assert 'param b' in str(listing)
listing
```

- `a`
- `b:int`   *param b*
- `c:str`   *param c*
- `return`

Signature formatting starts a new line after each documented parameter.
Undocumented parameters share a line when they fit.

``` python
formatted = _fmt_sig('foo', [('a:int', 'first'), ('b:str', None), ('c', 'third')], ')->int:', 80)
assert 'a:int, # first\n' in formatted
assert 'b:str, c, # third' in formatted
PrettyString(formatted)
```

    def foo(
        a:int, # first
        b:str, c, # third
    )->int:

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L369"
target="_blank" style="float:right; font-size:smaller">source</a>

### DocmentText

``` python
def DocmentText(
    obj, maxline:int=110, docstring:bool=True
):
```

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L385"
target="_blank" style="float:right; font-size:smaller">source</a>

### DocmentText.params

``` python
def params():
```

[`DocmentText`](https://fastcore.fast.ai/docments.html#docmenttext)
keeps formatted parameters separate from their descriptions before
assembling the signature.

``` python
parameters = DocmentText(_f).params
test_eq(parameters[1], ('b:int', 'param b'))
parameters
```

    [('a', None), ('b:int', 'param b'), ('c:str', 'param c')]

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L415"
target="_blank" style="float:right; font-size:smaller">source</a>

### DocmentText.\_\_str\_\_

``` python
def __str__():
```

*Return str(self).*

[`DocmentText`](https://fastcore.fast.ai/docments.html#docmenttext)
assembles those parameters into a Python signature with docments beside
them.

``` python
text = DocmentText(_f)
assert 'b:int, # param b' in str(text)
text
```

``` python
def _f(
    a, b:int, # param b
    c:str, # param c
):
    "Do a thing"
```

``` python
def _g(
    a, b:int, cccccccccccccccccccc:int, ccccccccdccccccccccc:int, cccccccccccecccccccc:int, cccccccfcccccccccc:int, ccccccccccccgccccc:int, # hi
    c:str='foo'
)->str:
    "Do a thing"

DocmentText(_g, maxline=80, docstring=False)
```

``` python
def _g(
    a, b:int, cccccccccccccccccccc:int, ccccccccdccccccccccc:int,
    cccccccccccecccccccc:int, cccccccfcccccccccc:int, ccccccccccccgccccc:int, # hi
    c:str='foo'
)->str:
```

A partial binds arguments without losing the remaining parameters’
docments.

``` python
DocmentText(partial(_g, 1), maxline=80, docstring=False)
```

``` python
def partial.__call__(
    b:int, cccccccccccccccccccc:int, ccccccccdccccccccccc:int,
    cccccccccccecccccccc:int, cccccccfcccccccccc:int, ccccccccccccgccccc:int, # hi
    c:str='foo'
)->str:
```

Non-callable objects render as their type and value, rather than
pretending to be functions:

``` python
test_eq(str(DocmentText(3, docstring=False)), 'int instance: 3')
```

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L431"
target="_blank" style="float:right; font-size:smaller">source</a>

### sig2str

``` python
def sig2str(
    func, maxline:int=110
):
```

*Generate function signature with docments as comments*

``` python
print(sig2str(_d))
```

    def _d(
        c:int, # First
        b:str, # Second
        *, a:int=2, z:str='b', # Last
    )->int: # Return an int

Signatures retain positional-only `/` and keyword-only `*` separators.
Varargs keep their `*`/`**` prefixes, whether annotated or not:

``` python
def _va(
    a:int, # First value
    /, *ids:str, limit:Annotated[int, 'Maximum results']=10, **kw
) -> list[str]: pass

sigtext = str(sig2str(_va))
assert all(s in sigtext for s in ('First value', '/', '*ids:str', 'limit:int=10', 'Maximum results', '**kw', 'list[str]'))
def _vb(a, /, *, b=1): pass
assert ', /, *,' in str(sig2str(_vb))
sig2str(_va)
```

``` python
def _va(
    a:int, # First value
    /, *ids:str, limit:int=10, # Maximum results
    **kw
)->list[str]:
```

## Documentation For An Object

Render the signature as well as the
[`docments`](https://fastcore.fast.ai/docments.html#docments) to show
complete documentation for an object.

NumPy parameter and return sections are omitted from the remaining prose
when their types agree with the signature. Their descriptions will
appear beside the parameters instead.

``` python
remaining = _docstring(add_np, docments(add_np, full=True))
assert 'Parameters' not in remaining
assert 'The sum of two numbers.' in remaining
PrettyString(remaining)
```

    The sum of two numbers.

        Used to demonstrate numpy-style docstrings.

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L454"
target="_blank" style="float:right; font-size:smaller">source</a>

### can_render

``` python
def can_render(
    sym
):
```

*Check if `sym` has a renderable signature*

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L467"
target="_blank" style="float:right; font-size:smaller">source</a>

### ShowDocRenderer

``` python
def ShowDocRenderer(
    sym, name:str | None=None, title_level:int=3, maxline:int=110
):
```

*Show documentation for `sym`*

------------------------------------------------------------------------

<a
href="https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/docments.py#L491"
target="_blank" style="float:right; font-size:smaller">source</a>

### MarkdownRenderer

``` python
def MarkdownRenderer(
    sym, name:str | None=None, title_level:int=3, maxline:int=110
):
```

*Markdown renderer for `show_doc`*

NumPy-style sections remain part of the full documentation. Parameter
and return descriptions appear beside the signature; notes, errors, and
examples stay below it.

``` python
def bounded_add(a:int, b:int=0) -> int:
    """Add two non-negative numbers.

    Parameters
    ----------
    a : int
        First operand.
    b : int
        Second operand.

    Returns
    -------
    int
        Their sum.

    Notes
    -----
    Both operands must be non-negative.

    Raises
    ------
    ValueError
        An operand is negative.

    Examples
    --------
    >>> bounded_add(2, 3)
    5
    """
    if min(a, b) < 0: raise ValueError('Negative operand')
    return a+b

rendered = str(MarkdownRenderer(bounded_add))
assert all(s in rendered for s in ('First operand.', 'Their sum.', 'Both operands', 'ValueError', 'bounded_add(2, 3)'))
assert 'Parameters\n' not in rendered and 'Returns\n' not in rendered
MarkdownRenderer(bounded_add)
```

``` python
def bounded_add(
    a:int, # First operand.
    b:int=0, # Second operand.
)->int: # Their sum.
```

*Add two non-negative numbers.*

## Notes

Both operands must be non-negative.

## Raises

ValueError An operand is negative.

## Examples

> > > bounded_add(2, 3) 5

If a symbol’s source is unreadable, the renderer shows its signature
without docments and warns with the symbol name and error:

``` python
def _stale(): pass
_stale.__code__ = _stale.__code__.replace(co_firstlineno=99999)
with warnings.catch_warnings(record=True) as w:
    warnings.simplefilter('always')
    r = MarkdownRenderer(_stale)
assert 'docments unavailable' in str(w[0].message)
assert '_stale' in str(r)
r
```

``` python
_stale()
```

``` python
def _f(
    a,
    b:callable=print, #param b
    c:str='foo'  #param c
)->str: # Result of doing it
    "Do a thing"
    ...

MarkdownRenderer(_f)
```

``` python
def _f(
    a, b:<built-in function callable>=print, # param b
    c:str='foo', # param c
)->str: # Result of doing it
```

*Do a thing*

``` python
print(MarkdownRenderer(_f))
```

    def _f(
        a, b:<built-in function callable>=print, # param b
        c:str='foo', # param c
    )->str: # Result of doing it"""Do a thing"""

Delegated signatures retain the wrapper’s own docments and the delegated
parameters.

``` python
def f(a:int=0 # aa
): pass

@delegates(f)
def g(
    b:int|str, # bb
    **kwargs
): return kwargs
```

``` python
MarkdownRenderer(g)
```

``` python
def g(
    b:int | str, # bb
    *, a:int=0, # aa
):
```

``` python
MarkdownRenderer(add_async)
```

``` python
async def add_async(
    a:int, # The first operand
    b:int, # This is the second of the operands to the *addition* operator.
    # Note that passing a negative value here is the equivalent of the *subtraction* operator.
)->int: # The result is calculated using Python's builtin `+` operator.
```

*Add `a` to `b`*

Some builtins have no inspectable signature. Their original docstring
still supplies the calling convention and behavior.

``` python
builtin_docs = MarkdownRenderer(next)
test_is(builtin_docs.sig, None)
assert 'StopIteration' in builtin_docs.docs
builtin_docs
```

``` python
def next():
```

*next(iterator\[, default\])*

Return the next item from the iterator. If default is given and the
iterator is exhausted, it is returned instead of raising StopIteration.

``` python
class Foo:
    "A callable calculator"
    async def __call__(self,
        x:int, # Input value
    ) -> int:
        "Calculate asynchronously"
        return x

foo = Foo()
rendered = str(MarkdownRenderer(foo))
assert all(s in rendered for s in ('async def', 'Input value', 'Calculate asynchronously'))
MarkdownRenderer(foo)
```

``` python
async def Foo.__call__(
    x:int, # Input value
)->int:
```

*Calculate asynchronously*

Fastgit’s `Git` resolves arbitrary subcommands through `__getattr__`,
including names such as `fget`. Probing for `fget` can mistake such an
instance for a property. The renderer uses `isinstance` to identify
properties. Here it documents the instance’s own `__call__`, not a
partial returned by `__getattr__`:

``` python
class Bar:
    def __call__(self, x):
        "Call bar"
        return x
    def __getattr__(self, nm):
        if nm.startswith('_'): raise AttributeError(nm)
        return partial(self, nm)

r = MarkdownRenderer(Bar())
assert 'Bar.__call__' in str(r)
r
```

``` python
def Bar.__call__(
    x
):
```

*Call bar*
