Python API

Sphinx-Needs provides an open API for other Sphinx-extensions to provide specific need-types, create needs or make usage of the filter possibilities.

The API allows the injection of extra configuration, but does not support manipulation of it (e.g remove need types), to keep the final configuration transparent for the Sphinx project authors.

What is public

A project or an extension may rely on the documented surface of Sphinx-Needs: its configuration, its directives and their options, the filter names (c.* included), the needs.json format, and the Python API this page documents (create_need, resolve_need_option and reading the configuration through NeedsSphinxConfig among it). A name in it – a configuration key, a directive or an option, a filter name, a needs.json key, a documented function, class, argument or attribute – is renamed or removed only after a deprecation: a needs.deprecated Sphinx warning (which suppress_warnings silences) or a Python DeprecationWarning, naming the replacement; the old spelling is removed only in a major release. What a name does is not frozen: a minor release may fix what it outputs, or report with a new warning what it accepted in silence, and the changelog says what changed and what to do.

What this page labels provisional follows its label. Through 9.x that is the hop types and NeedItemSource’s general constructor with its keywords: a minor release may add an optional field, a hop kind or a keyword, while a rename, a removal, or a change to the meaning or type of an existing one waits for 10.0, because published sibling extensions construct them. They stay outside the stable surface until the final format version 2 of needs.json carries them: the draft "2a1" (needs.json format 2a1 (draft)), which writes them as they are, does not end their provisional status.

Anything else is internal, and may change in a minor release without a deprecation: a name this page does not document, a module or a name starting with _, and whatever is reached through one. The extensions developed in the Sphinx-Needs repository are changed with it in the same pull request, and released with a raised minimum version of Sphinx-Needs; an older release of one of them may need upgrading together with Sphinx-Needs. New internal modules start with _, and public names live in, or are re-exported from, a public module with __all__; existing modules are not renamed wholesale.

Configuration

API to get or add specific sphinx needs configuration parameters.

All functions here are available under sphinx_needs.api.

add_dynamic_function(app: Sphinx, function: DynamicFunction, name: str | None = None, *, reads: Reads | None = None) → None

Registers a new dynamic function for sphinx-needs.

If name is not given, the name to call the function is automatically taken from the provided function. A callable without a __name__, such as a functools.partial, must be given its name, or NeedsApiConfigException is raised. The used name must be unique.

reads declares what the function reads, for a function you do not own: add_dynamic_function(app, their_function, reads=reads(own="option")) (see reads() and Declaring what a function reads). It wins over a declaration the function carries itself.

Usage:

from sphinx_needs.api import add_dynamic_function

def my_function(app, need, needs, *args, **kwargs):
    # Do magic here
    return "some data"

add_dynamic_function(app, my_function)

Read Dynamic functions for details about how to use dynamic functions.

Parameters:
  • app – Sphinx application object

  • function – Function to register

  • name – Name of the dynamic function as string

  • reads – What the function reads (see reads())

Returns:

None

Changed in version 9.1.0: The reads parameter.

add_extra_option(app: Sphinx, name: str, *, description: str = 'Added by add_extra_option API', schema: FieldStringSchemaType | FieldBooleanSchemaType | FieldIntegerSchemaType | FieldNumberSchemaType | FieldMultiValueSchemaType | None = None, nullable: bool | None = None, parse_variants: bool | None = None, parse_dynamic_functions: bool | None = None) → None

Adds an extra option to the configuration. This option can then later be used inside needs or create_need.

Same impact as using needs_extra_options manually.

Deprecated since version Use: add_field() instead.

Usage:

from sphinx_needs.api import add_extra_option

add_extra_option(app, 'my_extra_option')
Parameters:
  • app – Sphinx application object

  • name – Name of the extra option

  • description – Description of the extra option

  • schema – Schema definition for the extra option

  • nullable – Whether the field allows unset values.

  • parse_variants – Whether variants are parsed in this field.

  • parse_dynamic_functions – Whether dynamic functions are parsed in this field.

add_field(name: str, /, description: str, *, schema: FieldStringSchemaType | FieldBooleanSchemaType | FieldIntegerSchemaType | FieldNumberSchemaType | FieldMultiValueSchemaType | None = None, nullable: bool | None = None, default: Any | None = None, predicates: list[tuple[str, Any]] | None = None, parse_variants: bool | None = None, parse_dynamic_functions: bool | None = None, derive: dict[str, Any] | None = None, derive_by_type: dict[str, Any] | None = None, types: Sequence[str] | None = None) → None

Adds an need field to the configured need schema.

Same impact as using needs_fields manually, or, with types, as declaring the field in the needs_types entries of those need types alone (Fields per need type).

Usage:

from sphinx_needs.api import add_field

add_field('my_field')
Parameters:
  • name – Name of the field

  • description – Description of the field

  • schema – Schema definition for the field

  • nullable – Whether the field allows unset values.

  • default – Default value for the field, if not set in a need.

  • predicates – List of (need filter, value) pairs for default predicate values.

  • parse_variants – Whether variants are parsed in this field.

  • parse_dynamic_functions – Whether dynamic functions are parsed in this field.

  • derive – A rule computing the field for every need, which makes it a derived field: a dict naming a kind and its roles.

  • derive_by_type – Rules per need type, which make it a derived field too: a dict of need type (the directive name) to a rule as derive takes one, overriding derive on that type. A need of a type without a rule holds default (given only without derive), else None, or the empty value of the field’s type when it is not nullable.

  • types – The need types (their directive names) the field is declared on alone, as a field declared on need types alone (Fields per need type): on each of them, and on each type inheriting from one, it is this declaration; on every other type it is absent, null, and a value set there is reported (needs.undeclared_for_type) and ignored. A project’s needs_types entry naming the field on another type declares it there too; a project’s needs_fields entry of the same name is the field, on every type. A type named that no configuration or extension declares by the time the schema is built is one needs.config warning. None (the default): every type.

Raises:

NeedsApiConfigException – If types is not a sequence of at least one need type name (a bare string is refused, not read letter by letter).

Changed in version 9.0.0: The derive and derive_by_type parameters.

Changed in version 9.2.0: The types parameter.

add_need_layout(app: Sphinx, name: str, layout: dict[str, Any]) → None

Adds a new need layout, which a need then names with :layout:.

The given name must not be a layout of needs_layouts (sphinx-needs’ own layouts included), a name of needs_card_layouts (whether or not its card compiles), or one added with this function already, otherwise NeedsApiConfigException gets raised: each is refused before the configured layouts are merged and the cards compiled, as after. The configured GitHub services’ github layout does not count: a layout added under that name replaces the service’s.

The layout is added to sphinx-needs’ own registry, not to the needs_layouts configuration value, which holds only what the project configures (and sphinx-needs’ own layouts and the compiled needs_card_layouts). Every reader sees it, through NeedsSphinxConfig(app.config).layouts: the configured layouts, then the added ones, then the configured GitHub services’ one. The layout is used as given, with the structure a needs_layouts entry has (see Layouts).

Call it from a config-inited handler at a priority above 12, as add_need_type(), or later: before priority 10, the layouts and card layouts of a needs_from_toml file are not read yet, and one of their names would replace the added layout without a warning.

Usage:

from sphinx_needs.api import add_need_layout

add_need_layout(
    app, "my_card", {"grid": "simple", "layout": {"head": ["<<meta('title')>>"]}}
)
Parameters:
  • app – Sphinx application object

  • name – Name of the layout, e.g. ‘my_card’

  • layout – The layout: its grid and layout (and optional style)

Raises:

NeedsApiConfigException – If a layout of the name is configured or added.

add_need_type(app: Sphinx, directive: str, title: str, prefix: str, color: str = '#ffffff', style: str = 'node') → None

Adds a new need type, and registers its directive.

The given directive must not be added with this function already, nor be a default need type that no needs_types entry of the project names, otherwise NeedsApiConfigException gets raised. A project’s entry naming the directive, with or without a title and a prefix, narrows the type instead, however late it is added: the keys the entry gives win, the title, prefix, color and style it leaves out are the ones given here, and its inherits, fields and links apply (see narrowing an extension’s type). The configured GitHub services’ issue, pr and commit do not count: a type added under one of those directives replaces the service’s.

The type is added to sphinx-needs’ own registry, not to the needs_types configuration value, which holds only what the project configures (so Sphinx compares the same value on both sides of an incremental build). Read every type, the added ones included, with get_need_types(), or with NeedsSphinxConfig(app.config).types for the whole entries: the configured types, then the added ones, then the configured GitHub services’ ones.

Call it from a config-inited handler at a priority above 12, or later, as get_need_types() reads it; an earlier call is narrowed by the project’s entries all the same. Unlike a configured entry, the type is not validated, but for its directive, which must not have a :: every need type is also written .. need:<directive>::.

The bare directive is the need’s as the type’s directive_name in the project’s entry naming it (else needs_directive_name) says, decided at this call against the directives registered by then.

Changed in version 9.2.0: A project’s needs_types entry naming the directive narrows the type, where one with a title and a prefix raised NeedsApiConfigException and one with neither was ignored.

Usage:

from sphinx_needs.api import add_need_type

add_need_type(app, 'awesome', 'Awesome', 'AW_', '#000000', 'cloud')
Parameters:
  • app – Sphinx application object

  • directive – Name of the directive, e.g. ‘story’

  • title – Long, human-readable title, e.g. ‘User-Story’

  • prefix – Prefix, if IDs get automatically generated. E.g.: 'US_'

  • color – Hex-color code used in needflow representation. Default: '#ffffff'

  • style – Plantuml-style for needflow representation. Default: ‘node’

Returns:

None

add_warning(app: Sphinx, name: str, function: Callable[[NeedItem, SphinxLoggerAdapter], bool] | None = None, filter_string: str | None = None) → None

Registers a warning.

A warning can be based on the result of a given filter_string or an own defined function.

Parameters:
  • app – Sphinx app object

  • name – Name as string for the warning

  • function – function to execute to check the warning

  • filter_string – filter_string to use for the warning

Returns:

None

get_need_types(app: Sphinx) → list[str]

Returns a list of directive-names from all need types: the configured needs_types, then those added with add_need_type() by extensions, then the issue, pr and commit of the GitHub services needs_services configures (each only where no earlier type has its directive).

Call it from a config-inited handler at a priority above 12, or later: before that, a mapping-shaped needs_types is not yet resolved to its list, and the configured GitHub services’ types are not yet added. They are listed from sphinx-needs’ own priority-12 handler on, so from a handler above 12, after the types added by then (they used to be added only at env-before-read-docs).

Usage:

from sphinx_needs.api import get_need_types

all_types = get_need_types(app)
Parameters:

app – Sphinx application object

Returns:

list of strings

reads(*, own: str | Sequence[str] = (), every: str | Sequence[str] = ()) → Reads

Declare what a dynamic function of your own reads, so that it is computed in order, like a built-in function (see Declaring what a function reads).

Name the parameters whose argument is a field name the function reads, on the call’s own need (own) or on every need (every), each a name or a tuple of names; the argument of a *args parameter is each a field name:

from sphinx_needs.api import reads

@reads(own="test_option", every="target_option")
def my_link(app, need, needs, test_option, target_option):
    ...

@reads() declares that the function reads no computed value; a field nothing computes need not be declared. A field the function reads by a name written in its code, not given as an argument, cannot be declared yet: such a function is computed after the built-in functions, as one that declares nothing.

The result is a Reads, which declares the function it is applied to (its attribute needs_reads), and which add_dynamic_function() takes as reads for a function you do not own. A wrapper made with functools.wraps() carries the declaration of the function it wraps. The names are checked against the function’s parameters when it is registered: one that is not a parameter after app, need and needs is one needs.config warning, and the function declares nothing.

Parameters:
  • own – The parameters whose argument names a field read on the call’s own need.

  • every – The parameters whose argument names a field read on every need.

Returns:

The declaration, which returns the function it is applied to, declared.

Raises:

TypeError – If a name is not a string; @reads written without parentheses is refused by Python itself, as reads takes no positional argument.

Added in version 9.1.0.

resolve_need_option(app: Sphinx, need_type: str, name: str) → str | None

The option name the directive of need_type takes for name, as declared, or None if it takes no such option.

For an extension that wraps or presets need directives: it answers as the need directive itself matches an option it is given, against the same names (the directive’s own options, such as id, status or hide, and every field and link type of the project, including those another need type declares alone and derived fields): a name equal to one first, else a name equal to one ignoring case (str.lower, as docutils lowercases a reStructuredText option), which is returned as it is declared. So for a field declared Owner, "owner" and "OWNER" both give "Owner".

A match says the directive takes the option, not that need_type keeps a value for it: a field or link type declared on other need types alone is matched, and the directive then reports it as not declared for this type (needs.undeclared_for_type) and ignores the value; a derived field is matched and reported as needs.derive_authored.

When: once sphinx-needs has built this build’s schema, in its own env-before-read-docs handler (priority 500): from an env-before-read-docs handler connected at a priority above 500, or at 500 by an extension set up after sphinx_needs, or at any later event, while documents are read included, up to sphinx-needs’ own build-finished handler at priority 10000, which ends it. Before the schema is built, extensions and ubproject.toml may still add need types, fields and link types, so no answer would be final; and on an incremental build, or in the next build of the same application, the environment still holds the previous build’s schema, which is never used here.

Usage:

from sphinx_needs.api import resolve_need_option

def preset(app, env, docnames):
    option = resolve_need_option(app, "req", "owner")  # "Owner", or None

