dcmspec 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dcmspec/__init__.py +0 -0
- dcmspec/apps/__init__.py +1 -0
- dcmspec/apps/cli/__init__.py +0 -0
- dcmspec/apps/cli/dataelements.py +89 -0
- dcmspec/apps/cli/iodattributes.py +125 -0
- dcmspec/apps/cli/iodmodules.py +92 -0
- dcmspec/apps/cli/modattributes.py +265 -0
- dcmspec/apps/cli/tdwiicontent.py +365 -0
- dcmspec/apps/cli/uidvalues.py +84 -0
- dcmspec/apps/cli/upsdimseattributes.py +109 -0
- dcmspec/apps/cli/upsioddimseattributes.py +330 -0
- dcmspec/apps/ui/iod_explorer/README.md +33 -0
- dcmspec/apps/ui/iod_explorer/__init__.py +9 -0
- dcmspec/apps/ui/iod_explorer/config/README.md +171 -0
- dcmspec/apps/ui/iod_explorer/config/iod_explorer_config.json +4 -0
- dcmspec/apps/ui/iod_explorer/config/iod_explorer_config_debug.json +4 -0
- dcmspec/apps/ui/iod_explorer/config/iod_explorer_config_example.json +4 -0
- dcmspec/apps/ui/iod_explorer/config/iod_explorer_config_minimal_logging.json +4 -0
- dcmspec/apps/ui/iod_explorer/iod_explorer.py +989 -0
- dcmspec/config.py +90 -0
- dcmspec/csv_table_spec_parser.py +85 -0
- dcmspec/doc_handler.py +214 -0
- dcmspec/dom_table_spec_parser.py +831 -0
- dcmspec/dom_utils.py +116 -0
- dcmspec/iod_spec_builder.py +444 -0
- dcmspec/iod_spec_printer.py +59 -0
- dcmspec/json_spec_store.py +110 -0
- dcmspec/module_registry.py +51 -0
- dcmspec/pdf_doc_handler.py +451 -0
- dcmspec/progress.py +232 -0
- dcmspec/service_attribute_defaults.py +124 -0
- dcmspec/service_attribute_model.py +231 -0
- dcmspec/spec_factory.py +451 -0
- dcmspec/spec_merger.py +536 -0
- dcmspec/spec_model.py +461 -0
- dcmspec/spec_parser.py +37 -0
- dcmspec/spec_printer.py +131 -0
- dcmspec/spec_store.py +50 -0
- dcmspec/ups_xhtml_doc_handler.py +117 -0
- dcmspec/xhtml_doc_handler.py +182 -0
- dcmspec-0.2.1.dist-info/METADATA +139 -0
- dcmspec-0.2.1.dist-info/RECORD +45 -0
- dcmspec-0.2.1.dist-info/WHEEL +4 -0
- dcmspec-0.2.1.dist-info/entry_points.txt +11 -0
- dcmspec-0.2.1.dist-info/licenses/LICENSE +201 -0
dcmspec/config.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Configuration management for the dcmspec library.
|
|
2
|
+
|
|
3
|
+
This module provides the Config class for managing application configuration,
|
|
4
|
+
including cache directory handling and user-defined parameters.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import json
|
|
9
|
+
from typing import Any, Optional, Dict
|
|
10
|
+
from platformdirs import user_cache_dir, user_config_dir
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Config:
|
|
14
|
+
"""Manages application configuration.
|
|
15
|
+
|
|
16
|
+
Reserved configuration keys:
|
|
17
|
+
- cache_dir: Cache Directory path used by the library. If not set by the user, OS-specific default is used.
|
|
18
|
+
|
|
19
|
+
Users may add their own keys, but should not overwrite reserved keys unless they intend to change library behavior.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, app_name: str = "dcmspec", config_file: Optional[str] = None):
|
|
23
|
+
"""Initialize the Config object.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
app_name: The application name used for determining default config/cache directories.
|
|
27
|
+
config_file: Optional path to a specific config file. If not provided, a default location is used.
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
self.app_name: str = app_name
|
|
31
|
+
self.config_file: str = config_file or os.path.join(user_config_dir(app_name), "config.json")
|
|
32
|
+
|
|
33
|
+
# Check if config_file is a directory; if so, warn and fall back to default config
|
|
34
|
+
if os.path.isdir(self.config_file):
|
|
35
|
+
print(f"Warning: The config_file path '{self.config_file}' is a directory, not a file. Using default.")
|
|
36
|
+
self._data: Dict[str, Any] = {"cache_dir": user_cache_dir(app_name)}
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
# Initialize config with OS-specific default value for cache directory
|
|
40
|
+
self._data: Dict[str, Any] = {"cache_dir": user_cache_dir(app_name)}
|
|
41
|
+
|
|
42
|
+
self.load_config()
|
|
43
|
+
|
|
44
|
+
def load_config(self) -> None:
|
|
45
|
+
"""Load configuration from the config file if it exists.
|
|
46
|
+
|
|
47
|
+
Creates the cache directory if it does not exist.
|
|
48
|
+
"""
|
|
49
|
+
try:
|
|
50
|
+
if os.path.exists(self.config_file):
|
|
51
|
+
with open(self.config_file, "r", encoding="utf-8") as f:
|
|
52
|
+
config: Dict[str, Any] = json.load(f)
|
|
53
|
+
self._data.update(config)
|
|
54
|
+
except (OSError, json.JSONDecodeError) as e:
|
|
55
|
+
print(f"Failed to load configuration file {self.config_file}: {e}")
|
|
56
|
+
|
|
57
|
+
cache_dir = self.get_param("cache_dir")
|
|
58
|
+
try:
|
|
59
|
+
os.makedirs(cache_dir, exist_ok=True)
|
|
60
|
+
except FileExistsError:
|
|
61
|
+
print(f"Error: The cache_dir path '{cache_dir}' exists and is not a directory.")
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
# Handle rare case where the path may not be a directory and, for any reason, os.makedirs did not fail.
|
|
65
|
+
if not os.path.isdir(cache_dir):
|
|
66
|
+
print(f"Error: The cache_dir path '{cache_dir}' is not a directory.")
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
def save_config(self) -> None:
|
|
70
|
+
"""Save the current configuration to the config file."""
|
|
71
|
+
try:
|
|
72
|
+
os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
|
|
73
|
+
with open(self.config_file, "w", encoding="utf-8") as f:
|
|
74
|
+
json.dump(self._data, f, indent=4)
|
|
75
|
+
except OSError as e:
|
|
76
|
+
print(f"Failed to save configuration file {self.config_file}: {e}")
|
|
77
|
+
|
|
78
|
+
def set_param(self, key: str, value: Any) -> None:
|
|
79
|
+
"""Set a configuration parameter."""
|
|
80
|
+
self._data[key] = value
|
|
81
|
+
|
|
82
|
+
def get_param(self, key: str) -> Optional[Any]:
|
|
83
|
+
"""Get a configuration parameter by key."""
|
|
84
|
+
return self._data.get(key)
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def cache_dir(self) -> str:
|
|
88
|
+
"""Access the cache directory used by the library."""
|
|
89
|
+
return self.get_param("cache_dir")
|
|
90
|
+
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""CSV specification parser class for IHE profiles tables in dcmspec.
|
|
2
|
+
|
|
3
|
+
Provides the CSVTableSpecParser class for parsing DICOM specification tables in CSV format,
|
|
4
|
+
converting them into structured in-memory representations using anytree.
|
|
5
|
+
"""
|
|
6
|
+
from typing import Tuple
|
|
7
|
+
from anytree import Node
|
|
8
|
+
|
|
9
|
+
from dcmspec.spec_parser import SpecParser
|
|
10
|
+
|
|
11
|
+
class CSVTableSpecParser(SpecParser):
|
|
12
|
+
"""Base parser for DICOM Specification IHE tables in CSV-like format."""
|
|
13
|
+
|
|
14
|
+
def parse(
|
|
15
|
+
self,
|
|
16
|
+
table: dict,
|
|
17
|
+
column_to_attr,
|
|
18
|
+
name_attr="elem_name",
|
|
19
|
+
table_id=None,
|
|
20
|
+
include_depth=None,
|
|
21
|
+
) -> Tuple[Node, Node]:
|
|
22
|
+
"""Parse specification metadata and content from a single table dict.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
table (dict): A table dict as output by PDFDocHandler.concat_tables, with 'header' and 'data' keys.
|
|
26
|
+
column_to_attr (dict): Mapping from column indices to node attribute names.
|
|
27
|
+
name_attr (str): The attribute to use for node names.
|
|
28
|
+
table_id (str, optional): Table identifier for model parsing.
|
|
29
|
+
include_depth (int, optional): The depth to which included tables should be parsed.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
tuple: (metadata_node, content_node)
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
# Use the header and data from the grouped table dict
|
|
36
|
+
header = table.get("header", [])
|
|
37
|
+
data = table.get("data", [])
|
|
38
|
+
|
|
39
|
+
metadata = Node("metadata")
|
|
40
|
+
metadata.header = header
|
|
41
|
+
metadata.column_to_attr = column_to_attr
|
|
42
|
+
metadata.table_id = table_id
|
|
43
|
+
if include_depth is not None:
|
|
44
|
+
metadata.include_depth = int(include_depth)
|
|
45
|
+
content = self.parse_table([data], column_to_attr, name_attr)
|
|
46
|
+
return metadata, content
|
|
47
|
+
|
|
48
|
+
def parse_table(
|
|
49
|
+
self,
|
|
50
|
+
tables: list, # List of tables, each a list of rows (list of str)
|
|
51
|
+
column_to_attr: dict,
|
|
52
|
+
name_attr: str = "elem_name",
|
|
53
|
+
) -> Node:
|
|
54
|
+
"""Build a tree from tables using column mapping and '>' nesting logic.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
tables (list): List of tables, each a list of rows (list of str).
|
|
58
|
+
column_to_attr (dict): Mapping from column indices to node attribute names.
|
|
59
|
+
name_attr (str): The attribute to use for node names.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Node: The root node of the tree.
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
root = Node("content")
|
|
66
|
+
parent_nodes = {0: root}
|
|
67
|
+
for table in tables:
|
|
68
|
+
for row in table:
|
|
69
|
+
row_data = {}
|
|
70
|
+
for col_idx, attr in column_to_attr.items():
|
|
71
|
+
value = row[col_idx] if col_idx < len(row) else ""
|
|
72
|
+
# Clean up newlines in the cell to be used as node name
|
|
73
|
+
if attr == name_attr:
|
|
74
|
+
value = value.replace("\n", " ")
|
|
75
|
+
row_data[attr] = value
|
|
76
|
+
node_name = row_data[name_attr]
|
|
77
|
+
level = node_name.count(">") + 1
|
|
78
|
+
# Ensure all parent levels exist
|
|
79
|
+
if (level - 1) not in parent_nodes:
|
|
80
|
+
# If a parent is missing, attach to root
|
|
81
|
+
parent_nodes[level - 1] = root
|
|
82
|
+
parent = parent_nodes[level - 1]
|
|
83
|
+
child = Node(node_name, parent=parent, **row_data)
|
|
84
|
+
parent_nodes[level] = child
|
|
85
|
+
return root
|
dcmspec/doc_handler.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""Base class for handling DICOM specification documents in dcmspec.
|
|
2
|
+
|
|
3
|
+
Provides the DocHandler class for reading, parsing, and downloading DICOM documents
|
|
4
|
+
in various formats (e.g., XHTML, PDF). The base class supplies a generic download
|
|
5
|
+
method for both text and binary files, and defines the interface for document parsing.
|
|
6
|
+
Subclasses should implement the `load_document` method for their specific format.
|
|
7
|
+
"""
|
|
8
|
+
import os
|
|
9
|
+
from typing import Any, Optional
|
|
10
|
+
import logging
|
|
11
|
+
import requests
|
|
12
|
+
|
|
13
|
+
from dcmspec.config import Config
|
|
14
|
+
from dcmspec.progress import Progress, ProgressObserver, calculate_percent
|
|
15
|
+
# BEGIN LEGACY SUPPORT: Remove for int progress callback deprecation
|
|
16
|
+
from dcmspec.progress import ProgressStatus, handle_legacy_callback
|
|
17
|
+
from typing import Callable
|
|
18
|
+
# END LEGACY SUPPORT
|
|
19
|
+
|
|
20
|
+
class DocHandler:
|
|
21
|
+
"""Base class for DICOM document handlers.
|
|
22
|
+
|
|
23
|
+
Handles DICOM documents in various formats (e.g., XHTML, PDF).
|
|
24
|
+
Subclasses must implement the `load_document` method to handle
|
|
25
|
+
reading/parsing input files. The base class provides a generic
|
|
26
|
+
download method for both text and binary files.
|
|
27
|
+
|
|
28
|
+
Progress Reporting:
|
|
29
|
+
The observer pattern is used for progress reporting. Subclasses may extend
|
|
30
|
+
the Progress class and use the progress_observer to report additional information
|
|
31
|
+
(e.g., status, errors, or other context) beyond percent complete, enabling future
|
|
32
|
+
extensibility for richer progress tracking.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, config: Optional[Config] = None, logger: Optional[logging.Logger] = None):
|
|
36
|
+
"""Initialize the document handler with an optional logger.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
config (Optional[Config]): Config instance to use. If None, a default Config is created.
|
|
40
|
+
logger (Optional[logging.Logger]): Logger instance to use. If None, a default logger is created.
|
|
41
|
+
|
|
42
|
+
Logging:
|
|
43
|
+
A logger may be passed for custom logging control. If no logger is provided,
|
|
44
|
+
a default logger for this class is used. In both cases, no logging handlers
|
|
45
|
+
are added by default. To see log output, logging should be configured in the
|
|
46
|
+
application (e.g., with logging.basicConfig()).
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
if logger is not None and not isinstance(logger, logging.Logger):
|
|
50
|
+
raise TypeError("logger must be an instance of logging.Logger or None")
|
|
51
|
+
self.logger = logger or logging.getLogger(self.__class__.__name__)
|
|
52
|
+
|
|
53
|
+
if config is not None and not isinstance(config, Config):
|
|
54
|
+
raise TypeError("config must be an instance of Config or None")
|
|
55
|
+
self.config = config or Config()
|
|
56
|
+
|
|
57
|
+
def download(
|
|
58
|
+
self,
|
|
59
|
+
url: str,
|
|
60
|
+
file_path: str,
|
|
61
|
+
binary: bool = False,
|
|
62
|
+
progress_observer: 'Optional[ProgressObserver]' = None,
|
|
63
|
+
# BEGIN LEGACY SUPPORT: Remove for int progress callback deprecation
|
|
64
|
+
progress_callback: 'Optional[Callable[[int], None]]' = None
|
|
65
|
+
# END LEGACY SUPPORT
|
|
66
|
+
) -> str:
|
|
67
|
+
"""Download a file from a URL and save it to the specified path.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
url (str): The URL to download the file from.
|
|
71
|
+
file_path (str): The path to save the downloaded file.
|
|
72
|
+
binary (bool): If True, save as binary. If False, save as UTF-8 text.
|
|
73
|
+
progress_observer (Optional[ProgressObserver]): Optional observer to report download progress.
|
|
74
|
+
progress_callback (Optional[Callable[[int], None]]): [LEGACY, Deprecated] Optional callback to
|
|
75
|
+
report progress as an integer percent (0-100, or -1 if indeterminate). Use progress_observer
|
|
76
|
+
instead. Will be removed in a future release.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
str: The file path where the document was saved.
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
RuntimeError: If the download or save fails.
|
|
83
|
+
|
|
84
|
+
"""
|
|
85
|
+
# BEGIN LEGACY SUPPORT: Remove for int progress callback deprecation
|
|
86
|
+
progress_observer = handle_legacy_callback(progress_observer, progress_callback)
|
|
87
|
+
# END LEGACY SUPPORT
|
|
88
|
+
self.logger.info(f"Downloading document from {url} to {file_path}")
|
|
89
|
+
try:
|
|
90
|
+
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|
91
|
+
except OSError as e:
|
|
92
|
+
self.logger.error(f"Failed to create directory for {file_path}: {e}")
|
|
93
|
+
raise RuntimeError(f"Failed to create directory for {file_path}: {e}") from e
|
|
94
|
+
try:
|
|
95
|
+
with requests.get(url, timeout=30, stream=True, headers={"Accept-Encoding": "identity"}) as response:
|
|
96
|
+
response.raise_for_status()
|
|
97
|
+
total = int(response.headers.get('content-length', 0))
|
|
98
|
+
chunk_size = 8192
|
|
99
|
+
if binary:
|
|
100
|
+
self._download_binary(response, file_path, total, chunk_size, progress_observer)
|
|
101
|
+
else:
|
|
102
|
+
self._download_text(response, file_path, total, chunk_size, progress_observer)
|
|
103
|
+
self.logger.info(f"Document downloaded to {file_path}")
|
|
104
|
+
return file_path
|
|
105
|
+
except requests.exceptions.RequestException as e:
|
|
106
|
+
self.logger.error(f"Failed to download {url}: {e}")
|
|
107
|
+
raise RuntimeError(f"Failed to download {url}: {e}") from e
|
|
108
|
+
except OSError as e:
|
|
109
|
+
self.logger.error(f"Failed to save file {file_path}: {e}")
|
|
110
|
+
raise RuntimeError(f"Failed to save file {file_path}: {e}") from e
|
|
111
|
+
|
|
112
|
+
def _report_progress(self, downloaded, total, progress_observer, last_percent):
|
|
113
|
+
"""Report progress if percent changed.
|
|
114
|
+
|
|
115
|
+
If the total file size is unknown or invalid, calls the observer with -1 to indicate
|
|
116
|
+
indeterminate progress. Otherwise, calls the observer with the integer percent (0-100).
|
|
117
|
+
Adds status=ProgressStatus.DOWNLOADING to all progress events.
|
|
118
|
+
"""
|
|
119
|
+
if not progress_observer:
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
percent = calculate_percent(downloaded, total)
|
|
123
|
+
if percent != last_percent[0]:
|
|
124
|
+
progress_observer(Progress(percent, status=ProgressStatus.DOWNLOADING))
|
|
125
|
+
last_percent[0] = percent
|
|
126
|
+
|
|
127
|
+
def _download_binary(self, response, file_path, total, chunk_size, progress_observer):
|
|
128
|
+
"""Download and save a binary file with progress reporting.
|
|
129
|
+
|
|
130
|
+
Streams each chunk directly to the file to avoid high memory usage.
|
|
131
|
+
Reports progress using the provided observer.
|
|
132
|
+
"""
|
|
133
|
+
downloaded = 0
|
|
134
|
+
last_percent = [None]
|
|
135
|
+
with open(file_path, "wb") as f:
|
|
136
|
+
for chunk in response.iter_content(chunk_size=chunk_size):
|
|
137
|
+
if chunk:
|
|
138
|
+
# For binary, no cleaning is needed
|
|
139
|
+
f.write(chunk)
|
|
140
|
+
downloaded += len(chunk)
|
|
141
|
+
self._report_progress(downloaded, total, progress_observer, last_percent)
|
|
142
|
+
|
|
143
|
+
def _download_text(self, response, file_path, total, chunk_size, progress_observer):
|
|
144
|
+
"""Download and save a text file with progress reporting.
|
|
145
|
+
|
|
146
|
+
Streams cleaned chunks directly to the file to avoid high memory usage.
|
|
147
|
+
Reports progress using the provided observer using response.encoding if available
|
|
148
|
+
for accurate byte counting.
|
|
149
|
+
"""
|
|
150
|
+
downloaded = 0
|
|
151
|
+
last_percent = [None]
|
|
152
|
+
encoding = response.encoding or "utf-8"
|
|
153
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
154
|
+
for chunk in response.iter_content(chunk_size=chunk_size, decode_unicode=True):
|
|
155
|
+
if chunk:
|
|
156
|
+
cleaned_chunk = self.clean_text(chunk)
|
|
157
|
+
f.write(cleaned_chunk)
|
|
158
|
+
chunk_bytes = cleaned_chunk.encode(encoding)
|
|
159
|
+
downloaded += len(chunk_bytes)
|
|
160
|
+
self._report_progress(downloaded, total, progress_observer, last_percent)
|
|
161
|
+
|
|
162
|
+
def clean_text(self, text: str) -> str:
|
|
163
|
+
"""Clean text content before saving.
|
|
164
|
+
|
|
165
|
+
Subclasses can override this to perform format-specific cleaning (e.g., remove ZWSP/NBSP for XHTML).
|
|
166
|
+
By default, returns the text unchanged.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
text (str): The text content to clean.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
str: The cleaned text.
|
|
173
|
+
|
|
174
|
+
"""
|
|
175
|
+
return text
|
|
176
|
+
|
|
177
|
+
def load_document(
|
|
178
|
+
self,
|
|
179
|
+
cache_file_name: str,
|
|
180
|
+
url: Optional[str] = None,
|
|
181
|
+
force_download: bool = False,
|
|
182
|
+
progress_observer: 'Optional[ProgressObserver]' = None,
|
|
183
|
+
# BEGIN LEGACY SUPPORT: Remove for int progress callback deprecation
|
|
184
|
+
progress_callback: 'Optional[Callable[[int], None]]' = None,
|
|
185
|
+
# END LEGACY SUPPORT
|
|
186
|
+
*args: Any,
|
|
187
|
+
**kwargs: Any
|
|
188
|
+
) -> Any:
|
|
189
|
+
"""Implement this method to read and parse the document file, returning a parsed object.
|
|
190
|
+
|
|
191
|
+
Subclasses should implement this method to load and parse a document file
|
|
192
|
+
(e.g., XHTML, PDF, CSV) and return a format-specific parsed object.
|
|
193
|
+
The exact type of the returned object depends on the subclass
|
|
194
|
+
(e.g., BeautifulSoup for XHTML, pdfplumber.PDF for PDF).
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
cache_file_name (str): Path or name of the local cached file.
|
|
198
|
+
url (str, optional): URL to download the file from if not cached or if force_download is True.
|
|
199
|
+
force_download (bool, optional): If True, download the file even if it exists locally.
|
|
200
|
+
progress_observer (Optional[ProgressObserver]): Optional observer to report download progress.
|
|
201
|
+
progress_callback (Optional[Callable[[int], None]]): [LEGACY, Deprecated] Optional callback to
|
|
202
|
+
report progress as an integer percent (0-100, or -1 if indeterminate). Use progress_observer
|
|
203
|
+
instead. Will be removed in a future release.
|
|
204
|
+
*args: Additional positional arguments for format-specific loading.
|
|
205
|
+
**kwargs: Additional keyword arguments for format-specific loading.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
Any: The parsed document object (type depends on subclass).
|
|
209
|
+
|
|
210
|
+
"""
|
|
211
|
+
# BEGIN LEGACY SUPPORT: Remove for int progress callback deprecation
|
|
212
|
+
progress_observer = handle_legacy_callback(progress_observer, progress_callback)
|
|
213
|
+
# END LEGACY SUPPORT
|
|
214
|
+
raise NotImplementedError("Subclasses must implement load_document()")
|