Maps a Postgresql type to this class.  If the class's *table* attribute
        is empty, and the class has an attribute *pg_type* of tuple (schema, type),
        it is calculated and set by querying Postgres. Register inherited/inheriting
        classes in heirarchical order.
        Every time a SQL function returns a registered type (including array
        elements and individual columns, recursively), this class
        will be instantiated automatically.

        The object attributes will be passed to the provided callable in
        a form of keyword arguments.

        :param str name: the name of a PostgreSQL composite type, e.g. created using
            the *CREATE TYPE* command
        :param simpycity.handle.Handle handle:
        :param psycopg2.extras.CompositeCaster factory: use
            it to customize how to cast composite types
        :return: the registered *CompositeCaster* instance
            responsible for the conversion

def register_composite(cls, name, handle=None, factory=None):
        """
        Maps a Postgresql type to this class.  If the class's *table* attribute
        is empty, and the class has an attribute *pg_type* of tuple (schema, type),
        it is calculated and set by querying Postgres. Register inherited/inheriting
        classes in heirarchical order.
        Every time a SQL function returns a registered type (including array
        elements and individual columns, recursively), this class
        will be instantiated automatically.

        The object attributes will be passed to the provided callable in
        a form of keyword arguments.

        :param str name: the name of a PostgreSQL composite type, e.g. created using
            the *CREATE TYPE* command
        :param simpycity.handle.Handle handle:
        :param psycopg2.extras.CompositeCaster factory: use
            it to customize how to cast composite types
        :return: the registered *CompositeCaster* instance
            responsible for the conversion
        """
        class CustomCompositeCaster(psycopg2.extras.CompositeCaster):

            def make(self, values):
                d_out("CustomCompositeCaster.make: cls={0} values={1}".format(repr(cls), repr(values)))
                return cls(**dict(list(zip(self.attnames, values))))

        PG_TYPE_SQL = """SELECT array_agg(attname)
FROM
    (
        SELECT attname
        FROM
            pg_type t
            JOIN pg_namespace ns ON typnamespace = ns.oid
            JOIN pg_attribute a ON attrelid = typrelid
        WHERE nspname = %s AND typname = %s
            AND attnum > 0 AND NOT attisdropped
        ORDER BY attnum
    ) sub;"""
        if handle is None:
            handle = g_config.handle_factory()
        d_out("SimpleModel.register_composite: before: table for {0} is {1}".format(repr(cls.pg_type), cls.table))
        if cls.pg_type is not None:
            super_table = cls.__mro__[1].table if hasattr(cls.__mro__[1], 'table') else []
            if cls.table == [] or cls.table is super_table:
                cursor = handle.cursor()
                cursor.execute(PG_TYPE_SQL, cls.pg_type)
                row = cursor.fetchone()
                d_out("SimpleModel.register_composite: row={0}".format(row))
                row[0] = [_ for _ in row[0] if _ != 'base_']
                cls.table = cls.table + row[0]
                d_out("SimpleModel.register_composite: after: table for {0} is {1}".format(repr(cls.pg_type), cls.table))
        if factory is None:
            factory = CustomCompositeCaster
        if sys.version_info[0] < 3:
            name = str(name)
        return psycopg2.extras.register_composite(
            name,
            handle.conn,
            globally=True,  # in case of reconnects
            factory=factory
        )