Source code for genui.utils.inspection

"""
inspection

Created by: Martin Sicho
On: 4/30/20, 8:55 AM
"""

import importlib
import inspect
import sys

from django.urls import path, include

from genui import apps


[docs]def importFromPackage(package, module_name, exception=True): package_name = package if type(package) != str: package_name = package.__name__ try: return importlib.import_module(f'{package_name}.{module_name}') except ModuleNotFoundError as exp: if exception: raise exp else: return None
[docs]def importModuleWithException(module, *args, message=True, throw=False, **kwargs): try: return importlib.import_module(module, *args, **kwargs) except ModuleNotFoundError as exp: if message: print(f"WARNING: Failed to find core modelling module: {module}. It will be skipped.") if throw: raise exp return
[docs]def getSubclassesFromModule(base_cls, module): """ Fetch all subclasses of a given base class from a module. :param base_cls: The base class. :param module: The module. :return: """ ret = [] for item in inspect.getmembers(module, inspect.isclass): if issubclass(item[1], base_cls): ret.append(item[1]) return ret
[docs]def getSubclasses(cls): """ Fetch all existing subclasses of a class. :param cls: :return: """ return set(cls.__subclasses__()).union([s for c in cls.__subclasses__() for s in getSubclasses(c)])
[docs]def findSubclassByID(base, module, id_attr : str, id_attr_val : str): """ Function to fetch a given class from a certain module. It is identified by both its base class and a value of a specific identifying attribute of the class. :param base: The base class of the class we are looking for. :param module: Module containing the class of interest. :param id_attr: The name of the identifying attribute on the class. :param id_attr_val: The value of the searched attribute. :return: """ all_subclasses = getSubclasses(base) for class_ in all_subclasses: name = class_.__name__ if not hasattr(module, name): continue if not hasattr(class_, id_attr): raise Exception("Unspecified ID attribute on a class where it should be defined. Check if the class is properly annotated: ", repr(class_)) if not (id_attr_val == getattr(class_, id_attr)): continue return class_ raise LookupError(f'Could not find any valid subclass of {base.__name__} in module {module.__name__}.')
[docs]def getFullName(obj, moduleOnly=False): module = inspect.getmodule(obj) if module is None or module == str.__class__.__module__: return obj.__name__ if not moduleOnly else None else: return module.__name__ + '.' + obj.__name__ if not moduleOnly else module.__name__
[docs]def getObjectAndModuleFromFullName(name): module = importlib.import_module('.'.join(name.split(".")[0:-1])) obj = getattr(module, name.split(".")[-1]) return obj, module
[docs]def discover_extensions(packages): ret = [] for package_name in packages: try: package = importlib.import_module(package_name) except ModuleNotFoundError as exp: sys.stderr.write(f"WARNING: Failed to find extensions module: {package_name}. Skipping...\n") continue subs = package.__all__ for sub in subs: ret.append(f"{package_name}.{sub}") return ret
[docs]def disover_app_urls_module(app_name, parent=None): app = importlib.import_module(app_name) error_skipped = False if not importFromPackage(app, 'genuisetup', exception=False): sys.stderr.write(f"WARNING: Expected to find the {app_name}.genuisetup module, but it was not found.\n") error_skipped = True elif not importFromPackage(app, 'urls', exception=False): # sys.stderr.write(f"WARNING: No {app_name}.urls module found for app: {app_name}\n") error_skipped = True elif not hasattr(app.genuisetup, 'PARENT'): if parent == 'genui': return app.urls else: return None elif app.genuisetup.PARENT != parent: return None if error_skipped: # sys.stderr.write(f"WARNING: Skipping urls discovery for: {app_name}\n") return None return app.urls
[docs]def discover_apps_urls(app_names, prefix='', app_names_as_root=False): urls = [] for app in app_names: urls_module = disover_app_urls_module(app, parent='genui') if urls_module: urls.append(path( f'{prefix.rstrip("/") + "/" if prefix else ""}{app.split(".")[1] + "/" if app_names_as_root else ""}', include(urls_module.__name__) ) ) return urls
[docs]def discover_extensions_urlpatterns(parent): ret = [] for extension_name in apps.extensions(): urls = disover_app_urls_module(extension_name, parent) if urls: ret.extend(urls.urlpatterns) return ret