# HG changeset patch # User Denis Laxalde # Date 1524489835 -7200 # Node ID e08d8e1712380c6fb0e7657b870d2d409b33f2df # Parent 038ff1a7259fd6686ceca6a1792688ad6a68cf5c# Parent e36f2c862d5c26ab0376da7e2757cf14a5113222 Merge 3.26 diff -r 038ff1a7259f -r e08d8e171238 .hgtags --- a/.hgtags Mon Apr 23 13:50:50 2018 +0200 +++ b/.hgtags Mon Apr 23 15:23:55 2018 +0200 @@ -614,3 +614,14 @@ b8567725c473b701fe9352e578ad6e05c523c1f2 3.25.4 b8567725c473b701fe9352e578ad6e05c523c1f2 centos/3.25.4-1 b8567725c473b701fe9352e578ad6e05c523c1f2 debian/3.25.4-1 +199851fcddd4b45e3d7f40efcd1739134c33db2a 3.26.0 +199851fcddd4b45e3d7f40efcd1739134c33db2a debian/3.26.0-1 +199851fcddd4b45e3d7f40efcd1739134c33db2a centos/3.26.0-1 +027676243aaa6895492ecd31918f12d221fd503d 3.26.1 +027676243aaa6895492ecd31918f12d221fd503d debian/3.26.1-1 +027676243aaa6895492ecd31918f12d221fd503d centos/3.26.1-1 +9bee3134d304f6cc51ab728b4ca84d464d1b3fd8 3.26.2 +9bee3134d304f6cc51ab728b4ca84d464d1b3fd8 centos/3.26.2-1 +9bee3134d304f6cc51ab728b4ca84d464d1b3fd8 debian/3.26.2-1 +f7067be5f69cd05f34ce99fbb534e4674b3a782d 3.26.3 +f7067be5f69cd05f34ce99fbb534e4674b3a782d debian/3.26.3-1 diff -r 038ff1a7259f -r e08d8e171238 MANIFEST.in --- a/MANIFEST.in Mon Apr 23 13:50:50 2018 +0200 +++ b/MANIFEST.in Mon Apr 23 15:23:55 2018 +0200 @@ -1,5 +1,4 @@ include README -include README.pyramid.rst include COPYING include COPYING.LESSER include pylintrc @@ -31,7 +30,7 @@ recursive-include cubicweb/misc *.py *.png *.display include cubicweb/web/views/*.pt -recursive-include cubicweb/web/data external_resources *.js *.css *.py *.png *.gif *.ico *.ttf *.svg *.woff *.eot +recursive-include cubicweb/web/data *.js *.css *.py *.png *.gif *.ico *.ttf *.svg *.woff *.eot recursive-include cubicweb/web/wdoc *.rst *.png *.xml recursive-include cubicweb/devtools/data *.js *.css *.sh diff -r 038ff1a7259f -r e08d8e171238 README --- a/README Mon Apr 23 13:50:50 2018 +0200 +++ b/README Mon Apr 23 15:23:55 2018 +0200 @@ -14,7 +14,7 @@ Install ------- -More details at https://cubicweb.readthedocs.io/en/3.25/book/admin/setup +More details at https://cubicweb.readthedocs.io/en/3.26/book/admin/setup Getting started --------------- @@ -26,12 +26,12 @@ cubicweb-ctl start -D myblog sensible-browser http://localhost:8080/ -Details at https://cubicweb.readthedocs.io/en/3.25/tutorials/base/blog-in-five-minutes +Details at https://cubicweb.readthedocs.io/en/3.26/tutorials/base/blog-in-five-minutes Documentation ------------- -Look in the doc/ subdirectory or read https://cubicweb.readthedocs.io/en/3.25/ +Look in the doc/ subdirectory or read https://cubicweb.readthedocs.io/en/3.26/ CubicWeb includes the Entypo pictograms by Daniel Bruce — http://www.entypo.com diff -r 038ff1a7259f -r e08d8e171238 cubicweb.spec --- a/cubicweb.spec Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb.spec Mon Apr 23 15:23:55 2018 +0200 @@ -8,7 +8,7 @@ %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} Name: cubicweb -Version: 3.25.4 +Version: 3.26.3 Release: logilab.1%{?dist} Summary: CubicWeb is a semantic web application framework Source0: https://pypi.python.org/packages/source/c/cubicweb/cubicweb-%{version}.tar.gz diff -r 038ff1a7259f -r e08d8e171238 cubicweb/__pkginfo__.py --- a/cubicweb/__pkginfo__.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/__pkginfo__.py Mon Apr 23 15:23:55 2018 +0200 @@ -19,11 +19,6 @@ """cubicweb global packaging information for the cubicweb knowledge management software """ -import sys -from os import listdir -from os.path import join, isdir -import glob - modname = distname = "cubicweb" @@ -43,54 +38,8 @@ 'Programming Language :: JavaScript', ] -_server_migration_dir = join(modname, 'misc', 'migration') -_data_dir = join(modname, 'web', 'data') -_wdoc_dir = join(modname, 'web', 'wdoc') -_wdocimages_dir = join(_wdoc_dir, 'images') -_views_dir = join(modname, 'web', 'views') -_i18n_dir = join(modname, 'i18n') - -_pyversion = '.'.join(str(num) for num in sys.version_info[0:2]) -if '--home' in sys.argv: - # --home install - pydir = 'python' + _pyversion -else: - pydir = join('python' + _pyversion, 'site-packages') - # data files that shall be copied into the main package directory package_data = { 'cubicweb.web.views': ['*.pt'], 'cubicweb.pyramid': ['development.ini.tmpl'], } - -try: - # data files that shall be copied outside the main package directory - data_files = [ - # server data - [join('share', 'cubicweb', 'migration'), - [join(_server_migration_dir, filename) - for filename in listdir(_server_migration_dir)]], - # web data - [join('share', 'cubicweb', 'cubes', 'shared', 'data'), - [join(_data_dir, fname) for fname in listdir(_data_dir) - if not isdir(join(_data_dir, fname))]], - [join('share', 'cubicweb', 'cubes', 'shared', 'data', 'images'), - [join(_data_dir, 'images', fname) for fname in listdir(join(_data_dir, 'images'))]], - [join('share', 'cubicweb', 'cubes', 'shared', 'data', 'jquery-treeview'), - [join(_data_dir, 'jquery-treeview', fname) for fname in listdir(join(_data_dir, 'jquery-treeview')) - if not isdir(join(_data_dir, 'jquery-treeview', fname))]], - [join('share', 'cubicweb', 'cubes', 'shared', 'data', 'jquery-treeview', 'images'), - [join(_data_dir, 'jquery-treeview', 'images', fname) - for fname in listdir(join(_data_dir, 'jquery-treeview', 'images'))]], - [join('share', 'cubicweb', 'cubes', 'shared', 'wdoc'), - [join(_wdoc_dir, fname) for fname in listdir(_wdoc_dir) - if not isdir(join(_wdoc_dir, fname))]], - [join('share', 'cubicweb', 'cubes', 'shared', 'wdoc', 'images'), - [join(_wdocimages_dir, fname) for fname in listdir(_wdocimages_dir)]], - [join('share', 'cubicweb', 'cubes', 'shared', 'i18n'), - glob.glob(join(_i18n_dir, '*.po'))], - # skeleton - ] -except OSError: - # we are in an installed directory, don't care about this - pass diff -r 038ff1a7259f -r e08d8e171238 cubicweb/cwconfig.py --- a/cubicweb/cwconfig.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/cwconfig.py Mon Apr 23 15:23:55 2018 +0200 @@ -240,36 +240,6 @@ return modes[0] -def _find_prefix(start_path=None): - """Return the prefix path of CubicWeb installation. - - Walk parent directories of `start_path` looking for one containing a - 'share/cubicweb' directory. The first matching directory is assumed as the - prefix installation of CubicWeb. - - If run from within a virtualenv, the virtualenv root is used as - `start_path`. Otherwise, `start_path` defaults to cubicweb package - directory path. - """ - if start_path is None: - try: - prefix = os.environ['VIRTUAL_ENV'] - except KeyError: - prefix = CW_SOFTWARE_ROOT - else: - prefix = start_path - if not isdir(prefix): - prefix = dirname(prefix) - old_prefix = None - while (not isdir(join(prefix, 'share', 'cubicweb')) - or prefix.endswith('.egg')): - if prefix == old_prefix: - return sys.prefix - old_prefix = prefix - prefix = dirname(prefix) - return prefix - - def _cube_pkgname(cube): if not cube.startswith('cubicweb_'): return 'cubicweb_' + cube @@ -391,18 +361,8 @@ 'float' : 'Float', } -_forced_mode = os.environ.get('CW_MODE') -assert _forced_mode in (None, 'system', 'user') -# CWDEV tells whether directories such as i18n/, web/data/, etc. (ie containing -# some other resources than python libraries) are located with the python code -# or as a 'shared' cube -CWDEV = exists(join(CW_SOFTWARE_ROOT, 'i18n')) - -try: - _INSTALL_PREFIX = os.environ['CW_INSTALL_PREFIX'] -except KeyError: - _INSTALL_PREFIX = _find_prefix() +_INSTALL_PREFIX = os.environ.get('CW_INSTALL_PREFIX', sys.prefix) _USR_INSTALL = _INSTALL_PREFIX == '/usr' class CubicWebNoAppConfiguration(ConfigurationMixIn): @@ -420,15 +380,13 @@ quick_start = False if 'VIRTUAL_ENV' in os.environ: - mode = _forced_mode or 'user' - _CUBES_DIR = join(_INSTALL_PREFIX, 'share', 'cubicweb', 'cubes') - elif CWDEV and _forced_mode != 'system': - mode = 'user' - _CUBES_DIR = join(CW_SOFTWARE_ROOT, '../../cubes') + mode = os.environ.get('CW_MODE', 'user') else: - mode = _forced_mode or 'system' - _CUBES_DIR = join(_INSTALL_PREFIX, 'share', 'cubicweb', 'cubes') + mode = os.environ.get('CW_MODE', 'system') + assert mode in ('system', 'user'), '"CW_MODE" should be either "user" or "system"' + _CUBES_DIR = join(_INSTALL_PREFIX, 'share', 'cubicweb', 'cubes') + assert _CUBES_DIR # XXX only meaningful if CW_CUBES_DIR is not set CUBES_DIR = realpath(abspath(os.environ.get('CW_CUBES_DIR', _CUBES_DIR))) CUBES_PATH = os.environ.get('CW_CUBES_PATH', '').split(os.pathsep) @@ -493,20 +451,9 @@ return Configuration(options=PERSISTENT_OPTIONS) @classmethod - def shared_dir(cls): - """return the shared data directory (i.e. directory where standard - library views and data may be found) - """ - 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 CWDEV: - return join(CW_SOFTWARE_ROOT, 'i18n') - return join(cls.shared_dir(), 'i18n') + return join(dirname(__file__), 'i18n') @classmethod def cw_languages(cls): @@ -1031,11 +978,8 @@ @classmethod def migration_scripts_dir(cls): """cubicweb migration scripts directory""" - if CWDEV: - return join(CW_SOFTWARE_ROOT, 'misc', 'migration') - mdir = join(_INSTALL_PREFIX, 'share', 'cubicweb', 'migration') - if not exists(mdir): - raise ConfigurationError('migration path %s doesn\'t exist' % mdir) + mdir = join(dirname(__file__), 'misc', 'migration') + assert exists(mdir), 'migration path %s does not exist' % mdir return mdir @classmethod diff -r 038ff1a7259f -r e08d8e171238 cubicweb/cwctl.py --- a/cubicweb/cwctl.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/cwctl.py Mon Apr 23 15:23:55 2018 +0200 @@ -44,7 +44,7 @@ from logilab.common.decorators import clear_cache from cubicweb import ConfigurationError, ExecutionError, BadCommandUsage -from cubicweb.cwconfig import CubicWebConfiguration as cwcfg, CWDEV, CONFIGURATIONS +from cubicweb.cwconfig import CubicWebConfiguration as cwcfg, CONFIGURATIONS from cubicweb.toolsutils import Command, rm, create_dir, underline_title from cubicweb.__pkginfo__ import version @@ -760,7 +760,7 @@ if cubicwebversion > applcubicwebversion: toupgrade.append(('cubicweb', applcubicwebversion, cubicwebversion)) # only stop once we're sure we have something to do - if instance_running and not (CWDEV or self.config.nostartstop): + if instance_running and not self.config.nostartstop: StopInstanceCommand(self.logger).stop_instance(appid) # run cubicweb/componants migration scripts if self.config.fs_only or toupgrade: @@ -783,7 +783,7 @@ if helper: helper.postupgrade(repo) print('-> instance migrated.') - if instance_running and not (CWDEV or self.config.nostartstop): + if instance_running and not self.config.nostartstop: # restart instance through fork to get a proper environment, avoid # uicfg pb (and probably gettext catalogs, to check...) forkcmd = '%s start %s' % (sys.argv[0], appid) diff -r 038ff1a7259f -r e08d8e171238 cubicweb/pyramid/session.py --- a/cubicweb/pyramid/session.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/pyramid/session.py Mon Apr 23 15:23:55 2018 +0200 @@ -91,7 +91,10 @@ from pyramid.compat import pickle from pyramid.session import SignedCookieSessionFactory -from cubicweb import Binary +from cubicweb import ( + Binary, + UnknownEid, +) log = logging.getLogger(__name__) @@ -228,8 +231,16 @@ 'CWSession', cwsessiondata=data) sessioneid = session.eid else: - session = cnx.entity_from_eid(sessioneid) - session.cw_set(cwsessiondata=data) + try: + session = cnx.entity_from_eid(sessioneid) + except UnknownEid: + # Might occur if CWSession entity got dropped (e.g. + # the whole db got recreated) while user's cookie is + # still valid. We recreate the CWSession in this case. + sessioneid = cnx.create_entity( + 'CWSession', cwsessiondata=data).eid + else: + session.cw_set(cwsessiondata=data) cnx.commit() # Only if needed actually set the cookie diff -r 038ff1a7259f -r e08d8e171238 cubicweb/server/sources/native.py --- a/cubicweb/server/sources/native.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/server/sources/native.py Mon Apr 23 15:23:55 2018 +0200 @@ -1261,6 +1261,8 @@ def fti_unindex_entities(self, cnx, entities): """remove text content for entities from the full text index """ + if not cnx.repo.system_source.do_fti: + return cursor = cnx.cnxset.cu cursor_unindex_object = self.dbhelper.cursor_unindex_object try: @@ -1272,6 +1274,8 @@ def fti_index_entities(self, cnx, entities): """add text content of created/modified entities to the full text index """ + if not cnx.repo.system_source.do_fti: + return cursor_index_object = self.dbhelper.cursor_index_object cursor = cnx.cnxset.cu try: @@ -1296,6 +1300,8 @@ def precommit_event(self): cnx = self.cnx source = cnx.repo.system_source + if not source.do_fti: + return pendingeids = cnx.transaction_data.get('pendingeids', ()) done = cnx.transaction_data.setdefault('indexedeids', set()) to_reindex = set() diff -r 038ff1a7259f -r e08d8e171238 cubicweb/server/sqlutils.py --- a/cubicweb/server/sqlutils.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/server/sqlutils.py Mon Apr 23 15:23:55 2018 +0200 @@ -19,6 +19,7 @@ from __future__ import print_function +import os import sys import re import subprocess @@ -48,13 +49,16 @@ SQL_PREFIX = 'cw_' -def _run_command(cmd): +def _run_command(cmd, extra_env=None): + env = os.environ.copy() + for key, value in (extra_env or {}).items(): + env.setdefault(key, value) if isinstance(cmd, string_types): print(cmd) - return subprocess.call(cmd, shell=True) + return subprocess.call(cmd, shell=True, env=env) else: print(' '.join(cmd)) - return subprocess.call(cmd) + return subprocess.call(cmd, env=env) def sqlexec(sqlstmts, cursor_or_execute, withpb=True, @@ -342,18 +346,25 @@ """open and return a connection to the database""" return self.dbhelper.get_connection() + def _backup_restore_env(self): + if (self.config['db-driver'] == 'postgres' + and self.config['db-password'] is not None): + return {'PGPASSWORD': self.config['db-password']} + def backup_to_file(self, backupfile, confirm): + extra_env = self._backup_restore_env() for cmd in self.dbhelper.backup_commands(backupfile, keepownership=False): - if _run_command(cmd): + if _run_command(cmd, extra_env=extra_env): if not confirm(' [Failed] Continue anyway?', default='n'): raise Exception('Failed command: %s' % cmd) def restore_from_file(self, backupfile, confirm, drop=True): + extra_env = self._backup_restore_env() for cmd in self.dbhelper.restore_commands(backupfile, keepownership=False, drop=drop): - if _run_command(cmd): + if _run_command(cmd, extra_env=extra_env): if not confirm(' [Failed] Continue anyway?', default='n'): raise Exception('Failed command: %s' % cmd) diff -r 038ff1a7259f -r e08d8e171238 cubicweb/server/utils.py --- a/cubicweb/server/utils.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/server/utils.py Mon Apr 23 15:23:55 2018 +0200 @@ -26,7 +26,7 @@ from threading import Thread from getpass import getpass -from six import PY2 +from six import PY2, text_type from six.moves import input from passlib.utils import handlers as uh, to_hash_str @@ -106,7 +106,7 @@ while not user: user = input('login: ') if PY2: - user = unicode(user, sys.stdin.encoding) + user = text_type(user, sys.stdin.encoding) passwd = getpass('%s: ' % passwdmsg) if confirm: while True: diff -r 038ff1a7259f -r e08d8e171238 cubicweb/statsd_logger.py --- a/cubicweb/statsd_logger.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/statsd_logger.py Mon Apr 23 15:23:55 2018 +0200 @@ -47,7 +47,7 @@ There is also a decorator (``statsd_timeit``) that may be used to measure and send to the statsd_ server the time passed in a function or a method and the number of calls. It will send a message like:: - + .:|ms\n.:1|c\n @@ -56,7 +56,6 @@ """ - import time import socket @@ -112,10 +111,11 @@ @property def __doc__(self): return self.callable.__doc__ + @property def __name__(self): return self.callable.__name__ - + def __call__(self, *args, **kw): if _address is None: return self.callable(*args, **kw) @@ -123,13 +123,14 @@ try: return self.callable(*args, **kw) finally: - dt = 1000*(time.time()-t0) - msg = '{0}.{1}:{2:.4f}|ms\n{0}.{1}:1|c\n'.format(_bucket, self.__name__, dt) + dt = 1000 * (time.time() - t0) + msg = '{0}.{1}:{2:.4f}|ms\n{0}.{1}:1|c\n'.format( + _bucket, self.__name__, dt) _socket.sendto(msg, _address) - + def __get__(self, obj, objtype): """Support instance methods.""" - if obj is None: # class method or some already wrapped method + if obj is None: # class method or some already wrapped method return self import functools return functools.partial(self.__call__, obj) diff -r 038ff1a7259f -r e08d8e171238 cubicweb/test/unittest_cwconfig.py --- a/cubicweb/test/unittest_cwconfig.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/test/unittest_cwconfig.py Mon Apr 23 15:23:55 2018 +0200 @@ -35,17 +35,7 @@ from cubicweb.devtools import ApptestConfiguration from cubicweb.devtools.testlib import BaseTestCase, TemporaryDirectory from cubicweb.cwconfig import ( - CubicWebConfiguration, _find_prefix, _expand_modname) - - -def unabsolutize(path): - parts = path.split(os.sep) - for i, part in reversed(tuple(enumerate(parts))): - if part.startswith('cubicweb_'): - return os.sep.join([part[len('cubicweb_'):]] + parts[i + 1:]) - if part.startswith('cubicweb') or part == 'legacy_cubes': - return os.sep.join(parts[i + 1:]) - raise Exception('duh? %s' % path) + CubicWebConfiguration, _expand_modname) def templibdir(func): @@ -125,6 +115,12 @@ ApptestConfiguration.CUBES_PATH = [] cleanup_sys_modules([self.datapath('libpython')]) + def test_migration_scripts_dir(self): + mscripts = os.listdir(self.config.migration_scripts_dir()) + self.assertIn('bootstrapmigration_repository.py', mscripts) + self.assertIn('postcreate.py', mscripts) + self.assertIn('3.24.0_Any.py', mscripts) + @patch('pkg_resources.iter_entry_points', side_effect=iter_entry_points) def test_available_cubes(self, mock_iter_entry_points): expected_cubes = [ @@ -289,101 +285,6 @@ self.assertNotIn('cubicweb_mycube.ccplugin', sys.modules, sorted(sys.modules)) -class FindPrefixTC(unittest.TestCase): - - def make_dirs(self, basedir, *args): - path = join(basedir, *args) - if not os.path.exists(path): - os.makedirs(path) - return path - - def make_file(self, basedir, *args): - self.make_dirs(basedir, *args[:-1]) - file_path = join(basedir, *args) - with open(file_path, 'w') as f: - f.write('""" None """') - return file_path - - def test_samedir(self): - with TemporaryDirectory() as prefix: - self.make_dirs(prefix, 'share', 'cubicweb') - self.assertEqual(_find_prefix(prefix), prefix) - - def test_samedir_filepath(self): - with TemporaryDirectory() as prefix: - self.make_dirs(prefix, 'share', 'cubicweb') - file_path = self.make_file(prefix, 'bob.py') - self.assertEqual(_find_prefix(file_path), prefix) - - def test_dir_inside_prefix(self): - with TemporaryDirectory() as prefix: - self.make_dirs(prefix, 'share', 'cubicweb') - dir_path = self.make_dirs(prefix, 'bob') - self.assertEqual(_find_prefix(dir_path), prefix) - - def test_file_in_dir_inside_prefix(self): - with TemporaryDirectory() as prefix: - self.make_dirs(prefix, 'share', 'cubicweb') - file_path = self.make_file(prefix, 'bob', 'toto.py') - self.assertEqual(_find_prefix(file_path), prefix) - - def test_file_in_deeper_dir_inside_prefix(self): - with TemporaryDirectory() as prefix: - self.make_dirs(prefix, 'share', 'cubicweb') - file_path = self.make_file(prefix, 'bob', 'pyves', 'alain', - 'adim', 'syt', 'toto.py') - self.assertEqual(_find_prefix(file_path), prefix) - - def test_multiple_candidate_prefix(self): - with TemporaryDirectory() as tempdir: - self.make_dirs(tempdir, 'share', 'cubicweb') - prefix = self.make_dirs(tempdir, 'bob') - self.make_dirs(prefix, 'share', 'cubicweb') - file_path = self.make_file(prefix, 'pyves', 'alain', - 'adim', 'syt', 'toto.py') - self.assertEqual(_find_prefix(file_path), prefix) - - def test_sister_candidate_prefix(self): - with TemporaryDirectory() as prefix: - self.make_dirs(prefix, 'share', 'cubicweb') - self.make_dirs(prefix, 'bob', 'share', 'cubicweb') - file_path = self.make_file(prefix, 'bell', 'toto.py') - self.assertEqual(_find_prefix(file_path), prefix) - - def test_multiple_parent_candidate_prefix(self): - with TemporaryDirectory() as tempdir: - self.make_dirs(tempdir, 'share', 'cubicweb') - prefix = self.make_dirs(tempdir, 'share', 'cubicweb', 'bob') - self.make_dirs(tempdir, 'share', 'cubicweb', 'bob', 'share', - 'cubicweb') - file_path = self.make_file(tempdir, 'share', 'cubicweb', 'bob', - 'pyves', 'alain', 'adim', 'syt', - 'toto.py') - self.assertEqual(_find_prefix(file_path), prefix) - - def test_upper_candidate_prefix(self): - with TemporaryDirectory() as prefix: - self.make_dirs(prefix, 'share', 'cubicweb') - self.make_dirs(prefix, 'bell', 'bob', 'share', 'cubicweb') - file_path = self.make_file(prefix, 'bell', 'toto.py') - self.assertEqual(_find_prefix(file_path), prefix) - - def test_no_prefix(self): - with TemporaryDirectory() as prefix: - self.assertEqual(_find_prefix(prefix), sys.prefix) - - def test_virtualenv(self): - venv = os.environ.get('VIRTUAL_ENV') - try: - with TemporaryDirectory() as prefix: - os.environ['VIRTUAL_ENV'] = prefix - self.make_dirs(prefix, 'share', 'cubicweb') - self.assertEqual(_find_prefix(), prefix) - finally: - if venv: - os.environ['VIRTUAL_ENV'] = venv - - class ModnamesTC(unittest.TestCase): @templibdir diff -r 038ff1a7259f -r e08d8e171238 cubicweb/web/propertysheet.py --- a/cubicweb/web/propertysheet.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/web/propertysheet.py Mon Apr 23 15:23:55 2018 +0200 @@ -109,6 +109,12 @@ with os.fdopen(tmpfd, 'w') as stream: stream.write(content) try: + mode = os.stat(sourcefile).st_mode + os.chmod(tmpfile, mode) + except IOError: + self.warning('Cannot set access mode for %s; you may encouter ' + 'file permissions issues', cachefile) + try: os.rename(tmpfile, cachefile) except OSError as err: if err.errno != errno.EEXIST: diff -r 038ff1a7259f -r e08d8e171238 cubicweb/web/test/unittest_propertysheet.py --- a/cubicweb/web/test/unittest_propertysheet.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/web/test/unittest_propertysheet.py Mon Apr 23 15:23:55 2018 +0200 @@ -40,13 +40,13 @@ self.assertEqual(ps['fontcolor'], 'black') # defined by sheet1, extended by sheet2 self.assertEqual(ps['stylesheets'], ['http://cwtest.com/cubicweb.css', - 'http://cwtest.com/mycube.css']) + 'http://cwtest.com/mycube.css']) # lazy string defined by sheet1 self.assertIsInstance(ps['lazy'], lazystr) self.assertEqual(str(ps['lazy']), '#FFFFFF') # test compilation self.assertEqual(ps.compile('a {bgcolor: %(bgcolor)s; size: 1%;}'), - 'a {bgcolor: #FFFFFF; size: 1%;}') + 'a {bgcolor: #FFFFFF; size: 1%;}') self.assertEqual(ps.process_resource(DATADIR, 'pouet.css'), self.cachedir) self.assertFalse(ps.need_reload()) @@ -54,10 +54,17 @@ self.assertTrue(ps.need_reload()) ps.reload() self.assertFalse(ps.need_reload()) - ps.process_resource(DATADIR, 'pouet.css') # put in cache + ps.process_resource(DATADIR, 'pouet.css') # put in cache os.utime(self.data('pouet.css'), None) self.assertFalse(ps.need_reload()) + def test_chmod(self): + ps = PropertySheet(self.cachedir, datadir_url='http://cwtest.com') + ps.load(self.data('sheet1.py')) + rdir = ps.process_resource(DATADIR, 'pouet.css') + mode = os.stat(join(rdir, 'pouet.css')).st_mode + self.assertEqual(('%o' % mode)[-4:], '0644') + if __name__ == '__main__': main() diff -r 038ff1a7259f -r e08d8e171238 cubicweb/web/test/unittest_webconfig.py --- a/cubicweb/web/test/unittest_webconfig.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/web/test/unittest_webconfig.py Mon Apr 23 15:23:55 2018 +0200 @@ -19,6 +19,7 @@ """cubicweb.web.webconfig unit tests""" import os +from os import path from unittest import TestCase from cubicweb.devtools import ApptestConfiguration, fake @@ -52,6 +53,18 @@ 'neither "web" nor "shared" found in cubicwebcsspath (%s)' % cubicwebcsspath) + def test_locate_all_files(self): + wdocfiles = list(self.config.locate_all_files('toc.xml')) + for fpath in wdocfiles: + self.assertTrue(path.exists(fpath), fpath) + for expected in [path.join('cubes', 'file', 'wdoc', 'toc.xml'), + path.join('cubicweb', 'web', 'wdoc', 'toc.xml')]: + for fpath in wdocfiles: + if fpath.endswith(expected): + break + else: + raise AssertionError('%s not found in %s' % (expected, wdocfiles)) + def test_sign_text(self): signature = self.config.sign_text(u'hôp') self.assertTrue(self.config.check_text_sign(u'hôp', signature)) diff -r 038ff1a7259f -r e08d8e171238 cubicweb/web/views/ajaxcontroller.py --- a/cubicweb/web/views/ajaxcontroller.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/web/views/ajaxcontroller.py Mon Apr 23 15:23:55 2018 +0200 @@ -153,7 +153,7 @@ result = func(*args) except (RemoteCallFailed, DirectResponse): raise - except ValidationError: + except ValidationError as exc: raise RemoteCallFailed(exc_message(exc, self._cw.encoding), status=http_client.BAD_REQUEST) except Exception as exc: diff -r 038ff1a7259f -r e08d8e171238 cubicweb/web/views/authentication.py --- a/cubicweb/web/views/authentication.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/web/views/authentication.py Mon Apr 23 15:23:55 2018 +0200 @@ -17,6 +17,8 @@ # with CubicWeb. If not, see . """user authentication component""" +from six import text_type + from logilab.common.deprecation import class_renamed from logilab.common.textutils import unormalize @@ -113,7 +115,7 @@ self.data = {} def __unicode__(self): - return '' % (unicode(self.user.login), id(self)) + return '' % (text_type(self.user.login), id(self)) @property def anonymous_session(self): diff -r 038ff1a7259f -r e08d8e171238 cubicweb/web/webconfig.py --- a/cubicweb/web/webconfig.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/web/webconfig.py Mon Apr 23 15:23:55 2018 +0200 @@ -23,7 +23,7 @@ import os import hmac from uuid import uuid4 -from os.path import join, exists, split, isdir +from os.path import dirname, join, exists, split, isdir from warnings import warn from six import text_type @@ -37,6 +37,9 @@ from cubicweb.cwconfig import CubicWebConfiguration, register_persistent_options +_DATA_DIR = join(dirname(__file__), 'data') + + register_persistent_options( ( # site-wide only web ui configuration ('site-title', @@ -204,7 +207,7 @@ ('captcha-font-file', {'type' : 'string', - 'default': join(CubicWebConfiguration.shared_dir(), 'data', 'porkys.ttf'), + 'default': join(_DATA_DIR, 'porkys.ttf'), 'help': 'True type font to use for captcha image generation (you \ must have the python imaging library installed to use captcha)', 'group': 'web', 'level': 3, @@ -327,7 +330,7 @@ @cached def _fs_path_locate(self, rid, rdirectory): """return the directory where the given resource may be found""" - path = [self.apphome] + self.cubes_path() + [join(self.shared_dir())] + path = [self.apphome] + self.cubes_path() + [dirname(__file__)] for directory in path: if exists(join(directory, rdirectory, rid)): return directory @@ -352,7 +355,7 @@ def locate_all_files(self, rid, rdirectory='wdoc'): """return all files corresponding to the given resource""" - path = [self.apphome] + self.cubes_path() + [join(self.shared_dir())] + path = [self.apphome] + self.cubes_path() + [dirname(__file__)] for directory in path: fpath = join(directory, rdirectory, rid) if exists(fpath): @@ -399,7 +402,7 @@ self._init_uiprops(self.uiprops) def _init_uiprops(self, uiprops): - libuiprops = join(self.shared_dir(), 'data', 'uiprops.py') + libuiprops = join(_DATA_DIR, 'uiprops.py') uiprops.load(libuiprops) for path in reversed([self.apphome] + self.cubes_path()): self._load_ui_properties_file(uiprops, path) diff -r 038ff1a7259f -r e08d8e171238 cubicweb/web/webctl.py --- a/cubicweb/web/webctl.py Mon Apr 23 13:50:50 2018 +0200 +++ b/cubicweb/web/webctl.py Mon Apr 23 15:23:55 2018 +0200 @@ -20,9 +20,8 @@ """ from __future__ import print_function - - -import os, os.path as osp +import os +import os.path as osp from shutil import copy, rmtree from logilab.common.shellutils import ASK @@ -31,6 +30,7 @@ from cubicweb.cwctl import CWCTL from cubicweb.cwconfig import CubicWebConfiguration as cwcfg from cubicweb.toolsutils import Command, CommandHandler, underline_title +from cubicweb.web.webconfig import _DATA_DIR try: @@ -76,18 +76,19 @@ if not dest: dest = osp.join(config.appdatahome, 'data') if osp.exists(dest): - if config.verbosity and (not ask_clean or - not (config.verbosity and - ASK.confirm('Remove existing data directory %s?' % dest))): + if (config.verbosity + and (not ask_clean + or not (config.verbosity + and ASK.confirm('Remove existing data directory %s?' % dest)))): raise ExecutionError('Directory %s already exists. ' 'Remove it first.' % dest) rmtreecontent(dest) - config.quick_start = True # notify this is not a regular start + config.quick_start = True # notify this is not a regular start # list all resources (no matter their order) resources = set() for datadir in self._datadirs(config, repo=repo): for dirpath, dirnames, filenames in os.walk(datadir): - rel_dirpath = dirpath[len(datadir)+1:] + rel_dirpath = dirpath[len(datadir) + 1:] resources.update(osp.join(rel_dirpath, f) for f in filenames) # locate resources and copy them to destination @@ -115,7 +116,7 @@ cube_datadir = osp.join(cwcfg.cube_dir(cube), 'data') if osp.isdir(cube_datadir): yield cube_datadir - yield osp.join(config.shared_dir(), 'data') + yield _DATA_DIR class WebUpgradeHandler(CommandHandler, GenStaticDataDirMixIn): diff -r 038ff1a7259f -r e08d8e171238 debian/changelog --- a/debian/changelog Mon Apr 23 13:50:50 2018 +0200 +++ b/debian/changelog Mon Apr 23 15:23:55 2018 +0200 @@ -1,3 +1,27 @@ +cubicweb (3.26.3-1) unstable; urgency=medium + + * New upstream release. + + -- Denis Laxalde Mon, 23 Apr 2018 15:18:55 +0200 + +cubicweb (3.26.2-1) unstable; urgency=medium + + * new upstream release. + + -- Denis Laxalde Thu, 22 Mar 2018 13:52:54 +0100 + +cubicweb (3.26.1-1) unstable; urgency=medium + + * New upstream release. + + -- Denis Laxalde Wed, 21 Feb 2018 18:06:54 +0100 + +cubicweb (3.26.0-1) unstable; urgency=medium + + * New upstream release. + + -- Denis Laxalde Thu, 01 Feb 2018 09:24:01 +0100 + cubicweb (3.25.4-1) unstable; urgency=medium * New upstream release. diff -r 038ff1a7259f -r e08d8e171238 doc/changes/3.26.rst --- a/doc/changes/3.26.rst Mon Apr 23 13:50:50 2018 +0200 +++ b/doc/changes/3.26.rst Mon Apr 23 15:23:55 2018 +0200 @@ -1,5 +1,5 @@ -3.26 (unreleased) -================= +3.26 (1 February 2018) +====================== New features ------------ @@ -7,3 +7,8 @@ * For ``pyramid`` instance configuration kind, logging is not handled anymore by CubicWeb but should be configured through ``development.ini`` file following https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html. + +Backwards incompatible changes +------------------------------ + +* CubicWebConfiguration method 'shared_dir' got dropped. diff -r 038ff1a7259f -r e08d8e171238 doc/changes/changelog.rst --- a/doc/changes/changelog.rst Mon Apr 23 13:50:50 2018 +0200 +++ b/doc/changes/changelog.rst Mon Apr 23 15:23:55 2018 +0200 @@ -2,6 +2,7 @@ Changelog history =================== +.. include:: 3.26.rst .. include:: 3.25.rst .. include:: 3.24.rst .. include:: 3.23.rst diff -r 038ff1a7259f -r e08d8e171238 flake8-ok-files.txt --- a/flake8-ok-files.txt Mon Apr 23 13:50:50 2018 +0200 +++ b/flake8-ok-files.txt Mon Apr 23 15:23:55 2018 +0200 @@ -73,6 +73,7 @@ cubicweb/sobjects/test/unittest_notification.py cubicweb/sobjects/test/unittest_register_user.py cubicweb/sobjects/textparsers.py +cubicweb/statsd_logger.py cubicweb/test/data/libpython/cubicweb_comment/__init__.py cubicweb/test/data/libpython/cubicweb_comment/__pkginfo__.py cubicweb/test/data/libpython/cubicweb_email/entities.py @@ -110,6 +111,7 @@ cubicweb/web/test/data/entities.py cubicweb/web/test/unittest_application.py cubicweb/web/test/unittest_http_headers.py +cubicweb/web/test/unittest_propertysheet.py cubicweb/web/test/unittest_uicfg.py cubicweb/web/test/unittest_views_basetemplates.py cubicweb/web/test/unittest_views_cwsources.py @@ -122,6 +124,7 @@ cubicweb/web/views/staticcontrollers.py cubicweb/web/views/workflow.py cubicweb/web/views/uicfg.py +cubicweb/web/webctl.py cubicweb/xy.py cubicweb/pyramid/auth.py cubicweb/pyramid/bwcompat.py diff -r 038ff1a7259f -r e08d8e171238 setup.py --- a/setup.py Mon Apr 23 13:50:50 2018 +0200 +++ b/setup.py Mon Apr 23 15:23:55 2018 +0200 @@ -47,7 +47,6 @@ # import optional features distname = __pkginfo__['distname'] -data_files = __pkginfo__['data_files'] package_data = __pkginfo__['package_data'] @@ -62,7 +61,6 @@ author_email=author_email, packages=find_packages(), package_data=package_data, - data_files=data_files, include_package_data=True, install_requires=[ 'six >= 1.4.0',