plating 0.0.0.dev0__py3-none-any.whl
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.
- plating/__init__.py +27 -0
- plating/_version.py +53 -0
- plating/adorner/__init__.py +16 -0
- plating/adorner/adorner.py +115 -0
- plating/adorner/api.py +24 -0
- plating/adorner/finder.py +22 -0
- plating/adorner/templates.py +199 -0
- plating/cli.py +317 -0
- plating/config.py +106 -0
- plating/error_handling.py +125 -0
- plating/errors.py +142 -0
- plating/generator.py +192 -0
- plating/linting.py +235 -0
- plating/models.py +75 -0
- plating/plater.py +487 -0
- plating/plating.py +207 -0
- plating/schema.py +502 -0
- plating/template_filters.py +117 -0
- plating/template_functions.py +216 -0
- plating/templates.py +232 -0
- plating/test_runner.py +1005 -0
- plating/types.py +71 -0
- plating-0.0.0.dev0.dist-info/METADATA +223 -0
- plating-0.0.0.dev0.dist-info/RECORD +28 -0
- plating-0.0.0.dev0.dist-info/WHEEL +5 -0
- plating-0.0.0.dev0.dist-info/entry_points.txt +2 -0
- plating-0.0.0.dev0.dist-info/licenses/LICENSE +201 -0
- plating-0.0.0.dev0.dist-info/top_level.txt +1 -0
plating/generator.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#
|
|
2
|
+
# plating/generator.py
|
|
3
|
+
#
|
|
4
|
+
"""Main documentation generator class and entry point."""
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from provide.foundation import pout
|
|
9
|
+
|
|
10
|
+
from plating.plating import PlatingDiscovery
|
|
11
|
+
from plating.models import FunctionInfo, ProviderInfo, ResourceInfo
|
|
12
|
+
from plating.schema import SchemaProcessor
|
|
13
|
+
from plating.templates import TemplateProcessor
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DocsGenerator:
|
|
17
|
+
"""Main documentation generator class."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
provider_dir: Path,
|
|
22
|
+
provider_name: str | None = None,
|
|
23
|
+
rendered_provider_name: str | None = None,
|
|
24
|
+
examples_dir: str = "examples",
|
|
25
|
+
templates_dir: str = "templates",
|
|
26
|
+
output_dir: str = "docs",
|
|
27
|
+
ignore_deprecated: bool = False,
|
|
28
|
+
):
|
|
29
|
+
self.provider_dir = Path(provider_dir).resolve()
|
|
30
|
+
self.examples_dir = self.provider_dir / examples_dir
|
|
31
|
+
self.templates_dir = self.provider_dir / templates_dir
|
|
32
|
+
self.output_dir = self.provider_dir / output_dir
|
|
33
|
+
self.ignore_deprecated = ignore_deprecated
|
|
34
|
+
|
|
35
|
+
# Determine provider name
|
|
36
|
+
if provider_name:
|
|
37
|
+
self.provider_name = provider_name
|
|
38
|
+
else:
|
|
39
|
+
# Extract from directory name, removing terraform-provider- prefix
|
|
40
|
+
dir_name = self.provider_dir.name
|
|
41
|
+
if dir_name.startswith("terraform-provider-"):
|
|
42
|
+
self.provider_name = dir_name[19:] # Remove "terraform-provider-"
|
|
43
|
+
else:
|
|
44
|
+
self.provider_name = dir_name
|
|
45
|
+
|
|
46
|
+
self.rendered_provider_name = rendered_provider_name or self.provider_name
|
|
47
|
+
|
|
48
|
+
# Internal state
|
|
49
|
+
self.provider_schema = None
|
|
50
|
+
self.provider_info: ProviderInfo | None = None
|
|
51
|
+
self.resources: dict[str, ResourceInfo] = {}
|
|
52
|
+
self.data_sources: dict[str, ResourceInfo] = {}
|
|
53
|
+
self.functions: dict[str, FunctionInfo] = {}
|
|
54
|
+
|
|
55
|
+
# Initialize processors
|
|
56
|
+
self.schema_processor = SchemaProcessor(self)
|
|
57
|
+
self.template_processor = TemplateProcessor(self)
|
|
58
|
+
self.plating_discovery = PlatingDiscovery()
|
|
59
|
+
|
|
60
|
+
def process_examples(self):
|
|
61
|
+
"""Process example files and associate them with resources/data sources."""
|
|
62
|
+
if not self.examples_dir.exists():
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
# Process provider examples
|
|
66
|
+
provider_example = None
|
|
67
|
+
if (self.examples_dir / "provider" / "provider.tf").exists():
|
|
68
|
+
provider_example = (
|
|
69
|
+
self.examples_dir / "provider" / "provider.tf"
|
|
70
|
+
).read_text()
|
|
71
|
+
elif (self.examples_dir / "provider.tf").exists():
|
|
72
|
+
provider_example = (self.examples_dir / "provider.tf").read_text()
|
|
73
|
+
|
|
74
|
+
if provider_example and self.provider_info:
|
|
75
|
+
self.provider_info.has_example = True
|
|
76
|
+
self.provider_info.example_file = provider_example
|
|
77
|
+
|
|
78
|
+
# Process resource examples
|
|
79
|
+
for resource_name, resource_info in self.resources.items():
|
|
80
|
+
example_patterns = [
|
|
81
|
+
self.examples_dir / "resources" / f"{resource_name}" / "resource.tf",
|
|
82
|
+
self.examples_dir / "resources" / f"{resource_name}.tf",
|
|
83
|
+
self.examples_dir / f"{resource_name}.tf",
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
for pattern in example_patterns:
|
|
87
|
+
if pattern.exists():
|
|
88
|
+
resource_info.has_example = True
|
|
89
|
+
resource_info.example_file = pattern.read_text()
|
|
90
|
+
break
|
|
91
|
+
|
|
92
|
+
# Process import examples
|
|
93
|
+
import_patterns = [
|
|
94
|
+
self.examples_dir / "resources" / f"{resource_name}" / "import.sh",
|
|
95
|
+
self.examples_dir / "resources" / f"{resource_name}_import.sh",
|
|
96
|
+
self.examples_dir / f"{resource_name}_import.sh",
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
for pattern in import_patterns:
|
|
100
|
+
if pattern.exists():
|
|
101
|
+
resource_info.has_import = True
|
|
102
|
+
resource_info.import_file = pattern.read_text()
|
|
103
|
+
break
|
|
104
|
+
|
|
105
|
+
# Process data source examples
|
|
106
|
+
for ds_name, ds_info in self.data_sources.items():
|
|
107
|
+
example_patterns = [
|
|
108
|
+
self.examples_dir / "data-sources" / f"{ds_name}" / "data-source.tf",
|
|
109
|
+
self.examples_dir / "data-sources" / f"{ds_name}.tf",
|
|
110
|
+
self.examples_dir / f"{ds_name}.tf",
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
for pattern in example_patterns:
|
|
114
|
+
if pattern.exists():
|
|
115
|
+
ds_info.has_example = True
|
|
116
|
+
ds_info.example_file = pattern.read_text()
|
|
117
|
+
break
|
|
118
|
+
|
|
119
|
+
# Process function examples
|
|
120
|
+
for func_name, func_info in self.functions.items():
|
|
121
|
+
example_patterns = [
|
|
122
|
+
self.examples_dir / "functions" / f"{func_name}" / "function.tf",
|
|
123
|
+
self.examples_dir / "functions" / f"{func_name}.tf",
|
|
124
|
+
self.examples_dir / f"{func_name}.tf",
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
for pattern in example_patterns:
|
|
128
|
+
if pattern.exists():
|
|
129
|
+
func_info.has_example = True
|
|
130
|
+
func_info.example_file = pattern.read_text()
|
|
131
|
+
break
|
|
132
|
+
|
|
133
|
+
def generate(self):
|
|
134
|
+
"""Generate documentation for the provider."""
|
|
135
|
+
pout(f"🔍 Generating documentation for {self.provider_name} provider...")
|
|
136
|
+
|
|
137
|
+
# Extract provider schema
|
|
138
|
+
pout("📋 Extracting provider schema...")
|
|
139
|
+
self.provider_schema = self.schema_processor.extract_provider_schema()
|
|
140
|
+
|
|
141
|
+
# Parse schema into our internal structures
|
|
142
|
+
self.schema_processor.parse_provider_schema()
|
|
143
|
+
|
|
144
|
+
# Process examples
|
|
145
|
+
pout("📁 Processing examples...")
|
|
146
|
+
self.process_examples()
|
|
147
|
+
|
|
148
|
+
# Generate missing templates
|
|
149
|
+
pout("📄 Generating missing templates...")
|
|
150
|
+
self.template_processor.generate_missing_templates()
|
|
151
|
+
|
|
152
|
+
# Render templates
|
|
153
|
+
pout("🎨 Rendering templates...")
|
|
154
|
+
self.template_processor.render_templates()
|
|
155
|
+
|
|
156
|
+
pout(f"✅ Documentation generated successfully in {self.output_dir}")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def generate_docs(
|
|
160
|
+
provider_dir: Path = Path(),
|
|
161
|
+
provider_name: str | None = None,
|
|
162
|
+
rendered_provider_name: str | None = None,
|
|
163
|
+
examples_dir: str = "examples",
|
|
164
|
+
templates_dir: str = "templates",
|
|
165
|
+
output_dir: str = "docs",
|
|
166
|
+
ignore_deprecated: bool = False,
|
|
167
|
+
) -> None:
|
|
168
|
+
"""Generate documentation for a Pyvider provider.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
provider_dir: Path to the provider directory
|
|
172
|
+
provider_name: Name of the provider (auto-detected if None)
|
|
173
|
+
rendered_provider_name: Display name for the provider
|
|
174
|
+
examples_dir: Directory containing example files
|
|
175
|
+
templates_dir: Directory containing template files
|
|
176
|
+
output_dir: Directory to output generated documentation
|
|
177
|
+
ignore_deprecated: Whether to skip deprecated resources
|
|
178
|
+
"""
|
|
179
|
+
generator = DocsGenerator(
|
|
180
|
+
provider_dir=provider_dir,
|
|
181
|
+
provider_name=provider_name,
|
|
182
|
+
rendered_provider_name=rendered_provider_name,
|
|
183
|
+
examples_dir=examples_dir,
|
|
184
|
+
templates_dir=templates_dir,
|
|
185
|
+
output_dir=output_dir,
|
|
186
|
+
ignore_deprecated=ignore_deprecated,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
generator.generate()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# 🍲🥄📄🪄
|
plating/linting.py
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
#
|
|
2
|
+
# plating/linting.py
|
|
3
|
+
#
|
|
4
|
+
"""Markdown linting integration for documentation generation."""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import subprocess
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MarkdownLinter:
|
|
12
|
+
"""Handles markdown linting for generated documentation."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, config_file: Path | None = None):
|
|
15
|
+
self.config_file = config_file
|
|
16
|
+
|
|
17
|
+
def lint_templates(self, template_dir: Path) -> tuple[bool, list[dict]]:
|
|
18
|
+
"""Lint template files before generation.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
template_dir: Directory containing template files
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Tuple of (success, errors) where errors is a list of error dictionaries
|
|
25
|
+
"""
|
|
26
|
+
return self._run_markdownlint(f"{template_dir}/**/*.tmpl.md")
|
|
27
|
+
|
|
28
|
+
def lint_generated_docs(self, output_dir: Path) -> tuple[bool, list[dict]]:
|
|
29
|
+
"""Lint generated documentation files.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
output_dir: Directory containing generated markdown files
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Tuple of (success, errors) where errors is a list of error dictionaries
|
|
36
|
+
"""
|
|
37
|
+
return self._run_markdownlint(f"{output_dir}/**/*.md")
|
|
38
|
+
|
|
39
|
+
def auto_fix_templates(self, template_dir: Path) -> bool:
|
|
40
|
+
"""Attempt to auto-fix template linting issues.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
template_dir: Directory containing template files
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
True if fixes were applied successfully
|
|
47
|
+
"""
|
|
48
|
+
return self._run_markdownlint_fix(f"{template_dir}/**/*.tmpl.md")
|
|
49
|
+
|
|
50
|
+
def auto_fix_generated_docs(self, output_dir: Path) -> bool:
|
|
51
|
+
"""Attempt to auto-fix generated documentation linting issues.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
output_dir: Directory containing generated markdown files
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
True if fixes were applied successfully
|
|
58
|
+
"""
|
|
59
|
+
return self._run_markdownlint_fix(f"{output_dir}/**/*.md")
|
|
60
|
+
|
|
61
|
+
def _run_markdownlint(self, pattern: str) -> tuple[bool, list[dict]]:
|
|
62
|
+
"""Run markdownlint-cli2 on files matching pattern.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
pattern: Glob pattern for files to lint
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Tuple of (success, errors)
|
|
69
|
+
"""
|
|
70
|
+
cmd = ["markdownlint-cli2", pattern]
|
|
71
|
+
if self.config_file:
|
|
72
|
+
cmd.extend(["--config", str(self.config_file)])
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
|
76
|
+
|
|
77
|
+
errors = []
|
|
78
|
+
if result.returncode != 0:
|
|
79
|
+
# Parse error output
|
|
80
|
+
for line in result.stderr.split("\n"):
|
|
81
|
+
if line.strip() and ":" in line:
|
|
82
|
+
parts = line.split(":", 3)
|
|
83
|
+
if len(parts) >= 4:
|
|
84
|
+
errors.append(
|
|
85
|
+
{
|
|
86
|
+
"file": parts[0],
|
|
87
|
+
"line": parts[1] if parts[1].isdigit() else None,
|
|
88
|
+
"column": parts[2] if parts[2].isdigit() else None,
|
|
89
|
+
"rule": parts[3].split()[0]
|
|
90
|
+
if parts[3]
|
|
91
|
+
else "unknown",
|
|
92
|
+
"message": parts[3]
|
|
93
|
+
if parts[3]
|
|
94
|
+
else "Unknown error",
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return result.returncode == 0, errors
|
|
99
|
+
|
|
100
|
+
except FileNotFoundError:
|
|
101
|
+
raise RuntimeError(
|
|
102
|
+
"markdownlint-cli2 not found. Install with: npm install -g markdownlint-cli2"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def _run_markdownlint_fix(self, pattern: str) -> bool:
|
|
106
|
+
"""Run markdownlint-cli2 with --fix flag.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
pattern: Glob pattern for files to fix
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
True if fixes were applied successfully
|
|
113
|
+
"""
|
|
114
|
+
cmd = ["markdownlint-cli2", "--fix", pattern]
|
|
115
|
+
if self.config_file:
|
|
116
|
+
cmd.extend(["--config", str(self.config_file)])
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
|
120
|
+
|
|
121
|
+
return result.returncode == 0
|
|
122
|
+
|
|
123
|
+
except FileNotFoundError:
|
|
124
|
+
raise RuntimeError(
|
|
125
|
+
"markdownlint-cli2 not found. Install with: npm install -g markdownlint-cli2"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
def generate_lint_report(self, errors: list[dict], output_file: Path) -> None:
|
|
129
|
+
"""Generate a JSON report of linting errors for CI/CD integration.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
errors: List of error dictionaries from linting
|
|
133
|
+
output_file: Path to write JSON report
|
|
134
|
+
"""
|
|
135
|
+
report = {
|
|
136
|
+
"total_errors": len(errors),
|
|
137
|
+
"errors_by_rule": {},
|
|
138
|
+
"errors_by_file": {},
|
|
139
|
+
"errors": errors,
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# Group errors by rule
|
|
143
|
+
for error in errors:
|
|
144
|
+
rule = error.get("rule", "unknown")
|
|
145
|
+
if rule not in report["errors_by_rule"]:
|
|
146
|
+
report["errors_by_rule"][rule] = 0
|
|
147
|
+
report["errors_by_rule"][rule] += 1
|
|
148
|
+
|
|
149
|
+
# Group errors by file
|
|
150
|
+
for error in errors:
|
|
151
|
+
file_path = error.get("file", "unknown")
|
|
152
|
+
if file_path not in report["errors_by_file"]:
|
|
153
|
+
report["errors_by_file"][file_path] = 0
|
|
154
|
+
report["errors_by_file"][file_path] += 1
|
|
155
|
+
|
|
156
|
+
with open(output_file, "w") as f:
|
|
157
|
+
json.dump(report, f, indent=2)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def apply_markdown_fixes(content: str) -> str:
|
|
161
|
+
"""Apply common markdown fixes to content.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
content: Markdown content to fix
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
Fixed markdown content
|
|
168
|
+
"""
|
|
169
|
+
# Ensure trailing newline
|
|
170
|
+
content = content.rstrip() + "\n"
|
|
171
|
+
|
|
172
|
+
# Fix list marker spacing (convert double spaces to single)
|
|
173
|
+
import re
|
|
174
|
+
|
|
175
|
+
content = re.sub(r"^(\s*)- ", r"\1- ", content, flags=re.MULTILINE)
|
|
176
|
+
content = re.sub(r"^(\s*)\d+\. ", r"\1\d+. ", content, flags=re.MULTILINE)
|
|
177
|
+
|
|
178
|
+
# Add blank lines around headings
|
|
179
|
+
content = re.sub(r"\n(#{1,6}\s+.*)\n(?!\n)", r"\n\1\n\n", content)
|
|
180
|
+
content = re.sub(r"(?<!\n)\n(#{1,6}\s+.*)\n", r"\n\n\1\n", content)
|
|
181
|
+
|
|
182
|
+
# Add blank lines around fenced code blocks
|
|
183
|
+
content = re.sub(r"\n```(\w*)\n(?!\n)", r"\n\n```\1\n", content)
|
|
184
|
+
content = re.sub(r"(?<!\n)\n```(\w*)\n", r"\n\n```\1\n", content)
|
|
185
|
+
content = re.sub(r"\n```\n(?!\n)", r"\n```\n\n", content)
|
|
186
|
+
content = re.sub(r"(?<!\n)\n```\n", r"\n\n```\n", content)
|
|
187
|
+
|
|
188
|
+
return content
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def break_long_lines(content: str, max_length: int = 100) -> str:
|
|
192
|
+
"""Break long lines at word boundaries.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
content: Markdown content
|
|
196
|
+
max_length: Maximum line length
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
Content with lines broken at word boundaries
|
|
200
|
+
"""
|
|
201
|
+
lines = content.split("\n")
|
|
202
|
+
fixed_lines = []
|
|
203
|
+
|
|
204
|
+
for line in lines:
|
|
205
|
+
if len(line) <= max_length:
|
|
206
|
+
fixed_lines.append(line)
|
|
207
|
+
continue
|
|
208
|
+
|
|
209
|
+
# Skip code blocks and headings
|
|
210
|
+
if line.startswith("```") or line.startswith("#"):
|
|
211
|
+
fixed_lines.append(line)
|
|
212
|
+
continue
|
|
213
|
+
|
|
214
|
+
# Break at word boundaries
|
|
215
|
+
words = line.split(" ")
|
|
216
|
+
current_line = ""
|
|
217
|
+
|
|
218
|
+
for word in words:
|
|
219
|
+
if len(current_line + " " + word) <= max_length:
|
|
220
|
+
if current_line:
|
|
221
|
+
current_line += " " + word
|
|
222
|
+
else:
|
|
223
|
+
current_line = word
|
|
224
|
+
else:
|
|
225
|
+
if current_line:
|
|
226
|
+
fixed_lines.append(current_line)
|
|
227
|
+
current_line = word
|
|
228
|
+
|
|
229
|
+
if current_line:
|
|
230
|
+
fixed_lines.append(current_line)
|
|
231
|
+
|
|
232
|
+
return "\n".join(fixed_lines)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# 🍲🥄📄🪄
|
plating/models.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#
|
|
2
|
+
# plating/models.py
|
|
3
|
+
#
|
|
4
|
+
"""Data models for documentation generation."""
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from attrs import define, field
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@define
|
|
12
|
+
class ProviderInfo:
|
|
13
|
+
"""Information about a Pyvider provider."""
|
|
14
|
+
|
|
15
|
+
name: str
|
|
16
|
+
description: str
|
|
17
|
+
short_name: str = ""
|
|
18
|
+
rendered_name: str = ""
|
|
19
|
+
schema_markdown: str = ""
|
|
20
|
+
has_example: bool = False
|
|
21
|
+
example_file: str = ""
|
|
22
|
+
|
|
23
|
+
def __attrs_post_init__(self):
|
|
24
|
+
if not self.short_name:
|
|
25
|
+
self.short_name = self.name
|
|
26
|
+
if not self.rendered_name:
|
|
27
|
+
self.rendered_name = self.name
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@define
|
|
31
|
+
class ResourceInfo:
|
|
32
|
+
"""Information about a resource or data source."""
|
|
33
|
+
|
|
34
|
+
name: str
|
|
35
|
+
type: str # "Resource", "Data Source", or "Function"
|
|
36
|
+
description: str
|
|
37
|
+
schema_markdown: str = ""
|
|
38
|
+
schema: dict[str, Any] | None = None
|
|
39
|
+
has_example: bool = False
|
|
40
|
+
example_file: str = ""
|
|
41
|
+
has_import: bool = False
|
|
42
|
+
import_file: str = ""
|
|
43
|
+
has_import_id_config: bool = False
|
|
44
|
+
import_id_config_file: str = ""
|
|
45
|
+
has_import_identity_config: bool = False
|
|
46
|
+
import_identity_config_file: str = ""
|
|
47
|
+
# Co-located documentation fields
|
|
48
|
+
examples: dict[str, str] = field(factory=dict)
|
|
49
|
+
import_docs: str = ""
|
|
50
|
+
colocated_notes: str = ""
|
|
51
|
+
migration: str = ""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@define
|
|
55
|
+
class FunctionInfo:
|
|
56
|
+
"""Information about a provider-defined function."""
|
|
57
|
+
|
|
58
|
+
name: str
|
|
59
|
+
type: str = "Function"
|
|
60
|
+
description: str = ""
|
|
61
|
+
summary: str = ""
|
|
62
|
+
has_example: bool = False
|
|
63
|
+
example_file: str = ""
|
|
64
|
+
signature_markdown: str = ""
|
|
65
|
+
arguments_markdown: str = ""
|
|
66
|
+
has_variadic: bool = False
|
|
67
|
+
variadic_argument_markdown: str = ""
|
|
68
|
+
# Co-located documentation fields
|
|
69
|
+
examples: dict[str, str] = field(factory=dict)
|
|
70
|
+
import_docs: str = ""
|
|
71
|
+
colocated_notes: str = ""
|
|
72
|
+
migration: str = ""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# 🍲🥄📊🪄
|