Contents Menu Expand Light mode Dark mode Auto light/dark, in light mode Auto light/dark, in dark mode Skip to content
ubCode 0.32.0 documentation
Light Logo Dark Logo

Getting started

  • What is ubCode?
  • Key concepts
    • Glossary
  • Installation
  • Quickstart

Configuration & authoring

  • Configuration
    • Needs
    • Card designs with [needs.card_layouts]
    • Field links with [needs.string_links]
    • Schema validation
    • Deprecated needs options
    • Codelinks (source code tracing)
    • Intersphinx
    • Parsing
    • Linting
    • Formatting
    • Server
    • Project, source & scripts
    • HTML site
    • Workflow
    • Quality Analysis
  • Authoring
    • Directives
    • Roles
    • Needs
    • Variants
    • Diagrams
    • Markdown (MyST)
    • Differences from a Sphinx build

Core workflows

  • Needs tutorial
  • Coming from Sphinx-Needs
  • What works inside imported need content
  • Tracing source code with Codelinks
  • Pharaoh Agentic Engineer
    • Quick start
    • Configuring the workflow
    • Reviews and quality analysis
    • The ubc agent command
    • In VS Code

Guides

  • Writing a filter
  • Writing reports
  • Building a static HTML site
  • Variant builds
  • Using ubc in CI/CD

Reference

  • Features
    • Home view
    • Linting
    • RST preview
    • Realtime index
    • Model Context Protocol (MCP) server
    • Chat participant
    • Needs filtering
    • Needs graph view
    • Navigation
    • Site Map
    • Commands
    • Diff & impact analysis
    • needs.json view
  • Supported toolchain

Support

  • Troubleshooting
  • Request a license
  • Report an issue

Development

  • Roadmap
  • Changelog
    • 0.32.x
    • 0.31.x
    • 0.30.x
    • 0.29.x
    • 0.28.x
    • 0.27.x
    • 0.26.x
    • 0.25.x
    • 0.24.x
    • 0.23.x
    • 0.22.x
    • 0.21.x
    • 0.20.x
    • 0.19.x
    • 0.18.x
    • 0.17.x
    • 0.16.x
    • 0.15.x
    • 0.14.x
    • 0.13.x
    • 0.12.x
    • 0.11.x
    • 0.10.x
    • 0.9.x
    • 0.8.x
    • 0.7.x
    • 0.6.x
    • 0.5.x
    • 0.4.x
    • 0.3.x
    • 0.2.x
Back to top
View this page
Edit this page

Field links with [needs.string_links]¶

Added in version 0.31.2.

Some need fields hold a value that means a link: a ticket id, a commit hash, a URL to an external tracker. [needs.string_links] turns those values into hyperlinks wherever ubCode shows the field — in the need’s card, in a needtable, in the built site and in the editor’s rendered preview alike — without changing what the need actually stores.

A rule is a pattern plus two templates: values of the fields it names are matched against the pattern, and a match renders a link whose target and label come from the templates.

[needs.fields.ticket_url]

[needs.string_links.tracker]
regex = "^(?P<value>https://.+)$"
link_url = "{{value}}"
link_name = "Open in Tracker"
options = ["ticket_url"]

A need carrying :ticket_url: https://tracker.example.com/items/1 now shows that row as a link labelled Open in Tracker. A need whose value does not match the pattern shows it as plain text, and nothing is reported — a value that does not match is the ordinary case, not a fault.

The keys¶

Key

What it does

regex

The pattern a value must match. Its named capture groups — (?P<name>...) — are the variables the two templates render with.

link_url

The link target, as a template over those groups.

link_name

The link’s visible text, as a template over the same groups.

options

The need fields this rule applies to.

All four are required. A rule missing one — or whose regex or templates do not compile — is reported against its own configuration path and ignored, and the fields it names keep rendering as plain text. The rest of the project keeps building.

[config.string_link_incomplete]
  --> ubproject.toml
  needs.string_links.tracker: `regex` is missing or empty, so this rule
  could never draw a link — this rule is ignored (a string link needs all
  of `regex`, `link_url`, `link_name` and `options`)

An options entry naming a field the project never declared is reported too (config.string_link_unknown_field) and dropped, while the rule keeps its other fields: a misspelled field name is the common case, and losing the working half of a rule to it would cost more than it explains.

A worked example¶

A project that records ticket ids rather than URLs, several to a need:

[needs.fields.tickets]

[needs.string_links.tickets]
regex = "^(?P<tool>[A-Z]+)-(?P<num>\\d+)$"
link_url = "https://tickets.example.com/{{tool | lower}}/{{num}}"
link_name = "{{tool}} {{num}}"
options = ["tickets"]
.. req:: Rate limiting
   :id: REQ_001
   :tickets: AB-1, AB-2; ZZ-9

The row renders as three links — AB 1, AB 2, ZZ 9 — separated by ;, each pointing at its own ticket. Both templates see every named group the pattern captured, so tool and num are available to the target and to the label.

How a value is matched¶

Splitting. A field named in any rule’s options has its value split on , and ;, each item stripped, and each item matched on its own. That is what makes a multi-value field render as several links. It also means there is no escape for a literal comma or semicolon in a field a rule claims: https://example.com/a,b is two items, not one.

The split is not undone: the items are re-joined with ; for display even when none of them matched, so a claimed field holding alice, bob renders as alice; bob. A field no rule names is never touched — not matched, not split, not re-joined.

One rule per field. Where more than one rule lists the same field, only the first rule declared is ever tried. It is not retried against the others when its pattern does not match: if the first rule’s pattern fails, the value renders as plain text even though a later rule would have matched it. Rules are tried in the order they appear in ubproject.toml.

