Coverage for src / marimushka / export.py: 100%

37 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-28 17:41 +0000

1"""Export module providing the public Python API. 

2 

3This module provides the main Python API for exporting marimo notebooks. 

4For CLI usage, see the cli module. 

5 

6Example:: 

7 

8 # Python API 

9 from marimushka.export import main 

10 main(notebooks="notebooks", apps="apps", output="_site") 

11 

12The exported files will be placed in the specified output directory (default: _site). 

13""" 

14 

15from pathlib import Path 

16 

17from loguru import logger 

18 

19from . import __version__ 

20from .audit import get_audit_logger 

21from .exceptions import ProgressCallback 

22from .notebook import Kind, folder2notebooks 

23from .orchestrator import generate_index 

24from .validators import validate_template 

25 

26 

27def main( 

28 output: str | Path = "_site", 

29 template: str | Path = Path(__file__).parent / "templates" / "tailwind.html.j2", 

30 notebooks: str | Path = "notebooks", 

31 apps: str | Path = "apps", 

32 notebooks_wasm: str | Path = "notebooks", 

33 sandbox: bool = True, 

34 bin_path: str | Path | None = None, 

35 parallel: bool = True, 

36 max_workers: int = 4, 

37 timeout: int = 300, 

38 on_progress: ProgressCallback | None = None, 

39) -> str: 

40 """Export marimo notebooks and generate an index page. 

41 

42 Args: 

43 output: Output directory for generated files. Defaults to "_site". 

44 template: Path to Jinja2 template file. Defaults to built-in Tailwind template. 

45 notebooks: Directory containing static notebooks. Defaults to "notebooks". 

46 apps: Directory containing app notebooks. Defaults to "apps". 

47 notebooks_wasm: Directory containing interactive notebooks. Defaults to "notebooks". 

48 sandbox: Whether to run exports in isolated sandbox. Defaults to True. 

49 bin_path: Custom path to uvx executable. Defaults to None. 

50 parallel: Whether to export notebooks in parallel. Defaults to True. 

51 max_workers: Maximum number of parallel workers. Defaults to 4. 

52 timeout: Maximum time in seconds for each export. Defaults to 300. 

53 on_progress: Optional callback for progress tracking. Called after each notebook export 

54 with signature: on_progress(completed, total, notebook_name). 

55 

56 Returns: 

57 Rendered HTML content as string, empty if no notebooks found. 

58 

59 Raises: 

60 TemplateNotFoundError: If the template file does not exist. 

61 TemplateInvalidError: If the template path is not a file. 

62 TemplateRenderError: If the template fails to render. 

63 IndexWriteError: If the index file cannot be written. 

64 

65 Example:: 

66 

67 from marimushka.export import main 

68 

69 # Simple usage 

70 main(notebooks="my-notebooks", apps="my-apps") 

71 

72 # With progress callback 

73 def progress_handler(completed, total, name): 

74 print(f"[{completed}/{total}] Exported {name}") 

75 

76 main(notebooks="my-notebooks", on_progress=progress_handler) 

77 

78 """ 

79 logger.info("Starting marimushka build process") 

80 logger.info(f"Version of Marimushka: {__version__}") 

81 output = output or "_site" 

82 

83 # Convert output_dir explicitly to Path 

84 output_dir: Path = Path(output) 

85 logger.info(f"Output directory: {output_dir}") 

86 

87 # Make sure the output directory exists 

88 output_dir.mkdir(parents=True, exist_ok=True) 

89 

90 # Convert template to Path and validate early 

91 template_file: Path = Path(template) 

92 audit_logger = get_audit_logger() 

93 validate_template(template_file, audit_logger) 

94 

95 logger.info(f"Using template file: {template_file}") 

96 logger.info(f"Notebooks: {notebooks}") 

97 logger.info(f"Apps: {apps}") 

98 logger.info(f"Notebooks-wasm: {notebooks_wasm}") 

99 logger.info(f"Sandbox: {sandbox}") 

100 logger.info(f"Parallel: {parallel} (max_workers={max_workers})") 

101 logger.info(f"Bin path: {bin_path}") 

102 logger.info(f"Timeout: {timeout}s") 

103 

104 # Convert bin_path to Path if provided 

105 bin_path_obj: Path | None = Path(bin_path) if bin_path else None 

106 

107 notebooks_data = folder2notebooks(folder=notebooks, kind=Kind.NB) 

108 apps_data = folder2notebooks(folder=apps, kind=Kind.APP) 

109 notebooks_wasm_data = folder2notebooks(folder=notebooks_wasm, kind=Kind.NB_WASM) 

110 

111 logger.info(f"# notebooks_data: {len(notebooks_data)}") 

112 logger.info(f"# apps_data: {len(apps_data)}") 

113 logger.info(f"# notebooks_wasm_data: {len(notebooks_wasm_data)}") 

114 

115 # Exit if no notebooks or apps were found 

116 if not notebooks_data and not apps_data and not notebooks_wasm_data: 

117 logger.warning("No notebooks or apps found!") 

118 return "" 

119 

120 return generate_index( 

121 output=output_dir, 

122 template_file=template_file, 

123 notebooks=notebooks_data, 

124 apps=apps_data, 

125 notebooks_wasm=notebooks_wasm_data, 

126 sandbox=sandbox, 

127 bin_path=bin_path_obj, 

128 parallel=parallel, 

129 max_workers=max_workers, 

130 timeout=timeout, 

131 on_progress=on_progress, 

132 audit_logger=audit_logger, 

133 )