# used to define the default session used by models when first instantiated.
# this can be overridden in the models by passing a session arguement to the
# model when initialized.
default_session = None


class SessionDirtyException(Exception):
    pass


class Session(object):
    """
        The Session class is used to maintain a set of connections based on a specific
        configuration.  The configuration itself should implement attribute access, and it
        is assumed that the config is actually a class itself.

        The reason it assumes the config is a class, is so that it can pass itself to the
        config's __init__ or __call__, allowing the config to do things like toggle to a
        set of backup servers, and invalidating the connections currently in use.
    """
    @property
    def is_dirty(self):
        """
            Returns whether any of the currently open connections have uncommitted changes.
        """
        dirty = False

        for connection in self.connections:
            if connection.is_dirty:
                dirty = True

        return dirty

    @property
    def config(self):
        """
            Returns the current configuration for this session, returns None if one hasn't
            been specified yet.
        """
        return getattr(self, '_config', None)

    @config.setter
    def config(self, config):
        """
            Attempts to change the config file in use on a session, if the session has
            already been used to push data to the servers, but the transaction has not
            been committed, raises an exception.
        """
        if self.is_dirty:
            raise SessionDirtyException()

        self._config = config(self)

    def __init__(self, config, default=False):
        """
            The session object takes two parameters, the config file to be used,
            and whether or not this session should become the default session. If
            the default session is already set and is marked as dirty, raises an
            exception.
        """
        self.config = config

        if default:
            if not getattr(default_session, 'is_dirty', False):
                # pull in the global default_session var, so we can modify it.  The
                # purpose of this var is to maintain state for multiple models, after
                # the initial declaration, so this actually makes sense here.
                global default_session

                default_session = self

            else:
                raise SessionDirtyException()

    def commit(self):
        """
            Commits any currently uncommitted changes.
        """
        for connection in self.connections:
            if connection.is_dirty:
                connection.commit()

    def rollback(self):
        """
            Rolls back any currently uncommitted changes.
        """
        for connection in self.connections:
            if connection.is_dirty:
                connection.rollback()

add initial session handler