138def transpile_source(pine_source: str, *, filename: str =
"<input>") -> TranspileResult:
139 """Transpile source once and normalize all expected diagnostics.
141 Pine language errors are returned in ``TranspileResult`` rather than being
142 conflated with native compiler failures. Unexpected bridge failures are also
143 represented as an ``INTERNAL`` diagnostic so a CLI can emit one stable JSON
148 transpiler, compile_error = _load_codegen()
149 except ModuleNotFoundError:
155 phase=
"CONFIGURATION",
157 "pineforge-codegen is required to compile PineScript; "
158 "install pineforge-hpo[transpile] or provide a precompiled artifact"
166 payload = transpiler(pine_source, filename=filename)
167 except compile_error
as error:
170 diagnostics=tuple(_diagnostic_from_codegen(d)
for d
in error.diagnostics),
172 except Exception
as error:
179 message=f
"{type(error).__name__}: {error}",
185 if not isinstance(payload, Mapping):
192 message=
"transpile_full returned a non-object result",
198 cpp = payload.get(
"cpp")
199 inputs = payload.get(
"inputs", [])
200 strategy_params = payload.get(
"strategyParams", {})
202 not isinstance(cpp, str)
203 or not isinstance(inputs, list)
204 or not isinstance(strategy_params, Mapping)
212 message=
"transpile_full returned an invalid result shape",
218 normalized_inputs: list[dict[str, Any]] = []
220 if not isinstance(item, Mapping):
227 message=
"transpile_full input manifest contains a non-object entry",
232 normalized_inputs.append(dict(item))
236 inputs=tuple(normalized_inputs),
237 strategy_params=dict(strategy_params),
238 diagnostics=_diagnostics_from_payload(payload),
243 """Return version plus a hash that detects dirty/editable codegen installs."""
245 _transpiler, _compile_error = _load_codegen()
246 assert pineforge_codegen
is not None
249 version = metadata.version(
"pineforge-codegen")
250 except metadata.PackageNotFoundError:
251 version = str(getattr(pineforge_codegen,
"__version__",
"unknown"))
253 module_file = Path(pineforge_codegen.__file__
or "").resolve()
254 package_root = module_file.parent
255 digest = hashlib.sha256()
257 sorted(package_root.rglob(
"*.py"))
if package_root.is_dir()
else [module_file]
260 if not path.is_file():
263 relative = path.relative_to(package_root).as_posix()
266 digest.update(relative.encode(
"utf-8"))
268 digest.update(path.read_bytes())
272 implementation_sha256=digest.hexdigest(),
273 module_path=str(module_file),