# HG changeset patch # User Sylvain Thénault # Date 1466085181 -7200 # Node ID e4f11ef1face47f61d00b4951f84faf3fd216009 # Parent baed516c6f6e5b9b09d618932b84fe9730a9cf8c# Parent 19fcce6dc6d11031e558ffcc28d48a3b82387386 backport 3.22 changes diff -r baed516c6f6e -r e4f11ef1face .hgtags --- a/.hgtags Fri Jun 10 16:45:20 2016 +0200 +++ b/.hgtags Thu Jun 16 15:53:01 2016 +0200 @@ -517,6 +517,9 @@ 8c5dabbcd4d9505c3a617f9dbe2b10172bdc2b3a 3.20.13 8c5dabbcd4d9505c3a617f9dbe2b10172bdc2b3a debian/3.20.13-1 8c5dabbcd4d9505c3a617f9dbe2b10172bdc2b3a centos/3.20.13-1 +f66a4895759e0913b1203943fc2cd7be1a821e05 3.20.14 +f66a4895759e0913b1203943fc2cd7be1a821e05 debian/3.20.14-1 +f66a4895759e0913b1203943fc2cd7be1a821e05 centos/3.20.14-1 887c6eef807781560adcd4ecd2dea9011f5a6681 3.21.0 887c6eef807781560adcd4ecd2dea9011f5a6681 debian/3.21.0-1 887c6eef807781560adcd4ecd2dea9011f5a6681 centos/3.21.0-1 diff -r baed516c6f6e -r e4f11ef1face cubicweb.spec diff -r baed516c6f6e -r e4f11ef1face cubicweb/__init__.py --- a/cubicweb/__init__.py Fri Jun 10 16:45:20 2016 +0200 +++ b/cubicweb/__init__.py Thu Jun 16 15:53:01 2016 +0200 @@ -25,15 +25,21 @@ import sys import warnings import zlib -if (2, 7) <= sys.version_info < (2, 7, 4): + +warnings.filterwarnings('ignore', category=UserWarning, + message='.*was already imported', + module='.*pygments') + + +from six import PY2, binary_type, text_type +from six.moves import builtins + +if PY2: # http://bugs.python.org/issue10211 from StringIO import StringIO as BytesIO else: from io import BytesIO -from six import PY2, binary_type, text_type -from six.moves import builtins, cPickle as pickle - from logilab.common.deprecation import deprecated from logilab.common.logging_ext import set_log_methods from yams.constraints import BASE_CONVERTERS, BASE_CHECKERS diff -r baed516c6f6e -r e4f11ef1face cubicweb/__pkginfo__.py --- a/cubicweb/__pkginfo__.py Fri Jun 10 16:45:20 2016 +0200 +++ b/cubicweb/__pkginfo__.py Thu Jun 16 15:53:01 2016 +0200 @@ -23,7 +23,7 @@ modname = distname = "cubicweb" numversion = (3, 22, 2) -version = '.'.join(str(num) for num in numversion) +version = '.'.join(str(num) for num in numversion) + '.dev0' description = "a repository of entities / relations for knowledge management" author = "Logilab" diff -r baed516c6f6e -r e4f11ef1face cubicweb/cwctl.py --- a/cubicweb/cwctl.py Fri Jun 10 16:45:20 2016 +0200 +++ b/cubicweb/cwctl.py Thu Jun 16 15:53:01 2016 +0200 @@ -983,8 +983,12 @@ # WSGI ######### WSGI_CHOICES = {} -from cubicweb.wsgi import server as stdlib_server -WSGI_CHOICES['stdlib'] = stdlib_server +try: + from cubicweb.wsgi import server as stdlib_server +except ImportError: + pass +else: + WSGI_CHOICES['stdlib'] = stdlib_server try: from cubicweb.wsgi import wz except ImportError: @@ -1002,51 +1006,51 @@ def wsgichoices(): return tuple(WSGI_CHOICES) - -class WSGIStartHandler(InstanceCommand): - """Start an interactive wsgi server """ - name = 'wsgi' - actionverb = 'started' - arguments = '' +if WSGI_CHOICES: + class WSGIStartHandler(InstanceCommand): + """Start an interactive wsgi server """ + name = 'wsgi' + actionverb = 'started' + arguments = '' - @property - def options(self): - return ( - ("debug", - {'short': 'D', 'action': 'store_true', - 'default': False, - 'help': 'start server in debug mode.'}), - ('method', - {'short': 'm', - 'type': 'choice', - 'metavar': '', - 'default': 'stdlib', - 'choices': wsgichoices(), - 'help': 'wsgi utility/method'}), - ('loglevel', - {'short': 'l', - 'type': 'choice', - 'metavar': '', - 'default': None, - 'choices': ('debug', 'info', 'warning', 'error'), - 'help': 'debug if -D is set, error otherwise', - }), - ) + @property + def options(self): + return ( + ("debug", + {'short': 'D', 'action': 'store_true', + 'default': False, + 'help': 'start server in debug mode.'}), + ('method', + {'short': 'm', + 'type': 'choice', + 'metavar': '', + 'default': 'stdlib', + 'choices': wsgichoices(), + 'help': 'wsgi utility/method'}), + ('loglevel', + {'short': 'l', + 'type': 'choice', + 'metavar': '', + 'default': None, + 'choices': ('debug', 'info', 'warning', 'error'), + 'help': 'debug if -D is set, error otherwise', + }), + ) - def wsgi_instance(self, appid): - config = cwcfg.config_for(appid, debugmode=self['debug']) - init_cmdline_log_threshold(config, self['loglevel']) - assert config.name == 'all-in-one' - meth = self['method'] - server = WSGI_CHOICES[meth] - return server.run(config) + def wsgi_instance(self, appid): + config = cwcfg.config_for(appid, debugmode=self['debug']) + init_cmdline_log_threshold(config, self['loglevel']) + assert config.name == 'all-in-one' + meth = self['method'] + server = WSGI_CHOICES[meth] + return server.run(config) + CWCTL.register(WSGIStartHandler) for cmdcls in (ListCommand, CreateInstanceCommand, DeleteInstanceCommand, StartInstanceCommand, StopInstanceCommand, RestartInstanceCommand, - WSGIStartHandler, ReloadConfigurationCommand, StatusCommand, UpgradeInstanceCommand, ListVersionsInstanceCommand, diff -r baed516c6f6e -r e4f11ef1face cubicweb/dataimport/importer.py --- a/cubicweb/dataimport/importer.py Fri Jun 10 16:45:20 2016 +0200 +++ b/cubicweb/dataimport/importer.py Thu Jun 16 15:53:01 2016 +0200 @@ -319,6 +319,7 @@ """ schema = self.schema extid2eid = self.extid2eid + order_hint = list(self.etypes_order_hint) for ext_entity in ext_entities: # check data in the transitional representation and prepare it for # later insertion in the database @@ -328,12 +329,17 @@ queue.setdefault(ext_entity.etype, []).append(ext_entity) continue yield ext_entity + if not queue: + continue # check for some entities in the queue that may now be ready. We'll have to restart # search for ready entities until no one is generated + for etype in queue: + if etype not in order_hint: + order_hint.append(etype) new = True while new: new = False - for etype in self.etypes_order_hint: + for etype in order_hint: if etype in queue: new_queue = [] for ext_entity in queue[etype]: @@ -377,8 +383,8 @@ try: subject_eid = extid2eid[subject_uri] object_eid = extid2eid[object_uri] - except KeyError: - missing_relations.append((subject_uri, rtype, object_uri)) + except KeyError as exc: + missing_relations.append((subject_uri, rtype, object_uri, exc)) continue if (subject_eid, object_eid) not in existing: prepare_insert_relation(subject_eid, rtype, object_eid) @@ -400,8 +406,9 @@ raise Exception('\n'.join(msgs)) if missing_relations: msgs = ["can't create some relations, is there missing data?"] - for subject_uri, rtype, object_uri in missing_relations: - msgs.append("%s %s %s" % (subject_uri, rtype, object_uri)) + for subject_uri, rtype, object_uri, exc in missing_relations: + msgs.append("Could not find %s when trying to insert (%s, %s, %s)" + % (exc, subject_uri, rtype, object_uri)) map(error, msgs) if self.raise_on_error: raise Exception('\n'.join(msgs)) diff -r baed516c6f6e -r e4f11ef1face cubicweb/dataimport/test/unittest_importer.py --- a/cubicweb/dataimport/test/unittest_importer.py Fri Jun 10 16:45:20 2016 +0200 +++ b/cubicweb/dataimport/test/unittest_importer.py Thu Jun 16 15:53:01 2016 +0200 @@ -124,6 +124,19 @@ self.assertEqual(entity.nom, u'Richelieu') self.assertEqual(len(entity.connait), 0) + def test_import_order(self): + """Check import of ext entity in both order""" + with self.admin_access.repo_cnx() as cnx: + importer = self.importer(cnx) + richelieu = ExtEntity('Personne', 3, {'nom': set([u'Richelieu']), + 'enfant': set([4])}) + athos = ExtEntity('Personne', 4, {'nom': set([u'Athos'])}) + importer.import_entities([richelieu, athos]) + cnx.commit() + rset = cnx.execute('Any X WHERE X is Personne, X nom "Richelieu"') + entity = rset.get_entity(0, 0) + self.assertEqual(entity.enfant[0].nom, 'Athos') + def test_update(self): """Check update of ext entity""" with self.admin_access.repo_cnx() as cnx: diff -r baed516c6f6e -r e4f11ef1face cubicweb/devtools/test/requirements.txt diff -r baed516c6f6e -r e4f11ef1face cubicweb/entity.py --- a/cubicweb/entity.py Fri Jun 10 16:45:20 2016 +0200 +++ b/cubicweb/entity.py Thu Jun 16 15:53:01 2016 +0200 @@ -783,7 +783,7 @@ for rschema in self.e_schema.subject_relations(): if rschema.type in skip_copy_for['subject']: continue - if rschema.final or rschema.meta: + if rschema.final or rschema.meta or rschema.rule: continue # skip already defined relations if getattr(self, rschema.type): @@ -802,7 +802,7 @@ execute(rql, {'x': self.eid, 'y': ceid}) self.cw_clear_relation_cache(rschema.type, 'subject') for rschema in self.e_schema.object_relations(): - if rschema.meta: + if rschema.meta or rschema.rule: continue # skip already defined relations if self.related(rschema.type, 'object'): diff -r baed516c6f6e -r e4f11ef1face cubicweb/etwist/test/requirements.txt diff -r baed516c6f6e -r e4f11ef1face cubicweb/misc/migration/3.10.0_Any.py --- a/cubicweb/misc/migration/3.10.0_Any.py Fri Jun 10 16:45:20 2016 +0200 +++ b/cubicweb/misc/migration/3.10.0_Any.py Thu Jun 16 15:53:01 2016 +0200 @@ -1,7 +1,5 @@ from six import text_type -from cubicweb.server.session import hooks_control - for uri, cfg in config.read_sources_file().items(): if uri in ('system', 'admin'): continue @@ -11,7 +9,7 @@ add_relation_definition('CWSource', 'cw_source', 'CWSource') add_entity_type('CWSourceHostConfig') -with hooks_control(session, session.HOOKS_ALLOW_ALL, 'cw.sources'): +with session.allow_all_hooks_but('cw.sources'): create_entity('CWSource', type=u'native', name=u'system') commit() diff -r baed516c6f6e -r e4f11ef1face cubicweb/misc/migration/3.10.9_Any.py --- a/cubicweb/misc/migration/3.10.9_Any.py Fri Jun 10 16:45:20 2016 +0200 +++ b/cubicweb/misc/migration/3.10.9_Any.py Thu Jun 16 15:53:01 2016 +0200 @@ -14,7 +14,7 @@ enabled = interactive_mode with progress(title=title, nbops=nbops, size=30, enabled=enabled) as pb: for i, row in enumerate(rset): - with hooks_control(session, session.HOOKS_DENY_ALL, 'integrity'): + with session.deny_all_hooks_but('integrity'): data = {'eid': row[0], 'cwuri': row[1].replace(u'/eid', u'')} rql('SET X cwuri %(cwuri)s WHERE X eid %(eid)s', data) if not i % 100: # commit every 100 entities to limit memory consumption diff -r baed516c6f6e -r e4f11ef1face cubicweb/server/sources/ldapfeed.py --- a/cubicweb/server/sources/ldapfeed.py Fri Jun 10 16:45:20 2016 +0200 +++ b/cubicweb/server/sources/ldapfeed.py Thu Jun 16 15:53:01 2016 +0200 @@ -284,26 +284,27 @@ else: # user specified, we want to check user/password, no need to return # the connection which will be thrown out - self._authenticate(conn, user, userpwd) + if not self._authenticate(conn, user, userpwd): + raise AuthenticationError() return conn def _auth_simple(self, conn, user, userpwd): conn.authentication = ldap3.AUTH_SIMPLE conn.user = user['dn'] conn.password = userpwd - conn.bind() + return conn.bind() def _auth_digest_md5(self, conn, user, userpwd): conn.authentication = ldap3.AUTH_SASL conn.sasl_mechanism = 'DIGEST-MD5' # realm, user, password, authz-id conn.sasl_credentials = (None, user['dn'], userpwd, None) - conn.bind() + return conn.bind() def _auth_gssapi(self, conn, user, userpwd): conn.authentication = ldap3.AUTH_SASL conn.sasl_mechanism = 'GSSAPI' - conn.bind() + return conn.bind() def _search(self, cnx, base, scope, searchstr='(objectClass=*)', attrs=()): diff -r baed516c6f6e -r e4f11ef1face cubicweb/server/sources/native.py diff -r baed516c6f6e -r e4f11ef1face cubicweb/server/test/unittest_ldapsource.py --- a/cubicweb/server/test/unittest_ldapsource.py Fri Jun 10 16:45:20 2016 +0200 +++ b/cubicweb/server/test/unittest_ldapsource.py Thu Jun 16 15:53:01 2016 +0200 @@ -246,6 +246,8 @@ # ensure we won't be logged against self.assertRaises(AuthenticationError, source.authenticate, cnx, 'toto', 'toto') + self.assertRaises(AuthenticationError, + source.authenticate, cnx, 'syt', 'toto') self.assertTrue(source.authenticate(cnx, 'syt', 'syt')) session = self.repo.new_session('syt', password='syt') self.assertTrue(session) diff -r baed516c6f6e -r e4f11ef1face cubicweb/test/data/schema.py --- a/cubicweb/test/data/schema.py Fri Jun 10 16:45:20 2016 +0200 +++ b/cubicweb/test/data/schema.py Thu Jun 16 15:53:01 2016 +0200 @@ -17,7 +17,7 @@ # with CubicWeb. If not, see . from yams.buildobjs import (EntityType, String, RichString, Bytes, - SubjectRelation, RelationDefinition) + ComputedRelation, SubjectRelation, RelationDefinition) from cubicweb.schema import (WorkflowableEntityType, RQLConstraint, RQLVocabularyConstraint) @@ -26,6 +26,10 @@ from cubicweb import _ +class buddies(ComputedRelation): + rule = 'S in_group G, O in_group G' + + class Personne(EntityType): nom = String(required=True) prenom = String() diff -r baed516c6f6e -r e4f11ef1face cubicweb/test/unittest_binary.py --- a/cubicweb/test/unittest_binary.py Fri Jun 10 16:45:20 2016 +0200 +++ b/cubicweb/test/unittest_binary.py Thu Jun 16 15:53:01 2016 +0200 @@ -1,10 +1,29 @@ -from six import PY2 +# copyright 2016 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of CubicWeb. +# +# CubicWeb is free software: you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) +# any later version. +# +# CubicWeb is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with CubicWeb. If not, see . from unittest import TestCase -from tempfile import NamedTemporaryFile import os.path as osp +import pickle + +from six import PY2 from logilab.common.shellutils import tempdir + from cubicweb import Binary @@ -48,6 +67,11 @@ bobj = Binary.from_file(fpath) self.assertEqual(bobj.getvalue(), b'binaryblob') + def test_pickleable(self): + b = Binary(b'toto') + bb = pickle.loads(pickle.dumps(b)) + self.assertEqual(b, bb) + if __name__ == '__main__': from unittest import main diff -r baed516c6f6e -r e4f11ef1face cubicweb/test/unittest_entity.py --- a/cubicweb/test/unittest_entity.py Fri Jun 10 16:45:20 2016 +0200 +++ b/cubicweb/test/unittest_entity.py Thu Jun 16 15:53:01 2016 +0200 @@ -138,6 +138,22 @@ e.cw_clear_relation_cache('in_state', 'subject') self.assertEqual(e.cw_adapt_to('IWorkflowable').state, 'activated') + def test_copy_exclude_computed_relations(self): + """The `CWUser buddies CWUser` (computed) relation should not be copied. + """ + with self.admin_access.cnx() as cnx: + friends = cnx.create_entity('CWGroup', name=u'friends') + bob = self.create_user(cnx, u'bob', groups=('friends',)) + cnx.create_entity('EmailAddress', address=u'bob@cubicweb.org', + reverse_use_email=bob) + alice = self.create_user(cnx, u'alices', groups=('friends',)) + cnx.commit() + charles = self.create_user(cnx, u'charles') + cnx.commit() + # Just ensure this does not crash (it would if computed relation + # attempted to be copied). + charles.copy_relations(bob.eid) + def test_related_cache_both(self): with self.admin_access.web_request() as req: user = req.execute('Any X WHERE X eid %(x)s', {'x':req.user.eid}).get_entity(0, 0) diff -r baed516c6f6e -r e4f11ef1face cubicweb/test/unittest_schema.py --- a/cubicweb/test/unittest_schema.py Fri Jun 10 16:45:20 2016 +0200 +++ b/cubicweb/test/unittest_schema.py Thu Jun 16 15:53:01 2016 +0200 @@ -178,7 +178,7 @@ self.assertListEqual(sorted(expected_entities), entities) relations = sorted([str(r) for r in schema.relations()]) expected_relations = ['actionnaire', 'add_permission', 'address', 'alias', 'allowed_transition', 'associe', - 'bookmarked_by', 'by_transition', + 'bookmarked_by', 'by_transition', 'buddies', 'cardinality', 'comment', 'comment_format', 'composite', 'condition', 'config', 'connait', @@ -225,7 +225,7 @@ eschema = schema.eschema('CWUser') rels = sorted(str(r) for r in eschema.subject_relations()) - self.assertListEqual(rels, ['created_by', 'creation_date', 'custom_workflow', + self.assertListEqual(rels, ['buddies', 'created_by', 'creation_date', 'custom_workflow', 'cw_source', 'cwuri', 'eid', 'evaluee', 'firstname', 'has_group_permission', 'has_text', 'identity', @@ -235,7 +235,7 @@ 'primary_email', 'surname', 'upassword', 'use_email']) rels = sorted(r.type for r in eschema.object_relations()) - self.assertListEqual(rels, ['bookmarked_by', 'created_by', 'for_user', + self.assertListEqual(rels, ['bookmarked_by', 'buddies', 'created_by', 'for_user', 'identity', 'owned_by', 'wf_info_for']) rschema = schema.rschema('relation_type') properties = rschema.rdef('CWAttribute', 'CWRType') diff -r baed516c6f6e -r e4f11ef1face cubicweb/web/request.py diff -r baed516c6f6e -r e4f11ef1face cubicweb/web/test/requirements.txt diff -r baed516c6f6e -r e4f11ef1face cubicweb/web/test/unittest_http.py diff -r baed516c6f6e -r e4f11ef1face debian/changelog --- a/debian/changelog Fri Jun 10 16:45:20 2016 +0200 +++ b/debian/changelog Thu Jun 16 15:53:01 2016 +0200 @@ -64,6 +64,12 @@ -- Julien Cristau Fri, 10 Jul 2015 17:04:11 +0200 +cubicweb (3.20.14-1) unstable; urgency=medium + + * new upstream release + + -- Julien Cristau Mon, 21 Mar 2016 17:59:22 +0100 + cubicweb (3.20.13-1) unstable; urgency=medium * new upstream release diff -r baed516c6f6e -r e4f11ef1face debian/control