package for factory mechanism for sub classes and auto import mechanism
Project description
class_factory - package for factory mechanism for sub classes and auto import mechanism
This package contains the 'ClassFactoryUser' class that allow a parent class and its subclass to access
a factory mechanism to create new instances.
The class also enable the automatic import mehanism for subclasses. Handy when you design a all
familly of sub classes or plugin mechanism.
Basic Use
What you have to do is listed in the doc of the base class :
"""
This class represents the base class for classes using a factory for their subclasses
The following methods have to be overloaded :
* __get_factory_id
* __get_import_data_for_factory
* __get_base_class_for_factory
* __get_id_function_name_for_factory
The following methods may be overloaded :
* __get_handled_exceptions_for_factory
* __get_id_exception_for_factory
* add_base_class
"""
You also have of course to inherit from this base class :
from class_factory import ClassFactoryUser
class MyParentClass(ClassFactoryUser):
...
Description of the methods to overload
__get_factory_id
doc
@classmethod
def __get_factory_id(cls):
"""
This method is designed to get the ID of the factory dedicated to this class
The name of the directory where subclasses are to be found is a good idea
:rtype: str
:return: the ID of the factory dedicated to this class
"""
Details
Indeed, this method just requires to return a simple str ID. If you have absolutely no idea,
you can event return an empty string.
The role of this string is to differentiate several families of subclass, so if you have a single
family don't worry about it.
Example of completion
@classmethod
def __get_factory_id(cls):
"""
This method is designed to get the ID of the factory dedicated to this class
The name of the directory where subclasses are to be found is a good idea
:rtype: str
:return: the ID of the factory dedicated to this class
"""
return "My Plugins"
__get_base_class_for_factory
doc
@classmethod
def __get_base_class_for_factory(cls):
"""
This method is designed to get the base class for the factory
:rtype: type
:return: the base class for the factory
"""
Details
Here you have to return a class the one from which all subclasses referenced in the factory will inherit.
Usualy, you will return the class that directly inherit from ClassFactoryUser. Be careful in this
case and return precisely the class not 'cls' as it would change for any subclass.
Example of completion
@classmethod
def __get_base_class_for_factory(cls):
"""
This method is designed to get the base class for the factory
:rtype: type
:return: the base class for the factory
"""
return MyParentClass
__get_id_function_name_for_factory
doc
@classmethod
def __get_id_function_name_for_factory(cls):
"""
This method is designed to get the name of the method returning the ID of the class for the factory
:rtype: str
:return: the name of the method returning the ID of the class for the factory
"""
Details
In the factory, each sub class is identified by an ID. This ID is taken as the result of a class method. You will have to implement such a class method, but the factory requires the name of this method. This is precisely what you have to return here.
Example of completion
@classmethod
def __get_id_function_name_for_factory(cls):
"""
This method is designed to get the name of the method returning the ID of the class for the factory
:rtype: str
:return: the name of the method returning the ID of the class for the factory
"""
return "get_plugin_id"
__get_import_data_for_factory
doc
@classmethod
def __get_import_data_for_factory(cls):
"""
This method is designed to get the list of settings to import subclasses. Each item of the list contains :
- import_path : path from which files to import are searched recursively : <import_path>/*/**/<format_name>
- import_val : string replacing import path to import the files.
ex : <import_path>/a/b/<file name>
- if import_val == "":
the file will be imported this way : "from a.b import <module name>
- otherwise:
the file will be imported this way "from <import_val>.a.b import <module name>
- format_name : glob token used to find the file to import (ex : task_*.py)
:rtype: list[(str, str, str)]
:return: the list of settings to import subclasses : (import_path, import_val, format_name)
"""
Details
This is the technical part. You will have to tell the factory from where and how it will import the subclasses. You may want to import from different directories. This is usually the case when you have embeded plugins and user designed plugins in an other directory. All python modules containing a sub class will be searched through a glob pattern. To ease this mechanism it is a good practice to have a precise prefix or suffix for all those modules.
For each directory, you will have to return as described in the doc :
- import_path : path from which files to import are searched recursively :
- import_val : string replacing import path to import the files.
- format_name : glob token used to find the file to import (ex : task_*.py)
Example of completion
@classmethod
def __get_import_data_for_factory(cls):
"""
This method is designed to get the list of settings to import subclasses. Each item of the list contains :
- import_path : path from which files to import are searched recursively : <import_path>/*/**/<format_name>
- import_val : string replacing import path to import the files.
ex : <import_path>/a/b/<file name>
- if import_val == "":
the file will be imported this way : "from a.b import <module name>
- otherwise:
the file will be imported this way "from <import_val>.a.b import <module name>
- format_name : glob token used to find the file to import (ex : task_*.py)
:rtype: list[(str, str, str)]
:return: the list of settings to import subclasses : (import_path, import_val, format_name)
"""
my_dir = os.path.dirname(os.path.abspath("__file__"))
import_path = os.path.join(my_dir, "plugins")
format_name = "*_plugin.py"
import_val = "my_appp.plugins"
return [(import_path, import_val, format_name)]
add_base_class
doc
@classmethod
def add_base_class(cls):
"""
This method is designed to know if the base class must be regitred in the factory
:rtype: bool
:return: True if the base class must be registred in the factory, False otherwise
"""
Details
Usualy, the base class is not contained by the factory (supposed to be an abstract class). If you want to register the base class, have this method return True. (Default behaviour is 'return False')
Example of completion
@classmethod
def add_base_class(cls):
"""
This method is designed to know if the base class must be regitred in the factory
:rtype: bool
:return: True if the base class must be registred in the factory, False otherwise
"""
return True
__get_handled_exceptions_for_factory
doc
@classmethod
def __get_handled_exceptions_for_factory(cls):
"""
This method is designed to get the tuple of exception types to be displayed directly,
without traceback when there is an import problem
:rtype handled_exceptions: tuple[Type[Exception]]
:return: tuple of exception types to be displayed directly, without traceback when there is an import problem
"""
Details
The factory mechanism as been designed in order to ignore bugs when a sub class module is imported. A warning is still displayed. The default behaviour is to display the full traceback of the error that occured. However sometime, you may have conceived a precise error/exception and simply displaying this exception is enough. For instance, when one of your pluggin depends on a third party library.
Here you have to return the list of Exceptions that will be directly displayed without traceback
Example of completion
@classmethod
def __get_handled_exceptions_for_factory(cls):
"""
This method is designed to get the tuple of exception types to be displayed directly,
without traceback when there is an import problem
:rtype handled_exceptions: tuple[Type[Exception]]
:return: tuple of exception types to be displayed directly, without traceback when there is an import problem
"""
return (PyQtNotFoundLibrary, IncompleteEnvironmentError)
__get_id_exception_for_factory
doc
@classmethod
def __get_id_exception_for_factory(cls):
"""
This method is designed to get the Exception or method instanciating an Exception when an ID is not found
:rtype: NoneType | (str) -> Exception
:return: Exception or method instanciating an Exception when an ID is not found
"""
Details
When you will use the factory, you will have to specify the ID of the instance you want to create. Having a dedicated error for a unfound ID may be usefull for you, and you may have designed such an Exception.
This method must return this exception, or a function that can return such an exception from the missing ID.
Example of completion
@classmethod
def __get_id_exception_for_factory(cls):
"""
This method is designed to get the Exception or method instanciating an Exception when an ID is not found
:rtype: NoneType | (str) -> Exception
:return: Exception or method instanciating an Exception when an ID is not found
"""
return PluginIdNotFoundException
Example of fully completed parent class
import os
from class_factory import ClassFactoryUser
# ====================================
class MyParentClass(ClassFactoryUser):
"""
This class is parent class for all plugins
"""
# ==========
@classmethod
def __get_factory_id(cls):
"""
This method is designed to get the ID of the factory dedicated to this class
The name of the directory where subclasses are to be found is a good idea
:rtype: str
:return: the ID of the factory dedicated to this class
"""
return "My Plugins"
# ==========
@classmethod
def __get_base_class_for_factory(cls):
"""
This method is designed to get the base class for the factory
:rtype: type
:return: the base class for the factory
"""
return MyParentClass
# ==========
@classmethod
def __get_id_function_name_for_factory(cls):
"""
This method is designed to get the name of the method returning the ID of the class for the factory
:rtype: str
:return: the name of the method returning the ID of the class for the factory
"""
return "get_plugin_id"
# ==========
@classmethod
def __get_import_data_for_factory(cls):
"""
This method is designed to get the list of settings to import subclasses. Each item of the list contains :
- import_path : path from which files to import are searched recursively : <import_path>/*/**/<format_name>
- import_val : string replacing import path to import the files.
ex : <import_path>/a/b/<file name>
- if import_val == "":
the file will be imported this way : "from a.b import <module name>
- otherwise:
the file will be imported this way "from <import_val>.a.b import <module name>
- format_name : glob token used to find the file to import (ex : task_*.py)
:rtype: list[(str, str, str)]
:return: the list of settings to import subclasses : (import_path, import_val, format_name)
"""
my_dir = os.path.dirname(os.path.abspath("__file__"))
import_path = os.path.join(my_dir, "plugins")
format_name = "*_plugin.py"
import_val = "my_appp.plugins"
return [(import_path, import_val, format_name)]
# ==========
@classmethod
def add_base_class(cls):
"""
This method is designed to know if the base class must be regitred in the factory
:rtype: bool
:return: True if the base class must be registred in the factory, False otherwise
"""
return True
# ==========
@classmethod
def __get_handled_exceptions_for_factory(cls):
"""
This method is designed to get the tuple of exception types to be displayed directly,
without traceback when there is an import problem
:rtype handled_exceptions: tuple[Type[Exception]]
:return: tuple of exception types to be displayed directly, without traceback when there is an import problem
"""
return (PyQtNotFoundLibrary, IncompleteEnvironmentError)
# ==========
@classmethod
def __get_id_exception_for_factory(cls):
"""
This method is designed to get the Exception or method instanciating an Exception when an ID is not found
:rtype: NoneType | (str) -> Exception
:return: Exception or method instanciating an Exception when an ID is not found
"""
return PluginIdNotFoundException
# ==========
@classmethod
def get_plugin_id(cls):
"""
This method is designed to get the ID of the plugin
:rtype: str
:return: the ID of the plugin
"""
return cls.__name__
Designing a sub class
Example
class MyPlugin(MyParentClass):
"""
Class for a plugin
"""
# ==========
@classmethod
def get_plugin_id(cls):
"""
This method is designed to get the ID of the plugin
:rtype: str
:return: the ID of the plugin
"""
return "MY"
Details
Remember the '__get_id_function_name_for_factory' method returning "get_plugin_id" ?
It means that your sub class (in this example) must implement a 'get_plugin_id' class method.
In the previous section the method returned 'cls._name_'. This is a convenient way to have a default
behavious to generate all the ideas, but you need to use a specific ID for each sub class.
Simply overload the method whose name is returned from '__get_id_function_name_for_factory' and don't forget to do it ! It is the only thing you have to do in your sub classes.
Using the factory
Access the factory
Once all your sub classes have been created and registered in the factory, you'll want to use them.
First, you have to get the factory. You can access it from any sub class of ClassFactoryUser
factory = MyParentClass.get_factory()
Then the factory grants you access to several methods
Methods of the factory
get_ids_iterator
The get_ids_iterator method returns an iterator over the different IDs of classes handled by the factory.
get_ids
Similar to the previous method but returns a list instead of an iterator.
get_class_from_id
This method returns a registered class from its ID.
get_instance_from_id
This method is precisely what you expect from a factory and returns an instance of a registered class.
The first argument of the method is the ID of the class from which you want an instane.
The other arguments are the arguments you pass to the class to build your instance
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Hashes for class_factory-1.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 723850e49a98806bda9b316f8943d6b9eebeacbcac6385877068aea50c0870ca |
|
MD5 | 7a3a5c3efffac4f4781a163ad8b5bf78 |
|
BLAKE2b-256 | 9becc6f07936a60e1acafbd46e12039d2cf319dda65af3d72fbd6c00dd18f7b7 |