From 5f9547d600bcbce67b7ed4bf7d4cbbfbd7565b92 Mon Sep 17 00:00:00 2001 From: JustAnotherArchivist Date: Fri, 26 Jun 2020 16:22:13 +0000 Subject: [PATCH] Get rid of inheritance-level-based module selection and instead raise an exception if there are no or multiple matching modules --- codearchiver/core.py | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/codearchiver/core.py b/codearchiver/core.py index 982c0d6..9bcd54d 100644 --- a/codearchiver/core.py +++ b/codearchiver/core.py @@ -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) -