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
nameis not given, the name to call the function is automatically taken from the provided function. A callable without a__name__, such as afunctools.partial, must be given itsname, orNeedsApiConfigExceptionis raised. The used name must be unique.readsdeclares what the function reads, for a function you do not own:add_dynamic_function(app, their_function, reads=reads(own="option"))(seereads()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
readsparameter.
- 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
kindand 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
derivetakes one, overridingderiveon that type. A need of a type without a rule holdsdefault(given only withoutderive), elseNone, 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 oneneeds.configwarning.None(the default): every type.
- Raises:
NeedsApiConfigException – If
typesis 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
deriveandderive_by_typeparameters.Changed in version 9.2.0: The
typesparameter.
- 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’
githublayout 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-initedhandler at a priority above 12, asadd_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
gridandlayout(and optionalstyle)
- 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
titleand aprefix, narrows the type instead, however late it is added: the keys the entry gives win, thetitle,prefix,colorandstyleit leaves out are the ones given here, and itsinherits,fieldsandlinksapply (see narrowing an extension’s type). The configured GitHub services’issue,prandcommitdo 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 withNeedsSphinxConfig(app.config).typesfor the whole entries: the configured types, then the added ones, then the configured GitHub services’ ones.Call it from a
config-initedhandler at a priority above 12, or later, asget_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_namein 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_typesentry naming the directive narrows the type, where one with atitleand aprefixraised 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 theissue,prandcommitof the GitHub services needs_services configures (each only where no earlier type has its directive).Call it from a
config-initedhandler at a priority above 12, or later: before that, a mapping-shapedneeds_typesis 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 atenv-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*argsparameter 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 attributeneeds_reads), and whichadd_dynamic_function()takes asreadsfor a function you do not own. A wrapper made withfunctools.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 afterapp,needandneedsis oneneeds.configwarning, 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;
@readswritten without parentheses is refused by Python itself, asreadstakes 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_typetakes forname, as declared, orNoneif 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,statusorhide, 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 declaredOwner,"owner"and"OWNER"both give"Owner".A match says the directive takes the option, not that
need_typekeeps 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 asneeds.derive_authored.When: once sphinx-needs has built this build’s schema, in its own
env-before-read-docshandler (priority 500): from anenv-before-read-docshandler connected at a priority above 500, or at 500 by an extension set up aftersphinx_needs, or at any later event, while documents are read included, up to sphinx-needs’ ownbuild-finishedhandler at priority 10000, which ends it. Before the schema is built, extensions andubproject.tomlmay 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_typeis 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 valueneeds_<name>as resolved, with needs_from_toml applied, and fortypes,layouts,functionsandwarningswith what extensions and the configured services add included. Read it from abuilder-initedhandler or later; inconfig-inited, from a handler at a priority above 600, once sphinx-needs has merged its defaults and compiled the card layouts (typesalone 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;functionsgives the dynamic functions by name, a mapping, whereneeds_functionsis a list. Reading only: setting a value through it is not supported. Its other members are internal, but one:- variant_data_proxy¶
The
varproxy that filters read over the variant data (needs_variant_data, merged with its file), orNonewhen there is none. Read-only.
- class DynamicFunction(*args, **kwargs)¶
A protocol for a sphinx-needs dynamic 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 toadd_dynamic_function()asreads, 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,
list2needandneedservice, sphinx-codelinks’ one-line needs and sphinx-test-reports’ needs are created;needimport,needs_external_needsand 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;
Noneonly for an external source, whose need has no nodes.source – Where the need comes from, a
NeedItemSource. It gives the need itsdocname,linenoand the other location fields, and says whether it is imported or external. In a directive it isNeedItemSource.from_directive(self), as above; for a need on the page at a line the caller gives (the directive’s parser line, asadd_needtook 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 theNeedItemSourceProtocolinterface, 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,hideortemplate(titleis an argument of its own), and the fields the project declares (needs_fields). A valueNoneis 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
stror aStringList(a string with mapping to the source text), parsed by the parser of the document the need is created in; or as aMarkupContent, 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 themarkupof aMarkupContentcontent, 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
MarkupContentwhosemarkupnames 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; (typeinvalid_value) for afull_titlethat is not a string, whether or not anidis given; (typeinvalid_kwargs) for a name infieldsorlinksthe project does not declare, or one given in the wrong place: a link type infields, a field inlinks, or one of this function’s own arguments (title,content, …) in either.TypeError – If
sourceis not aNeedItemSource, before anything is recorded.ValueError – If
stateisNonefor 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
needimportdirective, or an external source ofneeds_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 overexternal_url.Deprecated since version 9.2.0: Any other object (one with the
NeedItemSourceProtocolinterface alone) is oneDeprecationWarningat 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_needallows 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 aNeedItemSource(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 aDeprecationWarningat the calling line;add_needis removed in 10.0. See Migrating from add_need.kwargscan contain options defined inneeds_fieldsandneeds_links. If an entry is found inkwargs, which is not specified in the configuration or registered e.g. viaadd_field, an exception is raised.If
is_externalis set toTrue, 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_titlethat is not a string (typeinvalid_value); also, before the need is recorded, if aMarkupContent’smarkupnames 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_externalisTrueexternal_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
stror aStringList(a string with mapping to the source text), parsed by the parser of the document the need is created in; or as aMarkupContent, 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:
contentmay be aMarkupContent.doctype – The source suffix the need is recorded as written in (e.g.
".rst"). If not given, it is themarkupof aMarkupContentcontent, 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.partwarning per need naming them.constraints_passed – Contains bool describing if all constraints have passed
hide – If True then the need is not rendered.
Nonemeans 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.
Nonemeans 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
partsorarchfields 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_titlethat is not a string (typeinvalid_value).
kwargscan contain options defined inneeds_fieldsandneeds_links. If an entry is found inkwargs, which is not specified in the configuration or registered e.g. viaadd_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_externalisTrueexternal_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.
Nonemeans 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.
Nonemeans 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 overdocname,lineno,lineno_content,is_import,is_externalandexternal_url.Deprecated since version 9.2.0: Any other object (one with the
NeedItemSourceProtocolinterface alone) is oneDeprecationWarningat 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()(andadd_need()) assigns to a need that is given noid.The arguments are those of the same name given to
create_need(), which derives the id with this same code: the type’sprefix, followed by a hash offull_title(if given, elsetitle), or ofcontentwhen 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 callscreate_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
stror aStringList, ascreate_need()takes it.full_title – The untrimmed title, if
titlewas trimmed: a string.
- Returns:
The generated id.
- Raises:
InvalidNeedException – If
need_typeis not a configured need type (typeinvalid_type), orfull_titleis not a string (typeinvalid_value), ascreate_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_version1.This is the path for needs that come from outside the page they are rendered on; needimport creates each of its needs through it.
recordholds the keys a need object has in a needs.json offormat_version1 (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 legacydescriptionis taken as thecontentwhen the record has none (nocontentkey: sphinx-needs before 4.0 wrote the content asdescription); a record with acontent, even an empty one, keeps itsdescriptionas the field or link of that name. A caller that builds records setscontent, even"", for a need without one, so that adescriptionit 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 asdocname,linenoor the<link>_backnames) are dropped silently, and so is the legacyfull_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 ascreate_need():typeas itsneed_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 carriesis_importyet.Deprecated since version 9.2.0: Any other object (one with the
NeedItemSourceProtocolinterface alone) is oneDeprecationWarningat the calling line, and is refused from 10.0.content_markup – The source suffix of the markup the record’s
contentis written in: given, the content is created as aMarkupContentwith thismarkup, and the need records it as itsdoctypeif the record has none.content_source – The
sourceof thatMarkupContent:(path, first_line)of the content. Only meaningful withcontent_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; (typecontent) if the record’scontent(or the legacydescriptiontaken as it) is present but not a string; (typeinvalid_type) if itstypeis not a string; (typeinvalid_value) if itstitleis missing or not a string; (typeinvalid_id) if itsidis given but empty or not a string; (typeparts) if itspartsis not a mapping of mappings; and (typecontent_markup) ifcontent_sourceis given withoutcontent_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:
|
|
|---|---|
|
|
|
|
|
|
|
|
every link type |
|
|
the same keyword |
|
|
|
|
|
none: for a need on the page they are computed from its content and the page (a
record’s |
the deprecated |
none ( |
the return value, |
|
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()ascontent: the content is then parsed by the parser the project registers formarkup, whatever the parser of the document the need is created in, and the need recordsmarkupas itsdoctypeunless one is given. See Content written in another markup.- Parameters:
text – The content, as a
stror aStringList. 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’ssource_suffixmaps to a reStructuredText or MyST parser.create_needraisesInvalidNeedException(typecontent_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 namespathand the line each content line sits on (first_line + i). Sphinx prints node-based locations as absolute paths, so pass an absolutepath.Noneanchors the content in the document the need is created in, as a need directive’s content is: at the source’slineno_content, elselineno, read as lines of the PARSER’s input, as a directive’sself.content_offset + 1andself.linenogive them (they differ from the file’s lines afterrst_prologor aninclude, 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 ofrst_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-emptystr) orsource(not astrpath, non-empty, and anintline of at least 1) raisesValueErrororTypeErrorhere, where the object is made; whether the project registers the markup iscreate_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-tablewith:file:,rawwith:file:), and images and other references to files by a relative path: Sphinx’sinclude,literalincludeand images, and everything in MyST content, resolve against the page, whilecsv-table :file:andraw :file:in reStructuredText content resolve against the content’s file;raw, and raw HTML in Markdown;need or
needimportdirectives: 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-processingEmitted before the
needextenddirectives are applied and before dynamic functions, links and constraints are resolved. This is the hook for an extension that writes into the needs: aneedextendin a document is applied afterwards, and can override what the extension wrote.needs-before-sealingEmitted after those steps, and before the needs become read-only. A callback sees the needs as the
needextenddirectives 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¶
Data¶
- class NeedConstraintResults(constraints: Mapping[str, tuple[tuple[str, bool, str | None], ...]])¶
A class representing the results of constraints on a need item.
- 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_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.
- 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_backlinks(link_type: str, *, as_str: Literal[True] = True) list[str]¶
- 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[True] = True) list[str]¶
- 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.
- iter_backlinks_items(as_str: Literal[True] = True) Iterable[tuple[str, list[str]]]¶
- 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, AllowedTypes | None]]¶
Yield all extras as key-value pairs.
- iter_links_items(as_str: Literal[True] = True) Iterable[tuple[str, list[str]]]¶
- iter_links_items(as_str: Literal[False] = False) Iterable[tuple[str, list[NeedLink]]]
Yield all links as (link_type, references) pairs.
- iter_part_items() Iterable[NeedPartItem]¶
Yield all parts, merged with the parent part, from a need.
- property modifications: tuple[NeedModification, ...]¶
Return the modifications of the need item.
- property parts: Iterable[NeedPartData]¶
Return the parts of the need item.
- 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_backlinks() None¶
Sort the back links, and the parts’ back links, in need-id order.
Ids are compared as strings (
REQ_10beforeREQ_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_links() None¶
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. DuplicateNeedLinkentries (same id, part, and condition) are collapsed.
- property source: NeedItemSourceProtocol¶
Return the source 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, asadd_needtook it; a line its data names),NeedItemSource.on_page(docname, lineno=lineno).Stable: the class’s name,
from_directive()(thefirsthops it takes are provisional, as every hop is),on_page(), and reading a source throughNeedItemSourceProtocol(dict_repr). Provisional: the constructor itself and its keywords (hops,lineno,lineno_content,parser_lineno,is_import,upstream), like the hop types, until the finalneeds.jsonformat 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, whichNeedItemSourceProtocolrequires), one rule each:docname: thedocumentof the placement, the last hop with one (Nonewithout);lineno:linenoas given, else the start line ofhops[0];lineno_content: as given;is_external/external_url: whetherhops[0]is anexternalhop, and its URL;is_import: as given (no hop says that aneedimportread the file: an included file is afilehop too).
Added in version 9.2.0.
- 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.linenois the line the directive is written on, resolved through the state machine (whichrst_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 aslineno_contentandparser_lineno.- Parameters:
directive – The directive, a docutils
Directive(aSphinxDirectiveis 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.
- classmethod on_page(docname: str | None, *, lineno: int | None = None) NeedItemSource¶
The source of a need placed on the page
docname, atlineno: onepagehop of that document, with no line of its own (nothing is read from the file, nothing checked against it), whose filecreate_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 keepingadd_need’s line, its own parser line (self.lineno), or one placing needs at the lines its data names; and as theneed_sourceofgenerate_need()oringest_need_record(). It works alike in a docutilsDirectiveand 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.linenois 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 renderedpre_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 reachesneeds.json.Nonemeans 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
hopsis.
- upstream: tuple[NeedSourceHop, ...]¶
The hops the need’s text came through before this build read it, innermost first: those a
needs.jsonof the experimental format 2a1 records for it, read by needimport or needs_external_needs, each with theoriginof the project it belongs to (provisional). They come beforehops, which are this build’s; no legacy key and noc.sourcefacet reads them.
- class NeedItemSourceProtocol(*args, **kwargs)¶
Protocol for need item source types.
- property dict_repr: NeedsSourceInfoType¶
Return a dictionary representation of the source.
- class NeedLink(*, id: str, part: str | None = None, condition: str | None = None)¶
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]]andID[[x == a[0]]]are the conditionx == 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.
- to_link_string() str¶
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 throughfrom_string()as the same link. An empty condition is writtenID[], 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_backlinks(link_type: str, *, as_str: Literal[True] = True) list[str]¶
- 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_links(link_type: str, *, as_str: Literal[True] = True) list[str]¶
- 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.
- iter_backlinks_items(as_str: Literal[True] = True) Iterable[tuple[str, list[str]]]¶
- iter_backlinks_items(as_str: Literal[False] = False) Iterable[tuple[str, list[NeedLink]]]
Yield all backlinks as (link_type, references) pairs.
- 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
filehop 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’seval-rstblock 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.jsonformat 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.- 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
pagehop, the page’s suffix its parser keys on (".rst",".md"); for afilehop, the file’s own suffix (".rst",".json"; an includedinc.rst.txtrecords".txt", whichever parser reads it); for acodehop, the language’s name ("python","cpp", …), as sphinx-codelinks records it, as thecodehop of a docstring autodoc reads records it ("python"), and as sphinx-test-reports names a test case’s file by its suffix (Nonefor a suffix it does not know).
- origin: str | None¶
The project the hop belongs to:
Nonefor a hop of this build; on a hop read from aneeds.jsonof the experimental format 2a1 (a need’supstream), 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’sjson_pathalike, 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;
Nonewhere 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 (afilehop, 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.jsonformat 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.- path: str | None¶
POSIX, relative to
root, with..for a file outside it; under aNoneroot, relative to the producer’s own root, as the producer gives it (still POSIX).
- 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.jsonformat 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 acodehop, 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.sidesfor 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_chaindoes). 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
pagehop’s entries use the document’s:"section","<domain>:<objtype>"("py:method"). Anexternalhop’s are the"section"entries of the need’s page on the other site, as its record gives them. Ageneratedhop’s name its generator ("service"). Acodehop’s use its producer’s entity kinds, read together with the hop’slanguage: 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 acodehop 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.
- parts: Mapping[str, NeedsPartType]¶
Mapping of parts, a.k.a. sub-needs, IDs to data that overrides the need’s data
- class NeedsInfoType¶
Data for a single need.
- class NeedsPartType¶
Data for a single need part.
- class NeedsSourceInfoType¶
Data for the source of a single need.
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.
- filter_has_tag(values: list[str]) NeedsAndPartsListView¶
Create new view with only needs/parts that have at least one of these values in the
tagsfield list.
- filter_id_complete(values: Iterable[str]) NeedsAndPartsListView¶
Create new view with only the needs/parts whose
id_completeis invalues.This selection is exact, which
filter_idsis 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
idfield.
- filter_is_external(value: bool) NeedsAndPartsListView¶
Create new view with only needs/parts where
is_externalfield is true/false.
- filter_statuses(values: list[str] | list[str | None]) NeedsAndPartsListView¶
Create new view with only needs/parts that have certain
statusfield values.- Parameters:
values – Statuses to filter by;
Noneselects 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
typefield values.- Parameters:
values – List of types to filter by.
or_type_names – If True, filter by both
typeandtype_namefield
- 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)
- filter_has_tag(values: list[str]) NeedsView¶
Create new view with only needs that have at least one of these values in the
tagsfield list.
- filter_is_external(value: bool) NeedsView¶
Create new view with only needs where
is_externalfield is true/false.
- filter_statuses(values: list[str] | list[str | None]) NeedsView¶
Create new view with only needs that have certain
statusfield values.- Parameters:
values – Statuses to filter by;
Noneselects the needs without one.
- filter_types(values: list[str], or_type_names: bool = False) NeedsView¶
Create new view with only needs that have certain
typefield values.- Parameters:
values – List of types to filter by.
or_type_names – If True, filter by both
typeandtype_namefield
- 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 fromneeds_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 alink_failof the field<l>_backat theseveritythe declaration gives (violationwhen it gives none).- property failure_severity: Literal['violation', 'warning', 'info']¶
The severity a need failing the count is reported at.
- 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 belowviolation(severity), which a need without a value fails without being refused. A derived field holds its empty value, notNone, where it is not nullable.
- convert_directive_option(value: str) FieldLiteralValue | FieldFunctionArray¶
Convert a string to the correct type for this field.
- Raises:
TypeError – if value is not a string
ValueError – if value cannot be converted to the correct type
FunctionParsingException – if a dynamic function is malformed
VariantParsingException – if a variant function is malformed
- 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:
ValueError – if value cannot be converted to the correct type
FunctionParsingException – if a dynamic function is malformed
VariantParsingException – if a variant function is malformed
- 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
deriverule computing this field, if it is derived.A derived field cannot be set in a need (
directive_optionisFalse) or by aneedextend(allow_extendisFalse), and takes no default. ADeriveInvalidmarks 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 overridingderiveon 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(): withderive_by_typeand noderive, the field may carry a literaldefaultfor it (though it allows no other defaults). An empty mapping is a field derived on no need type (itsderive_by_typecould not be read).
- property derived: bool¶
Whether the field is derived: it carries
derive, orderive_by_type.A derived field is closed on every need type, also on a type without a rule.
- property failure_severity: Literal['violation', 'warning', 'info']¶
The severity a need failing this declaration is reported at: its
severity, elseviolation.
- 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 anumberfield is a float); elseNonewhen 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_typeentry, elsederive, elseNone: a need of that type holds the field’soff_type_value().
- severity: Literal['violation', 'warning', 'info'] | None¶
The severity a need failing this declaration is reported at – its
schemakeywords, andnullable = false– orNone, 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 = falseis reported rather than enforced: a need without a value is created, holdingNone(admits_null), and the schema validation reports it.
- class FieldShape(*args, **kwargs)¶
The shape of a field: what is the same on every need type.
Its name, its kind, its
typeanditem_type, and its derivation (a field derived per need type says so in itsderive_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 throughFieldsSchema.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 throughglobal_declaration()andtype_declarations(). Overlays are registered after every other change to the schema, and once it is complete it is frozen (freeze()):create_schemafreezes the schema it builds, and the firstfor_type()one built by hand.- add_back_links(need_type: str, back_links: BackLinksSchema) None¶
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_link_field(field: LinkSchema) None¶
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_typeand 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
- back_links() Mapping[str, BackLinksSchema]¶
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.
- copying_link_types() list[str]¶
The link types that copy their links into
links(copy = true, deprecated), as a need’s creation copies them: those withcopy, butlinksitself and a derived one (whosecopyis refused), and none wherelinksis 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 whylinkstakes notarget_types: it holds links written in it and links copied into it.
- declares_default(name: str) bool¶
Whether a declaration of the field
namecarries 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’sadd_field(..., types=...)– orNonefor 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
deriveorderive_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 theirneeds_typesentries) 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_schemafreezes the schema it builds, as its last step; the firstfor_type()freezes a schema built by hand. Freezing again does nothing.
- 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_link_field(name: str) LinkShape | None¶
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,Noneif 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 throughfor_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 ofneed_typematches (set_id_patterns()), the root’s first; empty where neither the type nor an ancestor gives one.
- iter_all_fields() Iterable[FieldShape | LinkShape]¶
Iterate over all fields in the schema.
- iter_core_fields() Iterable[FieldShape]¶
Iterate over all core fields in the schema.
- iter_extra_fields() Iterable[FieldShape]¶
Iterate over all extra fields in the schema.
- property need_types: frozenset[str] | None¶
The configured need types (
set_need_types()),Noneif 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_typeor derivation, or not as a narrowing of the replacementTypeError – 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_regexpatterns the id of a need ofneed_typematches, besideneeds_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
nameanew, 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.
- 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.
- convert_directive_option(value: str) LinksLiteralValue | LinksFunctionArray¶
Convert a string to the correct type for this field.
- Raises:
TypeError – if value is not a string
ValueError – if value cannot be converted to the correct type
FunctionParsingException – if a dynamic function is malformed
VariantParsingException – if a variant function is malformed
- 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:
ValueError – if value cannot be converted to the correct type
FunctionParsingException – if a dynamic function is malformed
VariantParsingException – if a variant function is malformed
- copy: bool¶
If True, copy links to the common ‘links’ field (deprecated, removed in 10.0: a
unionlink 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
deriverule 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; aDeriveInvalidmarks 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 overridingderiveon 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 literaldefault, which onlyderive_by_typewithoutderivemay carry, else no links.
- property derived: bool¶
Whether the link type is derived: it carries
derive, orderive_by_type.A derived link type is closed on every need type, also on a type without a rule.
- 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, elseviolation.
- off_type_value() list[NeedLink]¶
The links a need holds when its type has no rule (see
rule_for()).The literal
defaultwhenderive_by_typestands withoutderive, else no links.
- path_on(need: NeedItem) DeriveRule | None¶
The
copyorcollectrule that computed this link type onneed, 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
needimportorneeds_external_needskeeps 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_typeentry, elsederive, elseNone: a need of that type holds the link type’soff_type_value().
- severity: Literal['violation', 'warning', 'info'] | None¶
The severity a need whose links fail this declaration’s
schemais reported at, orNone, not given:violation(failure_severity). AsFieldSchema.severity.
- summary: bool¶
Whether the link type is a summary (
summary = true, or acopyorcollectrule 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 withoutlink_types). Likedisplay, the same on every need type.
- target_types: LinkTargetTypes | None¶
The need types the links may point to (
target_types);None: any.Unlike
display,copyandallow_dead_linksit 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_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
unionrule that computed this link type onneed, 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
needimportorneeds_external_needskeeps 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 intolinks(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_typesentry declares them, orNonefor aneeds_links(orneeds_extra_links) entry; a type inheriting the declaration keeps its ancestor’s, so a message can name where the set comes from.
- 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 theirneeds_typesentries) that this type does not declare. The global view declares every one.It answers
Truefor any other name, a field or not, and a view made directly (not byFieldsSchema.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_link_field(name: str) LinkSchema | None¶
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.
- iter_link_fields() Iterable[LinkSchema]¶
Iterate over all link fields in the schema.
- narrowing_problems(base: FieldSchema | LinkSchema, overlay: FieldSchema | LinkSchema) list[str]¶
Why
overlayis not a narrowing ofbase: 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:
the same kind, a field or a link type (only this is reported when it differs);
the same
name,typeanditem_type;the same
deriveandderive_by_type;every schema keyword at least as strict (
schema_narrowing_problems);no schema keyword of
baseleft out;nullableonly from true to false;allow_extend,allow_defaultsanddirective_optiononly from true to false;the same
parse_variantsandparse_dynamic_functions: a needextend converts its value once, for the needs of every type;a literal
default, and each literal predicate default, valid against the schema ofoverlay, unless it is the base’s own literal, already invalid against the schema ofbase(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;for a link type, the same
display,copy,allow_dead_links,summaryandparse_conditions: a link type is drawn and resolved the same on every need type;for a link type whose
basehastarget_types,target_typeswhose accepted types (a listed type and the types inheriting from it, the EXPANDED sets compared) are among those ofbase: a link a need of the type may hold is one a need of any type may. An overlay withouttarget_typesunder 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;a
severityno lower than theseveritybasegives (infobelowwarningbelowviolation): what a declaration enforces, a narrower one enforces too. Wherebasegives none it is free, an overlay giving none under a base giving one drops it (defensive, as fortarget_types).
The
descriptionis 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.
- class DeriveInvalid(kind: str | None, reason: str, fallback: bool = False)¶
A
deriverule 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’sderiveon a need type where it cycles, rather than for an entry the configuration declares.
- class DeriveJoin(field: str, equals: str)¶
The
joinrole: the needs whosefieldequals one of this need’sequals.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
deriverule that can be read: the kind and its roles.- Variables:
kind – The kind.
field – The
fieldrole: the field read on each candidate, or on this need.fields – The
fieldsrole: the own fields hashed, in order (hash), or the link types whose links are united, in order (union).exclude – The
excluderole (fingerprint): what it leaves out.over – The
overrole: a link type, or<link type>_back.from_need – The
fromrole: the id of the one need read.join – The
joinrole (links,copy): the needs whose field equals one of this need’s values.where – The
whererole: the predicate narrowing the candidates (oncopy, withoverorjoinonly).test – The
testrole: the predicate asserted of each candidate.empty – The
emptyrole (any,all): the value over no candidate, oncewherehas dropped the candidates it does not hold on;None, unset, is the kind’s own value there,FalseforanyandTrueforall.select – The
selectrole of acopyover several targets.transitive – The
transitiverole ofmin/max.include_self – The
include_selfrole.include_parts – The
include_partsrole (links).after_derived – The
after = "derived"role.on_link – Whether a
copyorcollectis declared on a link type: it computes a path (path), a link list.
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.
- class FieldIntegerSchemaType¶
Integer field schema.
- 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.
- 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.
- class FieldStringSchemaType¶
String field schema.
- class LinkItemSchemaType¶
Items in array of link string ids
- 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.