Refactor the registry class logic and remove duplicate code for the same functionality.

This commit is contained in:
Ashesh Vashi 2021-06-24 11:30:11 +05:30 committed by Akshay Joshi
parent a53be65ab0
commit 9574f43f87
13 changed files with 334 additions and 148 deletions

View File

@ -223,7 +223,7 @@ def get_auth_sources(type):
if type in auth_sources:
return auth_sources[type]
auth_source = AuthSourceRegistry.create(type)
auth_source = AuthSourceRegistry.get(type)
if auth_source is not None:
auth_sources[type] = auth_source
@ -236,7 +236,7 @@ def init_app(app):
auth_sources = dict()
setattr(app, '_pgadmin_auth_sources', auth_sources)
AuthSourceRegistry.load_auth_sources()
AuthSourceRegistry.load_modules(app)
return auth_sources

View File

@ -10,56 +10,9 @@
"""External Authentication Registry."""
from flask_babelex import gettext
from abc import ABCMeta
from pgadmin.utils.dynamic_registry import create_registry_metaclass
def _decorate_cls_name(module_name):
length = len(__package__) + 1
if len(module_name) > length and module_name.startswith(__package__):
return module_name[length:]
return module_name
class AuthSourceRegistry(ABCMeta):
registry = None
auth_sources = dict()
def __init__(self, name, bases, d):
# Register this type of auth_sources, based on the module name
# Avoid registering the BaseAuthentication itself
AuthSourceRegistry.registry[_decorate_cls_name(d['__module__'])] = self
ABCMeta.__init__(self, name, bases, d)
@classmethod
def create(cls, name, **kwargs):
if name in AuthSourceRegistry.auth_sources:
return AuthSourceRegistry.auth_sources[name]
if name in AuthSourceRegistry.registry:
AuthSourceRegistry.auth_sources[name] = \
(AuthSourceRegistry.registry[name])(**kwargs)
return AuthSourceRegistry.auth_sources[name]
raise NotImplementedError(
gettext(
"Authentication source '{0}' has not been implemented."
).format(name)
)
@classmethod
def load_auth_sources(cls):
# Initialize the registry only if it has not yet been initialized
if AuthSourceRegistry.registry is None:
AuthSourceRegistry.registry = dict()
from importlib import import_module
from werkzeug.utils import find_modules
for module_name in find_modules(__package__, True):
import_module(module_name)
AuthSourceRegistry = create_registry_metaclass(
"AuthSourceRegistry", __package__, decorate_as_module=True
)

View File

@ -14,7 +14,7 @@ from regression.python_test_utils.template_helper import file_as_template
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils
DriverRegistry.load_drivers()
DriverRegistry.load_modules()
class TestColumnForeignKeyGetConstraintCols(BaseTestGenerator):

View File

@ -12,32 +12,19 @@ from flask import current_app
from .registry import DriverRegistry
def get_driver(type, app=None):
def get_driver(_type, app=None):
if app is not None:
DriverRegistry.load_drivers()
DriverRegistry.load_modules(app)
drivers = getattr(app or current_app, '_pgadmin_server_drivers', None)
if drivers is None or not isinstance(drivers, dict):
drivers = dict()
if type in drivers:
return drivers[type]
driver = DriverRegistry.create(type)
if driver is not None:
drivers[type] = driver
setattr(app or current_app, '_pgadmin_server_drivers', drivers)
return driver
return DriverRegistry.get(_type)
def init_app(app):
drivers = dict()
setattr(app, '_pgadmin_server_drivers', drivers)
DriverRegistry.load_drivers()
DriverRegistry.load_modules(app)
return drivers

View File

