research_helpers.build

This module covers the registry of generated artefacts and the drift checks over them, as well as the Document that groups a manuscript’s registries. See Adding a generated table for the end-to-end workflow, and several documents for an example of a project with more than one manuscript.

class research_helpers.build.Kind(name, suffix, directory, label_prefix, setting, reference, block, float_is_generated, binary=False)[source]

Bases: object

A class of generated artefact (and how the paper refers to it).

Parameters:
stem(label)[source]

Return the filename that ‘label’ is written to (without extension).

Return type:

str

Parameters:

label (str)

filename(label)[source]

Return the filename that ‘label’ is written to.

Return type:

str

Parameters:

label (str)

referenced(paper)[source]

Return the stems the paper pulls in with any extension removed.

Return type:

set[str]

Parameters:

paper (str)

hand_written(paper)[source]

Return the floats the paper carries with no emitter behind them, keyed by label.

Where the emitter produces the float itself, any literal float is hand-written. Where it produces only the contents, a float counts as hand-written when nothing inside it is pulled in from this kind’s directory.

Return type:

dict[str, str]

Parameters:

paper (str)

read(path)[source]

Read a built or installed artefact for comparison.

Return type:

str | bytes

Parameters:

path (Path)

write(path, content)[source]

Write one artefact, creating the parent directory.

Return type:

None

Parameters:
class research_helpers.build.Artefact(label, emitter, description='', metadata=<factory>)[source]

Bases: object

A registered artefact.

Parameters:
class research_helpers.build.Drift(stale, missing, ungenerated)[source]

Bases: NamedTuple

The three ways a paper and its emitters can disagree.

Checked without a manuscript, in a repository that publishes a document’s floats but not its source, ‘missing’ is None.

Parameters:
stale: list[str]

Alias for field number 0

missing: list[str] | None

Alias for field number 1

ungenerated: list[str]

Alias for field number 2

class research_helpers.build.Registry(kind)[source]

Bases: object

The emitters for one kind of artefact and the checks over them.

Parameters:

kind (Kind)

register(label, *, description='', **metadata)[source]

Register the decorated function as the emitter for ‘label’.

Parameters:
  • label (str) – the ‘\label’ the paper cites this artefact as, e.g. ‘tab:scores’. Must carry the kind’s prefix, since the filename is the label without it.

  • description (str, default: '') – one line, shown by ‘–list’.

  • **metadata (Any) – kept on the ‘artefact’ for a project’s own use.

Return type:

Callable[[TypeVar(EmitterT, bound= Callable[[], str | bytes])], TypeVar(EmitterT, bound= Callable[[], str | bytes])]

Returns:

The decorator, which returns the emitter unchanged so it stays directly callable.

Raises:

ValueError – if the label is already registered or lacks the kind’s prefix.

property labels: list[str]

Return every registered label, in registration order.

build_dir(directory=None)[source]

Return where artefacts are built, defaulting to the project’s build directory.

Return type:

Path

Parameters:

directory (Path | str | None)

install_dir(destination=None)[source]

Return where the paper reads artefacts from, defaulting to the project’s setting.

Return type:

Path

Parameters:

destination (Path | str | None)

write_all(directory=None)[source]

Build every artefact into ‘directory’, one file per label, and return the paths.

Return type:

list[Path]

Parameters:

directory (Path | str | None)

install(destination=None, source=None)[source]

Copy built artefacts to where the paper reads them.

Parameters:
  • destination (Path | str | None, default: None) – where the paper reads from. Defaults to the project’s setting.

  • source (Path | str | None, default: None) – where the artefacts were built. Defaults to the project’s build directory.

Return type:

list[Path]

Returns:

The paths written.

Raises:

FileNotFoundError – if an artefact has not been built.

drift(paper=None, destination=None, *, manuscript=None)[source]

Compare what the paper carries against what the emitters produce.

Parameters:
  • paper (Path | str | None, default: None) – the manuscript. Defaults to the project’s ‘paper.main’.

  • destination (Path | str | None, default: None) – where the paper reads artefacts from. Defaults to the project’s setting.

  • manuscript (bool | None, default: None) – whether there is a manuscript to read. Defaults to true if ‘paper’ is given, and otherwise to the project’s ‘paper.manuscript’.

Return type:

Drift

Returns:

The three ways they disagree; falsy when they do not.

Raises:
  • FileNotFoundError – if a manuscript is expected and is not there.

  • ValueError – if ‘paper’ is given while ‘manuscript’ is false.

report(found, document=PAPER_DOCUMENT, *, destination=None)[source]

Return a description of what drifted.

Parameters:
  • found (Drift) – what ‘drift’ returned.

  • document (str, default: PAPER_DOCUMENT) – what to call the manuscript in the message.

  • destination (Path | str | None, default: None) – the directory of the installed copies, included in the message if there is no manuscript to check against.

Return type:

str

main(argv=None)[source]

Run the build command line.

Parameters:

argv (list[str] | None, default: None) – command line arguments, or None to read ‘sys.argv’.

Return type:

int

Returns:

A process exit status – 0 on success, 1 if ‘–check’ found drift.

class research_helpers.build.Document(name, *registries, prose=None, settings=None)[source]

Bases: object

One manuscript, the registries of what it carries, and the checks over them.

A project with one paper can use its registries directly. A project with a second document (a technical note, a supplement, the second half of a split paper) gives each document its own registries and a ‘Document’ class, so that each is built into its own directories and checked against its own manuscript:

paper = Document(‘paper’, tables, figures, prose=prose) note = Document(‘note’, note_tables, note_figures, prose=prose)

A float both documents carry is registered in both documents’ registries. The decorator returns the emitter unchanged, so it stacks. A prose registry may be shared, since each document checks only the entries it prints.

Parameters:
property settings: PaperSettings

Return where the document lives.

build_dir(registry)[source]

Return where a registry’s artefacts are built.

Return type:

Path

Parameters:

registry (Registry)

install_dir(registry)[source]

Return where a registry’s artefacts are read from.

Return type:

Path

Parameters:

registry (Registry)

write_all()[source]

Build every artefact of every registry, and return the paths.

Return type:

list[Path]

install()[source]

Copy every built artefact to where the document reads it, and return the paths.

Return type:

list[Path]

manuscript()[source]

Return the manuscript.

Raises:

FileNotFoundError – if it does not exist, or the document is configured without one.

Return type:

Path

drift()[source]

Compare the manuscript against every registry by kind.

A document configured without a manuscript has each artefact compared with its installed copy instead (see ‘Drift’).

Return type:

dict[str, Drift]

check_prose()[source]

Check the numbers this document states in prose.

Return type:

ProseCheck | None

Returns:

What the check found, or None if the document has no prose registry or no manuscript.

report(drift, prose=None)[source]

Return a description of everything that drifted, and of anything left unchecked.

Return type:

str

Parameters:
main(argv=None)[source]

Run the document’s command line.

Parameters:

argv (list[str] | None, default: None) – command line arguments, or None to read ‘sys.argv’.

Return type:

int

Returns:

A process exit status – 0 on success, 1 if ‘–check’ found anything.