|
|
@@ -145,35 +145,28 @@ def get_module_class(inputUrl: InputURL) -> typing.Type['Module']: |
|
|
|
# This can't be done at the top because the modules need to refer back to the Module class. |
|
|
|
import codearchiver.modules |
|
|
|
|
|
|
|
# Collect all the Module subclasses and their inheritance level |
|
|
|
modules = {} # Module -> level:int |
|
|
|
# Collect all the Module subclasses |
|
|
|
modules = set() |
|
|
|
q = queue.Queue() |
|
|
|
q.put_nowait((Module, 0)) |
|
|
|
q.put_nowait(Module) |
|
|
|
while not q.empty(): |
|
|
|
class_, level = q.get_nowait() |
|
|
|
class_ = q.get_nowait() |
|
|
|
for c in class_.__subclasses__(): |
|
|
|
logger.debug(f'Found module {c.__module__}.{c.__name__} at level {level + 1}') |
|
|
|
modules[c] = level + 1 # Implicitly only keeps the highest level, i.e. deepest inheritance |
|
|
|
q.put_nowait((c, level + 1)) |
|
|
|
|
|
|
|
# Restructure into level->[modules] mapping |
|
|
|
levels = collections.defaultdict(list) |
|
|
|
for class_, level in modules.items(): |
|
|
|
levels[level].append(class_) |
|
|
|
|
|
|
|
# Process in descending level order |
|
|
|
for level in reversed(levels): |
|
|
|
matches = [class_ for class_ in levels[level] if class_.matches(inputUrl)] |
|
|
|
if len(matches) >= 2: |
|
|
|
logger.warning('Multiple matching modules for input URL, using the first found') |
|
|
|
logger.debug(f'Matching modules at level {level}: {matches!r}') |
|
|
|
logger.debug(f'Modules: {levels!r}') |
|
|
|
if matches: |
|
|
|
logger.info(f'Selecting module {matches[0].__module__}.{matches[0].__name__}') |
|
|
|
return matches[0] |
|
|
|
logger.debug(f'Found module {c.__module__}.{c.__name__}') |
|
|
|
modules.add(c) |
|
|
|
q.put_nowait(c) |
|
|
|
|
|
|
|
matches = [class_ for class_ in modules if class_.matches(inputUrl)] |
|
|
|
if len(matches) >= 2: |
|
|
|
logger.error('Multiple matching modules for input URL') |
|
|
|
logger.debug(f'Matching modules: {matches!r}') |
|
|
|
raise RuntimeError('Multiple matching modules for input URL') |
|
|
|
if matches: |
|
|
|
logger.info(f'Selecting module {matches[0].__module__}.{matches[0].__name__}') |
|
|
|
return matches[0] |
|
|
|
raise RuntimeError('No matching modules for input URL') |
|
|
|
|
|
|
|
|
|
|
|
def get_module_instance(inputUrl: InputURL, **kwargs) -> 'Module': |
|
|
|
'''Get an instance of the Module class most suitable for handling `inputUrl`.''' |
|
|
|
return get_module_class(inputUrl)(inputUrl, **kwargs) |
|
|
|
|