--- a/__init__.py Tue Oct 13 18:21:24 2009 +0200
+++ b/__init__.py Thu Oct 15 10:31:54 2009 +0200
@@ -21,6 +21,7 @@
from logilab.common.logging_ext import set_log_methods
+
if os.environ.get('APYCOT_ROOT'):
logging.basicConfig(level=logging.CRITICAL)
else:
@@ -71,7 +72,6 @@
}
-
# XXX cubic web cube migration map
CW_MIGRATION_MAP = {'erudi': 'cubicweb',
'eaddressbook': 'addressbook',
--- a/cwconfig.py Tue Oct 13 18:21:24 2009 +0200
+++ b/cwconfig.py Thu Oct 15 10:31:54 2009 +0200
@@ -5,10 +5,71 @@
:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-.. envvar:: CW_CUBES_PATH
+
+If cubicweb is a mercurial checkout (eg `CWDEV` is true), located in
+`CW_SOFTWARE_ROOT`:
+
+ * main cubes directory is `<CW_SOFTWARE_ROOT>/../cubes`. You can specify
+ another one with `CW_INSTANCES_DIR` environment variable or simply add some
+ other directories by using `CW_CUBES_PATH`.
+
+ * cubicweb migration files are by default searched in
+ `<CW_SOFTWARE_ROOT>/misc/migration` instead of
+ `/usr/share/cubicweb/migration/`(unless another emplacement is specified
+ using `CW_MIGRATION_DIR`.
+
+ * Cubicweb will start in 'user' mode (see below)
+
+
+On startup, Cubicweb is using a specific *mode*. A mode corresponds to some
+default setting for various resource directories. There are currently 2 main
+modes : 'system', for system wide installation, and 'user', fur user local
+installation (e.g. no root privileges).
+
+'user' mode is activated automatically when cubicweb is a mercurial checkout
+(e.g. has a .hg directory). You can also force mode by using the `CW_MODE`
+environment variable, to:
+
+* use system wide installation but user specific instances and all, without root
+ privileges on the system (`export CW_MODE=user`)
+
+* use local checkout of cubicweb on system wide instances (requires root
+ privileges on the system (`export CW_MODE=system`)
+ Here is the default resource directories settings according to mode:
+
+* 'system': ::
+
+ CW_INSTANCES_DIR = /etc/cubicweb.d/
+ CW_INSTANCES_DATA_DIR = /var/lib/cubicweb/instances/
+ CW_RUNTIME_DIR = /var/run/cubicweb/
+
+ * 'user': ::
+
+ CW_INSTANCES_DIR = ~/etc/cubicweb.d/
+ CW_INSTANCES_DATA_DIR = ~/etc/cubicweb.d/
+ CW_RUNTIME_DIR = /tmp
+
+
+.. envvar:: CW_MODE
+ Resource mode: user or system
+
+.. envvar:: CW_CUBES_PATH
Augments the default search path for cubes
+.. envvar:: CW_INSTANCES_DIR
+ Directory where cubicweb instances will be found
+
+.. envvar:: CW_INSTANCES_DATA_DIR
+ Directory where cubicweb instances data will be written
+
+.. envvar:: CW_RUNTIME_DIR
+ Directory where pid files will be written
+
+.. envvar:: CW_MIGRATION_DIR
+ Directory where cubicweb migration files will be found
+
+
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
__docformat__ = "restructuredtext en"
@@ -138,6 +199,8 @@
_forced_mode = os.environ.get('CW_MODE')
assert _forced_mode in (None, 'system', 'user')
+CWDEV = exists(join(CW_SOFTWARE_ROOT, '.hg'))
+
class CubicWebNoAppConfiguration(ConfigurationMixIn):
"""base class for cubicweb configuration without a specific instance directory
"""
@@ -148,17 +211,22 @@
log_format = '%(asctime)s - (%(name)s) %(levelname)s: %(message)s'
# nor remove appobjects based on unused interface
cleanup_interface_sobjects = True
+ # debug mode
+ debugmode = False
if os.environ.get('APYCOT_ROOT'):
mode = 'test'
CUBES_DIR = '%(APYCOT_ROOT)s/local/share/cubicweb/cubes/' % os.environ
# create __init__ file
file(join(CUBES_DIR, '__init__.py'), 'w').close()
- elif (exists(join(CW_SOFTWARE_ROOT, '.hg')) and _forced_mode != 'system') or _forced_mode == 'user':
- mode = 'dev'
+ elif (CWDEV and _forced_mode != 'system'):
+ mode = 'user'
CUBES_DIR = abspath(normpath(join(CW_SOFTWARE_ROOT, '../cubes')))
else:
- mode = 'installed'
+ if _forced_mode == 'user':
+ mode = 'user'
+ else:
+ mode = 'system'
CUBES_DIR = '/usr/share/cubicweb/cubes/'
options = (
@@ -245,14 +313,14 @@
"""return the shared data directory (i.e. directory where standard
library views and data may be found)
"""
- if cls.mode in ('dev', 'test') and not os.environ.get('APYCOT_ROOT'):
+ if CWDEV:
return join(CW_SOFTWARE_ROOT, 'web')
return cls.cube_dir('shared')
@classmethod
def i18n_lib_dir(cls):
"""return instance's i18n directory"""
- if cls.mode in ('dev', 'test') and not os.environ.get('APYCOT_ROOT'):
+ if CWDEV:
return join(CW_SOFTWARE_ROOT, 'i18n')
return join(cls.shared_dir(), 'i18n')
@@ -261,7 +329,7 @@
cubes = set()
for directory in cls.cubes_search_path():
for cube in os.listdir(directory):
- if isdir(join(directory, cube)) and not cube in ('CVS', '.svn', 'shared', '.hg'):
+ if isdir(join(directory, cube)) and not cube == 'shared':
cubes.add(cube)
return sorted(cubes)
@@ -497,6 +565,7 @@
logthreshold = 'DEBUG'
else:
logthreshold = self['log-threshold']
+ self.debugmode = debug
init_log(debug, syslog, logthreshold, logfile, self.log_format)
# configure simpleTal logger
logging.getLogger('simpleTAL').setLevel(logging.ERROR)
@@ -538,7 +607,7 @@
class CubicWebConfiguration(CubicWebNoAppConfiguration):
"""base class for cubicweb server and web configurations"""
- INSTANCE_DATA_DIR = None
+ INSTANCES_DATA_DIR = None
if CubicWebNoAppConfiguration.mode == 'test':
root = os.environ['APYCOT_ROOT']
REGISTRY_DIR = '%s/etc/cubicweb.d/' % root
@@ -546,15 +615,19 @@
MIGRATION_DIR = '%s/local/share/cubicweb/migration/' % root
if not exists(REGISTRY_DIR):
os.makedirs(REGISTRY_DIR)
- elif CubicWebNoAppConfiguration.mode == 'dev':
- REGISTRY_DIR = expanduser('~/etc/cubicweb.d/')
- RUNTIME_DIR = tempfile.gettempdir()
- MIGRATION_DIR = join(CW_SOFTWARE_ROOT, 'misc', 'migration')
- else: #mode = 'installed'
- REGISTRY_DIR = '/etc/cubicweb.d/'
- INSTANCE_DATA_DIR = '/var/lib/cubicweb/instances/'
- RUNTIME_DIR = '/var/run/cubicweb/'
- MIGRATION_DIR = '/usr/share/cubicweb/migration/'
+ else:
+ if CubicWebNoAppConfiguration.mode == 'user':
+ REGISTRY_DIR = expanduser('~/etc/cubicweb.d/')
+ RUNTIME_DIR = tempfile.gettempdir()
+ INSTANCES_DATA_DIR = REGISTRY_DIR
+ else: #mode = 'system'
+ REGISTRY_DIR = '/etc/cubicweb.d/'
+ RUNTIME_DIR = '/var/run/cubicweb/'
+ INSTANCES_DATA_DIR = '/var/lib/cubicweb/instances/'
+ if CWDEV:
+ MIGRATION_DIR = join(CW_SOFTWARE_ROOT, 'misc', 'migration')
+ else:
+ MIGRATION_DIR = '/usr/share/cubicweb/migration/'
# for some commands (creation...) we don't want to initialize gettext
set_language = True
@@ -612,8 +685,7 @@
@classmethod
def instance_data_dir(cls):
"""return the instance data directory"""
- return env_path('CW_INSTANCES_DATA_DIR',
- cls.INSTANCE_DATA_DIR or cls.REGISTRY_DIR,
+ return env_path('CW_INSTANCES_DATA_DIR', cls.INSTANCES_DATA_DIR,
'additional data')
@classmethod
@@ -666,7 +738,7 @@
def default_log_file(self):
"""return default path to the log file of the instance'server"""
- if self.mode == 'dev':
+ if self.mode == 'user':
basepath = join(tempfile.gettempdir(), '%s-%s' % (basename(self.appid), self.name))
path = basepath + '.log'
i = 1
--- a/cwctl.py Tue Oct 13 18:21:24 2009 +0200
+++ b/cwctl.py Thu Oct 15 10:31:54 2009 +0200
@@ -18,10 +18,9 @@
from logilab.common.shellutils import ASK
from cubicweb import ConfigurationError, ExecutionError, BadCommandUsage
-from cubicweb.cwconfig import CubicWebConfiguration as cwcfg, CONFIGURATIONS
+from cubicweb.cwconfig import CubicWebConfiguration as cwcfg, CWDEV, CONFIGURATIONS
from cubicweb.toolsutils import Command, main_run, rm, create_dir, underline_title
-
def wait_process_end(pid, maxtry=10, waittime=1):
"""wait for a process to actually die"""
import signal
@@ -657,7 +656,7 @@
for cube, fromversion, toversion in toupgrade:
print '-> migration needed from %s to %s for %s' % (fromversion, toversion, cube)
# only stop once we're sure we have something to do
- if not (cwcfg.mode == 'dev' or self.config.nostartstop):
+ if not (CWDEV or self.config.nostartstop):
StopInstanceCommand().stop_instance(appid)
# run cubicweb/componants migration scripts
mih.migrate(vcconf, reversed(toupgrade), self.config)
@@ -680,7 +679,7 @@
mih.shutdown()
print
print '-> instance migrated.'
- if not (cwcfg.mode == 'dev' or self.config.nostartstop):
+ if not (CWDEV or self.config.nostartstop):
StartInstanceCommand().start_instance(appid)
print
--- a/cwvreg.py Tue Oct 13 18:21:24 2009 +0200
+++ b/cwvreg.py Thu Oct 15 10:31:54 2009 +0200
@@ -344,7 +344,7 @@
def register_objects(self, path, force_reload=None):
"""overriden to remove objects requiring a missing interface"""
if force_reload is None:
- force_reload = self.config.mode == 'dev'
+ force_reload = self.config.debugmode
try:
self._register_objects(path, force_reload)
except RegistryOutOfDate:
--- a/devtools/devctl.py Tue Oct 13 18:21:24 2009 +0200
+++ b/devtools/devctl.py Thu Oct 15 10:31:54 2009 +0200
@@ -492,8 +492,6 @@
if len(args) != 1:
raise BadCommandUsage("exactly one argument (cube name) is expected")
cubename, = args
- #if ServerConfiguration.mode != "dev":
- # self.fail("you can only create new cubes in development mode")
verbose = self.get('verbose')
cubesdir = self.get('directory')
if not cubesdir:
--- a/devtools/fill.py Tue Oct 13 18:21:24 2009 +0200
+++ b/devtools/fill.py Thu Oct 15 10:31:54 2009 +0200
@@ -404,12 +404,14 @@
for subjeid, objeid in used:
if subjcard in '?1' and subjeid in subjeids:
subjeids.remove(subjeid)
- if objeid in objeids:
- objeids.remove(objeid)
+ # XXX why?
+ #if objeid in objeids:
+ # objeids.remove(objeid)
if objcard in '?1' and objeid in objeids:
objeids.remove(objeid)
- if subjeid in subjeids:
- subjeids.remove(subjeid)
+ # XXX why?
+ #if subjeid in subjeids:
+ # subjeids.remove(subjeid)
if not subjeids:
check_card_satisfied(objcard, objeids, subj, rschema, obj)
return
--- a/doc/book/en/development/testing/index.rst Tue Oct 13 18:21:24 2009 +0200
+++ b/doc/book/en/development/testing/index.rst Thu Oct 15 10:31:54 2009 +0200
@@ -56,7 +56,7 @@
# the default admin connection and one may be tempted to close it
self.restore_connection()
-Take care of the references kept to the entities created with a connection or the other.
+Do not use the references kept to the entities created with a connection from another.
Email notifications tests
--- a/entities/wfobjs.py Tue Oct 13 18:21:24 2009 +0200
+++ b/entities/wfobjs.py Thu Oct 15 10:31:54 2009 +0200
@@ -417,17 +417,11 @@
def cwetype_workflow(self):
"""return the default workflow for entities of this type"""
# XXX CWEType method
- wfrset = self._cw.execute('Any WF WHERE X is ET, X eid %(x)s, '
- 'WF workflow_of ET', {'x': self.eid}, 'x')
- if len(wfrset) == 1:
+ wfrset = self._cw.execute('Any WF WHERE ET default_workflow WF, '
+ 'ET name %(et)s', {'et': self.id})
+ if wfrset:
return wfrset.get_entity(0, 0)
- if len(wfrset) > 1:
- for wf in wfrset.entities():
- if wf.is_default_workflow_of(self.__regid__):
- return wf
- self.warning("can't find default workflow for %s", self.__regid__)
- else:
- self.warning("can't find any workflow for %s", self.__regid__)
+ self.warning("can't find any workflow for %s", self.id)
return None
def possible_transitions(self, type='normal'):
--- a/entity.py Tue Oct 13 18:21:24 2009 +0200
+++ b/entity.py Thu Oct 15 10:31:54 2009 +0200
@@ -18,7 +18,7 @@
from rql import parse
from rql.utils import rqlvar_maker
-from cubicweb import Unauthorized
+from cubicweb import Unauthorized, typed_eid
from cubicweb.rset import ResultSet
from cubicweb.selectors import yes
from cubicweb.appobject import AppObject
@@ -232,7 +232,7 @@
meaning that the entity has to be created
"""
try:
- int(self.eid)
+ typed_eid(self.eid)
return True
except (ValueError, TypeError):
return False
@@ -587,14 +587,18 @@
def related_rql(self, rtype, role='subject', targettypes=None):
rschema = self._cw.vreg.schema[rtype]
if role == 'subject':
+ restriction = 'E eid %%(x)s, E %s X' % rtype
if targettypes is None:
targettypes = rschema.objects(self.e_schema)
- restriction = 'E eid %%(x)s, E %s X' % rtype
+ else:
+ restriction += 'E is IN (%s)' % ','.join(targettypes)
card = greater_card(rschema, (self.e_schema,), targettypes, 0)
else:
+ restriction = 'E eid %%(x)s, X %s E' % rtype
if targettypes is None:
targettypes = rschema.subjects(self.e_schema)
- restriction = 'E eid %%(x)s, X %s E' % rtype
+ else:
+ restriction += 'E is IN (%s)' % ','.join(targettypes)
card = greater_card(rschema, targettypes, (self.e_schema,), 1)
if len(targettypes) > 1:
fetchattrs_list = []
@@ -741,9 +745,14 @@
self._related_cache.pop('%s_%s' % (rtype, role), None)
def clear_all_caches(self):
+ haseid = 'eid' in self
self.clear()
for rschema, _, role in self.e_schema.relation_definitions():
self.clear_related_cache(rschema.type, role)
+ # set eid if it was in, else we may get nasty error while editing this
+ # entity if it's bound to a repo session
+ if haseid:
+ self['eid'] = self.eid
# raw edition utilities ###################################################
@@ -763,6 +772,20 @@
self._cw.execute('SET %s WHERE X eid %%(x)s' % ','.join(relations),
kwargs, 'x')
+ def set_relations(self, _cw_unsafe=False, **kwargs):
+ if _cw_unsafe:
+ execute = self.req.unsafe_execute
+ else:
+ execute = self.req.execute
+ for attr, values in kwargs.iteritems():
+ if attr.startswith('reverse_'):
+ restr = 'Y %s X' % attr[len('reverse_'):]
+ else:
+ restr = 'X %s Y' % attr
+ execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % (
+ restr, ','.join(str(r.eid) for r in values)),
+ {'x': self.eid}, 'x')
+
def delete(self):
assert self.has_eid(), self.eid
self._cw.execute('DELETE %s X WHERE X eid %%(x)s' % self.e_schema,
--- a/etwist/server.py Tue Oct 13 18:21:24 2009 +0200
+++ b/etwist/server.py Thu Oct 15 10:31:54 2009 +0200
@@ -180,8 +180,8 @@
def render(self, request):
"""Render a page from the root resource"""
- # reload modified files (only in development or debug mode)
- if self.config.mode == 'dev' or self.debugmode:
+ # reload modified files in debug mode
+ if self.debugmode:
self.appli.vreg.register_objects(self.config.vregistry_path())
if self.config['profile']: # default profiler don't trace threads
return self.render_request(request)
--- a/etwist/twconfig.py Tue Oct 13 18:21:24 2009 +0200
+++ b/etwist/twconfig.py Thu Oct 15 10:31:54 2009 +0200
@@ -47,7 +47,7 @@
'default': None,
'help': 'if this option is set, use the specified user to start \
the repository rather than the user running the command',
- 'group': 'main', 'inputlevel': (WebConfiguration.mode == 'installed') and 0 or 1,
+ 'group': 'main', 'inputlevel': WebConfiguration.mode == 'system'
}),
('session-time',
{'type' : 'int',
--- a/hooks/workflow.py Tue Oct 13 18:21:24 2009 +0200
+++ b/hooks/workflow.py Thu Oct 15 10:31:54 2009 +0200
@@ -106,6 +106,34 @@
outputs.add(ep.subwf_state.eid)
+class _SubWorkflowExitOp(PreCommitOperation):
+
+ def precommit_event(self):
+ session = self.session
+ forentity = self.forentity
+ trinfo = self.trinfo
+ # we're in a subworkflow, check if we've reached an exit point
+ wftr = forentity.subworkflow_input_transition()
+ if wftr is None:
+ # inconsistency detected
+ msg = session._("state doesn't belong to entity's current workflow")
+ raise ValidationError(self.trinfo.eid, {'to_state': msg})
+ tostate = wftr.get_exit_point(forentity, trinfo['to_state'])
+ if tostate is not None:
+ # reached an exit point
+ msg = session._('exiting from subworkflow %s')
+ msg %= session._(forentity.current_workflow.name)
+ session.transaction_data[(forentity.eid, 'subwfentrytr')] = True
+ # XXX iirk
+ req = forentity.req
+ forentity.req = session.super_session
+ try:
+ trinfo = forentity.change_state(tostate, msg, u'text/plain',
+ tr=wftr)
+ finally:
+ forentity.req = req
+
+
# hooks ########################################################################
class WorkflowHook(hook.Hook):
@@ -229,33 +257,13 @@
events = ('after_add_entity',)
def __call__(self):
- session = self._cw
- entity = self.entity
- _change_state(session, entity['wf_info_for'],
- entity['from_state'], entity['to_state'])
- forentity = session.entity_from_eid(entity['wf_info_for'])
- assert forentity.current_state.eid == entity['to_state']
+ trinfo = self.entity
+ _change_state(self._cw, trinfo['wf_info_for'],
+ trinfo['from_state'], trinfo['to_state'])
+ forentity = self._cw.entity_from_eid(trinfo['wf_info_for'])
+ assert forentity.current_state.eid == trinfo['to_state']
if forentity.main_workflow.eid != forentity.current_workflow.eid:
- # we're in a subworkflow, check if we've reached an exit point
- wftr = forentity.subworkflow_input_transition()
- if wftr is None:
- # inconsistency detected
- msg = session._("state doesn't belong to entity's current workflow")
- raise ValidationError(entity.eid, {'to_state': msg})
- tostate = wftr.get_exit_point(forentity, entity['to_state'])
- if tostate is not None:
- # reached an exit point
- msg = session._('exiting from subworkflow %s')
- msg %= session._(forentity.current_workflow.name)
- session.transaction_data[(forentity.eid, 'subwfentrytr')] = True
- # XXX iirk
- req = forentity._cw
- forentity._cw = session.super_session
- try:
- trinfo = forentity.change_state(tostate, msg, u'text/plain',
- tr=wftr)
- finally:
- forentity._cw = req
+ _SubWorkflowExitOp(self._cw, forentity=forentity, trinfo=trinfo)
class CheckInStateChangeAllowed(WorkflowHook):
--- a/interfaces.py Tue Oct 13 18:21:24 2009 +0200
+++ b/interfaces.py Thu Oct 15 10:31:54 2009 +0200
@@ -127,7 +127,7 @@
def children_rql(self):
"""XXX returns RQL to get children"""
- def __iter__(self):
+ def iterchildren(self):
"""iterates over the item's children"""
def is_leaf(self):
--- a/misc/migration/3.5.3_Any.py Tue Oct 13 18:21:24 2009 +0200
+++ b/misc/migration/3.5.3_Any.py Thu Oct 15 10:31:54 2009 +0200
@@ -1,7 +1,7 @@
# type attribute might already be there if migrating from
# version < 3.5 to version >= 3.5.3, BaseTransition being added
# in bootstrap_migration
-if versions_map['cubicweb'][0] < (3, 5, 0):
+if versions_map['cubicweb'][0] >= (3, 5, 0):
add_attribute('BaseTransition', 'type')
sync_schema_props_perms('state_of')
sync_schema_props_perms('transition_of')
--- a/req.py Tue Oct 13 18:21:24 2009 +0200
+++ b/req.py Thu Oct 15 10:31:54 2009 +0200
@@ -108,31 +108,55 @@
# XXX move to CWEntityManager or even better as factory method (unclear
# where yet...)
- def create_entity(self, etype, *args, **kwargs):
+ def create_entity(self, etype, **kwargs):
"""add a new entity of the given type
Example (in a shell session):
- c = create_entity('Company', name='Logilab')
- create_entity('Person', ('works_for', 'Y'), Y=c.eid, firstname='John', lastname='Doe')
+ c = create_entity('Company', name=u'Logilab')
+ create_entity('Person', works_for=c, firstname=u'John', lastname=u'Doe')
+
"""
rql = 'INSERT %s X' % etype
relations = []
- restrictions = []
+ restrictions = set()
cachekey = []
- for rtype, rvar in args:
- relations.append('X %s %s' % (rtype, rvar))
- restrictions.append('%s eid %%(%s)s' % (rvar, rvar))
- cachekey.append(rvar)
- for attr in kwargs:
- if attr in cachekey:
- continue
- relations.append('X %s %%(%s)s' % (attr, attr))
+ pending_relations = []
+ for attr, value in kwargs.iteritems():
+ if isinstance(value, (tuple, list, set, frozenset)):
+ if len(value) == 1:
+ value = iter(value).next()
+ else:
+ pending_relations.append( (attr, value) )
+ continue
+ if hasattr(value, 'eid'): # non final relation
+ rvar = attr.upper()
+ # XXX safer detection of object relation
+ if attr.startswith('reverse_'):
+ relations.append('%s %s X' % (rvar, attr[len('reverse_'):]))
+ else:
+ relations.append('X %s %s' % (attr, rvar))
+ restriction = '%s eid %%(%s)s' % (rvar, attr)
+ if not restriction in restrictions:
+ restrictions.add(restriction)
+ cachekey.append(attr)
+ kwargs[attr] = value.eid
+ else: # attribute
+ relations.append('X %s %%(%s)s' % (attr, attr))
if relations:
rql = '%s: %s' % (rql, ', '.join(relations))
if restrictions:
rql = '%s WHERE %s' % (rql, ', '.join(restrictions))
- return self.execute(rql, kwargs, cachekey).get_entity(0, 0)
+ created = self.execute(rql, kwargs, cachekey).get_entity(0, 0)
+ for attr, values in pending_relations:
+ if attr.startswith('reverse_'):
+ restr = 'Y %s X' % attr[len('reverse_'):]
+ else:
+ restr = 'X %s Y' % attr
+ self.execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % (
+ restr, ','.join(str(r.eid) for r in values)),
+ {'x': created.eid}, 'x')
+ return created
def ensure_ro_rql(self, rql):
"""raise an exception if the given rql is not a select query"""
--- a/selectors.py Tue Oct 13 18:21:24 2009 +0200
+++ b/selectors.py Thu Oct 15 10:31:54 2009 +0200
@@ -43,7 +43,7 @@
__docformat__ = "restructuredtext en"
import logging
-from warnings import warn
+from warnings import warn, filterwarnings
from logilab.common.compat import all
from logilab.common.interface import implements as implements_iface
@@ -64,7 +64,7 @@
def lltrace(selector):
# don't wrap selectors if not in development mode
- if CubicWebConfiguration.mode == 'installed':
+ if CubicWebConfiguration.mode == 'system': # XXX config.debug
return selector
def traced(cls, *args, **kwargs):
# /!\ lltrace decorates pure function or __call__ method, this
--- a/server/serverconfig.py Tue Oct 13 18:21:24 2009 +0200
+++ b/server/serverconfig.py Thu Oct 15 10:31:54 2009 +0200
@@ -15,7 +15,7 @@
from logilab.common.decorators import wproperty, cached, clear_cache
from cubicweb import CW_SOFTWARE_ROOT, RegistryNotFound
-from cubicweb.toolsutils import env_path, read_config, restrict_perms_to_user
+from cubicweb.toolsutils import read_config, restrict_perms_to_user
from cubicweb.cwconfig import CubicWebConfiguration, merge_options
from cubicweb.server import SOURCE_TYPES
@@ -75,12 +75,6 @@
class ServerConfiguration(CubicWebConfiguration):
"""standalone RQL server"""
name = 'repository'
- if os.environ.get('APYCOT_ROOT'):
- root = os.environ['APYCOT_ROOT']
- elif CubicWebConfiguration.mode == 'dev':
- BACKUP_DIR = CubicWebConfiguration.RUNTIME_DIR
- else:
- BACKUP_DIR = '/var/lib/cubicweb/backup/'
cubicweb_appobject_path = CubicWebConfiguration.cubicweb_appobject_path | set(['sobjects', 'hooks'])
cube_appobject_path = CubicWebConfiguration.cube_appobject_path | set(['sobjects', 'hooks'])
--- a/server/sources/__init__.py Tue Oct 13 18:21:24 2009 +0200
+++ b/server/sources/__init__.py Thu Oct 15 10:31:54 2009 +0200
@@ -191,6 +191,9 @@
* if this source doesn't support the relation, can be crossed unless
explicitly specified in .dont_cross_relations
"""
+ # XXX find a way to have relation such as state_of in dont cross
+ # relation (eg composite relation without both end type available?
+ # card 1 relation ? ...)
if self.support_relation(rtype):
return rtype in self.cross_relations
return rtype not in self.dont_cross_relations
--- a/server/test/unittest_msplanner.py Tue Oct 13 18:21:24 2009 +0200
+++ b/server/test/unittest_msplanner.py Thu Oct 15 10:31:54 2009 +0200
@@ -37,7 +37,7 @@
support_entities = {'Card': True, 'Note': True, 'State': True}
support_relations = {'in_state': True, 'multisource_rel': True, 'multisource_inlined_rel': True,
'multisource_crossed_rel': True}
- dont_cross_relations = set(('fiche', 'in_state'))
+ dont_cross_relations = set(('fiche', 'state_of'))
cross_relations = set(('multisource_crossed_rel',))
def syntax_tree_search(self, *args, **kwargs):
@@ -1977,6 +1977,14 @@
None, None, [self.system], {}, [])],
{'x': 999999, 'u': 999998})
+ def test_state_of_cross(self):
+ self._test('DELETE State X WHERE NOT X state_of Y',
+ [('DeleteEntitiesStep',
+ [('OneFetchStep',
+ [('Any X WHERE NOT X state_of Y, X is State, Y is Workflow',
+ [{'X': 'State', 'Y': 'Workflow'}])],
+ None, None, [self.system], {}, [])])]
+ )
class MSPlannerTwoSameExternalSourcesTC(BasePlannerTC):
"""test planner related feature on a 3-sources repository:
--- a/server/test/unittest_rql2sql.py Tue Oct 13 18:21:24 2009 +0200
+++ b/server/test/unittest_rql2sql.py Thu Oct 15 10:31:54 2009 +0200
@@ -567,6 +567,15 @@
FROM cw_EmailAddress AS O
WHERE NOT EXISTS(SELECT 1 FROM use_email_relation AS rel_use_email0 WHERE rel_use_email0.eid_from=1 AND rel_use_email0.eid_to=O.cw_eid) AND EXISTS(SELECT 1 FROM use_email_relation AS rel_use_email1 WHERE rel_use_email1.eid_to=O.cw_eid AND EXISTS(SELECT 1 FROM cw_CWGroup AS D WHERE rel_use_email1.eid_from=2 AND NOT EXISTS(SELECT 1 FROM in_group_relation AS rel_in_group2 WHERE rel_in_group2.eid_from=2 AND rel_in_group2.eid_to=D.cw_eid) AND D.cw_name=guests))
ORDER BY 4 DESC'''),
+
+
+ ("Any X WHERE X eid 0, X eid 0",
+ '''SELECT 0'''),
+
+ ("Any X WHERE X eid 0, X eid 0, X test TRUE",
+ '''SELECT X.cw_eid
+FROM cw_Personne AS X
+WHERE X.cw_eid=0 AND X.cw_eid=0 AND X.cw_test='''),
]
MULTIPLE_SEL = [
--- a/test/unittest_entity.py Tue Oct 13 18:21:24 2009 +0200
+++ b/test/unittest_entity.py Thu Oct 15 10:31:54 2009 +0200
@@ -185,7 +185,19 @@
Personne.fetch_attrs, Personne.fetch_order = fetch_config(('nom', ))
# XXX
self.assertEquals(p.related_rql('evaluee'),
- 'Any X,AA ORDERBY Z DESC WHERE X modification_date Z, E eid %(x)s, E evaluee X, X modification_date AA')
+ 'Any X,AA ORDERBY Z DESC '
+ 'WHERE X modification_date Z, E eid %(x)s, E evaluee X, '
+ 'X modification_date AA')
+
+ tag = self.vreg['etypes'].etype_class('Tag')(self.request())
+ self.assertEquals(tag.related_rql('tags', 'subject'),
+ 'Any X,AA ORDERBY Z DESC '
+ 'WHERE X modification_date Z, E eid %(x)s, E tags X, '
+ 'X modification_date AA')
+ self.assertEquals(tag.related_rql('tags', 'subject', ('Personne',)),
+ 'Any X,AA,AB ORDERBY AA ASC '
+ 'WHERE E eid %(x)s, E tags XE is IN (Personne), X nom AA, '
+ 'X modification_date AB')
def test_unrelated_rql_security_1(self):
user = self.request().user
@@ -460,6 +472,21 @@
self.assertEquals(card.absolute_url(),
'http://testing.fr/cubicweb/card/eid/%s' % card.eid)
+ def test_create_entity(self):
+ p1 = self.add_entity('Personne', nom=u'fayolle', prenom=u'alexandre')
+ p2 = self.add_entity('Personne', nom=u'campeas', prenom=u'aurelien')
+ note = self.add_entity('Note', type=u'z')
+ req = self.request()
+ p = req.create_entity('Personne', nom=u'di mascio', prenom=u'adrien',
+ connait=p1, evaluee=[p1, p2],
+ reverse_ecrit_par=note)
+ self.assertEquals(p.nom, 'di mascio')
+ self.assertEquals([c.nom for c in p.connait], ['fayolle'])
+ self.assertEquals(sorted([c.nom for c in p.evaluee]), ['campeas', 'fayolle'])
+ self.assertEquals([c.type for c in p.reverse_ecrit_par], ['z'])
+
+
+
if __name__ == '__main__':
from logilab.common.testlib import unittest_main
unittest_main()
--- a/vregistry.py Tue Oct 13 18:21:24 2009 +0200
+++ b/vregistry.py Thu Oct 15 10:31:54 2009 +0200
@@ -202,7 +202,7 @@
% (args, kwargs.keys(),
[repr(v) for v in appobjects]))
if len(winners) > 1:
- if self.config.mode == 'installed':
+ if self.config.debugmode:
self.error('select ambiguity, args: %s\nkwargs: %s %s',
args, kwargs.keys(), [repr(v) for v in winners])
else:
--- a/web/views/editcontroller.py Tue Oct 13 18:21:24 2009 +0200
+++ b/web/views/editcontroller.py Thu Oct 15 10:31:54 2009 +0200
@@ -157,7 +157,7 @@
todelete = self._cw.list_form_param('__delete', formparams, pop=True)
self.delete_relations(parse_relations_descr(todelete))
if formparams.has_key('__cloned_eid'):
- entity.copy_relations(formparams['__cloned_eid'])
+ entity.copy_relations(typed_eid(formparams['__cloned_eid']))
if formparams.has_key('__insert'):
toinsert = self._cw.list_form_param('__insert', formparams, pop=True)
self.insert_relations(parse_relations_descr(toinsert))
--- a/web/views/editforms.py Tue Oct 13 18:21:24 2009 +0200
+++ b/web/views/editforms.py Thu Oct 15 10:31:54 2009 +0200
@@ -177,6 +177,7 @@
form = self._build_form(
entity, rtype, role, default, onsubmit, reload)
if not self.should_edit_attribute(entity, rschema, role, form):
+ self.w(entity.printable_value(rtype))
return
value = entity.printable_value(rtype) or default
self.attribute_form(lzone, value, form,
@@ -184,13 +185,15 @@
else:
if rvid is None:
rvid = self._compute_best_vid(entity.e_schema, rschema, role)
- if not self.should_edit_relation(entity, rschema, role, rvid):
- return
rset = entity.related(rtype, role)
if rset:
value = self._cw.view(rvid, rset)
else:
value = default
+ if not self.should_edit_relation(entity, rschema, role, rvid):
+ if rset:
+ self.w(value)
+ return
onsubmit = ("return inlineValidateRelationForm('%(rtype)s', '%(role)s', '%(eid)s', "
"'%(divid)s', %(reload)s, '%(vid)s', '%(default)s', '%(lzone)s');")
form = self._build_form(
--- a/web/webconfig.py Tue Oct 13 18:21:24 2009 +0200
+++ b/web/webconfig.py Thu Oct 15 10:31:54 2009 +0200
@@ -172,7 +172,7 @@
('print-traceback',
{'type' : 'yn',
- 'default': not CubicWebConfiguration.mode == 'installed',
+ 'default': CubicWebConfiguration.mode != 'system',
'help': 'print the traceback on the error page when an error occured',
'group': 'web', 'inputlevel': 2,
}),