# fastcore Module Documentation ## fastcore.aio > Bridging async and sync code: `run_sync`, `iter_sync`, `ctx_sync`, `athreaded`, `maybe_await`, and `then` > > ## Calling async code from sync code > > Async libraries return coroutines, which normally force every caller up the stack to become async too. `run_sync` (coroutines), `iter_sync` (async generators), and `ctx_sync` (async context managers) let ordinary sync code drive them instead, sharing a single event loop that runs on a background daemon thread, created on first use; `maybe_await(o)` awaits `o` only if it's awaitable, else returns it as-is. > > ## Calling sync code from async code > > `athreaded` is the same bridge crossed in the other direction: it wraps a blocking function as an honest `async def`, running the original body in a worker thread so the event loop stays free. Use it to give async color to APIs that only exist in blocking form. > > `then` lets one function body serve sync and async callers alike. It applies each of `fs` in turn, awaiting anything awaitable along the way (the starting value or any step's result), and the caller gets back a plain value if the whole chain was sync, or an awaitable otherwise. This is what a convenience method on a client offering both sync and async modes needs: written as `return then(self.gists.get(gid), ~Self.files.values(), first)`, a sync client returns the file directly while an async client returns something to `await`, with no duplicated method. Because step results are awaited as part of the chain, steps may themselves be async calls; the flip side is that a step cannot return an awaitable *as* its value. > > Docs: https://fastcore.fast.ai/aio.html.md - `def run_sync(coro)` Run coroutine `coro` to completion from sync code and return its result - `def iter_sync(agen)` Iterate async generator `agen` from sync code - `@contextmanager def ctx_sync(acm)` Use async context manager `acm` in a plain `with` block - `@metadec def athreaded(f)` Run `f` in a worker thread, awaitably; use as `@athreaded` or `@athreaded(executor=...)` - `def maybe_await(o)` Await `o` if needed, and return it - `def then(x, *fs)` Pipe `x` through each of `fs`, awaiting values as needed; result is awaitable only if `x` or a step result is - `def acache(f)` Cache results of async function `f` - `class CachedAwaitable` Cache the result from an awaitable - `def __init__(self, o)` - `def __await__(self)` - `def reawaitable(func)` Wraps the result of an asynchronous function into an object which can be awaited more than once - `def is_async_callable(obj)` Check if `obj` is an async callable, handling `partial` wrappers and callable instances - `def to_aiter(items)` Async yield each item in `items` with `asyncio.sleep(0)` between - `def maybe_aiter(items)` If `items` already async, return it; otherwise to_aiter - `def mapa(f, items)` Async `map`; apply `f` (sync or async) to `items` (sync or async iter) concurrently via `gather` - `def noopa(x, *args, **kwargs)` Do nothing (async) - `def enable_async_magics(ip, fmt)` Let line and cell magics on `ip` be async: coroutine results are awaited. Idempotent per shell. - `def disable_async_magics(ip)` Undo `enable_async_magics` on `ip` ## fastcore.ansi > Filters for processing ANSI colors. - `def strip_ansi(source, term_queries)` Remove ANSI escape codes from text. - `def ansi2html(text)` Convert ANSI colors to HTML colors. - `def ansi2latex(text)` Convert ANSI colors to LaTeX colors. ## fastcore.apisurface > Turn operation metadata into documented, introspectable callables: real signatures, informative docstrings, and browsable grouped namespaces > > A client generated from a machine-readable spec (an OpenAPI document, a Google Discovery document, an SDK symbol graph) is only pleasant to use if its runtime-created callables behave like hand-written ones: tab completion shows real parameters, `?` and `doc()` show real docs, and related operations sit together in namespaces you can browse. This module builds those pieces from plain *operation records*, ducks with these attributes: `name`, `group` (a nesting path: string or list), `summary`, `docs_url`, `params` (ordered parameter names), `required_params`, `param_types`, `param_defaults`, and `param_docs`. `mk_sig` turns a record into an `inspect.Signature` (spec names sanitized to Python identifiers via `sanitized_params`), `mk_doc` renders a docstring, and `OpGroup`/`mk_groups`/`full_docs` assemble named callables into an attribute-chained tree with markdown summaries at every level. > > `fastspec` builds its HTTP clients on this layer. > > Docs: https://fastcore.fast.ai/apisurface.html.md - `def snake(s)` Convert an identifier-ish string to snake_case. - `def sanitized_params(ps)` Mapping from spec param names `ps` to valid Python identifiers; exact names win collisions. - `def mk_sig(op, sparams, defaults)` An `inspect.Signature` for operation record `op`; `defaults` values make their params optional. - `def mk_doc(op, sig, sparams)` Render operation docstring with summary, docs URL, and parameter hints. - `class OpGroup` Namespace for grouped operations: each op is an attribute, and the repr lists them all - `def __init__(self, name, ops)` - `@property def __doc__` - `def __dir__(self)` - `def __allow__(self)` - `def mk_groups(ops)` Nested tree of `OpGroup`s from ops, following each op's `group` path. - `def full_docs(groups)` Complete markdown API reference for a `mk_groups` tree: every group and operation. ## fastcore.basics > Basic functionality used in the fastai library > > The staples used throughout fastai code, each replacing a common multi-line pattern with a one-liner: `ifnone(a,b)` is `b if a is None else a` (though both args are always evaluated); `listify` and `tuplify` convert anything to a list or tuple the way you'd mean it (`None` becomes `[]`, a `str` or `dict` stays a single item, a generator is consumed); and `basic_repr('a,b')` gives a class a deterministic `key=value` repr with no memory address, so notebook and git diffs stay clean. > > Curried versions of the comparison and arithmetic functions in Python's `operator` module: `lt gt le ge eq ne add sub mul truediv is_ is_not in_ mod`. With two args they work like `operator`'s versions; with one arg they return a partial that binds it as the *second* argument, so `lt(3)` means "is less than 3" and `in_(vals)` means "is contained in `vals`". They read especially well as `cmp` arguments to `fastcore.test.test`, e.g. `test(x, valid, in_)`. > > `AttrDict` is a `dict` whose keys are also attributes, so `d.foo` reads and writes `d['foo']` (to convert a whole nested structure at once, see `dict2obj` in `fastcore.xtras`); `NS` is the same idea built on `SimpleNamespace`, adding indexing and iteration. `store_attr()`, called inside `__init__`, stores the function's arguments as attributes of `self` in one line: > > ```python > class Point: > def __init__(self, x, y, scale=1): store_attr() > p = Point(1, 2) > test_eq((p.x, p.y, p.scale), (1, 2, 1)) > ``` > > Tools for making and transforming functions: `compose(f,g,...)` chains functions left to right; `bind` is `partial` extended with `arg0`,`arg1`,... placeholders for reordering positional arguments; and `fail_clean` re-raises exceptions with the library's own traceback frames stripped, for errors that are part of a function's contract rather than bugs. > > `~Self` is a concise alternative to `lambda` for a function that operates on a single object (note the capitalization!). Write the chain of attribute accesses, method calls, and indexing just as you would after a variable name, with `~Self` in its place, and the result is a plain function that runs the chain on its argument: > > - `~Self.sum()` is `lambda o: o.sum()` > - `~Self.imag` is `lambda o: o.imag` > - `~Self[1]` is `lambda o: o[1]` > - `~Self.sum().real` is `lambda o: o.sum().real` > - `~Self` alone is the identity, `lambda o: o` > > Since `.`, `()`, and `[]` bind tighter than `~`, the whole chain builds first and `~` then converts it to a function, so the chain never needs its own parentheses: `map(~Self.imag, nums)` works as written. > > `@patch` adds a function to an existing class as a method, using the function's `self:` type annotation to pick the class (a union annotation patches several classes at once); `@patch_to(Cls)` is the same with the class passed explicitly. Both take `as_prop`, `set_prop`, and `cls_method`. fastai code uses this to build classes incrementally across a notebook, so expect to find a class's methods defined far from the class itself: > > ```python > @patch > def total(self:Point): return self.x + self.y > test_eq(Point(3, 4).total(), 7) > ``` > > Docs: https://fastcore.fast.ai/basics.html.md - `def ifnone(a, b)` `b` if `a` is None else `a` - `def req(x, msg, exc)` `x` unless it's `None`, in which case raise `exc(msg)` - `def maybe_attr(o, attr)` `getattr(o,attr,o)` - `def basic_repr(flds)` Minimal `__repr__` - `class BasicRepr` Base class for objects needing a basic `__repr__` - `def is_array(x)` `True` if `x` supports `__array__` or `iloc` - `def listify(o, *rest)` Convert `o` to a `list` - `def tuplify(o, use_list, match)` Make `o` a tuple - `def true(x)` Test whether `x` is truthy; collections with >0 elements are considered `True` - `class NullType` An object that is `False` and can be called, chained, and indexed - `def __getattr__(self, *args)` - `def __call__(self, *args, **kwargs)` - `def __getitem__(self, *args)` - `def __bool__(self)` - `def tonull(x)` Convert `None` to `null` - `def get_class(nm, *fld_names, **flds)` Dynamically create a class, optionally inheriting from `sup`, containing `fld_names` - `def mk_class(nm, *fld_names, **flds)` Create a class using `get_class` and add to the caller's module - `def wrap_class(nm, *fld_names, **flds)` Decorator: makes function a method of a new class `nm` passing parameters to `mk_class` - `class ignore_exceptions` Context manager to ignore exceptions - `def __enter__(self)` - `def __exit__(self, *args)` - `def exec_local(code, var_name)` Call `exec` on `code` and return the var `var_name` - `def risinstance(types, obj)` Curried `isinstance` but with args reversed - `class Inf` Infinite lists - `def in_(x, a)` `True` if `x in a` - `def ret_true(*args, **kwargs)` Predicate: always `True` - `def ret_false(*args, **kwargs)` Predicate: always `False` - `def stop(e)` Raises exception `e` (by default `StopIteration`) - `def gen(func, seq, cond)` Like `(func(o) for o in seq if cond(func(o)))` but handles `StopIteration` - `def chunked(it, chunk_sz, drop_last, n_chunks, pad, pad_val)` Return batches from iterator `it` of size `chunk_sz` (or return `n_chunks` total) - `def otherwise(x, tst, y)` `y if tst(x) else x` - `def custom_dir(c, add)` Implement custom `__dir__`, adding `add` to `cls` - `class adict` `dict` subclass that also provides access to keys as attrs - `def __getattr__(self, k)` - `def __setattr__(self, k, v)` - `def __dir__(self)` - `class AttrDict` `dict` subclass that also provides access to keys as attrs, and has a pretty markdown repr - `def copy(self)` - `class AttrDictDefault` `AttrDict` subclass that returns `default_` for missing attrs - `def __init__(self, *args, **kwargs)` - `def __getattr__(self, k)` - `class NS` `SimpleNamespace` subclass that also adds `iter` and `dict` support - `def __iter__(self)` - `def __getitem__(self, x)` - `def __setitem__(self, x, y)` - `def get_annotations_ex(obj)` Backport of py3.10 `get_annotations` that returns globals/locals - `def eval_type(t, glb, loc)` `eval` a type or collection of types, if needed, for annotations in py3.10+ - `def type_hints(f)` Like `typing.get_type_hints` but returns `{}` if not allowed type - `def annotations(o)` Annotations for `o`, or `type(o)` - `def anno_ret(func)` Get the return annotation of `func` - `def signature_ex(obj, eval_str)` Backport of `inspect.signature(..., eval_str=True` to '9.2k' - `def class2attr(self, cls_name)` Return the snake-cased name of the class; strip ending `cls_name` if it exists. - `def getcallable(o, attr)` Calls `getattr` with a default of `noop` - `def getattrs(o, *attrs)` List of all `attrs` in `o` - `def hasattrs(o, attrs)` Test whether `o` contains all `attrs` - `def try_attrs(obj, *attrs)` Return first attr that exists in `obj` - `class DepProp` Property decorator with dependency update triggering - `def __init__(self, fchange, fnorm)` - `def __set_name__(self, owner, name)` - `def norm(self, fn)` - `def __get__(self, o, objtype)` - `def __set__(self, o, v)` - `def __delete__(self, o)` - `class GetAttrBase` Basic delegation of `__getattr__` and `__dir__` - `def __getattr__(self, k)` - `def __dir__(self)` - `class GetAttr` Inherit from this to have all attr accesses in `self._xtra` passed down to `self.default` - `def __getattr__(self, k)` - `def __dir__(self)` - `def __setstate__(self, data)` - `def delegate_attr(self, k, to)` Use in `__getattr__` to delegate to attr `to` without inheriting from `GetAttr` - `class ShowPrint` Base class that prints for `show` - `def show(self, *args, **kwargs)` - `class Int` An extensible `int` - `class Str` An extensible `str` - `class Float` An extensible `float` - `def partition(coll, f)` Partition a collection by a predicate - `def partition_dict(d, f)` Partition a dict by a predicate that takes key/value params - `def flatten(o)` Concatenate all collections and items as a generator - `def concat(colls)` Concatenate all collections and items as a list - `def strcat(its, sep)` Concatenate stringified items `its` - `def detuplify(x)` If `x` is a tuple with one thing, extract it - `def replicate(item, match)` Create tuple of `item` copied `len(match)` times - `def setify(o)` Turn any list like-object into a set. - `def merge(*ds)` Merge all dictionaries in `ds` - `def range_of(x)` All indices of collection `x` (i.e. `list(range(len(x)))`) - `def groupby(x, key, val)` Like `itertools.groupby` but doesn't need to be sorted, and isn't lazy, plus some extensions - `def last_index(x, o)` Finds the last index of occurence of `x` in `o` (returns -1 if no occurence) - `def filter_dict(d, func)` Filter a `dict` using `func`, applied to keys and values - `def filter_keys(d, func)` Filter a `dict` using `func`, applied to keys - `def filter_values(d, func)` Filter a `dict` using `func`, applied to values - `def cycle(o)` Like `itertools.cycle` except creates list of `None`s if `o` is empty - `def zip_cycle(x, *args)` Like `itertools.zip_longest` but `cycle`s through elements of all but first argument - `def sorted_ex(iterable, key, reverse, cmp, **kwargs)` Like `sorted`, but if key is str use `attrgetter`; if int use `itemgetter`; use `cmp` comparator function or `key` with `kwargs` - `def not_(f)` Create new function that negates result of `f` - `def argwhere(iterable, f, negate, **kwargs)` Like `filter_ex`, but return indices for matching items - `def filter_ex(iterable, f, negate, gen, **kwargs)` Like `filter`, but passing `kwargs` to `f`, defaulting `f` to `noop`, and adding `negate` and `gen` - `def renumerate(iterable, start)` Same as `enumerate`, but returns index as 2nd element instead of 1st - `def first(x, f, negate, **kwargs)` First element of `x`, optionally filtered by `f`, or None if missing - `def last(x, f, negate, **kwargs)` Last element of `x`, optionally filtered by `f`, or None if missing - `def only(o)` Return the only item of `o`, raise if `o` doesn't have exactly one item - `def nested_attr(o, attr, default)` Same as `getattr`, but if `attr` includes a `.`, then looks inside nested objects - `def nested_setdefault(o, attr, default)` Same as `setdefault`, but if `attr` includes a `.`, then looks inside nested objects - `def nested_callable(o, attr)` Same as `nested_attr` but if not found will return `noop` - `def nested_idx(coll, *idxs)` Index into nested collections, dicts, etc, with `idxs` - `def set_nested_idx(coll, value, *idxs)` Set value indexed like `nested_idx - `def val2idx(x)` Dict from value to index - `def uniqueify(x, sort, bidir, start)` Unique elements in `x`, optional `sort`, optional return reverse correspondence, optional prepend with elements. - `def loop_first_last(values)` Iterate and generate a tuple with a flag for first and last value. - `def loop_first(values)` Iterate and generate a tuple with a flag for first value. - `def loop_last(values)` Iterate and generate a tuple with a flag for last value. - `def first_match(lst, f, default)` First element of `lst` matching predicate `f`, or `default` if none - `def last_match(lst, f, default)` Last element of `lst` matching predicate `f`, or `default` if none - `def joins(sep, its)` Sugar for `sep.join(map(str, its))` - `class fastuple` A `tuple` with elementwise ops and more friendly __init__ behavior - `def __new__(cls, x, *rest)` - `def mul(self, *args)` `*` is already defined in `tuple` for replicating, so use `mul` instead - `def add(self, *args)` `+` is already defined in `tuple` for concat, so use `add` instead - `class bind` Same as `partial`, except you can use `arg0` `arg1` etc param placeholders - `def __init__(self, func, *pargs, **pkwargs)` - `def __call__(self, *args, **kwargs)` - `def mapt(func, *iterables)` Tuplified `map` - `def map_ex(iterable, f, *args, **kwargs)` Like `map`, but use `bind`, and supports `str` and indexing - `def compose(*funcs)` Create a function that composes all functions in `funcs`, passing along remaining `*args` and `**kwargs` to all - `def maps(*args)` Like `map`, except funcs are composed first - `def partialler(f, *args, **kwargs)` Like `functools.partial` but also copies over docstring - `def instantiate(t)` Instantiate `t` if it's a type, otherwise do nothing - `def using_attr(f, attr)` Construct a function which applies `f` to the argument's attribute `attr` - `def negate(f)` Returns the negation of `f` - `def fail_clean(*excs)` Re-raise `excs` (default: `Exception`) without internal traceback frames - `def dstar(f)` Wrap `f` to accept a single dict, unpacking it as keyword args - `def copy_func(f)` Copy a non-builtin function (NB `copy.copy` does not work for this) - `def patch_to(cls, as_prop, cls_method, set_prop, static_method, nm, glb)` Decorator: add `f` to `cls` - `def patch(f)` Decorator: add `f` to the first parameter's class (based on f's type annotations) - `def extend_enum(cls, n, v)` Add new member `n` with value `v` to enum class `cls` at runtime - `def compile_re(pat)` Compile `pat` if it's not None - `class ImportEnum(Enum)` An `Enum` that can have its values imported Members: - `@classmethod imports(cls)` - `class StrEnum(Enum)` An `ImportEnum` that behaves like a `str` Members: - `__str__(self)` - `def str_enum(name, *vals)` Simplified creation of `StrEnum` types - `class ValEnum(Enum)` An `ImportEnum` that stringifies using values Members: - `__str__(self)` - `class Stateful` A base class/mixin for objects that should not serialize all their state - `def __init__(self, *args, **kwargs)` - `def __getstate__(self)` - `def __setstate__(self, state)` - `class NotStr` Behaves like a `str`, but isn't an instance of one - `def __init__(self, s)` - `def __repr__(self)` - `def __str__(self)` - `def __add__(self, b)` - `def __mul__(self, b)` - `def __len__(self)` - `def __eq__(self, b)` - `def __lt__(self, b)` - `def __hash__(self)` - `def __bool__(self)` - `def __contains__(self, b)` - `def __iter__(self)` - `def __getitem__(self, i)` - `class PrettyString` Little hack to get strings to show properly in Jupyter. - `def __repr__(self)` - `def even_mults(start, stop, n)` Build log-stepped array from `start` to `stop` in `n` steps. - `def num_cpus()` Get number of cpus - `def add_props(f, g, n)` Create properties passing each of `range(n)` to f - `def str2bool(s)` Case-insensitive convert string `s` too a bool (`y`,`yes`,`t`,`true`,`on`,`1`->`True`) - `def str2int(s)` Convert `s` to an `int` - `def str2float(s)` Convert `s` to a float - `def str2list(s)` Convert `s` to a list - `def str2date(s)` `date.fromisoformat` with empty string handling - `def str2dt(s)` `datetime.fromisoformat` with `Z` suffix and empty string handling - `def typed(_func)` Decorator to check param and return types at runtime, with optional casting - `def exec_new(code)` Execute `code` in a new environment and return it - `def exec_import(mod, sym)` Import `sym` from `mod` in a new environment - `def xdumps(o, **kwargs)` `json.dumps`, using `__json__` methods when available - `def revive_dates(o)` Recursively convert ISO8601-formatted strings in a decoded JSON tree into `datetime` objects ## fastcore.docments > Document parameters using comments. > > A *docment* documents a parameter with a trailing comment on its line in the signature, right next to the name, type, and default it describes; `docments(f)` retrieves them programmatically, so tools (docs tables, CLIs like `fastcore.script`, ...) can use them. This replaces repeating parameter names in a specially-formatted docstring, which has to be kept in sync with the signature by hand: > > ```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'}) > ``` > > Comment lines *above* a param also work when one line isn't enough; `typing.Annotated` string metadata is an alternative to comments; numpy-style docstring sections are merged in when present; and `docments(f, full=True)` returns each param's default, type, and docment together. > > Docs: https://fastcore.fast.ai/docments.html.md - `def docstring(sym)` Get docstring for `sym` for functions ad classes - `def parse_docstring(sym)` Parse a numpy-style docstring in `sym` - `def isdataclass(s)` Check if `s` is a dataclass but not a dataclass' instance - `def get_dataclass_source(s)` Get source code for dataclass `s` - `def get_source(s)` Get source code for string, function object or dataclass `s` - `def get_name(obj)` Get the name of `obj` - `def qual_name(obj)` Get the qualified name of `obj` - `def ann_parts(anno)` The underlying type and metadata tuple of an `Annotated`, else `(anno, ())` - `def docments(s, full, eval_str, returns, args_kwargs)` Get docments for `s` - `def sig_source(obj)` Full source of signature line(s) for a function or class. - `def extract_docstrings(code)` Create a dict from function/class/method names to tuples of docstrings and param lists - `class DocmentTbl` - `def __init__(self, obj, verbose, returns)` Compute the docment table string - `@property def has_docment` - `@property def has_return` - `@property def hdr_str` - `@property def params_str` - `@property def return_str` - `def __eq__(self, other)` - `class DocmentList` - `class DocmentText` - `def __init__(self, obj, maxline, docstring)` - `@property def params` - `def __str__(self)` - `def sig2str(func, maxline)` Generate function signature with docments as comments - `def can_render(sym)` Check if `sym` has a renderable signature - `class ShowDocRenderer` - `def __init__(self, sym, name, title_level, maxline)` Show documentation for `sym` - `class MarkdownRenderer` Markdown renderer for `show_doc` - `def __repr__(self)` ## fastcore.docscrape > Parse numpy-style docstrings - `def strip_blank_lines(l)` Remove leading and trailing blank lines from a list of lines - `class Reader` A line-based string reader. - `def __init__(self, data)` - `def __getitem__(self, n)` - `def reset(self)` - `def read(self)` - `def seek_next_non_empty_line(self)` - `def eof(self)` - `def read_to_condition(self, condition_func)` - `def read_to_next_empty_line(self)` - `def read_to_next_unindented_line(self)` - `def peek(self, n)` - `def is_empty(self)` - `class ParseError` - `def __str__(self)` - `class NumpyDocString` Parses a numpydoc string to an abstract representation - `def __init__(self, docstring, config, supported_sections, supports_params)` - `def __iter__(self)` - `def __len__(self)` - `def __getitem__(self, key)` - `def __setitem__(self, key, val)` - `def dedent_lines(lines, split)` Deindent a list of lines maximally ## fastcore.foundation > The `L` class and helpers for it > > `add_docs` (or the `docs` class decorator) attaches docstrings to a class and its methods from one dict kept separate from the code, so one-line methods stay one line; it also raises if any public method is left undocumented. `coll_repr` renders long list-likes as a length-prefixed preview like `(#2000) [0, 1, ...]`, and `flatmap(f, xs)` is a named `[y for x in xs for y in f(x)]`: a map where each input may produce zero, one, or many outputs. > > `L` is a drop-in replacement for a python `list`. Inspired by [NumPy](http://www.numpy.org/), it supports advanced indexing — an int, slice, collection of ints, or boolean mask — and its methods (`map`, `filter`, `sorted`, `unique`, `itemgot`, `attrgot`, and many more) return a new `L`, encouraging simple expressive chains: > > ```python > t = L(1,2,3) > t.append(4) > test_eq(t[[0,3]], [1,4]) > test_eq(t[[False,True,False,True]], [2,4]) > test_eq(t.map(lambda o:o*2), [2,4,6,8]) > ``` > > Most `L` methods are also *curryable*: called on the class instead of an instance, they return a partial awaiting an iterable, so `L(lines).map(L.map(int))` converts each nested list. The curryable methods are `map`, `filter`, `groupby`, `argwhere`, `argfirst`, `first`, `last`, `sorted`, `reduce`, `partition`, `takewhile`, `dropwhile`, and `accumulate`. The `star` adapter wraps a function to unpack its *last* argument as individual args, so it works uniformly across these methods (`map` passes each item last; `reduce` passes `(acc, item)`, so only the item is unpacked); `rstar` unpacks in reversed order. > > `L` also wraps the `itertools` verbs as methods: `cycle`, `takewhile`, `dropwhile`, `accumulate`, `pairwise`, `batched`, `compress`, `permutations`, `combinations`, plus `partition` (split into two `L`s by a predicate) and recursive `flatten` (strings kept atomic). > > Docs: https://fastcore.fast.ai/foundation.html.md - `@contextmanager def working_directory(path)` Change working directory to `path` and return to previous on exit. - `def add_docs(cls, cls_doc, **docs)` Copy values from `docs` to `cls` docstrings, and confirm all public methods are documented - `def docs(cls)` Decorator version of `add_docs`, using `_docs` dict - `def coll_repr(c, max_n)` String repr of up to `max_n` items of (possibly lazy) collection `c` - `def is_bool(x)` Check whether `x` is a bool or None - `def mask2idxs(mask)` Convert bool mask or index list to index `L` - `def is_indexer(idx)` Test whether `idx` will index a single item in a list - `def product(xs)` The product of elements of `xs`, with `None`s removed - `def flatmap(f, xs, **kwargs)` Apply f to each element and flatten the results into a single list. - `class CollBase` Base class for composing a list of `items` - `def __init__(self, items)` - `def __len__(self)` - `def __getitem__(self, k)` - `def __setitem__(self, k, v)` - `def __delitem__(self, i)` - `def __repr__(self)` - `def __iter__(self)` - `class L` Behaves like a list of `items` but can also index with list of indices or masks - `def __init__(self, items, *rest)` - `def __getitem__(self, idx)` Retrieve `idx` (can be list of indices, or mask, or int) items - `def __setitem__(self, idx, o)` Set `idx` (can be list of indices, or mask, or int) items to `o` (which is broadcast if not iterable) - `def __eq__(self, b)` - `def __iter__(self)` - `def __json__(self)` - `def __contains__(self, b)` - `def __reversed__(self)` - `def __invert__(self)` - `def __repr__(self)` - `def __mul__(a, b)` - `def __add__(a, b)` - `def __radd__(a, b)` - `def __addi__(a, b)` - `@patch def unique(self, sort, bidir, start)` Unique items, in stable order - `@patch(cls_method=True) def range(cls, a, b, step)` Class Method: Same as `range`, but returns `L`. Can pass collection for `a`, to use `len(a)` - `@patch def enumerate(self)` Same as `enumerate` - `@patch(cls_method=True) def split(cls, s, sep, maxsplit)` Class Method: Same as `str.split`, but returns an `L` - `@patch(cls_method=True) def splitlines(cls, s, keepends)` Class Method: Same as `str.splitlines`, but returns an `L` - `@patch @curryable def map(self, f, *args, **kwargs)` Create new `L` with `f` applied to all `items`, passing `args` and `kwargs` to `f` - `def star(f)` Adapt `f` to unpack its last argument, e.g. for use in `map`-style functions - `def rstar(f)` Like `star`, but unpack the last argument in reverse order - `def splitter(sep, maxsplit)` Create a partial function that splits strings into `L` - `def linesplitter(keepends)` Create a partial function that splits strings by lines into `L` - `@patch def map_dict(self, f, *args, **kwargs)` Like `map`, but creates a dict from `items` to function results - `@patch def zip(self, cycled)` Create new `L` with `zip(*items)` - `@patch def map_zip(self, f, *args, **kwargs)` Apply `f` to `zip` of items, unpacking each zipped tuple - `@patch def zipwith(self, *rest)` Create new `L` with `self` zip with each of `*rest` - `@patch def map_zipwith(self, f, *rest, **kwargs)` Apply `f` to `zipwith` of items, unpacking each zipped tuple - `@patch @curryable def filter(self, f, negate, **kwargs)` Create new `L` filtered by predicate `f`, passing `args` and `kwargs` to `f` - `@patch @curryable def argfirst(self, f, negate)` Return index of first matching item - `@patch def itemgot(self, *idxs)` Create new `L` with item `idx` of all `items` - `@patch def attrgot(self, k, default)` Create new `L` with attr `k` (or value `k` for dicts) of all `items`. - `@patch @curryable def sorted(self, key, reverse, cmp, **kwargs)` New `L` sorted by `key`, using `sort_ex`. If key is str use `attrgetter`; if int use `itemgetter` - `@patch def shuffle(self)` Same as `random.shuffle`, but not inplace - `@patch def sum(self)` Sum of the items - `@patch def map_first(self, f, g, *args, **kwargs)` First element of `map_filter` - `@patch @curryable def takewhile(self, f)` Same as `itertools.takewhile` - `@patch @curryable def dropwhile(self, f)` Same as `itertools.dropwhile` - `@patch @curryable def accumulate(self, f, initial)` Same as `itertools.accumulate` - `@patch def pairwise(self)` Same as `itertools.pairwise` - `@patch def compress(self, selectors)` Same as `itertools.compress` - `@patch def permutations(self, r)` Same as `itertools.permutations` - `@patch def combinations(self, r)` Same as `itertools.combinations` ## fastcore.funccall > Generate JSON schemas from Python functions, call functions from LLM tool-use requests, and convert schemas back to callables > > Docs: https://fastcore.fast.ai/funccall.html.md - `def handle_type(t, defs)` Convert a type annotation to JSON Schema - `def union_schema(t, defs)` Schema for union type `t`: its schema-representable members, unwrapped if only one, `anyOf` otherwise - `def get_schema(f, pname, evalable, skip_hidden, name)` Generate JSON schema for a class, function, or method - `def coerce_inputs(func, inputs)` Coerce inputs based on function type annotations, repairing common LLM container mistakes - `def get_schema_nm(nm, ns, dot2dash, **kwargs)` Get schema for symbol `nm` in namespace `ns`, preserving the full dotted name - `def call_func(fc_name, fc_inputs, ns, raise_on_err)` Call the function `fc_name` with the given `fc_inputs` using namespace `ns`. - `def call_func_async(fc_name, fc_inputs, ns, raise_on_err)` Awaits the function `fc_name` with the given `fc_inputs` using namespace `ns`. - `def mk_param(orig, props, req, pynm)` Create a `Parameter` for `orig` with schema `props` - `def schema2sig(tool)` Convert json schema `tool` to a `Signature` - `def mk_tool(dispfn, tool)` Create a callable function from a JSON schema tool definition ## fastcore.imghdr > Recognize image file formats based on their first few bytes. - `def test_jpeg(h, f)` JPEG data with JFIF or Exif markers; and raw JPEG including COM segments - `def test_gif(h, f)` GIF ('87 and '89 variants) - `def test_tiff(h, f)` TIFF (can be in Motorola or Intel byte order) - `def test_rgb(h, f)` SGI image library - `def test_pbm(h, f)` PBM (portable bitmap) - `def test_pgm(h, f)` PGM (portable graymap) - `def test_ppm(h, f)` PPM (portable pixmap) - `def test_rast(h, f)` Sun raster file - `def test_xbm(h, f)` X bitmap (X10 or X11) ## fastcore.imports - `def is_iter(o)` Test whether `o` can be used in a `for` loop - `def is_coll(o)` Test whether `o` is a collection (i.e. has a usable `len`) - `def all_equal(a, b)` Compares whether `a` and `b` are the same length and have the same contents - `def noop(x, *args, **kwargs)` Do nothing - `def noops(self, x, *args, **kwargs)` Do nothing (method) - `def isinstance_str(x, cls_name)` Like `isinstance`, except takes a type name instead of a type - `def equals(a, b)` Compares `a` and `b` for equality; supports sublists, tensors and arrays too - `def ipython_shell()` Same as `get_ipython` but returns `False` if not in IPython - `def in_ipython()` Check if code is running in some kind of IPython environment - `def in_colab()` Check if the code is running in Google Colaboratory - `def in_jupyter()` Check if the code is running in a jupyter notebook - `def in_notebook()` Check if the code is running in a jupyter notebook - `def remove_prefix(text, prefix)` Temporary until py39 is a prereq - `def remove_suffix(text, suffix)` Temporary until py39 is a prereq - `def is_usable_tool(func)` True if the function has a docstring and all parameters have types, meaning that it can be used as an LLM tool. - `def llmtool(f, **tmpls)` Decorator to mark a function as an LLM tool. Pass `**tmpls` to format the docstring. ## fastcore.meta > Metaclasses > > See this [blog post](https://realpython.com/python-metaclasses/) for more information about metaclasses. > > - `FixSigMeta` preserves signature introspection (i.e. tab completion in IDEs) when inheritance through a `__new__` or a metaclass `__call__` would otherwise obscure it; the other metaclasses here all inherit from it. > - `PrePostInitMeta` runs `__pre_init__` and `__post_init__` automatically around `__init__` (without having to call them yourself). > - `AutoInit` is a `PrePostInitMeta` mixin that calls `super().__init__` for you. > - `NewChkMeta` makes `C(x)` return `x` unchanged when `x` is already a `C` and no other args are passed (how `L` avoids re-wrapping an `L`). > - `BypassNewMeta` casts an object of another class (`_bypass_type`) to this class without copying it. > > `use_kwargs`/`use_kwargs_dict` replace `**kwargs` in a signature with named params from an explicit list or dict, and `funcs_kwargs` lets a class accept method overrides as constructor kwargs. The one used everywhere in fastai code is `@delegates`: decorating `wrapper` with `@delegates(base)` replaces `**kwargs` in `wrapper`'s shown signature with `base`'s keyword parameters, so introspection and tab completion reveal the real options. Only params with defaults are copied over; `keep=True` retains `**kwargs`; `but=` excludes names; and on a class with no argument it delegates to the superclass: > > ```python > def basefn(a, b=2, c=3): ... > @delegates(basefn) > def wrapper(d, **kwargs): ... > test_eq(str(inspect.signature(wrapper)), '(d, *, b=2, c=3)') > ``` > > Docs: https://fastcore.fast.ai/meta.html.md - `def test_sig(f, b)` Test the signature of an object - `class FixSigMeta` A metaclass that fixes the signature on classes that override `__new__` - `def __new__(cls, name, bases, dict)` - `class PrePostInitMeta` A metaclass that calls optional `__pre_init__` and `__post_init__` methods - `def __call__(cls, *args, **kwargs)` - `class AutoInit` Same as `object`, but no need for subclasses to call `super().__init__` - `def __pre_init__(self, *args, **kwargs)` - `class NewChkMeta` Metaclass to avoid recreating object passed to constructor - `def __call__(cls, x, *args, **kwargs)` - `class BypassNewMeta` Metaclass: casts `x` to this class if it's of type `cls._bypass_type` - `def __call__(cls, x, *args, **kwargs)` - `def empty2none(p)` Replace `Parameter.empty` with `None` - `def anno_dict(f)` `__annotation__ dictionary with `empty` cast to `None`, returning empty if doesn't exist - `def use_kwargs_dict(keep, **kwargs)` Decorator: replace `**kwargs` in signature with `names` params - `def use_kwargs(names, keep)` Decorator: replace `**kwargs` in signature with `names` params - `def delegates(to, keep, but, sort_args)` Decorator: replace `**kwargs` in signature with params from `to` - `def method(f)` Mark `f` as a method - `def delegated(to, keep, but, sort_args)` Like `delegates` but also populates delegated default kwargs at call time - `def funcs_kwargs(as_method)` Replace methods in `cls._methods` with those from `kwargs` - `def metadec(d)` Decorator for decorators: make `d(f, *, ...)` usable as `@d` or `@d(**params)` - `def splice_sig(wrapper, fn, *skips)` Replace `*args`/`**kwargs` in wrapper's sig with fn's params (minus skips) ## fastcore.nbio > Reading, writing, and running Jupyter notebooks > > Cell tools apply `fastcore.tools`' string editing primitives to one notebook cell's source, addressed by path and cell id, mirroring that module's file tools: the same operations and parameters, with `path, cell_id` in place of `path`. Each editor (including the structural `cell_ast_replace`) returns a diff of the change, and `view_cell` shows a cell's source with optional line numbers or exhash addresses. > > Naming and parameter conventions shared across the editing toolkit are documented in `fastcore.editskill`, which also re-exports this module's editing tools. > > Docs: https://fastcore.fast.ai/nbio.html.md - `class NbCell` - `def __init__(self, idx, cell, lang)` - `def __setattr__(self, k, v)` - `def __setitem__(self, k, v)` - `def set_source(self, source)` - `def parsed_(self)` - `def __hash__(self)` - `def __eq__(self, o)` - `def dict2nb(js, **kwargs)` Convert dict `js` to an `AttrDict`, - `def read_nb(path)` Return notebook at `path` (expands `~`) - `def mk_cell(text, cell_type, **kwargs)` Create an `NbCell` containing `text` - `def new_nb(cells, meta, nbformat, nbformat_minor)` Returns an empty new notebook - `def cell_frontmatter(s, strvals)` Frontmatter mapping from a cell source that is entirely a literal `---` block, else {} - `def md_frontmatter(s)` Frontmatter synthesized from an H1-formatted markdown cell: `# title`, `> description`, and `- key: value` lines - `def nb_frontmatter(nb, strvals)` Frontmatter from `nb`: its first raw cell plus first markdown cell (literal `---` block, or `# title` synthesis), raw keys winning - `def first_code_ln(code_list, re_pattern, lang)` get first line number where code occurs, where `code_list` is a list of code - `def dir_tag(meta)` Meta-form nbdev directives in `meta` as a compact `[k k=v]` bracket tag, or `''` if none - `@patch def directive(self, name, default)` Value of directive `name` (`''` if bare), or `default` if absent - `@patch def remove_directives(self, quarto)` Strip directives from source, keeping cell magics; with `quarto`, instead materialize every directive (metadata included) as a Quarto option line - `def nb2dict(d, k)` Convert parsed notebook to `dict` - `def nb2str(nb)` Convert `nb` to a `str` - `def write_nb(nb, path)` Write `nb` to `path` (expands `~`) - `def find_id(items, k, unit)` The item with id `k`; a missing or ambiguous id raises `KeyError` naming the problem - `def cell_edit(f, name)` Wrap text editor `f` as a cell editing function: `path, cell_id` addressing, diff-or-error return - `def view_cell(path, cell_id, start_line, end_line, nums, lnhashs, incl_out, trunc_out)` View a cell's source, optionally limited to 1-based line range - `def validate_cell(cell, idx)` Raise `ValueError` for structural problems in notebook cell dict `cell`; returns it unchanged if fine - `def validate_nb(nb)` Raise `ValueError` for structural problems in notebook `nb`; returns it unchanged if fine - `def repair_cell(cell, idx)` Fix deterministic structural problems in `cell`, returning a list of repairs made - `def repair_nb(nb)` Fix deterministic structural problems in `nb`, returning a list of repairs made - `def join_out(d)` Join Jupyter's list-of-lines output data into one string - `def mk_stream(name, text)` Helper to create an output stream dict - `def mk_result(metadata, **data)` Helper to create an execute_result output dict - `def mk_display(metadata, **data)` Helper to create a display_data output dict - `def mk_error(traceback, ename, evalue)` Helper to create an error output dict - `def concat_streams(outputs)` Concatenate stream outputs by name (stdout/stderr), preserving execute_result at end - `@delegates(preferred_out) def preferred_msg_out(out, **kwargs)` Preferred mime type and content for any Jupyter output dict (stream, error, or data-bearing) - `def render_output(out)` Convert a single output dict to an HTML string - `def render_outputs(outputs)` Render a full list of outputs, concatenating streams first. - `def render_text(outputs, html1st, tb_maxlen)` Render notebook outputs to concise ANSI-stripped text, using XML-ish tags when multiple outputs are present; `tb_maxlen` caps over-long error-traceback lines - `def item2xml(typ, content, out, id, meta, **attrs)` A notebook cell or dialog message as concise XML: content, then an `` section when `out` is non-empty - `def cell2xml(cell, ids, incl_out)` Convert NbCell to concise XML format - `def cells2xml(cells, wrap, ids, incl_out, **kw)` Convert notebook cells to XML format - `class Notebook` Read, query, and edit Jupyter notebooks - `def __init__(self, nb, path)` - `@classmethod def open(cls, path)` - `def save(self, path)` - `@property def cells` - `@property def meta` - `def __getitem__(self, k)` - `def __setitem__(self, k, source)` - `def __len__(self)` - `def __iter__(self)` - `def __contains__(self, k)` - `def __delitem__(self, k)` - `def __repr__(self)` - `@property def concise` - `@patch def add(self, source, cell_type, idx, after, before, **kwargs)` Add a new cell with `source` at `idx` (default: end), or `after`/`before` a cell id - `@patch def md(self, source, idx, after, before, **kwargs)` Add a new cell with `source` at `idx` (default: end), or `after`/`before` a cell id - `@patch def move(self, src_ids, after, before)` Move cells with `src_ids` after/before a cell id, or to end - `class CellRow` Snapshot of one cell, shown as `id:t[directives]:source` (t: c=code m=markdown r=raw); a context row from a find shows `-` in place of its final `:` - `def __init__(self, c, maxlen, kind)` - `def __repr__(self)` - `class CellRows` - `def __repr__(self)` - `@patch def summary(self, maxlen)` One `CellRow` line per cell - `def summary_nb(path, maxlen)` One snapshot line per cell of the notebook at `path` - `@patch def to_dict(self)` The plain dict form of the held notebook (`nb2dict`): the representation layer - `class Found` Mixin for find-result containers: `matched` ids, id-only indexing, and match/context kinds - `def __init__(self, items, matched)` - `def kind(self, o)` - `def __getitem__(self, k)` - `class FoundCells` Find results: cells indexed by id (exact or unique prefix), shown as `CellRow` lines with context rows marked - `def __repr__(self)` - `@patch def find_cells(self, pat, cell_type, ids, context)` Find live cells matching all the given criteria, plus `context` neighbouring cells - `def deep_merge(d, u)` Copy of `d` updated by `u` - `def update_cell(path, cell_id, mergemeta, export, **kwargs)` Update a cell's attributes, metadata, or export directive - `def select_cells(nb, *msgids)` Select code cells from `nb` by cell id or unique prefix, in the order given - `def run_cell(shell, raw_cell, store_history, silent, shell_futures, cell_id)` Run one cell on `shell`: transform, await `run_cell_async` on the calling loop, and fire the post events; returns the `ExecutionResult` - `def msg2out(msg)` nbformat-style output dict for Jupyter iopub message dict `msg` - `def msgs2outs(msgs)` nbformat-style output dicts for the output messages in `msgs`, skipping other message types ## fastcore.net > Network, HTTP, and URL functions > > `urlread`/`urljson` retrieve a URL (POSTing when `data` or kwargs are given), `urlsave` downloads to a file named from the URL, and `urlopen` wraps `urllib` with quoting, header, and data handling; on HTTP failure they raise a status-specific exception (`HTTP404NotFoundError`, ...), each a subclass of `HTTP4xxClientError` or `HTTP5xxServerError`, with the response body included in the message. `start_server`/`start_client` make TCP — or, passing a string `port`, Unix-socket — connections; `waitfor` polls a callable until truthy with a timeout; and `is_port_free`/`wait_port_free` help coordinate with servers. > > Docs: https://fastcore.fast.ai/net.html.md - `def urlquote(url)` Update url's path with `urllib.parse.quote` - `def urlwrap(url, data, headers)` Wrap `url` in a urllib `Request` with `urlquote` - `class HTTP4xxClientError` Base class for client exceptions (code 4xx) from `url*` functions - `class HTTP5xxServerError` Base class for server exceptions (code 5xx) from `url*` functions - `def urlopen(url, data, headers, timeout, **kwargs)` Like `urllib.request.urlopen`, but first `urlwrap` the `url`, and encode `data` - `def urlread(url, data, headers, decode, return_json, return_headers, timeout, **kwargs)` Retrieve `url`, using `data` dict or `kwargs` to `POST` if present - `def urljson(url, data, headers, timeout)` Retrieve `url` and decode json - `def urlclean(url)` Remove fragment, params, and querystring from `url` if present - `def urlsave(url, dest, reporthook, headers, timeout)` Retrieve `url` and save based on its name - `def urlvalid(x)` Test if `x` is a valid URL - `def start_server(port, host, dgram, reuse_addr, n_queue)` Create a `socket` server on `port`, with optional `host`, of type `dgram` - `def start_client(port, host, dgram)` Create a `socket` client on `port`, with optional `host`, of type `dgram` - `def tobytes(s)` Convert `s` into HTTP-ready bytes format - `def http_response(body, status, hdrs, **kwargs)` Create an HTTP-ready response, adding `kwargs` to `hdrs` - `@threaded def recv_once(host, port)` Spawn a thread to receive a single HTTP request and store in `d['r']` - `def waitfor(f, timeout, msg)` Call `f` every 0.1s until it returns truthy, raising `TimeoutError` with `msg` after `timeout` secs - `def waitfor_async(f, timeout, msg)` Async version of `waitfor` - `def is_port_free(port, host)` Is `port` free on `host`? - `def wait_port_free(port, host, max_wait)` Wait for `port` to be free on `host` - `def wait_port_free_async(port, host, max_wait)` Async wait for `port` to be free on `host` ## fastcore.parallel > Threading and multiprocessing functions > > `parallel(f, items)` maps `f` over `items` using a process pool (`threadpool=True` for threads instead), with `n_workers`, optional `progress` bar, a `pause` between starts (to stagger e.g. web requests), and `return_exceptions` to collect errors instead of raising; `n_workers=0` runs serially, which makes debugging easy. `parallel_async` is the asyncio version, using a semaphore to limit concurrency. `@threaded` makes a function run in a `Thread` (or `Process`) whose eventual return value lands in its `result` attr, and `startthread`/`startproc` start one immediately. > > Docs: https://fastcore.fast.ai/parallel.html.md - `@metadec def threaded(f)` Run `f` in a `Thread` (or `Process` if `process=True`), and returns it - `@metadec def startthread(f)` Like `threaded`, but start thread immediately - `@metadec def startproc(f)` Like `threaded(process=True)`, but start Process immediately - `class ThreadPoolExecutor` Same as Python's ThreadPoolExecutor, except can pass `max_workers==0` for serial execution - `def __init__(self, max_workers, on_exc, pause, **kwargs)` - `def map(self, f, items, *args, **kwargs)` - `@delegates() class ProcessPoolExecutor` Same as Python's ProcessPoolExecutor, except can pass `max_workers==0` for serial execution - `def __init__(self, max_workers, on_exc, pause, **kwargs)` - `def map(self, f, items, *args, **kwargs)` - `def parallel(f, items, *args, **kwargs)` Applies `func` in parallel to `items`, using `n_workers` - `def parallel_async_gen(f, items, *args, **kwargs)` Yield `(index,result)` pairs as `f` applied to each of `items` completes, in completion order - `@delegates(parallel_async_gen) def parallel_async_dict(f, items, *args, **kwargs)` Apply `f` to `items` in parallel, returning `{index: result}` in completion order - `@delegates(parallel_async_dict, but=['cancel_on_exit']) def parallel_async(f, items, *args, **kwargs)` Applies `f` to `items` in parallel using asyncio and a semaphore to limit concurrency. - `def bg_task(coro)` Like `asyncio.create_task` but logs exceptions for fire-and-forget tasks ## fastcore.py2pyi > Docs: https://fastcore.fast.ai/py2pyi.html.md - `def imp_mod(module_path, package)` Import dynamically the module referenced in `fn` - `def has_deco(node, name)` Check if a function node `node` has a decorator named `name` - `def create_pyi(fn, package)` Convert `fname.py` to `fname.pyi` by removing function bodies and expanding `delegates` kwargs - `@call_parse def py2pyi(fname, package)` Convert `fname.py` to `fname.pyi` by removing function bodies and expanding `delegates` kwargs - `@call_parse def replace_wildcards(path)` Expand wildcard imports in the specified Python file. ## fastcore.script > Creates a CLI from a Python function decorated with `call_parse`. > > The function's parameters become the script's arguments, its docstring becomes the program description, and its [docments](https://fastcore.fast.ai/docments.html) comments become the help for each argument. > > Here's a complete example (`examples/test_fastcore.py` in the fastcore repo): > > ```python > from fastcore.script import * > @call_parse > def main( > msg:str, # The message > upper:bool # Convert to uppercase? > ): > "Print `msg`, optionally converting to uppercase" > print(msg.upper() if upper else msg) > ``` > > Copy that into a file and run it, and you get a CLI with help, defaults, and error handling, with no `if __name__ == "__main__"` or argument parsing code: > > ``` > $ examples/test_fastcore.py --help > usage: test_fastcore.py [-h] [--upper] msg > > Print `msg`, optionally converting to uppercase > > positional arguments: > msg The message > > options: > -h, --help show this help message and exit > --upper Convert to uppercase? (default: False) > ``` > > The function is still a plain Python function too, so it can also be called normally, e.g from a REPL such as Jupyter. > > ## Annotated params > > For `argparse` features that docments can't express, use `typing.Annotated` type hints instead of plain types: > > ```python > from fastcore.script import * > from typing import Annotated > @call_parse > def main(msg:Annotated[str, "The message"], > upper:Annotated[store_true, "Convert to uppercase?"]): > "Print `msg`, optionally converting to uppercase" > print(msg.upper() if upper else msg) > ``` > > The first element of `Annotated` is the parameter's type, and the first string in its metadata is its help. A `dict` in the metadata passes extra arguments to argparse: its keys can be `opt`, `action`, `nargs`, `const`, `choices`, `required`, and `version`. All but `opt` go straight to `add_argument`; `opt` is a bool that overrides whether the param is a flag or positional, which is normally inferred from whether it has a default. > > ## Short flags > > A capital letter in a parameter name declares a short flag: the capitalized letter becomes the short spelling and the lowercased name the long one, so `Resume:int=None` gets both `-r` and `--resume`. The capital can be any letter (`sUggest:str=None` gives `-u/--suggest`), only the first capital counts, and names without capitals get a long flag only. Since the flags are lowercased, the parameter's actual name keeps its capital -- so a Python caller writes `main(Resume=3)`, which usefully advertises that it's invoking a CLI entry point. Positional (default-less) parameters have no flags, so capitals there are left alone. > > ## Positional params > > Parameters without a default are positional, and the rest are flags. Pass `pos` to `call_parse` (or to `anno_parser`) to keep named parameters positional even when they have a default, in which case they are optional on the command line and take the default when omitted. Command line order follows the signature, not the order of the names in `pos`. Naming a bool parameter in `pos` raises, since a flag takes no value. > > ## Param types > > A `bool` parameter is a `store_true` flag defaulting to `False`. If its default is `True` it becomes a `--no-` prefixed `store_false` flag instead, so passing the flag turns it off. Use `bool_arg` as the type when you want an explicit `--flag true|false` argument that honors its default. > > Union types such as `int|str` try each type in turn, and `enum` types such as those from `str_enum` become argparse choices. > > ## The CLI entry point > > The decorated function is always the CLI entry point, and only the entry point gets `sys.argv`: argv is parsed when the function's file is run directly (`python foo.py`, `python -m foo`, or `%run foo.py`), or when it's called with no arguments from the top-level body of a directly-run file (which is how console script wrappers invoke it). Every other call is a plain Python call that ignores argv, whether from a notebook, the REPL, another function, or another `call_parse` function. > > Use the `nested` keyword argument to create nested parsers, where earlier parsers consume only their known args from `sys.argv` before later parsers are used. This is useful to create one command line application that executes another. For example: > > ```sh > myrunner --keyword 1 script.py -- > ``` > > A separating `--` after the first application's args is recommended though not always required, otherwise args may be parsed in unexpected ways. For example: > > ```sh > myrunner script.py -h > ``` > > would display `myrunner`'s help and not `script.py`'s. > > A function usable both from the command line and from Python can call `is_cli` to tell which way it was invoked, e.g. returning a value to Python callers but printing it (or exiting with an error code) when run as a CLI: > > ```python > @call_parse > def sum_args(a:int=0, b:int=0): > "Add `a` and `b`" > if is_cli(): print(a+b) > else: return a+b > > test_eq(sum_args(1,2), 3) # Python call: returns the value, prints nothing > ``` > > Docs: https://fastcore.fast.ai/script.html.md - `def store_true()` Placeholder annotation type for a `store_true` argparse action - `def store_false()` Placeholder annotation type for a `store_false` argparse action - `def bool_arg(v)` Annotation type giving `bool` behavior for CLI args - `def anno_parser(func, prog, pos)` Look at params (with type/docments/`Annotated` annotations) in func and return an `ArgumentParser` - `def args_from_prog(func, prog)` Extract args from `prog` - `def call_parse(func, nested, pos)` Decorator to create a simple CLI from `func` using `anno_parser` - `def is_cli(func)` True if a `call_parse` CLI run is in progress, optionally checking that `func` is the function being run ## fastcore.style > Fast styling for friendly CLIs. > > Build ANSI terminal styles by chaining attributes on the exported `S` object, then apply by calling with a string: `S.blue.bold('hi')` returns the escaped string, nesting correctly inside other styles. Chains are plain objects, so a style can be defined once and reused; tab completion on `S.` lists the available colors and effects. > > Docs: https://fastcore.fast.ai/style.html.md - `class StyleCode` An escape sequence for styling terminal text. - `def __init__(self, name, code, typ)` - `def __str__(self)` - `class Style` A minimal terminal text styler. - `def __init__(self, codes)` - `def __dir__(self)` - `def __getattr__(self, k)` - `def __call__(self, obj)` - `def __repr__(self)` - `def demo()` Demonstrate all available styles and their codes. ## fastcore.test > Helper functions to quickly write tests in notebooks > > All the helpers here wrap `test(a,b,cmp)`: an `assert cmp(a,b)` that shows both values when the comparison fails, rather than a bare `AssertionError`. The named comparisons are `test_eq`, `test_ne`, `test_eq_type` (equal and same type), `test_close` (within `eps`, default 1e-5), `test_is`, `test_shuffled` (equal ignoring order), `test_stdout` (what `f` prints), and `test_warns`. > > To check that code fails as expected, use `test_fail(f, contains=..., exc=...)` for a callable, or `expect_fail` as a context manager, which also accepts `regex`: > > ```python > def _fail(): raise ValueError('oops') > test_fail(_fail, contains='oops') > with expect_fail(ValueError, regex='o+ps'): _fail() > ``` > > `cmp` can be any callable, and fastcore's curried operators (`in_`, `gt`, `is_`, and friends from `fastcore.basics`) make good ones: `test(x, valid, in_)` beats a bare `assert x in valid` by showing both values when it fails: > > ```python > test(2, [1,2,3], in_) > test('b', 'abc', in_) > ``` > > `test_eq` compares with `equals`, which works by content across types: generators are consumed, and lists, tuples, sets, dicts, numpy arrays, torch tensors, and DataFrames all compare their contents, including mixed cases like an array against a plain list: > > ```python > test_eq([1,2], map(int,[1,2])) > test_eq(array([1,2]), [1,2]) > test_eq(dict(a=1,b=2), dict(b=2,a=1)) > ``` > > Docs: https://fastcore.fast.ai/test.html.md - `def test_fail(f, msg, contains, exc, args, kwargs)` Fails with `msg` unless `f()` raises an exception of type `exc` and (optionally) has `contains` in `e.args`; `expect_fail` is normally preferred - `@contextmanager def expect_fail(exc, contains, msg, regex)` Context manager that fails with `msg` unless body raises `exc`, optionally containing `contains` or matching `regex` - `def test(a, b, cmp, cname)` `assert` that `cmp(a,b)`; display inputs and `cname or cmp.__name__` if it fails - `def nequals(a, b)` Compares `a` and `b` for `not equals` - `def test_eq(a, b)` `test` that `a==b` - `def test_eq_type(a, b)` `test` that `a==b` and are same type - `def test_ne(a, b)` `test` that `a!=b` - `def is_close(a, b, eps)` Is `a` within `eps` of `b` - `def test_close(a, b, eps)` `test` that `a` is within `eps` of `b` - `def test_is(a, b)` `test` that `a is b` - `def test_shuffled(a, b)` `test` that `a` and `b` are shuffled versions of the same sequence of items - `def test_stdout(f, exp, regex)` Test that `f` prints `exp` to stdout, optionally checking as `regex` - `def test_fig_exists(ax)` Test there is a figure displayed in `ax` - `class ExceptionExpected` Context manager that tests if an exception is raised. Deprecated: use `expect_fail` instead - `def __init__(self, ex, regex)` - `def __enter__(self)` - `def __exit__(self, type, value, traceback)` ## fastcore.tools > Text and file editing primitives shared by the fastai editing tools > > The editors here are string-level: each takes `text` plus edit parameters and returns the new text, raising `ValueError` when an edit can't apply. The file tools below wrap them with path I/O and diff reporting; message-level wrappers live in aidialog. (This module previously held experimental LLM path-editing and command tools, superseded by safecmd, rgapi, and the tools here.) Naming, parameter, and workflow conventions for the whole editing toolkit, this module included, are documented in `fastcore.editskill`, which also re-exports these tools alongside `fastcore.nbio`'s. > > `line_hash`, `lnhash`, and `lnhash_at` implement the [exhash](https://answerdotai.github.io/exhash) line-address format in pure Python: `lineno|hash|`, where the hash is 4 hex chars of crc32. They let any tool create lnhash-addressed views of text it holds, without depending on the exhash package. > > File tools wrap the primitives with path I/O, returning unified diffs of what changed ("none: No changes." / "error: ..." otherwise). The path is the first argument, e.g: > > view_file('~/a/b.py', 3) > create_file('~/a/b/c.py', 'content here') > file_str_replace('myfile.py', 'old_name', 'new_name') > file_del_lines('myfile.py', 2, 4) > file_replace_lines('myfile.py', new_content=src) # no line numbers: replace the entire contents > > `file_str_replace`, `file_strs_replace`, and `file_del_lines` support `re_filter` and `invert_filter` for targeting only lines matching (or not matching) a regex, like ex's `g//` and `g!//`, combined with `start_line`/`end_line` to restrict to a region. `ast_replace(text, repls)` and `file_ast_replace(path, repls)` apply ast-grep `(pattern, replacement)` rules with `$VAR` metavariables (requires the optional `remold` package). > > Docs: https://fastcore.fast.ai/tools.html.md - `def insert_line(text, insert_line, new_str)` Insert new_str at specified line number - `def str_replace(text, old_str, new_str, start_line, end_line, n_matches, re_filter, invert_filter, use_regex)` Replace occurrence(s) of old_str with new_str - `def strs_replace(text, old_strs, new_strs, start_line, end_line, n_matches, re_filter, invert_filter, use_regex)` Replace multiple strings simultaneously - `def replace_lines(text, start_line, end_line, new_content)` Replace line range with new content; the defaults replace the entire contents - `def del_lines(text, start_line, end_line, re_filter, invert_filter)` Delete line range; deletion is destructive, so both line numbers must be given explicitly (`1, -1` for all lines) - `def line_hash(line)` 4-char hex hash of `line` - `def lnhash(lineno, line)` `lineno|hash|` exhash address for `line` at `lineno` - `def lnhash_at(s, line)` `lineno|hash|` exhash address of line `line` of `s` - `def view_file(path, start_line, end_line, nums, lnhashs)` Read file contents, optionally limited to 1-based line range - `def create_file(path, contents, overwrite)` Create a new file with contents. Error if file exists, unless `overwrite`. - `def file_edit(f, name)` Wrap text editor `f` as a file editing function: `path` addressing, diff-or-error return - `def ast_replace(text, repls)` Apply ast-grep structural pattern replacements to python source ## fastcore.xdg > XDG Base Directory Specification helpers. > > `xdg_cache_home`, `xdg_config_home`, `xdg_data_home`, `xdg_state_home`, and `xdg_runtime_dir` each return the `Path` given by their `XDG_*` environment variable, falling back to the [spec](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)'s default (`~/.cache`, `~/.config`, `~/.local/share`, `~/.local/state`; `None` for the runtime dir) when it's unset, empty, or relative. `xdg_config_dirs` and `xdg_data_dirs` likewise return the colon-split search-path lists. > > Docs: https://fastcore.fast.ai/xdg.html.md - `def xdg_cache_home()` Path corresponding to `XDG_CACHE_HOME` - `def xdg_config_dirs()` Paths corresponding to `XDG_CONFIG_DIRS` - `def xdg_config_home()` Path corresponding to `XDG_CONFIG_HOME` - `def xdg_data_dirs()` Paths corresponding to XDG_DATA_DIRS` - `def xdg_data_home()` Path corresponding to `XDG_DATA_HOME` - `def xdg_runtime_dir()` Path corresponding to `XDG_RUNTIME_DIR` - `def xdg_state_home()` Path corresponding to `XDG_STATE_HOME` ## fastcore.xml > Concise generation of XML. > > `ft(tag, *children, **attrs)` builds an `FT` tree — a lightweight XML/HTML element — and `to_xml` renders it (this is the foundation [fasthtml](https://fastht.ml) builds on). The common HTML tags are predefined as `ft` partials (`Div`, `P`, `A`, ...). Attributes are passed as keywords: use `klass` and `fr` for the reserved words `class` and `for`, `_` in an attr name becomes `-`, and attrs can be got and set on the object directly. Children can also be supplied after the attrs by calling the element, e.g. `Div(id='x')(child1, child2)`: > > ```python > t = Div(P('hi', klass='a'), hx_get='/get') > test_eq(to_xml(t), '
>

