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: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Here are some tips on using fastcore:
- **Advanced file operations**: Use the extended `Path` class, which adds methods like `ls()`, `read_json()`, and others to [`pathlib.Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path).
- **Flexible data structures**: Convert between dictionaries and attribute-access objects using [`dict2obj`](https://fastcore.fast.ai/xtras.html#dict2obj) and [`obj2dict`](https://fastcore.fast.ai/xtras.html#obj2dict) for more intuitive data handling.
- **Functional programming paradigms**: Use tools like [`compose`](https://fastcore.fast.ai/basics.html#compose), [`maps`](https://fastcore.fast.ai/basics.html#maps), and [`filter_ex`](https://fastcore.fast.ai/basics.html#filter_ex) to write more functional-style Python code.
- **Documentation**: Use [`docments`](https://fastcore.fast.ai/docments.html#docments) where possible to document parameters of functions and methods.
- **Documentation**: Document parameters and return values with [`docments`](https://fastcore.fast.ai/docments.html#docments), using source comments or `Annotated` metadata for generated APIs. [`MarkdownRenderer`](https://fastcore.fast.ai/docments.html#markdownrenderer) shows the effective signature and retains usage sections such as Notes, Raises, and Examples.
- **Time-aware caching**: Apply the [`timed_cache`](https://fastcore.fast.ai/xtras.html#timed_cache) decorator to add time-based expiration to the standard `lru_cache` functionality.
- **Simplified CLI creation**: Use `fastcore.script` to easily transform Python functions into command-line interfaces.

Expand Down
2 changes: 1 addition & 1 deletion fastcore/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
- **Advanced file operations**: Use the extended `Path` class, which adds methods like `ls()`, `read_json()`, and others to `pathlib.Path`.
- **Flexible data structures**: Convert between dictionaries and attribute-access objects using `dict2obj` and `obj2dict` for more intuitive data handling.
- **Functional programming paradigms**: Use tools like `compose`, `maps`, and `filter_ex` to write more functional-style Python code.
- **Documentation**: Use `docments` where possible to document parameters of functions and methods.
- **Documentation**: Document parameters and return values with `docments`, using source comments or `Annotated` metadata for generated APIs. `MarkdownRenderer` shows the effective signature and retains usage sections such as Notes, Raises, and Examples.
- **Time-aware caching**: Apply the `timed_cache` decorator to add time-based expiration to the standard `lru_cache` functionality.
- **Simplified CLI creation**: Use `fastcore.script` to easily transform Python functions into command-line interfaces.

Expand Down
11 changes: 3 additions & 8 deletions fastcore/_modidx.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,11 +315,8 @@
'fastcore.docments.DocmentTbl.__eq__': ('docments.html#docmenttbl.__eq__', 'fastcore/docments.py'),
'fastcore.docments.DocmentTbl.__init__': ('docments.html#docmenttbl.__init__', 'fastcore/docments.py'),
'fastcore.docments.DocmentTbl._columns': ('docments.html#docmenttbl._columns', 'fastcore/docments.py'),
'fastcore.docments.DocmentTbl._hdr_list': ('docments.html#docmenttbl._hdr_list', 'fastcore/docments.py'),
'fastcore.docments.DocmentTbl._repr_markdown_': ( 'docments.html#docmenttbl._repr_markdown_',
'fastcore/docments.py'),
'fastcore.docments.DocmentTbl._row': ('docments.html#docmenttbl._row', 'fastcore/docments.py'),
'fastcore.docments.DocmentTbl._row_list': ('docments.html#docmenttbl._row_list', 'fastcore/docments.py'),
'fastcore.docments.DocmentTbl.has_docment': ( 'docments.html#docmenttbl.has_docment',
'fastcore/docments.py'),
'fastcore.docments.DocmentTbl.has_return': ( 'docments.html#docmenttbl.has_return',
Expand Down Expand Up @@ -361,12 +358,11 @@
'fastcore.docments._DocstringExtractor.visit_Module': ( 'docments.html#_docstringextractor.visit_module',
'fastcore/docments.py'),
'fastcore.docments._bold': ('docments.html#_bold', 'fastcore/docments.py'),
'fastcore.docments._callable_instance': ('docments.html#_callable_instance', 'fastcore/docments.py'),
'fastcore.docments._clean_comment': ('docments.html#_clean_comment', 'fastcore/docments.py'),
'fastcore.docments._clean_text_sig': ('docments.html#_clean_text_sig', 'fastcore/docments.py'),
'fastcore.docments._docstring': ('docments.html#_docstring', 'fastcore/docments.py'),
'fastcore.docments._escape_markdown': ('docments.html#_escape_markdown', 'fastcore/docments.py'),
'fastcore.docments._f_name': ('docments.html#_f_name', 'fastcore/docments.py'),
'fastcore.docments._fmt_anno': ('docments.html#_fmt_anno', 'fastcore/docments.py'),
'fastcore.docments._fmt_default': ('docments.html#_fmt_default', 'fastcore/docments.py'),
'fastcore.docments._fmt_sig': ('docments.html#_fmt_sig', 'fastcore/docments.py'),
'fastcore.docments._fullname': ('docments.html#_fullname', 'fastcore/docments.py'),
Expand All @@ -377,13 +373,11 @@
'fastcore.docments._ital_first': ('docments.html#_ital_first', 'fastcore/docments.py'),
'fastcore.docments._list2row': ('docments.html#_list2row', 'fastcore/docments.py'),
'fastcore.docments._maybe_nm': ('docments.html#_maybe_nm', 'fastcore/docments.py'),
'fastcore.docments._merge_doc': ('docments.html#_merge_doc', 'fastcore/docments.py'),
'fastcore.docments._merge_docs': ('docments.html#_merge_docs', 'fastcore/docments.py'),
'fastcore.docments._non_empty_keys': ('docments.html#_non_empty_keys', 'fastcore/docments.py'),
'fastcore.docments._param_locs': ('docments.html#_param_locs', 'fastcore/docments.py'),
'fastcore.docments._parses': ('docments.html#_parses', 'fastcore/docments.py'),
'fastcore.docments._show_param': ('docments.html#_show_param', 'fastcore/docments.py'),
'fastcore.docments._tokens': ('docments.html#_tokens', 'fastcore/docments.py'),
'fastcore.docments._type_str': ('docments.html#_type_str', 'fastcore/docments.py'),
'fastcore.docments._unwrap_sym': ('docments.html#_unwrap_sym', 'fastcore/docments.py'),
'fastcore.docments.ann_parts': ('docments.html#ann_parts', 'fastcore/docments.py'),
'fastcore.docments.can_render': ('docments.html#can_render', 'fastcore/docments.py'),
Expand Down Expand Up @@ -650,6 +644,7 @@
'fastcore.nbio.new_nb': ('nbio.html#new_nb', 'fastcore/nbio.py'),
'fastcore.nbio.preferred_msg_out': ('nbio.html#preferred_msg_out', 'fastcore/nbio.py'),
'fastcore.nbio.preferred_out': ('nbio.html#preferred_out', 'fastcore/nbio.py'),
'fastcore.nbio.prev_line': ('nbio.html#prev_line', 'fastcore/nbio.py'),
'fastcore.nbio.read_nb': ('nbio.html#read_nb', 'fastcore/nbio.py'),
'fastcore.nbio.render_output': ('nbio.html#render_output', 'fastcore/nbio.py'),
'fastcore.nbio.render_outputs': ('nbio.html#render_outputs', 'fastcore/nbio.py'),
Expand Down
50 changes: 25 additions & 25 deletions fastcore/apisurface.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
__all__ = ['snake', 'sanitize_param_name', 'sanitized_params', 'mk_sig', 'mk_doc', 'OpGroup', 'mk_groups', 'full_docs']

# %% ../nbs/05a_apisurface.ipynb #b2797a42
import keyword
import keyword,inspect
from inspect import Parameter, Signature
from urllib.parse import urlparse, urljoin
from .imports import *
from .basics import *
from .xtras import UNSET
from .docments import ann_parts

# %% ../nbs/05a_apisurface.ipynb #475598fa
_pat_non_alnum = re.compile(r"[^a-zA-Z0-9]+")
Expand All @@ -32,13 +33,6 @@ def snake(s: str):
# %% ../nbs/05a_apisurface.ipynb #3e19a6d0
def sanitize_param_name(p): return snake(re.sub(r'\W', '_', p).strip('_'))

# %% ../nbs/05a_apisurface.ipynb #f44a2c97
def _mk_param(name, required, anno=None, default=None):
"Create a function signature parameter."
anno = Parameter.empty if anno is None else anno
if default is None: default = Parameter.empty if required else UNSET
return Parameter(name, kind=Parameter.POSITIONAL_OR_KEYWORD, default=default, annotation=anno)

# %% ../nbs/05a_apisurface.ipynb #c10b6dcc
def sanitized_params(ps):
"Mapping from spec param names `ps` to valid Python identifiers; exact names win collisions."
Expand All @@ -55,21 +49,28 @@ def _sani(p):
res[p] = name
return res

# %% ../nbs/05a_apisurface.ipynb #32fca522
# %% ../nbs/05a_apisurface.ipynb #f44a2c97
def _mk_param(name, required, anno=None, default=Parameter.empty):
"Create a function signature parameter."
anno = Parameter.empty if anno is None else anno
if default is Parameter.empty: default = Parameter.empty if required else UNSET
return Parameter(name, kind=Parameter.POSITIONAL_OR_KEYWORD, default=default, annotation=anno)

def _sort_key(o):
if o.default is Parameter.empty: return 0
if o.default is UNSET: return 1
return 2

# %% ../nbs/05a_apisurface.ipynb #4f63ac5e
def mk_sig(op, sparams=None, defaults=None):
"An `inspect.Signature` for operation record `op`; `defaults` values make their params optional."
"An operation signature with parameter descriptions in `Annotated`; `defaults` makes those params optional"
if sparams is None: sparams = sanitized_params(op.params)
defaults = defaults or {}
params = []
for pname, sname in sparams.items():
default = defaults.get(pname, op.param_defaults.get(pname))
params.append(_mk_param(sname, pname in op.required_params, op.param_types.get(pname), default))
default = defaults.get(pname, op.param_defaults.get(pname, Parameter.empty))
anno = op.param_types.get(pname)
if doc := op.param_docs.get(pname): anno = typing.Annotated[anno or typing.Any, doc]
params.append(_mk_param(sname, pname in op.required_params, anno, default))
return Signature(sorted(params, key=_sort_key))

# %% ../nbs/05a_apisurface.ipynb #940536d9
Expand All @@ -81,14 +82,6 @@ def _op_summary(op):
base = f"{p.scheme}://{p.netloc}"
return re.sub(r"\]\((/[^)]+)\)", lambda m: f"]({urljoin(base, m[1].strip())})", s)

# %% ../nbs/05a_apisurface.ipynb #08067b8a
def _op_line(op, sig):
head = f"{'.'.join(snake(g) for g in listify(op.group))}.{op.name}"
if op.docs_url: head = f"[{head}]({op.docs_url})"
s = f"({', '.join(sig.parameters)})"
summ = _op_summary(op)
return f"{head}{s}: *{summ}*"

# %% ../nbs/05a_apisurface.ipynb #ed62e194
def mk_doc(op, sig, sparams):
"Render operation docstring with summary, docs URL, and parameter hints."
Expand All @@ -102,11 +95,19 @@ def mk_doc(op, sig, sparams):
for nm,p in sig.parameters.items():
orig = rsparams.get(nm, nm)
r = f"default: {p.default!r}" if p.default not in (Parameter.empty, UNSET) else "required" if orig in req else "optional"
ann = '' if p.annotation is Parameter.empty else p.annotation.__name__
ann = '' if p.annotation is Parameter.empty else inspect.formatannotation(ann_parts(p.annotation)[0]).removeprefix('typing.')
desc = (op.param_docs or {}).get(orig, "")
lines.append(f"- {nm} ({ann}, {r}){': ' + desc if desc else ''}")
return "\n".join(lines)

# %% ../nbs/05a_apisurface.ipynb #08067b8a
def _op_line(op, sig):
head = f"{'.'.join(snake(g) for g in listify(op.group))}.{op.name}"
if op.docs_url: head = f"[{head}]({op.docs_url})"
s = f"({', '.join(sig.parameters)})"
summ = _op_summary(op)
return f"{head}{s}: *{summ}*"

# %% ../nbs/05a_apisurface.ipynb #e4d35409
class OpGroup:
"Namespace for grouped operations: each op is an attribute, and the repr lists them all"
Expand All @@ -123,10 +124,9 @@ def __doc__(self):
def __dir__(self): return object.__dir__(self)
def __allow__(self): return self.ops + list(self.subgroups.values())

def _repr_markdown_(self): return self.__doc__
def _repr_markdown_(self): return self.__doc__ + '\n\nOverview only. Read `doc(group.operation)` for parameter details and `doc(group.subgroup)` to descend.'
__repr__ = _repr_markdown_


# %% ../nbs/05a_apisurface.ipynb #62b12f6b
def mk_groups(ops):
"Nested tree of `OpGroup`s from ops, following each op's `group` path."
Expand All @@ -152,6 +152,6 @@ def _group_docs(name, g, lvl=2):
return "\n\n".join(res)

def full_docs(groups):
"Complete markdown API reference for a `mk_groups` tree: every group and operation."
"Markdown overview of every group and operation in a `mk_groups` tree."
return "\n\n".join(_group_docs(nm, g) for nm,g in sorted(groups.items()))

Loading