def __init__(cls, name, bases, attrs): if attrs.get('pickup'): datastores = attrs.get('pickup') if not isinstance(datastores, list): datastores = [datastores] # Cycle through all the classes that have been declared in this module, so we can augment this class with those ones # Limitation: You have to declare your branches in the same place as this module # TODO: Get around above limitation by passing a string and importing that way for datastore in datastores: find_classes = [] all_modules = sys.modules.keys() # For ease in use, we inspect all modules available at this place in the code # and look for the pickups classes for module_key in all_modules: module = sys.modules[module_key] get_all_module_classes = inspect.getmembers(module, inspect.isclass) # Now go through all classes in this particular module for class_name, class_reference in get_all_module_classes: nested_class_name = NS2.string( cls.qual_name_format, branch=cls.__name__, delim=cls.qual_name_delimiter, subbranch=class_reference.__name__ ) if class_name in attrs.keys(): # first check to see if the programmer declared something else by the same name declared_class = attrs[class_name] # this is now whatever the programmer declared if inspect.isclass(declared_class) and issubclass(declared_class, datastore): # If we're here, we need to adjust some augment, to match what we would do automatically (like below) setattr(declared_class, '__qualname__', nested_class_name) setattr(declared_class, '__outerclass__', cls) elif class_reference is not datastore: # check to ensure our heuristic doesn't detect itself if is_immediate_subclass(class_reference, datastore): # now see if this object is subclass of class represented by `pickup` # okay, we need to manually pickup the class and bring it into ours # copy the class entirely (won't work otherwise) copied_class = type(name, class_reference.__bases__, dict(class_reference.__dict__)) # set up magic copied_class.__qualname__ = nested_class_name copied_class.__outerclass__ = cls # and assign this brand new class to ours setattr(cls, class_reference.__name__, copied_class) else: pass # nothing to do here, programmer defined a method or object with the same name but not a subclass of class referenced by `pickup`
def subtract(self): """ Yields ModificationStatements, which get fed back into the go method, which in turn dispatches the message to lower-level routines The ModfiicationStatements represent the changes that have occured. This method inspects meta data and passes on further inspections to the model itself """ # First, process the meta data about the model, which # is basically just checking the keys for additions # then looking for differences between each item # then we check the keys for subtractions assert(len(self.left.get_subbranches()) == len(self.right.get_subbranches())) # NOTE, removed custom_profile_fields and timetable_datas subbranches = [ 'cohorts', 'courses', 'teachers', 'students', 'groups', 'parents', 'parent_links', 'online_portfolios' ] # self.left.get_subbranches() for subbranch in subbranches: self.default_logger("Subbranch: {}".format(subbranch)) left_branch = self.get_subbranch(self.left, subbranch) right_branch = self.get_subbranch(self.right, subbranch) self.default_logger("There are {} items in {}'s left branch".format(len(right_branch), subbranch)) self.default_logger("There are {} items in {}'s right branch".format(len(right_branch), subbranch)) self.default_logger("Looking for new {}:".format(subbranch)) # Loop through missing stuff and do with it what we must for key in right_branch.keys() - left_branch.keys(): yield ModificationStatement( left = left_branch.get(key), right = right_branch.get(key), status = NS2.string("new_{term}", term=subbranch[:-1]), param = key ) # Now go through the model and inspect the individual items # We have to go through each key on the left side, and on the right for subbranch in subbranches: self.default_logger("Individual items: {}".format(subbranch)) left_branch = self.get_subbranch(self.left, subbranch) right_branch = self.get_subbranch(self.right, subbranch) # Left side: for item_key in left_branch: item_left = left_branch.get(item_key) item_right = right_branch.get(item_key) if item_left and item_right: # The model itself defines how to look at each item with __sub__ # And we just yield that result for left_minus_right in item_left - item_right: yield ModificationStatement( left = left_minus_right.left, right = left_minus_right.right, status = left_minus_right.status, param = left_minus_right.param ) # short circuit out of this if subbranch in ["parent_links", "online_portfolios"]: # not compatible with the below, especially with make, because it wants the child continue # Right side: for item_key in right_branch: item_left = left_branch.get(item_key) item_right = right_branch.get(item_key) # Same principal as with the left side, but # there is a special case where, there is something on the right # but nothing on the left # The modification statement for "new_" will be spit out by the above # so we can just make a proxy object in order to manufacture the yield statements if item_right and not item_left and item_right.idnumber: # Makes an empty, default one fake = getattr(self.left, subbranch).\ make(item_right.idnumber) # Notice fake - item_right for right_minus_left in fake - item_right: yield ModificationStatement( left = right_minus_left.left, right = right_minus_left.right, status = right_minus_left.status, param = right_minus_left.param ) for subbranch in subbranches: self.default_logger("Looking for old {}".format(subbranch)) left_branch = self.get_subbranch(self.left, subbranch) right_branch = self.get_subbranch(self.right, subbranch) for key in left_branch.keys() - right_branch.keys(): yield ModificationStatement( left = left_branch.get(key), right = right_branch.get(key), status = NS2.string("old_{term}", term=subbranch[:-1]), param = key )