Anchoring. The pattern is searched for, not matched against the whole value, so an unanchored pattern matches anywhere inside the item — and when it does, the whole item is replaced by the link. see JIRA-123 please, matched by (?P<id>[A-Z]+-\d+), renders as one link and the surrounding words are gone. Anchor with ^ and $ unless you mean the whole value to be replaced.

Empty labels. A link_name that renders empty draws no link: the value renders as plain text. That is how a rule can decline a value it matched.

List fields. A list-valued field (tags, or any field declared with an array schema) links per element, in the need’s card and in a needtable alike. Its elements are the items; they are not split further.

Which columns link. In a needtable, the ID column and every link-type column keep their own navigation and ignore string links entirely. Every other column — including TITLE — goes through the rules.

The regular expression engine¶

Patterns are compiled by ubCode’s regular expression engine, which guarantees a linear-time match — it cannot be made to hang on a pathological pattern. The price is that some constructs Python’s re accepts are not supported. Three are common enough that a pattern using one is reported by name rather than as a bare parse failure:

  • look-ahead ((?=...), (?!...))

  • look-behind ((?<=...), (?<!...))

  • backreferences ((?P=name), \1)

A few rarer ones — conditionals ((?(1)...)), atomic groups ((?>...)), \Z and inline comments ((?#...)) — are refused too, as ordinary parse errors carrying the engine’s own cause.

The named case reads:

[config.string_link_regex_invalid]
  --> ubproject.toml
  needs.string_links.tracker: `regex` uses look-ahead, which ubCode's
  regular expression engine does not support, so this rule is ignored and
  the fields it names keep rendering as plain text — rewrite the pattern
  without it (the engine is the price of a guaranteed linear-time match)

Named groups, non-capturing groups, character classes, alternation and inline flags ((?i), (?s), (?m)) all work as usual. Only named groups reach the templates; an unnamed group is invisible to them. A named group that did not participate in the match renders as the empty string.

The template engine¶

Both templates are rendered with MiniJinja, which is the engine sphinx-needs renders needs_string_links with too, so a template that works there works here. Its stock filters are available ({{tool | lower}}, {{id | replace("_", "-")}}); anything beyond them does not resolve. An undefined variable renders as empty rather than raising.

The templates see the pattern’s named groups and nothing else. ubCode has no equivalent of sphinx-needs’ needs_render_context, so a template cannot reach a project-wide variable — which also means a global cannot silently shadow a capture group named value.

A template that fails to render (an unknown filter is the usual cause) leaves the value as plain text and reports build.need_string_link_invalid, once per message however many needs it applies to.

Accepted link targets¶

A rendered link_url must be one of:

  • an https:// or http:// URL,

  • a mailto: address,

  • a relative reference or same-page fragment (reports/index.html, #section).

A relative reference is refused only when its leading characters are scheme-shaped — a letter followed by letters, digits, +, . or -, up to a colon, as in notes:2024.html: by the URL grammar a leading word: is a scheme, and there is no way to tell the two apart from the text alone. Write ./notes:2024.html if you mean the path. A colon later in the reference (docs/2024:notes.html, 2024:notes.html) is not scheme-shaped and passes.

Anything else is refused, and no link is drawn — javascript:, data:, file:, an unknown scheme, a protocol-relative //host target, or a target carrying a control character. The value renders as plain text and the build reports build.need_string_link_invalid naming the rule and the refused target.

The check runs on what the template rendered, not on the field value, so a dangerous scheme is refused whether it came from the value or was written into link_url itself. This matters because a need field is not always something your project wrote: a need can arrive from an imported needs.json, and escaping a value stops attribute break-out but does not stop a scheme.

Differences from sphinx-needs¶

The matching rules above are ported from sphinx-needs and measured against it. Some behaviours deliberately differ, each because the upstream one is a defect rather than a contract; they are listed with the rest in differences:

  • No trailing separator. Upstream’s meta area up to release 8.3 emits a ; after the last item as well as between items (its own needtable does not). ubCode emits separators between items only, on both surfaces — as does Sphinx-Needs from its next release (fixed upstream in sphinx-needs PR 1718).

  • List fields link everywhere. Upstream applies string links to a list field’s elements in a needtable but not in the need’s own meta area — the same field, two answers, on one page. ubCode links them on both.

  • A failed template keeps the value. Upstream drops the field’s value out of the page entirely when a template raises. ubCode renders the plain text and reports it.

  • Whitespace-only items are dropped. AB-1, , AB-2 is two items, not three.

  • A non-participating optional group renders empty. Upstream prints the literal None there.

Further differences are consequences of the engines: ubCode refuses a dangerous link target where sphinx-needs emits it, and ubCode validates a rule when the configuration loads where sphinx-needs raises out of the first need it renders (so a rule with a missing key ends an upstream build even when no need carries the field).

Upgrade cost¶

Every project pays a one-off cost on the release that adds this feature, whether or not it configures a rule: the resolved configuration gained the section, which moves the configuration hash that keys the index cache, and the build manifest’s schema version moved with the renderer — so the first build after upgrading re-indexes and re-renders once, then settles.

After that the cost is the ordinary one: adding or editing a rule changes the configuration hash again, so that build re-indexes and re-renders once too. That is the intended trade — it is also what makes an edit to a rule’s link_url re-render every page that draws it.

Next
Schema validation
Previous
Card designs with [needs.card_layouts]
Copyright © 2026, team useblocks
Made with Sphinx and @pradyunsg's Furo
On this page
  • Field links with [needs.string_links]
    • The keys
    • A worked example
    • How a value is matched
    • The regular expression engine
    • The template engine
    • Accepted link targets
    • Differences from sphinx-needs
    • Upgrade cost