def setup(app):
    app.connect("env-before-read-docs", preset, priority=600)
Parameters:
  • app – Sphinx application object.

  • need_type – The need type, by the name of its directive (as get_need_types() lists it).

  • name – The option name, as an author may write it.

Returns:

The option’s name as declared, or None.

Raises:
  • ValueError – If need_type is no need type of the project.

  • RuntimeError – If it is called before sphinx-needs has built this build’s schema (see above).

Added in version 9.2.0.

Reading the configuration

class NeedsSphinxConfig(config)

The project’s Sphinx-Needs configuration, for an extension to read: NeedsSphinxConfig(app.config).<name> is the configuration value needs_<name> as resolved, with needs_from_toml applied, and for types, layouts, functions and warnings with what extensions and the configured services add included. Read it from a builder-inited handler or later; in config-inited, from a handler at a priority above 600, once sphinx-needs has merged its defaults and compiled the card layouts (types alone is complete from a priority above 12). What an extension adds is included once the extension has added it.

from sphinx_needs.config import NeedsSphinxConfig

need_types = {t["directive"]: t for t in NeedsSphinxConfig(app.config).types}

Every attribute named after a Sphinx-Needs configuration value (Configuration), without the needs_ prefix, is stable to read; functions gives the dynamic functions by name, a mapping, where needs_functions is a list. Reading only: setting a value through it is not supported. Its other members are internal, but one:

variant_data_proxy

The var proxy that filters read over the variant data (needs_variant_data, merged with its file), or None when there is none. Read-only.

class DynamicFunction(*args, **kwargs)

A protocol for a sphinx-needs dynamic function.

__call__(app: Sphinx, need: NeedItem | NeedPartItem | None, needs: NeedsView | NeedsMutable, *args: Any, **kwargs: Any) → str | int | float | list[str] | list[int] | list[float] | list[NeedLink] | None

Call self as a function.

class Reads(own: tuple[str, ...] = (), every: tuple[str, ...] = ())

What a dynamic function of your own reads, as reads() declares it: the parameters whose argument is a field name, read on the call’s own need (own) or on every need (every).

Applied to a function, it declares it: reads(own="option")(my_function) is @reads(own="option"). Given to add_dynamic_function() as reads, it declares a function you do not own.

Need

create_need(app: Sphinx, state: RSTState | None, source: NeedItemSource, need_type: str, title: str, *, id: str | None = None, fields: Mapping[str, Any] | None = None, links: Mapping[str, Any] | None = None, content: str | StringList | MarkupContent = '', doctype: str | None = None, jinja_content: bool | None = None, full_title: str | None = None, allow_type_coercion: bool = True) → tuple[NeedItem, list[Node]]

Create a need, record it in the project, and return it with its nodes.

The need’s field values and its links are two mappings, kept apart from the function’s own parameters, so any name the project declares for a field or a link type can be used: nothing a project names a field meets an argument of this function. It is how sphinx-needs’ need directives, list2need and needservice, sphinx-codelinks’ one-line needs and sphinx-test-reports’ needs are created; needimport, needs_external_needs and sphinx-codelinks’ multi-line needs are created from records, through the same path.

from sphinx.util.docutils import SphinxDirective

from sphinx_needs.api import NeedItemSource, create_need


class ReqDirective(SphinxDirective):
    def run(self):
        need, need_nodes = create_need(
            self.env.app,
            self.state,
            NeedItemSource.from_directive(self),
            "req",
            "A requirement",
            id="REQ_1",
            fields={"status": "open", "priority": "high"},
            links={"links": ["REQ_0"]},
        )
        return need_nodes
Parameters:
  • app – Sphinx application object.

  • state – The parser state of the document the need is rendered in; None only for an external source, whose need has no nodes.

  • source – Where the need comes from, a NeedItemSource. It gives the need its docname, lineno and the other location fields, and says whether it is imported or external. In a directive it is NeedItemSource.from_directive(self), as above; for a need on the page at a line the caller gives (the directive’s parser line, as add_need took it), NeedItemSource.on_page(docname, lineno=lineno). Those two spellings are stable; NeedItemSource’s general constructor and its keywords are provisional (see What is public). Any other object, even one with the NeedItemSourceProtocol interface, is refused: the protocol is how a need’s source is read (need.source), not what a need is created from.

  • need_type – The need type, by the name of its directive.

  • title – The need’s title.

  • id – The need’s id. If not given, one is generated (see generate_need_id()).

  • fields – The need’s field values, by field name: the core fields the field schema declares, such as status, tags, hide or template (title is an argument of its own), and the fields the project declares (needs_fields). A value None is a value not given, so a default applies.

  • links – The need’s links, by link type (needs_links): a string of comma separated ids, or a list of ids.

  • content – The need’s content, either as a str or a StringList (a string with mapping to the source text), parsed by the parser of the document the need is created in; or as a MarkupContent, the content with the markup it is written in. See Content written in another markup.

  • doctype – The source suffix the need is recorded as written in (e.g. ".rst"). If not given, it is the markup of a MarkupContent content, else the suffix of the document the need is created in.

  • jinja_content – If true, the content is rendered as a Jinja template first.

  • full_title – The title before it was trimmed, if it was: the generated id is derived from it. A string; any other value is refused.

  • allow_type_coercion – If true, values are coerced to the type of their field where possible.

Returns:

The need, as it is recorded in the project (the object, not a copy), and the nodes to insert into the document (none for an external need). The need is the one created in this read of the document: in a parallel build (-j) the merge of the processes’ needs may still drop it, for an id another process created too.

Raises:
  • InvalidNeedException – If the need is refused, before anything is recorded: for a value its field or link type refuses, an id that is missing, invalid or taken, a MarkupContent whose markup names no reStructuredText or MyST parser of the project, and any other validation the need fails, such as an unknown need type, a constraint not in needs_constraints, a template that does not exist, or Jinja content that does not render; (type invalid_value) for a full_title that is not a string, whether or not an id is given; (type invalid_kwargs) for a name in fields or links the project does not declare, or one given in the wrong place: a link type in fields, a field in links, or one of this function’s own arguments (title, content, …) in either.

  • TypeError – If source is not a NeedItemSource, before anything is recorded.

  • ValueError – If state is None for a source that is not external.

Added in version 9.2.0.

class DefaultContextData
RecordReader

Which reader a needs.json record is read by: a needimport directive, or an external source of needs_external_needs.

alias of Literal[‘import’, ‘external’]

add_external_need(app: Sphinx, need_type: str, title: str | None = None, id: str | None = None, need_source: NeedItemSourceProtocol | None = None, external_url: str | None = None, external_css: str = 'external_link', content: str = '', status: str | None = None, tags: str | list[str] | None = None, constraints: str | None = None, **kwargs: Any) → list[Node]

Adds an external need from an external source. This need does not have any representation in the current documentation project. However, it can be linked and filtered. It’s reference will open a link to another, external sphinx documentation project.

It returns an empty list (without any nodes), so no nodes will be added to the document.

Parameters:
  • app – Sphinx application object.

  • need_type – Name of the need type to create.

  • title – String as title.

  • id – ID as string. If not given, a id will get generated.

  • external_url – URL as string, which shall be used as link to the original need source

  • content – Content as single string.

  • status – Status as string.

  • tags – A list of tags, or a comma separated string.

  • constraints – constraints as single, comma separated string.

  • external_css – CSS class name as string, which is set for the <a> tag.

  • need_source –

    Where the need comes from, a NeedItemSource; given, it wins over external_url.

    Deprecated since version 9.2.0: Any other object (one with the NeedItemSourceProtocol interface alone) is one DeprecationWarning at the calling line, and is refused from 10.0.

Raises:

InvalidNeedException – If the need could not be added due to a validation issue.

add_need(app: Sphinx, state: RSTState | None = None, docname: str | None = None, lineno: int | None = None, need_type: str = '', title: str = '', *, need_source: NeedItemSourceProtocol | None = None, id: str | None = None, content: str | StringList | MarkupContent = '', lineno_content: int | None = None, doctype: str | None = None, status: str | None = None, tags: str | list[str] | None = None, constraints: str | list[str] | None = None, parts: dict[str, NeedsPartType] | None = None, arch: dict[str, str] | None = None, signature: str | None = None, sections: list[str] | None = None, jinja_content: bool | None = None, hide: bool | str | None = None, collapse: bool | str | None = None, style: str | None = None, layout: str | None = None, template: str | None = None, pre_template: str | None = None, post_template: str | None = None, is_import: bool = False, is_external: bool = False, external_url: str | None = None, external_css: str = 'external_link', full_title: str | None = None, allow_type_coercion: bool = True, **kwargs: Any) → list[Node]

Creates a new need and returns its node.

add_need allows to create needs programmatically and use its returned node to be integrated in any docutils based structure.

Deprecated since version 9.2.0: Use create_need(), which takes the need’s location as a NeedItemSource (NeedItemSource.from_directive(self) in a directive) and its fields and links as two mappings, and returns the need with its nodes. Each call is a DeprecationWarning at the calling line; add_need is removed in 10.0. See Migrating from add_need.

kwargs can contain options defined in needs_fields and needs_links. If an entry is found in kwargs, which is not specified in the configuration or registered e.g. via add_field, an exception is raised.

If is_external is set to True, no node will be created. Instead, the need is referencing an external url. Used mostly for needs_external_needs to integrate and reference needs from external documentation.

Raises:

InvalidNeedException – If the need could not be added due to a validation issue, such as a full_title that is not a string (type invalid_value); also, before the need is recorded, if a MarkupContent’s markup names no reStructuredText or MyST parser of the project.

If the need is within the current project, i.e. not an external need, the following parameters are used to help provide source mapped warnings and errors:

Parameters:
  • docname – documentation identifier, for the referencing document.

  • lineno – line number of the top of the directive (1-indexed).

  • lineno_content – line number of the content start of the directive (1-indexed).

Otherwise, the following parameters are used:

Parameters:
  • is_external – Is true, no node is created and need is referencing external url

  • external_url – URL as string, which is used as target if is_external is True

  • external_css – CSS class name as string, which is set for the <a> tag.

Additional parameters:

Parameters:
  • app – Sphinx application object.

  • state – Current state object.

  • need_type – Name of the need type to create.

  • title – String as title.

  • full_title – This is given, if an auto-generated title was trimmed. It is used to auto-generate the ID, if required. A string.

  • id – ID as string. If not given, an id will get generated.

  • content –

    Content of the need, either as a str or a StringList (a string with mapping to the source text), parsed by the parser of the document the need is created in; or as a MarkupContent, the content with the markup it is written in (parsed by that markup’s parser) and optionally the file and line it came from. See Content written in another markup.

    Changed in version 9.0.0: content may be a MarkupContent.

  • doctype – The source suffix the need is recorded as written in (e.g. ".rst"). If not given, it is the markup of a MarkupContent content, else the suffix of the document the need is created in.

  • status – Status as string.

  • tags – A list of tags, or a comma separated string.

  • constraints – Constraints as single, comma separated, string.

  • parts – The need’s parts, keyed by part id. Kept as given only for an external or a hidden need: a need on a page takes its parts from its content (its need_part / np roles), a given part whose id the content marks takes the content’s text, and the given part ids the content does not mark are dropped, with one needs.part warning per need naming them.

  • constraints_passed – Contains bool describing if all constraints have passed

  • hide – If True then the need is not rendered. None means that the value can be overriden by global defaults, else it is set to False.

  • collapse – If True, then hide the meta-data information of the need. None means that the value can be overriden by global defaults, else it is set to False.

  • style – String value of class attribute of node.

  • layout – String value of layout definition to use

  • template – Template name to use for the content of this need

  • pre_template – Template name to use for content added before need

  • post_template – Template name to use for the content added after need

  • allow_type_coercion – If true, values will be coerced to the expected type where possible.

Returns:

list of nodes

del_need(app: Sphinx, need_id: str) → None

Deletes an existing need.

Parameters:
  • app – Sphinx application object.

  • need_id – Sphinx need id.

generate_need(needs_config: NeedsSphinxConfig, needs_schema: FieldsSchema, need_type: str, title: str, *, need_source: NeedItemSourceProtocol | None = None, docname: str | None = None, lineno: int | None = None, id: str | None = None, doctype: str = '.rst', content: str = '', lineno_content: int | None = None, status: str | None = None, tags: str | list[str] | None = None, constraints: str | list[str] | None = None, parts: dict[str, NeedsPartType] | None = None, arch: dict[str, str] | None = None, signature: str | None = None, sections: Sequence[str] | None = None, jinja_content: bool | None = None, hide: bool | str | None = None, collapse: bool | str | None = None, style: str | None = None, layout: str | None = None, template_root: Path | None = None, template: str | None = None, pre_template: str | None = None, post_template: str | None = None, is_import: bool = False, is_external: bool = False, external_url: str | None = None, external_css: str = 'external_link', full_title: str | None = None, allow_type_coercion: bool = True, **kwargs: Any) → NeedItem

Creates a validated need data entry, without adding it to the project.

Important

This function does not parse or analyse the content, and so will not auto-populate the parts or arch fields of the need from the content.

It will also not validate that the ID is not already in use within the project.

Raises:

InvalidNeedException – If the data fails any validation issue, such as a full_title that is not a string (type invalid_value).

kwargs can contain options defined in needs_fields and needs_links. If an entry is found in kwargs, which is not specified in the configuration or registered e.g. via add_field, an exception is raised.

