__init__.py
changeset 11057 0b59724cb3f2
parent 11052 058bb3dc685f
child 11058 23eb30449fe5
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
     1 # copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    16 # You should have received a copy of the GNU Lesser General Public License along
       
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
       
    18 """CubicWeb is a generic framework to quickly build applications which describes
       
    19 relations between entitites.
       
    20 """
       
    21 __docformat__ = "restructuredtext en"
       
    22 
       
    23 # ignore the pygments UserWarnings
       
    24 import warnings
       
    25 import zlib
       
    26 warnings.filterwarnings('ignore', category=UserWarning,
       
    27                         message='.*was already imported',
       
    28                         module='.*pygments')
       
    29 
       
    30 
       
    31 from six import PY2, binary_type, text_type
       
    32 from six.moves import builtins
       
    33 
       
    34 CW_SOFTWARE_ROOT = __path__[0]
       
    35 
       
    36 import sys, os, logging
       
    37 from io import BytesIO
       
    38 
       
    39 from six.moves import cPickle as pickle
       
    40 
       
    41 from logilab.common.deprecation import deprecated
       
    42 from logilab.common.logging_ext import set_log_methods
       
    43 from yams.constraints import BASE_CONVERTERS, BASE_CHECKERS
       
    44 
       
    45 # pre python 2.7.2 safety
       
    46 logging.basicConfig()
       
    47 
       
    48 from cubicweb.__pkginfo__ import version as __version__
       
    49 
       
    50 
       
    51 set_log_methods(sys.modules[__name__], logging.getLogger('cubicweb'))
       
    52 
       
    53 # make all exceptions accessible from the package
       
    54 from cubicweb._exceptions import *
       
    55 from logilab.common.registry import ObjectNotFound, NoSelectableObject, RegistryNotFound
       
    56 
       
    57 
       
    58 # '_' is available to mark internationalized string but should not be used to
       
    59 # do the actual translation
       
    60 _ = text_type
       
    61 if not hasattr(builtins, '_'):
       
    62     builtins._ = deprecated("[3.22] Use 'from cubicweb import _'")(_)
       
    63 
       
    64 
       
    65 # convert eid to the right type, raise ValueError if it's not a valid eid
       
    66 @deprecated('[3.17] typed_eid() was removed. replace it with int() when needed.')
       
    67 def typed_eid(eid):
       
    68     return int(eid)
       
    69 
       
    70 #def log_thread(f, w, a):
       
    71 #    print f.f_code.co_filename, f.f_code.co_name
       
    72 #import threading
       
    73 #threading.settrace(log_thread)
       
    74 
       
    75 class Binary(BytesIO):
       
    76     """class to hold binary data. Use BytesIO to prevent use of unicode data"""
       
    77     _allowed_types = (binary_type, bytearray, buffer if PY2 else memoryview)
       
    78 
       
    79     def __init__(self, buf=b''):
       
    80         assert isinstance(buf, self._allowed_types), \
       
    81                "Binary objects must use bytes/buffer objects, not %s" % buf.__class__
       
    82         super(Binary, self).__init__(buf)
       
    83 
       
    84     def write(self, data):
       
    85         assert isinstance(data, self._allowed_types), \
       
    86                "Binary objects must use bytes/buffer objects, not %s" % data.__class__
       
    87         super(Binary, self).write(data)
       
    88 
       
    89     def to_file(self, fobj):
       
    90         """write a binary to disk
       
    91 
       
    92         the writing is performed in a safe way for files stored on
       
    93         Windows SMB shares
       
    94         """
       
    95         pos = self.tell()
       
    96         self.seek(0)
       
    97         if sys.platform == 'win32':
       
    98             while True:
       
    99                 # the 16kB chunksize comes from the shutil module
       
   100                 # in stdlib
       
   101                 chunk = self.read(16*1024)
       
   102                 if not chunk:
       
   103                     break
       
   104                 fobj.write(chunk)
       
   105         else:
       
   106             fobj.write(self.read())
       
   107         self.seek(pos)
       
   108 
       
   109     @staticmethod
       
   110     def from_file(filename):
       
   111         """read a file and returns its contents in a Binary
       
   112 
       
   113         the reading is performed in a safe way for files stored on
       
   114         Windows SMB shares
       
   115         """
       
   116         binary = Binary()
       
   117         with open(filename, 'rb') as fobj:
       
   118             if sys.platform == 'win32':
       
   119                 while True:
       
   120                     # the 16kB chunksize comes from the shutil module
       
   121                     # in stdlib
       
   122                     chunk = fobj.read(16*1024)
       
   123                     if not chunk:
       
   124                         break
       
   125                     binary.write(chunk)
       
   126             else:
       
   127                 binary.write(fobj.read())
       
   128         binary.seek(0)
       
   129         return binary
       
   130 
       
   131     def __eq__(self, other):
       
   132         if not isinstance(other, Binary):
       
   133             return False
       
   134         return self.getvalue() == other.getvalue()
       
   135 
       
   136 
       
   137     # Binary helpers to store/fetch python objects
       
   138 
       
   139     @classmethod
       
   140     def zpickle(cls, obj):
       
   141         """ return a Binary containing a gzipped pickle of obj """
       
   142         retval = cls()
       
   143         retval.write(zlib.compress(pickle.dumps(obj, protocol=2)))
       
   144         return retval
       
   145 
       
   146     def unzpickle(self):
       
   147         """ decompress and loads the stream before returning it """
       
   148         return pickle.loads(zlib.decompress(self.getvalue()))
       
   149 
       
   150 
       
   151 def check_password(eschema, value):
       
   152     return isinstance(value, (binary_type, Binary))
       
   153 BASE_CHECKERS['Password'] = check_password
       
   154 
       
   155 def str_or_binary(value):
       
   156     if isinstance(value, Binary):
       
   157         return value
       
   158     return binary_type(value)
       
   159 BASE_CONVERTERS['Password'] = str_or_binary
       
   160 
       
   161 
       
   162 # use this dictionary to rename entity types while keeping bw compat
       
   163 ETYPE_NAME_MAP = {}
       
   164 
       
   165 # XXX cubic web cube migration map. See if it's worth keeping this mecanism
       
   166 #     to help in cube renaming
       
   167 CW_MIGRATION_MAP = {}
       
   168 
       
   169 def neg_role(role):
       
   170     if role == 'subject':
       
   171         return 'object'
       
   172     return 'subject'
       
   173 
       
   174 def role(obj):
       
   175     try:
       
   176         return obj.role
       
   177     except AttributeError:
       
   178         return neg_role(obj.target)
       
   179 
       
   180 def target(obj):
       
   181     try:
       
   182         return obj.target
       
   183     except AttributeError:
       
   184         return neg_role(obj.role)
       
   185 
       
   186 
       
   187 class CubicWebEventManager(object):
       
   188     """simple event / callback manager.
       
   189 
       
   190     Typical usage to register a callback::
       
   191 
       
   192       >>> from cubicweb import CW_EVENT_MANAGER
       
   193       >>> CW_EVENT_MANAGER.bind('after-registry-reload', mycallback)
       
   194 
       
   195     Typical usage to emit an event::
       
   196 
       
   197       >>> from cubicweb import CW_EVENT_MANAGER
       
   198       >>> CW_EVENT_MANAGER.emit('after-registry-reload')
       
   199 
       
   200     emit() accepts an additional context parameter that will be passed
       
   201     to the callback if specified (and only in that case)
       
   202     """
       
   203     def __init__(self):
       
   204         self.callbacks = {}
       
   205 
       
   206     def bind(self, event, callback, *args, **kwargs):
       
   207         self.callbacks.setdefault(event, []).append( (callback, args, kwargs) )
       
   208 
       
   209     def emit(self, event, context=None):
       
   210         for callback, args, kwargs in self.callbacks.get(event, ()):
       
   211             if context is None:
       
   212                 callback(*args, **kwargs)
       
   213             else:
       
   214                 callback(context, *args, **kwargs)
       
   215 
       
   216 CW_EVENT_MANAGER = CubicWebEventManager()
       
   217 
       
   218 def onevent(event, *args, **kwargs):
       
   219     """decorator to ease event / callback binding
       
   220 
       
   221     >>> from cubicweb import onevent
       
   222     >>> @onevent('before-registry-reload')
       
   223     ... def mycallback():
       
   224     ...     print 'hello'
       
   225     ...
       
   226     >>>
       
   227     """
       
   228     def _decorator(func):
       
   229         CW_EVENT_MANAGER.bind(event, func, *args, **kwargs)
       
   230         return func
       
   231     return _decorator
       
   232 
       
   233 
       
   234 from yams.schema import role_name as rname
       
   235 
       
   236 def validation_error(entity, errors, substitutions=None, i18nvalues=None):
       
   237     """easy way to retrieve a :class:`cubicweb.ValidationError` for an entity or eid.
       
   238 
       
   239     You may also have 2-tuple as error keys, :func:`yams.role_name` will be
       
   240     called automatically for them.
       
   241 
       
   242     Messages in errors **should not be translated yet**, though marked for
       
   243     internationalization. You may give an additional substition dictionary that
       
   244     will be used for interpolation after the translation.
       
   245     """
       
   246     if substitutions is None:
       
   247         # set empty dict else translation won't be done for backward
       
   248         # compatibility reason (see ValidationError.translate method)
       
   249         substitutions = {}
       
   250     for key in list(errors):
       
   251         if isinstance(key, tuple):
       
   252             errors[rname(*key)] = errors.pop(key)
       
   253     return ValidationError(getattr(entity, 'eid', entity), errors,
       
   254                            substitutions, i18nvalues)
       
   255 
       
   256 
       
   257 # exceptions ##################################################################
       
   258 
       
   259 class ProgrammingError(Exception): #DatabaseError):
       
   260     """Exception raised for errors that are related to the database's operation
       
   261     and not necessarily under the control of the programmer, e.g. an unexpected
       
   262     disconnect occurs, the data source name is not found, a transaction could
       
   263     not be processed, a memory allocation error occurred during processing,
       
   264     etc.
       
   265     """