Skip to content

Commit 8693b04

Browse files
committed
Add Configuration overview
1 parent de0c505 commit 8693b04

4 files changed

Lines changed: 192 additions & 0 deletions

File tree

‎setup.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,7 @@ def build():
640640
ret.append( (f'{g_root}/src/utils.py', to_dir) )
641641
ret.append( (f'{g_root}/src/_wxcolors.py', to_dir) )
642642
ret.append( (f'{g_root}/src/_apply_pages.py', to_dir) )
643+
ret.append( (f'{g_root}/src/show_features.py', to_dir) )
643644
ret.append( (f'{g_root}/src/build/extra.py', to_dir) )
644645
ret.append( (b'', f'{to_dir}/py.typed') )
645646
ret.append( (f'{g_root}/src/build/{path_so_leaf}', to_dir) )
@@ -1390,6 +1391,7 @@ def platform_release_tuple():
13901391
g_entry_points = textwrap.dedent('''
13911392
[console_scripts]
13921393
pymupdf = pymupdf.__main__:main
1394+
pymupdf-config = pymupdf.show_features:config
13931395
''')
13941396

13951397

‎src/__main__.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,7 @@ def _internal(args):
844844
pymupdf.message('This is from PyMuPDF message().')
845845
pymupdf.log('This is from PyMuPDF log().')
846846