If the need is within the current project, i.e. not an external need, the following parameters are used to help provide source mapped warnings and errors:

Parameters:
  • docname – documentation identifier, for the referencing document.

  • lineno – line number of the top of the directive (1-indexed).

  • lineno_content – line number of the content start of the directive (1-indexed).

Otherwise, the following parameters are used:

Parameters:
  • is_external – Is true, no node is created and need is referencing external url

  • external_url – URL as string, which is used as target if is_external is True

  • external_css – CSS class name as string, which is set for the <a> tag.

Additional parameters:

Parameters:
  • app – Sphinx application object.

  • state – Current state object.

  • need_type – Name of the need type to create.

  • title – String as title.

  • full_title – This is given, if an auto-generated title was trimmed. It is used to auto-generate the ID, if required. A string.

  • id – ID as string. If not given, an id will get generated.

  • content – Content of the need

  • status – Status as string.

  • tags – A list of tags, or a comma separated string.

  • constraints – Constraint names as comma separated string or list of strings

  • constraints_passed – Contains bool describing if all constraints have passed

  • hide – If True then the need is not rendered. None means that the value can be overriden by global defaults, else it is set to False.

  • collapse – If True, then hide the meta-data information of the need. None means that the value can be overriden by global defaults, else it is set to False.

  • style – String value of class attribute of node.

  • layout – String value of layout definition to use

  • template_root – Root path for template files, only required if the template_path config is relative.

  • template – Template name to use for the content of this need

  • pre_template – Template name to use for content added before need

  • post_template – Template name to use for the content added after need

  • allow_type_coercion – If true, values will be coerced to the expected type where possible.

  • need_source –

    Where the need comes from, a NeedItemSource (NeedItemSource.on_page(docname, lineno=lineno)); given, it wins over docname, lineno, lineno_content, is_import, is_external and external_url.

    Deprecated since version 9.2.0: Any other object (one with the NeedItemSourceProtocol interface alone) is one DeprecationWarning at the calling line, and is refused from 10.0.

generate_need_id(app: Sphinx, need_type: str, title: str, content: str | StringList | MarkupContent = '', *, full_title: str | None = None) → str

Return the id create_need() (and add_need()) assigns to a need that is given no id.

The arguments are those of the same name given to create_need(), which derives the id with this same code: the type’s prefix, followed by a hash of full_title (if given, else title), or of content when that is empty, shaped by needs_id_from_title and needs_id_length. An extension that creates needs can use it to find out, before it calls create_need(), whether a need with that id already exists.

The id is not checked: create_need() may still refuse the need, for example because needs_id_required is set, the id does not match needs_id_regex, or a need with that id already exists.

Parameters:
  • app – Sphinx application object.

  • need_type – Name of the need type, as given to create_need().

  • title – Title of the need.

  • content – Content of the need, either as a str or a StringList, as create_need() takes it.

  • full_title – The untrimmed title, if title was trimmed: a string.

Returns:

The generated id.

Raises:

InvalidNeedException – If need_type is not a configured need type (type invalid_type), or full_title is not a string (type invalid_value), as create_need() raises it.

get_needs_view(app: Sphinx) → NeedsView

Return a read-only view of all resolved needs.

Important

this should only be called within the write phase, after the needs have been fully collected. If not already done, this will ensure all needs are resolved (e.g. back links have been computed etc), and then lock the data to prevent further modification.

ingest_need_record(app: Sphinx, state: RSTState, record: Mapping[str, Any], *, need_source: NeedItemSourceProtocol, content_markup: str | None = None, content_source: tuple[str, int] | None = None, allow_type_coercion: bool = True, unknown_keys: set[str] | None = None) → tuple[list[Node], set[str]]

Create one need from a record of a needs.json format_version 1.

This is the path for needs that come from outside the page they are rendered on; needimport creates each of its needs through it.

record holds the keys a need object has in a needs.json of format_version 1 (the format sphinx-needs writes): type, title, id, fields and links as authored, content, doctype, and so on. A record of a later format is not read by this function. It is not modified. A legacy description is taken as the content when the record has none (no content key: sphinx-needs before 4.0 wrote the content as description); a record with a content, even an empty one, keeps its description as the field or link of that name. A caller that builds records sets content, even "", for a need without one, so that a description it gives is not taken as the content. Keys the project does not know are dropped and returned; keys that are computed rather than imported (such as docname, lineno or the <link>_back names) are dropped silently, and so is the legacy full_title, unless the project has a field or a link type of that name. The need is created from the rest through the same path as create_need(): type as its need_type, and its fields and links as one mapping, so a record key named like an argument of that path is the field of its name.

Parameters:
  • app – The Sphinx application.

  • state – The parser state of the document the need is rendered in.

  • record – The need record.

  • need_source –

    Where the need is recorded as coming from, a NeedItemSource: for a need placed at a line the caller gives, NeedItemSource.on_page(docname, lineno=lineno); for one placed at a directive, NeedItemSource.from_directive(self). A need recorded as imported (is_import) takes the provisional general constructor, NeedItemSource(hops, lineno=lineno, is_import=True), as needimport does: no stable constructor carries is_import yet.

    Deprecated since version 9.2.0: Any other object (one with the NeedItemSourceProtocol interface alone) is one DeprecationWarning at the calling line, and is refused from 10.0.

  • content_markup – The source suffix of the markup the record’s content is written in: given, the content is created as a MarkupContent with this markup, and the need records it as its doctype if the record has none.

  • content_source – The source of that MarkupContent: (path, first_line) of the content. Only meaningful with content_markup.

  • allow_type_coercion – As for create_need().

  • unknown_keys – If given, the record’s keys that are unknown to the project are added to it before the need is created, so a caller collecting them over several records has them also for a record whose need cannot be created.

Returns:

The need’s nodes, and the set of the record’s keys that were dropped as unknown to the project (the caller decides whether to warn about them).

Raises:

InvalidNeedException – What create_need() raises; (type content) if the record’s content (or the legacy description taken as it) is present but not a string; (type invalid_type) if its type is not a string; (type invalid_value) if its title is missing or not a string; (type invalid_id) if its id is given but empty or not a string; (type parts) if its parts is not a mapping of mappings; and (type content_markup) if content_source is given without content_markup. The caller decides how to report it.

Added in version 9.0.0.

Migrating from add_need

Deprecated since version 9.2.0: add_need() is deprecated, and is removed in 10.0. Each call is one Python DeprecationWarning at the calling line (not a Sphinx warning, so a -W build of a project using an extension that still calls it is not failed by it).

create_need() takes the need’s location as one NeedItemSource, and its field values and links as two mappings, kept apart from its own arguments, so no name a project gives a field meets one of them. In a directive the source is NeedItemSource.from_directive(self):

# before
from sphinx.util.docutils import SphinxDirective

from sphinx_needs.api import add_need


class ReqDirective(SphinxDirective):
    def run(self):
        return add_need(
            self.env.app, self.state, self.env.docname, self.lineno,
            "req", self.arguments[0],
            id=self.options.get("id"), status="open", priority="high", links="REQ_0",
        )
# after
from sphinx.util.docutils import SphinxDirective

from sphinx_needs.api import NeedItemSource, create_need


class ReqDirective(SphinxDirective):
    def run(self):
        need, nodes = create_need(
            self.env.app, self.state, NeedItemSource.from_directive(self),
            "req", self.arguments[0],
            id=self.options.get("id"),
            fields={"status": "open", "priority": "high"},
            links={"links": "REQ_0"},
        )
        return nodes

from_directive works for any docutils Directive, not only a SphinxDirective. With it, need["lineno"] is the line the directive is written on, which differs from self.lineno where rst_prolog, an include or generated input shifts the parser’s count; to keep the old number exactly, use NeedItemSource.on_page(self.env.docname, lineno=self.lineno), which is the source add_need built. on_page gives the need its page and the line the caller passes, nothing more, so it is the source wherever the caller chooses the line: the directive’s own parser line, as add_need took it, or the lines a directive’s data names. It works in a docutils Directive and in a nested parse alike, and is also the need_source to give generate_need; create_need records the page’s file on its hop, generate_need, which has no application, does not. from_directive and on_page are stable, as the class’s name is; the hops they build, and NeedItemSource’s general constructor with its keywords (hops, lineno, lineno_content, parser_lineno, is_import, upstream), are provisional until the final needs.json format version 2 carries them (the draft "2a1" does not end that): through 9.x a minor release only adds to them, and a rename or a removal waits for 10.0 (see What is public, and the provisional note under “Data”, below). The constructor is for sphinx-needs’ own producers and advanced use. create_need takes a NeedItemSource and nothing else: an object with the NeedItemSourceProtocol interface alone is refused with a TypeError.

A need placed on a page is created with a parser state, through create_need as through add_need: an event handler or a role, which has none, cannot create one, and without a state only an external need can be created (add_external_need). Where add_need refused that with an AssertionError, create_need raises ValueError.

Deprecated since version 9.2.0: A need_source that is not a NeedItemSource – an object with the NeedItemSourceProtocol interface alone – given to generate_need(), add_external_need() or ingest_need_record() is one Python DeprecationWarning at the calling line, and the need is created as before; it is refused from 10.0, as create_need refuses it now. add_need gives no second warning for it.

add_need’s other arguments:

add_need

create_need

docname, lineno

source=NeedItemSource.from_directive(self); for a line the caller gives (add_need’s self.lineno), NeedItemSource.on_page(docname, lineno=lineno)

lineno_content

from_directive sets it (the provisional constructor takes lineno_content=)

need_source

source, a NeedItemSource (an object with the NeedItemSourceProtocol interface alone, which add_need took, is refused)

status, tags, constraints, hide, collapse, style, layout, template, pre_template, post_template, and every field

fields={...}

every link type

links={...}

id, content, doctype, jinja_content, full_title, allow_type_coercion

the same keyword

is_external, external_url, external_css

add_external_need(), which is not deprecated (an external hop as create_need’s source, with state=None, is the provisional hop type, and takes no external_css)

is_import

NeedItemSource(..., is_import=True) (the provisional constructor), or ingest_need_record() for a needs.json-style record

parts, arch, sections, signature

none: for a need on the page they are computed from its content and the page (a record’s parts and arch through ingest_need_record()); an external need add_need made kept them as given, which neither create_need nor add_external_need takes (add_need warns, once per need on a page, of the given parts its content drops)

the deprecated links_string, hide_tags, hide_status, delete, constraints_passed

none (add_need drops them, with a warning)

the return value, list[nodes.Node]

(need, nodes): the need as recorded, and the nodes

The five source classes of 9.1.0, NeedItemSourceUnknown, NeedItemSourceDirective, NeedItemSourceService, NeedItemSourceExternal and NeedItemSourceImport, are deprecated names of sphinx_needs.need_item until 10.0: each is a function taking its class’s keywords and returning the NeedItemSource with the legacy keys that class gave, and reading one is a DeprecationWarning. NeedItemSourceUnknown(docname=docname, lineno=lineno) is NeedItemSource.on_page(docname, lineno=lineno).

Content written in another markup

An extension that creates needs whose text was written somewhere else – a comment in a source file, an imported file – in a markup that is not the page’s passes MarkupContent(text, markup=..., source=...) as the content of create_need() (add_need() takes the same content). markup is the source suffix of that markup (".rst", ".md", or any suffix source_suffix maps to a reStructuredText or MyST parser): the content is parsed by that parser, whatever the page’s parser, and the need records the suffix as its doctype. With source=(path, first_line), the warnings raised while parsing the content, and those for references in it that do not resolve, name path and the line the content came from rather than the page – through sphinx.util.docutils.switch_source_input, the same mechanism sphinx.ext.autodoc uses to report a docstring’s lines at the Python file. create_need takes no other argument for this, and takes the fields and links as mappings, so no field or link name is reserved. ingest_need_record() creates a need from a needs.json-style record, and is the path needimport itself takes for each need it imports; with needs_import_parse_by_doctype or its :parse_by_doctype: option set, needimport passes a record’s doctype as the content’s markup when the project parses it and the need is not rendered through a template.

class MarkupContent(text: str | StringList, *, markup: str, source: tuple[str, int] | None = None)

A need’s content, with the markup it is written in and where it came from.

Pass it to create_need() as content: the content is then parsed by the parser the project registers for markup, whatever the parser of the document the need is created in, and the need records markup as its doctype unless one is given. See Content written in another markup.

Parameters:
  • text – The content, as a str or a StringList. Its lines are those a newline separates (a carriage return ending one is ignored, a form feed inside one does not end it).

  • markup – The source suffix of the markup the text is written in, such as ".rst" or ".md" – any suffix the project’s source_suffix maps to a reStructuredText or MyST parser. create_need raises InvalidNeedException (type content_markup) before recording the need if it maps to neither.

  • source –

    (path, first_line): the file the lines were written in, and the 1-based line of the first of them in it. Every diagnostic raised while parsing the content, and every node created from it, then names path and the line each content line sits on (first_line + i). Sphinx prints node-based locations as absolute paths, so pass an absolute path.

    None anchors the content in the document the need is created in, as a need directive’s content is: at the source’s lineno_content, else lineno, read as lines of the PARSER’s input, as a directive’s self.content_offset + 1 and self.lineno give them (they differ from the file’s lines after rst_prolog or an include, and are mapped back to them). Pass them as parser lines: a resolved file line is mapped as if it were a parser line and then names a wrong line, possibly in another file (an included one, or the pseudo-file of rst_prolog); only when it falls outside the parser’s input is that caught, and the content anchored at the need’s own line.

    Ignored for content rendered from a template or with jinja_content, which no file holds: that is anchored at the need’s own line.

