from __future__ import annotations import tempfile from pathlib import Path import duckdb from .type_mapper import TypeMapper from expression.transpiler import ExpressionTranspiler class RunContext: def __init__( self, workflow_dir: str, verbose: bool = False, output_dir: str | None = None, params: dict | None = None, ): self.workflow_dir = Path(workflow_dir) self.verbose = verbose self.output_dir: Path | None = Path(output_dir) if output_dir else None self.duckdb_con = duckdb.connect(":memory:") self.temp_dir = Path(tempfile.mkdtemp(prefix="alteryx_runner_")) self.type_mapper = TypeMapper() self.transpiler = ExpressionTranspiler(self.duckdb_con) self.constants: dict = params or {} def resolve_path(self, path: str) -> Path: # Normalise Windows backslashes so relative segments like .. work on # POSIX platforms (workflow XMLs are authored on Windows). path = path.replace("\\", "/") path = path.replace("%temp%", str(self.temp_dir) + "/") path = path.replace("%Desktop%", str(Path.home() / "Desktop") + "/") # Substitute workflow constants for k, v in self.constants.items(): path = path.replace(f"%{k}%", v) p = Path(path) if not p.is_absolute(): p = self.workflow_dir / p # If output_dir override active, remap file-write destinations return p def resolve_output_path(self, path: str) -> Path: p = self.resolve_path(path) if self.output_dir is not None: return self.output_dir / p.name return p def __del__(self): try: self.duckdb_con.close() except Exception: pass