Skip to main content

vyyhti

Tangle (Finnish: vyyhti) — scan and process embedded processing instructions in text documents.

Requires Python 3.11 or later.

Install

pip install vyyhti

Quickstart

Run vyyhti scan with a text file to list every detected embedding:

$ vyyhti scan doc.md
line_command:1:0: '\\newpage'
inline_code:9:22: '`$.document.notes[*].group_ids[*]`'
line_command:11:0: '\\columns=10%,,%30%'
block_pi:21:0: '```{.text} <!--json-path-list-->'

Print the version:

$ vyyhti version
vyyhti 2026.6.21
$ vyyhti -V
vyyhti 2026.6.21

For a dedicated feature walkthrough you can follow in minutes visit quickstart. A step-by-step build of a JSONPath expression linter for Markdown documents is provided in the tutorial.

Embedding kinds

Inline code

A single-backtick span on any non-fenced line:

The expression `$.notes[*].id` must yield at least one result.

Line command

A line whose only content is a backslash command (as used by liitos and LaTeX preprocessors):

\newpage
\columns=10%,,30%

Block processing instruction

A fenced code block whose info string contains an HTML processing instruction comment:

```{.text} <!--json-path-list-->
$.document.notes[*].group_ids[*]
$.vulnerabilities[*].flags[*].group_ids[*]
```

The PI name (json-path-list above) is extracted and available to handlers. The fence body is preserved verbatim.

Library API

scan(text, config=None)

Scans a text string and returns a list of Location objects — one per detected embedding.

from vyyhti import scan, LocationKind

locs = scan(open('doc.md').read())
for loc in locs:
    print(loc.kind, loc.line, loc.text)

Each Location is a frozen dataclass:

Field Type Meaning
kind LocationKind INLINE_CODE, LINE_COMMAND, or BLOCK_PI
line int 1-based line number of the embedding start
col int 1-based column for INLINE_CODE; 0 for whole-line kinds
text str Raw matched text (backtick span / command / opening fence line)
body str \ None For BLOCK_PI: fence body; None otherwise
pi str \ None For BLOCK_PI: content of <!--…-->; None otherwise

run(text, handlers, stages=None, config=None)

Scans the text and applies a list of handlers through the processing pipeline. Returns (embeddings, findings).

from vyyhti import run, Handler, LocationKind

class MyHandler(Handler):
    name = 'my-handler'

    def identify(self, location):
        return location.kind == LocationKind.INLINE_CODE

    def parse(self, location):
        return location.text[1:-1]   # strip backticks

    def verify(self, location, payload):
        return [] if payload.startswith('$') else ['not a JSONPath']

embeddings, findings = run(text, [MyHandler()])

Each matched Location becomes an Embedding (with handler name and payload set). Each problem becomes a Finding (with stage, message, and level).

Pass stages={'parse', 'verify'} to limit which pipeline stages execute. Pass a ScannerConfig to configure which embedding kinds are scanned.

ScannerConfig

Controls which embedding kinds are active and overrides the default match patterns. Pass an instance to scan() or run().

from vyyhti import scan, ScannerConfig

cfg = ScannerConfig(line_command=False)   # skip \commands
locs = scan(text, cfg)
Field Type Default Meaning
inline_code bool True Scan for inline-code spans
line_command bool True Scan for backslash line commands
block_pi bool True Scan for fenced-block PIs
block_pi_pi_pattern str <!--(.*?)--> Regex for PI comment in fence info string
line_command_pattern str ^\s*(\\...)$ Regex for line commands

Load from a YAML file (kebab-case keys) with load_scanner_config(path) from vyyhti._config.

Handler

An abstract base class. Subclass it and implement the methods you need.

Method Signature Required Default
name str class attribute yes —
identify (location) -> bool yes —
parse (location) -> Any no returns location.text
verify (location, payload) -> list[str] no returns []
validate (location, payload) -> list[str] no returns []
process (location, payload) -> Any no returns None

Finding

Field Type Meaning
location Location The embedding where the problem was found
stage str 'parse', 'verify', 'validate', 'process'
message str Human-readable description
level str 'error' (default), 'warning', or 'info'

Design

Handlers live in the tools that use vyyhti, not in the library itself. scan() finds all structural embedding locations; each handler decides what it claims via identify(). The pipeline then applies only the stages the handler cares about. Handlers for different embedding kinds can coexist in the same run() call.

Exit codes

  • 0 — success.
  • 1 — error: file not found, unreadable, or missing required argument.

See also

man vyyhti

Design and requirements

Document Identifier File
Software Requirements Specification VYY-SRS-001 requirements/srs/
Software Design Description VYY-SDD-001 design/sdd/

Both documents follow the MIL-STD-498 DID structure and are rendered into the documentation site alongside the quickstart and tutorial.

Changes

See docs/changes.md for the release history.

Coverage

The test suite maintains 99% branch coverage. The HTML report (if generated) is in site/coverage/.

SBOM

Runtime dependency information is published in docs/sbom/ in SPDX 3.0 (JSON-LD) and CycloneDX 1.6 (JSON) formats. See docs/sbom/README.md for the component inventory and validation guide.

Metadata

Release files for vyyhti 2026.10.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vyyhti 2026.10.3
File Size Uploaded
vyyhti-2026.10.3.tar.gz 20.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for vyyhti 2026.10.3
File Interpreter ABI Platform
vyyhti-2026.10.3-py3-none-any.whl Python 3 none any Details

Total release size: 31.3 kB

Release files / vyyhti-2026.10.3.tar.gz

Download URL vyyhti-2026.10.3.tar.gz
Size 20.8 kB
Tags Source
SHA-256 checksum
How to use checksums
1ebccc23b25381fac56f8597982cbae1f29237465d88974b84acd803406974de
BLAKE2b-256 checksum
How to use checksums
f90f3719a6a8b8abb6739baa8dca3c0ca0c01ad71cbb758f5e5721b99bdc4bc8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.5

Release files / vyyhti-2026.10.3-py3-none-any.whl

Download URL vyyhti-2026.10.3-py3-none-any.whl
Size 10.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3124d09e1167a8ed698b22c22d2cd2d4c56a0014ff731903482204b85af1153f
BLAKE2b-256 checksum
How to use checksums
f1c8056082d7a94019a9edd0cd9bfe24855036f3506220dea97a581091f80e77
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.5

Release history Release notifications | RSS feed

This release

2026.10.3 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page