def query_pa_no_flush(session, permission, role, obj):
    """Query for a :class:`PermissionAssignment` using `session` without any
    `flush()`.

    It works by looking in session `new`, `dirty` and `deleted`, and issuing a
    query with no autoflush.

    .. note::

        This function is used by `add_permission` and `delete_permission` to allow
        to add/remove the same assignment twice without issuing any flush. Since
        :class:`Entity` creates its initial permissions in during
        :sqlalchemy:`sqlalchemy.orm.events.SessionEvents.after_attach`, it might be
        problematic to issue a flush when entity is not yet ready to be flushed
        (missing required attributes for example).
    """
    to_visit = [session.deleted, session.dirty, session.new]
    with session.no_autoflush:
        # no_autoflush is required to visit PERMISSIONS_ATTR without emitting a
        # flush()
        if obj:
            to_visit.append(getattr(obj, PERMISSIONS_ATTR))

        permissions = (
            p for p in chain(*to_visit) if isinstance(p, PermissionAssignment)
        )

        for instance in permissions:
            if (
                instance.permission == permission
                and instance.role == role
                and instance.object == obj
            ):
                return instance

        # Last chance: perform a filtered query. If obj is not None, sometimes
        # getattr(obj, PERMISSIONS_ATTR) has objects not present in session
        # not in this query (maybe in a parent session transaction `new`?).
        if obj is not None and obj.id is None:
            obj = None

        return (
            session.query(PermissionAssignment)
            .filter(
                PermissionAssignment.permission == permission,
                PermissionAssignment.role == role,
                PermissionAssignment.object == obj,
            )
            .first()
        )