Commit 690b4106 authored by Sylvain Thénault's avatar Sylvain Thénault
Browse files

R [vreg] important refactoring of the vregistry, moving behaviour to end...

R [vreg] important refactoring of the vregistry, moving behaviour to end dictionnary (and so leaving room for more flexibility ; keep bw compat ; update api usage in cw
parent 5d4a943695d1
......@@ -55,11 +55,11 @@ class AppRsetObject(VObject):
__select__ = yes()
@classmethod
def registered(cls, vreg):
super(AppRsetObject, cls).registered(vreg)
cls.vreg = vreg
cls.schema = vreg.schema
cls.config = vreg.config
def registered(cls, reg):
super(AppRsetObject, cls).registered(reg)
cls.vreg = reg.vreg
cls.schema = reg.schema
cls.config = reg.config
cls.register_properties()
return cls
......@@ -151,8 +151,8 @@ class AppRsetObject(VObject):
# try to get page boundaries from the navigation component
# XXX we should probably not have a ref to this component here (eg in
# cubicweb.common)
nav = self.vreg.select_object('components', 'navigation', self.req,
rset=self.rset)
nav = self.vreg['components'].select_object('navigation', self.req,
rset=self.rset)
if nav:
start, stop = nav.page_boundaries()
rql = self._limit_offset_rql(stop - start, start)
......@@ -188,10 +188,11 @@ class AppRsetObject(VObject):
rqlst.parent = None
return rql
def view(self, __vid, rset=None, __fallback_vid=None, **kwargs):
def view(self, __vid, rset=None, __fallback_oid=None, __registry='views',
**kwargs):
"""shortcut to self.vreg.view method avoiding to pass self.req"""
return self.vreg.render(__vid, self.req, __fallback_vid, rset=rset,
**kwargs)
return self.vreg[__registry].render(__vid, self.req, __fallback_oid,
rset=rset, **kwargs)
def initialize_varmaker(self):
varmaker = self.req.get_page_data('rql_varmaker')
......
This diff is collapsed.
......@@ -16,9 +16,10 @@ from time import time, clock
from itertools import count
from logilab.common.logging_ext import set_log_methods
from logilab.common.decorators import monkeypatch
from cubicweb import ETYPE_NAME_MAP, ConnectionError, RequestSessionMixIn
from cubicweb.cwvreg import CubicWebRegistry, MulCnxCubicWebRegistry
from cubicweb.cwconfig import CubicWebNoAppConfiguration
from cubicweb import cwvreg, cwconfig
_MARKER = object()
......@@ -28,6 +29,54 @@ def _fake_property_value(self, name):
except KeyError:
return ''
def _fix_cls_attrs(reg, vobject):
vobject.vreg = reg.vreg
vobject.schema = reg.schema
vobject.config = reg.config
def multiple_connections_fix():
"""some monkey patching necessary when an application has to deal with
several connections to different repositories. It tries to hide buggy class
attributes since classes are not designed to be shared among multiple
registries.
"""
defaultcls = cwvreg.VRegistry.REGISTRY_FACTORY[None]
orig_select_best = defaultcls.orig_select_best = defaultcls.select_best
@monkeypatch(defaultcls)
def select_best(self, vobjects, *args, **kwargs):
"""return an instance of the most specific object according
to parameters
raise NoSelectableObject if no object apply
"""
for vobjectcls in vobjects:
_fix_cls_attrs(self, vobjectcls)
selected = orig_select_best(self, vobjects, *args, **kwargs)
# redo the same thing on the instance so it won't use equivalent class
# attributes (which may change)
_fix_cls_attrs(self, selected)
return selected
etypescls = cwvreg.VRegistry.REGISTRY_FACTORY['etypes']
orig_etype_class = etypescls.orig_etype_class = etypescls.etype_class
@monkeypatch(defaultcls)
def etype_class(self, etype):
"""return an entity class for the given entity type.
Try to find out a specific class for this kind of entity or
default to a dump of the class registered for 'Any'
"""
usercls = orig_etype_class(self, etype)
if etype == 'Any':
return usercls
usercls.e_schema = self.schema.eschema(etype)
return usercls
def multiple_connections_unfix():
defaultcls = cwvreg.VRegistry.REGISTRY_FACTORY[None]
defaultcls.select_best = defaultcls.orig_select_best
etypescls = cwvreg.VRegistry.REGISTRY_FACTORY['etypes']
etypescls.etype_class = etypescls.orig_etype_class
class ConnectionProperties(object):
def __init__(self, cnxtype=None, lang=None, close=True, log=False):
self.cnxtype = cnxtype or 'pyro'
......@@ -88,12 +137,11 @@ def connect(database=None, login=None, password=None, host=None,
"""Constructor for creating a connection to the CubicWeb repository.
Returns a Connection object.
When method is 'pyro' and setvreg is True, use a special registry class
(MulCnxCubicWebRegistry) made to deal with connections to differents instances
in the same process unless specified otherwise by setting the mulcnx to
False.
When method is 'pyro', setvreg is True, try to deal with connections to
differents instances in the same process unless specified otherwise by
setting the mulcnx to False.
"""
config = CubicWebNoAppConfiguration()
config = cwconfig.CubicWebNoAppConfiguration()
if host:
config.global_set_option('pyro-ns-host', host)
if port:
......@@ -107,9 +155,8 @@ def connect(database=None, login=None, password=None, host=None,
vreg = repo.vreg
elif setvreg:
if mulcnx:
vreg = MulCnxCubicWebRegistry(config, initlog=initlog)
else:
vreg = CubicWebRegistry(config, initlog=initlog)
multiple_connections_fix()
vreg = cwvreg.CubicWebVRegistry(config, initlog=initlog)
schema = repo.get_schema()
for oldetype, newetype in ETYPE_NAME_MAP.items():
if oldetype in schema:
......@@ -126,7 +173,7 @@ def in_memory_cnx(config, login, password):
"""usefull method for testing and scripting to get a dbapi.Connection
object connected to an in-memory repository instance
"""
if isinstance(config, CubicWebRegistry):
if isinstance(config, cwvreg.CubicWebVRegistry):
vreg = config
config = None
else:
......@@ -476,8 +523,9 @@ class Connection(object):
if req is None:
req = self.request()
rset = req.eid_rset(eid, 'CWUser')
user = self.vreg.etype_class('CWUser')(req, rset, row=0, groups=groups,
properties=properties)
user = self.vreg['etypes'].etype_class('CWUser')(req, rset, row=0,
groups=groups,
properties=properties)
user['login'] = login # cache login
return user
......
......@@ -14,7 +14,7 @@ from logilab.common.pytest import pause_tracing, resume_tracing
import yams.schema
from cubicweb.dbapi import repo_connect, ConnectionProperties, ProgrammingError
from cubicweb.cwvreg import CubicWebRegistry
from cubicweb.cwvreg import CubicWebVRegistry
from cubicweb.web.application import CubicWebPublisher
from cubicweb.web import Redirect
......@@ -79,7 +79,7 @@ class TestEnvironment(object):
source = config.sources()['system']
if verbose:
print "init test database ..."
self.vreg = vreg = CubicWebRegistry(config)
self.vreg = vreg = CubicWebVRegistry(config)
self.admlogin = source['db-user']
# restore database <=> init database
self.restore_database()
......@@ -191,7 +191,7 @@ class TestEnvironment(object):
optional_args = {}
optional_args['vid'] = vid
req = self.create_request(rql=rql, **optional_args)
return self.vreg.main_template(req, template)
return self.vreg['views'].main_template(req, template)
def call_edit(self, req):
"""shortcut for self.app.edit()"""
......@@ -207,7 +207,7 @@ class TestEnvironment(object):
def iter_possible_views(self, req, rset):
"""returns a list of possible vids for <rql>"""
for view in self.vreg.possible_views(req, rset):
for view in self.vreg['views'].possible_views(req, rset):
if view.category == 'startupview':
continue
yield view.id
......@@ -216,7 +216,7 @@ class TestEnvironment(object):
def iter_startup_views(self, req):
"""returns the list of startup views"""
for view in self.vreg.possible_views(req, None):
for view in self.vreg['views'].possible_views(req, None):
if view.category != 'startupview':
continue
yield view.id
......@@ -233,7 +233,7 @@ class ExistingTestEnvironment(TestEnvironment):
if verbose:
print "init test database ..."
source = config.sources()['system']
self.vreg = CubicWebRegistry(config)
self.vreg = CubicWebVRegistry(config)
self.cnx = init_test_database(driver=source['db-driver'],
vreg=self.vreg)[1]
if verbose:
......
......@@ -175,7 +175,7 @@ class EnvBasedTC(TestCase):
def etype_instance(self, etype, req=None):
req = req or self.request()
e = self.env.vreg.etype_class(etype)(req, None, None)
e = self.env.vreg['etypes'].etype_class(etype)(req)
e.eid = None
return e
......@@ -224,21 +224,21 @@ class EnvBasedTC(TestCase):
self.vreg.config.global_set_option(optname, value)
def pviews(self, req, rset):
return sorted((a.id, a.__class__) for a in self.vreg.possible_views(req, rset))
return sorted((a.id, a.__class__) for a in self.vreg['views'].possible_views(req, rset=rset))
def pactions(self, req, rset, skipcategories=('addrelated', 'siteactions', 'useractions')):
return [(a.id, a.__class__) for a in self.vreg.possible_vobjects('actions', req, rset=rset)
return [(a.id, a.__class__) for a in self.vreg['actions'].possible_vobjects(req, rset=rset)
if a.category not in skipcategories]
def pactions_by_cats(self, req, rset, categories=('addrelated',)):
return [(a.id, a.__class__) for a in self.vreg.possible_vobjects('actions', req, rset=rset)
return [(a.id, a.__class__) for a in self.vreg['actions'].possible_vobjects(req, rset=rset)
if a.category in categories]
paddrelactions = deprecated()(pactions_by_cats)
def pactionsdict(self, req, rset, skipcategories=('addrelated', 'siteactions', 'useractions')):
res = {}
for a in self.vreg.possible_vobjects('actions', req, rset=rset):
for a in self.vreg['actions'].possible_vobjects(req, rset=rset):
if a.category not in skipcategories:
res.setdefault(a.category, []).append(a.__class__)
return res
......
......@@ -90,7 +90,7 @@ def generate_schema_pot(w, cubedir=None):
notice that relation definitions description and static vocabulary
should be marked using '_' and extracted using xgettext
"""
from cubicweb.cwvreg import CubicWebRegistry
from cubicweb.cwvreg import CubicWebVRegistry
cube = cubedir and split(cubedir)[-1]
libconfig = DevDepConfiguration(cube)
libconfig.cleanup_interface_sobjects = False
......@@ -102,7 +102,7 @@ def generate_schema_pot(w, cubedir=None):
config = libconfig
libconfig = None
schema = config.load_schema(remove_unused_rtypes=False)
vreg = CubicWebRegistry(config)
vreg = CubicWebVRegistry(config)
# set_schema triggers objects registrations
vreg.set_schema(schema)
w(DEFAULT_POT_HEAD)
......@@ -187,8 +187,8 @@ def _generate_schema_pot(w, vreg, schema, libconfig=None, cube=None):
#cube = (cube and 'cubes.%s.' % cube or 'cubicweb.')
done = set()
if libconfig is not None:
from cubicweb.cwvreg import CubicWebRegistry
libvreg = CubicWebRegistry(libconfig)
from cubicweb.cwvreg import CubicWebVRegistry
libvreg = CubicWebVRegistry(libconfig)
libvreg.set_schema(libschema) # trigger objects registration
# prefill done set
list(_iter_vreg_objids(libvreg, done))
......
......@@ -35,26 +35,21 @@ class FakeConfig(dict, BaseApptestConfiguration):
def sources(self):
return {}
class FakeVReg(object):
class FakeVReg(dict):
def __init__(self, schema=None, config=None):
self.schema = schema
self.config = config or FakeConfig()
self.properties = {'ui.encoding': 'UTF8',
'ui.language': 'en',
}
self.update({
'controllers' : {'login': []},
'views' : {},
})
def property_value(self, key):
return self.properties[key]
_registries = {
'controllers' : [Mock(id='view'), Mock(id='login'),
Mock(id='logout'), Mock(id='edit')],
'views' : [Mock(id='primary'), Mock(id='oneline'), Mock(id='list')],
}
def registry_objects(self, name, oid=None):
return self._registries[name]
def etype_class(self, etype):
class Entity(dict):
e_schema = self.schema[etype]
......
......@@ -185,7 +185,8 @@ class WebTest(EnvBasedTC):
req = req or rset and rset.req or self.request()
req.form['vid'] = vid
kwargs['rset'] = rset
view = self.vreg.select('views', vid, req, **kwargs)
viewsreg = self.vreg['views']
view = viewsreg.select(vid, req, **kwargs)
# set explicit test description
if rset is not None:
self.set_description("testing %s, mod=%s (%s)" % (
......@@ -197,9 +198,9 @@ class WebTest(EnvBasedTC):
viewfunc = view.render
else:
kwargs['view'] = view
templateview = self.vreg.select('views', template, req, **kwargs)
viewfunc = lambda **k: self.vreg.main_template(req, template,
**kwargs)
templateview = viewsreg.select(template, req, **kwargs)
viewfunc = lambda **k: viewsreg.main_template(req, template,
**kwargs)
kwargs.pop('rset')
return self._test_view(viewfunc, view, template, kwargs)
......@@ -272,7 +273,8 @@ class WebTest(EnvBasedTC):
req = rset.req
only_once_vids = ('primary', 'secondary', 'text')
req.data['ex'] = ValueError("whatever")
for vid, views in self.vreg.registry('views').items():
viewsvreg = self.vreg['views']
for vid, views in viewsvreg.items():
if vid[0] == '_':
continue
if rset.rowcount > 1 and vid in only_once_vids:
......@@ -282,7 +284,7 @@ class WebTest(EnvBasedTC):
and not issubclass(view, NotificationView)]
if views:
try:
view = self.vreg.select_best(views, req, rset=rset)
view = viewsvreg.select_best(views, req, rset=rset)
if view.linkable():
yield view
else:
......@@ -295,19 +297,19 @@ class WebTest(EnvBasedTC):
def list_actions_for(self, rset):
"""returns the list of actions that can be applied on `rset`"""
req = rset.req
for action in self.vreg.possible_objects('actions', req, rset=rset):
for action in self.vreg['actions'].possible_objects(req, rset=rset):
yield action
def list_boxes_for(self, rset):
"""returns the list of boxes that can be applied on `rset`"""
req = rset.req
for box in self.vreg.possible_objects('boxes', req, rset=rset):
for box in self.vreg['boxes'].possible_objects(req, rset=rset):
yield box
def list_startup_views(self):
"""returns the list of startup views"""
req = self.request()
for view in self.vreg.possible_views(req, None):
for view in self.vreg['views'].possible_views(req, None):
if view.category == 'startupview':
yield view.id
else:
......@@ -385,26 +387,25 @@ def vreg_instrumentize(testclass):
from cubicweb.devtools.apptest import TestEnvironment
env = testclass._env = TestEnvironment('data', configcls=testclass.configcls,
requestcls=testclass.requestcls)
vreg = env.vreg
vreg._selected = {}
orig_select_best = vreg.__class__.select_best
def instr_select_best(self, *args, **kwargs):
selected = orig_select_best(self, *args, **kwargs)
try:
self._selected[selected.__class__] += 1
except KeyError:
self._selected[selected.__class__] = 1
except AttributeError:
pass # occurs on vreg used to restore database
return selected
vreg.__class__.select_best = instr_select_best
for reg in env.vreg.values():
reg._selected = {}
orig_select_best = reg.__class__.select_best
def instr_select_best(self, *args, **kwargs):
selected = orig_select_best(self, *args, **kwargs)
try:
self._selected[selected.__class__] += 1
except KeyError:
self._selected[selected.__class__] = 1
except AttributeError:
pass # occurs on reg used to restore database
return selected
reg.__class__.select_best = instr_select_best
def print_untested_objects(testclass, skipregs=('hooks', 'etypes')):
vreg = testclass._env.vreg
for registry, vobjectsdict in vreg.items():
if registry in skipregs:
for regname, reg in testclass._env.vreg.iteritems():
if regname in skipregs:
continue
for vobjects in vobjectsdict.values():
for vobjects in reg.itervalues():
for vobject in vobjects:
if not vreg._selected.get(vobject):
print 'not tested', registry, vobject
if not reg._selected.get(vobject):
print 'not tested', regname, vobject
......@@ -276,8 +276,8 @@ class SpecializedEntityClassesTC(EnvBasedTC):
def select_eclass(self, etype):
# clear selector cache
clear_cache(self.vreg, 'etype_class')
return self.vreg.etype_class(etype)
clear_cache(self.vreg['etypes'], 'etype_class')
return self.vreg['etypes'].etype_class(etype)
def test_etype_class_selection_and_specialization(self):
# no specific class for Subdivisions, the default one should be selected
......
......@@ -265,7 +265,7 @@ class Entity(AppRsetObject, dict):
continue
if card == '?':
restrictions[-1] += '?' # left outer join if not mandatory
destcls = cls.vreg.etype_class(desttype)
destcls = cls.vreg['etypes'].etype_class(desttype)
destcls._fetch_restrictions(var, varmaker, destcls.fetch_attrs,
selection, orderby, restrictions,
user, ordermethod, visited=visited)
......@@ -277,8 +277,9 @@ class Entity(AppRsetObject, dict):
@classmethod
@cached
def parent_classes(cls):
parents = [cls.vreg.etype_class(e.type) for e in cls.e_schema.ancestors()]
parents.append(cls.vreg.etype_class('Any'))
parents = [cls.vreg['etypes'].etype_class(e.type)
for e in cls.e_schema.ancestors()]
parents.append(cls.vreg['etypes'].etype_class('Any'))
return parents
@classmethod
......@@ -364,10 +365,10 @@ class Entity(AppRsetObject, dict):
def has_perm(self, action):
return self.e_schema.has_perm(self.req, action, self.eid)
def view(self, vid, **kwargs):
def view(self, vid, __registry='views', **kwargs):
"""shortcut to apply a view on this entity"""
return self.vreg.render(vid, self.req, rset=self.rset,
row=self.row, col=self.col, **kwargs)
return self.vreg[__registry].render(vid, self.req, rset=self.rset,
row=self.row, col=self.col, **kwargs)
def absolute_url(self, *args, **kwargs):
"""return an absolute url to view this entity"""
......@@ -702,13 +703,13 @@ class Entity(AppRsetObject, dict):
if len(targettypes) > 1:
fetchattrs_list = []
for ttype in targettypes:
etypecls = self.vreg.etype_class(ttype)
etypecls = self.vreg['etypes'].etype_class(ttype)
fetchattrs_list.append(set(etypecls.fetch_attrs))
fetchattrs = reduce(set.intersection, fetchattrs_list)
rql = etypecls.fetch_rql(self.req.user, [restriction], fetchattrs,
settype=False)
else:
etypecls = self.vreg.etype_class(targettypes[0])
etypecls = self.vreg['etypes'].etype_class(targettypes[0])
rql = etypecls.fetch_rql(self.req.user, [restriction], settype=False)
# optimisation: remove ORDERBY if cardinality is 1 or ? (though
# greater_card return 1 for those both cases)
......@@ -764,7 +765,7 @@ class Entity(AppRsetObject, dict):
else:
restriction += [cstr.restriction for cstr in constraints
if isinstance(cstr, RQLConstraint)]
etypecls = self.vreg.etype_class(targettype)
etypecls = self.vreg['etypes'].etype_class(targettype)
rql = etypecls.fetch_rql(self.req.user, restriction,
mainvar=searchedvar, ordermethod=ordermethod)
# ensure we have an order defined
......
......@@ -274,8 +274,8 @@ class Model(entities.AnyEntity):
def view(self, vid, __registry='views', **kwargs):
"""shortcut to apply a view on this entity"""
return self.vreg.render(__registry, vid, self.req, rset=self.rset,
row=self.row, col=self.col, **kwargs)
return self.vreg[__registry]render(vid, self.req, rset=self.rset,
row=self.row, col=self.col, **kwargs)
@classmethod
def _rest_attr_info(cls):
......
......@@ -11,7 +11,7 @@ from os import listdir
from os.path import join, isdir
from cubicweb import CW_SOFTWARE_ROOT
from cubicweb.cwvreg import CubicWebRegistry
from cubicweb.cwvreg import CubicWebVRegistry
def _pkg_name(cube, module):
......@@ -19,7 +19,7 @@ def _pkg_name(cube, module):
return module
return 'cubes.%s.%s' % (cube, module)
class GAERegistry(CubicWebRegistry):
class GAEVRegistry(CubicWebVRegistry):
def set_schema(self, schema):
"""disable reload hooks of cubicweb registry set_schema method"""
......
......@@ -22,8 +22,8 @@ GAEConfiguration.ext_resources['JAVASCRIPTS'].append('DATADIR/goa.js')
config = GAEConfiguration('toto', APPLROOT)
# dynamic objects registry
from cubicweb.goa.goavreg import GAERegistry
vreg = GAERegistry(config, debug=goa.MODE == 'dev')
from cubicweb.goa.goavreg import GAEVregistry
vreg = GAEVregistry(config, debug=goa.MODE == 'dev')
# trigger automatic classes registration (metaclass magic), should be done
# before schema loading
......
......@@ -46,10 +46,10 @@ class SomeViewsTC(GAEBasedTC):
self.blog.put(self.req)
def test_hcal(self):
self.vreg.render('views', 'hcal', self.req, rset=self.blog.rset)
self.vreg['views'].render('hcal', self.req, rset=self.blog.rset)
def test_django_index(self):
self.vreg.render('views', 'index', self.req, rset=None)
self.vreg'views'].render('index', self.req, rset=None)
for vid in ('primary', 'oneline', 'incontext', 'outofcontext', 'text'):
setattr(SomeViewsTC, 'test_%s'%vid, lambda self, vid=vid: self.blog.view(vid))
......
......@@ -42,7 +42,7 @@ except ImportError, exc:
from cubicweb.devtools.fake import FakeRequest
from cubicweb.goa.goavreg import GAERegistry
from cubicweb.goa.goavreg import GAEVregistry
from cubicweb.goa.goaconfig import GAEConfiguration
from cubicweb.goa.dbinit import (create_user, create_groups, fix_entities,
init_persistent_schema, insert_versions)
......@@ -115,7 +115,7 @@ class GAEBasedTC(TestCase):
self.config.init_log(logging.CRITICAL)
self.schema = self.config.load_schema(self.MODEL_CLASSES,
self.load_schema_hook)
self.vreg = GAERegistry(self.config)
self.vreg = GAEVregistry(self.config)
self.vreg.schema = self.schema
self.vreg.load_module(db)
from cubicweb.goa.appobjects import sessions
......
......@@ -28,11 +28,11 @@ def initialize_vregistry(applroot):
# apply monkey patches first
from cubicweb.goa import do_monkey_patch
do_monkey_patch()
from cubicweb.goa.goavreg import GAERegistry
from cubicweb.goa.goavreg import GAEVregistry
from cubicweb.goa.goaconfig import GAEConfiguration
#WebConfiguration.ext_resources['JAVASCRIPTS'].append('DATADIR/goa.js')
config = GAEConfiguration('toto', applroot)
vreg = GAERegistry(config)
vreg = GAEVregistry(config)
vreg.set_schema(config.load_schema())
return vreg
......
......@@ -83,8 +83,8 @@ class ResultSet(object):
try:
return self._rsetactions[key]
except KeyError:
actions = self.vreg.possible_vobjects('actions', self.req,
rset=self, **kwargs)
actions = self.vreg['actions'].possible_vobjects(
self.req, rset=self, **kwargs)
self._rsetactions[key] = actions
return actions
......@@ -383,7 +383,8 @@ class ResultSet(object):
pass
# build entity instance
etype = self.description[row][col]
entity = self.vreg.etype_class(etype)(req, self, row, col)
entity = self.vreg['etypes'].etype_class(etype)(req, rset=self,
row=row, col=col)
entity.set_eid(eid)
# cache entity
req.set_entity_cache(entity)
......
......@@ -19,7 +19,7 @@ class SchemaViewer(object):
self.req = req
if req is not None:
self.req.add_css('cubicweb.schema.css')