agentmark-templatedx 0.1.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.
Files changed (28) hide show
  1. agentmark_templatedx-0.1.0/.gitignore +70 -0
  2. agentmark_templatedx-0.1.0/PKG-INFO +104 -0
  3. agentmark_templatedx-0.1.0/README.md +80 -0
  4. agentmark_templatedx-0.1.0/package.json +11 -0
  5. agentmark_templatedx-0.1.0/pyproject.toml +57 -0
  6. agentmark_templatedx-0.1.0/src/templatedx/__init__.py +58 -0
  7. agentmark_templatedx-0.1.0/src/templatedx/constants.py +21 -0
  8. agentmark_templatedx-0.1.0/src/templatedx/engine.py +155 -0
  9. agentmark_templatedx-0.1.0/src/templatedx/expression.py +763 -0
  10. agentmark_templatedx-0.1.0/src/templatedx/filter_plugins/__init__.py +5 -0
  11. agentmark_templatedx-0.1.0/src/templatedx/filter_plugins/builtin.py +179 -0
  12. agentmark_templatedx-0.1.0/src/templatedx/filter_registry.py +89 -0
  13. agentmark_templatedx-0.1.0/src/templatedx/py.typed +0 -0
  14. agentmark_templatedx-0.1.0/src/templatedx/scope.py +98 -0
  15. agentmark_templatedx-0.1.0/src/templatedx/tag_plugin.py +235 -0
  16. agentmark_templatedx-0.1.0/src/templatedx/tag_plugins/__init__.py +13 -0
  17. agentmark_templatedx-0.1.0/src/templatedx/tag_plugins/conditional.py +113 -0
  18. agentmark_templatedx-0.1.0/src/templatedx/tag_plugins/for_each.py +298 -0
  19. agentmark_templatedx-0.1.0/src/templatedx/tag_plugins/raw.py +32 -0
  20. agentmark_templatedx-0.1.0/src/templatedx/tag_registry.py +91 -0
  21. agentmark_templatedx-0.1.0/src/templatedx/transformer.py +244 -0
  22. agentmark_templatedx-0.1.0/src/templatedx/utils.py +26 -0
  23. agentmark_templatedx-0.1.0/tests/__init__.py +1 -0
  24. agentmark_templatedx-0.1.0/tests/conftest.py +11 -0
  25. agentmark_templatedx-0.1.0/tests/test_expression.py +371 -0
  26. agentmark_templatedx-0.1.0/tests/test_filters.py +186 -0
  27. agentmark_templatedx-0.1.0/tests/test_scope.py +86 -0
  28. agentmark_templatedx-0.1.0/tests/test_transformer.py +424 -0
