Core engine for the automatic generation of storable instances.

    Finds the attributes exposed by the objects of a given type.

    Mostly Python3-only.
    Does not handle types which `__new__` method requires extra arguments either.

    Arguments:

        python_type (type): object type.

    Returns:

        list: attributes exposed.

def most_exposes(python_type):
    """
    Core engine for the automatic generation of storable instances.

    Finds the attributes exposed by the objects of a given type.

    Mostly Python3-only.
    Does not handle types which `__new__` method requires extra arguments either.

    Arguments:

        python_type (type): object type.

    Returns:

        list: attributes exposed.

    """
    _exposes = set()
    try:
        # list all standard class attributes and methods:
        do_not_expose = set(python_type.__dir__(object) + \
            ['__slots__', '__module__', '__weakref__']) # may raise `AttributeError`
        empty = python_type.__new__(python_type) # may raise `TypeError`
    except AttributeError: # Py2 does not have `__dir__`
        try:
            _exposes = python_type.__slots__
        except AttributeError:
            pass
    except TypeError: # `__new__` requires input arguments
        for _workaround in storable_workarounds:
            try:
                _exposes = _workaround(python_type)
            except (SystemExit, KeyboardInterrupt):
                raise
            except:
                pass
            else:
                break
    else:
        # note that slots from parent classes are not in `__dict__` (like all slots)
        # and - in principle - not in `__slots__` either.
        all_members = empty.__dir__() # all slots are supposed to appear in this list
        for attr in all_members:
            if attr in do_not_expose:
                # note that '__dict__' is in `do_not_expose` (comes from `object`)
                continue
            try: # identify the methods and properties
                getattr(empty, attr)
            except AttributeError as e: # then `attr` might be a slot
                # properties can still throw an `AttributeError`;
                # try to filter some more out
                if e.args:
                    msg = e.args[0]
                    if msg == attr or msg.endswith("' object has no attribute '{}'".format(attr)):
                        _exposes.add(attr)
            except (SystemExit, KeyboardInterrupt):
                raise
            except:
                pass
        for attr in ('__dict__',):
            if attr in all_members:
                _exposes.add(attr)
    return list(_exposes)