Load all schema classes of the given kind. Used as part of __init__.

def _set_up_schema_elements_of_kind(self, class_name_to_definition, kind, class_names):
        """Load all schema classes of the given kind. Used as part of __init__."""
        allowed_duplicated_edge_property_names = frozenset({
            EDGE_DESTINATION_PROPERTY_NAME, EDGE_SOURCE_PROPERTY_NAME
        })
        orientdb_base_classes = frozenset({
            ORIENTDB_BASE_VERTEX_CLASS_NAME,
            ORIENTDB_BASE_EDGE_CLASS_NAME,
        })

        for class_name in class_names:
            class_definition = class_name_to_definition[class_name]

            class_fields = class_definition.get('customFields')
            if class_fields is None:
                # OrientDB likes to make empty collections be None instead.
                # We convert this field back to an empty dict, for our general sanity.
                class_fields = dict()

            abstract = class_definition['abstract']
            if class_name in orientdb_base_classes:
                # Special-case the V and E base classes:
                # OrientDB won't let us make them abstract, but we don't want to create
                # any vertices or edges with those types either.
                # Pretend they are marked abstract in OrientDB's schema.
                abstract = True

            property_name_to_descriptor = {}
            all_property_lists = (
                class_name_to_definition[inherited_class_name]['properties']
                for inherited_class_name in self._inheritance_sets[class_name]
            )
            links = {EDGE_DESTINATION_PROPERTY_NAME: [], EDGE_SOURCE_PROPERTY_NAME: []}
            for property_definition in chain.from_iterable(all_property_lists):
                property_name = property_definition['name']

                # The only properties we allow to be redefined are the in/out properties
                # of edge classes. All other properties may only be defined once
                # in the entire inheritance hierarchy of any schema class, of any kind.
                duplication_allowed = all((
                    property_name in allowed_duplicated_edge_property_names,
                    kind == SchemaElement.ELEMENT_KIND_EDGE
                ))

                if not duplication_allowed and property_name in property_name_to_descriptor:
                    raise AssertionError(u'The property "{}" on class "{}" is defined '
                                         u'more than once, this is not allowed!'
                                         .format(property_name, class_name))

                property_descriptor = self._create_descriptor_from_property_definition(
                    class_name, property_definition, class_name_to_definition)

                if property_name in allowed_duplicated_edge_property_names:
                    links[property_name].append(property_descriptor)
                else:
                    property_name_to_descriptor[property_name] = property_descriptor

            for property_name in allowed_duplicated_edge_property_names:
                elements = {
                    property_descriptor.qualifier
                    for property_descriptor in links[property_name]
                }
                # If there are multiple in/out properties, we choose to include the one that
                # is a subclass of all the elements present in the in/out properties.
                for property_descriptor in links[property_name]:
                    subclass_set = self._subclass_sets[property_descriptor.qualifier]
                    if len(elements.intersection(subclass_set)) == 1:
                        current_descriptor = property_name_to_descriptor.get(property_name, None)
                        if current_descriptor and current_descriptor != property_descriptor:
                            raise AssertionError(u'There already exists property "{}" in addition '
                                                 u'to property "{}" which is a subclass of all '
                                                 u'in/out properties for class "{}".'
                                                 .format(current_descriptor,
                                                         property_descriptor, class_name))
                        property_name_to_descriptor[property_name] = property_descriptor

                if (property_name not in property_name_to_descriptor and not abstract and
                        kind == SchemaElement.ELEMENT_KIND_EDGE):
                    raise AssertionError(u'For property "{}" of non-abstract edge class "{}", '
                                         u'no such subclass-of-all-elements exists.'
                                         .format(property_name, class_name))

            self._elements[class_name] = SchemaElement(class_name, kind, abstract,
                                                       property_name_to_descriptor, class_fields)