def generate_model(root_class=ROOT_CLASS): """ Generate a data-structure that represents the current configuration model. Changes to output may require regenerating the persisted version on-disk. This should switch to Pyrsistent's introspection API once it exists: https://github.com/tobgu/pyrsistent/issues/47 """ classes_result = {} result = {u"root": fqpn(root_class), u"classes": classes_result} classes = {root_class} while classes: klass = classes.pop() klass_name = fqpn(klass) if klass_name in classes_result: continue if issubclass(klass, (PRecord, PClass)): to_model = _precord_model elif issubclass(klass, CheckedPMap): to_model = _pmap_model elif issubclass(klass, (CheckedPSet, CheckedPVector)): to_model = _psequence_model else: to_model = lambda klass: (None, set()) record, further_classes = to_model(klass) classes_result[klass_name] = record classes |= further_classes return result
def _pmap_model(klass): """ Serialize a ``PMap`` model to something JSON-encodable. :param klass: A ``PMap`` subclass. :return: Tuple of (model dictionary, further classes to process). """ record = { u"category": u"map", u"fields": { u"key": sorted( fqpn(cls) for cls in klass._checked_key_types), u"value": sorted( fqpn(cls) for cls in klass._checked_value_types)}} further_classes = set() for cls in klass._checked_key_types + klass._checked_value_types: further_classes.add(cls) return record, further_classes
def _pmap_model(klass): """ Serialize a ``PMap`` model to something JSON-encodable. :param klass: A ``PMap`` subclass. :return: Tuple of (model dictionary, further classes to process). """ record = { u"category": u"map", u"fields": { u"key": sorted(fqpn(cls) for cls in klass._checked_key_types), u"value": sorted(fqpn(cls) for cls in klass._checked_value_types) } } further_classes = set() for cls in klass._checked_key_types + klass._checked_value_types: further_classes.add(cls) return record, further_classes
def assert_catches_changes(self, original_class, changed_class): """ Assert that ``generate_model`` changes its output when the underlying class has changed. :param original_class: Class in initial state. :param changed_class: Class in changed state. """ original_model = generate_model(original_class) changed_model = generate_model(changed_class) # Make sure result is JSON serializable: dumps(original_model) dumps(changed_model) self.assertEqual( # If not the calling test is buggy, since it's catching wrong # thing, a mere name change: (fqpn(original_class) == fqpn(changed_class), # Changes result in a difference: original_model != changed_model, # No changes result in same output: original_model == generate_model(original_class), changed_model == generate_model(changed_class)), (True, True, True, True))
def _psequence_model(klass): """ Serialize a ``PVector`` or ``PSet`` model to something JSON-encodable. :param klass: A ``PVector`` or ``PSet`` subclass. :return: Tuple of (model dictionary, further classes to process). """ category = u"set" if issubclass(klass, CheckedPSet) else u"list" record = { u"category": category, u"type": sorted(fqpn(cls) for cls in klass._checked_types), } further_classes = set(klass._checked_types) return record, further_classes
def _precord_model(klass): """ Serialize a ``PRecord`` or ``PClass`` model to something JSON-encodable. :param klass: A ``PRecord`` or ``PClass`` subclass. :return: Tuple of (model dictionary, further classes to process). """ further_classes = set() if issubclass(klass, PRecord): attr_name = "_precord_fields" else: attr_name = "_pclass_fields" record = {u"category": u"record", u"fields": {}} for name, field_info in getattr(klass, attr_name).items(): record[u"fields"][name] = sorted(fqpn(cls) for cls in field_info.type) for cls in field_info.type: further_classes.add(cls) return record, further_classes
def _precord_model(klass): """ Serialize a ``PRecord`` or ``PClass`` model to something JSON-encodable. :param klass: A ``PRecord`` or ``PClass`` subclass. :return: Tuple of (model dictionary, further classes to process). """ further_classes = set() if issubclass(klass, PRecord): attr_name = "_precord_fields" else: attr_name = "_pclass_fields" record = {u"category": u"record", u"fields": {}} for name, field_info in getattr(klass, attr_name).items(): record[u"fields"][name] = sorted( fqpn(cls) for cls in field_info.type) for cls in field_info.type: further_classes.add(cls) return record, further_classes