# HG changeset patch # User Sylvain Thénault # Date 1331890155 -3600 # Node ID 8be58694f4166128575ad37a0afb36f7c201af0b # Parent 4da49700b06a4dbce6cabfdbcb20a87f2e983953# Parent 2279e02e62be26872fcb04ce62d93a3de6531738 backport stable diff -r 2279e02e62be -r 8be58694f416 __init__.py --- a/__init__.py Tue Mar 13 15:27:30 2012 +0100 +++ b/__init__.py Fri Mar 16 10:29:15 2012 +0100 @@ -55,6 +55,7 @@ # 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 typed_eid = int @@ -77,25 +78,24 @@ "Binary objects must use raw strings, not %s" % data.__class__ StringIO.write(self, data) - def to_file(self, filename): + 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() - with open(filename, 'wb') as fobj: - 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(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 diff -r 2279e02e62be -r 8be58694f416 __pkginfo__.py --- a/__pkginfo__.py Tue Mar 13 15:27:30 2012 +0100 +++ b/__pkginfo__.py Fri Mar 16 10:29:15 2012 +0100 @@ -1,5 +1,5 @@ # pylint: disable=W0622,C0103 -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# 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. @@ -22,7 +22,7 @@ modname = distname = "cubicweb" -numversion = (3, 14, 6) +numversion = (3, 15, 0) version = '.'.join(str(num) for num in numversion) description = "a repository of entities / relations for knowledge management" @@ -40,7 +40,7 @@ ] __depends__ = { - 'logilab-common': '>= 0.57.0', + 'logilab-common': '>= 0.58.0', 'logilab-mtconverter': '>= 0.8.0', 'rql': '>= 0.28.0', 'yams': '>= 0.34.0', @@ -52,7 +52,7 @@ 'Twisted': '', # XXX graphviz # server dependencies - 'logilab-database': '>= 1.8.1', + 'logilab-database': '>= 1.8.2', 'pysqlite': '>= 2.5.5', # XXX install pysqlite2 } @@ -63,6 +63,7 @@ 'fyzz': '>= 0.1.0', # for sparql 'vobject': '>= 0.6.0', # for ical view 'rdflib': None, # + 'pyzmq': None, #'Products.FCKeditor':'', #'SimpleTAL':'>= 4.1.6', } diff -r 2279e02e62be -r 8be58694f416 _exceptions.py --- a/_exceptions.py Tue Mar 13 15:27:30 2012 +0100 +++ b/_exceptions.py Fri Mar 16 10:29:15 2012 +0100 @@ -1,4 +1,4 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -15,10 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . -"""Exceptions shared by different cubicweb packages. +"""Exceptions shared by different cubicweb packages.""" - -""" __docformat__ = "restructuredtext en" from yams import ValidationError @@ -32,9 +30,10 @@ if self.msg: if self.args: return self.msg % tuple(self.args) - return self.msg - return ' '.join(unicode(arg) for arg in self.args) - + else: + return self.msg + else: + return u' '.join(unicode(arg) for arg in self.args) class ConfigurationError(CubicWebException): """a misconfiguration error""" @@ -83,6 +82,7 @@ class UniqueTogetherError(RepositoryError): """raised when a unique_together constraint caused an IntegrityError""" + # security exceptions ######################################################### class Unauthorized(SecurityError): @@ -114,32 +114,8 @@ # registry exceptions ######################################################### -class RegistryException(CubicWebException): - """raised when an unregistered view is called""" - -class RegistryNotFound(RegistryException): - """raised when an unknown registry is requested - - this is usually a programming/typo error... - """ - -class ObjectNotFound(RegistryException): - """raised when an unregistered object is requested - - this may be a programming/typo or a misconfiguration error - """ - -class NoSelectableObject(RegistryException): - """raised when no appobject is selectable for a given context.""" - def __init__(self, args, kwargs, appobjects): - self.args = args - self.kwargs = kwargs - self.appobjects = appobjects - - def __str__(self): - return ('args: %s, kwargs: %s\ncandidates: %s' - % (self.args, self.kwargs.keys(), self.appobjects)) - +# pre 3.15 bw compat +from logilab.common.registry import RegistryException, ObjectNotFound, NoSelectableObject class UnknownProperty(RegistryException): """property found in database but unknown in registry""" @@ -154,6 +130,35 @@ a non final entity """ +class UndoTransactionException(QueryError): + """Raised when undoing a transaction could not be performed completely. + + Note that : + 1) the partial undo operation might be acceptable + depending upon the final application + + 2) the undo operation can also fail with a `ValidationError` in + cases where the undoing breaks integrity constraints checked + immediately. + + 3) It might be that neither of those exception is raised but a + subsequent `commit` might raise a `ValidationError` in cases + where the undoing breaks integrity constraints checked at + commit time. + + :type txuuix: int + :param txuuid: Unique identifier of the partialy undone transaction + + :type errors: list + :param errors: List of errors occured during undoing + """ + msg = u"The following error(s) occured while undoing transaction #%d : %s" + + def __init__(self, txuuid, errors): + super(UndoTransactionException, self).__init__(txuuid, errors) + self.txuuid = txuuid + self.errors = errors + # tools exceptions ############################################################ class ExecutionError(Exception): @@ -161,3 +166,4 @@ # pylint: disable=W0611 from logilab.common.clcommands import BadCommandUsage + diff -r 2279e02e62be -r 8be58694f416 appobject.py --- a/appobject.py Tue Mar 13 15:27:30 2012 +0100 +++ b/appobject.py Fri Mar 16 10:29:15 2012 +0100 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# 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. @@ -35,281 +35,25 @@ from logging import getLogger from warnings import warn -from logilab.common.deprecation import deprecated +from logilab.common.deprecation import deprecated, class_renamed from logilab.common.decorators import classproperty from logilab.common.logging_ext import set_log_methods +from logilab.common.registry import yes from cubicweb.cwconfig import CubicWebConfiguration - -def class_regid(cls): - """returns a unique identifier for an appobject class""" - return cls.__regid__ - -# helpers for debugging selectors -TRACED_OIDS = None - -def _trace_selector(cls, selector, args, ret): - # /!\ lltrace decorates pure function or __call__ method, this - # means argument order may be different - if isinstance(cls, Selector): - selname = str(cls) - vobj = args[0] - else: - selname = selector.__name__ - vobj = cls - if TRACED_OIDS == 'all' or class_regid(vobj) in TRACED_OIDS: - #SELECTOR_LOGGER.warning('selector %s returned %s for %s', selname, ret, cls) - print '%s -> %s for %s(%s)' % (selname, ret, vobj, vobj.__regid__) - -def lltrace(selector): - """use this decorator on your selectors so the becomes traceable with - :class:`traced_selection` - """ - # don't wrap selectors if not in development mode - if CubicWebConfiguration.mode == 'system': # XXX config.debug - return selector - def traced(cls, *args, **kwargs): - ret = selector(cls, *args, **kwargs) - if TRACED_OIDS is not None: - _trace_selector(cls, selector, args, ret) - return ret - traced.__name__ = selector.__name__ - traced.__doc__ = selector.__doc__ - return traced - -class traced_selection(object): - """ - Typical usage is : - - .. sourcecode:: python - - >>> from cubicweb.selectors import traced_selection - >>> with traced_selection(): - ... # some code in which you want to debug selectors - ... # for all objects - - Don't forget the 'from __future__ import with_statement' at the module top-level - if you're using python prior to 2.6. - - This will yield lines like this in the logs:: - - selector one_line_rset returned 0 for - - You can also give to :class:`traced_selection` the identifiers of objects on - which you want to debug selection ('oid1' and 'oid2' in the example above). - - .. sourcecode:: python - - >>> with traced_selection( ('regid1', 'regid2') ): - ... # some code in which you want to debug selectors - ... # for objects with __regid__ 'regid1' and 'regid2' - - A potentially usefull point to set up such a tracing function is - the `cubicweb.vregistry.Registry.select` method body. - """ - - def __init__(self, traced='all'): - self.traced = traced - - def __enter__(self): - global TRACED_OIDS - TRACED_OIDS = self.traced - - def __exit__(self, exctype, exc, traceback): - global TRACED_OIDS - TRACED_OIDS = None - return traceback is None - -# selector base classes and operations ######################################## - -def objectify_selector(selector_func): - """Most of the time, a simple score function is enough to build a selector. - The :func:`objectify_selector` decorator turn it into a proper selector - class:: - - @objectify_selector - def one(cls, req, rset=None, **kwargs): - return 1 - - class MyView(View): - __select__ = View.__select__ & one() - - """ - return type(selector_func.__name__, (Selector,), - {'__doc__': selector_func.__doc__, - '__call__': lambda self, *a, **kw: selector_func(*a, **kw)}) - - -def _instantiate_selector(selector): - """ensures `selector` is a `Selector` instance - - NOTE: This should only be used locally in build___select__() - XXX: then, why not do it ?? - """ - if isinstance(selector, types.FunctionType): - return objectify_selector(selector)() - if isinstance(selector, type) and issubclass(selector, Selector): - return selector() - return selector - - -class Selector(object): - """base class for selector classes providing implementation - for operators ``&``, ``|`` and ``~`` - - This class is only here to give access to binary operators, the - selector logic itself should be implemented in the __call__ method +# XXX for bw compat +from logilab.common.registry import objectify_predicate, traced_selection, Predicate - a selector is called to help choosing the correct object for a - particular context by returning a score (`int`) telling how well - the class given as first argument apply to the given context. - - 0 score means that the class doesn't apply. - """ - - @property - def func_name(self): - # backward compatibility - return self.__class__.__name__ - - def search_selector(self, selector): - """search for the given selector, selector instance or tuple of - selectors in the selectors tree. Return None if not found. - """ - if self is selector: - return self - if (isinstance(selector, type) or isinstance(selector, tuple)) and \ - isinstance(self, selector): - return self - return None - - def __str__(self): - return self.__class__.__name__ - - def __and__(self, other): - return AndSelector(self, other) - def __rand__(self, other): - return AndSelector(other, self) - def __iand__(self, other): - return AndSelector(self, other) - def __or__(self, other): - return OrSelector(self, other) - def __ror__(self, other): - return OrSelector(other, self) - def __ior__(self, other): - return OrSelector(self, other) - - def __invert__(self): - return NotSelector(self) - - # XXX (function | function) or (function & function) not managed yet - - def __call__(self, cls, *args, **kwargs): - return NotImplementedError("selector %s must implement its logic " - "in its __call__ method" % self.__class__) - - def __repr__(self): - return u'' % (self.__class__.__name__, id(self)) - - -class MultiSelector(Selector): - """base class for compound selector classes""" - - def __init__(self, *selectors): - self.selectors = self.merge_selectors(selectors) - - def __str__(self): - return '%s(%s)' % (self.__class__.__name__, - ','.join(str(s) for s in self.selectors)) - - @classmethod - def merge_selectors(cls, selectors): - """deal with selector instanciation when necessary and merge - multi-selectors if possible: +objectify_selector = deprecated('[3.15] objectify_selector has been renamed to objectify_predicates in logilab.common.registry')(objectify_predicate) +traced_selection = deprecated('[3.15] traced_selection has been moved to logilab.common.registry')(traced_selection) +Selector = class_renamed( + 'Selector', Predicate, + '[3.15] Selector has been renamed to Predicate in logilab.common.registry') - AndSelector(AndSelector(sel1, sel2), AndSelector(sel3, sel4)) - ==> AndSelector(sel1, sel2, sel3, sel4) - """ - merged_selectors = [] - for selector in selectors: - try: - selector = _instantiate_selector(selector) - except Exception: - pass - #assert isinstance(selector, Selector), selector - if isinstance(selector, cls): - merged_selectors += selector.selectors - else: - merged_selectors.append(selector) - return merged_selectors - - def search_selector(self, selector): - """search for the given selector or selector instance (or tuple of - selectors) in the selectors tree. Return None if not found - """ - for childselector in self.selectors: - if childselector is selector: - return childselector - found = childselector.search_selector(selector) - if found is not None: - return found - # if not found in children, maybe we are looking for self? - return super(MultiSelector, self).search_selector(selector) - - -class AndSelector(MultiSelector): - """and-chained selectors (formerly known as chainall)""" - @lltrace - def __call__(self, cls, *args, **kwargs): - score = 0 - for selector in self.selectors: - partscore = selector(cls, *args, **kwargs) - if not partscore: - return 0 - score += partscore - return score - - -class OrSelector(MultiSelector): - """or-chained selectors (formerly known as chainfirst)""" - @lltrace - def __call__(self, cls, *args, **kwargs): - for selector in self.selectors: - partscore = selector(cls, *args, **kwargs) - if partscore: - return partscore - return 0 - -class NotSelector(Selector): - """negation selector""" - def __init__(self, selector): - self.selector = selector - - @lltrace - def __call__(self, cls, *args, **kwargs): - score = self.selector(cls, *args, **kwargs) - return int(not score) - - def __str__(self): - return 'NOT(%s)' % self.selector - - -class yes(Selector): - """Return the score given as parameter, with a default score of 0.5 so any - other selector take precedence. - - Usually used for appobjects which can be selected whatever the context, or - also sometimes to add arbitrary points to a score. - - Take care, `yes(0)` could be named 'no'... - """ - def __init__(self, score=0.5): - self.score = score - - def __call__(self, *args, **kwargs): - return self.score - +@deprecated('[3.15] lltrace decorator can now be removed') +def lltrace(func): + return func # the base class for all appobjects ############################################ @@ -464,3 +208,6 @@ info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None set_log_methods(AppObject, getLogger('cubicweb.appobject')) + +# defined here to avoid warning on usage on the AppObject class +yes = deprecated('[3.15] yes has been moved to logilab.common.registry')(yes) diff -r 2279e02e62be -r 8be58694f416 cwconfig.py --- a/cwconfig.py Tue Mar 13 15:27:30 2012 +0100 +++ b/cwconfig.py Fri Mar 16 10:29:15 2012 +0100 @@ -825,7 +825,7 @@ _cubes = None def init_cubes(self, cubes): - assert self._cubes is None, self._cubes + assert self._cubes is None, repr(self._cubes) self._cubes = self.reorder_cubes(cubes) # load cubes'__init__.py file first for cube in cubes: diff -r 2279e02e62be -r 8be58694f416 cwvreg.py --- a/cwvreg.py Tue Mar 13 15:27:30 2012 +0100 +++ b/cwvreg.py Fri Mar 16 10:29:15 2012 +0100 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# 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. @@ -15,12 +15,12 @@ # # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . -""".. VRegistry: +""".. RegistryStore: -The `VRegistry` ---------------- +The `RegistryStore` +------------------- -The `VRegistry` can be seen as a two-level dictionary. It contains +The `RegistryStore` can be seen as a two-level dictionary. It contains all dynamically loaded objects (subclasses of :ref:`appobject`) to build a |cubicweb| application. Basically: @@ -34,7 +34,7 @@ A *registry* holds a specific kind of application objects. There is for instance a registry for entity classes, another for views, etc... -The `VRegistry` has two main responsibilities: +The `RegistryStore` has two main responsibilities: - being the access point to all registries @@ -76,13 +76,13 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here are the registration methods that you can use in the `registration_callback` -to register your objects to the `VRegistry` instance given as argument (usually +to register your objects to the `RegistryStore` instance given as argument (usually named `vreg`): -.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_all -.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register_and_replace -.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.register -.. automethod:: cubicweb.cwvreg.CubicWebVRegistry.unregister +.. automethod:: cubicweb.cwvreg.CWRegistryStore.register_all +.. automethod:: cubicweb.cwvreg.CWRegistryStore.register_and_replace +.. automethod:: cubicweb.cwvreg.CWRegistryStore.register +.. automethod:: cubicweb.cwvreg.CWRegistryStore.unregister Examples: @@ -193,41 +193,44 @@ __docformat__ = "restructuredtext en" _ = unicode +import sys +from os.path import join, dirname, realpath from warnings import warn from datetime import datetime, date, time, timedelta from logilab.common.decorators import cached, clear_cache from logilab.common.deprecation import deprecated, class_deprecated from logilab.common.modutils import cleanup_sys_modules +from logilab.common.registry import ( + RegistryStore, Registry, classid, + ObjectNotFound, NoSelectableObject, RegistryNotFound) from rql import RQLHelper from yams.constraints import BASE_CONVERTERS -from cubicweb import (ETYPE_NAME_MAP, Binary, UnknownProperty, UnknownEid, - ObjectNotFound, NoSelectableObject, RegistryNotFound, - CW_EVENT_MANAGER) -from cubicweb.vregistry import VRegistry, Registry, class_regid, classid +from cubicweb import (CW_SOFTWARE_ROOT, ETYPE_NAME_MAP, CW_EVENT_MANAGER, + Binary, UnknownProperty, UnknownEid) from cubicweb.rtags import RTAGS +from cubicweb.predicates import (implements, appobject_selectable, + _reset_is_instance_cache) def clear_rtag_objects(): for rtag in RTAGS: rtag.clear() def use_interfaces(obj): - """return interfaces used by the given object by searching for implements - selectors + """return interfaces required by the given object by searching for + `implements` predicate """ - from cubicweb.selectors import implements impl = obj.__select__.search_selector(implements) if impl: return sorted(impl.expected_ifaces) return () def require_appobject(obj): - """return interfaces used by the given object by searching for implements - selectors + """return appobjects required by the given object by searching for + `appobject_selectable` predicate """ - from cubicweb.selectors import appobject_selectable impl = obj.__select__.search_selector(appobject_selectable) if impl: return (impl.registry, impl.regids) @@ -253,16 +256,13 @@ key=lambda x: x.cw_propval('order')) -VRegistry.REGISTRY_FACTORY[None] = CWRegistry - class ETypeRegistry(CWRegistry): def clear_caches(self): clear_cache(self, 'etype_class') clear_cache(self, 'parent_classes') - from cubicweb import selectors - selectors._reset_is_instance_cache(self.vreg) + _reset_is_instance_cache(self.vreg) def initialization_completed(self): """on registration completed, clear etype_class internal cache @@ -272,7 +272,7 @@ self.clear_caches() def register(self, obj, **kwargs): - oid = kwargs.get('oid') or class_regid(obj) + oid = kwargs.get('oid') or obj.__regid__ if oid != 'Any' and not oid in self.schema: self.error('don\'t register %s, %s type not defined in the ' 'schema', obj, oid) @@ -354,8 +354,6 @@ fetchattrs_list.append(set(etypecls.fetch_attrs)) return reduce(set.intersection, fetchattrs_list) -VRegistry.REGISTRY_FACTORY['etypes'] = ETypeRegistry - class ViewsRegistry(CWRegistry): @@ -389,8 +387,6 @@ self.exception('error while trying to select %s view for %s', vid, rset) -VRegistry.REGISTRY_FACTORY['views'] = ViewsRegistry - class ActionsRegistry(CWRegistry): def poss_visible_objects(self, *args, **kwargs): @@ -408,8 +404,6 @@ result.setdefault(action.category, []).append(action) return result -VRegistry.REGISTRY_FACTORY['actions'] = ActionsRegistry - class CtxComponentsRegistry(CWRegistry): def poss_visible_objects(self, *args, **kwargs): @@ -445,8 +439,6 @@ component.cw_extra_kwargs['context'] = context return thisctxcomps -VRegistry.REGISTRY_FACTORY['ctxcomponents'] = CtxComponentsRegistry - class BwCompatCWRegistry(object): def __init__(self, vreg, oldreg, redirecttoreg): @@ -462,14 +454,15 @@ def clear(self): pass def initialization_completed(self): pass -class CubicWebVRegistry(VRegistry): + +class CWRegistryStore(RegistryStore): """Central registry for the cubicweb instance, extending the generic - VRegistry with some cubicweb specific stuff. + RegistryStore with some cubicweb specific stuff. This is one of the central object in cubicweb instance, coupling dynamically loaded objects with the schema and the configuration objects. - It specializes the VRegistry by adding some convenience methods to access to + It specializes the RegistryStore by adding some convenience methods to access to stored objects. Currently we have the following registries of objects known by the web instance (library may use some others additional registries): @@ -492,11 +485,29 @@ plugged into the application """ + REGISTRY_FACTORY = {None: CWRegistry, + 'etypes': ETypeRegistry, + 'views': ViewsRegistry, + 'actions': ActionsRegistry, + 'ctxcomponents': CtxComponentsRegistry, + } + def __init__(self, config, initlog=True): if initlog: # first init log service config.init_log() - super(CubicWebVRegistry, self).__init__(config) + super(CWRegistryStore, self).__init__(config.debugmode) + self.config = config + # need to clean sys.path this to avoid import confusion pb (i.e. having + # the same module loaded as 'cubicweb.web.views' subpackage and as + # views' or 'web.views' subpackage. This is mainly for testing purpose, + # we should'nt need this in production environment + for webdir in (join(dirname(realpath(__file__)), 'web'), + join(dirname(__file__), 'web')): + if webdir in sys.path: + sys.path.remove(webdir) + if CW_SOFTWARE_ROOT in sys.path: + sys.path.remove(CW_SOFTWARE_ROOT) self.schema = None self.initialized = False # XXX give force_reload (or refactor [re]loading...) @@ -515,10 +526,10 @@ return self[regid] def items(self): - return [item for item in super(CubicWebVRegistry, self).items() + return [item for item in super(CWRegistryStore, self).items() if not item[0] in ('propertydefs', 'propertyvalues')] def iteritems(self): - return (item for item in super(CubicWebVRegistry, self).iteritems() + return (item for item in super(CWRegistryStore, self).iteritems() if not item[0] in ('propertydefs', 'propertyvalues')) def values(self): @@ -528,7 +539,7 @@ def reset(self): CW_EVENT_MANAGER.emit('before-registry-reset', self) - super(CubicWebVRegistry, self).reset() + super(CWRegistryStore, self).reset() self._needs_iface = {} self._needs_appobject = {} # two special registries, propertydefs which care all the property @@ -597,7 +608,7 @@ the given `ifaces` interfaces at the end of the registration process. Extra keyword arguments are given to the - :meth:`~cubicweb.cwvreg.CubicWebVRegistry.register` function. + :meth:`~cubicweb.cwvreg.CWRegistryStore.register` function. """ self.register(obj, **kwargs) if not isinstance(ifaces, (tuple, list)): @@ -613,7 +624,7 @@ If `clear` is true, all objects with the same identifier will be previously unregistered. """ - super(CubicWebVRegistry, self).register(obj, *args, **kwargs) + super(CWRegistryStore, self).register(obj, *args, **kwargs) # XXX bw compat ifaces = use_interfaces(obj) if ifaces: @@ -630,7 +641,7 @@ def register_objects(self, path): """overriden to give cubicweb's extrapath (eg cubes package's __path__) """ - super(CubicWebVRegistry, self).register_objects( + super(CWRegistryStore, self).register_objects( path, self.config.extrapath) def initialization_completed(self): @@ -685,7 +696,7 @@ self.debug('unregister %s (no %s object in registry %s)', classid(obj), ' or '.join(regids), regname) self.unregister(obj) - super(CubicWebVRegistry, self).initialization_completed() + super(CWRegistryStore, self).initialization_completed() for rtag in RTAGS: # don't check rtags if we don't want to cleanup_interface_sobjects rtag.init(self.schema, check=self.config.cleanup_interface_sobjects) diff -r 2279e02e62be -r 8be58694f416 dbapi.py --- a/dbapi.py Tue Mar 13 15:27:30 2012 +0100 +++ b/dbapi.py Fri Mar 16 10:29:15 2012 +0100 @@ -58,9 +58,9 @@ attributes since classes are not designed to be shared among multiple registries. """ - defaultcls = cwvreg.VRegistry.REGISTRY_FACTORY[None] + defaultcls = cwvreg.CWRegistryStore.REGISTRY_FACTORY[None] - etypescls = cwvreg.VRegistry.REGISTRY_FACTORY['etypes'] + etypescls = cwvreg.CWRegistryStore.REGISTRY_FACTORY['etypes'] orig_etype_class = etypescls.orig_etype_class = etypescls.etype_class @monkeypatch(defaultcls) def etype_class(self, etype): @@ -75,7 +75,7 @@ return usercls def multiple_connections_unfix(): - etypescls = cwvreg.VRegistry.REGISTRY_FACTORY['etypes'] + etypescls = cwvreg.CWRegistryStore.REGISTRY_FACTORY['etypes'] etypescls.etype_class = etypescls.orig_etype_class @@ -192,7 +192,7 @@ elif setvreg: if mulcnx: multiple_connections_fix() - vreg = cwvreg.CubicWebVRegistry(config, initlog=initlog) + vreg = cwvreg.CWRegistryStore(config, initlog=initlog) schema = repo.get_schema() for oldetype, newetype in ETYPE_NAME_MAP.items(): if oldetype in schema: @@ -207,7 +207,7 @@ def in_memory_repo(config): """Return and in_memory Repository object from a config (or vreg)""" - if isinstance(config, cwvreg.CubicWebVRegistry): + if isinstance(config, cwvreg.CWRegistryStore): vreg = config config = None else: @@ -280,13 +280,17 @@ def __init__(self, vreg, session=None): super(DBAPIRequest, self).__init__(vreg) + #: 'language' => translation_function() mapping + self.translation = {} try: # no vreg or config which doesn't handle translations self.translations = vreg.config.translations except AttributeError: - self.translations = {} + pass + #: Request language identifier eg: 'en' + self.lang = None self.set_default_language(vreg) - # cache entities built during the request + #: cache entities built during the request self._eid_cache = {} if session is not None: self.set_session(session) @@ -556,6 +560,12 @@ except Exception: pass + # server-side service call ################################################# + + @check_not_closed + def call_service(self, regid, async=False, **kwargs): + return self._repo.call_service(self.sessionid, regid, async, **kwargs) + # connection initialization methods ######################################## def load_appobjects(self, cubes=_MARKER, subpath=None, expand=True): diff -r 2279e02e62be -r 8be58694f416 debian/control --- a/debian/control Tue Mar 13 15:27:30 2012 +0100 +++ b/debian/control Fri Mar 16 10:29:15 2012 +0100 @@ -35,8 +35,9 @@ Conflicts: cubicweb-multisources Replaces: cubicweb-multisources Provides: cubicweb-multisources -Depends: ${misc:Depends}, ${python:Depends}, cubicweb-common (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-logilab-database (>= 1.8.1), cubicweb-postgresql-support | cubicweb-mysql-support | python-pysqlite2 +Depends: ${misc:Depends}, ${python:Depends}, cubicweb-common (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-logilab-database (>= 1.8.2), cubicweb-postgresql-support | cubicweb-mysql-support | python-pysqlite2 Recommends: pyro (<< 4.0.0), cubicweb-documentation (= ${source:Version}) +Suggests: python-zmq Description: server part of the CubicWeb framework CubicWeb is a semantic web application framework. . @@ -99,7 +100,7 @@ Package: cubicweb-common Architecture: all XB-Python-Version: ${python:Versions} -Depends: ${misc:Depends}, ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.8.0), python-logilab-common (>= 0.57.0), python-yams (>= 0.34.0), python-rql (>= 0.28.0), python-lxml +Depends: ${misc:Depends}, ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.8.0), python-logilab-common (>= 0.58.0), python-yams (>= 0.34.0), python-rql (>= 0.28.0), python-lxml Recommends: python-simpletal (>= 4.0), python-crypto Conflicts: cubicweb-core Replaces: cubicweb-core diff -r 2279e02e62be -r 8be58694f416 devtools/__init__.py --- a/devtools/__init__.py Tue Mar 13 15:27:30 2012 +0100 +++ b/devtools/__init__.py Fri Mar 16 10:29:15 2012 +0100 @@ -168,7 +168,7 @@ def load_configuration(self): super(TestServerConfiguration, self).load_configuration() # no undo support in tests - self.global_set_option('undo-support', '') + self.global_set_option('undo-enabled', 'n') def main_config_file(self): """return instance's control configuration file""" @@ -480,8 +480,8 @@ session = repo._sessions[cnx.sessionid] session.set_cnxset() _commit = session.commit - def keep_cnxset_commit(): - _commit(free_cnxset=False) + def keep_cnxset_commit(free_cnxset=False): + _commit(free_cnxset=free_cnxset) session.commit = keep_cnxset_commit pre_setup_func(session, self.config) session.commit() diff -r 2279e02e62be -r 8be58694f416 devtools/devctl.py --- a/devtools/devctl.py Tue Mar 13 15:27:30 2012 +0100 +++ b/devtools/devctl.py Fri Mar 16 10:29:15 2012 +0100 @@ -107,7 +107,7 @@ notice that relation definitions description and static vocabulary should be marked using '_' and extracted using xgettext """ - from cubicweb.cwvreg import CubicWebVRegistry + from cubicweb.cwvreg import CWRegistryStore if cubedir: cube = osp.split(cubedir)[-1] config = DevConfiguration(cube) @@ -119,7 +119,7 @@ cube = libconfig = None cleanup_sys_modules(config) schema = config.load_schema(remove_unused_rtypes=False) - vreg = CubicWebVRegistry(config) + vreg = CWRegistryStore(config) # set_schema triggers objects registrations vreg.set_schema(schema) w(DEFAULT_POT_HEAD) @@ -138,13 +138,13 @@ w('\n') vregdone = set() if libconfig is not None: - from cubicweb.cwvreg import CubicWebVRegistry, clear_rtag_objects + from cubicweb.cwvreg import CWRegistryStore, clear_rtag_objects libschema = libconfig.load_schema(remove_unused_rtypes=False) afs = deepcopy(uicfg.autoform_section) appearsin_addmenu = deepcopy(uicfg.actionbox_appearsin_addmenu) clear_rtag_objects() cleanup_sys_modules(libconfig) - libvreg = CubicWebVRegistry(libconfig) + libvreg = CWRegistryStore(libconfig) libvreg.set_schema(libschema) # trigger objects registration libafs = uicfg.autoform_section libappearsin_addmenu = uicfg.actionbox_appearsin_addmenu diff -r 2279e02e62be -r 8be58694f416 devtools/fake.py --- a/devtools/fake.py Tue Mar 13 15:27:30 2012 +0100 +++ b/devtools/fake.py Fri Mar 16 10:29:15 2012 +0100 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# 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. @@ -23,7 +23,7 @@ from logilab.database import get_db_helper from cubicweb.req import RequestSessionBase -from cubicweb.cwvreg import CubicWebVRegistry +from cubicweb.cwvreg import CWRegistryStore from cubicweb.web.request import CubicWebRequestBase from cubicweb.web.http_headers import Headers @@ -33,7 +33,9 @@ class FakeConfig(dict, BaseApptestConfiguration): translations = {} uiprops = {} + https_uiprops = {} apphome = None + debugmode = False def __init__(self, appid='data', apphome=None, cubes=()): self.appid = appid self.apphome = apphome @@ -43,6 +45,7 @@ self['base-url'] = BASE_URL self['rql-cache-size'] = 3000 self.datadir_url = BASE_URL + 'data/' + self.https_datadir_url = (BASE_URL + 'data/').replace('http://', 'https://') def cubes(self, expand=False): return self._cubes @@ -56,7 +59,7 @@ def __init__(self, *args, **kwargs): if not (args or 'vreg' in kwargs): - kwargs['vreg'] = CubicWebVRegistry(FakeConfig(), initlog=False) + kwargs['vreg'] = CWRegistryStore(FakeConfig(), initlog=False) kwargs['https'] = False self._url = kwargs.pop('url', None) or 'view?rql=Blop&vid=blop' super(FakeRequest, self).__init__(*args, **kwargs) @@ -144,7 +147,7 @@ if vreg is None: vreg = getattr(self.repo, 'vreg', None) if vreg is None: - vreg = CubicWebVRegistry(FakeConfig(), initlog=False) + vreg = CWRegistryStore(FakeConfig(), initlog=False) self.vreg = vreg self.cnxset = FakeConnectionsSet() self.user = user or FakeUser() @@ -179,7 +182,7 @@ self._count = 0 self.schema = schema self.config = config or FakeConfig() - self.vreg = vreg or CubicWebVRegistry(self.config, initlog=False) + self.vreg = vreg or CWRegistryStore(self.config, initlog=False) self.vreg.schema = schema self.sources = [] diff -r 2279e02e62be -r 8be58694f416 devtools/test/data/views.py --- a/devtools/test/data/views.py Tue Mar 13 15:27:30 2012 +0100 +++ b/devtools/test/data/views.py Fri Mar 16 10:29:15 2012 +0100 @@ -1,4 +1,4 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# 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. @@ -18,7 +18,7 @@ """only for unit tests !""" from cubicweb.view import EntityView -from cubicweb.selectors import is_instance +from cubicweb.predicates import is_instance HTML_PAGE = u""" diff -r 2279e02e62be -r 8be58694f416 devtools/testlib.py --- a/devtools/testlib.py Tue Mar 13 15:27:30 2012 +0100 +++ b/devtools/testlib.py Fri Mar 16 10:29:15 2012 +0100 @@ -605,7 +605,7 @@ dump = json.dumps args = [dump(arg) for arg in args] req = self.request(fname=fname, pageid='123', arg=args) - ctrl = self.vreg['controllers'].select('json', req) + ctrl = self.vreg['controllers'].select('ajax', req) return ctrl.publish(), req def app_publish(self, req, path='view'): diff -r 2279e02e62be -r 8be58694f416 doc/3.15.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/3.15.rst Fri Mar 16 10:29:15 2012 +0100 @@ -0,0 +1,62 @@ +Whats new in CubicWeb 3.15 +========================== + + +API changes +----------- + +* The base registry implementation has been moved to a new + `logilab.common.registry` module (see #1916014). This includes code from : + + * `cubicweb.vreg` (the whole things that was in there) + * `cw.appobject` (base selectors and all). + + In the process, some renaming was done: + + * the top level registry is now `RegistryStore` (was `VRegistry`), but that + should not impact cubicweb client code ; + + * former selectors functions are now known as "predicate", though you still use + predicates to build an object'selector ; + + * for consistency, the `objectify_selector` decoraror has hence be renamed to + `objectify_predicate` ; + + * on the CubicWeb side, the `selectors` module has been renamed to + `predicates`. + + Debugging refactoring dropped the more need for the `lltrace` decorator. + + There should be full backward compat with proper deprecation warnings. + + Notice the `yes` predicate and `objectify_predicate` decorator, as well as the + `traced_selection` function should now be imported from the + `logilab.common.registry` module. + + +Unintrusive API changes +----------------------- + +* new 'ldapfeed' source type, designed to replace 'ldapuser' source with + data-feed (i.e. copy based) source ideas. + + +RQL +--- + + + +User interface changes +---------------------- + + + +Configuration +------------- + +Base schema changes +------------------- +Email address 'read' permission is now more restrictive: only managers and +users to which an address belong may see them. Application that wish other +settings should set them explicitly. + diff -r 2279e02e62be -r 8be58694f416 doc/book/en/admin/instance-config.rst --- a/doc/book/en/admin/instance-config.rst Tue Mar 13 15:27:30 2012 +0100 +++ b/doc/book/en/admin/instance-config.rst Fri Mar 16 10:29:15 2012 +0100 @@ -17,6 +17,7 @@ each option name is prefixed with its own section and followed by its default value if necessary, e.g. "`
.