BabelDOC 0.1.6__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.
- babeldoc/__init__.py +1 -0
- babeldoc/asynchronize/__init__.py +51 -0
- babeldoc/const.py +7 -0
- babeldoc/converter.py +510 -0
- babeldoc/document_il/__init__.py +43 -0
- babeldoc/document_il/backend/__init__.py +0 -0
- babeldoc/document_il/backend/pdf_creater.py +402 -0
- babeldoc/document_il/frontend/__init__.py +0 -0
- babeldoc/document_il/frontend/il_creater.py +348 -0
- babeldoc/document_il/il_version_1.py +799 -0
- babeldoc/document_il/il_version_1.rnc +141 -0
- babeldoc/document_il/il_version_1.rng +390 -0
- babeldoc/document_il/il_version_1.xsd +235 -0
- babeldoc/document_il/midend/__init__.py +0 -0
- babeldoc/document_il/midend/add_debug_information.py +138 -0
- babeldoc/document_il/midend/il_translator.py +553 -0
- babeldoc/document_il/midend/layout_parser.py +180 -0
- babeldoc/document_il/midend/paragraph_finder.py +452 -0
- babeldoc/document_il/midend/remove_descent.py +168 -0
- babeldoc/document_il/midend/styles_and_formulas.py +701 -0
- babeldoc/document_il/midend/typesetting.py +761 -0
- babeldoc/document_il/translator/__init__.py +0 -0
- babeldoc/document_il/translator/cache.py +145 -0
- babeldoc/document_il/translator/translator.py +267 -0
- babeldoc/document_il/utils/fontmap.py +227 -0
- babeldoc/document_il/utils/layout_helper.py +467 -0
- babeldoc/document_il/utils/style_helper.py +86 -0
- babeldoc/document_il/xml_converter.py +51 -0
- babeldoc/docvision/README.md +0 -0
- babeldoc/docvision/__init__.py +0 -0
- babeldoc/docvision/doclayout.py +278 -0
- babeldoc/docvision/rpc_doclayout.py +162 -0
- babeldoc/high_level.py +468 -0
- babeldoc/main.py +466 -0
- babeldoc/pdfinterp.py +464 -0
- babeldoc/progress_monitor.py +146 -0
- babeldoc/translation_config.py +156 -0
- babeldoc-0.1.6.dist-info/METADATA +283 -0
- babeldoc-0.1.6.dist-info/RECORD +42 -0
- babeldoc-0.1.6.dist-info/WHEEL +4 -0
- babeldoc-0.1.6.dist-info/entry_points.txt +2 -0
- babeldoc-0.1.6.dist-info/licenses/LICENSE +661 -0
babeldoc/main.py
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import configargparse
|
|
6
|
+
import tqdm
|
|
7
|
+
from rich.progress import BarColumn
|
|
8
|
+
from rich.progress import MofNCompleteColumn
|
|
9
|
+
from rich.progress import Progress
|
|
10
|
+
from rich.progress import TextColumn
|
|
11
|
+
from rich.progress import TimeElapsedColumn
|
|
12
|
+
from rich.progress import TimeRemainingColumn
|
|
13
|
+
|
|
14
|
+
import babeldoc.high_level
|
|
15
|
+
from babeldoc.const import get_cache_file_path
|
|
16
|
+
from babeldoc.document_il.translator.translator import BingTranslator
|
|
17
|
+
from babeldoc.document_il.translator.translator import GoogleTranslator
|
|
18
|
+
from babeldoc.document_il.translator.translator import OpenAITranslator
|
|
19
|
+
from babeldoc.document_il.translator.translator import set_translate_rate_limiter
|
|
20
|
+
from babeldoc.translation_config import TranslationConfig
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
__version__ = "0.1.6"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def create_parser():
|
|
27
|
+
parser = configargparse.ArgParser(
|
|
28
|
+
config_file_parser_class=configargparse.TomlConfigParser(["babeldoc"]),
|
|
29
|
+
)
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
"-c",
|
|
32
|
+
"--my-config",
|
|
33
|
+
required=False,
|
|
34
|
+
is_config_file=True,
|
|
35
|
+
help="config file path",
|
|
36
|
+
)
|
|
37
|
+
parser.add_argument(
|
|
38
|
+
"--version",
|
|
39
|
+
action="version",
|
|
40
|
+
version=f"%(prog)s {__version__}",
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--files",
|
|
44
|
+
type=str,
|
|
45
|
+
# nargs="*",
|
|
46
|
+
action="append",
|
|
47
|
+
help="One or more paths to PDF files.",
|
|
48
|
+
)
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"--debug",
|
|
51
|
+
"-d",
|
|
52
|
+
default=False,
|
|
53
|
+
action="store_true",
|
|
54
|
+
help="Use debug logging level.",
|
|
55
|
+
)
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"--warmup",
|
|
58
|
+
default=False,
|
|
59
|
+
action="store_true",
|
|
60
|
+
help="Only download and verify required assets then exit.",
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"--rpc-doclayout",
|
|
64
|
+
default=None,
|
|
65
|
+
help="RPC service host address for document layout analysis",
|
|
66
|
+
type=str,
|
|
67
|
+
)
|
|
68
|
+
translation_params = parser.add_argument_group(
|
|
69
|
+
"Translation",
|
|
70
|
+
description="Used during translation",
|
|
71
|
+
)
|
|
72
|
+
translation_params.add_argument(
|
|
73
|
+
"--pages",
|
|
74
|
+
"-p",
|
|
75
|
+
type=str,
|
|
76
|
+
help="Pages to translate. If not set, translate all pages. like: 1,2,1-,-3,3-5",
|
|
77
|
+
)
|
|
78
|
+
translation_params.add_argument(
|
|
79
|
+
"--lang-in",
|
|
80
|
+
"-li",
|
|
81
|
+
type=str,
|
|
82
|
+
default="en",
|
|
83
|
+
help="The code of source language.",
|
|
84
|
+
)
|
|
85
|
+
translation_params.add_argument(
|
|
86
|
+
"--lang-out",
|
|
87
|
+
"-lo",
|
|
88
|
+
type=str,
|
|
89
|
+
default="zh",
|
|
90
|
+
help="The code of target language.",
|
|
91
|
+
)
|
|
92
|
+
translation_params.add_argument(
|
|
93
|
+
"--output",
|
|
94
|
+
"-o",
|
|
95
|
+
type=str,
|
|
96
|
+
default=None,
|
|
97
|
+
help="Output directory for files. if not set, use same as input.",
|
|
98
|
+
)
|
|
99
|
+
translation_params.add_argument(
|
|
100
|
+
"--qps",
|
|
101
|
+
"-q",
|
|
102
|
+
type=int,
|
|
103
|
+
default=4,
|
|
104
|
+
help="QPS limit of translation service",
|
|
105
|
+
)
|
|
106
|
+
translation_params.add_argument(
|
|
107
|
+
"--ignore-cache",
|
|
108
|
+
"-ic",
|
|
109
|
+
default=False,
|
|
110
|
+
action="store_true",
|
|
111
|
+
help="Ignore translation cache.",
|
|
112
|
+
)
|
|
113
|
+
translation_params.add_argument(
|
|
114
|
+
"--no-dual",
|
|
115
|
+
default=False,
|
|
116
|
+
action="store_true",
|
|
117
|
+
help="Do not output bilingual PDF files",
|
|
118
|
+
)
|
|
119
|
+
translation_params.add_argument(
|
|
120
|
+
"--no-mono",
|
|
121
|
+
default=False,
|
|
122
|
+
action="store_true",
|
|
123
|
+
help="Do not output monolingual PDF files",
|
|
124
|
+
)
|
|
125
|
+
translation_params.add_argument(
|
|
126
|
+
"--formular-font-pattern",
|
|
127
|
+
type=str,
|
|
128
|
+
default=None,
|
|
129
|
+
help="Font pattern to identify formula text",
|
|
130
|
+
)
|
|
131
|
+
translation_params.add_argument(
|
|
132
|
+
"--formular-char-pattern",
|
|
133
|
+
type=str,
|
|
134
|
+
default=None,
|
|
135
|
+
help="Character pattern to identify formula text",
|
|
136
|
+
)
|
|
137
|
+
translation_params.add_argument(
|
|
138
|
+
"--split-short-lines",
|
|
139
|
+
default=False,
|
|
140
|
+
action="store_true",
|
|
141
|
+
help="Force split short lines into different paragraphs (may cause poor typesetting & bugs)",
|
|
142
|
+
)
|
|
143
|
+
translation_params.add_argument(
|
|
144
|
+
"--short-line-split-factor",
|
|
145
|
+
type=float,
|
|
146
|
+
default=0.8,
|
|
147
|
+
help="Split threshold factor. The actual threshold is the median length of all lines on the current page * this factor",
|
|
148
|
+
)
|
|
149
|
+
translation_params.add_argument(
|
|
150
|
+
"--skip-clean",
|
|
151
|
+
default=False,
|
|
152
|
+
action="store_true",
|
|
153
|
+
help="Skip PDF cleaning step",
|
|
154
|
+
)
|
|
155
|
+
translation_params.add_argument(
|
|
156
|
+
"--dual-translate-first",
|
|
157
|
+
default=False,
|
|
158
|
+
action="store_true",
|
|
159
|
+
help="Put translated pages first in dual PDF mode",
|
|
160
|
+
)
|
|
161
|
+
service_params = translation_params.add_mutually_exclusive_group()
|
|
162
|
+
service_params.add_argument(
|
|
163
|
+
"--openai",
|
|
164
|
+
default=False,
|
|
165
|
+
action="store_true",
|
|
166
|
+
help="Use OpenAI translator.",
|
|
167
|
+
)
|
|
168
|
+
service_params.add_argument(
|
|
169
|
+
"--google",
|
|
170
|
+
default=False,
|
|
171
|
+
action="store_true",
|
|
172
|
+
help="Use Google translator.",
|
|
173
|
+
)
|
|
174
|
+
service_params.add_argument(
|
|
175
|
+
"--bing",
|
|
176
|
+
default=False,
|
|
177
|
+
action="store_true",
|
|
178
|
+
help="Use Bing translator.",
|
|
179
|
+
)
|
|
180
|
+
openai_params = parser.add_argument_group(
|
|
181
|
+
"Translation - OpenAI Options",
|
|
182
|
+
description="OpenAI specific options",
|
|
183
|
+
)
|
|
184
|
+
openai_params.add_argument(
|
|
185
|
+
"--openai-model",
|
|
186
|
+
"-m",
|
|
187
|
+
type=str,
|
|
188
|
+
default="gpt-4o-mini",
|
|
189
|
+
help="The OpenAI model to use for translation.",
|
|
190
|
+
)
|
|
191
|
+
openai_params.add_argument(
|
|
192
|
+
"--openai-base-url",
|
|
193
|
+
"-b",
|
|
194
|
+
type=str,
|
|
195
|
+
default=None,
|
|
196
|
+
help="The base URL for the OpenAI API.",
|
|
197
|
+
)
|
|
198
|
+
openai_params.add_argument(
|
|
199
|
+
"--openai-api-key",
|
|
200
|
+
"-k",
|
|
201
|
+
type=str,
|
|
202
|
+
default=None,
|
|
203
|
+
help="The API key for the OpenAI API.",
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
return parser
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def create_progress_handler(translation_config: TranslationConfig):
|
|
210
|
+
"""Create a progress handler function based on the configuration.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
translation_config: The translation configuration.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
A tuple of (progress_context, progress_handler), where progress_context is a context
|
|
217
|
+
manager that should be used to wrap the translation process, and progress_handler
|
|
218
|
+
is a function that will be called with progress events.
|
|
219
|
+
"""
|
|
220
|
+
if translation_config.use_rich_pbar:
|
|
221
|
+
progress = Progress(
|
|
222
|
+
TextColumn("[progress.description]{task.description}"),
|
|
223
|
+
BarColumn(),
|
|
224
|
+
MofNCompleteColumn(),
|
|
225
|
+
TimeElapsedColumn(),
|
|
226
|
+
TimeRemainingColumn(),
|
|
227
|
+
)
|
|
228
|
+
translate_task_id = progress.add_task("translate", total=100)
|
|
229
|
+
stage_tasks = {}
|
|
230
|
+
|
|
231
|
+
def progress_handler(event):
|
|
232
|
+
if event["type"] == "progress_start":
|
|
233
|
+
stage_tasks[event["stage"]] = progress.add_task(
|
|
234
|
+
f"{event['stage']}",
|
|
235
|
+
total=event.get("stage_total", 100),
|
|
236
|
+
)
|
|
237
|
+
elif event["type"] == "progress_update":
|
|
238
|
+
stage = event["stage"]
|
|
239
|
+
if stage in stage_tasks:
|
|
240
|
+
progress.update(
|
|
241
|
+
stage_tasks[stage],
|
|
242
|
+
completed=event["stage_current"],
|
|
243
|
+
total=event["stage_total"],
|
|
244
|
+
description=f"{event['stage']} ({event['stage_current']}/{event['stage_total']})",
|
|
245
|
+
refresh=True,
|
|
246
|
+
)
|
|
247
|
+
progress.update(
|
|
248
|
+
translate_task_id,
|
|
249
|
+
completed=event["overall_progress"],
|
|
250
|
+
refresh=True,
|
|
251
|
+
)
|
|
252
|
+
elif event["type"] == "progress_end":
|
|
253
|
+
stage = event["stage"]
|
|
254
|
+
if stage in stage_tasks:
|
|
255
|
+
progress.update(
|
|
256
|
+
stage_tasks[stage],
|
|
257
|
+
completed=event["stage_total"],
|
|
258
|
+
total=event["stage_total"],
|
|
259
|
+
description=f"{event['stage']} (Complete)",
|
|
260
|
+
refresh=True,
|
|
261
|
+
)
|
|
262
|
+
progress.update(
|
|
263
|
+
translate_task_id,
|
|
264
|
+
completed=event["overall_progress"],
|
|
265
|
+
refresh=True,
|
|
266
|
+
)
|
|
267
|
+
progress.refresh()
|
|
268
|
+
|
|
269
|
+
return progress, progress_handler
|
|
270
|
+
else:
|
|
271
|
+
pbar = tqdm.tqdm(total=100, desc="translate")
|
|
272
|
+
|
|
273
|
+
def progress_handler(event):
|
|
274
|
+
if event["type"] == "progress_update":
|
|
275
|
+
pbar.update(event["overall_progress"] - pbar.n)
|
|
276
|
+
pbar.set_description(
|
|
277
|
+
f"{event['stage']} ({event['stage_current']}/{event['stage_total']})",
|
|
278
|
+
)
|
|
279
|
+
elif event["type"] == "progress_end":
|
|
280
|
+
pbar.set_description(f"{event['stage']} (Complete)")
|
|
281
|
+
pbar.refresh()
|
|
282
|
+
|
|
283
|
+
return pbar, progress_handler
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
async def main():
|
|
287
|
+
parser = create_parser()
|
|
288
|
+
args = parser.parse_args()
|
|
289
|
+
|
|
290
|
+
if args.debug:
|
|
291
|
+
logging.getLogger().setLevel(logging.DEBUG)
|
|
292
|
+
|
|
293
|
+
if args.warmup:
|
|
294
|
+
logger.info("Warmup completed, exiting...")
|
|
295
|
+
return
|
|
296
|
+
|
|
297
|
+
# 验证翻译服务选择
|
|
298
|
+
if not (args.openai or args.google or args.bing):
|
|
299
|
+
parser.error("必须选择一个翻译服务:--openai、--google 或 --bing")
|
|
300
|
+
|
|
301
|
+
# 验证 OpenAI 参数
|
|
302
|
+
if args.openai and not args.openai_api_key:
|
|
303
|
+
parser.error("使用 OpenAI 服务时必须提供 API key")
|
|
304
|
+
|
|
305
|
+
# 实例化翻译器
|
|
306
|
+
if args.openai:
|
|
307
|
+
translator = OpenAITranslator(
|
|
308
|
+
lang_in=args.lang_in,
|
|
309
|
+
lang_out=args.lang_out,
|
|
310
|
+
model=args.openai_model,
|
|
311
|
+
base_url=args.openai_base_url,
|
|
312
|
+
api_key=args.openai_api_key,
|
|
313
|
+
ignore_cache=args.ignore_cache,
|
|
314
|
+
)
|
|
315
|
+
elif args.bing:
|
|
316
|
+
translator = BingTranslator(
|
|
317
|
+
lang_in=args.lang_in,
|
|
318
|
+
lang_out=args.lang_out,
|
|
319
|
+
ignore_cache=args.ignore_cache,
|
|
320
|
+
)
|
|
321
|
+
else:
|
|
322
|
+
translator = GoogleTranslator(
|
|
323
|
+
lang_in=args.lang_in,
|
|
324
|
+
lang_out=args.lang_out,
|
|
325
|
+
ignore_cache=args.ignore_cache,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# 设置翻译速率限制
|
|
329
|
+
set_translate_rate_limiter(args.qps)
|
|
330
|
+
|
|
331
|
+
# 初始化文档布局模型
|
|
332
|
+
if args.rpc_doclayout:
|
|
333
|
+
from babeldoc.docvision.rpc_doclayout import RpcDocLayoutModel
|
|
334
|
+
|
|
335
|
+
doc_layout_model = RpcDocLayoutModel(host=args.rpc_doclayout)
|
|
336
|
+
else:
|
|
337
|
+
from babeldoc.docvision.doclayout import DocLayoutModel
|
|
338
|
+
|
|
339
|
+
doc_layout_model = DocLayoutModel.load_onnx()
|
|
340
|
+
|
|
341
|
+
pending_files = []
|
|
342
|
+
for file in args.files:
|
|
343
|
+
# 清理文件路径,去除两端的引号
|
|
344
|
+
if file.startswith("--files="):
|
|
345
|
+
file = file[len("--files=") :]
|
|
346
|
+
file = file.lstrip("-").strip("\"'")
|
|
347
|
+
if not Path(file).exists():
|
|
348
|
+
logger.error(f"文件不存在:{file}")
|
|
349
|
+
exit(1)
|
|
350
|
+
if not file.endswith(".pdf"):
|
|
351
|
+
logger.error(f"文件不是 PDF 文件:{file}")
|
|
352
|
+
exit(1)
|
|
353
|
+
pending_files.append(file)
|
|
354
|
+
|
|
355
|
+
font_path = get_cache_file_path("source-han-serif-cn.ttf")
|
|
356
|
+
|
|
357
|
+
# 验证字体
|
|
358
|
+
if font_path:
|
|
359
|
+
if not Path(font_path).exists():
|
|
360
|
+
logger.error(f"字体文件不存在:{font_path}")
|
|
361
|
+
exit(1)
|
|
362
|
+
if not str(font_path).endswith(".ttf"):
|
|
363
|
+
logger.error(f"字体文件不是 TTF 文件:{font_path}")
|
|
364
|
+
exit(1)
|
|
365
|
+
|
|
366
|
+
if args.output:
|
|
367
|
+
if not Path(args.output).exists():
|
|
368
|
+
logger.info(f"输出目录不存在,创建:{args.output}")
|
|
369
|
+
try:
|
|
370
|
+
Path(args.output).mkdir(parents=True, exist_ok=True)
|
|
371
|
+
except OSError:
|
|
372
|
+
logger.critical(
|
|
373
|
+
f"Failed to create output folder at {args.output}",
|
|
374
|
+
exc_info=True,
|
|
375
|
+
)
|
|
376
|
+
exit(1)
|
|
377
|
+
else:
|
|
378
|
+
args.output = None
|
|
379
|
+
|
|
380
|
+
for file in pending_files:
|
|
381
|
+
# 清理文件路径,去除两端的引号
|
|
382
|
+
file = file.strip("\"'")
|
|
383
|
+
# 创建配置对象
|
|
384
|
+
config = TranslationConfig(
|
|
385
|
+
input_file=file,
|
|
386
|
+
font=font_path,
|
|
387
|
+
pages=args.pages,
|
|
388
|
+
output_dir=args.output,
|
|
389
|
+
translator=translator,
|
|
390
|
+
debug=args.debug,
|
|
391
|
+
lang_in=args.lang_in,
|
|
392
|
+
lang_out=args.lang_out,
|
|
393
|
+
no_dual=args.no_dual,
|
|
394
|
+
no_mono=args.no_mono,
|
|
395
|
+
qps=args.qps,
|
|
396
|
+
formular_font_pattern=args.formular_font_pattern,
|
|
397
|
+
formular_char_pattern=args.formular_char_pattern,
|
|
398
|
+
split_short_lines=args.split_short_lines,
|
|
399
|
+
short_line_split_factor=args.short_line_split_factor,
|
|
400
|
+
doc_layout_model=doc_layout_model,
|
|
401
|
+
skip_clean=args.skip_clean,
|
|
402
|
+
dual_translate_first=args.dual_translate_first,
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
# Create progress handler
|
|
406
|
+
progress_context, progress_handler = create_progress_handler(config)
|
|
407
|
+
|
|
408
|
+
# 开始翻译
|
|
409
|
+
with progress_context:
|
|
410
|
+
async for event in babeldoc.high_level.async_translate(config):
|
|
411
|
+
progress_handler(event)
|
|
412
|
+
if config.debug:
|
|
413
|
+
logger.debug(event)
|
|
414
|
+
if event["type"] == "finish":
|
|
415
|
+
result = event["translate_result"]
|
|
416
|
+
logger.info("Translation Result:")
|
|
417
|
+
logger.info(f" Original PDF: {result.original_pdf_path}")
|
|
418
|
+
logger.info(f" Time Cost: {result.total_seconds:.2f}s")
|
|
419
|
+
logger.info(f" Mono PDF: {result.mono_pdf_path or 'None'}")
|
|
420
|
+
logger.info(f" Dual PDF: {result.dual_pdf_path or 'None'}")
|
|
421
|
+
break
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
# for backward compatibility
|
|
425
|
+
def create_cache_folder():
|
|
426
|
+
return babeldoc.high_level.create_cache_folder()
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
# for backward compatibility
|
|
430
|
+
def download_font_assets():
|
|
431
|
+
return babeldoc.high_level.download_font_assets()
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def cli():
|
|
435
|
+
"""Command line interface entry point."""
|
|
436
|
+
from rich.logging import RichHandler
|
|
437
|
+
|
|
438
|
+
logging.basicConfig(level=logging.INFO, handlers=[RichHandler()])
|
|
439
|
+
|
|
440
|
+
logging.getLogger("httpx").setLevel("CRITICAL")
|
|
441
|
+
logging.getLogger("httpx").propagate = False
|
|
442
|
+
logging.getLogger("openai").setLevel("CRITICAL")
|
|
443
|
+
logging.getLogger("openai").propagate = False
|
|
444
|
+
logging.getLogger("httpcore").setLevel("CRITICAL")
|
|
445
|
+
logging.getLogger("httpcore").propagate = False
|
|
446
|
+
logging.getLogger("http11").setLevel("CRITICAL")
|
|
447
|
+
logging.getLogger("http11").propagate = False
|
|
448
|
+
for v in logging.Logger.manager.loggerDict.values():
|
|
449
|
+
if getattr(v, "name", None) is None:
|
|
450
|
+
continue
|
|
451
|
+
if (
|
|
452
|
+
v.name.startswith("pdfminer")
|
|
453
|
+
or v.name.startswith("peewee")
|
|
454
|
+
or v.name.startswith("httpx")
|
|
455
|
+
or "http11" in v.name
|
|
456
|
+
or "openai" in v.name
|
|
457
|
+
):
|
|
458
|
+
v.disabled = True
|
|
459
|
+
v.propagate = False
|
|
460
|
+
|
|
461
|
+
babeldoc.high_level.init()
|
|
462
|
+
asyncio.run(main())
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
if __name__ == "__main__":
|
|
466
|
+
cli()
|