hi

>
> ') > test_eq(to_xml(Div(id='x')('child')), '
child
> ') > ``` > > Docs: https://fastcore.fast.ai/xml.html.md - `class FT` A 'Fast Tag' structure, containing `tag`,`children`,and `attrs` - `def __init__(self, tag, cs, attrs, void_, **kwargs)` - `def on(self, f)` - `def changed(self)` - `def __setattr__(self, k, v)` - `def __getattr__(self, k)` - `@property def list` - `def get(self, k, default)` - `def __repr__(self)` - `def __iter__(self)` - `def __getitem__(self, idx)` - `def __setitem__(self, i, o)` - `def __call__(self, *c, **kw)` - `def set(self, *c, **kw)` Set children and/or attributes (chainable) - `def ft(tag, *c, **kw)` Create an `FT` structure for `to_xml()` - `def Html(*c, **kwargs)` An HTML tag, optionally preceeded by `!DOCTYPE HTML` - `class Safe` - `def __html__(self)` - `def to_xml(*elms)` Convert `ft` element tree into an XML string - `def dict2xml(d, do_escape, unwrap)` Convert `d` to XML tags, one per key/value pair, unwrapping if only single key in `unwrap` exists - `def highlight(s, lang)` Markdown to syntax-highlight `s` in language `lang` - `def mk_getattr(f)` Create a module `__getattr__` for mapping undefined attributes to kebab-case HTML tags via factory `f` ## fastcore.xtras > Utility functions used in the fastai library > > Utilities (other than extensions to `Pathlib.Path`) for dealing with IO: `maybe_open` accepts a path or an already-open file, only closing what it opened; `run` passes a command line to `subprocess.run` and returns its `stdout`, raising `IOError` on failure; `atomic_save` writes via a temp file and renames, so readers never see a partial write; `untar_dir` extracts an archive the way you'd want (a lone top-level item lands directly in `dest`); and `globtastic` is a recursive glob with file/folder include and exclude filters. > > `Config` wraps a `ConfigParser` ini file with a single `DEFAULT` section: keys read as attributes, items, or `.get` with a default; `create=` writes initial contents if the file is missing; `types=` coerces values on read, resolving `Path` values relative to the config file's directory; and `Config.find(name)` searches a directory and its parents for the file. > > `dict2obj` converts a (possibly nested) dict, or list of dicts, to attribute access: dicts become `AttrDict` and lists become `L`, so `d.b.c` replaces `d['b']['c']`; `obj2dict` reverses it: > > ```python > d = dict2obj({'a':1, 'b':{'c':2, 'd':[3,4]}}) > test_eq(d.b.c, 2) > test_eq(d.b.d[1], 4) > ``` > > The following methods are added to the standard python library [Pathlib.Path](https://docs.python.org/3/library/pathlib.html#basic-use): `ls()` (dir contents as an `L`, with optional `file_type` MIME-prefix and `file_exts` filters); `read_json`/`write_json`, and `read_jsonl` which is safe for JSON Lines (only ` > ` ends a record, unlike `splitlines`); `readlines`, `mk_write` (create parent dirs, then write), `delete`, `relpath`, and `normpath`. If `Path.BASE_PATH` is set, paths repr relative to it. > > Highlights among the rest: `exec_eval` runs a code string like a notebook cell, returning the last expression's value; `fenced` wraps text in a markdown code fence guaranteed longer than any run of the fence char inside it; `str_diff` returns a unified diff, `''` when the texts match; `unqid` and `friendly_name` generate random identifiers (a valid Python name, or a memorable `adjective-noun-...` string); and `flexicache` is an `lru_cache` with pluggable eviction policies such as `time_policy` and `mtime_policy`. > > Docs: https://fastcore.fast.ai/xtras.html.md - `def walk(path, symlinks, keep_file, keep_folder, skip_folder, func, ret_folders, sort, maxdepth)` Generator: yields files and (optionally) folders as unified, inline-sorted entries - `def exttypes(types)` Get exts for comma-separated or list `typ`; if not found in list, return list with just `types`. Supported: py, js, java, c, cpp, rb, r, ex, sh, web, doc, cfg - `def globtastic(path, recursive, maxdepth, symlinks, file_glob, file_re, folder_re, skip_file_glob, skip_file_re, skip_folder_re, func, ret_folders, sort, types, exts)` A more powerful `glob`, including regex matches, symlink handling, and skip parameters - `@fdelegates(globtastic) def pglob(path, func, **kwargs)` Shortcut for `globtastic(..., call=Path)` - `@contextmanager def maybe_open(f, mode, **kwargs)` Context manager: open `f` if it is a path (and close on exit) - `def mkdir(path, exist_ok, parents, overwrite, **kwargs)` Creates and returns a directory defined by `path`, optionally removing previous existing directory if `overwrite` is `True` - `def image_size(fn)` Tuple of (w,h) for png, gif, or jpg; `None` otherwise - `def detect_mime(data)` Get the MIME type for bytes `data`, covering common PDF, audio, video, and image types - `def bunzip(fn)` bunzip `fn`, raising exception if output already exists - `def loads(s, **kw)` Same as `json.loads`, but handles `None` - `def loads_multi(s)` Generator of >=0 decoded json dicts, possibly with non-json ignored text at start and end - `def dumps(obj, **kw)` Same as `json.dumps` - `def untar_dir(fname, dest, rename, overwrite, uid, gid)` untar `file` into `dest`, creating a directory if the root contains more than one item; recursively chown if `uid`/`gid` set - `def repo_details(url)` Tuple of `owner,name` from ssh or https git repo `url` - `def shell(*args, **kwargs)` Shortcut for `subprocess.run(shell=True)` - `def ssh(host, args, user, sock)` Run SSH command with given arguments - `def rsync_multi(ip, files, user, persist)` Transfer multiple files with rename using persistent SSH connection - `def run(cmd, *rest)` Pass `cmd` (splitting with `shlex` if string) to `subprocess.run`; return `stdout`; raise `IOError` if fails - `def open_file(fn, mode, **kwargs)` Open a file, with optional compression if gz or bz2 suffix - `def save_pickle(fn, o)` Save a pickle file, to a file name or opened file - `def load_pickle(fn)` Load a pickle file from a file name or opened file - `def parse_env(s, fn)` Parse a shell-style environment string or file - `def expand_wildcards(code)` Expand all wildcard imports in the given code string. - `@patch def mkdir_perms(self, mode, parents, exist_ok, uid, gid)` Create directory like Path.mkdir but optionally set uid/gid on newly created dirs - `@contextmanager def atomic_save(fn, mode, uid, gid, **kwargs)` Context manager for writing a file atomically via a temp file that is renamed on close - `def load_mod(name, path)` Load module `name` from file `path` - `def import_no_init(name)` Import dotted `name` without running any `__init__.py` - `def save_config_file(file, d, **kwargs)` Write settings dict to a new config file, or overwrite the existing one. - `def find_file_parents(fname, frompath)` Search `cfg_path` and its parents to find `cfg_name` - `class Config` Reading and writing `ConfigParser` ini files - `def __init__(self, cfg_path, cfg_name, create, save, extra_files, types, **cfg_kwargs)` - `def __repr__(self)` - `def __setitem__(self, k, v)` - `def __contains__(self, k)` - `def save(self)` - `def __getattr__(self, k)` - `def __getitem__(self, k)` - `def get(self, k, default)` - `def path(self, k, default)` - `@classmethod def find(cls, cfg_name, cfg_path, **kwargs)` Search `cfg_path` and its parents to find `cfg_name` - `class Unset(Enum)` Members: - `__repr__(self)` - `__str__(self)` - `__bool__(self)` - `def dict2obj(d, list_func, dict_func, **kwargs)` Convert (possibly nested) dicts (or lists of dicts) to `AttrDict` - `def obj2dict(d)` Convert (possibly nested) AttrDicts (or lists of AttrDicts) to `dict` - `def repr_dict(d)` Print nested dicts and lists, such as returned by `dict2obj` - `def is_listy(x)` `isinstance(x, (tuple,list,L,slice,Generator,set,frozenset))` - `def mapped(f, it)` map `f` over `it`, unless it's not listy, in which case return `f(it)` - `def take_lines(s, pat, atstart, atend, drop)` Take contiguous lines matching `pat` from either end of `s` - `@patch def readlines(self, hint, encoding)` Read the content of `self` - `@patch def read_json(self, encoding, errors)` Same as `read_text` followed by `loads` - `@patch def read_jsonl(self, encoding, errors)` Parse newline-delimited JSON, returning one object per line - `@patch def mk_write(self, data, encoding, errors, mode, uid, gid)` Make all parent dirs of `self`, and write `data` - `@patch def write_json(self, data, encoding, errors, mode, uid, gid, **kw)` Same as `dumps`followed by `mk_write` - `@patch def relpath(self, start)` Same as `os.path.relpath`, but returns a `Path`, and resolves symlinks - `@patch def ls(self, n_max, file_type, file_exts)` Contents of path as a list - `@patch def normpath(self)` Normalize path, eliminating double slashes, etc. - `@patch def delete(self)` Delete a file, symlink, or directory tree - `class IterLen` Base class to add iteration to anything supporting `__len__` and `__getitem__` - `def __iter__(self)` - `@docs class ReindexCollection` Reindexes collection `coll` with indices `idxs` and optional LRU cache of size `cache` - `def __init__(self, coll, idxs, cache, tfm)` - `def __getitem__(self, i)` - `def __len__(self)` - `def reindex(self, idxs)` - `def shuffle(self)` - `def cache_clear(self)` - `def __getstate__(self)` - `def __setstate__(self, s)` - `class SaveReturn` Wrap an iterator such that the generator function's return value is stored in `.value` - `def __init__(self, its)` - `def __iter__(self)` - `def trim_wraps(f, n)` Like wraps, but removes the first n parameters from the signature - `def save_iter(g)` Decorator that allows a generator function to store values in the returned iterator object - `def asave_iter(g)` Like `save_iter`, but for async iterators - `def frontmatter(txt, strvals)` Tuple of (dict, body) from frontmatter in `txt`; missing frontmatter returns ({}, txt), malformed YAML raises - `def clean_cli_output(txt, strip)` Render CLI output as it appears on screen: alternate screen, `\r` overwrites, `\b` backspaces, and ANSI escapes - `def unqid(seeded)` Generate a unique id suitable for use as a Python identifier - `def rtoken_hex(nbytes)` Generate a random hex string using Python's random module. - `def friendly_name(levels, suffix)` Generate a random human-readable name with customizable word levels and suffix length - `def n_friendly_names(levels, suffix)` Number of possible combos for `friendly_names - `def exec_eval(code, g, l)` Evaluate `code` in `g` (defaults to `globals()`) and `l` (defaults to `locals()`) - `def get_source_link(func)` Return link to `func` in source code - `def sparkline(data, mn, mx, empty_zero)` Sparkline for `data`, with `None`s (and zero, if `empty_zero`) shown as empty column - `def modify_exception(e, msg, replace)` Modifies `e` with a custom message attached - `def round_multiple(x, mult, round_down)` Round `x` to nearest multiple of `mult` - `def set_num_threads(nt)` Get numpy (and others) to use `nt` threads - `def join_path_file(file, path, ext)` Return `path/file` if file is a string or a `Path`, file otherwise - `def autostart(g)` Decorator that automatically starts a generator - `class EventTimer` An event timer with history of `store` items of time `span` - `def __init__(self, store, span)` - `def add(self, n)` Record `n` events - `@property def duration` - `@property def freq` - `def stringfmt_names(s)` Unique brace-delimited names in `s` - `class PartialFormatter` A `string.Formatter` that doesn't error on missing fields, and tracks missing fields and unused args - `def __init__(self)` - `def get_field(self, nm, args, kwargs)` - `def check_unused_args(self, used, args, kwargs)` - `def partial_format(s, **kwargs)` string format `s`, ignoring missing field errors, returning missing and extra fields - `def truncstr(s, maxlen, suf, space, sizevar)` Truncate `s` to length `maxlen`, adding suffix `suf` if truncated - `def fenced(text, info, ch)` Wrap `text` in a fence of `ch`, one char longer than any run of `ch` inside it (min 3) - `def fenced_blocks(text, ch)` Top-level fenced blocks in `text`, fence-nesting-aware: the inverse of `fenced` - `def str_diff(a, b, n, names)` Unified diff of `a` and `b` - `def utc2local(dt)` Convert `dt` from UTC to local time - `def local2utc(dt)` Convert `dt` from local to UTC time - `def trace(f)` Add `set_trace` to an existing function `f` - `@contextmanager def modified_env(*delete, **replace)` Context manager temporarily modifying `os.environ` by deleting `delete` and replacing `replace` - `class ContextManagers` Wrapper for `contextlib.ExitStack` which enters a collection of context managers - `def __init__(self, mgrs)` - `def __enter__(self)` - `def __exit__(self, *args, **kwargs)` - `def shufflish(x, pct)` Randomly relocate items of `x` up to `pct` of `len(x)` from their starting location - `def console_help(libname)` Show help for all console scripts from `libname` - `def hl_md(s, lang, show)` Syntax highlight `s` using `lang`. - `def type2str(typ)` Stringify `typ` - `def nullable_dc(cls)` Like `dataclass`, but default of `UNSET` added to fields without defaults - `def flexiclass(cls)` Convert `cls` into a `dataclass` like `make_nullable`. Converts in place and also returns the result. - `def asdict(o)` Convert `o` to a `dict`, supporting dataclasses, namedtuples, iterables, and `__dict__` attrs. - `def vars_pub(x)` Get public non-skipped vars - `def is_typeddict(cls)` Check if `cls` is a `TypedDict` - `def is_namedtuple(cls)` `True` if `cls` is a namedtuple type - `class CachedIter` Cache the result returned by an iterator - `def __init__(self, o)` - `def __iter__(self)` - `def flexicache(*funcs)` Like `lru_cache`, but customisable with policy `funcs` - `def time_policy(seconds)` A `flexicache` policy that expires cached items after `seconds` have passed - `def mtime_policy(filepath, arg)` A `flexicache` policy that expires cached items after the watched file's modified-time changes - `def timed_cache(seconds, maxsize)` Like `lru_cache`, but also with time-based eviction