A malformed markup (not a non-empty str) or source (not a str path, non-empty, and an int line of at least 1) raises ValueError or TypeError here, where the object is made; whether the project registers the markup is create_need()’s question.

Added in version 9.0.0.

The content is parsed into the page it is rendered on, so a label defined in it belongs to that page and other pages reference it there. The warnings myst-parser logs itself, such as an unknown directive or role in Markdown content, name the page with the content’s line; with myst-parser 4 their file is the content’s path with .rst appended. In a reStructuredText page, MyST content’s [text](#anchor) links are not resolved and a missing anchor is not reported; use the {ref} and {need} roles, which work in both.

What such content should not contain

This version parses the content as the page would parse it, and refuses none of the following; a later version will, as ubCode’s reader already does:

  • directives that read files (include, literalinclude, csv-table with :file:, raw with :file:), and images and other references to files by a relative path: Sphinx’s include, literalinclude and images, and everything in MyST content, resolve against the page, while csv-table :file: and raw :file: in reStructuredText content resolve against the content’s file;

  • raw, and raw HTML in Markdown;

  • need or needimport directives: the needs they create are recorded against the page;

  • anything relying on the page’s substitutions or rst_prolog: it works, but the same text means something else on another page.

Events

Sphinx-Needs registers two Sphinx events, which an extension connects to with app.connect(event_name, callback). Both are emitted once per build, in the main process (also in a parallel build), when the needs are first requested after all documents are read. Each callback is called as callback(app, needs), where needs is the mutable mapping of need ids to needs, NeedsMutable.

needs-before-post-processing

Emitted before the needextend directives are applied and before dynamic functions, links and constraints are resolved. This is the hook for an extension that writes into the needs: a needextend in a document is applied afterwards, and can override what the extension wrote.

needs-before-sealing

Emitted after those steps, and before the needs become read-only. A callback sees the needs as the needextend directives left them, with dynamic functions, links and constraints resolved.

def default_status(app, needs):
    for need in needs.values():
        if need["type"] == "req" and not need["status"]:
            need["status"] = "draft"

def setup(app):
    app.connect("needs-before-post-processing", default_status)

A field with a derive rule cannot be set from a callback, a filter function or a dynamic function: it holds what its rule computes, and need[name] = value raises KeyError.

Exceptions

exception FunctionParsingException(message: str, name: str | None)

Called if parsing of given function string has not worked

exception InvalidNeedException(type_: Literal['invalid_kwargs', 'invalid_type', 'invalid_field_value', 'invalid_link_option', 'missing_id', 'invalid_id', 'duplicate_id', 'invalid_title', 'invalid_status', 'invalid_tags', 'invalid_layout', 'invalid_style', 'invalid_value', 'invalid_constraints', 'invalid_jinja_content', 'invalid_template', 'global_option', 'failed_init', 'content_markup', 'content', 'parts'], message: str)

Raised when a need could not be created/added, due to a validation issue.

exception NeedsApiConfigException

A configuration changes collides with the already provided configuration by the user.

Example: An extension wants to add an already existing needs_type.

exception NeedsApiConfigWarning
exception NeedsConfigException
exception NeedsConstraintFailed
exception NeedsInvalidException
exception NeedsInvalidFilter
exception VariantParsingException(message: str)

Called if parsing of given function string has not worked

Data

class NeedConstraintResults(constraints: Mapping[str, tuple[tuple[str, bool, str | None], ...]])

A class representing the results of constraints on a need item.

all_passed() → bool

Return True if all constraints passed, False otherwise.

first_error() → str | None

Return the first error message found in the constraint results, or None if none found.

to_dict() → dict[str, dict[str, bool]]

Convert the constraint results to a dictionary.

class NeedItem(*, source: NeedItemSourceProtocol | None, content: NeedsContent, core: NeedsInfoType, extras: dict[str, AllowedTypes | None], links: dict[str, list[str]] | dict[str, list[NeedLink]], backlinks: dict[str, list[str]] | dict[str, list[NeedLink]] | None = None, parts: Sequence[NeedPartData] = (), modifications: Sequence[NeedModification] = (), constraint_results: NeedConstraintResults | None = None, dynamic_fields: dict[str, FieldFunctionArray | LinksFunctionArray] | None = None, derived: Iterable[str] = (), _validate: bool = True)

A class representing a single need item.

Add a backlink to the need.

add_modification(modification: NeedModification) → None

Add a modification to the need item.

Parameters:

modification – The modification to add.

property constraint_results: NeedConstraintResults | None

Return the constraint results of the need item.

property content: NeedsContent

Return the content of the need item.

copy() → NeedItem

Return a copy of the NeedItem.

filter_context() → dict[str, Any]

Build a flat dict of all need fields, suitable for use as an eval/template context.

This is more efficient than {**need} because it avoids the per-key multi-namespace lookup chain and materializes link string lists only once.

get(key: str, default: Any = None) → Any

Get an item by key with a default value.

get_backlinks(link_type: str, *, as_str: Literal[False]) → list[NeedLink]

Get backlink references by link_type key.

Raises:

KeyError – If the link_type is not a backlink type.

get_extra(key: str) → AllowedTypes | None

Get an extra by key.

Raises:

KeyError – If the key is not an extra.

get_links(link_type: str, *, as_str: Literal[False]) → list[NeedLink]

Get link references by link_type key.

Raises:

KeyError – If the link_type is not a link type.

get_part(part_id: str) → NeedPartData | None

Get a part by its ID.

get_part_item(part_id: str) → NeedPartItem | None

Get a part, merged with its parent need, by its ID.

property has_dynamic_fields: bool

Return True if the need item has dynamic fields, False otherwise.

property id: str

Return the ID of the need item.

is_in_document(docname: str) → bool

Check if the need is in a given document.

items() → Iterable[tuple[str, Any]]

Return the items of the need item.

iter_backlinks_items(as_str: Literal[False] = False) → Iterable[tuple[str, list[NeedLink]]]

Yield all backlinks as (link_type, references) pairs.

iter_core_items() → Iterable[tuple[str, Any]]

Return the core items of the need item.

iter_extra_items() → Iterable[tuple[str, AllowedTypes | None]]

Yield all extras as key-value pairs.

iter_extra_keys() → Iterable[str]

Yield all extra keys.

iter_links_items(as_str: Literal[False] = False) → Iterable[tuple[str, list[NeedLink]]]

Yield all links as (link_type, references) pairs.

Yield all link_type keys.

iter_part_items() → Iterable[NeedPartItem]

Yield all parts, merged with the parent part, from a need.

keys() → Iterable[str]

Return the keys of the need item.

property modifications: tuple[NeedModification, ...]

Return the modifications of the need item.

property parts: Iterable[NeedPartData]

Return the parts of the need item.

Reset all backlinks to empty lists (including parts).

set_constraint_results(constraint_results: NeedConstraintResults | None, *, _recompute: bool = True) → None

Set the constraint results for the need item.

Parameters:

constraint_results – The constraint results to set.

set_content(content: NeedsContent) → None

Replace the content of the need item.

Parameters:

new_content – The new content to set.

set_parts(parts: Sequence[NeedPartData], *, _recompute: bool = True) → None

Set the parts of the need item.

Parameters:

parts – The parts to set.

Sort the back links, and the parts’ back links, in need-id order.

Ids are compared as strings (REQ_10 before REQ_9), as the dynamic functions read the needs of a set. This is the order a dynamic function reads a back link list in; sort_links() sorts it again for the output.

Sort all link and backlink lists in place, removing duplicates.

Sorts outgoing links, backlinks, and part backlinks by the link’s string representation, treating embedded digit sequences as integers so that e.g. REQ_2 < REQ_9 < REQ_10, ids with one such key by the string itself (R_02 < R_2 < r_2), and links to one target by the link with its condition (R_2 < R_2[status == 'open']). This makes the order of linked need IDs deterministic, regardless of the order in which they were added or the iteration order of the needs collection. Duplicate NeedLink entries (same id, part, and condition) are collapsed.

property source: NeedItemSourceProtocol

Return the source of the need item.

values() → Iterable[Any]

Return the values of the need item.

class NeedItemSource(hops: Iterable[NeedSourceHop] = (), *, lineno: int | None = None, lineno_content: int | None = None, parser_lineno: int | None = None, is_import: bool = False, upstream: Iterable[NeedSourceHop] = ())

Where a need comes from: the hops its text came through, and the location facts the hops cannot give yet.

Every need sphinx-needs creates has one, and so does a need an extension creates through create_need(): in a directive, NeedItemSource.from_directive(self); for a need on the page at a line the caller gives (the directive’s parser line, as add_need took it; a line its data names), NeedItemSource.on_page(docname, lineno=lineno).

Stable: the class’s name, from_directive() (the first hops it takes are provisional, as every hop is), on_page(), and reading a source through NeedItemSourceProtocol (dict_repr). Provisional: the constructor itself and its keywords (hops, lineno, lineno_content, parser_lineno, is_import, upstream), like the hop types, until the final needs.json format version 2 carries them (the draft "2a1" does not end that): through 9.x a minor release only adds to them (a new optional keyword), and a rename, a removal or a change of a keyword’s meaning or type waits for 10.0, because published sibling extensions construct them (see What is public). They are the stored legacy facts a later release turns into projections of the hops, and are for sphinx-needs’ own producers and advanced use.

The legacy location keys of the need (dict_repr, which NeedItemSourceProtocol requires), one rule each:

  • docname: the document of the placement, the last hop with one (None without);

  • lineno: lineno as given, else the start line of hops[0];

  • lineno_content: as given;

  • is_external / external_url: whether hops[0] is an external hop, and its URL;

  • is_import: as given (no hop says that a needimport read the file: an included file is a file hop too).

Added in version 9.2.0.

property docname: str | None

The document of the placement hop.

classmethod from_directive(directive: docutils.parsers.rst.Directive, *, first: Iterable[NeedSourceHop] = ()) → NeedItemSource

The source of a need a directive creates, as a need directive records its own.

Its hops are the file and the page the directive is written on (the lines of the directive where the file holds them), after first: the hops the need’s text came through before the directive, innermost first, if any. lineno is the line the directive is written on, resolved through the state machine (which rst_prolog, an include or generated input shift), in its file also inside an include’s slice (:start-line:, :start-after:), where docutils counts from the slice’s first line; its content offset and its line in the parser’s own count are kept as lineno_content and parser_lineno.

Parameters:
  • directive – The directive, a docutils Directive (a SphinxDirective is one) being run in a Sphinx build.

  • first – Hops to place before the directive’s own (provisional, as the hops are).

hops: tuple[NeedSourceHop, ...]

Where the need’s text came from, innermost first (provisional).

is_import: bool

Whether needimport read the need from a needs.json.

property lineno: int | None

lineno as given, else the start line of hops[0].

lineno_content: int | None

The line of the content, as the parser counts it.

classmethod on_page(docname: str | None, *, lineno: int | None = None) → NeedItemSource

The source of a need placed on the page docname, at lineno: one page hop of that document, with no line of its own (nothing is read from the file, nothing checked against it), whose file create_need() records (generate_need, which has no application, records none); no hop without a document.

It gives the need its page and the line the caller passes, nothing more: it is the source add_need(app, state, docname, lineno, ...) built. Use it where a parser state is at hand and the caller chooses the need’s line: a directive keeping add_need’s line, its own parser line (self.lineno), or one placing needs at the lines its data names; and as the need_source of generate_need() or ingest_need_record(). It works alike in a docutils Directive and in a nested parse. A need placed on a page is created with a parser state, so an event handler or a role, which has none, cannot create one: without a state only an external need is created (add_external_need()). For a need the directive itself is written as, from_directive() records more, and is the one to use.

Parameters:
  • docname – The document the need is placed on, or None.

  • lineno – The need’s line, as the caller counts it, or None.

parser_lineno: int | None

The directive’s line in the parser’s own line space, where that differs from lineno.

lineno is the line the directive is written on, resolved back through the state machine. The parser counts the lines it was handed instead, and anything that puts text into the document – rst_prolog, .. include::, .. list2need:: – shifts that count. Content that exists in no file (a rendered pre_template/post_template) is parsed at an offset into that space, so the unresolved number has to survive alongside the resolved one.

Deliberately not part of dict_repr: it is a parser coordinate rather than need data, and never reaches needs.json. None means the two are the same, or the need has no directive.

property placement: NeedSourceHop | None

The hop of the page the need is placed on: the last hop with a document.

Provisional, as hops is.

upstream: tuple[NeedSourceHop, ...]

The hops the need’s text came through before this build read it, innermost first: those a needs.json of the experimental format 2a1 records for it, read by needimport or needs_external_needs, each with the origin of the project it belongs to (provisional). They come before hops, which are this build’s; no legacy key and no c.source facet reads them.

class NeedItemSourceProtocol(*args, **kwargs)

Protocol for need item source types.

property dict_repr: NeedsSourceInfoType

Return a dictionary representation of the source.

A class representing a link from one need to another.

Added in version 8.0.0.

static from_string(link_str: str, *, parse_conditions: bool = True) → NeedLink

Parse a link from a string (infallible, best-effort).

Supports formats: ID, ID.part, ID[condition], ID.part[condition], ID[[nested_condition]].

The condition opens with the run of [ after the address, and runs to the same number of ] that end the string, so it may itself hold and end in ]: ID[x == a[0]] and ID[[x == a[0]]] are the condition x == a[0]. A condition that starts with [ and ends with ] is written with one space after the opening brackets, which is dropped (ID[ [1] == a[:1]] is [1] == a[:1]); one that starts with [ and does not end with ] needs nothing (ID[[1] == y] is [1] == y). An empty condition is no condition.

