From f985f5d0ff27db8f5d86fef701fcedc2f4d12996 Mon Sep 17 00:00:00 2001 From: mohammad Date: Sun, 21 Jun 2026 21:44:49 +0200 Subject: [PATCH 1/2] use importlib --- s2protocol/versions/__init__.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/s2protocol/versions/__init__.py b/s2protocol/versions/__init__.py index aaed865..2dd02a7 100644 --- a/s2protocol/versions/__init__.py +++ b/s2protocol/versions/__init__.py @@ -1,7 +1,7 @@ import os import re -import imp +import importlib import sys @@ -9,8 +9,8 @@ def _import_protocol(base_path, protocol_module_name): """ Import a module from a base path, used to import protocol modules. - This implementation is derived from the __import__ example here: - https://docs.python.org/2/library/imp.html + This implementation is derived from the import_module example here: + https://docs.python.org/3/library/importlib.html#approximating-importlib-import-module """ # Try to return the module if it's been loaded already @@ -19,16 +19,21 @@ def _import_protocol(base_path, protocol_module_name): except KeyError: pass - # If any of the following calls raises an exception, - # there's a problem we can't handle -- let the caller handle it. - # - fp, pathname, description = imp.find_module(protocol_module_name, [base_path]) - try: - return imp.load_module(protocol_module_name, fp, pathname, description) - finally: - # Since we may exit via an exception, close fp explicitly. - if fp: - fp.close() + # Otherwise try to load the module + spec = importlib.util.spec_from_file_location( + protocol_module_name, + os.path.join(base_path, protocol_module_name+'.py') + ) + module = importlib.util.module_from_spec(spec) + + # If the module does not exist, raise an exception and let the + # caller handle it + if( not os.path.exists(spec.origin) ): + raise ImportError("No module named '{}'".format(protocol_module_name)) + + sys.modules[protocol_module_name] = module + spec.loader.exec_module(module) + return module def list_all(base_path=None): From 6d442103ed6990d1225f1cb3de590b7d047d745d Mon Sep 17 00:00:00 2001 From: mohammad Date: Tue, 23 Jun 2026 17:49:27 +0200 Subject: [PATCH 2/2] recovering from missing module --- s2protocol/versions/__init__.py | 41 ++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/s2protocol/versions/__init__.py b/s2protocol/versions/__init__.py index 2dd02a7..2f7fb6a 100644 --- a/s2protocol/versions/__init__.py +++ b/s2protocol/versions/__init__.py @@ -1,10 +1,10 @@ - +import logging import os import re import importlib import sys - +PROTOCOLS=[] def _import_protocol(base_path, protocol_module_name): """ Import a module from a base path, used to import protocol modules. @@ -13,6 +13,21 @@ def _import_protocol(base_path, protocol_module_name): https://docs.python.org/3/library/importlib.html#approximating-importlib-import-module """ + requested_protocol = protocol_module_name + if protocol_module_name not in PROTOCOLS: + fallback_module_name = max( + PROTOCOLS, + key=protocol_version + ) + logger = logging.getLogger(__name__) + logger.warning( + "Protocol %s not supported, using %s", + requested_protocol, + fallback_module_name, + ) + protocol_module_name = fallback_module_name.split('.')[0] + + # Try to return the module if it's been loaded already try: return sys.modules[protocol_module_name] @@ -24,6 +39,10 @@ def _import_protocol(base_path, protocol_module_name): protocol_module_name, os.path.join(base_path, protocol_module_name+'.py') ) + if spec is None or spec.loader is None: + raise ImportError( + f"Unable to load protocol module '{protocol_module_name}'" + ) module = importlib.util.module_from_spec(spec) # If the module does not exist, raise an exception and let the @@ -35,18 +54,28 @@ def _import_protocol(base_path, protocol_module_name): spec.loader.exec_module(module) return module +def protocol_version(name): + m = re.search(r'(\d+)', name) + if not m: + raise ValueError(f"Invalid protocol name: {name}") + return int(m.group(1)) def list_all(base_path=None): """ Returns a list of the current protocol version file names in the versions module sorted by name. """ + global PROTOCOLS if base_path is None: base_path = os.path.dirname(__file__) pattern = re.compile('.*protocol[0-9]+.py$') - files = [ f for f in os.listdir(base_path) \ - if pattern.match(f) ] - files.sort() - return files + PROTOCOLS = [ f for f in os.listdir(base_path) \ + if pattern.match(f) ] + + """ + sort string containing integers is problematic. + """ + PROTOCOLS.sort(key=protocol_version) + return PROTOCOLS def latest():