@@ -0,0 +1,70 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+
27
+ # PyInstaller
28
+ *.manifest
29
+ *.spec
30
+
31
+ # Installer logs
32
+ pip-log.txt
33
+ pip-delete-this-directory.txt
34
+
35
+ # Unit test / coverage reports
36
+ htmlcov/
37
+ .tox/
38
+ .nox/
39
+ .coverage
40
+ .coverage.*
41
+ .cache
42
+ nosetests.xml
43
+ coverage.xml
44
+ *.cover
45
+ *.py,cover
46
+ .hypothesis/
47
+ .pytest_cache/
48
+
49
+ # Environments
50
+ .env
51
+ .venv
52
+ env/
53
+ venv/
54
+ ENV/
55
+ env.bak/
56
+ venv.bak/
57
+
58
+ # mypy
59
+ .mypy_cache/
60
+ .dmypy.json
61
+ dmypy.json
62
+
63
+ # Ruff
64
+ .ruff_cache/
65
+
66
+ # IDE
67
+ .idea/
68
+ .vscode/
69
+ *.swp
70
+ *.swo
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentmark-templatedx
3
+ Version: 0.1.0
4
+ Summary: Python implementation of the AgentMark templatedx transformer
5
+ Project-URL: Homepage, https://github.com/agentmark/agentmark
6
+ Project-URL: Repository, https://github.com/agentmark/agentmark
7
+ Author: AgentMark Team
8
+ License-Expression: MIT
9
+ Keywords: agentmark,mdx,template,templatedx,transformer
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Typing :: Typed
17
+ Requires-Python: >=3.12
18
+ Provides-Extra: dev
19
+ Requires-Dist: mypy>=1.0; extra == 'dev'
20
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
21
+ Requires-Dist: pytest>=7.0; extra == 'dev'
22
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
23
+ Description-Content-Type: text/markdown
24
+
25
+ # AgentMark TemplateDX (Python)
26
+
27
+ Python implementation of the AgentMark templatedx transformer.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install agentmark-templatedx
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ This package transforms pre-parsed MDX AST trees. The AST is typically obtained by:
38
+ - Parsing MDX with the TypeScript `@agentmark-ai/templatedx` package
39
+ - Loading a pre-parsed AST from a JSON file
40
+ - Receiving an AST from the AgentMark runtime
41
+
42
+ ```python
43
+ import asyncio
44
+ import json
45
+ from templatedx import TemplateDX
46
+
47
+ async def main():
48
+ engine = TemplateDX()
49
+
50
+ # Load a pre-parsed MDX AST (from TypeScript parser or JSON file)
51
+ with open("template.ast.json") as f:
52
+ ast = json.load(f)
53
+
54
+ # Transform the AST with props
55
+ result = await engine.transform(
56
+ ast,
57
+ props={"name": "Alice", "items": [1, 2, 3]}
58
+ )
59
+
60
+ print(result)
61
+
62
+ asyncio.run(main())
63
+ ```
64
+
65
+ ## Custom Plugins
66
+
67
+ ```python
68
+ from templatedx import TagPlugin, PluginContext
69
+
70
+ class MyPlugin(TagPlugin):
71
+ async def transform(self, props, children, context):
72
+ # Transform children and return result
73
+ transformer = context.create_node_transformer(context.scope)
74
+ return await transformer.transform_children(children)
75
+
76
+ engine = TemplateDX()
77
+ engine.register_tag_plugin(MyPlugin(), ["MyTag"])
78
+ ```
79
+
80
+ ## Custom Filters
81
+
82
+ ```python
83
+ engine = TemplateDX()
84
+ engine.register_filter("double", lambda x: x * 2)
85
+ ```
86
+
87
+ ## Built-in Filters
88
+
89
+ - `capitalize(str)` - Capitalize first character
90
+ - `upper(str)` - Uppercase string
91
+ - `lower(str)` - Lowercase string
92
+ - `truncate(str, length)` - Truncate with ellipsis
93
+ - `abs(num)` - Absolute value
94
+ - `join(arr, separator)` - Join array elements
95
+ - `round(num, decimals)` - Round number
96
+ - `replace(str, search, replacement)` - Replace occurrences
97
+ - `urlencode(str)` - URL encode string
98
+ - `dump(any)` - JSON stringify
99
+
100
+ ## Built-in Tags
101
+
102
+ - `<If condition={...}>` / `<ElseIf condition={...}>` / `<Else>` - Conditional rendering
103
+ - `<ForEach arr={...}>{(item, index) => ...}</ForEach>` - Array iteration
104
+ - `<Raw>...</Raw>` - Raw content passthrough
@@ -0,0 +1,80 @@
1
+ # AgentMark TemplateDX (Python)
2
+
3
+ Python implementation of the AgentMark templatedx transformer.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install agentmark-templatedx
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ This package transforms pre-parsed MDX AST trees. The AST is typically obtained by:
14
+ - Parsing MDX with the TypeScript `@agentmark-ai/templatedx` package
15
+ - Loading a pre-parsed AST from a JSON file
16
+ - Receiving an AST from the AgentMark runtime
17
+
18
+ ```python
19
+ import asyncio
20
+ import json
21
+ from templatedx import TemplateDX
22
+
23
+ async def main():
24
+ engine = TemplateDX()
25
+
26
+ # Load a pre-parsed MDX AST (from TypeScript parser or JSON file)
27
+ with open("template.ast.json") as f:
28
+ ast = json.load(f)
29
+
30
+ # Transform the AST with props
31
+ result = await engine.transform(
32
+ ast,
33
+ props={"name": "Alice", "items": [1, 2, 3]}
34
+ )
35
+
36
+ print(result)
37
+
38
+ asyncio.run(main())
39
+ ```
40
+
41
+ ## Custom Plugins
42
+
43
+ ```python
44
+ from templatedx import TagPlugin, PluginContext
45
+
46
+ class MyPlugin(TagPlugin):
47
+ async def transform(self, props, children, context):
48
+ # Transform children and return result
49
+ transformer = context.create_node_transformer(context.scope)
50
+ return await transformer.transform_children(children)
51
+
52
+ engine = TemplateDX()
53
+ engine.register_tag_plugin(MyPlugin(), ["MyTag"])
54
+ ```
55
+
56
+ ## Custom Filters
57
+
58
+ ```python
59
+ engine = TemplateDX()
60
+ engine.register_filter("double", lambda x: x * 2)
61
+ ```
62
+
63
+ ## Built-in Filters
64
+
65
+ - `capitalize(str)` - Capitalize first character
66
+ - `upper(str)` - Uppercase string
67
+ - `lower(str)` - Lowercase string
68
+ - `truncate(str, length)` - Truncate with ellipsis
69
+ - `abs(num)` - Absolute value
70
+ - `join(arr, separator)` - Join array elements
71
+ - `round(num, decimals)` - Round number
72
+ - `replace(str, search, replacement)` - Replace occurrences
73
+ - `urlencode(str)` - URL encode string
74
+ - `dump(any)` - JSON stringify
75
+
76
+ ## Built-in Tags
77
+
78
+ - `<If condition={...}>` / `<ElseIf condition={...}>` / `<Else>` - Conditional rendering
79
+ - `<ForEach arr={...}>{(item, index) => ...}</ForEach>` - Array iteration
80
+ - `<Raw>...</Raw>` - Raw content passthrough
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@agentmark-ai/templatedx-python",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "postinstall": "node ../../scripts/setup-python-venv.js -e \".[dev]\"",
7
+ "test": "node ../../scripts/run-venv.js pytest tests/",
8
+ "lint": "node ../../scripts/run-venv.js ruff check src/ tests/ && node ../../scripts/run-venv.js mypy src --strict",
9
+ "lint:fix": "node ../../scripts/run-venv.js ruff check --fix src/ tests/"
10
+ }
11
+ }
@@ -0,0 +1,57 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "agentmark-templatedx"
7
+ version = "0.1.0"
8
+ description = "Python implementation of the AgentMark templatedx transformer"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.12"
12
+ authors = [
13
+ { name = "AgentMark Team" }
14
+ ]
15
+ keywords = ["agentmark", "templatedx", "mdx", "transformer", "template"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Typing :: Typed",
24
+ ]
25
+ dependencies = []
26
+
27
+ [project.optional-dependencies]
28
+ dev = [
29
+ "pytest>=7.0",
30
+ "pytest-asyncio>=0.21",
31
+ "ruff>=0.1.0",
32
+ "mypy>=1.0",
33
+ ]
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/agentmark/agentmark"
37
+ Repository = "https://github.com/agentmark/agentmark"
38
+
39
+ [tool.hatch.build.targets.wheel]
40
+ packages = ["src/templatedx"]
41
+
42
+ [tool.pytest.ini_options]
43
+ asyncio_mode = "auto"
44
+ testpaths = ["tests"]
45
+
46
+ [tool.ruff]
47
+ target-version = "py312"
48
+ line-length = 100
49
+
50
+ [tool.ruff.lint]
51
+ select = ["E", "F", "I", "N", "W", "UP"]
52
+
53
+ [tool.mypy]
54
+ python_version = "3.12"
55
+ strict = true
56
+ warn_return_any = true
57
+ warn_unused_ignores = true
@@ -0,0 +1,58 @@
1
+ """TemplateDX - Python implementation of the AgentMark templatedx transformer.
2
+
3
+ This package provides a Python implementation of the templatedx transformer
4
+ for processing AgentMark MDX AST trees.
5
+
6
+ Example:
7
+ ```python
8
+ import asyncio
9
+ from templatedx import TemplateDX
10
+
11
+ async def main():
12
+ engine = TemplateDX()
13
+ result = await engine.transform(
14
+ ast,
15
+ props={"name": "Alice"}
16
+ )
17
+ print(result)
18
+
19
+ asyncio.run(main())
20
+ ```
21
+ """
22
+
23
+ from .constants import MDX_JSX_ATTRIBUTE_TYPES, NODE_TYPES
24
+ from .engine import TemplateDX
25
+ from .expression import EvaluationError, ExpressionEvaluator, LexerError, ParseError
26
+ from .filter_registry import FilterRegistry
27
+ from .scope import Scope
28
+ from .tag_plugin import Node, NodeHelpers, PluginContext, TagPlugin
29
+ from .tag_registry import TagPluginRegistry
30
+ from .transformer import NodeTransformer, transform_tree
31
+
32
+ __version__ = "0.1.0"
33
+
34
+ __all__ = [
35
+ # Main engine
36
+ "TemplateDX",
37
+ # Core classes
38
+ "NodeTransformer",
39
+ "Scope",
40
+ "TagPlugin",
41
+ "PluginContext",
42
+ "NodeHelpers",
43
+ # Registries
44
+ "TagPluginRegistry",
45
+ "FilterRegistry",
46
+ # Expression evaluation
47
+ "ExpressionEvaluator",
48
+ "LexerError",
49
+ "ParseError",
50
+ "EvaluationError",
51
+ # Constants
52
+ "NODE_TYPES",
53
+ "MDX_JSX_ATTRIBUTE_TYPES",
54
+ # Types
55
+ "Node",
56
+ # Convenience functions
57
+ "transform_tree",
58
+ ]
@@ -0,0 +1,21 @@
1
+ """Constants for node types and attribute types."""
2
+
3
+ NODE_TYPES = {
4
+ "MDX_JSX_FLOW_ELEMENT": "mdxJsxFlowElement",
5
+ "MDX_JSX_TEXT_ELEMENT": "mdxJsxTextElement",
6
+ "MDX_JSX_ESM": "mdxjsEsm",
7
+ "YAML": "yaml",
8
+ "MDX_TEXT_EXPRESSION": "mdxTextExpression",
9
+ "MDX_FLOW_EXPRESSION": "mdxFlowExpression",
10
+ "LIST": "list",
11
+ "LIST_ITEM": "listItem",
12
+ "TEXT": "text",
13
+ "PARAGRAPH": "paragraph",
14
+ "HTML": "html",
15
+ }
16
+
17
+ MDX_JSX_ATTRIBUTE_TYPES = {
18
+ "MDX_JSX_ATTRIBUTE": "mdxJsxAttribute",
19
+ "MDX_JSX_ATTRIBUTE_VALUE_EXPRESSION": "mdxJsxAttributeValueExpression",
20
+ "MDX_JSX_EXPRESSION_ATTRIBUTE": "mdxJsxExpressionAttribute",
21
+ }
@@ -0,0 +1,155 @@
1
+ """TemplateDX engine - main entry point for the templatedx transformer."""
2
+
3
+ from collections.abc import Callable
4
+ from typing import Any
5
+
6
+ from .filter_plugins import register_builtin_filters
7
+ from .filter_registry import FilterRegistry
8
+ from .scope import Scope
9
+ from .tag_plugin import Node, TagPlugin
10
+ from .tag_plugins import ElseIfPlugin, ElsePlugin, ForEachPlugin, IfPlugin, RawPlugin
11
+ from .tag_registry import TagPluginRegistry
12
+ from .transformer import NodeTransformer
13
+
14
+
15
+ def _register_builtin_tag_plugins() -> None:
16
+ """Register all built-in tag plugins globally."""
17
+ TagPluginRegistry.register_global(IfPlugin(), ["If"])
18
+ TagPluginRegistry.register_global(ElseIfPlugin(), ["ElseIf"])
19
+ TagPluginRegistry.register_global(ElsePlugin(), ["Else"])
20
+ TagPluginRegistry.register_global(ForEachPlugin(), ["ForEach"])
21
+ TagPluginRegistry.register_global(RawPlugin(), ["Raw"])
22
+
23
+
24
+ # Register built-in plugins on module load
25
+ _register_builtin_tag_plugins()
26
+ register_builtin_filters()
27
+
28
+
29
+ class TemplateDX:
30
+ """Stateful TemplateDX engine with isolated plugin registries.
31
+
32
+ This is the main entry point for using templatedx. It provides
33
+ instance-level plugin registries that inherit from global registries.
34
+
35
+ Example:
36
+ ```python
37
+ engine = TemplateDX()
38
+
39
+ # Transform an AST
40
+ result = await engine.transform(ast, props={"name": "Alice"})
41
+
42
+ # Register custom plugins
43
+ engine.register_tag_plugin(MyPlugin(), ["MyTag"])
44
+ engine.register_filter("double", lambda x: x * 2)
45
+ ```
46
+ """
47
+
48
+ def __init__(self) -> None:
49
+ """Initialize a new TemplateDX engine.
50
+
51
+ Creates instance-level registries that inherit from global registries.
52
+ """
53
+ self._tag_registry = TagPluginRegistry()
54
+ self._filter_registry = FilterRegistry()
55
+
56
+ # Copy built-in plugins to instance
57
+ self._tag_registry.copy_from_global()
58
+ self._filter_registry.copy_from_global()
59
+
60
+ def register_tag_plugin(self, plugin: TagPlugin, names: list[str]) -> None:
61
+ """Register a tag plugin on this instance.
62
+
63
+ Args:
64
+ plugin: The tag plugin instance
65
+ names: List of tag names this plugin handles
66
+ """
67
+ self._tag_registry.register(plugin, names)
68
+
69
+ def remove_tag_plugin(self, name: str) -> None:
70
+ """Remove a tag plugin from this instance.
71
+
72
+ Args:
73
+ name: Tag name to remove
74
+ """
75
+ self._tag_registry.remove(name)
76
+
77
+ def get_tag_plugin(self, name: str) -> TagPlugin | None:
78
+ """Get a tag plugin by name.
79
+
80
+ Args:
81
+ name: Tag name
82
+
83
+ Returns:
84
+ The plugin, or None if not found
85
+ """
86
+ return self._tag_registry.get(name)
87
+
88
+ def get_tag_registry(self) -> TagPluginRegistry:
89
+ """Get the tag plugin registry.
90
+
91
+ Returns:
92
+ The tag plugin registry for this instance
93
+ """
94
+ return self._tag_registry
95
+
96
+ def register_filter(self, name: str, func: Callable[..., Any]) -> None:
97
+ """Register a filter function on this instance.
98
+
99
+ Args:
100
+ name: Filter name
101
+ func: Filter function
102
+ """
103
+ self._filter_registry.register(name, func)
104
+
105
+ def remove_filter(self, name: str) -> None:
106
+ """Remove a filter from this instance.
107
+
108
+ Args:
109
+ name: Filter name to remove
110
+ """
111
+ self._filter_registry.remove(name)
112
+
113
+ def get_filter(self, name: str) -> Callable[..., Any] | None:
114
+ """Get a filter function by name.
115
+
116
+ Args:
117
+ name: Filter name
118
+
119
+ Returns:
120
+ The filter function, or None if not found
121
+ """
122
+ return self._filter_registry.get(name)
123
+
124
+ def get_filter_registry(self) -> FilterRegistry:
125
+ """Get the filter registry.
126
+
127
+ Returns:
128
+ The filter registry for this instance
129
+ """
130
+ return self._filter_registry
131
+
132
+ async def transform(
133
+ self,
134
+ tree: Node,
135
+ props: dict[str, Any] | None = None,
136
+ shared: dict[str, Any] | None = None,
137
+ ) -> Node:
138
+ """Transform an AST tree with the given props.
139
+
140
+ Note: Props are wrapped as {"props": props} to match TS behavior.
141
+ Templates access variables as `props.name`, not just `name`.
142
+
143
+ Args:
144
+ tree: Root AST node (pre-parsed MDX AST as dict)
145
+ props: Props to pass to the template
146
+ shared: Shared/global context accessible from all scopes
147
+
148
+ Returns:
149
+ Transformed AST tree
150
+ """
151
+ # Wrap props to match TypeScript behavior
152
+ variables = {"props": props if props is not None else {}}
153
+ scope = Scope(variables=variables, shared=shared if shared is not None else {})
154
+ transformer = NodeTransformer(scope, self)
155
+ return await transformer.transform(tree)