On malformed brackets (unclosed, trailing text), falls back to parsing without a condition. Use from_string_with_warnings() if you need to detect malformed input.

Parameters:

parse_conditions – Whether to parse [condition] brackets.

static from_string_with_warnings(link_str: str, *, parse_conditions: bool = True) → tuple[NeedLink, list[str]]

Parse a link from a string, returning warnings for malformed input.

Same parsing as from_string(), but returns a list of warning messages instead of silently ignoring malformed brackets.

Parameters:

parse_conditions – Whether to parse [condition] brackets.

Returns:

A tuple of (NeedLink, warnings).

static parse_address(address: str, /, *, condition: str | None = None) → NeedLink

Parse an address string into a NeedLink, optionally with a condition.

to_filter_string() → str

Convert the link to a filter string, e.g. ‘NEED-1’ or ‘NEED-1.part’.

This does not include the condition.

Serialize the link including the condition, e.g. ‘NEED-1[cond]’ or ‘NEED-1.part[cond]’.

Uses bracket depth one greater than the longest consecutive run of ] in the condition, and one space after the opening brackets when the condition starts with [ (after any spaces) and ends with ], so the result reads back through from_string() as the same link. An empty condition is written ID[], which reads as no condition.

class NeedModification(*, docname: str | None = None, lineno: int | None = None)

A class representing a modification to a need item, by a needextend directive.

class NeedPartData(*, id: str, content: str, backlinks: dict[str, list[~sphinx_needs.need_item.NeedLink]]=<factory>)

A class representing a part of a need, which is generally derived from :np: role within the parent need.

class NeedPartItem(need: NeedItem, part_id: str)

A class representing a part of a need, which is a sub-need, merged with the parent need.

Any data coming from the part will override the data from the parent need.

Note this is a read-only view into a need part. It does not allow modification of the underlying data.

copy() → NeedPartItem

Return a copy of the NeedPartItem.

copy_for_needtable() → NeedPartItem

Return a copy of the NeedPartItem with specific fields for needtable.

filter_context() → dict[str, Any]

Build a flat dict of all need part fields, suitable for use as an eval/template context.

This is more efficient than {**need_part} because it avoids per-key lookup overhead.

get(key: str, default: Any = None) → Any

Get an item by key with a default value.

get_backlinks(link_type: str, *, as_str: Literal[False]) → list[NeedLink]

Get backlink references by link_type key.

Raises:

KeyError – If the link_type is not a backlink type.

get_extra(key: str) → str

Get an extra by key.

Raises:

KeyError – If the key is not an extra.

get_links(link_type: str, *, as_str: Literal[False]) → list[NeedLink]

Get link references by link_type key.

Raises:

KeyError – If the link_type is not a link type.

is_in_document(docname: str) → bool

Check if the need is in a given document.

items() → Iterable[tuple[str, Any]]

Return the items of the need item.

iter_backlinks_items(as_str: Literal[False] = False) → Iterable[tuple[str, list[NeedLink]]]

Yield all backlinks as (link_type, references) pairs.

iter_extra_items() → Iterable[tuple[str, str]]

Yield all extras as key-value pairs.

iter_extra_keys() → Iterable[str]

Yield all extra keys.

iter_links_items(as_str: Literal[False] = False) → Iterable[tuple[str, list[NeedLink]]]

Yield all links as (link_type, references) pairs.

Yield all link_type keys.

keys() → Iterable[str]

Return the keys of the need item.

property part_id: str

Return the part ID.

values() → Iterable[Any]

Return the values of the need item.

class NeedsContent(*, doctype: str, content: str, pre_content: str | None = None, post_content: str | None = None, jinja_content: bool = False, template: str | None = None, pre_template: str | None = None, post_template: str | None = None)

A class representing the content of a need item.

Note

Stable: NeedItemSource’s name, NeedItemSource.from_directive and NeedItemSource.on_page, and reading a source through NeedItemSourceProtocol (dict_repr). Provisional: NeedItemSource’s general constructor and its keywords (hops, lineno, lineno_content, parser_lineno, is_import, upstream), the stored legacy facts a later release turns into projections, for sphinx-needs’ own producers and advanced use; and the hops, below. upstream holds the hops a need read from a needs.json of the draft format "2a1" was recorded with in the project that wrote it (needs.json format 2a1 (draft)); no legacy key and no c.source facet reads them. Provisional means additions only through 9.x: a minor release may add an optional field, a hop kind or a keyword, and a rename, a removal, or a change to an existing one’s meaning or type waits for 10.0, because published sibling extensions construct them (What is public).

Provisional: a NeedItemSource has hops, where the need’s text came from, innermost first; the hop types, NeedSourceHop, NeedSourcePhysical and NeedSourceSemantic, are importable from sphinx_needs.api. The first hop is the innermost place sphinx-needs knows the need came through: for a need directive or list2need, the file the need is written in (an included file’s own, in an include; the module, for an autodoc docstring); for an imported need, the needs.json it was read from (where the exporting project wrote the need is not a hop yet); for a service, the service that made it, then the file of the needservice directive; for an external need, its page on the other site, its only hop, with the need’s sections there as its semantic location, outermost first (the legacy sections keeps them innermost first). In a nested reStructuredText include, the included file’s hop is followed by one file hop per file it was included through, innermost first, each with its path and suffix and no line, as docutils’ include_log names them. Two known gaps: myst-parser’s {include} is never logged, so a chain through MyST includes records its reStructuredText links alone (a reStructuredText include in eval-rst is logged, a MyST include around it is not), and under docutils 0.22 and later a file included with :parser: is not logged (it is parsed into a document of its own), so it is no link of a chain through it, and a need written in that file itself records no chain at all (docutils 0.21 logs it). A need created through add_need or NeedItemSource.on_page has its page alone, whose file create_need records with no line (the line the caller gives is not checked in the file); generate_need has no application, so it records no page path; and no hops without a document (NeedItemSource.from_directive records the directive’s file and lines, as a need directive does). The last hop with a document is the page the need is placed on (an external need has none), and carries its sections and the domain objects it is written in, outermost first. A hop has a line range only where the file, read from disk, holds the need’s text there: in reStructuredText every line of the directive equals the file’s line (whole lines, after a block quote’s or a table cell’s border, and the bullets of the list item the directive opens); in MyST the line opens the directive’s fence with its name and title. Elsewhere the hop has no line. A recorded line can still be wrong only where the file holds an identical copy of the need’s text at the line the parser claims: a MyST need nested at once inside an identical twin (same name, same title). In reStructuredText, a need in an included slice (:start-after:, :start-line:) records the line it is written on in the file, as its lineno and on its file hop, although docutils’ own messages about the same text (an unknown role in its content, say) name the line counted from the slice’s start. It keeps the slice’s line where the slice cannot be matched with the file (an include-read handler that rewrote it, a file decoded otherwise than with the project’s source_encoding, a :parser: include), and for a need in a table cell, in list2need item content, in content parsed in a declared markup, or generated by a test-report. Every path is POSIX and relative to its hop’s root: the source directory ("srcdir", with .. for a file outside it), or, where root is None, the producer’s own root, which this build does not know, the path as the producer gives it (a test report’s test file, relative to the test run’s root). A hop’s semantic is None where it is not determined, or where nothing finer than the file is known. Until the shape reaches needs.json, a 9.x minor release only adds to it, as above; NeedItemSourceProtocol does not include it, so read it with getattr(need.source, "hops", ()). A filter reads it through the c.source facets (Filtering by where a need comes from (c.source)), its stable view: each facet names a position in the hops (c.source.path the innermost, c.source.paths every one), and keeps its meaning when the hops change shape. Nothing a table or needs.json shows depends on it.

A hop’s language is what its text is written in, by the kind of hop: for a page hop, the page’s suffix its parser keys on (.rst, .md); for a file hop, the file’s own suffix (.rst, .json; an included inc.rst.txt records .txt, whichever parser reads it); for a code hop, the language’s name (python, cpp, …), as sphinx-codelinks records it, as the code hop of a docstring autodoc reads records it (python), and as sphinx-test-reports names a test case’s file by its suffix (none for a suffix it does not know).

A semantic entry’s kind is in the vocabulary of its hop’s kind and producer: a page hop’s entries use the document’s (section, <domain>:<objtype>), an external hop’s are the section entries of the other site’s page, a generated hop’s name its generator (service), and a code hop’s use its producer’s entity kinds, read with the hop’s language: sphinx-codelinks’ bare ones (function, method, class, impl, …). In a code hop the kinds may carry identity the names alone do not (Python’s <locals> boundary is implied by a function or method followed by another entry), so code hops compare by their producer’s identity rule, not entry by entry. The innermost entry of a code hop, the entity, carries its qualified name in its file as the producer renders it, a ("qualified_name", ...) extra: sphinx-codelinks’ analysis renders it by the language (ns::Widget::draw, <Widget as Shape>::area, Shape.area.<locals>.inner), and autodoc’s docstring hop renders a Python entity as sphinx-codelinks does (make.<locals>.Made.go), except a name CPython NFKC-normalises (file reads file in autodoc’s hop and as written in sphinx-codelinks’). It is the producer’s rendering of the entries’ names, so it adds no identity, and identity comparisons (sphinx-codelinks’ name_chain) leave it out. The entity’s own line range is not recorded: it is not identity.

Autodoc’s docstring hop uses the same Python kinds as sphinx-codelinks, read from the syntax of the file the docstring is defined in: class, method for a def directly in a class, function for any other def, with <locals> implied, so a method of a class made in a function is function make > class Made > method go. A property, a staticmethod or a classmethod is a method, an exception a class: the page hop keeps autodoc’s object type (py:property). An attribute documented with #: is a leaf attribute (class Shape > attribute sides), where sphinx-codelinks attaches the same comment to the enclosing class, or to the file. Autodoc names a docstring by its object’s module and qualified name; the name is tried at each module it starts with, longest first, and the definition it resolves to is trusted only where its docstring holds every line of the need. The file is then that definition’s, so a member inherited from another module records that module’s file. A module docstring records its file alone. Where no definition holds the text, the hop records the file autodoc gives and nothing finer, never another entity: an alias (autodoc names it by the object it aliases), an override that inherits its docstring, an object made at run time, a function with no Python source. A function whose __module__ is rewritten to another module records that module’s file, which does not hold the text either: a known gap.

class NeedSourceHop(*, kind: str, origin: str | None = None, physical: NeedSourcePhysical | None = None, semantic: tuple[NeedSourceSemantic, ...] | None = None, document: str | None = None, language: str | None = None)

One place a need’s text passed through.

Text in an included file has a file hop of that file, then one per file it was included through, innermost first, each with no line, read from docutils’ document.include_log, then the page. Two known gaps: myst-parser’s {include} is never logged, so a chain through MyST includes records its reStructuredText links alone (an include in a MyST page’s eval-rst block is one); and under docutils 0.22 and later a file included with :parser: is not logged (it is parsed as a document of its own), so it is no link of a chain through it, and a need written in that file itself records no chain at all (docutils 0.21 logs it).

Provisional until the final needs.json format version 2 carries it (the draft "2a1" does not end that): additions only through 9.x (new optional fields; a rename, a removal or a change of an existing field’s meaning or type waits for 10.0), because published sibling extensions construct it. See What is public.

document: str | None

The docname, on the hop of a page the need is placed on.

kind: str

"page", "file", "code", "external", "generated", "report" (a test report, which sphinx-test-reports records); the vocabulary is open: a minor release may add a kind.

language: str | None

What the hop’s text is written in, by the kind of hop: for a page hop, the page’s suffix its parser keys on (".rst", ".md"); for a file hop, the file’s own suffix (".rst", ".json"; an included inc.rst.txt records ".txt", whichever parser reads it); for a code hop, the language’s name ("python", "cpp", …), as sphinx-codelinks records it, as the code hop of a docstring autodoc reads records it ("python"), and as sphinx-test-reports names a test case’s file by its suffix (None for a suffix it does not know).

origin: str | None

The project the hop belongs to: None for a hop of this build; on a hop read from a needs.json of the experimental format 2a1 (a need’s upstream), the name of the project that wrote it, as the file gives it – or, where the file names no project, the file as the reader names it (its path relative to the source directory, a needimport’s file or an external source’s json_path alike, or its URL as written). A key into a registry of origins (a repository, a test run) later.

semantic: tuple[NeedSourceSemantic, ...] | None

Outermost to innermost; None where it is not determined (the placement’s, until the doctree has been read; an external need’s whose record gives no sections), or where nothing finer than the file is known (a file hop, a whole report).

class NeedSourcePhysical(*, root: str | None = None, path: str | None = None, url: str | None = None, start_line: int | None = None, end_line: int | None = None)

Where a hop’s text physically is: a file under a named root, or a URL.

Provisional until the final needs.json format version 2 carries it (the draft "2a1" does not end that): additions only through 9.x (new optional fields; a rename, a removal or a change of an existing field’s meaning or type waits for 10.0), because published sibling extensions construct it. See What is public.

end_line: int | None

1-based and inclusive; recorded only where it is a true line of the file.

