[connection] move hook control logic on Connection
The _hook_control context manager now operate on connection. We have to keep a
specific entry point in Session to ensure Connection object are properly
cleaned up when using session to "manage" Connection live cycle.
Related to #2503918
# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
#
# CubicWeb 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 2.1 of the License, or (at your option)
# any later version.
#
# CubicWeb 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 CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""CubicWeb is a generic framework to quickly build applications which describes
relations between entitites.
"""
__docformat__ = "restructuredtext en"
# ignore the pygments UserWarnings
import warnings
warnings.filterwarnings('ignore', category=UserWarning,
message='.*was already imported',
module='.*pygments')
import __builtin__
# '_' is available in builtins to mark internationalized string but should
# not be used to do the actual translation
if not hasattr(__builtin__, '_'):
__builtin__._ = unicode
CW_SOFTWARE_ROOT = __path__[0]
import sys, os, logging
from StringIO import StringIO
from logilab.common.deprecation import deprecated
from logilab.common.logging_ext import set_log_methods
from yams.constraints import BASE_CONVERTERS
if os.environ.get('APYCOT_ROOT'):
logging.basicConfig(level=logging.CRITICAL)
else:
logging.basicConfig()
from cubicweb.__pkginfo__ import version as __version__
set_log_methods(sys.modules[__name__], logging.getLogger('cubicweb'))
# make all exceptions accessible from the package
from cubicweb._exceptions import *
from logilab.common.registry import ObjectNotFound, NoSelectableObject, RegistryNotFound
# convert eid to the right type, raise ValueError if it's not a valid eid
@deprecated('[3.17] typed_eid() was removed. replace it with int() when needed.')
def typed_eid(eid):
return int(eid)
#def log_thread(f, w, a):
# print f.f_code.co_filename, f.f_code.co_name
#import threading
#threading.settrace(log_thread)
class Binary(StringIO):
"""customize StringIO to make sure we don't use unicode"""
def __init__(self, buf=''):
assert isinstance(buf, (str, buffer)), \
"Binary objects must use raw strings, not %s" % buf.__class__
StringIO.__init__(self, buf)
def write(self, data):
assert isinstance(data, (str, buffer)), \
"Binary objects must use raw strings, not %s" % data.__class__
StringIO.write(self, data)
def to_file(self, fobj):
"""write a binary to disk
the writing is performed in a safe way for files stored on
Windows SMB shares
"""
pos = self.tell()
self.seek(0)
if sys.platform == 'win32':
while True:
# the 16kB chunksize comes from the shutil module
# in stdlib
chunk = self.read(16*1024)
if not chunk:
break
fobj.write(chunk)
else:
fobj.write(self.read())
self.seek(pos)
@staticmethod
def from_file(filename):
"""read a file and returns its contents in a Binary
the reading is performed in a safe way for files stored on
Windows SMB shares
"""
binary = Binary()
with open(filename, 'rb') as fobj:
if sys.platform == 'win32':
while True:
# the 16kB chunksize comes from the shutil module
# in stdlib
chunk = fobj.read(16*1024)
if not chunk:
break
binary.write(chunk)
else:
binary.write(fobj.read())
binary.seek(0)
return binary
def str_or_binary(value):
if isinstance(value, Binary):
return value
return str(value)
BASE_CONVERTERS['Password'] = str_or_binary
# use this dictionary to rename entity types while keeping bw compat
ETYPE_NAME_MAP = {}
# XXX cubic web cube migration map. See if it's worth keeping this mecanism
# to help in cube renaming
CW_MIGRATION_MAP = {}
def neg_role(role):
if role == 'subject':
return 'object'
return 'subject'
def role(obj):
try:
return obj.role
except AttributeError:
return neg_role(obj.target)
def target(obj):
try:
return obj.target
except AttributeError:
return neg_role(obj.role)
class CubicWebEventManager(object):
"""simple event / callback manager.
Typical usage to register a callback::
>>> from cubicweb import CW_EVENT_MANAGER
>>> CW_EVENT_MANAGER.bind('after-registry-reload', mycallback)
Typical usage to emit an event::
>>> from cubicweb import CW_EVENT_MANAGER
>>> CW_EVENT_MANAGER.emit('after-registry-reload')
emit() accepts an additional context parameter that will be passed
to the callback if specified (and only in that case)
"""
def __init__(self):
self.callbacks = {}
def bind(self, event, callback, *args, **kwargs):
self.callbacks.setdefault(event, []).append( (callback, args, kwargs) )
def emit(self, event, context=None):
for callback, args, kwargs in self.callbacks.get(event, ()):
if context is None:
callback(*args, **kwargs)
else:
callback(context, *args, **kwargs)
CW_EVENT_MANAGER = CubicWebEventManager()
def onevent(event, *args, **kwargs):
"""decorator to ease event / callback binding
>>> from cubicweb import onevent
>>> @onevent('before-registry-reload')
... def mycallback():
... print 'hello'
...
>>>
"""
def _decorator(func):
CW_EVENT_MANAGER.bind(event, func, *args, **kwargs)
return func
return _decorator
from yams.schema import role_name as rname
def validation_error(entity, errors, substitutions=None, i18nvalues=None):
"""easy way to retrieve a :class:`cubicweb.ValidationError` for an entity or eid.
You may also have 2-tuple as error keys, :func:`yams.role_name` will be
called automatically for them.
Messages in errors **should not be translated yet**, though marked for
internationalization. You may give an additional substition dictionary that
will be used for interpolation after the translation.
"""
if substitutions is None:
# set empty dict else translation won't be done for backward
# compatibility reason (see ValidationError.translate method)
substitutions = {}
for key in list(errors):
if isinstance(key, tuple):
errors[rname(*key)] = errors.pop(key)
return ValidationError(getattr(entity, 'eid', entity), errors,
substitutions, i18nvalues)