847+
847848
def main():
848849
"""Define command configurations."""
849850
parser = argparse.ArgumentParser(

‎src/show_features.py‎

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import subprocess
2+
import sys
3+
import importlib.metadata
4+
import pymupdf
5+
6+
def get_tesseract_version():
7+
try:
8+
out = subprocess.check_output(
9+
["tesseract", "--version"], stderr=subprocess.STDOUT
10+
)
11+
line = out.decode().splitlines()[0]
12+
return line.split()[-1]
13+
except Exception:
14+
return None
15+
16+
17+
def get_pkg_version(name: str):
18+
try:
19+
return importlib.metadata.version(name)
20+
except importlib.metadata.PackageNotFoundError:
21+
return None
22+
23+
24+
_pymupdf_feature_list = [
25+
(
26+
"pymupdf_core",
27+
pymupdf.__version__,
28+
"Core PyMuPDF Functionality",
29+
"",
30+
),
31+
(
32+
"pymupdf4llm",
33+
pymupdf.__version__,
34+
"4LLM Extensions",
35+
"",
36+
),
37+
(
38+
"pymupdf_layout",
39+
pymupdf.__version__,
40+
"ML Based Layout Extensions",
41+
"",
42+
),
43+
(
44+
"pymupdf_fonts",
45+
"1.0.4",
46+
"Additional Fonts",
47+
"An optional package that makes more fonts available to PyMuPDF programmers.",
48+
),
49+
(
50+
"pymupdf_office",
51+
pymupdf.__version__,
52+
"Extended Support for Office Documents",
53+
"A commercial extension that handles Office files like native documents.\n Install via 'pip install pymupdf-office', and obtain a license key from this\n website: https://pymupdf.io/office",
54+
),
55+
]
56+
57+
_other_feature_list = [
58+
(
59+
"tesseract",
60+
"4.0.0",
61+
"Tesseract OCR Engine",
62+
"A popular OCR engine that PyMuPDF can use during document extraction.",
63+
),
64+
(
65+
"rapidocr",
66+
"3.9.2",
67+
"Rapid OCR Engine",
68+
"An OCR engine that PyMuPDF can use during document extraction.\n Its installation is strongly recommended for much better OCR results\n than with Tesseract OCR alone.",
69+
),
70+
(
71+
"Pillow",
72+
"0",
73+
"Python Imaging Library",
74+
"A popular image library that PyMuPDF can use to deal with images having exotic formats.",
75+
),
76+
(
77+
"pandas",
78+
"0",
79+
"Data Analysis Library",
80+
"A popular data analysis library that PyMuPDF can use for storing tables as DataFrames.",
81+
),
82+
]
83+
84+
85+
def _check_installed_package_version(pack, vsn):
86+
if pack == "tesseract": # this is not a Python package!
87+
version = get_tesseract_version()
88+
else:
89+
version = get_pkg_version(pack)
90+
if version is None:
91+
return 2, None
92+
if version >= vsn:
93+
return 0, version
94+
else:
95+
return 1, version
96+
97+
98+
def _build_feature_dict(feature_list):
99+
"""
100+
Builds a dict {package_name: info_dict} for the given list of
101+
(pack, required_version, short, desc) tuples.
102+
"""
103+
_STATE_LABELS = {
104+
0: "installed",
105+
1: "outdated",
106+
2: "missing",
107+
}
108+
109+
result = {}
110+
for pack, vsn, short, desc in feature_list:
111+
state, installed_version = _check_installed_package_version(pack, vsn)
112+
result[pack] = {
113+
"required_version": vsn,
114+
"installed_version": installed_version,
115+
"status": _STATE_LABELS[state],
116+
"short": short,
117+
"desc": desc,
118+
}
119+
return result
120+
121+
122+
def print_feature_dict(title, feature_dict):
123+
print(f"\n{title}")
124+
print("=" * len(title))
125+
max_pack_len = max(len(pack) for pack in feature_dict.keys()) + 2
126+
pack_fmt = f" {{:<{max_pack_len}}}"
127+
max_version_len = (
128+
max(len(info["required_version"]) for info in feature_dict.values()) + 2
129+
)
130+
max_version_len = max(max_version_len, len("min required") + 2)
131+
version_fmt = f"{{:<{max_version_len}}}"
132+
header = f"{'Package':<{max_pack_len+2}}{'Min Required':<{max_version_len}}Status"
133+
print(header)
134+
print("-" * (len(header) + 15))
135+
for pack, info in feature_dict.items():
136+
status = info["status"]
137+
vsn = info["required_version"]
138+
if vsn == "0":
139+
vsn = "any"
140+
this_version = info["installed_version"]
141+
if status == "missing":
142+
print(pack_fmt.format(pack) + version_fmt.format(vsn) + "not installed")
143+
print(f" {info['desc']}")
144+
elif status == "outdated":
145+
print(
146+
pack_fmt.format(pack)
147+
+ version_fmt.format(vsn)
148+
+ f"installed: {this_version} (out of date)"
149+
)
150+
print(f" {info['short']}")
151+
elif status == "installed":
152+
if vsn == "any" or vsn != this_version:
153+
print(
154+
pack_fmt.format(pack)
155+
+ version_fmt.format(vsn)
156+
+ f"installed: {this_version}"
157+
)
158+
else:
159+
print(pack_fmt.format(pack) + version_fmt.format(vsn) + "installed")
160+
161+
print(f" {info['short']}")
162+
print()
163+
164+
165+
def collect_features():
166+
"""
167+
Determines the install status of all the different packages that
168+
contribute to PyMuPDF features (both default and optional ones) and
169+
stores the results in two dictionaries:
170+
171+
- pymupdf_components: the PyMuPDF packages themselves
172+
- other_packages: useful third-party packages PyMuPDF can use
173+
174+
Returns:
175+
tuple[dict, dict]: (pymupdf_components, other_packages)
176+
"""
177+
pymupdf_components = _build_feature_dict(_pymupdf_feature_list)
178+
other_packages = _build_feature_dict(_other_feature_list)
179+
return pymupdf_components, other_packages
180+
181+
182+
def config():
183+
print(f"Python: {sys.version}")
184+
print("PyMuPDF Configuration Overview")
185+
pymupdf_components, other_packages = collect_features()
186+
print_feature_dict("PyMuPDF Configuration", pymupdf_components)
187+
print_feature_dict("Useful Third-party Packages for PyMuPDF", other_packages)
188+

‎tests/test_pylint.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ def test_pylint():
129129
'pymupdf.py',
130130
'table.py',
131131
'utils.py',
132+
'show_features.py',
132133
]
133134
leafs.sort()
134135
try:

0 commit comments

Comments
 (0)