Needs¶
The [needs] section is the heart of your ubproject.toml.
It tells ubCode how to identify, validate, and relate the need objects
in your reStructuredText files —
types, link types, custom fields, statuses, tags, and external needs.
See also
Minimal example¶
[needs]
id_required = true
id_regex = "^[A-Z]{3}_[0-9]{3}$"
[[needs.types]]
directive = "requirement"
title = "Requirement"
prefix = "REQ_"
[needs.links.implements]
outgoing = "implements"
incoming = "implemented by"
[needs.fields.priority]
description = "Priority level"
Core options¶
- id_required
Type:
boolean(default:false)If
true, every need must have an explicit ID. Iffalse, IDs will be auto-generated when not provided.- id_regex
Type:
string(default:"^[A-Z0-9_]{5,}")Regular expression pattern to validate need IDs against. Only applies when
id_requiredistrue.- id_length
Type:
integer(default:5)Length of auto-generated IDs when not explicitly provided.
- id_from_title
Type:
boolean(default:false)When
true, auto-generated IDs will be based on the need title instead of being random.- title_optional
Type:
boolean(default:false)When
true, allows needs to be created without a title.- title_from_content
Type:
boolean(default:false)When
true, auto-generates titles from the first line of need content when no title is provided.
Need types¶
The types array defines the available need directives in your project:
[[needs.types]]
directive = "requirement" # The RST directive name
title = "Requirement" # Human-readable title
prefix = "REQ_" # Prefix for auto-generated IDs
color = "#BFD8D2" # Color for diagrams
style = "node" # Style for diagrams
Each type supports:
directive(required): The reStructuredText directive nametitle(required): Human-readable title for the need typeprefix(required): Prefix used for auto-generated need IDscolor(optional, default:"#000000"): Color used in need diagramsstyle(optional, default:"node"): Style used in need diagrams
Link types¶
Links connect needs to one another and express relationships like implements, tests, or depends on. ubCode supports two configuration formats: the dict-based format (sphinx-needs ≥ 6.3, recommended) and the list-based format (sphinx-needs < 6.3).
Dict-based format (recommended)¶
Note
The dict-based [needs.links] format requires sphinx-needs ≥ 6.3.
It became the recommended format in sphinx-needs 7.0,
where the older list-based needs_extra_links was formally deprecated.
Use [needs.links.<name>] to define each link type as a named TOML table:
[needs.links.implements]
outgoing = "implements"
incoming = "implemented by"
copy = false
color = "#0000FF"
allow_dead_links = false
[needs.links.tests]
outgoing = "tests"
incoming = "tested by"
copy = true
Each link supports:
outgoing(optional): Description for outgoing linksincoming(optional): Description for incoming linkscopy(optional, default:false): Whether to copy to common linksallow_dead_links(optional, default:false): Allow links to non-existent needscolor(optional, default:"#000000"): Color for diagramsstyle(optional, default:"solid"): Line style for diagramsstyle_part(optional, default:"dotted"): Style for partial linksstyle_start(optional, default:"-"): Start marker for diagramsstyle_end(optional, default:">"): End marker for diagramsparse_variants(optional): Whether to parse variant functions (<<...>>) and variant data references (<{ ... }>, see Variant data) in this link’s values. sphinx-needs ≥ 7.0.parse_dynamic_functions(optional): Whether to parse dynamic functions in this link’s values. sphinx-needs ≥ 6.3.parse_conditions(optional, default:true): Whether bracket syntax in link values is interpreted as a condition expression. sphinx-needs ≥ 8.0.description(optional): Human-readable description of the link type. sphinx-needs ≥ 6.3.schema(optional): JSON Schema validation for link values
List-based format (sphinx-needs < 6.3)¶
Deprecated since version 0.25.0: The list-based [[needs.extra_links]] format
(needs_extra_links in sphinx-needs)
was deprecated in sphinx-needs 6.3 in favour of the dict-based format,
and formally migrated in sphinx-needs 7.0.
It remains supported for backward compatibility.
For new projects, prefer the dict-based [needs.links] format.
[[needs.extra_links]]
option = "implements" # The option name used in need directives
incoming = "implemented by" # Description for incoming links
outgoing = "implements" # Description for outgoing links
copy = false # Whether to copy to common links
color = "#0000FF" # Color for diagrams
style = "solid" # Line style for diagrams
style_part = "dotted" # Style for partial links
style_start = "-" # Start marker for diagrams
style_end = ">" # End marker for diagrams
allow_dead_links = false # Allow links to non-existent needs
Fields¶
Every need carries fields — named pieces of metadata
such as status, tags, or any custom attribute you define.
Some fields are built in (id, title, status, tags);
you can add your own through the configuration.
ubCode supports two formats:
the dict-based [needs.fields] format (aligned with sphinx-needs ≥ 7)
and the list-based extra_options format (for sphinx-needs < 7).
Dict-based format (recommended)¶
Note
The dict-based [needs.fields] format requires sphinx-needs ≥ 7.0.
It replaces the older list-based needs_extra_options
and also allows specializing core fields (e.g. status, tags)
by narrowing their type constraints.
Use [needs.fields.<name>] to define each custom field:
[needs.fields.priority]
description = "Priority level"
default = "medium"
[needs.fields.assignee]
description = "Assigned team member"
[needs.fields.effort]
description = "Story points"
schema = {type = "integer", minimum = 1, maximum = 13}
Each field supports:
description(optional): Human-readable descriptiondefault(optional): Default value if not specified on a needschema(optional): JSON Schema to validate values (see Field schemas below). Schema support for fields was introduced in sphinx-needs 6.0 (via ``needs_extra_options``) and carried forward to ``needs_fields`` in sphinx-needs 7.0.nullable(optional): Whether the field accepts null values. sphinx-needs ≥ 7.0.predicates(optional): Conditional defaults (see Global options). sphinx-needs ≥ 7.0.parse_variants(optional): Whether to parse variant functions (<<...>>) and variant data references (<{ ... }>, see Variant data) in this field’s values. sphinx-needs ≥ 7.0.parse_dynamic_functions(optional): Whether to parse dynamic functions in this field’s values. sphinx-needs ≥ 6.3.
Once declared, these fields can be used on any need:
.. req:: Encrypt data at rest
:id: REQ_002
:status: open
:priority: high
:assignee: alice
:effort: 5
List-based format (sphinx-needs < 7)¶
Deprecated since version 0.25.0: The list-based extra_options format
(needs_extra_options in sphinx-needs)
was deprecated in sphinx-needs 7.0 in favour of the dict-based format.
It remains supported for backward compatibility.
For new projects, prefer the dict-based [needs.fields] format.
Simple format (string list):
extra_options = ["priority", "effort", "assignee", "component"]
Detailed format (with descriptions and schemas):
extra_options = [
{name = "priority", description = "Priority level"},
{name = "effort", description = "Story points", schema = {type = "integer", minimum = 1, maximum = 13}},
{name = "assignee", description = "Assigned team member"},
]
Field schemas¶
The schema key on a field applies
JSON Schema validation
to every value entered for that field.
Invalid data is caught at index time, before the docs are built.
String schemas:
[needs.fields.contact]
schema = {type = "string", format = "email"}
[needs.fields.id_pattern]
schema = {type = "string", pattern = "^[A-Z]{3}-[0-9]{4}$"}
[needs.fields.severity]
schema = {type = "string", enum = ["low", "medium", "high", "critical"]}
String format options:
The format property supports various predefined formats:
"date": RFC 3339 date format (e.g.,2023-10-22)"date-time": RFC 3339 date-time format (e.g.,2023-10-22T14:30:00Z)"time": RFC 3339 time format (e.g.,14:30:00Z)"email": RFC 5322 email address (e.g.,user@example.com)"hostname": RFC 1123 hostname (e.g.,example.com)"uri": RFC 3986 URI (e.g.,https://example.com/path)"uuid": RFC 4122 UUID (e.g.,550e8400-e29b-41d4-a716-446655440000)"regex": ECMAScript regular expression
Integer and number schemas:
[needs.fields.priority]
schema = {type = "integer", minimum = 1, maximum = 10}
[needs.fields.rating]
schema = {type = "number", minimum = 0.0, maximum = 5.0}
[needs.fields.fibonacci]
schema = {type = "integer", enum = [1, 2, 3, 5, 8, 13, 21]}
Boolean schemas:
[needs.fields.approved]
schema = {type = "boolean"}
Array schemas:
[needs.fields.reviewers]
schema = {type = "array", items = {type = "string", format = "email"}, minItems = 1, maxItems = 3}
[needs.fields.categories]
schema = {type = "array", items = {type = "string"}, uniqueItems = true}
Usage in need directives:
Once defined, these fields can be used in your need directives:
.. requirement:: User Authentication
:priority: 5
:effort: 8
:assignee: developer@company.com
:due_date: 2024-12-31
:tags: security, authentication
:component: auth, backend
:active: true
Users must be able to authenticate using their corporate credentials.
External needs¶
Load needs from external sources (JSON files or URLs):
# From local file
[[needs.external_needs]]
json_path = "external/project_needs.json"
id_prefix = "EXT"
base_url = "https://external-project.com"
target_url = "docs/{{ id }}.html"
# From remote URL
[[needs.external_needs]]
json_url = "https://api.example.com/needs.json"
id_prefix = "REMOTE"
Each external needs source supports:
json_path: Path to a local JSON filejson_url: URL to a remote JSON fileid_prefix: Prefix prepended to external need IDsbase_url: Base URL for linking back to the source projecttarget_url: URL template for individual need links (supports{{ id }}placeholder)css_class: CSS class applied to external needsversion: Version string for the external needs sourceskip_in_ubcode(default:false): Whentrue, skip loading these external needs in ubCode analysis
Variants¶
The [needs.variants] section defines named filter expressions
that can be referenced from need fields using the <<name: value, fallback>> syntax.
This implements the Sphinx-Needs
variant functions
feature.
[needs.variants]
is_open = "status == 'open'"
is_html = "'html' in build_tags"
is_production = "environment == 'production'"
With these variants defined, need fields can use them:
.. req:: My requirement
:id: REQ_001
:status: <<is_open: active, inactive>>
:extra: <<is_html: web_only, general>>
You can also use inline bracketed expressions without pre-defining a variant:
.. req:: Another requirement
:id: REQ_002
:status: <<[status == 'open']: active, inactive>>
The last comma-separated value is always the fallback when no expression matches.
Note
Variant syntax is only parsed for fields that opt in.
In sphinx-needs ≥ 7.0, set parse_variants = true
on individual fields in [needs.fields].
For older versions, use the deprecated variant_options list
(see Deprecated needs options).
Parse and evaluation errors are surfaced as needs.variant diagnostics.
See also
Variant data for the related var.* namespace
and the <{ ... }> reference syntax, which inject build-wide data
rather than selecting between values conditionally.
Variant data¶
sphinx-needs ≥ 8.2.
Variant data is a nested, read-only key-value store that ubCode exposes
under the var.* namespace inside filter expressions.
It lets a single “150%” model —
one set of needs that describes every product variant —
be filtered, extended, and rendered differently for each build,
without duplicating content.
See also
Variant builds walks through the end-to-end workflow — including building and diffing each variant from the command line. This section is the configuration reference for the options it uses.
For example, a project that targets several CPU architectures and build configurations can keep one copy of its requirements and select the data relevant to the current build:
[needs.variant_data]
platform = "arm"
debug = false
opt_level = 2
archs = ["arm", "x86"]
[needs.variant_data.build]
compiler = "clang"
features = ["networking", "logging"]
With this configuration the following values are available in any filter expression:
var.platform == "arm" # scalar string
var.opt_level == 2 # scalar integer
var.debug == False # scalar boolean
"x86" in var.archs # membership in an array
var.build.compiler == "clang" # nested scalar
"networking" in var.build.features # membership in a nested array
Note
var.* resolves only against the configured variant data.
Unlike need fields or filter_data keys,
it does not depend on the need currently being evaluated,
which makes it suitable for global, build-wide switches.
Looking up an unknown key (e.g. var.missing) is a
needs.variant diagnostic, not a silent false.
Defining variant data¶
Variant data can be provided inline, from a JSON file, or both.
- variant_data
Type:
object(default:{})An inline, nested table of variant data, declared directly in
ubproject.tomlunder[needs.variant_data](as shown above).- variant_data_file
Type:
string(default:"")Path to a JSON file containing variant data, resolved relative to the
ubproject.tomlthat declares it. This is convenient when the values are generated by a build system or shared across several projects.[needs] variant_data_file = "variant_data.json"
{ "platform": "linux", "build": {"compiler": "gcc", "features": ["networking"]} }
When both are set, the file is loaded first as the base,
and the inline [needs.variant_data] values are deep-merged on top,
with the inline values winning:
At nested maps, keys are merged recursively.
At scalars and arrays, the inline value replaces the file value entirely.
[needs]
# file sets platform = "linux", build.compiler = "gcc", build.features = [...]
variant_data_file = "variant_data.json"
[needs.variant_data]
platform = "windows" # overrides the file's scalar value
[needs.variant_data.build]
compiler = "clang" # overrides build.compiler;
# build.features is preserved from the file
Because inline values take precedence, you can keep shared defaults in the file under version control and override individual switches per build.
Value types¶
Variant data values may be:
Scalars — string, integer, number (float), or boolean.
Uniform arrays of scalars — every element must share the same type (mixed-type arrays are reported as a
needs.variantdiagnostic).Nested maps — arbitrarily deep tables, accessed with further dots (
var.build.sanitizers.asan).
Using var.* in filters¶
The var.* namespace is available anywhere ubCode evaluates a
filter expression against your needs configuration.
Variant functions — select a field value per build (see Variants):
.. req:: Bootloader
:id: REQ_001
:status: <<[var.platform == "windows"]: active, inactive>>
Conditional defaults — vary a field’s default via predicates:
[needs.fields.compiler_note]
default = "unknown-compiler"
predicates = [
['var.build.compiler == "clang"', "using-clang"],
['var.build.compiler == "gcc"', "using-gcc"],
]
needextend — extend only the needs relevant to the current build:
.. needextend:: var.platform == "windows"
:status: supported
needimport — import only the needs that match:
.. needimport:: external.json
:filter: var.platform == "windows"
External needs — filter needs loaded from another project:
[[needs.external_needs]]
json_path = "imported.json"
filter = 'var.platform == "windows"'
Referencing variant data in field values (<{ ... }>)¶
In addition to filtering, variant data can be injected directly into a
field or link value using the <{ ... }> syntax.
The text between the delimiters is a var.* path,
and ubCode substitutes the stored value at index time.
Unlike variant functions (<<...>>),
which choose between candidate values using conditional logic,
a <{ ... }> reference is a direct lookup —
there is no condition, just a substitution.
This syntax is only parsed for fields and links that opt in with
parse_variants = true (the same flag that enables <<...>>):
[needs.variant_data]
platform = "arm"
links = ["REQ_001", "REQ_002"]
[needs.variant_data.build]
opt_level = 2
[needs.fields.arch]
schema = {type = "string"}
parse_variants = true
[needs.fields.opt]
schema = {type = "integer"}
parse_variants = true
[needs.links.derived_from]
parse_variants = true
The references can then be used on any need:
.. req:: Scalar reference
:id: REQ_001
:arch: <{ var.platform }>
:opt: <{ var.build.opt_level }>
.. req:: Embedded in a string
:id: REQ_002
:arch: built for <{ var.platform }>
.. req:: Within an array and a link field
:id: REQ_003
:tags: a, <{ var.platform }>, c
:derived_from: <{ var.links }>
A few rules apply:
For string fields the reference may be embedded inside surrounding text; for non-string scalar fields (integer, number, boolean) the reference must be the entire value.
The resolved value is type-checked against the field’s schema.
If the field does not set
parse_variants = true, the<{ ... }>text is kept literally.
The following situations are reported as needs.variant diagnostics
(and leave the value unset):
Invalid expression — the content is not a dotted
var.*path, e.g.<{ platform }>(missing thevar.root).Unknown key — the path does not exist in the variant data, e.g.
<{ var.nonexistent }>or<{ var.build.missing }>.Non-leaf value — the path resolves to a nested map rather than a scalar or array, e.g.
<{ var.build }>.Type mismatch — the resolved value’s type does not match the field, e.g. injecting a string into an integer field.
See also
The corresponding sphinx-needs feature is documented under variant data.
Dynamic functions¶
ubCode resolves some built-in dynamic functions using the [[...]] syntax
from sphinx-needs.
Four built-in functions are supported:
echo("text")— returns the given string unchangedtest(*args, **kwargs)— returns a debug string with the need ID and argumentscopy("field")— copies a field value from the current need (also supportslowerandupperkwargs)links_from_content()— returns the needs referenced via:need:roles in the need’s own content (theneed_idandfilterarguments are not yet supported)
For example:
.. req:: My requirement
:id: REQ_001
:status: [[copy("priority")]]
.. spec:: My specification
:id: SPEC_001
:links: [[links_from_content()]]
This specification realises :need:`REQ_001`.
Dynamic function parsing is enabled by default and can be controlled globally or per-field/per-link:
[needs]
parse_dynamic_functions = false # disable globally (sphinx-needs ≥ 7.0)
[needs.fields.my_field]
parse_dynamic_functions = true # re-enable for this field (sphinx-needs ≥ 6.3)
[needs.links.implements]
parse_dynamic_functions = true # re-enable for this link (sphinx-needs ≥ 6.3)
- parse_dynamic_functions
Type:
boolean(default:true)Whether dynamic functions are parsed by default. Applies to extra fields and links unless overridden per-field/per-link. Core fields are not affected by this global setting.
The global
parse_dynamic_functionsoption was added in sphinx-needs 7.0 (needs_parse_dynamic_functions). Per-field and per-linkparse_dynamic_functionswas added in sphinx-needs 6.3.
filter_data¶
Type: object (default: {})
Additional context variables that can be referenced in filter expressions throughout your needs configuration. This allows you to define custom variables for conditional logic in multiple contexts.
Where filter_data can be used:
Predicate default filters — in
global_optionspredicates (orpredicateson individual fields/links in sphinx-needs ≥ 7.0) to conditionally set default valuesneedimport item filters — to filter which needs are imported from external sources
needextend filters — to conditionally extend or modify needs based on context
[needs]
filter_data = { environment = "production", team = "backend", version = "2.0" }
# Using predicates on a field directly (sphinx-needs ≥ 7.0, recommended)
[needs.fields.priority]
default = "medium"
predicates = [
["environment == 'production'", "high"],
["team == 'backend'", "high"]
]
# Or via global_options (sphinx-needs < 7, deprecated)
# [needs.global_options.priority]
# default = "medium"
# predicates = [
# ["environment == 'production'", "high"],
# ["team == 'backend'", "high"]
# ]
Example with needimport:
.. needimport:: external_needs.json
:filter: environment == 'production' and version == '2.0'
Example with needextend:
.. needextend:: team == 'backend'
:status: critical
Common use cases:
Setting environment-specific defaults (development, staging, production)
Filtering imported needs based on release version or team ownership
Applying team or project-specific configurations
Conditional formatting or validation based on build context
Dynamic behaviour in CI/CD pipelines
import_keys¶
Type: object (default: {})
Mapping of keys for needimport directives,
allowing short keys to reference longer file paths.
Available since sphinx-needs 4.2.
[needs.import_keys]
backend = "/path/to/backend_needs.json"
frontend = "/path/to/frontend_needs.json"