def _check_class_types_duplicates(class_types, classes): """Check duplicated types for input list of class types.""" duplicates = find_duplicates(class_types) if len(duplicates) != 0: # Return the list of classes with duplicate type duplicates_classes = [ (class_tuple[0], get_private(class_tuple[1], 'class_type')) for class_tuple in classes if get_private(class_tuple[1], 'class_type', None) in duplicates] raise ValueError("following classes have the same class type. Fix " "before continue. {:}".format(duplicates_classes))
def get_class_from_type(cls, class_type): """Return the class associated with input type. This will NOT check for classes with duplicated class type. The first class found with matching type will be returned. Parameters ---------- class_type : str Type of the class which will be looked up for. Returns ------- class_obj : class Desired class, if found. This is NOT an instance of the class. """ # Why this method is a classmethod? Just an exception for simplicity # classmethods should normally return an instance of calling class if cls.__super__ != cls.__name__: raise TypeError("only superclasses can be used.") # Get all the subclasses of the superclass subclasses = cls.get_subclasses() # Look for desired class type for class_data in subclasses: if get_private(class_data[1], 'class_type', None) == class_type: return class_data[1] raise NameError("no class of type `{:}` found within the package " "of class '{:}'".format(class_type, cls.__module__))
def class_type(self): """Defines class type.""" try: # Convert the private attribute to public property return get_private(self.__class__, 'class_type') except AttributeError: raise AttributeError("'class_type' not defined for '{:}'" "".format(self.__class__.__name__))
def import_class_types(classes): """Returns types associated with input list of classes. Abstract properties are ignored. Returns ------- types : list List of class types associated with input list of classes. """ # Get all class types from the input list of classes (to check duplicates) # Leaving out the classes not defining a class_type class_types = map( lambda class_file: get_private(class_file[1], 'class_type', None), classes) # skipping non string class_types -> classes not supporting creator return [class_type for class_type in class_types if isinstance(class_type, str)]
def create(cls, class_item=None, *args, **kwargs): """This method creates an instance of a class with given type. The list of subclasses of calling superclass is looked for any class defining `class_item = 'value'`. If found, the class type is listed. Also a class instance can be passed as main argument. In this case the class instance is returned as is. Parameters ---------- class_item : str or class instance or None, optional Type of the class to instantiate. If a class instance of cls is passed, instead, it returns the instance directly. If this is None, an instance of the classing superclass is created. args, kwargs : optional arguments Any other argument for the class to create. If a class instance is passed as `class_item`, optional arguments are NOT allowed. Returns ------- instance_class : any class Instance of the class having the given type (`class_type`) or the same class instance passed as input. """ if cls.__super__ != cls.__name__: raise TypeError("classes can be created from superclasses only.") # Create an instance of the calling superclass if class_item is None: return cls(*args, **kwargs) # Pycharm says are unexpected args # We accept strings and class instances only if isclass(class_item): # Returns false for instances raise TypeError("creator only accepts a class type " "as string or a class instance.") # CCreator cannot be created! if class_item.__class__ == CCreator: raise TypeError("class 'CCreator' is not callable.") # If a class instance is passed, it's returned as is if not is_str(class_item): if not isinstance(class_item, cls): raise TypeError("input instance should be a {:} " "subclass.".format(cls.__name__)) if len(args) + len(kwargs) != 0: raise TypeError("optional arguments are not allowed " "when a class instance is passed.") return class_item # Get all the subclasses of the superclass subclasses = cls.get_subclasses() # Get all class types from the list of subclasses (to check duplicates) class_types = import_class_types(subclasses) # Check for duplicates _check_class_types_duplicates(class_types, subclasses) # Everything seems fine now, look for desired class type for class_data in subclasses: if get_private(class_data[1], 'class_type', None) == class_item: return class_data[1](*args, **kwargs) raise NameError("no class of type `{:}` is a subclass of '{:}' " "from module '{:}'".format( class_item, cls.__name__, cls.__module__))