path: str | None

POSIX, relative to root, with .. for a file outside it; under a None root, relative to the producer’s own root, as the producer gives it (still POSIX).

root: str | None

The key of the root path is relative to: "srcdir", the Sphinx source directory; None, the producer’s own root, which this build does not know (the test run’s, for a test report’s test file).

start_line: int | None

1-based; recorded only where it is a true line of the file.

url: str | None

For a file that is not on this machine: a downloaded needs.json, the page of an external need.

class NeedSourceSemantic(*, kind: str, name: str, extras: tuple[tuple[str, str], ...] = ())

One entry of a hop’s semantic location: a section, a domain object, a service.

Provisional until the final needs.json format version 2 carries it (the draft "2a1" does not end that): additions only through 9.x (new optional fields; a rename, a removal or a change of an existing field’s meaning or type waits for 10.0), because published sibling extensions construct it. See What is public.

extras: tuple[tuple[str, str], ...]

Identity-bearing extras, e.g. ("id", <the first signature id>), with one exception. The innermost entry of a code hop, the entity itself, carries its qualified name in its file as its producer renders it, ("qualified_name", ...): sphinx-codelinks’ analysis (ns::Widget::draw, <Widget as Shape>::area, Shape.area.<locals>.inner), and for autodoc’s docstring hop the same rendering of a Python entity (make.<locals>.Made.go; Shape.sides for an attribute; a name CPython NFKC-normalises reads normalised there, as written in sphinx-codelinks’). It renders the entries’ names, so it adds no identity, and identity comparisons leave it out (sphinx-codelinks’ name_chain does). The entity’s own line range is not an extra: it is not identity.

kind: str

