Skip to content

Commit 1776423

Browse files
committed
Factor out the import of modules from the initialization
load_modules() is split in two: one part imports and registers the modules while the other does the proper initialization of the extension objects (ExampleFinder, ExampleParser, ExampleRunner, ZoneDelimiter and Concern). The first part imports the (python) modules and registers them in sys.modules so they objects are pickle-able and the access to them does not require another import. This is to maintain compatibility with the form that Python pre-3.10 used to load the modules. Also, if a module cannot be loaded (typically due a syntax error), emit an error by default. Adding -vvv will print the full traceback as usual.
1 parent a75eb59 commit 1776423

1 file changed

Lines changed: 45 additions & 16 deletions

File tree

byexample/init.py

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from __future__ import unicode_literals
2-
import sys, pkgutil, inspect, pprint, os, operator
2+
import sys, pkgutil, inspect, pprint, os, operator, traceback
33
import importlib.util
44

55
from itertools import chain as chain_iters
@@ -91,6 +91,38 @@ def _is_empty(self):
9191
return not bool(self._attribute_names())
9292

9393

94+
def import_and_register_modules_iter(dirnames):
95+
''' Import and register the (python) modules located in the given
96+
directories.
97+
98+
The loaded modules will be registered and accessible
99+
from sys.modules as any imported python module.
100+
101+
This function will not try to instantiate any
102+
object from the loaded modules.
103+
104+
Moreover, this function will not assume that it is running
105+
in the main process of byexample so it will not use anything
106+
from byexample's runtime like clog() as this function may
107+
be called by a child process.
108+
'''
109+
for importer, name, is_pkg in pkgutil.iter_modules(dirnames):
110+
path = importer.path
111+
err = None
112+
113+
try:
114+
spec = importer.find_spec(name)
115+
module = importlib.util.module_from_spec(spec)
116+
spec.loader.exec_module(module)
117+
118+
sys.modules[name] = module
119+
120+
except Exception as e:
121+
err = e
122+
123+
yield (path, name, module, err)
124+
125+
94126
@log_context('byexample.load')
95127
@profile
96128
def load_modules(dirnames, cfg):
@@ -103,20 +135,13 @@ def load_modules(dirnames, cfg):
103135
'zdelimiters': {},
104136
}
105137
namespaces_by_class = {}
106-
for importer, name, is_pkg in pkgutil.iter_modules(dirnames):
107-
path = importer.path
108-
109-
clog().debug("From '%s' loading '%s'...", path, name)
110-
111-
try:
112-
spec = importer.find_spec(name)
113-
module = importlib.util.module_from_spec(spec)
114-
spec.loader.exec_module(module)
115-
116-
sys.modules[name] = module
117-
except Exception as e:
118-
clog().info(
119-
"From '%s' loading '%s'...failed: %s", path, name, str(e)
138+
for path, name, module, err in import_and_register_modules_iter(dirnames):
139+
if err:
140+
clog().exception(
141+
"From '%s' loading module '%s' failed. Skipping.",
142+
path,
143+
name,
144+
exc_info=err
120145
)
121146
continue
122147

@@ -127,7 +152,11 @@ def load_modules(dirnames, cfg):
127152
):
128153
stability = 'experimental/%s?' % str(stability)
129154

130-
clog().chat("From '%s' loaded '%s' (%s)", path, name, stability)
155+
clog().chat(
156+
"From '%s' loaded module '%s' (%s). Searching for extensions...",
157+
path, name, stability
158+
)
159+
131160
for klass, key, is_multikey, what in [
132161
(ExampleRunner, 'language', False, 'runners'),
133162
(ExampleParser, 'language', False, 'parsers'),

0 commit comments

Comments
 (0)