# HG changeset patch # User Sylvain Thénault # Date 1329229002 -3600 # Node ID cc70da744f43167c93c921847ecadc83514f8700 # Parent 00435a332502bba247bec08441cdd288f0d51e6b# Parent c2a91d6639d8dd3d6b3026bab802a88b37c7ffb1 backport stable diff -r 00435a332502 -r cc70da744f43 __init__.py --- a/__init__.py Tue Feb 14 11:34:53 2012 +0100 +++ b/__init__.py Tue Feb 14 15:16:42 2012 +0100 @@ -117,6 +117,7 @@ binary.write(chunk) else: binary.write(fobj.read()) + binary.seek(0) return binary diff -r 00435a332502 -r cc70da744f43 debian/control --- a/debian/control Tue Feb 14 11:34:53 2012 +0100 +++ b/debian/control Tue Feb 14 15:16:42 2012 +0100 @@ -127,7 +127,7 @@ Architecture: all XB-Python-Version: ${python:Versions} Depends: ${misc:Depends}, ${python:Depends}, cubicweb-server (= ${source:Version}), cubicweb-web (= ${source:Version}), python-pysqlite2 -Suggests: w3c-dtd-xhtml +Suggests: w3c-dtd-xhtml, xvfb Description: tests suite and development tools for the CubicWeb framework CubicWeb is a semantic web application framework. . diff -r 00435a332502 -r cc70da744f43 devtools/qunit.py --- a/devtools/qunit.py Tue Feb 14 11:34:53 2012 +0100 +++ b/devtools/qunit.py Tue Feb 14 15:16:42 2012 +0100 @@ -70,13 +70,16 @@ fnull = open(os.devnull, 'w') stdout = TemporaryFile() stderr = TemporaryFile() + self.firefox_cmd = ['firefox', '-no-remote'] + if os.name == 'posix': + self.firefox_cmd = ['xvfb-run', '-a'] + self.firefox_cmd try: home = osp.expanduser('~') user = getlogin() assert os.access(home, os.W_OK), \ 'No write access to your home directory, Firefox will crash.'\ ' Are you sure "%s" is a valid home for user "%s"' % (home, user) - check_call(['firefox', '-no-remote', '-CreateProfile', + check_call(self.firefox_cmd + ['-CreateProfile', '%s %s' % (self._profile_name, self._tmp_dir)], stdout=stdout, stderr=stderr) except CalledProcessError, cpe: @@ -87,7 +90,7 @@ def start(self, url): self.stop() fnull = open(os.devnull, 'w') - self._process = Popen(['firefox', '-no-remote', '-P', self._profile_name, url], + self._process = Popen(self.firefox_cmd + ['-P', self._profile_name, url], stdout=fnull, stderr=fnull) def stop(self): diff -r 00435a332502 -r cc70da744f43 hooks/notification.py --- a/hooks/notification.py Tue Feb 14 11:34:53 2012 +0100 +++ b/hooks/notification.py Tue Feb 14 15:16:42 2012 +0100 @@ -30,7 +30,7 @@ """delay rendering of notification view until precommit""" view = None # make pylint happy - def precommit_event(self): + def postcommit_event(self): view = self.view if view.cw_rset is not None and not view.cw_rset: return # entity added and deleted in the same transaction (cache effect) diff -r 00435a332502 -r cc70da744f43 i18n.py --- a/i18n.py Tue Feb 14 11:34:53 2012 +0100 +++ b/i18n.py Tue Feb 14 15:16:42 2012 +0100 @@ -60,7 +60,9 @@ status != 0 """ from subprocess import call - print cmd.replace(os.getcwd() + os.sep, '') + # use getcwdu as cmd may be unicode and cwd may contains non-ascii + # characters + print cmd.replace(os.getcwdu() + os.sep, '') status = call(cmd, shell=True) if status != 0: raise Exception('status = %s' % status) diff -r 00435a332502 -r cc70da744f43 server/migractions.py --- a/server/migractions.py Tue Feb 14 11:34:53 2012 +0100 +++ b/server/migractions.py Tue Feb 14 15:16:42 2012 +0100 @@ -1352,7 +1352,7 @@ getattr(entity, attribute) storage.migrate_entity(entity, attribute) # remove from entity cache to avoid memory exhaustion - del entity[attribute] + del entity.cw_attr_cache[attribute] pb.update() print source.set_storage(etype, attribute, storage) diff -r 00435a332502 -r cc70da744f43 server/repository.py --- a/server/repository.py Tue Feb 14 11:34:53 2012 +0100 +++ b/server/repository.py Tue Feb 14 15:16:42 2012 +0100 @@ -509,6 +509,8 @@ results['%s_cache_hit' % title] = hits results['%s_cache_miss' % title] = misses results['%s_cache_hit_percent' % title] = (hits * 100) / (hits + misses) + results['type_source_cache_size'] = len(self._type_source_cache) + results['extid_cache_size'] = len(self._extid_cache) results['sql_no_cache'] = self.system_source.no_cache results['nb_open_sessions'] = len(self._sessions) results['nb_active_threads'] = threading.activeCount() @@ -517,6 +519,43 @@ results['threads'] = ', '.join(sorted(str(t) for t in threading.enumerate())) return results + def gc_stats(self, nmax=20): + """Return a dictionary containing some statistics about the repository + memory usage. + + This is a public method, not requiring a session id. + + nmax is the max number of (most) referenced object returned as + the 'referenced' result + """ + + from cubicweb._gcdebug import gc_info + from cubicweb.appobject import AppObject + from cubicweb.rset import ResultSet + from cubicweb.dbapi import Connection, Cursor + from cubicweb.web.request import CubicWebRequestBase + from rql.stmts import Union + + lookupclasses = (AppObject, + Union, ResultSet, + Connection, Cursor, + CubicWebRequestBase) + try: + from cubicweb.server.session import Session, InternalSession + lookupclasses += (InternalSession, Session) + except ImportError: + pass # no server part installed + + results = {} + counters, ocounters, garbage = gc_info(lookupclasses, + viewreferrersclasses=()) + values = sorted(counters.iteritems(), key=lambda x: x[1], reverse=True) + results['lookupclasses'] = values + values = sorted(ocounters.iteritems(), key=lambda x: x[1], reverse=True)[:nmax] + results['referenced'] = values + results['unreachable'] = len(garbage) + return results + def get_schema(self): """Return the instance schema. diff -r 00435a332502 -r cc70da744f43 server/serverctl.py --- a/server/serverctl.py Tue Feb 14 11:34:53 2012 +0100 +++ b/server/serverctl.py Tue Feb 14 15:16:42 2012 +0100 @@ -25,6 +25,7 @@ import sys import os import logging +import subprocess from logilab.common import nullobject from logilab.common.configuration import Configuration @@ -258,12 +259,13 @@ cfgname = 'repository' def start_server(self, config): - command = ['cubicweb-ctl start-repository '] + command = ['cubicweb-ctl', 'start-repository'] if config.debugmode: command.append('--debug') - command.append('--loglevel %s' % config['log-threshold'].lower()) + command.append('--loglevel') + command.append(config['log-threshold'].lower()) command.append(config.appid) - os.system(' '.join(command)) + subprocess.call(command) return 1 diff -r 00435a332502 -r cc70da744f43 server/sources/__init__.py --- a/server/sources/__init__.py Tue Feb 14 11:34:53 2012 +0100 +++ b/server/sources/__init__.py Tue Feb 14 15:16:42 2012 +0100 @@ -135,7 +135,8 @@ source_config.pop('type') def __repr__(self): - return '<%s source %s @%#x>' % (self.uri, self.eid, id(self)) + return '<%s %s source %s @%#x>' % (self.uri, self.__class__.__name__, + self.eid, id(self)) def __cmp__(self, other): """simple comparison function to get predictable source order, with the diff -r 00435a332502 -r cc70da744f43 server/sources/native.py --- a/server/sources/native.py Tue Feb 14 11:34:53 2012 +0100 +++ b/server/sources/native.py Tue Feb 14 15:16:42 2012 +0100 @@ -1586,9 +1586,11 @@ pwd = rset[0][0] except IndexError: raise AuthenticationError('bad login') + if pwd is None: + # if pwd is None but a password is provided, something is wrong + raise AuthenticationError('bad password') # passwords are stored using the Bytes type, so we get a StringIO - if pwd is not None: - args['pwd'] = Binary(crypt_password(password, pwd.getvalue()[:2])) + args['pwd'] = Binary(crypt_password(password, pwd.getvalue()[:2])) # get eid from login and (crypted) password rset = self.source.syntax_tree_search(session, self._auth_rqlst, args) try: