Scan and process embedded processing instructions in Text documents.
Project description
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
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vyyhti-2026.6.21.tar.gz.
File metadata
- Download URL: vyyhti-2026.6.21.tar.gz
- Upload date:
- Size: 19.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dd054bb497b152dcd1926b74be822c83a706ed51312eed97e844a0c5401fa791
|
|
| MD5 |
0660793ce4812e1e1158c6fb81c68e09
|
|
| BLAKE2b-256 |
3a6ef80160687f6edc2802fdb136f11fd349606d4169b76c76c336eb03666800
|
File details
Details for the file vyyhti-2026.6.21-py3-none-any.whl.
File metadata
- Download URL: vyyhti-2026.6.21-py3-none-any.whl
- Upload date:
- Size: 9.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c06405d91acd68ec2be6185df4e93203fbbff9a13750d7c6aeec722609a2afeb
|
|
| MD5 |
b832451f1dcc55ec84af54b31ba9afcd
|
|
| BLAKE2b-256 |
539084efa25a96a1d29f36fb846f47a549a3f497461399d969e83622068373ca
|