{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "5b3e6248", "metadata": {}, "outputs": [], "source": [ "#|default_exp showdoc" ] }, { "cell_type": "markdown", "id": "04b59a7c", "metadata": {}, "source": [ "# showdoc\n", "> Display symbol documentation in notebook and website" ] }, { "cell_type": "code", "execution_count": null, "id": "7f371f15", "metadata": {}, "outputs": [], "source": [ "#|export\n", "from __future__ import annotations\n", "from nbdev.doclinks import *\n", "from nbdev.read import get_config\n", "\n", "from fastcore.dispatch import TypeDispatch\n", "from fastcore.docments import *\n", "from fastcore.utils import *\n", "\n", "from importlib import import_module\n", "import inspect, sys\n", "from inspect import Signature, Parameter\n", "from collections import OrderedDict\n", "from dataclasses import dataclass, is_dataclass\n", "from textwrap import fill" ] }, { "cell_type": "code", "execution_count": null, "id": "37779036-032f-41cc-bedb-d928f1a25df7", "metadata": {}, "outputs": [], "source": [ "#|hide\n", "from fastcore.test import *" ] }, { "cell_type": "markdown", "id": "f6c573b6-73cf-4c5e-b4a6-989b62071cc1", "metadata": {}, "source": [ "## Rendering docment Tables\n", "\n", "Render nicely formatted tables that shows `docments` for any function or method. " ] }, { "cell_type": "code", "execution_count": null, "id": "5de6dc35-565d-4847-8505-b9feb680cb1a", "metadata": {}, "outputs": [], "source": [ "#|export\n", "def _non_empty_keys(d:dict): return L([k for k,v in d.items() if v != inspect._empty])\n", "def _bold(s): return f'**{s}**' if s.strip() else s" ] }, { "cell_type": "code", "execution_count": null, "id": "8ea735b2-a5ee-4176-8143-add7ab08a3cd", "metadata": {}, "outputs": [], "source": [ "#|export\n", "def _maybe_nm(o): \n", " if (o == inspect._empty): return ''\n", " else: return o.__name__ if hasattr(o, '__name__') else str(o)" ] }, { "cell_type": "code", "execution_count": null, "id": "908341c8-dfb4-4e05-9122-30dcae4bc130", "metadata": {}, "outputs": [], "source": [ "test_eq(_maybe_nm(list), 'list')\n", "test_eq(_maybe_nm('fastai'), 'fastai')" ] }, { "cell_type": "code", "execution_count": null, "id": "63418af8-3f68-4301-9da3-da5787f8c257", "metadata": {}, "outputs": [], "source": [ "#|export\n", "def _list2row(l:list): return '| '+' | '.join([_maybe_nm(o) for o in l]) + ' |'" ] }, { "cell_type": "code", "execution_count": null, "id": "b74936bc-a38d-4e98-a4f8-3c97a8bd8e86", "metadata": {}, "outputs": [], "source": [ "#|hide\n", "test_eq(_list2row(['Hamel', 'Jeremy']), '| Hamel | Jeremy |')\n", "test_eq(_list2row([inspect._empty, bool, 'foo']), '| | bool | foo |')" ] }, { "cell_type": "code", "execution_count": null, "id": "7eff0337-25ff-4fbe-afe0-a7ef0788ebf9", "metadata": {}, "outputs": [], "source": [ "#|export\n", "class DocmentTbl:\n", " # this is the column order we want these items to appear\n", " _map = OrderedDict({'anno':'Type', 'default':'Default', 'docment':'Details'})\n", " \n", " def __init__(self, obj, verbose=True, returns=True):\n", " \"Compute the docment table string\"\n", " self.verbose = verbose\n", " self.returns = False if isdataclass(obj) else returns\n", " if isinstance_str(obj, 'property'): self.params = []\n", " else:\n", " try: self.params = L(signature_ex(obj, eval_str=True).parameters.keys())\n", " except (ValueError,TypeError): self.params=[]\n", " try: _dm = docments(obj, full=True, returns=returns)\n", " except: _dm = {}\n", " if 'self' in _dm: del _dm['self']\n", " for d in _dm.values(): d['docment'] = ifnone(d['docment'], inspect._empty)\n", " self.dm = _dm\n", " \n", " @property\n", " def _columns(self):\n", " \"Compute the set of fields that have at least one non-empty value so we don't show tables empty columns\"\n", " cols = set(flatten(L(self.dm.values()).filter().map(_non_empty_keys)))\n", " candidates = self._map if self.verbose else {'docment': 'Details'}\n", " return OrderedDict({k:v for k,v in candidates.items() if k in cols})\n", " \n", " @property\n", " def has_docment(self): return 'docment' in self._columns and self._row_list \n", "\n", " @property\n", " def has_return(self): return self.returns and bool(_non_empty_keys(self.dm.get('return', {})))\n", " \n", " def _row(self, nm, props): \n", " \"unpack data for single row to correspond with column names.\"\n", " return [nm] + [props[c] for c in self._columns]\n", " \n", " @property\n", " def _row_list(self):\n", " \"unpack data for all rows.\"\n", " ordered_params = [(p, self.dm[p]) for p in self.params if p != 'self' and p in self.dm]\n", " return L([self._row(nm, props) for nm,props in ordered_params])\n", " \n", " @property\n", " def _hdr_list(self): return [' '] + [_bold(l) for l in L(self._columns.values())]\n", "\n", " @property\n", " def hdr_str(self):\n", " \"The markdown string for the header portion of the table\"\n", " md = _list2row(self._hdr_list)\n", " return md + '\\n' + _list2row(['-' * len(l) for l in self._hdr_list])\n", " \n", " @property\n", " def params_str(self): \n", " \"The markdown string for the parameters portion of the table.\"\n", " return '\\n'.join(self._row_list.map(_list2row))\n", " \n", " @property\n", " def return_str(self):\n", " \"The markdown string for the returns portion of the table.\"\n", " return _list2row(['**Returns**']+[_bold(_maybe_nm(self.dm['return'][c])) for c in self._columns])\n", " \n", " def _repr_markdown_(self):\n", " if not self.has_docment: return ''\n", " _tbl = [self.hdr_str, self.params_str]\n", " if self.has_return: _tbl.append(self.return_str)\n", " return '\\n'.join(_tbl)\n", " \n", " def __eq__(self,other): return self.__str__() == str(other).strip()\n", "\n", " def __str__(self): return self._repr_markdown_()" ] }, { "cell_type": "markdown", "id": "6bf2c677-d0d5-42cf-a2c7-fcbdec725720", "metadata": {}, "source": [ "`DocmentTbl` can render a markdown table showing `docments` if appropriate. This is an example of how a `docments` table will render for a function:" ] }, { "cell_type": "code", "execution_count": null, "id": "ac9a8ddc-9fe6-48c6-b2a5-bc0f1a7a0926", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "| | **Type** | **Default** | **Details** |\n", "| -- | -------- | ----------- | ----------- |\n", "| a | | | description of param a |\n", "| b | bool | True | description of param b |\n", "| c | str | None | |\n", "| **Returns** | **int** | | |" ], "text/plain": [ "<__main__.DocmentTbl at 0x7fc8d5d35e20>" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def _f(a, # description of param a \n", " b=True, # description of param b\n", " c:str=None\n", " ) -> int: ...\n", "\n", "_dm = DocmentTbl(_f)\n", "_dm" ] }, { "cell_type": "code", "execution_count": null, "id": "c44d2bba-377e-4f72-9c4e-1b583acaedb7", "metadata": {}, "outputs": [], "source": [ "#|hide\n", "_exp_res=\"\"\"\n", "| | **Type** | **Default** | **Details** |\n", "| -- | -------- | ----------- | ----------- |\n", "| a | | | description of param a |\n", "| b | bool | True | description of param b |\n", "| c | str | None | |\n", "| **Returns** | **int** | | |\n", "\"\"\"\n", "\n", "test_eq(_dm, _exp_res)" ] }, { "cell_type": "markdown", "id": "affcab1f-d4a6-4f0e-9f5a-da92611e7cf2", "metadata": {}, "source": [ "If one column in the table has no information, for example because there are no default values, that column will not be shown. In the below example, the **Default** column, will not be shown. Additionally, if the return of the function is not annotated the **Returns** row will not be rendered:" ] }, { "cell_type": "code", "execution_count": null, "id": "7339ca82-b816-424f-b7f4-63b8db99cef0", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "| | **Details** |\n", "| -- | ----------- |\n", "| a | |\n", "| b | param b |\n", "| c | param c |" ], "text/plain": [ "<__main__.DocmentTbl at 0x7fc8d5d355b0>" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def _f(a, \n", " b, #param b\n", " c #param c\n", " ): ...\n", "\n", "_dm2 = DocmentTbl(_f)\n", "_dm2" ] }, { "cell_type": "code", "execution_count": null, "id": "b18421bf-108e-433f-b7ca-9f13749bcc42", "metadata": {}, "outputs": [], "source": [ "#|hide\n", "_exp_res2 = \"\"\"\n", "| | **Details** |\n", "| -- | ----------- |\n", "| a | |\n", "| b | param b |\n", "| c | param c |\n", "\"\"\"\n", "\n", "test_eq(_dm2, _exp_res2)" ] }, { "cell_type": "markdown", "id": "d9e7a6d5-4fff-4df2-8f35-f4b4be968d28", "metadata": {}, "source": [ "`DocmentTbl` also works on classes. By default, the `__init__` will be rendered:" ] }, { "cell_type": "code", "execution_count": null, "id": "669f06c4-cd96-40d3-bae8-ed01fa88fc77", "metadata": {}, "outputs": [], "source": [ "class _Test:\n", " def __init__(self, \n", " a, # description of param a \n", " b=True, # description of param b\n", " c:str=None):\n", " ...\n", " \n", " def foo(self, \n", " c:int, # description of param c\n", " d=True, # description of param d\n", " ):\n", " ..." ] }, { "cell_type": "code", "execution_count": null, "id": "a3230ab3-5902-48bb-925a-99cfea7d6a37", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "| | **Type** | **Default** | **Details** |\n", "| -- | -------- | ----------- | ----------- |\n", "| a | | | description of param a |\n", "| b | bool | True | description of param b |\n", "| c | str | None | |" ], "text/plain": [ "<__main__.DocmentTbl at 0x7fc8d5d35820>" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "DocmentTbl(_Test)" ] }, { "cell_type": "markdown", "id": "d2debf2b-6c58-4966-8600-8e6cbba3b3fd", "metadata": {}, "source": [ "You can also pass a method to be rendered as well:" ] }, { "cell_type": "code", "execution_count": null, "id": "c499a450-0182-4ad0-8cab-f10f190a7545", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "| | **Type** | **Default** | **Details** |\n", "| -- | -------- | ----------- | ----------- |\n", "| c | int | | description of param c |\n", "| d | bool | True | description of param d |" ], "text/plain": [ "<__main__.DocmentTbl at 0x7fc8d5d35070>" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "DocmentTbl(_Test.foo)" ] }, { "cell_type": "code", "execution_count": null, "id": "c113ea93-9e0f-4b52-a5fd-87ed0de24c90", "metadata": {}, "outputs": [], "source": [ "#|hide\n", "_exp_res3 = \"\"\"\n", "| | **Type** | **Default** | **Details** |\n", "| -- | -------- | ----------- | ----------- |\n", "| c | int | | description of param c |\n", "| d | bool | True | description of param d |\n", "\"\"\"\n", "\n", "test_eq(DocmentTbl(_Test.foo), _exp_res3)" ] }, { "cell_type": "markdown", "id": "773b5bfb", "metadata": {}, "source": [ "## Show Complete Documentation For An Object" ] }, { "cell_type": "markdown", "id": "0eb7314a-675d-4fd8-8f9b-425666e1bd86", "metadata": {}, "source": [ "Render the signature as well as the `docments` to show complete documentation for an object." ] }, { "cell_type": "code", "execution_count": null, "id": "e2570925", "metadata": {}, "outputs": [], "source": [ "#|export\n", "class ShowDocRenderer:\n", " def __init__(self, sym, disp:bool=True, name:str|None=None, title_level:int|None=None):\n", " \"Show documentation for `sym`\"\n", " store_attr()\n", " self.nm = name or qual_name(sym)\n", " self.isfunc = inspect.isfunction(sym)\n", " self.isprop = isinstance_str(sym, 'property')\n", " if self.isprop: self.sig = None\n", " else:\n", " try: self.sig = signature_ex(sym, eval_str=True)\n", " except (ValueError,TypeError): self.sig = None\n", " self.docs = docstring(sym)\n", " self.dm = DocmentTbl(sym)" ] }, { "cell_type": "code", "execution_count": null, "id": "b406e38f-25c8-46e4-bb65-f039cb7b5a2a", "metadata": {}, "outputs": [], "source": [ "#|export\n", "def _fmt_sig(sig):\n", " if sig is None: return ''\n", " p = {k:v for k,v in sig.parameters.items()}\n", " _params = [str(p[k]).replace(' ','') for k in p.keys() if k != 'self']\n", " return \"(\" + ', '.join(_params) + \")\"\n", "\n", "def _wrap_sig(s):\n", " \"wrap a signature to appear on multiple lines if necessary.\"\n", " pad = '> ' + ' ' * 5\n", " indent = pad + ' ' * (s.find('(') + 1)\n", " return fill(s, width=80, initial_indent=pad, subsequent_indent=indent)" ] }, { "cell_type": "code", "execution_count": null, "id": "cd1aa1d7-d545-4a76-9de8-394beb4f2b8e", "metadata": {}, "outputs": [], "source": [ "#|hide\n", "def _long_f(a_param, b_param=True, c_param:str='Some quite long value', d:int=2, e:bool=False):\n", " \"A docstring\"\n", " ...\n", " \n", "_res = \"> (a_param, b_param=True, c_param:str='Somequitelongvalue', d:int=2,\\n> e:bool=False)\"\n", "_sig = _fmt_sig(signature_ex(_long_f, eval_str=True))\n", "test_eq(_wrap_sig(_sig), _res)" ] }, { "cell_type": "code", "execution_count": null, "id": "61d7f084", "metadata": {}, "outputs": [], "source": [ "#|export\n", "class BasicMarkdownRenderer(ShowDocRenderer):\n", " def _repr_markdown_(self):\n", " doc = '---\\n\\n'\n", " if self.isfunc or self.isprop: doc += '#'\n", " sig = _wrap_sig(f\"{self.nm} {_fmt_sig(self.sig)}\") if self.sig else ''\n", " doc += f'### {self.nm}\\n\\n{sig}'\n", " if self.docs: doc += f\"\\n\\n{self.docs.splitlines()[0]}\"\n", " if self.dm.has_docment: doc += f\"\\n\\n{self.dm}\"\n", " return doc" ] }, { "cell_type": "code", "execution_count": null, "id": "1256ef79", "metadata": {}, "outputs": [], "source": [ "#|export\n", "def show_doc(sym, disp=True, renderer=None, name:str|None=None, title_level:int|None=None):\n", " if renderer is None: renderer = get_config().get('renderer', None)\n", " if renderer is None: renderer=BasicMarkdownRenderer\n", " elif isinstance(renderer,str):\n", " p,m = renderer.rsplit('.', 1)\n", " renderer = getattr(import_module(p), m)\n", " if isinstance(sym, TypeDispatch): pass\n", " else:return renderer(sym or show_doc, disp=disp, name=name, title_level=title_level)" ] }, { "cell_type": "markdown", "id": "a38843d9-4fa3-40f3-bb4d-949344b2bdee", "metadata": {}, "source": [ "You can use `show_doc` to document apis of functions, classes or methods:" ] }, { "cell_type": "code", "execution_count": null, "id": "53a1bc00", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "---\n", "\n", "#### f\n", "\n", "> f (x:int=1)\n", "\n", "func docstring" ], "text/plain": [ "<__main__.BasicMarkdownRenderer at 0x7fc8d5c50220>" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def f(x:int=1):\n", " \"func docstring\"\n", " ...\n", "\n", "show_doc(f)" ] }, { "cell_type": "markdown", "id": "66a8dfcd-dc2b-41ce-9d35-5c35e040f25d", "metadata": {}, "source": [ ":::{.callout-warning}\n", "If you are using a version of python that is older than 3.10, type hints might be rendered as strings when running `show_doc`. We recommend upgrading to python 3.10 locally if possible so you can preview docs without this artifact. We have set the version of python to be 3.10 `.github/workflows/deploy.yaml` for this reason as well.\n", ":::" ] }, { "cell_type": "code", "execution_count": null, "id": "ac17ee63-d1d3-4281-956b-48b465b181bd", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "---\n", "\n", "#### f\n", "\n", "> f (x:int=1)\n", "\n", "func docstring\n", "\n", "| | **Type** | **Default** | **Details** |\n", "| -- | -------- | ----------- | ----------- |\n", "| x | int | 1 | the parameter x |\n", "| **Returns** | **None** | | **this function doesn't return anything** |" ], "text/plain": [ "<__main__.BasicMarkdownRenderer at 0x7fc8d5c500a0>" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def f(x:int=1 # the parameter x\n", " ) -> None: # this function doesn't return anything\n", " \"func docstring\"\n", " ...\n", "\n", "show_doc(f)" ] }, { "cell_type": "markdown", "id": "d88c9513-2d6b-49ed-ae68-37330753c98c", "metadata": {}, "source": [ "### Numpy Docstrings\n", "\n", "if you have [numpy docstrings](https://numpydoc.readthedocs.io/en/latest/format.html) instead of `docments`, `show_doc` will attempt to parse and render those just like `docments`:" ] }, { "cell_type": "code", "execution_count": null, "id": "37c1c293-40f6-47fd-8d25-a5cb005b3b3b", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "---\n", "\n", "#### f\n", "\n", "> f (x=1)\n", "\n", "func docstring in the numpy style.\n", "\n", "| | **Type** | **Default** | **Details** |\n", "| -- | -------- | ----------- | ----------- |\n", "| x | int | 1 | the parameter x |\n", "| **Returns** | **None** | | **this function doesn't return anything** |" ], "text/plain": [ "<__main__.BasicMarkdownRenderer at 0x7fc8d5d194f0>" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def f(x=1):\n", " \"\"\"\n", " func docstring in the numpy style.\n", " \n", " Parameters\n", " ----------\n", " x : int\n", " the parameter x\n", " \n", " Returns\n", " -------\n", " None\n", " this function doesn't return anything\n", " \"\"\"\n", " ...\n", "\n", "show_doc(f)" ] }, { "cell_type": "markdown", "id": "5f668274-4fd0-4c73-b9cf-eba4fd995543", "metadata": {}, "source": [ ":::{.callout-warning}\n", "Numpy docstring formatting is very strict. If your docstrings do not strictly adhere to the numpy format, it will not be parsed properly and information about parameters and return values may not properly be rendered in the table below the signature. Where possible, we recommend using `docments` to annonate your function instead.\n", ":::" ] }, { "cell_type": "markdown", "id": "c690f30b-628f-499a-a4d7-f91f872bdd34", "metadata": {}, "source": [ "## `show_doc` on Classes\n", "\n", "show_doc works on Classes, too including when you use `@patch`:" ] }, { "cell_type": "code", "execution_count": null, "id": "1c28ec9d-29ff-497f-97b0-ca262f530cf6", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "---\n", "\n", "### Foo\n", "\n", "> Foo (d:'str', e:'int')\n", "\n", "This is the docstring for the __init__ method" ], "text/plain": [ "<__main__.BasicMarkdownRenderer at 0x7fc8d5d19be0>" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class Foo:\n", " def __init__(self, d:str,e:int):\n", " \"This is the docstring for the __init__ method\"\n", " ...\n", " @property\n", " def some_prop(self): \n", " \"This is a class property.\"\n", " return 'foo property'\n", "\n", "show_doc(Foo)" ] }, { "cell_type": "markdown", "id": "20010f4e-5f96-4929-9c1f-5fc85f7c0d5f", "metadata": {}, "source": [ "You can define methods for the class `Foo` with `@patch` which is convenient in allowing you to break up code for documentation in notebooks:" ] }, { "cell_type": "code", "execution_count": null, "id": "3441e6c7-472b-411c-a179-b1e37dcbceac", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "---\n", "\n", "#### Foo.a_method\n", "\n", "> Foo.a_method (a:list, b:dict, c)\n", "\n", "This is a method\n", "\n", "| | **Type** | **Details** |\n", "| -- | -------- | ----------- |\n", "| a | list | param a |\n", "| b | dict | |\n", "| c | | |" ], "text/plain": [ "<__main__.BasicMarkdownRenderer at 0x7fc8d5d22670>" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@patch\n", "def a_method(self:Foo, \n", " a:list, # param a\n", " b:dict,c):\n", " \"This is a method\"\n", " ...\n", "\n", "_res = show_doc(Foo.a_method)\n", "_res" ] }, { "cell_type": "code", "execution_count": null, "id": "a05a8a3c-7e89-41ff-857f-7479492fc9b0", "metadata": {}, "outputs": [], "source": [ "#|hide\n", "# signature and docment should show properly when using @patch\n", "assert '(a:list, b:dict, c)' in _res._repr_markdown_()\n", "assert 'param a' in _res._repr_markdown_()" ] }, { "cell_type": "markdown", "id": "fe518a59-07e4-4672-8a51-b3cc6475b4ba", "metadata": {}, "source": [ "Class properties also work with showdoc:" ] }, { "cell_type": "code", "execution_count": null, "id": "f2727145-be26-4fd1-b67c-78efae89b871", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "---\n", "\n", "#### Foo.some_prop\n", "\n", "\n", "\n", "This is a class property." ], "text/plain": [ "<__main__.BasicMarkdownRenderer at 0x7fc8d5d220a0>" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "_res = show_doc(Foo.some_prop)\n", "_res" ] }, { "cell_type": "code", "execution_count": null, "id": "66667142-1f69-4beb-b9a6-ba0475358acf", "metadata": {}, "outputs": [], "source": [ "#|hide\n", "test_eq(_res._repr_markdown_(), '---\\n\\n#### Foo.some_prop\\n\\n\\n\\nThis is a class property.')" ] }, { "cell_type": "code", "execution_count": null, "id": "147626ee", "metadata": {}, "outputs": [], "source": [ "#|export\n", "class BasicHtmlRenderer(ShowDocRenderer):\n", " def _repr_html_(self):\n", " doc = '
\\n'\n", " lvl = 4 if self.isfunc else 3\n", " doc += f'{self.nm}\\n
{self.nm}{self.sig}
'\n", " if self.docs: doc += f\"

{self.docs}

\"\n", " return doc" ] }, { "cell_type": "code", "execution_count": null, "id": "059071e3", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "

F

\n", "
F(x: 'int' = 1)

class docstring

" ], "text/plain": [ "<__main__.BasicHtmlRenderer at 0x7fc8d5ce0e20>" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class F:\n", " \"class docstring\"\n", " def __init__(self, x:int=1): ...\n", "\n", " @classmethod\n", " def class_method(cls, \n", " foo:str, # docment for parameter foo\n", " bar:int):\n", " \"This is a class method.\"\n", " pass\n", " \n", " def regular_method(self,\n", " baz:bool=True, # docment for parameter baz\n", " ):\n", " \"This is a regular method\"\n", " pass\n", "\n", " \n", "show_doc(F, renderer=BasicHtmlRenderer)" ] }, { "cell_type": "code", "execution_count": null, "id": "e217c559-58b6-49a5-bb58-1575a4a42ded", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "---\n", "\n", "### F.class_method\n", "\n", "> F.class_method (foo:str, bar:int)\n", "\n", "This is a class method.\n", "\n", "| | **Type** | **Details** |\n", "| -- | -------- | ----------- |\n", "| foo | str | docment for parameter foo |\n", "| bar | int | |" ], "text/plain": [ "<__main__.BasicMarkdownRenderer at 0x7fc8d5ce0fd0>" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "_res = show_doc(F.class_method)\n", "_res" ] }, { "cell_type": "code", "execution_count": null, "id": "f2866a4b-0307-4700-9c23-a2c7de078f54", "metadata": {}, "outputs": [], "source": [ "#|hide\n", "# There should be docments for a class method\n", "assert 'docment for parameter foo' in _res._repr_markdown_(), 'No docment found for class method'" ] }, { "cell_type": "code", "execution_count": null, "id": "57a1e24f-2534-41d7-ad98-ade5d42f096c", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "---\n", "\n", "#### F.regular_method\n", "\n", "> F.regular_method (baz:bool=True)\n", "\n", "This is a regular method\n", "\n", "| | **Type** | **Default** | **Details** |\n", "| -- | -------- | ----------- | ----------- |\n", "| baz | bool | True | docment for parameter baz |" ], "text/plain": [ "<__main__.BasicMarkdownRenderer at 0x7fc8d5d225e0>" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "show_doc(F.regular_method)" ] }, { "cell_type": "code", "execution_count": null, "id": "35043aa7-6b60-4ddf-8949-39a78577f23d", "metadata": {}, "outputs": [], "source": [ "#|export\n", "def showdoc_nm(tree):\n", " \"Get the fully qualified name for showdoc.\"\n", " return ifnone(get_patch_name(tree), tree.name)" ] }, { "cell_type": "code", "execution_count": null, "id": "fe22731d-ad7d-4080-ac91-e0d24e8b681c", "metadata": {}, "outputs": [], "source": [ "#|hide\n", "import ast\n", "code=\"\"\"\n", "@bar\n", "@patch\n", "@foo\n", "def a_method(self:Foo, a:list,b:dict,c):\n", " \"This is a method\"\n", " ...\n", "\"\"\"\n", "\n", "code2=\"\"\"\n", "@bar\n", "@foo\n", "def a_method(self:Foo, a:list,b:dict,c):\n", " \"This is a method\"\n", " ...\n", "\"\"\"\n", "\n", "_tree = ast.parse(code).body[0]\n", "test_eq(showdoc_nm(_tree), 'Foo.a_method')\n", "\n", "_tree2 = ast.parse(code2).body[0]\n", "test_eq(showdoc_nm(_tree2), 'a_method')" ] }, { "cell_type": "markdown", "id": "4be98ffb", "metadata": {}, "source": [ "## Other helpers" ] }, { "cell_type": "code", "execution_count": null, "id": "e947414d", "metadata": {}, "outputs": [], "source": [ "#|export\n", "def colab_link(path):\n", " \"Get a link to the notebook at `path` on Colab\"\n", " from IPython.display import Markdown\n", " cfg = get_config()\n", " pre = 'https://colab.research.google.com/github/'\n", " res = f'{pre}{cfg.user}/{cfg.lib_name}/blob/{cfg.branch}/{cfg.path(\"nbs_path\").name}/{path}.ipynb'\n", " display(Markdown(f'[Open `{path}` in Colab]({res})'))" ] }, { "cell_type": "code", "execution_count": null, "id": "80e0a6cd", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "[Open `index` in Colab](https://colab.research.google.com/github/fastai/nbdev/blob/master/nbs/index.ipynb)" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "colab_link('index')" ] }, { "cell_type": "markdown", "id": "ac6f8e04-ff14-4978-a4ba-09f7a2b91098", "metadata": {}, "source": [ "## Test Edgecases -" ] }, { "cell_type": "code", "execution_count": null, "id": "66569922", "metadata": {}, "outputs": [], "source": [ "#|hide\n", "e = enum.Enum('e', 'a b')\n", "test_eq(show_doc(e)._repr_markdown_(), '---\\n\\n### e\\n\\n> e (value, names=None, module=None, qualname=None, type=None, start=1)\\n\\nAn enumeration.')" ] }, { "cell_type": "code", "execution_count": null, "id": "c8d4499b", "metadata": {}, "outputs": [], "source": [ "#|hide\n", "# disabled due to py310 anno issue\n", "# from typing import Sequence\n", "\n", "# @dataclass\n", "# class A:\n", "# \"Test dataclass\"\n", "# a:int = 2 # First\n", "# b:Sequence[int] = (1,2,3) # Second\n", "\n", "# def test(self, \n", "# c:int = 1, # it's a test\n", "# d:str = 'test' # it's a second test\n", "# )->str: # it's a return string\n", "# return d\n", " \n", "# test_eq(show_doc(A)._repr_markdown_(), '---\\n\\n### A\\n\\n> A (a:int=2, b:Sequence[int]=(1,2,3))\\n\\nTest dataclass')\n", "# test_eq(show_doc(A.test)._repr_markdown_(),\n", "# \"---\\n\\n#### A.test\\n\\n> A.test (c:int=1, d:str='test')\\n\\n| | **Type** | **Default** | **Details** |\\n| -- | -------- | ----------- | ----------- |\\n| c | int | 1 | it's a test |\\n| d | str | test | it's a second test |\\n| **Returns** | **str** | | **it's a return string** |\")" ] }, { "cell_type": "code", "execution_count": null, "id": "bedf2b4d-4ee1-4836-ae91-416ab7c346a9", "metadata": {}, "outputs": [], "source": [ "#|hide\n", "from fastcore.dispatch import typedispatch\n", "\n", "@typedispatch\n", "def _typ_test(\n", " a:list, # A list\n", " b:str, # A second integer\n", ") -> float:\n", " \"Perform op\"\n", " return a.extend(b)\n", "\n", "@typedispatch\n", "def _typ_test(\n", " a:str, # An integer\n", " b:str # A str\n", ") -> float:\n", " \"Perform op\"\n", " return str(a) + b\n", "\n", "test_eq(show_doc(_typ_test), None) # show_doc ignores typedispatch at the moment" ] }, { "cell_type": "markdown", "id": "ec7a2f01", "metadata": {}, "source": [ "## Export -" ] }, { "cell_type": "code", "execution_count": null, "id": "d20a1ed8", "metadata": {}, "outputs": [], "source": [ "#|hide\n", "#|eval: false\n", "from nbdev.doclinks import nbdev_export\n", "nbdev_export()" ] }, { "cell_type": "code", "execution_count": null, "id": "ee92c8ba-b284-4608-8725-ac6fe21b5d4d", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 5 }