Source code for summer.aop

# Copyright (C) 2009-2020 Martin Slouf <martinslouf@users.sourceforge.net>
#
# This file is a part of Summer.
#
# Summer is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 3 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

"""Provides summer *AOP* functionality.

You can find here most useful decorators :py:func:`transactional` for SQL
transaction support and :py:func:`ldapaop`.

"""

import functools
import logging
import types

from .sf import SessionFactory
from .lsf import LdapSessionFactory

logger = logging.getLogger(__name__)


[docs]def transactional(method: types.FunctionType) -> types.FunctionType: """Method decorator marking method to be transactional. Every decorated method behaves as follows: * if no transaction is active and database is *not* in autocommit mode, start a new transaction, doing commit/rollback at the end * if there is a transaction, just simple continue within the transaction without doing commit/rollback at the end (that is left to the top-most transactional method) Args: method (types.FunctionType): function to be decorated Returns: (types.FunctionType): decorated function """ # mame .. method name # mame = "%s.%s" % (func.im_class.__name__, func.__name__) mame = method.__name__ logger.debug("method %s decorated as transactional", mame) transactional.session_factory = None @functools.wraps(method) def _transactional(*args, **kwargs) -> object: assert isinstance(transactional.session_factory, SessionFactory) session = transactional.session_factory.session logger.debug("tx begin with session %s", session) active = session.active try: if active: logger.debug("tx active, continue") else: session.begin() logger.debug("tx not active, started") result = method(*args, **kwargs) if active: logger.debug("tx active, no commit") else: session.commit() logger.debug("tx commit") except Exception as ex: session.rollback() # session.close() # close session in case of an exception logger.exception("tx rollback on error") raise ex finally: if active: logger.debug("tx active, not closing") else: # session can outlive the transaction to be reused later on # session.close() # logger.debug("tx closed") pass return result return _transactional
[docs]def ldapaop(method: types.FunctionType) -> types.FunctionType: """Method decorator marking method to be run in LDAP session. Analogy to :py:func:`summer.aop.transactional` with same logic. Intended use case for this decorator is to decorate :py:class:`summer.ldapdao.LdapEntityDao` methods and then access current *ldap3* session/connection by using :py:attr:`summer.ldapdao.LdapDao.session` from within the *DAO* method. Thus you can access *ldap3* session/connection (and manipulate data), and still have the transaction boundaries defined on top of your business methods. Args: method (types.FunctionType): function to be decorated Returns: (types.FunctionType): decorated function """ # mame .. method name # mame = "%s.%s" % (func.im_class.__name__, func.__name__) mame = method.__name__ logger.debug("method %s decorated as ldapaop", mame) ldapaop.ldap_session_factory = None @functools.wraps(method) def _ldapaop(*args, **kwargs) -> object: assert isinstance(ldapaop.ldap_session_factory, LdapSessionFactory) session = ldapaop.ldap_session_factory.session logger.debug("lx begin with session %s", session) active = session.active try: if active: logger.debug("lx active, continue") else: session.bind() logger.debug("lx not active, bounded") result = method(*args, **kwargs) logger.debug("lx ok") except Exception as ex: logger.exception("lx error") raise ex finally: if active: logger.debug("lx active, not closing") else: # session can outlive single call to be reused later on # session.unbound() # logger.debug("lx session unbounded") pass return result return _ldapaop