@ -9,80 +9,9 @@
from abc import ABCMeta
from flask_babelex import gettext
from pgadmin.utils.dynamic_registry import create_registry_metaclass
def _decorate_cls_name(module_name):
length = len(__package__) + 1
if len(module_name) > length and module_name.startswith(__package__):
return module_name[length:]
return module_name
class DriverRegistry(ABCMeta):
"""
class DriverRegistry(object)
Every Driver will be registered automatically by its module name.
This uses factory pattern to genreate driver object based on its name
automatically.
Class-level Methods:
----------- -------
* __init__(...)
- It will be used to register type of drivers. You don't need to call
this function explicitly. This will be automatically executed, whenever
we create class and inherit from BaseDriver, it will register it as
available driver in DriverRegistry. Because - the __metaclass__ for
BaseDriver is set it to DriverRegistry, and it will create new instance
of this DriverRegistry per class.
* create(type, *args, **kwargs)
- Create type of driver object for this server, from the available
driver list (if available, or raise exception).
* load_drivers():
- Use this function from init_app(...) to load all available drivers in
the registry.
"""
registry = None
drivers = dict()
def __init__(self, name, bases, d):
# Register this type of driver, based on the module name
# Avoid registering the BaseDriver itself
if name != 'BaseDriver':
DriverRegistry.registry[_decorate_cls_name(d['__module__'])] = self
ABCMeta.__init__(self, name, bases, d)
@classmethod
def create(cls, name, **kwargs):
if name in DriverRegistry.drivers:
return DriverRegistry.drivers[name]
if name in DriverRegistry.registry:
DriverRegistry.drivers[name] = \
(DriverRegistry.registry[name])(**kwargs)
return DriverRegistry.drivers[name]
raise NotImplementedError(
gettext("Driver '{0}' has not been implemented.").format(name)
)
@classmethod
def load_drivers(cls):
# Initialize the registry only if it has not yet been initialized
if DriverRegistry.registry is None:
DriverRegistry.registry = dict()
from importlib import import_module
from werkzeug.utils import find_modules
for module_name in find_modules(__package__, True):
import_module(module_name)
DriverRegistry = create_registry_metaclass(
"DriverRegistry", __package__, decorate_as_module=True
)

View File

@ -0,0 +1,104 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2021, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
#########################################################################
"""This file contains functions for creating dynamic registry meta class."""
from abc import ABCMeta
# Constructor
def __constructor(self, name, bases, kwargs):
# Register this type of auth_sources, based on the module name
# Avoid registering the BaseAuthentication itself
cls = self.__class__
entry = self._decorate_cls_name(name, kwargs)
if cls._registry is None:
cls._registry = dict()
else:
if entry in cls._registry:
raise RuntimeError((
"{} class is already been registered with {} "
"(package: {})"
).format(entry, cls._name_, cls._package_))
if cls._initialized is True:
cls._registry[entry] = self
cls._initialized = True
ABCMeta.__init__(self, name, bases, kwargs)
@classmethod
def __get(cls, name, **kwargs):
if name in cls._objects:
return cls._objects[name]
if cls._registry is not None and name in cls._registry:
cls._objects[name] = (cls._registry[name])(**kwargs)
return cls._objects[name]
raise NotImplementedError(
"{} '{}' has not been implemented.".format(cls.__name__, name)
)
@classmethod
def __load_modules(cls, app=None):
# Initialize the registry only if it has not yet been initialized
if cls._registry is None:
cls._registry = dict()
from importlib import import_module
from werkzeug.utils import find_modules
for module_name in find_modules(cls._package_, True):
module = import_module(module_name)
if "init_app" in module.__dict__.keys():
module.__dict__["init_app"](app)
def __get_module_name(self, name, kwargs):
module_name = kwargs["__module__"]
length = len(self._package_) + 1
if len(module_name) > length and module_name.startswith(self._package_):
return module_name[length:]
return module_name
def __get_class_name(self, name, kwargs):
return name
def create_registry_metaclass(name, package, decorate_as_module=True):
class_params = {
# constructor
"__init__": __constructor,
# Class members
"_registry": None,
"_objects": dict(),
"_package_": package,
# Member functions
"get": __get,
"load_modules": __load_modules,
"_name_": name,
"_decorate_cls_name": __get_module_name
if decorate_as_module is True else __get_class_name,
"_initialized": False,
}
# Creating class dynamically
return type(package + "." + name, (ABCMeta, ), class_params)

View File

@ -0,0 +1,8 @@
#######################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2021, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################

View File

