1111:class:`RuntimeError` when the backing library is missing, so the core
1212package stays lean and import-time stays Qt-free / dependency-free.
1313"""
14- import importlib
1514from pathlib import Path
1615from typing import Any , Dict , List
1716
17+ _HINT = "pip install je_auto_control[office]"
1818
19- def _import (module_name : str , pip_name : str ) -> Any :
20- """Import an optional Office backend or raise a helpful error."""
19+
20+ def _openpyxl () -> Any :
21+ """Import openpyxl (optional Excel backend) or raise a helpful error."""
22+ try :
23+ import openpyxl
24+ except ImportError as error :
25+ raise RuntimeError (f"Excel I/O requires openpyxl ({ _HINT } )." ) from error
26+ return openpyxl
27+
28+
29+ def _docx () -> Any :
30+ """Import python-docx (optional Word backend) or raise a helpful error."""
31+ try :
32+ import docx
33+ except ImportError as error :
34+ raise RuntimeError (
35+ f"Word I/O requires python-docx ({ _HINT } )." ) from error
36+ return docx
37+
38+
39+ def _pptx () -> Any :
40+ """Import python-pptx (optional PPT backend) or raise a helpful error."""
2141 try :
22- return importlib . import_module ( module_name )
42+ import pptx
2343 except ImportError as error :
2444 raise RuntimeError (
25- f"This feature requires { pip_name } "
26- f"(pip install je_auto_control[office])." ) from error
45+ f"PowerPoint I/O requires python-pptx ( { _HINT } )." ) from error
46+ return pptx
2747
2848
2949def _existing (path : str ) -> Path :
@@ -40,7 +60,7 @@ def read_workbook(path: str, sheet: str = "") -> List[Dict[str, Any]]:
4060
4161 ``sheet`` defaults to the active sheet.
4262 """
43- openpyxl = _import ( "openpyxl" , "openpyxl" )
63+ openpyxl = _openpyxl ( )
4464 workbook = openpyxl .load_workbook (filename = str (_existing (path )),
4565 read_only = True , data_only = True )
4666 try :
@@ -58,7 +78,7 @@ def read_workbook(path: str, sheet: str = "") -> List[Dict[str, Any]]:
5878def write_workbook (path : str , rows : List [Dict [str , Any ]],
5979 sheet : str = "Sheet1" ) -> str :
6080 """Write ``rows`` (list of dicts) to an ``.xlsx`` file; return the path."""
61- openpyxl = _import ( "openpyxl" , "openpyxl" )
81+ openpyxl = _openpyxl ( )
6282 workbook = openpyxl .Workbook ()
6383 worksheet = workbook .active
6484 worksheet .title = sheet
@@ -77,14 +97,14 @@ def write_workbook(path: str, rows: List[Dict[str, Any]],
7797
7898def read_document (path : str ) -> Dict [str , List [str ]]:
7999 """Read a ``.docx`` file's paragraph texts."""
80- docx = _import ( "docx" , "python-docx" )
100+ docx = _docx ( )
81101 document = docx .Document (str (_existing (path )))
82102 return {"paragraphs" : [para .text for para in document .paragraphs ]}
83103
84104
85105def write_document (path : str , paragraphs : List [str ]) -> str :
86106 """Write ``paragraphs`` to a ``.docx`` file; return the path."""
87- docx = _import ( "docx" , "python-docx" )
107+ docx = _docx ( )
88108 document = docx .Document ()
89109 for paragraph in paragraphs :
90110 document .add_paragraph (str (paragraph ))
@@ -97,7 +117,7 @@ def write_document(path: str, paragraphs: List[str]) -> str:
97117
98118def read_presentation (path : str ) -> Dict [str , List [List [str ]]]:
99119 """Read a ``.pptx`` file's per-slide text runs."""
100- pptx = _import ( "pptx" , "python-pptx" )
120+ pptx = _pptx ( )
101121 presentation = pptx .Presentation (str (_existing (path )))
102122 slides = []
103123 for slide in presentation .slides :
@@ -121,7 +141,7 @@ def _add_slide(presentation: Any, layout: Any, spec: Any) -> None:
121141
122142def write_presentation (path : str , slides : List [Any ]) -> str :
123143 """Write ``slides`` (each ``{title, body:[...]}``) to a ``.pptx`` file."""
124- pptx = _import ( "pptx" , "python-pptx" )
144+ pptx = _pptx ( )
125145 presentation = pptx .Presentation ()
126146 layout = presentation .slide_layouts [1 ] # "Title and Content"
127147 for spec in slides :
0 commit comments