Skip to content

Commit b701f95

Browse files
committed
REF: Replace import_module_from_file() with pkgutil-based iter_modules_from_dir()
Fixes packaging build with PyInstaller >= 6.10. Refs: pyinstaller/pyinstaller#9015 (comment) Thanks @rokm
1 parent 8a2a0fd commit b701f95

File tree

4 files changed

+13
-50
lines changed

4 files changed

+13
-50
lines changed

efck/tabs/__init__.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,15 @@
11
import logging
22
import typing
3-
from glob import glob
4-
from pathlib import Path
53

64
from ..tab import Tab
7-
from ..util import import_pyinstaller_bundled_submodules, iter_config_dirs, import_module_from_file
5+
from ..util import iter_config_dirs, iter_modules_from_dir
86

97
logger = logging.getLogger(__name__)
108

119
_modules = [] # Fixes missing Filters tab due to lost reference on PySide6
1210

13-
_modules.extend(import_pyinstaller_bundled_submodules(__name__))
14-
1511
for dir in iter_config_dirs('tabs'):
16-
for file in sorted(glob(str(dir / '*.py*'))):
17-
basename = Path(file).stem
18-
if basename.startswith('_'):
19-
continue
20-
logger.debug('Loading tab "%s"', file)
21-
module = import_module_from_file(f'efck.tabs.{basename}', file)
12+
for module in iter_modules_from_dir(dir, 'efck.tabs.'):
2213
_modules.append(module)
2314

2415
# Export tabs here.

efck/tabs/filters.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
import logging
22
from functools import lru_cache
3-
from glob import glob
4-
from pathlib import Path
53

64
from ..qt import *
75
from ..gui import ICON_DIR
86
from ..tab import Tab
9-
from ..util import (
10-
import_module_from_file,
11-
import_pyinstaller_bundled_submodules,
12-
iter_config_dirs,
13-
)
7+
from ..util import iter_config_dirs, iter_modules_from_dir
148

159
logger = logging.getLogger(__name__)
1610

@@ -19,14 +13,9 @@ def load_modules():
1913
"""Return built-in filter modules, shadowed by name by user's config-local filter modules."""
2014
all_modules = {}
2115

22-
for mod in import_pyinstaller_bundled_submodules('efck.filters'):
23-
all_modules[module_basename(mod)] = mod
24-
2516
for dir in iter_config_dirs('filters'):
26-
for file in sorted(glob(str(dir / '*.py*'))):
27-
logger.debug('Loading filter "%s"', file)
28-
mod = import_module_from_file(f'efck.filters.{Path(file).stem}', file)
29-
all_modules[module_basename(mod)] = mod
17+
for module in iter_modules_from_dir(dir, 'efck.filters.'):
18+
all_modules[module_basename(module)] = module
3019

3120
# Empty user's config-local filters by the same name can shadow out builtins
3221
for name, mod in tuple(all_modules.items()):

efck/util.py

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
import importlib.util
22
import logging
3-
import os
3+
import pkgutil
44
from pathlib import Path
55

66
from . import CONFIG_DIRS
77

88
logger = logging.getLogger(__name__)
99

1010

11-
def import_module_from_file(module_name, path):
12-
path = os.path.abspath(path)
13-
# Should handle all sorts of importable modules (*.py, *.pyc, ...)
14-
spec = importlib.util.spec_from_file_location(module_name, path)
15-
module = importlib.util.module_from_spec(spec)
16-
spec.loader.exec_module(module)
17-
return module
11+
def iter_modules_from_dir(dir: str, prefix: str):
12+
for modinfo in pkgutil.iter_modules([dir], prefix=prefix):
13+
spec = modinfo.module_finder.find_spec(modinfo.name)
14+
module = importlib.util.module_from_spec(spec)
15+
spec.loader.exec_module(module)
16+
yield module
1817

1918

2019
def iter_config_dirs(subdir):
@@ -24,19 +23,3 @@ def iter_config_dirs(subdir):
2423
path = Path(dir) / subdir
2524
if path.is_dir():
2625
yield path
27-
28-
29-
def import_pyinstaller_bundled_submodules(package):
30-
# This works with PyInstaller who compiles all our modules into an
31-
# import-hooked archive rather than keeping non-data files on disk.
32-
# A few alternatives would be:
33-
# - Include these submodules in pyinstaller datas (and remove them from PYZ),
34-
# - List and maintain literal imports
35-
# This is safe.
36-
# https://github.com/pyinstaller/pyinstaller/blame/7875d7684c/PyInstaller/loader/pyimod02_importers.py#L115-L117
37-
modules = []
38-
if hasattr(__loader__, 'toc'):
39-
modules = sorted(k for k in __loader__.toc
40-
if k.startswith(f'{package}.') and '._' not in k)
41-
modules = [importlib.import_module(mod) for mod in modules]
42-
return modules

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
'dev': [
4040
'flake8',
4141
'coverage',
42-
'pyinstaller == 6.9',
42+
'pyinstaller',
4343
'pillow', # for pyinstaller
4444
],
4545
'extra': [

0 commit comments

Comments
 (0)