Skip to content

Commit 0699365

Browse files
authored
feat: support custom root and update type stub (#154)
1 parent 16143e8 commit 0699365

3 files changed

Lines changed: 46 additions & 4 deletions

File tree

python/typst/__init__.pyi

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ class Compiler:
108108
format: Optional[OutputFormat] = None,
109109
ppi: Optional[float] = None,
110110
sys_inputs: Union[EllipsisType, None, Dict[str, str]] = ...,
111+
pdf_standards: Optional[
112+
Union[Literal["1.7", "a-2b", "a-3b"], List[Literal["1.7", "a-2b", "a-3b"]]]
113+
] = [],
114+
root: Optional[PathLike] = None,
111115
) -> Optional[Union[bytes, List[bytes]]]:
112116
"""Compile a Typst project.
113117
Args:
@@ -121,6 +125,9 @@ class Compiler:
121125
- Ellipsis (default): Keep existing sys_inputs from initialization or previous compile.
122126
- None: Clear sys_inputs (equivalent to empty dictionary).
123127
- Dict[str, str]: Use the provided dictionary as sys_inputs.
128+
pdf_standards (Optional[Union[Literal["1.7", "a-2b", "a-3b"], List[Literal["1.7", "a-2b", "a-3b"]]]]):
129+
One or more PDF standard profiles to apply when exporting.
130+
root (Optional[PathLike]): Override the root path for this compilation.
124131
Returns:
125132
Optional[Union[bytes, List[bytes]]]: Return the compiled file as `bytes` if output is `None`.
126133
"""
@@ -132,6 +139,10 @@ class Compiler:
132139
format: Optional[OutputFormat] = None,
133140
ppi: Optional[float] = None,
134141
sys_inputs: Union[EllipsisType, None, Dict[str, str]] = ...,
142+
pdf_standards: Optional[
143+
Union[Literal["1.7", "a-2b", "a-3b"], List[Literal["1.7", "a-2b", "a-3b"]]]
144+
] = [],
145+
root: Optional[PathLike] = None,
135146
) -> Tuple[Optional[Union[bytes, List[bytes]]], List[TypstWarning]]:
136147
"""Compile a Typst project and return both result and warnings.
137148
Args:
@@ -145,6 +156,9 @@ class Compiler:
145156
- Ellipsis (default): Keep existing sys_inputs from initialization or previous compile.
146157
- None: Clear sys_inputs (equivalent to empty dictionary).
147158
- Dict[str, str]: Use the provided dictionary as sys_inputs.
159+
pdf_standards (Optional[Union[Literal["1.7", "a-2b", "a-3b"], List[Literal["1.7", "a-2b", "a-3b"]]]]):
160+
One or more PDF standard profiles to apply when exporting.
161+
root (Optional[PathLike]): Override the root path for this compilation.
148162
Returns:
149163
Tuple[Optional[Union[bytes, List[bytes]]], List[TypstWarning]]: Return a tuple of (compiled_data, warnings).
150164
The first element is the compiled file as `bytes` if output is `None`, otherwise `None`.
@@ -157,13 +171,15 @@ class Compiler:
157171
field: Optional[str] = None,
158172
one: bool = False,
159173
format: Optional[Literal["json", "yaml"]] = None,
174+
root: Optional[PathLike] = None,
160175
) -> str:
161176
"""Query a Typst document.
162177
Args:
163178
selector (str): Typst selector like `<label>`.
164179
field (Optional[str], optional): Field to query.
165180
one (bool, optional): Query only one element.
166181
format (Optional[str]): Output format, `json` or `yaml`.
182+
root (Optional[PathLike]): Override the root path for this query.
167183
Returns:
168184
str: Return the query result.
169185
"""

src/lib.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,15 @@ pub struct Compiler {
350350
}
351351

352352
impl Compiler {
353+
fn apply_root(&mut self, root: Option<PathBuf>) -> PyResult<()> {
354+
if let Some(root) = root {
355+
self.world
356+
.set_root(root)
357+
.map_err(|msg| PyRuntimeError::new_err(msg.to_string()))?;
358+
}
359+
Ok(())
360+
}
361+
353362
fn apply_input(&mut self, input: Option<Input>) -> PyResult<()> {
354363
if let Some(input) = input {
355364
self.world
@@ -516,7 +525,7 @@ impl Compiler {
516525

517526
/// Compile a typst file to PDF
518527
#[allow(clippy::too_many_arguments)]
519-
#[pyo3(name = "compile", signature = (input = None, output = None, format = None, ppi = None, sys_inputs = SysInputsOption::Keep, pdf_standards = Vec::new()))]
528+
#[pyo3(name = "compile", signature = (input = None, output = None, format = None, ppi = None, sys_inputs = SysInputsOption::Keep, pdf_standards = Vec::new(), root = None))]
520529
fn py_compile(
521530
&mut self,
522531
py: Python<'_>,
@@ -526,7 +535,9 @@ impl Compiler {
526535
ppi: Option<f32>,
527536
#[pyo3(from_py_with = extract_sys_inputs_option)] sys_inputs: SysInputsOption,
528537
#[pyo3(from_py_with = extract_pdf_standards)] pdf_standards: Vec<typst_pdf::PdfStandard>,
538+
root: Option<PathBuf>,
529539
) -> PyResult<Py<PyAny>> {
540+
self.apply_root(root)?;
530541
self.apply_input(input)?;
531542
self.apply_sys_inputs(sys_inputs);
532543
if let Some(output) = output {
@@ -592,7 +603,7 @@ impl Compiler {
592603

593604
/// Compile a typst file and return both result and warnings
594605
#[allow(clippy::too_many_arguments)]
595-
#[pyo3(name = "compile_with_warnings", signature = (input = None, output = None, format = None, ppi = None, sys_inputs = SysInputsOption::Keep, pdf_standards = Vec::new()))]
606+
#[pyo3(name = "compile_with_warnings", signature = (input = None, output = None, format = None, ppi = None, sys_inputs = SysInputsOption::Keep, pdf_standards = Vec::new(), root = None))]
596607
fn py_compile_with_warnings(
597608
&mut self,
598609
py: Python<'_>,
@@ -602,7 +613,9 @@ impl Compiler {
602613
ppi: Option<f32>,
603614
#[pyo3(from_py_with = extract_sys_inputs_option)] sys_inputs: SysInputsOption,
604615
#[pyo3(from_py_with = extract_pdf_standards)] pdf_standards: Vec<typst_pdf::PdfStandard>,
616+
root: Option<PathBuf>,
605617
) -> PyResult<Py<PyAny>> {
618+
self.apply_root(root)?;
606619
self.apply_input(input)?;
607620
self.apply_sys_inputs(sys_inputs);
608621
let result = py
@@ -656,15 +669,17 @@ impl Compiler {
656669
}
657670

658671
/// Query a typst document
659-
#[pyo3(name = "query", signature = (selector, field = None, one = false, format = None))]
672+
#[pyo3(name = "query", signature = (selector, field = None, one = false, format = None, root = None))]
660673
fn py_query(
661674
&mut self,
662675
py: Python<'_>,
663676
selector: &str,
664677
field: Option<&str>,
665678
one: bool,
666679
format: Option<&str>,
680+
root: Option<PathBuf>,
667681
) -> PyResult<Py<PyAny>> {
682+
self.apply_root(root)?;
668683
py.detach(|| self.query(selector, field, one, format))
669684
.map(|s| PyString::new(py, &s).into())
670685
}
@@ -713,6 +728,7 @@ fn compile(
713728
ppi,
714729
SysInputsOption::Keep,
715730
pdf_standards,
731+
None,
716732
)
717733
}
718734

@@ -759,6 +775,7 @@ fn compile_with_warnings(
759775
ppi,
760776
SysInputsOption::Keep,
761777
pdf_standards,
778+
None,
762779
)
763780
}
764781

@@ -801,7 +818,7 @@ fn py_query(
801818
sys_inputs,
802819
package_path,
803820
)?;
804-
compiler.py_query(py, selector, field, one, format)
821+
compiler.py_query(py, selector, field, one, format, None)
805822
}
806823

807824
/// Python binding to typst

src/world.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,15 @@ impl SystemWorld {
135135
&self.root
136136
}
137137

138+
/// Update the root path for the world.
139+
pub fn set_root(&mut self, root: PathBuf) -> StrResult<()> {
140+
self.root = root
141+
.canonicalize()
142+
.map_err(|err| format!("Failed to canonicalize root: {}", err))?;
143+
self.reset();
144+
Ok(())
145+
}
146+
138147
/// The current working directory.
139148
pub fn workdir(&self) -> &Path {
140149
self.workdir.as_deref().unwrap_or(Path::new("."))

0 commit comments

Comments
 (0)