What the entry is, in the vocabulary of its hop’s kind and producer. A page hop’s entries use the document’s: "section", "<domain>:<objtype>" ("py:method"). An external hop’s are the "section" entries of the need’s page on the other site, as its record gives them. A generated hop’s name its generator ("service"). A code hop’s use its producer’s entity kinds, read together with the hop’s language: sphinx-codelinks’ bare ones ("function", "method", "class", "impl", …). Autodoc’s docstring hop uses the same Python kinds as sphinx-codelinks, read from the file’s syntax, and "attribute" for an attribute (sphinx-codelinks attaches a #: comment to the enclosing class or file instead). The kind describes the entry, the name identifies it; but in a code hop the kinds may carry identity the names alone do not (Python’s <locals> boundary is implied by a function or method followed by another entry), so code hops compare by their producer’s identity rule, not by each entry’s (name, extras).

Module to control access to sphinx-needs data, which is stored in the Sphinx environment.

class NeedsInfoComputedType

Data for a single need, that can be computed from existing data.

These fields are used for convenience in filters.

constraints_error: str | None

An error message set if any constraint failed, and error_message field is set in config.

constraints_passed: bool | None

True if all constraints passed, False if any failed, None if not yet checked.

constraints_results: Mapping[str, Mapping[str, bool]] | None

Mapping of constraint name, to check name, to result, None if not yet checked.

id_complete: str

<parent ID>.<self ID>, or <self ID> if not a part.

id_parent: str

<parent ID>, or <self ID> if not a part.

is_modified: bool

Whether the need was modified by needextend.

modifications: int

Number of modifications by needextend.

parent_need: str | None

Simply the first parent id.

parts: Mapping[str, NeedsPartType]

Mapping of parts, a.k.a. sub-needs, IDs to data that overrides the need’s data

section_name: str | None

Simply the first section.

class NeedsInfoType

Data for a single need.

arch: Mapping[str, str]

Mapping of uml key to uml content.

collapse: bool

Hide the meta-data information of the need.

constraints: list[str]

List of constraint names, which are defined for this need.

external_css: str

CSS class name, added to the external reference.

True if any links reference need ids that are not found in the need list.

True if any links reference need ids that are not found in the need list, and the link type does not allow dead links.

hide: bool

If true, the need is not rendered.

id: str

ID of the data.

layout: str | None

Key of the layout, which is used to render the need.

signature: str | None

Derived from a docutils desc_name node.

style: str | None

Comma-separated list of CSS classes (all appended by needs_style_).

title: str

Title of the need.

type: str

Type of the need.

type_color: str

Hexadecimal color code of the type.

class NeedsMutable

A mutable view of the needs, before resolution

alias of dict[str, NeedItem]

class NeedsPartType

Data for a single need part.

content: str

Content of the part.

id: str

ID of the part

class NeedsSourceInfoType

Data for the source of a single need.

docname: str | None

Name of the document where the need is defined (None if external).

external_url: str | None

URL of the need, if it is an external need.

is_external: bool

If true, no node is created and need is referencing external url.

is_import: bool

If true, the need was derived from an import.

lineno: int | None

Line number where the need is defined (None if external).

lineno_content: int | None

Line number on which the need content starts (None if external).

Views

These views are returned by certain functions, and injected into filters, but should not be instantiated directly.

class NeedsAndPartsListView(*, _indexes: _LazyIndexes, _selected_ids: dict[tuple[str, str | None], None] | None)

A read-only view of needs and parts, after resolution (e.g. back links have been computed etc)

The parts are created by creating a copy of the need for each item in parts, and then overwriting a subset of fields with the values from the part.

__iter__() → Iterator[NeedItem | NeedPartItem]

Iterate over the needs and parts in the view.

__len__() → int
filter_has_tag(values: list[str]) → NeedsAndPartsListView

Create new view with only needs/parts that have at least one of these values in the tags field list.

filter_id_complete(values: Iterable[str]) → NeedsAndPartsListView

Create new view with only the needs/parts whose id_complete is in values.

This selection is exact, which filter_ids is not: a part is selected on its own, without its need and without its sibling parts, and a need is selected without its parts. Values that are not in the view are ignored.

filter_ids(values: Iterable[str]) → NeedsAndPartsListView

Create new view with needs/parts filtered by the id field.

filter_is_external(value: bool) → NeedsAndPartsListView

Create new view with only needs/parts where is_external field is true/false.

filter_statuses(values: list[str] | list[str | None]) → NeedsAndPartsListView

Create new view with only needs/parts that have certain status field values.

Parameters:

values – Statuses to filter by; None selects the needs/parts without one.

filter_types(values: list[str], or_type_names: bool = False) → NeedsAndPartsListView

Create new view with only needs/parts that have certain type field values.

Parameters:
  • values – List of types to filter by.

  • or_type_names – If True, filter by both type and type_name field

get_need(id: str, part_id: str | None = None) → NeedItem | NeedPartItem | None

Get a need by id, or return None if it does not exist.

If part_id is provided, return the part of the need with that id, or None if it does not exist.

class NeedsView(*, _indexes: _LazyIndexes, _selected_ids: dict[str, None] | None)

A read-only view of needs, mapping need ids to need data, with “fast” filtering methods.

The needs are read-only and fully resolved (e.g. dynamic values and back links have been computed etc)

__getitem__(key: str) → NeedItem
__iter__() → Iterator[str]
__len__() → int
filter_has_tag(values: list[str]) → NeedsView

Create new view with only needs that have at least one of these values in the tags field list.

filter_ids(values: Iterable[str]) → NeedsView

Create new view with needs filtered by the id field.

filter_is_external(value: bool) → NeedsView

Create new view with only needs where is_external field is true/false.

filter_statuses(values: list[str] | list[str | None]) → NeedsView

Create new view with only needs that have certain status field values.

Parameters:

values – Statuses to filter by; None selects the needs without one.

filter_types(values: list[str], or_type_names: bool = False) → NeedsView

Create new view with only needs that have certain type field values.

Parameters:
  • values – List of types to filter by.

  • or_type_names – If True, filter by both type and type_name field

to_list_with_parts() → NeedsAndPartsListView

Create a new view with needs and parts.

Schema

AllowedTypes: TypeAlias = str | bool | int | float | list[str] | list[bool] | list[int] | list[float]

Type alias for all allowed types in need fields.

This includes scalar types (str, bool, int, float) and their corresponding list types.

class BackLinksSchema(*, name: str, link_type: str, description: str = '', schema: Mapping[str, int], severity: Literal['violation', 'warning', 'info'] | None = None)

A need type’s declaration of the back links of a link type, <l>_back: how many links of that link type point to a need of the type (a [needs.types.<t>.back_links.<l>] entry).

The count is the length of the need’s <l>_back: every link of the link type to it, whatever the type of the need it comes from. It is checked by the schema validation, once every link is final, on the needs of the declaring type and of the types inheriting from it, but those from needs_external_needs: the links of such a need’s own project to it are not loaded here, so its count would be partial. A link to a part of the need counts for the need, a part it lacks too (a dead link where it is written). A need that fails it is a link_fail of the field <l>_back at the severity the declaration gives (violation when it gives none).

property failure_severity: Literal['violation', 'warning', 'info']

The severity a need failing the count is reported at.

json_schema() → dict[str, Any]

The schema the back links of a need of the type are validated against.

The link type whose links are counted.

name: str

The back links’ name, <link_type>_back.

schema: Mapping[str, int]

The minItems and maxItems given, each a non-negative integer.

severity: Literal['violation', 'warning', 'info'] | None

The severity a need failing the count is reported at; None: violation.

class FieldFunctionArray(value: 'tuple[DynamicFunctionParsed | VariantFunctionParsed | VariantDataParsed | str | bool | int | float, ...]')
class FieldLiteralValue(value: 'AllowedTypes')
class FieldSchema(*, name: str, description: str = '', schema: FieldStringSchemaType | FieldBooleanSchemaType | FieldIntegerSchemaType | FieldNumberSchemaType | FieldMultiValueSchemaType, nullable: bool = False, directive_option: bool = False, parse_dynamic_functions: bool = False, parse_variants: bool = False, allow_defaults: bool = False, allow_extend: bool = False, predicate_defaults: tuple[tuple[str, FieldLiteralValue | FieldFunctionArray], ...] = (), default: FieldLiteralValue | FieldFunctionArray | None = None, derive: DeriveRule | DeriveInvalid | None = None, derive_by_type: Mapping[str, DeriveRule | DeriveInvalid] | None = None, severity: Literal['violation', 'warning', 'info'] | None = None)

Schema for a single field.

Raises:

ValueError – if any of the parameters are invalid

property admits_null: bool

Whether a need of this declaration may hold None: it is nullable, or not nullable at a severity below violation (severity), which a need without a value fails without being refused. A derived field holds its empty value, not None, where it is not nullable.

convert_directive_option(value: str) → FieldLiteralValue | FieldFunctionArray

Convert a string to the correct type for this field.

Raises:
convert_or_type_check(value: Any, *, allow_coercion: bool) → FieldLiteralValue | FieldFunctionArray | None

Convert a value to the correct type for this field, or check if it is of the correct type.

Parameters:
  • value – The value to convert or check.

  • allow_coercion – Whether to allow coercion of string values to the correct type. This will also allow dynamic and variant functions if the field allows them.

Returns:

True if the value is of the correct type, False otherwise.

Raises:
default: FieldLiteralValue | FieldFunctionArray | None

The default value for this field.

Used if the field has not been specifically set, and no predicate matches.

derive: DeriveRule | DeriveInvalid | None

The derive rule computing this field, if it is derived.

A derived field cannot be set in a need (directive_option is False) or by a needextend (allow_extend is False), and takes no default. A DeriveInvalid marks a rule that cannot be read: the field is closed all the same, and holds its empty value.

derive_by_type: Mapping[str, DeriveRule | DeriveInvalid] | None

The rules per need type (derive_by_type): need type to rule, each overriding derive on its type, if any.

The field is derived (closed) on every need type. A need of a type without a rule holds off_type_value(): with derive_by_type and no derive, the field may carry a literal default for it (though it allows no other defaults). An empty mapping is a field derived on no need type (its derive_by_type could not be read).

property derived: bool

Whether the field is derived: it carries derive, or derive_by_type.

A derived field is closed on every need type, also on a type without a rule.

describe_derived() → str

The rules of a derived field, as messages name them: kind 'sum'.

property failure_severity: Literal['violation', 'warning', 'info']

The severity a need failing this declaration is reported at: its severity, else violation.

json_schema() → dict[str, Any]

Return a JSON schema representation of this field.

property kind: Literal['field']

What it declares: a field (a core field or an extra field).

off_type_value() → Any

The value a need holds when its type has no rule (see rule_for()).

The literal default, in the field’s type (an integer in a number field is a float); else None when the field is nullable; else the empty value of its type.

predicate_defaults: tuple[tuple[str, FieldLiteralValue | FieldFunctionArray], ...]

List of (need filter, value) pairs for default predicate values.

Used if the field has not been specifically set.

The value from the first matching filter will be used, if any.

rule_for(need_type: str) → DeriveRule | DeriveInvalid | None

The rule computing the field on a need of need_type.

The type’s own derive_by_type entry, else derive, else None: a need of that type holds the field’s off_type_value().

severity: Literal['violation', 'warning', 'info'] | None

The severity a need failing this declaration is reported at – its schema keywords, and nullable = false – or None, not given: violation (failure_severity). A need type’s entry gives it; a type inheriting the declaration keeps it, and may raise it but not lower it.

Below violation, nullable = false is reported rather than enforced: a need without a value is created, holding None (admits_null), and the schema validation reports it.

type_check(value: Any) → bool

Check if a value is of the correct type for this field.

type_check_item(value: Any) → bool

Check if a value is of the correct item type for this field.

For ‘array’ field types, this checks against the item_type. For other fields, returns False.

class FieldShape(*args, **kwargs)

The shape of a field: what is the same on every need type.

Its name, its kind, its type and item_type, and its derivation (a field derived per need type says so in its derive_by_type). The fields schema answers a field’s shape (FieldsSchema.get_any_field() and the other getters); a field’s declaration in full, which a need type may declare anew, is read through FieldsSchema.for_type() (FieldSchema, LinkSchema).

class FieldsSchema

A schema for the fields of a single need.

A field’s declaration holds for every need type, unless the need type carries an overlay of it (add_type_overlay()), or the field is declared on need types alone and the type does not declare it: for_type() is the schema of the needs of one need type, its overlays in place of the declarations they replace.

The schema answers a field’s shape, the same on every need type (its name, kind, type and derivation); a need’s field is read through for_type() of its type, and configuration code reads a field’s declarations through global_declaration() and type_declarations(). Overlays are registered after every other change to the schema, and once it is complete it is frozen (freeze()): create_schema freezes the schema it builds, and the first for_type() one built by hand.

Declare how many links of a link type point to a need of one need type: the type’s declaration of the link type’s back links (BackLinksSchema).

It is read by back_links() of the schema of that need type (for_type()); a second declaration of the same back links for the same need type replaces the first. Registered for each type it reaches – the declaring type and every type inheriting from it – as an overlay is.

Raises:
  • ValueError – if the need type is not configured, or the schema has no link type whose back links they are

  • TypeError – if the schema is frozen, or the schema of one need type

add_core_field(field: FieldSchema) → None

Add a core field to the schema.

Raises:
  • ValueError – if a field with the same name already exists

  • TypeError – if the schema is frozen, or the schema of one need type

add_extra_field(field: FieldSchema, *, project: bool = False) → None

Add an extra field to the schema.

Parameters:

project – Whether the project declares it (needs_fields, needs_extra_options), rather than an extension or a service.

Raises:
  • ValueError – if a field with the same name already exists

  • TypeError – if the schema is frozen, or the schema of one need type

Add a link field to the schema.

Raises:
  • ValueError – if a field with the same name already exists

  • TypeError – if the schema is frozen, or the schema of one need type

add_type_overlay(need_type: str, field: FieldSchema | LinkSchema) → None

Declare a field anew for the needs of one need type.

The overlay is the field’s declaration in for_type() of that need type; a second overlay of the same field for the same need type replaces the first. The overlay is a narrowing of the field’s declaration (narrowing_problems()): of a field this schema has, of the same kind, type, item_type and derivation, admitting no value the field does not, opening nothing the field closes. Once the schema knows the configured need types (set_need_types()), the need type is one of them. Overlays are registered after every other change to the schema.

Raises:
  • ValueError – if the need type is not configured, the schema has no field of that name, or the overlay is not a narrowing of it (every reason, joined by "; ")

  • TypeError – if the schema is frozen, or the schema of one need type

The declarations of back links of the need type this is the schema of (for_type()), by name, read-only: empty for the global view, and for the schema itself, which is the schema of no need type.

The link types that copy their links into links (copy = true, deprecated), as a need’s creation copies them: those with copy, but links itself and a derived one (whose copy is refused), and none where links is derived (nothing is copied into it).

Read on for_type()’s schema, they are those a need of that type copies; on the global view, they are why links takes no target_types: it holds links written in it and links copied into it.

declares_default(name: str) → bool

Whether a declaration of the field name carries a default or a predicate default: its global declaration, or any need type’s.

Before a need is created it is unknown which declaration, and which predicate, will apply: this is the over-approximation for “the field may be given a default”. Reading it does not freeze the schema.

declaring_types(name: str) → frozenset[str] | None

The need types declaring name, a field or link type declared on need types alone – by their entries (Fields per need type), or by an extension’s add_field(..., types=...) – or None for one every need type has (see _localise).

property derived_names: frozenset[str]

The names of the derived extra fields and link types: each one carrying derive or derive_by_type.

Derivation is a property of the field, the same on every need type (an overlay keeps it, add_type_overlay(), and a field declared on need types alone is derived on the types that do not declare it too), so the set is one for the schema: the schema of each need type (for_type()) answers the schema’s own set, the same object. A need refuses a write to any of these names (NeedItem). It is computed once the schema is frozen (freeze()), and at each read before.

for_type(need_type: str | None) → TypeFieldsSchema

The schema of the needs of need_type, every field in full.

With no need type, or a need type without overlays that declares every field declared on need types alone, it is the global view: the fields as every need type shares them, one instance. Else it is a schema whose fields are this one’s, in the same order, with the need type’s overlays (add_type_overlay()) in place of the fields they replace, and each field declared on need types alone (by their needs_types entries) that the type does not declare absent. A need type the schema does not know reads the global view: an unknown type is reported where a need is created. Neither can be changed, and the first call freezes a schema not frozen yet (freeze()). The schema of one need type answers as the schema it is resolved from.

freeze() → None

Stop the schema from changing: every later change raises TypeError.

The schema of each need type with overlays, of each that does not declare a field declared on need types alone, and the global view, are resolved now; for_type() then answers with one of them, so what a need was created with is what every later read of its type answers (a schema never told its need types resolves the view of a type lacking such a field at its first read). create_schema freezes the schema it builds, as its last step; the first for_type() freezes a schema built by hand. Freezing again does nothing.

property frozen: bool

Whether the schema can no longer be changed (freeze()).

get_any_field(name: str) → FieldShape | LinkShape | None

Get a field by name: its shape (for_type() reads it in full).

get_core_field(name: str) → FieldShape | None

Get a core field by name: its shape (for_type() reads it in full).

get_extra_field(name: str) → FieldShape | None

Get an extra field by name: its shape (for_type() reads it in full).

Get a link field by name: its shape (for_type() reads it in full).

global_declaration(name: str) → FieldSchema | LinkSchema | None

The global declaration of the field name, in full, None if none.

The declaration every need type shares unless it declares the field anew (type_declarations()): for configuration code and audits, which read a field before any need, or across needs of every type. A need reads its field through for_type() of its type. Reading it does not freeze the schema.

id_patterns(need_type: str) → tuple[tuple[str, str], ...]

The (declaring need type, id_regex) pairs the id of a need of need_type matches (set_id_patterns()), the root’s first; empty where neither the type nor an ancestor gives one.

is_project_field(name: str) → bool

Whether the extra field name is one the project declares.

iter_all_fields() → Iterable[FieldShape | LinkShape]

Iterate over all fields in the schema.

iter_core_field_names() → Iterable[str]

Iterate over all core field names in the schema.

iter_core_fields() → Iterable[FieldShape]

Iterate over all core fields in the schema.

iter_extra_field_names() → Iterable[str]

Iterate over all extra field names in the schema.

iter_extra_fields() → Iterable[FieldShape]

Iterate over all extra fields in the schema.

Iterate over all link field names in the schema.

Iterate over all link fields in the schema.

property need_types: frozenset[str] | None

The configured need types (set_need_types()), None if not told.

replace_field(field: FieldSchema | LinkSchema) → None

Replace the core field, extra field or link field of the same name.

The field keeps its place in the iteration order, and the type, item type and derivation of the field a need type declares anew (add_type_overlay()); each such declaration stays a narrowing of the replacement (narrowing_problems()).

Raises:
  • ValueError – if no field of that name and kind exists, or a need type declares it with another type, item_type or derivation, or not as a narrowing of the replacement

  • TypeError – if the schema is frozen, or the schema of one need type

set_id_patterns(need_type: str, patterns: Iterable[tuple[str, str]]) → None

Set the id_regex patterns the id of a need of need_type matches, beside needs_id_regex: each a (declaring need type, pattern) pair, the type’s own and those of the types it inherits from. A second call replaces the first.

Raises:
  • ValueError – if the need type is not configured

  • TypeError – if the schema is frozen, or the schema of one need type

set_need_types(names: Iterable[str]) → None

Tell the schema the configured need types (their directive names).

Once told, an overlay names one of them (add_type_overlay()); a read for any need type stays allowed (for_type()). It is told once.

Raises:
  • ValueError – if it was told already, or a need type with overlays is not among them

  • TypeError – if the schema is frozen, or the schema of one need type

type_declarations(name: str) → Mapping[str, FieldSchema | LinkSchema]

The need types that declare the field name anew, each with its declaration (add_type_overlay()), read-only.

In the order the need types were first given an overlay; empty when no need type declares the field anew. Reading it does not freeze the schema.

class LinkDisplayConfig(*, incoming: str, outgoing: str, color: str = '#000000', style: str = '', style_part: str = 'dotted', style_start: str = '-', style_end: str = '->')

Display/rendering configuration for a link type.

color: str

Color used for needflow diagrams.

incoming: str

Title for incoming links (e.g., ‘links incoming’).

outgoing: str

Title for outgoing links (e.g., ‘links outgoing’).

style: str

Line style used for needflow diagrams.

style_end: str

Arrow end style for needflow diagrams.

style_part: str

Line style used for need parts in needflow diagrams.

style_start: str

Arrow start style for needflow diagrams.

class LinkSchema(*, name: str, description: str = '', schema: LinkSchemaType, directive_option: bool = False, allow_extend: bool = False, parse_dynamic_functions: bool = False, parse_variants: bool = False, parse_conditions: bool = True, allow_defaults: bool = False, predicate_defaults: tuple[tuple[str, LinksLiteralValue | LinksFunctionArray], ...] = (), default: LinksLiteralValue | LinksFunctionArray | None = None, display: LinkDisplayConfig, copy: bool = False, allow_dead_links: bool = False, summary: bool = False, target_types: LinkTargetTypes | None = None, derive: DeriveRule | DeriveInvalid | None = None, derive_by_type: Mapping[str, DeriveRule | DeriveInvalid] | None = None, severity: Literal['violation', 'warning', 'info'] | None = None)

Schema for a single link field.

If True, add a ‘forbidden’ class to dead links instead of warning.

convert_directive_option(value: str) → LinksLiteralValue | LinksFunctionArray

Convert a string to the correct type for this field.

Raises:
convert_or_type_check(value: Any, *, allow_coercion: bool) → LinksLiteralValue | LinksFunctionArray

Convert a value to the correct type for this field, or check if it is of the correct type.

Parameters:
  • value – The value to convert or check.

  • allow_coercion – Whether to allow coercion of string values to the correct type. This will also allow dynamic and variant functions if the field allows them.

Returns:

True if the value is of the correct type, False otherwise.

Raises:
copy: bool

If True, copy links to the common ‘links’ field (deprecated, removed in 10.0: a union link type replaces it).

default: LinksLiteralValue | LinksFunctionArray | None

The default value for this field.

Used if the field has not been specifically set, and no predicate matches.

derive: DeriveRule | DeriveInvalid | None

The derive rule computing this link type, if it is derived.

A derived link type cannot be set in a need or by a needextend, and takes no default; a DeriveInvalid marks a rule that cannot be read, and the link type holds no links.

derive_by_type: Mapping[str, DeriveRule | DeriveInvalid] | None

The rules per need type (derive_by_type): need type to rule, each overriding derive on its type, if any.

The link type is derived (closed) on every need type. A need of a type without a rule holds off_type_value(): the literal default, which only derive_by_type without derive may carry, else no links.

property derived: bool

Whether the link type is derived: it carries derive, or derive_by_type.

A derived link type is closed on every need type, also on a type without a rule.

describe_derived() → str

The rules of a derived link type, as messages name them: kind 'links'.

display: LinkDisplayConfig

Display/rendering configuration for this link type.

property failure_severity: Literal['violation', 'warning', 'info']

The severity a need whose links fail this declaration is reported at: its severity, else violation.

json_schema() → dict[str, Any]

Return a JSON schema representation of this field.

property kind: Literal['link']

What it declares: a link type.

off_type_value() → list[NeedLink]

The links a need holds when its type has no rule (see rule_for()).

The literal default when derive_by_type stands without derive, else no links.

path_on(need: NeedItem) → DeriveRule | None

The copy or collect rule that computed this link type on need, if one did (path).

Its links are read from other needs’ links, so, as for a union, it reports no dead link of its own, nor does a dead link of it make one forbidden: the link type it collects does, where the link is written. A need from needimport or needs_external_needs keeps the links its file gives: there the link type is checked as any other.

predicate_defaults: tuple[tuple[str, LinksLiteralValue | LinksFunctionArray], ...]

List of (need filter, value) pairs for default predicate values.

Used if the field has not been specifically set.

The value from the first matching filter will be used, if any.

rule_for(need_type: str) → DeriveRule | DeriveInvalid | None

The rule computing the link type on a need of need_type.

The type’s own derive_by_type entry, else derive, else None: a need of that type holds the link type’s off_type_value().

severity: Literal['violation', 'warning', 'info'] | None

The severity a need whose links fail this declaration’s schema is reported at, or None, not given: violation (failure_severity). As FieldSchema.severity.

summary: bool

Whether the link type is a summary (summary = true, or a copy or collect rule computes it): its links materialise a path, not an edge, so it is read where it is named, and left out of every view reading all link types (a needflow without link_types). Like display, the same on every need type.

target_types: LinkTargetTypes | None

The need types the links may point to (target_types); None: any.

Unlike display, copy and allow_dead_links it is no part of the link type’s shape: a need type may narrow it (narrowing_problems()), so it is read from the declaration for a need’s type (FieldsSchema.for_type()). A derived link type takes none.

type_check(value: Any) → bool

Check if a value is of the correct type for this field.

type_check_item(value: Any) → bool

Check if a value is of the correct item type for this field.

For ‘array’ fields, this checks the type of the array items. For other fields, this checks the type of the field itself.

union_on(need: NeedItem) → DeriveRule | None

The union rule that computed this link type on need, if one did.

There its links are its members’ links, so it reports no dead link and no link condition of its own, nor does a dead link of it make one forbidden: the members do, as they allow dead links. A need from needimport or needs_external_needs keeps the links its file gives, which no rule computed: there the link type is checked as any other. Every check of a need’s links asks this.

class LinkShape(*args, **kwargs)

The shape of a link type: what is the same on every need type.

Besides a field’s shape, how its links are drawn (display), whether they are copied into links (copy), whether a dead link is allowed (allow_dead_links) and whether it is a summary (summary): every need type declares a link type with the same ones.

class LinkTargetTypes(*, names: tuple[str, ...], types: frozenset[str], declared_by: str | None = None)

The need types the links of a link type may point to: its target_types.

A link to a need of a listed type, or of a type inheriting from one, transitively, is accepted; the check is made where the links are resolved, on the outgoing links of the needs of the need type the declaration is for.

declared_by: str | None

The need type whose needs_types entry declares them, or None for a needs_links (or needs_extra_links) entry; a type inheriting the declaration keeps its ancestor’s, so a message can name where the set comes from.

names: tuple[str, ...]

The need types as declared, by directive name, each a need type of the project (the names that are not are reported and left out where it is declared).

types: frozenset[str]

The accepted need types: names and every type inheriting from one of them, expanded once, through the parents inherits leaves after its checks.

class LinksFunctionArray(value: 'tuple[NeedLink | DynamicFunctionParsed | VariantFunctionParsed | VariantDataParsed, ...]')
class LinksLiteralValue(value: 'list[NeedLink]')
class TypeFieldsSchema

The schema of the needs of one need type (FieldsSchema.for_type()).

Its fields are the full declarations its needs read: the need type’s overlays in place of the fields they replace, or, for the global view, every field as every need type shares it. Its getters answer them in full (FieldSchema, LinkSchema), where the schema it is resolved from answers their shapes. It cannot be changed.

declares(name: str) → bool

Whether the need type declares the field or link type name: every one but a field declared on need types alone (in their needs_types entries) that this type does not declare. The global view declares every one.

It answers True for any other name, a field or not, and a view made directly (not by FieldsSchema.for_type()) declares every name: ask it about the fields of the view.

get_any_field(name: str) → FieldSchema | LinkSchema | None

Get a field by name.

get_core_field(name: str) → FieldSchema | None

Get a core field by name.

get_extra_field(name: str) → FieldSchema | None

Get an extra field by name.

Get a link field by name.

iter_all_fields() → Iterable[FieldSchema | LinkSchema]

Iterate over all fields in the schema.

iter_core_fields() → Iterable[FieldSchema]

Iterate over all core fields in the schema.

iter_extra_fields() → Iterable[FieldSchema]

Iterate over all extra fields in the schema.

Iterate over all link fields in the schema.

narrowing_problems(base: FieldSchema | LinkSchema, overlay: FieldSchema | LinkSchema) → list[str]

Why overlay is not a narrowing of base: one reason per rule it breaks.

A need type’s declaration of a field (an overlay) is a narrowing of the field’s declaration when a need of that type is usable wherever a need of any type is (Liskov substitution). The rules, in this order:

  1. the same kind, a field or a link type (only this is reported when it differs);

  2. the same name, type and item_type;

  3. the same derive and derive_by_type;

  4. every schema keyword at least as strict (schema_narrowing_problems);

  5. no schema keyword of base left out;

  6. nullable only from true to false;

  7. allow_extend, allow_defaults and directive_option only from true to false;

  8. the same parse_variants and parse_dynamic_functions: a needextend converts its value once, for the needs of every type;

  9. a literal default, and each literal predicate default, valid against the schema of overlay, unless it is the base’s own literal, already invalid against the schema of base (that defect is the field’s, which validation reports on each need); a literal the declaration introduces must be valid against its schema; a dynamic function or a variant is not checked;

  10. for a link type, the same display, copy, allow_dead_links, summary and parse_conditions: a link type is drawn and resolved the same on every need type;

  11. for a link type whose base has target_types, target_types whose accepted types (a listed type and the types inheriting from it, the EXPANDED sets compared) are among those of base: a link a need of the type may hold is one a need of any type may. An overlay without target_types under a base with them drops them: defensive, since a declaration from the configuration that gives none (or names no need type) keeps the base’s;

  12. a severity no lower than the severity base gives (info below warning below violation): what a declaration enforces, a narrower one enforces too. Where base gives none it is free, an overlay giving none under a base giving one drops it (defensive, as for target_types).

The description is free. A reason of the rules 1 to 3 reads on from the name of the field (“Cannot declare field ‘x’ for need type ‘t’ …”), the others are sentences. Pure: it changes neither field, and does not raise.

Returns:

Every reason, in the order of the rules; empty for a narrowing.

Declared derived fields: the derive rule of a needs_fields or needs_links entry.

A field (or link type) whose entry carries a derive table is derived: its value is computed by the rule, for every need the project creates from its sources, and an author cannot set it – not in a need, not by a needextend. The table names a kind and the kind’s operands, each under a fixed role name:

[needs.fields.total]
schema = { type = "number" }
derive = { kind = "sum", field = "hours", over = "links" }

Beside it, or instead of it, a derive_by_type table gives a rule per need type, overriding derive on that type; a need of a type without a rule holds the field’s default (else None, or the empty value of its type). The field is derived, and closed, on every need type:

[needs.fields.total]
schema = { type = "number" }
default = 0
derive_by_type.epic = { kind = "sum", field = "hours", over = "links" }

This module reads the tables (parse_derive, parse_derive_by_type) and checks the rules against the declared fields and link types (check_derive_rules). A rule that cannot be read is kept as a DeriveInvalid marker rather than dropped, so the field stays closed to authors and holds its empty value, as the rule promised it would not be authored.

Nothing here touches Sphinx: the checks return the problems, and the caller reports them (as needs.derive_invalid).

class DeriveCall(rule: DeriveRule)

A derived field’s rule, as the value the pass computes for one need.

The pass gives every need from the project’s sources one per derived field, from the current schema, as the field’s one dynamic item: a structured value, ordered and computed like a [[…]] of the kind’s stratum.

Variables:

rule – The rule.

describe() → str

The rule as messages name it: derive rule 'sum'.

class DeriveInvalid(kind: str | None, reason: str, fallback: bool = False)

A derive rule that cannot be read.

The field (or link type) stays derived: an author cannot set it, nor can a needextend, and it holds its empty value.

Variables:
  • kind – The kind as written, if it is a string.

  • reason – Why the rule cannot be read.

  • fallback – Whether the marker stands, in derive_by_type, for the field’s derive on a need type where it cycles, rather than for an entry the configuration declares.

describe() → str

The rule as messages name it.

class DeriveJoin(field: str, equals: str)

The join role: the needs whose field equals one of this need’s equals.

Both are text fields: a string is split on ,, an array taken by item, each value stripped, and a need matches when the two share a value.

Variables:
  • field – The field read on every need, the candidates.

  • equals – The field read on the need holding the rule.

class DeriveRule(*, kind: Literal['copy', 'sum', 'count', 'min', 'max', 'any', 'all', 'collect', 'hash', 'fingerprint', 'links', 'content_links', 'union'], field: str | None = None, fields: tuple[str, ...] = (), exclude: tuple[str, ...] = (), over: str | None = None, from_need: str | None = None, join: DeriveJoin | None = None, where: str | None = None, test: str | None = None, empty: bool | None = None, select: Literal['first', 'unique', 'list'] = 'first', transitive: bool = False, include_self: bool = False, include_parts: bool = False, after_derived: bool = False, on_link: bool = False)

A derive rule that can be read: the kind and its roles.

Variables:
  • kind – The kind.

  • field – The field role: the field read on each candidate, or on this need.

  • fields – The fields role: the own fields hashed, in order (hash), or the link types whose links are united, in order (union).

  • exclude – The exclude role (fingerprint): what it leaves out.

  • over – The over role: a link type, or <link type>_back.

  • from_need – The from role: the id of the one need read.

  • join – The join role (links, copy): the needs whose field equals one of this need’s values.

  • where – The where role: the predicate narrowing the candidates (on copy, with over or join only).

  • test – The test role: the predicate asserted of each candidate.

  • empty – The empty role (any, all): the value over no candidate, once where has dropped the candidates it does not hold on; None, unset, is the kind’s own value there, False for any and True for all.

  • select – The select role of a copy over several targets.

  • transitive – The transitive role of min / max.

  • include_self – The include_self role.

  • include_parts – The include_parts role (links).

  • after_derived – The after = "derived" role.

  • on_link – Whether a copy or collect is declared on a link type: it computes a path (path), a link list.

describe() → str

The rule as messages name it: kind 'sum'.

property late: bool

Whether the rule runs in the late stage, after every other derivation.

Whether the rule computes a link list.

property path: bool

Whether the rule computes a path through other links: a copy or a collect on a link type, which makes the link type a summary.

Types and violations for schema validation.

There are 3 places the types are used: 1. In the field and links definition. 2. For the JSON schema coming from schema_definitions and schema_definitions_from_json configs. 3. For runtime type checking of the loaded JSON schema config.

All below types have the field ‘type’ set as required field. For case 1. this is important, as it is the primar type information source and users have to provide it. For 2. and 3. it is not required. If it is not given, it is injected before the type check occurs. If it is given, the types have to match what is provided in 1.

class FieldBooleanSchemaType

Boolean field schema.

A predefined set of truthy/falsy strings are accepted: - truthy = {“true”, “yes”, “y”, “on”, “1”} - falsy = {“false”, “no”, “n”, “off”, “0”}

For fixed values, the const field can be used. enum is not supported as const is functionally equivalent and less verbose.

const: NotRequired[bool]

A constant value that the integer must match.

type: Literal['boolean']

Field boolean type.

class FieldIntegerSchemaType

Integer field schema.

const: NotRequired[int]

A constant value that the integer must match.

enum: NotRequired[list[int]]

A list of allowed values.

exclusiveMaximum: NotRequired[int]

Exclusive maximum value.

exclusiveMinimum: NotRequired[int]

Exclusive minimum value.

maximum: NotRequired[int]

Maximum value.

minimum: NotRequired[int]

Minimum value.

multipleOf: NotRequired[int]

Restriction to multiple of a given integer, must be positive.

type: Literal['integer']

Field integer type.

class FieldMultiValueSchemaType

Multi value field such as a list of strings, integers, numbers, or booleans.

Current SN use case are tags.

contains: NotRequired[FieldStringSchemaType | FieldBooleanSchemaType | FieldIntegerSchemaType | FieldNumberSchemaType]

Schema constraints for some items.

maxContains: NotRequired[int]

Maximum number of contains items in the array.

maxItems: NotRequired[int]

Maximum number of items in the array.

minContains: NotRequired[int]

Minimum number of contains items in the array.

minItems: NotRequired[int]

Minimum number of items in the array.

type: Literal['array']

Multi value field such as a list of strings, integers, numbers, or booleans.

uniqueItems: NotRequired[bool]

Whether all items in the array must be unique.

class FieldNumberSchemaType

Float field schema.

Python reads in JSON numbers as floats. The jsonschema library handles precision issues with floats using a tolerance-based approach.

const: NotRequired[float]

A constant value that the number must match.

enum: NotRequired[list[float]]

A list of allowed values.

exclusiveMaximum: NotRequired[float]

Exclusive maximum value.

exclusiveMinimum: NotRequired[float]

Exclusive minimum value.

maximum: NotRequired[float]

Maximum value.

minimum: NotRequired[float]

Minimum value.

multipleOf: NotRequired[float]

Restriction to multiple of a given number, must be positive.

type: Literal['number']

Field integer type.

class FieldStringSchemaType

String field schema.

const: NotRequired[str]

A constant value that the string must match.

enum: NotRequired[list[str]]

A list of allowed values for the string.

format: NotRequired[Literal['date-time', 'date', 'time', 'duration', 'email', 'uri', 'uuid']]

A format string to validate against, e.g. ‘date-time’.

maxLength: NotRequired[int]

Maximum length of the string.

minLength: NotRequired[int]

Minimum length of the string.

pattern: NotRequired[str]

A regex pattern to validate against.

type: Literal['string']

Field string type.

class LinkItemSchemaType

Items in array of link string ids

maxLength: NotRequired[int]

Maximum string length of each outgoing link id.

minLength: NotRequired[int]

Minimum string length of each outgoing link id.

pattern: NotRequired[str]

A regex pattern to validate against.

type: Literal['string']

Link string type, can only be string and is injected automatically.

class LinkSchemaType

Defines a schema for unresolved need extra string links.

contains: NotRequired[LinkItemSchemaType]

Schema constraints that must be contained in the need id string list.

maxContains: NotRequired[int]

Maximum number of contains items in the array (outgoing link ids).

maxItems: NotRequired[int]

Maximum number of items in the array (outgoing link ids).

minContains: NotRequired[int]

Minimum number of contains items in the array (outgoing link ids).

minItems: NotRequired[int]

Minimum number of items in the array (outgoing link ids).

type: Literal['array']

Type for links, can only be array and is injected automatically.