@ -0,0 +1,99 @@
#######################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2021, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import six
from pgadmin.utils.dynamic_registry import create_registry_metaclass
from .registry import TestModuleRegistry, TestNamedRegistry, TestNameBase
from .test1 import TestModule1
def test_load_modules_based_registry():
TestModuleRegistry.load_modules()
if len(TestModuleRegistry._registry) != 2:
return "Length of the registry should have been 2"
if 'test1' not in TestModuleRegistry._registry:
return "'test1' is not found in the registry"
if 'test2' not in TestModuleRegistry._registry:
return "'test2' is not found in the registry"
obj_test1_1 = TestModuleRegistry.get('test1')
obj_test1_2 = TestModuleRegistry.get('test1')
obj_test2_1 = TestModuleRegistry.get('test2')
if id(obj_test1_1) != id(obj_test1_2):
return "Registry has created two separate instances"
if isinstance(obj_test1_1, TestModule1) is False:
return "Registry created wrong object"
if id(obj_test1_1) == id(obj_test2_1):
return "Registry should have created a separate instances for " \
"different classes"
def test_load_classname_registry():
TestNamedRegistry.load_modules()
if 'ClassTestName1' not in TestNamedRegistry._registry:
return "'ClassTestName1' is not found in the registry"
if 'ClassTestName2' not in TestNamedRegistry._registry:
return "'ClassTestName2' is not found in the registry"
obj_test1_1 = TestNamedRegistry.get('ClassTestName1')
obj_test1_2 = TestNamedRegistry.get('ClassTestName1')
obj_test2_1 = TestNamedRegistry.get('ClassTestName2')
if id(obj_test1_1) != id(obj_test1_2):
return "Registry has created two separate instances"
if id(obj_test1_1) == id(obj_test2_1):
return "Registry should have created a separate instances for " \
"different classes"
try:
class ClassTestName1(TestNameBase): # NOSONAR
pass
return "Expected an runtime error when using the same classname"
except RuntimeError:
pass
def test_empty_registry():
EmptyModuleRegistry = create_registry_metaclass( # NOSONAR
"EmptyModuleRegistry", __package__, decorate_as_module=True
)
if EmptyModuleRegistry._registry is not None:
return "Registry was supposed to be None"
if EmptyModuleRegistry._initialized is not False:
return "Registry initialized flag should be false"
def test_create_base_class():
RegistryWithBaseClass = create_registry_metaclass( # NOSONAR
'RegistryWithBaseClass', __package__, decorate_as_module=False
)
@six.add_metaclass(RegistryWithBaseClass)
class TestBase(object):
pass
registry = RegistryWithBaseClass._registry
if registry is None or len(registry) != 0:
return "Registry was not supposed to be None, and empty"
if RegistryWithBaseClass._initialized is False:
return "Registry initialized flag should not be true"

View File

@ -0,0 +1,38 @@
#######################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2021, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import six
from pgadmin.utils.dynamic_registry import create_registry_metaclass
TestModuleRegistry = create_registry_metaclass(
'TestModuleRegistry', __package__, decorate_as_module=True
)
TestNamedRegistry = create_registry_metaclass(
'TestRegistry', __package__, decorate_as_module=False
)
@six.add_metaclass(TestModuleRegistry)
class TestModuleBase(object):
pass
@six.add_metaclass(TestNamedRegistry)
class TestNameBase(object):
pass
class ClassTestName1(TestNameBase):
pass
class ClassTestName2(TestNameBase):
pass

View File

@ -0,0 +1,15 @@
#######################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2021, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
from .registry import TestModuleBase
# This class will be registered with TestModuleRegistry (registry)
class TestModule1(TestModuleBase):
pass

View File

@ -0,0 +1,14 @@
#######################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2021, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
from .registry import TestModuleBase
# This class will be registered with TestModuleRegistry (registry)
class TestModule2(TestModuleBase):
pass

View File

@ -0,0 +1,39 @@
#######################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2021, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
from pgadmin.utils.route import BaseTestGenerator
from .registry import test_load_modules_based_registry, test_empty_registry, \
test_create_base_class, test_load_classname_registry
class TestDynamicRegistry(BaseTestGenerator):
scenarios = [
(
"Check empty registry",
dict(test=test_empty_registry),
),
(
'Load the registry based on the modules',
dict(test=test_load_modules_based_registry),
),
(
'Load the registry based on the name of the classes',
dict(test=test_load_classname_registry),
),
(
"When created a base class registry is initialized",
dict(test=test_create_base_class),
),
]
def runTest(self):
error = self.test()
if error is not None:
self.fail(error)

View File

@ -15,7 +15,7 @@ from pgadmin.utils.driver import DriverRegistry
from pgadmin.utils.versioned_template_loader \
import get_version_mapping_directories
DriverRegistry.load_drivers()
DriverRegistry.load_modules()
class SQLTemplateTestBase(BaseTestGenerator):