intenttext 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: actions/setup-python@v5
14
+ with:
15
+ python-version: "3.11"
16
+ - run: pip install hatch
17
+ - run: hatch build
18
+ - run: pip install pytest && pytest
19
+ - uses: pypa/gh-action-pypi-publish@release/v1
20
+ with:
21
+ password: ${{ secrets.PYPI_API_TOKEN }}
@@ -0,0 +1,18 @@
1
+ name: Test
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ python-version: ["3.10", "3.11", "3.12"]
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: actions/setup-python@v5
14
+ with:
15
+ python-version: ${{ matrix.python-version }}
16
+ - run: python -m pip install -U pip
17
+ - run: pip install -e .[dev]
18
+ - run: pytest -q
@@ -0,0 +1,106 @@
1
+ Metadata-Version: 2.4
2
+ Name: intenttext
3
+ Version: 1.0.0
4
+ Summary: IntentText - the semantic document format that is natively JSON
5
+ Project-URL: Homepage, https://github.com/intenttext/IntentText
6
+ Project-URL: Repository, https://github.com/intenttext/intenttext-python
7
+ Project-URL: Documentation, https://github.com/intenttext/IntentText/blob/main/docs/SPEC.md
8
+ License: MIT
9
+ Requires-Python: >=3.10
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest; extra == 'dev'
12
+ Requires-Dist: pytest-cov; extra == 'dev'
13
+ Description-Content-Type: text/markdown
14
+
15
+ # IntentText Python
16
+
17
+ Python implementation of the IntentText parser and renderer.
18
+
19
+ Independent implementation (not a Node.js wrapper), designed for Python workflows and AI stacks.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ pip install intenttext
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ```python
30
+ from intenttext import parse, render_html, merge_data, validate, query
31
+
32
+ # Parse a document
33
+ source = """
34
+ title: Sprint Planning
35
+ section: Tasks
36
+ task: Write tests | owner: Ahmed | due: Friday
37
+ task: Deploy to staging | owner: Sarah | due: Monday
38
+ gate: Final approval | approver: Lead | timeout: 24h
39
+ """.strip()
40
+
41
+ doc = parse(source)
42
+
43
+ # Query for tasks
44
+ tasks = query(doc, type="task")
45
+ for task in tasks:
46
+ print(f"{task.content} -> {task.properties.get('owner', 'unassigned')}")
47
+
48
+ # Validate workflow semantics
49
+ result = validate(doc)
50
+ if not result.valid:
51
+ for issue in result.issues:
52
+ print(f"[{issue.type.upper()}] {issue.message}")
53
+
54
+ # Render to HTML
55
+ html = render_html(doc)
56
+ ```
57
+
58
+ ## API
59
+
60
+ - `parse(source: str) -> IntentDocument`
61
+ - `parse_safe(source: str, ...) -> ParseResult`
62
+ - `render_html(doc: IntentDocument, include_css: bool = True) -> str`
63
+ - `render_print(doc: IntentDocument) -> str`
64
+ - `render_markdown(doc: IntentDocument) -> str`
65
+ - `merge_data(template: IntentDocument, data: dict) -> IntentDocument`
66
+ - `parse_and_merge(template_source: str, data: dict) -> IntentDocument`
67
+ - `validate(doc: IntentDocument) -> ValidationResult`
68
+ - `query(doc: IntentDocument, ...) -> list[IntentBlock]`
69
+ - `to_source(doc: IntentDocument) -> str`
70
+
71
+ ## Development
72
+
73
+ ```bash
74
+ pip install -e .[dev]
75
+ pytest
76
+ ```
77
+
78
+ ## Release (PyPI)
79
+
80
+ ```bash
81
+ # 1) Ensure tests pass
82
+ python3 -m pytest -q
83
+
84
+ # 2) Build source + wheel
85
+ python3 -m pip install -U hatch twine
86
+ hatch build
87
+
88
+ # 3) Validate package metadata and long description
89
+ twine check dist/*
90
+
91
+ # 4) Upload (interactive)
92
+ twine upload dist/*
93
+ ```
94
+
95
+ Tag-based release flow:
96
+
97
+ ```bash
98
+ git tag v1.0.0
99
+ git push origin v1.0.0
100
+ ```
101
+
102
+ GitHub Action publish workflow uses `PYPI_API_TOKEN` for automated release on `v*` tags.
103
+
104
+ ## License
105
+
106
+ MIT
@@ -0,0 +1,92 @@
1
+ # IntentText Python
2
+
3
+ Python implementation of the IntentText parser and renderer.
4
+
5
+ Independent implementation (not a Node.js wrapper), designed for Python workflows and AI stacks.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install intenttext
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```python
16
+ from intenttext import parse, render_html, merge_data, validate, query
17
+
18
+ # Parse a document
19
+ source = """
20
+ title: Sprint Planning
21
+ section: Tasks
22
+ task: Write tests | owner: Ahmed | due: Friday
23
+ task: Deploy to staging | owner: Sarah | due: Monday
24
+ gate: Final approval | approver: Lead | timeout: 24h
25
+ """.strip()
26
+
27
+ doc = parse(source)
28
+
29
+ # Query for tasks
30
+ tasks = query(doc, type="task")
31
+ for task in tasks:
32
+ print(f"{task.content} -> {task.properties.get('owner', 'unassigned')}")
33
+
34
+ # Validate workflow semantics
35
+ result = validate(doc)
36
+ if not result.valid:
37
+ for issue in result.issues:
38
+ print(f"[{issue.type.upper()}] {issue.message}")
39
+
40
+ # Render to HTML
41
+ html = render_html(doc)
42
+ ```
43
+
44
+ ## API
45
+
46
+ - `parse(source: str) -> IntentDocument`
47
+ - `parse_safe(source: str, ...) -> ParseResult`
48
+ - `render_html(doc: IntentDocument, include_css: bool = True) -> str`
49
+ - `render_print(doc: IntentDocument) -> str`
50
+ - `render_markdown(doc: IntentDocument) -> str`
51
+ - `merge_data(template: IntentDocument, data: dict) -> IntentDocument`
52
+ - `parse_and_merge(template_source: str, data: dict) -> IntentDocument`
53
+ - `validate(doc: IntentDocument) -> ValidationResult`
54
+ - `query(doc: IntentDocument, ...) -> list[IntentBlock]`
55
+ - `to_source(doc: IntentDocument) -> str`
56
+
57
+ ## Development
58
+
59
+ ```bash
60
+ pip install -e .[dev]
61
+ pytest
62
+ ```
63
+
64
+ ## Release (PyPI)
65
+
66
+ ```bash
67
+ # 1) Ensure tests pass
68
+ python3 -m pytest -q
69
+
70
+ # 2) Build source + wheel
71
+ python3 -m pip install -U hatch twine
72
+ hatch build
73
+
74
+ # 3) Validate package metadata and long description
75
+ twine check dist/*
76
+
77
+ # 4) Upload (interactive)
78
+ twine upload dist/*
79
+ ```
80
+
81
+ Tag-based release flow:
82
+
83
+ ```bash
84
+ git tag v1.0.0
85
+ git push origin v1.0.0
86
+ ```
87
+
88
+ GitHub Action publish workflow uses `PYPI_API_TOKEN` for automated release on `v*` tags.
89
+
90
+ ## License
91
+
92
+ MIT
@@ -0,0 +1,27 @@
1
+ from intenttext import parse, parse_and_merge, query, render_html, to_source, validate
2
+
3
+ source = """
4
+ title: Sprint Planning
5
+ section: Tasks
6
+ task: Write tests | owner: Ahmed | due: Friday
7
+ task: Deploy to staging | owner: Sarah | due: Monday
8
+ gate: Final approval | approver: Lead | timeout: 24h
9
+ """.strip()
10
+
11
+ doc = parse(source)
12
+
13
+ tasks = query(doc, type="task")
14
+ for task in tasks:
15
+ print(f"{task.content} -> {task.properties.get('owner', 'unassigned')}")
16
+
17
+ validation = validate(doc)
18
+ print("Valid:", validation.valid)
19
+
20
+ html = render_html(doc)
21
+ print("Rendered HTML length:", len(html))
22
+
23
+ merged = parse_and_merge(
24
+ "title: Invoice {{invoice.number}}\nnote: Bill To {{client.name}}",
25
+ {"invoice": {"number": "2026-042"}, "client": {"name": "Acme Corp"}},
26
+ )
27
+ print(to_source(merged))
@@ -0,0 +1,39 @@
1
+ from .merge import merge_data, parse_and_merge
2
+ from .parser import parse, parse_safe
3
+ from .query import query
4
+ from .renderer import render_html, render_markdown, render_print
5
+ from .source import to_source
6
+ from .types import (
7
+ InlineSegment,
8
+ IntentBlock,
9
+ IntentDocument,
10
+ IntentMetadata,
11
+ ParseResult,
12
+ ParseWarning,
13
+ ValidationIssue,
14
+ ValidationResult,
15
+ )
16
+ from .validate import validate
17
+
18
+ __version__ = "1.0.0"
19
+
20
+ __all__ = [
21
+ "parse",
22
+ "parse_safe",
23
+ "render_html",
24
+ "render_print",
25
+ "render_markdown",
26
+ "merge_data",
27
+ "parse_and_merge",
28
+ "validate",
29
+ "query",
30
+ "to_source",
31
+ "IntentDocument",
32
+ "IntentBlock",
33
+ "IntentMetadata",
34
+ "InlineSegment",
35
+ "ParseResult",
36
+ "ParseWarning",
37
+ "ValidationResult",
38
+ "ValidationIssue",
39
+ ]
@@ -0,0 +1,85 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from copy import deepcopy
5
+ from datetime import datetime
6
+ from typing import Any
7
+
8
+ from .parser import parse
9
+ from .types import IntentDocument
10
+
11
+
12
+ def merge_data(template: IntentDocument, data: dict[str, Any]) -> IntentDocument:
13
+ doc = deepcopy(template)
14
+ now = datetime.now()
15
+ system_vars = {
16
+ "timestamp": now.isoformat(),
17
+ "date": now.strftime("%d %B %Y"),
18
+ "year": str(now.year),
19
+ }
20
+ merged_data = {**system_vars, **data}
21
+
22
+ for block in doc.blocks:
23
+ block.content = _resolve_string(block.content, merged_data)
24
+ block.original_content = _resolve_string(block.original_content, merged_data)
25
+ block.properties = {
26
+ k: _resolve_string(v, merged_data) if isinstance(v, str) else v
27
+ for k, v in block.properties.items()
28
+ }
29
+
30
+ _refresh_metadata(doc)
31
+
32
+ return doc
33
+
34
+
35
+ def parse_and_merge(template_source: str, data: dict[str, Any]) -> IntentDocument:
36
+ template = parse(template_source)
37
+ return merge_data(template, data)
38
+
39
+
40
+ def _resolve_string(text: str, data: dict[str, Any]) -> str:
41
+ def replacer(match: re.Match[str]) -> str:
42
+ path = match.group(1).strip()
43
+ if path in ("page", "pages"):
44
+ return match.group(0)
45
+ value = _get_by_path(data, path)
46
+ return str(value) if value is not None else match.group(0)
47
+
48
+ return re.sub(r"\{\{([^}]+)\}\}", replacer, text)
49
+
50
+
51
+ def _get_by_path(obj: Any, path: str) -> Any:
52
+ parts = path.split(".")
53
+ current = obj
54
+
55
+ for part in parts:
56
+ if current is None:
57
+ return None
58
+
59
+ if isinstance(current, list):
60
+ try:
61
+ current = current[int(part)]
62
+ except (ValueError, IndexError):
63
+ return None
64
+ elif isinstance(current, dict):
65
+ current = current.get(part)
66
+ else:
67
+ return None
68
+
69
+ return current
70
+
71
+
72
+ def _refresh_metadata(doc: IntentDocument) -> None:
73
+ for block in doc.blocks:
74
+ if block.type == "title":
75
+ doc.metadata.title = block.content
76
+ elif block.type == "summary":
77
+ doc.metadata.summary = block.content
78
+ elif block.type == "agent":
79
+ doc.metadata.agent = block.content
80
+ if "model" in block.properties:
81
+ doc.metadata.model = str(block.properties["model"])
82
+ elif block.type == "context":
83
+ doc.metadata.context.update(
84
+ {k: str(v) for k, v in block.properties.items()}
85
+ )