# HG changeset patch # User Sylvain Thénault # Date 1252563202 -7200 # Node ID edfe43ceaa3549219aae50ad9aa5886ac8f43572 # Parent 8184bec7414d355b6b8906dac33bf13509b49514# Parent d6ae24439beecc4ccf9b58bfaeb0a5ed0ce71e2a backport 3.5 diff -r 8184bec7414d -r edfe43ceaa35 .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Thu Sep 10 08:13:22 2009 +0200 @@ -0,0 +1,10 @@ +\.svn +^build$ +^dist$ +\.pyc$ +\.pyo$ +\.bak$ +\.old$ +\~$ +\#.*?\#$ +\.swp$ diff -r 8184bec7414d -r edfe43ceaa35 .hgtags --- a/.hgtags Wed Sep 02 16:42:07 2009 +0200 +++ b/.hgtags Thu Sep 10 08:13:22 2009 +0200 @@ -62,3 +62,5 @@ 7fd294cbf6ff3cf34475cc50e972f650a34ae6e8 cubicweb-debian-version-3.4.5-1 921fdbf8b3038dc27a2ec5398a0fbcbc5b9ba4be cubicweb-version-3.4.6 52dba800ca4d4b82c47f3befb824bd91ef015368 cubicweb-debian-version-3.4.6-1 +0e549b299f0b357837ea620c561aa843f46de17a cubicweb-version-3.4.7 +ebb92e62eb040a070deb1f2d2434734cfac3af01 cubicweb-debian-version-3.4.7-1 diff -r 8184bec7414d -r edfe43ceaa35 __init__.py --- a/__init__.py Wed Sep 02 16:42:07 2009 +0200 +++ b/__init__.py Thu Sep 10 08:13:22 2009 +0200 @@ -55,8 +55,7 @@ "Binary objects must use raw strings, not %s" % data.__class__ StringIO.write(self, data) - -# XXX 2.45 is allowing nicer entity type names, use this map for bw compat +# use this dictionary for renaming of entity types while keeping bw compath ETYPE_NAME_MAP = {# 3.2 migration 'ECache': 'CWCache', 'EUser': 'CWUser', @@ -69,31 +68,19 @@ 'EConstraintType': 'CWConstraintType', 'EConstraint': 'CWConstraint', 'EPermission': 'CWPermission', - # 2.45 migration - 'Eetype': 'CWEType', - 'Ertype': 'CWRType', - 'Efrdef': 'CWAttribute', - 'Enfrdef': 'CWRelation', - 'Econstraint': 'CWConstraint', - 'Econstrainttype': 'CWConstraintType', - 'Epermission': 'CWPermission', - 'Egroup': 'CWGroup', - 'Euser': 'CWUser', - 'Eproperty': 'CWProperty', - 'Emailaddress': 'EmailAddress', - 'Rqlexpression': 'RQLExpression', - 'Trinfo': 'TrInfo', } # XXX cubic web cube migration map CW_MIGRATION_MAP = {'erudi': 'cubicweb', - 'eaddressbook': 'addressbook', 'ebasket': 'basket', 'eblog': 'blog', 'ebook': 'book', + 'eclassschemes': 'keyword', + 'eclassfolders': 'folder', + 'eclasstags': 'tag', 'ecomment': 'comment', 'ecompany': 'company', 'econference': 'conference', @@ -113,20 +100,6 @@ 'ezone': 'zone', 'i18ncontent': 'i18ncontent', 'svnfile': 'vcsfile', - - 'eclassschemes': 'keyword', - 'eclassfolders': 'folder', - 'eclasstags': 'tag', - - 'jpl': 'jpl', - 'jplintra': 'jplintra', - 'jplextra': 'jplextra', - 'jplorg': 'jplorg', - 'jplrecia': 'jplrecia', - 'crm': 'crm', - 'agueol': 'agueol', - 'docaster': 'docaster', - 'asteretud': 'asteretud', } def neg_role(role): diff -r 8184bec7414d -r edfe43ceaa35 __pkginfo__.py diff -r 8184bec7414d -r edfe43ceaa35 _exceptions.py --- a/_exceptions.py Wed Sep 02 16:42:07 2009 +0200 +++ b/_exceptions.py Thu Sep 10 08:13:22 2009 +0200 @@ -117,7 +117,7 @@ class NoSelectableObject(RegistryException): """some views with the given vid have been found but no - one is applyable to the result set + one is applicable to the result set """ class UnknownProperty(RegistryException): diff -r 8184bec7414d -r edfe43ceaa35 bin/cubicweb-ctl.bat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/cubicweb-ctl.bat Thu Sep 10 08:13:22 2009 +0200 @@ -0,0 +1,18 @@ +@echo off +rem = """-*-Python-*- script +rem -------------------- DOS section -------------------- +rem You could set PYTHONPATH or TK environment variables here +python -x "%~f0" %* +goto exit + +""" +# -------------------- Python section -------------------- +from cubicweb.cwctl import run +import sys +run(sys.argv[1:]) + +DosExitLabel = """ +:exit +rem """ + + diff -r 8184bec7414d -r edfe43ceaa35 common/i18n.py --- a/common/i18n.py Wed Sep 02 16:42:07 2009 +0200 +++ b/common/i18n.py Thu Sep 10 08:13:22 2009 +0200 @@ -9,6 +9,7 @@ import re import os +import sys from os.path import join, basename, splitext, exists from glob import glob @@ -44,9 +45,13 @@ status != 0 """ print cmd.replace(os.getcwd() + os.sep, '') - status = os.system(cmd) + if sys.platform == 'win32': + from subprocess import call + else: + call = os.system + status = call(cmd) if status != 0: - raise Exception() + raise Exception('status = %s' % status) def available_catalogs(i18ndir=None): @@ -74,15 +79,15 @@ mergedpo = join(destdir, '%s_merged.po' % lang) try: # merge instance/cubes messages catalogs with the stdlib's one - execute('msgcat --use-first --sort-output --strict %s > %s' - % (' '.join(pofiles), mergedpo)) - # make sure the .mo file is writeable and compile with *msgfmt* + execute('msgcat --use-first --sort-output --strict -o "%s" %s' + % (mergedpo, ' '.join('"%s"' % f for f in pofiles))) + # make sure the .mo file is writeable and compiles with *msgfmt* applmo = join(destdir, lang, 'LC_MESSAGES', 'cubicweb.mo') try: ensure_fs_mode(applmo) except OSError: pass # suppose not exists - execute('msgfmt %s -o %s' % (mergedpo, applmo)) + execute('msgfmt "%s" -o "%s"' % (mergedpo, applmo)) except Exception, ex: errors.append('while handling language %s: %s' % (lang, ex)) try: diff -r 8184bec7414d -r edfe43ceaa35 common/mail.py --- a/common/mail.py Wed Sep 02 16:42:07 2009 +0200 +++ b/common/mail.py Thu Sep 10 08:13:22 2009 +0200 @@ -21,6 +21,7 @@ return 'XXX' from cubicweb.view import EntityView +from cubicweb.entity import Entity def header(ustring): return Header(ustring.encode('UTF-8'), 'UTF-8') @@ -176,28 +177,38 @@ else: refs = () msgid = None - userdata = self.req.user_data() - origlang = self.req.lang - for emailaddr, lang in recipients: - self.req.set_language(lang) + req = self.req + self.user_data = req.user_data() + origlang = req.lang + for something in recipients: + if isinstance(something, Entity): + # hi-jack self.req to get a session for the returned user + self.req = self.req.hijack_user(something) + emailaddr = something.get_email() + else: + emailaddr, lang = something + self.req.set_language(lang) # since the same view (eg self) may be called multiple time and we # need a fresh stream at each iteration, reset it explicitly self.w = None # XXX call render before subject to set .row/.col attributes on the # view - content = self.render(row=0, col=0, **kwargs) - subject = self.subject() - msg = format_mail(userdata, [emailaddr], content, subject, + try: + content = self.render(row=0, col=0, **kwargs) + subject = self.subject() + except SkipEmail: + continue + msg = format_mail(self.user_data, [emailaddr], content, subject, config=self.config, msgid=msgid, references=refs) yield [emailaddr], msg # restore language - self.req.set_language(origlang) + req.set_language(origlang) # recipients / email sending ############################################### def recipients(self): - """return a list of 2-uple (email, language) to who this email should be - sent + """return a list of either 2-uple (email, language) or user entity to + who this email should be sent """ finder = self.vreg['components'].select('recipients_finder', self.req, rset=self.rset, @@ -230,7 +241,7 @@ subject = self.req._(self.message) etype = entity.dc_type() eid = entity.eid - login = self.user_login() + login = self.user_data['login'] return self.req._('%(subject)s %(etype)s #%(eid)s (%(login)s)') % locals() def context(self, **kwargs): @@ -238,17 +249,13 @@ for key, val in kwargs.iteritems(): if val and isinstance(val, unicode) and val.strip(): kwargs[key] = self.req._(val) - kwargs.update({'user': self.user_login(), + kwargs.update({'user': self.user_data['login'], 'eid': entity.eid, 'etype': entity.dc_type(), 'url': entity.absolute_url(), 'title': entity.dc_long_title(),}) return kwargs - def user_login(self): - try: - # if req is actually a session (we are on the server side), and we - # have to prevent nested internal session - return self.req.actual_session().user.login - except AttributeError: - return self.req.user.login + +class SkipEmail(Exception): + """raise this if you decide to skip an email during its generation""" diff -r 8184bec7414d -r edfe43ceaa35 common/migration.py --- a/common/migration.py Wed Sep 02 16:42:07 2009 +0200 +++ b/common/migration.py Thu Sep 10 08:13:22 2009 +0200 @@ -338,7 +338,7 @@ configfile = self.config.main_config_file() if self._option_changes: read_old_config(self.config, self._option_changes, configfile) - _, newconfig = tempfile.mkstemp() + fd, newconfig = tempfile.mkstemp() for optdescr in self._option_changes: if optdescr[0] == 'added': optdict = self.config.get_option_def(optdescr[1]) @@ -346,6 +346,7 @@ self.config.input_option(optdescr[1], optdict) self.config.generate_config(open(newconfig, 'w')) show_diffs(configfile, newconfig) + os.close(fd) if exists(newconfig): os.unlink(newconfig) diff -r 8184bec7414d -r edfe43ceaa35 common/test/unittest_mail.py --- a/common/test/unittest_mail.py Wed Sep 02 16:42:07 2009 +0200 +++ b/common/test/unittest_mail.py Thu Sep 10 08:13:22 2009 +0200 @@ -8,7 +8,7 @@ """ import os -import pwd +import sys from logilab.common.testlib import unittest_main from logilab.common.umessage import message_from_string @@ -22,7 +22,11 @@ (man 3 getlogin) Another solution would be to use $LOGNAME, $USER or $USERNAME """ - return pwd.getpwuid(os.getuid())[0] + if sys.platform != 'win32': + import pwd + return pwd.getpwuid(os.getuid())[0] + else: + return os.environ.get('USERNAME') class EmailTC(CubicWebTC): diff -r 8184bec7414d -r edfe43ceaa35 cwconfig.py --- a/cwconfig.py Wed Sep 02 16:42:07 2009 +0200 +++ b/cwconfig.py Thu Sep 10 08:13:22 2009 +0200 @@ -20,6 +20,7 @@ from smtplib import SMTP from threading import Lock from os.path import exists, join, expanduser, abspath, normpath, basename, isdir +import tempfile from logilab.common.decorators import cached from logilab.common.deprecation import deprecated @@ -526,13 +527,13 @@ if CubicWebNoAppConfiguration.mode == 'test': root = os.environ['APYCOT_ROOT'] REGISTRY_DIR = '%s/etc/cubicweb.d/' % root - RUNTIME_DIR = '/tmp/' + RUNTIME_DIR = tempfile.gettempdir() 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 = '/tmp/' + RUNTIME_DIR = tempfile.gettempdir() MIGRATION_DIR = join(CW_SOFTWARE_ROOT, 'misc', 'migration') else: #mode = 'installed' REGISTRY_DIR = '/etc/cubicweb.d/' @@ -651,7 +652,7 @@ def default_log_file(self): """return default path to the log file of the instance'server""" if self.mode == 'dev': - basepath = '/tmp/%s-%s' % (basename(self.appid), self.name) + basepath = join(tempfile.gettempdir(), '%s-%s' % (basename(self.appid), self.name)) path = basepath + '.log' i = 1 while exists(path) and i < 100: # arbitrary limit to avoid infinite loop @@ -794,8 +795,8 @@ from glob import glob yield 'en' # ensure 'en' is yielded even if no .mo found for path in glob(join(self.apphome, 'i18n', - '*', 'LC_MESSAGES', 'cubicweb.mo')): - lang = path.split(os.sep)[-3] + '*', 'LC_MESSAGES')): + lang = path.split(os.sep)[-2] if lang != 'en': yield lang diff -r 8184bec7414d -r edfe43ceaa35 cwctl.py --- a/cwctl.py Wed Sep 02 16:42:07 2009 +0200 +++ b/cwctl.py Thu Sep 10 08:13:22 2009 +0200 @@ -5,7 +5,13 @@ %s""" import sys -from os import remove, listdir, system, kill, getpgid, pathsep +from os import remove, listdir, system, pathsep +try: + from os import kill, getpgid +except ImportError: + def kill(*args): pass + def getpgid(): pass + from os.path import exists, join, isfile, isdir from logilab.common.clcommands import register_commands, pop_arg @@ -24,7 +30,7 @@ while nbtry < maxtry: try: kill(pid, signal.SIGUSR1) - except OSError: + except (OSError, AttributeError): # XXX win32 break nbtry += 1 sleep(waittime) diff -r 8184bec7414d -r edfe43ceaa35 cwvreg.py --- a/cwvreg.py Wed Sep 02 16:42:07 2009 +0200 +++ b/cwvreg.py Thu Sep 10 08:13:22 2009 +0200 @@ -146,7 +146,12 @@ try: objects = self[btype] assert len(objects) == 1, objects - cls = objects[0] + if btype == etype: + cls = objects[0] + else: + # recurse to ensure issubclass(etype_class('Child'), + # etype_class('Parent')) + cls = self.etype_class(btype) break except ObjectNotFound: pass diff -r 8184bec7414d -r edfe43ceaa35 dbapi.py --- a/dbapi.py Wed Sep 02 16:42:07 2009 +0200 +++ b/dbapi.py Thu Sep 10 08:13:22 2009 +0200 @@ -285,6 +285,12 @@ # server session compat layer ############################################# + def hijack_user(self, user): + """return a fake request/session using specified user""" + req = DBAPIRequest(self.vreg) + req.set_connection(self.cnx, user) + return req + @property def user(self): if self._user is None and self.cnx: diff -r 8184bec7414d -r edfe43ceaa35 debian/changelog --- a/debian/changelog Wed Sep 02 16:42:07 2009 +0200 +++ b/debian/changelog Thu Sep 10 08:13:22 2009 +0200 @@ -1,3 +1,9 @@ +cubicweb (3.4.7-1) unstable; urgency=low + + * new upstream release + + -- Sylvain Thénault Wed, 09 Sep 2009 14:08:41 +0200 + cubicweb (3.4.6-1) unstable; urgency=low * new upstream release diff -r 8184bec7414d -r edfe43ceaa35 devtools/__init__.py diff -r 8184bec7414d -r edfe43ceaa35 devtools/devctl.py --- a/devtools/devctl.py Wed Sep 02 16:42:07 2009 +0200 +++ b/devtools/devctl.py Thu Sep 10 08:13:22 2009 +0200 @@ -10,7 +10,7 @@ import sys from datetime import datetime -from os import mkdir, chdir +from os import mkdir, chdir, getcwd from os.path import join, exists, abspath, basename, normpath, split, isdir from warnings import warn @@ -285,20 +285,20 @@ if lang is not None: cmd += ' -L %s' % lang potfile = join(tempdir, '%s.pot' % id) - execute(cmd % (potfile, ' '.join(files))) + execute(cmd % (potfile, ' '.join('"%s"' % f for f in files))) if exists(potfile): potfiles.append(potfile) else: print '-> WARNING: %s file was not generated' % potfile print '-> merging %i .pot files' % len(potfiles) cubicwebpot = join(tempdir, 'cubicweb.pot') - execute('msgcat %s > %s' % (' '.join(potfiles), cubicwebpot)) + execute('msgcat -o %s %s' % (cubicwebpot, ' '.join('"%s"' % f for f in potfiles))) print '-> merging main pot file with existing translations.' chdir(I18NDIR) toedit = [] for lang in LANGS: target = '%s.po' % lang - execute('msgmerge -N --sort-output %s %s > %snew' % (target, cubicwebpot, target)) + execute('msgmerge -N --sort-output -o "%snew" "%s" "%s"' % (target, target, cubicwebpot)) ensure_fs_mode(target) shutil.move('%snew' % target, target) toedit.append(abspath(target)) @@ -392,12 +392,13 @@ cubefiles = find('.', '.py', blacklist=STD_BLACKLIST+('test',)) cubefiles.append(tali18nfile) execute('xgettext --no-location --omit-header -k_ -o %s %s' - % (tmppotfile, ' '.join(cubefiles))) + % (tmppotfile, ' '.join('"%s"' % f for f in cubefiles))) if exists(tmppotfile): # doesn't exists of no translation string found potfiles.append(tmppotfile) potfile = join(tempdir, 'cube.pot') print '-> merging %i .pot files:' % len(potfiles) - execute('msgcat %s > %s' % (' '.join(potfiles), potfile)) + execute('msgcat -o %s %s' % (potfile, + ' '.join('"%s"' % f for f in potfiles))) print '-> merging main pot file with existing translations:' chdir('i18n') for lang in LANGS: @@ -406,7 +407,7 @@ if not exists(cubepo): shutil.copy(potfile, cubepo) else: - execute('msgmerge -N -s %s %s > %snew' % (cubepo, potfile, cubepo)) + execute('msgmerge -N -s -o %snew %s %s' % (cubepo, cubepo, potfile)) ensure_fs_mode(cubepo) shutil.move('%snew' % cubepo, cubepo) toedit.append(abspath(cubepo)) diff -r 8184bec7414d -r edfe43ceaa35 devtools/htmlparser.py --- a/devtools/htmlparser.py Wed Sep 02 16:42:07 2009 +0200 +++ b/devtools/htmlparser.py Thu Sep 10 08:13:22 2009 +0200 @@ -36,7 +36,11 @@ class DTDValidator(Validator): def __init__(self): Validator.__init__(self) - self.parser = etree.XMLParser(dtd_validation=True) + # XXX understand what's happening under windows + validate = True + if sys.platform == 'win32': + validate = False + self.parser = etree.XMLParser(dtd_validation=validate) def preprocess_data(self, data): """used to fix potential blockquote mess generated by docutils""" diff -r 8184bec7414d -r edfe43ceaa35 devtools/testlib.py --- a/devtools/testlib.py Wed Sep 02 16:42:07 2009 +0200 +++ b/devtools/testlib.py Thu Sep 10 08:13:22 2009 +0200 @@ -180,14 +180,16 @@ cls.admlogin = unicode(source['db-user']) cls.admpassword = source['db-password'] # uncomment the line below if you want rql queries to be logged - #config.global_set_option('query-log-file', '/tmp/test_rql_log.' + `os.getpid()`) + #config.global_set_option('query-log-file', + # '/tmp/test_rql_log.' + `os.getpid()`) config.global_set_option('log-file', None) # set default-dest-addrs to a dumb email address to avoid mailbox or # mail queue pollution config.global_set_option('default-dest-addrs', ['whatever']) try: send_to = '%s@logilab.fr' % os.getlogin() - except OSError: + # AttributeError since getlogin not available under all platforms + except (OSError, AttributeError): send_to = '%s@logilab.fr' % (os.environ.get('USER') or os.environ.get('USERNAME') or os.environ.get('LOGNAME')) diff -r 8184bec7414d -r edfe43ceaa35 doc/book/en/B0015-define-permissions.en.txt --- a/doc/book/en/B0015-define-permissions.en.txt Wed Sep 02 16:42:07 2009 +0200 +++ b/doc/book/en/B0015-define-permissions.en.txt Thu Sep 10 08:13:22 2009 +0200 @@ -114,7 +114,7 @@ require_group = SubjectRelation('EGroup', cardinality='+*', description=_('groups to which the permission is granted')) require_state = SubjectRelation('State', - description=_("entity'state in which the permission is applyable")) + description=_("entity'state in which the permission is applicable")) # can be used on any entity require_permission = ObjectRelation('**', cardinality='*1', composite='subject', description=_("link a permission to the entity. This " diff -r 8184bec7414d -r edfe43ceaa35 doc/book/en/admin/setup.rst --- a/doc/book/en/admin/setup.rst Wed Sep 02 16:42:07 2009 +0200 +++ b/doc/book/en/admin/setup.rst Thu Sep 10 08:13:22 2009 +0200 @@ -77,6 +77,150 @@ In both cases, make sure you have installed the dependencies (see appendixes for the list). +Windows installation +```````````````````` + +Base elements +_____________ + +Setting up a windows development environment is not too complicated +but requires a series of small steps. What is proposed there is only +an example of what can be done. We assume everything goes into C:\ in +this document. Adjusting the installation drive should be +straightforward. + +You should start by downloading and installing the Python(x,y) +distribution. It contains python 2.5 plus numerous useful third-party +modules and applications:: + + http://www.pythonxy.com/download_fr.php + +At the time of this writting, one gets version 2.1.15. Among the many +things provided, one finds Eclipse + pydev (an arguably good IDE for +python under windows). + +Then you must grab Twisted. There is a windows installer directly +available from this page:: + + http://twistedmatrix.com/trac/ + +A windows installer for lxml will be found there:: + + http://pypi.python.org/pypi/lxml/2.2.1 + +Check out the lxml-2.2.1-win32-py2.5.exe file. More recent bugfix +releases should probably work, too. + +You should find postgresql 8.4 there:: + + http://www.enterprisedb.com/products/pgdownload.do#windows + +The python drivers for posgtresql are to be found there:: + + http://www.stickpeople.com/projects/python/win-psycopg/#Version2 + +Please be careful to select the right python (2.5) and postgres (8.4) +versions. + +Pyro enable remote access to cubicweb repository instances. Get it +there:: + + http://sourceforge.net/projects/pyro/files/ + +To access LDAP/Active directory directories, we need the python-ldap +package. Windows binaries are available from:: + + http://www.osuch.org/python-ldap + +Check out the latest release. + +Having graphviz will allow schema drawings, which is quite recommended +(albeit not mandatory). You should get an msi installer there:: + + http://www.graphviz.org/Download_windows.php + +Simplejson will be provided within the forest, but a win32 compiled +version will run much faster:: + + http://www.osuch.org/python-simplejson%3Awin32 + +Tools +_____ + +Get mercurial + its standard windows GUI (TortoiseHG) there (the +latest is the greatest):: + + http://bitbucket.org/tortoisehg/stable/wiki/download + +If you need to peruse mercurial over ssh, it can be helpful to get an +ssh client like Putty:: + + http://www.putty.org/ + +Integration of mercurial and Eclipse is convenient enough that we want +it. Instructions are set there, in the `Download & Install` section:: + + http://www.vectrace.com/mercurialeclipse/ + +Setting up the sources +______________________ + +You need to enable the mercurial forest extension. To do this, edit +the file:: + + C:\Program Files\TortoiseHg\Mercurial.ini + +In the [extensions] section, add the following line:: + + forest=C:\Program Files\TortoiseHg\ext\forest\forest.py + +Now, you need to clone the cubicweb repository. We assume that you use +Eclipse. From the IDE, choose File -> Import. In the box, select +`Mercurial/Clone repository using MercurialEclipse`. + +In the import main panel you just have to: + +* fill the URL field with http://www.logilab.org/hg/forests/cubicwin32 + +* check the 'Repository is a forest' box. + +Then, click on 'Finish'. It might take some time to get it all. Note +that the `cubicwin32` forest contains additional python packages such +as yapps, vobject, simplejson and twisted-web2 which are not provided +with Python(x,y). This is provided for convenience, as we do not +ensure the up-to-dateness of these packages, especially with respect +to security fixes. + +Environment variables +_____________________ + +You will need some convenience environment variables once all is set +up. These variables are settable through the GUI by getting at the +'System properties' window (by righ-clicking on 'My Computer' -> +properties). + +In the 'advanced' tab, there is an 'Environment variables' +button. Click on it. That opens a small window allowing edition of +user-related and system-wide variables. + +We will consider only user variables. First, the PATH variable. You +should ensure it contains, separated by semi-colons, and assuming you +are logged in as user Jane:: + + C:\Documents and Settings\Jane\My Documents\Python\cubicweb\cubicweb\bin + C:\Program Files\Graphviz2.24\bin + +The PYTHONPATH variable should also contain:: + + C:\Documents and Settings\Jane\My Documents\Python\cubicweb\ + +From now, on a fresh `cmd` shell, you should be able to type:: + + cubicweb-ctl list + +... and get a meaningful output. + + PostgreSQL installation ``````````````````````` @@ -135,8 +279,6 @@ Databases configuration ----------------------- - - .. _ConfigurationPostgresql: PostgreSQL configuration diff -r 8184bec7414d -r edfe43ceaa35 doc/book/en/development/datamodel/definition.rst --- a/doc/book/en/development/datamodel/definition.rst Wed Sep 02 16:42:07 2009 +0200 +++ b/doc/book/en/development/datamodel/definition.rst Thu Sep 10 08:13:22 2009 +0200 @@ -390,7 +390,7 @@ require_group = SubjectRelation('CWGroup', cardinality='+*', description=_('groups to which the permission is granted')) require_state = SubjectRelation('State', - description=_("entity'state in which the permission is applyable")) + description=_("entity's state in which the permission is applicable")) # can be used on any entity require_permission = ObjectRelation('**', cardinality='*1', composite='subject', description=_("link a permission to the entity. This " diff -r 8184bec7414d -r edfe43ceaa35 entities/test/unittest_base.py --- a/entities/test/unittest_base.py Wed Sep 02 16:42:07 2009 +0200 +++ b/entities/test/unittest_base.py Thu Sep 10 08:13:22 2009 +0200 @@ -16,7 +16,6 @@ from cubicweb import ValidationError from cubicweb.interfaces import IMileStone, IWorkflowable from cubicweb.entities import AnyEntity -from cubicweb.entities.authobjs import CWUser class BaseEntityTC(CubicWebTC): @@ -101,6 +100,7 @@ class InterfaceTC(CubicWebTC): def test_nonregr_subclasses_and_mixins_interfaces(self): + CWUser = self.vreg['etypes'].etype_class('CWUser') self.failUnless(implements(CWUser, IWorkflowable)) class MyUser(CWUser): __implements__ = (IMileStone,) @@ -108,7 +108,8 @@ self.vreg.register_appobject_class(MyUser) self.vreg['etypes'].initialization_completed() MyUser_ = self.vreg['etypes'].etype_class('CWUser') - self.failUnless(MyUser is MyUser_) + self.failIf(MyUser is MyUser_.__bases__) + self.failUnless(MyUser in MyUser_.__bases__) self.failUnless(implements(MyUser_, IMileStone)) self.failUnless(implements(MyUser_, IWorkflowable)) @@ -133,11 +134,12 @@ id = etype self.vreg.register_appobject_class(Foo) eclass = self.select_eclass('SubDivision') + self.failUnless(eclass.__autogenerated__) + self.failIf(eclass is Foo) if etype == 'SubDivision': - self.failUnless(eclass is Foo) + self.assertEquals(eclass.__bases__, (Foo,)) else: - self.failUnless(eclass.__autogenerated__) - self.assertEquals(eclass.__bases__, (Foo,)) + self.assertEquals(eclass.__bases__[0].__bases__, (Foo,)) # check Division eclass is still selected for plain Division entities eclass = self.select_eclass('Division') self.assertEquals(eclass.id, 'Division') diff -r 8184bec7414d -r edfe43ceaa35 entity.py --- a/entity.py Wed Sep 02 16:42:07 2009 +0200 +++ b/entity.py Thu Sep 10 08:13:22 2009 +0200 @@ -576,14 +576,16 @@ self.set_related_cache(rtype, role, rset) return self.related(rtype, role, limit, entities) - def related_rql(self, rtype, role='subject'): + def related_rql(self, rtype, role='subject', targettypes=None): rschema = self.req.vreg.schema[rtype] if role == 'subject': - targettypes = rschema.objects(self.e_schema) + if targettypes is None: + targettypes = rschema.objects(self.e_schema) restriction = 'E eid %%(x)s, E %s X' % rtype card = greater_card(rschema, (self.e_schema,), targettypes, 0) else: - targettypes = rschema.subjects(self.e_schema) + if targettypes is None: + targettypes = rschema.subjects(self.e_schema) restriction = 'E eid %%(x)s, X %s E' % rtype card = greater_card(rschema, targettypes, (self.e_schema,), 1) if len(targettypes) > 1: diff -r 8184bec7414d -r edfe43ceaa35 etwist/server.py --- a/etwist/server.py Wed Sep 02 16:42:07 2009 +0200 +++ b/etwist/server.py Thu Sep 10 08:13:22 2009 +0200 @@ -15,8 +15,13 @@ from urlparse import urlsplit, urlunsplit import hotshot -from twisted.application import service, strports -from twisted.scripts._twistd_unix import daemonize +from twisted.application import strports +try: + from twisted.scripts._twistd_unix import daemonize +except ImportError: + def daemonize(): + raise NotImplementedError('not yet for win32') + from twisted.internet import reactor, task, threads from twisted.internet.defer import maybeDeferred from twisted.web2 import channel, http, server, iweb diff -r 8184bec7414d -r edfe43ceaa35 ext/xhtml2fo.py --- a/ext/xhtml2fo.py Wed Sep 02 16:42:07 2009 +0200 +++ b/ext/xhtml2fo.py Thu Sep 10 08:13:22 2009 +0200 @@ -1,6 +1,3 @@ -from cubicweb.utils import can_do_pdf_conversion -assert can_do_pdf_conversion() - from xml.etree.ElementTree import QName, fromstring from pysixt.standard.xhtml_xslfo.transformer import XHTML2FOTransformer from pysixt.utils.xslfo.standard import cm diff -r 8184bec7414d -r edfe43ceaa35 hooks/notification.py --- a/hooks/notification.py Wed Sep 02 16:42:07 2009 +0200 +++ b/hooks/notification.py Thu Sep 10 08:13:22 2009 +0200 @@ -98,6 +98,9 @@ 'after_add_entity', 'before_update_entity') def __call__(self): + # XXX use proper selectors + if self._cw.is_super_session or self._cw.repo.config.repairing: + return # ignore changes triggered by hooks or maintainance shell dest = self._cw.vreg.config['supervising-addrs'] if not dest: # no supervisors, don't do this for nothing... return diff -r 8184bec7414d -r edfe43ceaa35 i18n/en.po --- a/i18n/en.po Wed Sep 02 16:42:07 2009 +0200 +++ b/i18n/en.po Thu Sep 10 08:13:22 2009 +0200 @@ -608,7 +608,9 @@ msgid "You can use any of the following substitutions in your text" msgstr "" -msgid "You have no access to this view or it's not applyable to current data" +msgid "" +"You have no access to this view or it can not be used to display the current " +"data." msgstr "" msgid "" @@ -1646,12 +1648,13 @@ msgid "download" msgstr "" +#, python-format +msgid "download %s" +msgstr "" + msgid "download icon" msgstr "" -msgid "download image" -msgstr "" - msgid "download schema as owl" msgstr "" @@ -2052,6 +2055,12 @@ msgid "invalid date" msgstr "" +msgid "invalid float value" +msgstr "" + +msgid "invalid integer value" +msgstr "" + msgid "is" msgstr "" diff -r 8184bec7414d -r edfe43ceaa35 i18n/es.po --- a/i18n/es.po Wed Sep 02 16:42:07 2009 +0200 +++ b/i18n/es.po Thu Sep 10 08:13:22 2009 +0200 @@ -625,8 +625,10 @@ "Puede realizar cualquiera de las siguientes sustituciones en el contenido de " "su email." -msgid "You have no access to this view or it's not applyable to current data" -msgstr "No tiene acceso a esta vista o No es aplicable a los datos actuales" +msgid "" +"You have no access to this view or it can not be used to display the current " +"data." +msgstr "No tiene acceso a esta vista o No se puede utilizare para los datos actuales." msgid "" "You're not authorized to access this page. If you think you should, please " @@ -1708,12 +1710,13 @@ msgid "download" msgstr "Descargar" +#, python-format +msgid "download %s" +msgstr "" + msgid "download icon" msgstr "ícono de descarga" -msgid "download image" -msgstr "" - msgid "download schema as owl" msgstr "Descargar esquema en OWL" @@ -2128,6 +2131,12 @@ msgid "invalid date" msgstr "Esta fecha no es válida" +msgid "invalid float value" +msgstr "" + +msgid "invalid integer value" +msgstr "" + msgid "is" msgstr "es" @@ -3183,3 +3192,7 @@ msgid "you should probably delete that property" msgstr "deberia probablamente suprimir esta propriedad" + +#~ msgid "" +#~ "You have no access to this view or it's not applyable to current data" +#~ msgstr "No tiene acceso a esta vista o No es aplicable a los datos actuales" diff -r 8184bec7414d -r edfe43ceaa35 i18n/fr.po --- a/i18n/fr.po Wed Sep 02 16:42:07 2009 +0200 +++ b/i18n/fr.po Thu Sep 10 08:13:22 2009 +0200 @@ -624,9 +624,10 @@ "Vous pouvez utiliser n'importe quelle substitution parmi la liste suivante " "dans le contenu de votre courriel." -msgid "You have no access to this view or it's not applyable to current data" -msgstr "" -"Vous n'avez pas accès à cette vue ou elle ne s'applique pas aux données" +msgid "" +"You have no access to this view or it can not be used to display the current " +"data." +msgstr "Vous n'avez pas accès à cette vue ou elle ne peut pas afficher ces données." msgid "" "You're not authorized to access this page. If you think you should, please " @@ -1721,12 +1722,13 @@ msgid "download" msgstr "télécharger" +#, python-format +msgid "download %s" +msgstr "télécharger %s" + msgid "download icon" msgstr "icône de téléchargement" -msgid "download image" -msgstr "image de téléchargement" - msgid "download schema as owl" msgstr "télécharger le schéma OWL" @@ -2141,6 +2143,12 @@ msgid "invalid date" msgstr "cette date n'est pas valide" +msgid "invalid float value" +msgstr "nombre flottant non valide" + +msgid "invalid integer value" +msgstr "nombre entier non valide" + msgid "is" msgstr "de type" @@ -3199,3 +3207,11 @@ msgid "you should probably delete that property" msgstr "vous devriez probablement supprimer cette propriété" + +#~ msgid "" +#~ "You have no access to this view or it's not applyable to current data" +#~ msgstr "" +#~ "Vous n'avez pas accès à cette vue ou elle ne s'applique pas aux données" + +#~ msgid "download image" +#~ msgstr "image de téléchargement" diff -r 8184bec7414d -r edfe43ceaa35 md5crypt.py --- a/md5crypt.py Wed Sep 02 16:42:07 2009 +0200 +++ b/md5crypt.py Thu Sep 10 08:13:22 2009 +0200 @@ -1,10 +1,8 @@ ######################################################### """ - +XXX clarify this header :organization: Logilab -:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr -:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses """ # md5crypt.py # @@ -58,8 +56,9 @@ v = v >> 6 return ret - def crypt(pw, salt, magic=None): + if isinstance(pw, unicode): + pw = pw.encode('utf-8') if magic is None: magic = MAGIC # Take care of the magic string if present diff -r 8184bec7414d -r edfe43ceaa35 req.py --- a/req.py Wed Sep 02 16:42:07 2009 +0200 +++ b/req.py Thu Sep 10 08:13:22 2009 +0200 @@ -234,18 +234,9 @@ userinfo['email'] = "" return userinfo user = self.actual_session().user - rql = "Any F,S,A where U eid %(x)s, U firstname F, U surname S, U primary_email E, E address A" - try: - firstname, lastname, email = self.execute(rql, {'x': user.eid}, 'x')[0] - if firstname is None and lastname is None: - userinfo['name'] = '' - else: - userinfo['name'] = ("%s %s" % (firstname, lastname)) - userinfo['email'] = email - except IndexError: - userinfo['name'] = None - userinfo['email'] = None userinfo['login'] = user.login + userinfo['name'] = user.name() + userinfo['email'] = user.get_email() return userinfo def is_internal_session(self): diff -r 8184bec7414d -r edfe43ceaa35 server/migractions.py --- a/server/migractions.py Wed Sep 02 16:42:07 2009 +0200 +++ b/server/migractions.py Thu Sep 10 08:13:22 2009 +0200 @@ -166,14 +166,20 @@ % (self.config.appid, backupfile)): return # unpack backup - bkup = tarfile.open(backupfile, 'r|gz') - for name in bkup.getnames(): - if name[0] in '/.': - raise Exception('Security check failed, path starts with "/" or "."') - bkup.close() # XXX seek error if not close+open !?! - bkup = tarfile.open(backupfile, 'r|gz') tmpdir = tempfile.mkdtemp() - bkup.extractall(path=tmpdir) + try: + bkup = tarfile.open(backupfile, 'r|gz') + except tarfile.ReadError: + # assume restoring old backup + shutil.copy(backupfile, osp.join(tmpdir, 'system')) + else: + for name in bkup.getnames(): + if name[0] in '/.': + raise Exception('Security check failed, path starts with "/" or "."') + bkup.close() # XXX seek error if not close+open !?! + bkup = tarfile.open(backupfile, 'r|gz') + bkup.extractall(path=tmpdir) + bkup.close() self.config.open_connections_pools = False repo = self.repo_connect() @@ -186,7 +192,6 @@ print '-> error trying to restore [%s]' % exc if not self.confirm('Continue anyway?', default='n'): raise SystemExit(1) - bkup.close() shutil.rmtree(tmpdir) # call hooks repo.open_connections_pools() diff -r 8184bec7414d -r edfe43ceaa35 server/querier.py --- a/server/querier.py Wed Sep 02 16:42:07 2009 +0200 +++ b/server/querier.py Thu Sep 10 08:13:22 2009 +0200 @@ -608,6 +608,8 @@ # return an empty result instead of raising UnknownEid return empty_rset(session, rql, args) cachekey.append(etype) + # ensure eid is correctly typed in args + args[key] = typed_eid(args[key]) cachekey = tuple(cachekey) else: cachekey = rql diff -r 8184bec7414d -r edfe43ceaa35 server/serverctl.py --- a/server/serverctl.py Wed Sep 02 16:42:07 2009 +0200 +++ b/server/serverctl.py Thu Sep 10 08:13:22 2009 +0200 @@ -312,7 +312,8 @@ # postgres specific stuff if driver == 'postgres': # install plpythonu/plpgsql language if not installed by the cube - for extlang in ('plpythonu', 'plpgsql'): + langs = ('plpgsql',) if sys.platform == 'win32' else ('plpythonu', 'plpgsql') + for extlang in langs: helper.create_language(cursor, extlang) cursor.close() cnx.commit() @@ -676,7 +677,8 @@ import tempfile srcappid = pop_arg(args, 1, msg='No source instance specified !') destappid = pop_arg(args, msg='No destination instance specified !') - output = tempfile.mkstemp(dir='/tmp/')[1] + fd, output = tempfile.mkstemp() + os.close(fd) if ':' in srcappid: host, srcappid = srcappid.split(':') _remote_dump(host, srcappid, output, self.config.sudo) diff -r 8184bec7414d -r edfe43ceaa35 server/session.py --- a/server/session.py Wed Sep 02 16:42:07 2009 +0200 +++ b/server/session.py Thu Sep 10 08:13:22 2009 +0200 @@ -75,6 +75,12 @@ return '<%ssession %s (%s 0x%x)>' % (self.cnxtype, self.user.login, self.id, id(self)) + def hijack_user(self, user): + """return a fake request/session using specified user""" + session = Session(user, self.repo) + session._threaddata = self.actual_session()._threaddata + return session + def _change_relation(self, cb, fromeid, rtype, toeid): if self.is_super_session: cb(self, fromeid, rtype, toeid) @@ -112,6 +118,8 @@ self._change_relation(self.repo.glob_delete_relation, fromeid, rtype, toeid) + # relations cache handling ################################################# + def update_rel_cache_add(self, subject, rtype, object, symetric=False): self._update_entity_rel_cache_add(subject, rtype, 'subject', object) if symetric: diff -r 8184bec7414d -r edfe43ceaa35 server/test/data/migratedapp/schema.py --- a/server/test/data/migratedapp/schema.py Wed Sep 02 16:42:07 2009 +0200 +++ b/server/test/data/migratedapp/schema.py Thu Sep 10 08:13:22 2009 +0200 @@ -50,9 +50,9 @@ 'PE require_permission P, P name "add_note", ' 'P require_group G'),)} + whatever = Int() # keep it before `date` for unittest_migraction.test_add_attribute_int date = Datetime() type = String(maxsize=1) - whatever = Int() mydate = Date(default='TODAY') shortpara = String(maxsize=64) ecrit_par = SubjectRelation('Personne', constraints=[RQLConstraint('S concerne A, O concerne A')]) diff -r 8184bec7414d -r edfe43ceaa35 server/test/unittest_extlite.py --- a/server/test/unittest_extlite.py Wed Sep 02 16:42:07 2009 +0200 +++ b/server/test/unittest_extlite.py Thu Sep 10 08:13:22 2009 +0200 @@ -24,35 +24,34 @@ self._cleanup() def test(self): - lock = threading.Lock() - + lock1 = threading.Lock() + lock2 = threading.Lock() + def run_thread(): cnx2 = get_connection('sqlite', database=self.sqlite_file) - lock.acquire() + lock1.acquire() cu = cnx2.cursor() cu.execute('SELECT name FROM toto') self.failIf(cu.fetchall()) cnx2.commit() - lock.release() - time.sleep(0.1) - lock.acquire() + lock1.release() + lock2.acquire() cu.execute('SELECT name FROM toto') self.failUnless(cu.fetchall()) - lock.release() + lock2.release() cnx1 = get_connection('sqlite', database=self.sqlite_file) - lock.acquire() + lock1.acquire() + lock2.acquire() thread = threading.Thread(target=run_thread) thread.start() cu = cnx1.cursor() cu.execute('SELECT name FROM toto') - lock.release() - time.sleep(0.1) + lock1.release() cnx1.commit() - lock.acquire() cu.execute("INSERT INTO toto(name) VALUES ('toto')") cnx1.commit() - lock.release() + lock2.release() if __name__ == '__main__': unittest_main() diff -r 8184bec7414d -r edfe43ceaa35 server/test/unittest_migractions.py --- a/server/test/unittest_migractions.py Wed Sep 02 16:42:07 2009 +0200 +++ b/server/test/unittest_migractions.py Thu Sep 10 08:13:22 2009 +0200 @@ -54,17 +54,24 @@ def test_add_attribute_int(self): self.failIf('whatever' in self.schema) - paraordernum = self.mh.rqlexec('Any O WHERE X name "Note", RT name "para", RDEF from_entity X, RDEF relation_type RT, RDEF ordernum O')[0][0] + orderdict = dict(self.mh.rqlexec('Any RTN, O WHERE X name "Note", RDEF from_entity X, ' + 'RDEF relation_type RT, RDEF ordernum O, RT name RTN')) self.mh.cmd_add_attribute('Note', 'whatever') self.failUnless('whatever' in self.schema) self.assertEquals(self.schema['whatever'].subjects(), ('Note',)) self.assertEquals(self.schema['whatever'].objects(), ('Int',)) - paraordernum2 = self.mh.rqlexec('Any O WHERE X name "Note", RT name "para", RDEF from_entity X, RDEF relation_type RT, RDEF ordernum O')[0][0] - self.assertEquals(paraordernum2, paraordernum+1) + orderdict2 = dict(self.mh.rqlexec('Any RTN, O WHERE X name "Note", RDEF from_entity X, ' + 'RDEF relation_type RT, RDEF ordernum O, RT name RTN')) + whateverorder = migrschema['whatever'].rproperty('Note', 'Int', 'order') + for k, v in orderdict.iteritems(): + if v >= whateverorder: + orderdict[k] = v+1 + orderdict['whatever'] = whateverorder + self.assertDictEquals(orderdict, orderdict2) #self.assertEquals([r.type for r in self.schema['Note'].ordered_relations()], # ['modification_date', 'creation_date', 'owned_by', # 'eid', 'ecrit_par', 'inline1', 'date', 'type', - # 'whatever', 'para', 'in_basket']) + # 'whatever', 'date', 'in_basket']) # NB: commit instead of rollback make following test fail with py2.5 # this sounds like a pysqlite/2.5 bug (the same eid is affected to # two different entities) @@ -373,7 +380,9 @@ self.mh.cmd_remove_cube('email', removedeps=True) # file was there because it's an email dependancy, should have been removed self.failIf('email' in self.config.cubes()) + self.failIf(self.config.cube_dir('email') in self.config.cubes_path()) self.failIf('file' in self.config.cubes()) + self.failIf(self.config.cube_dir('file') in self.config.cubes_path()) for ertype in ('Email', 'EmailThread', 'EmailPart', 'File', 'Image', 'sender', 'in_thread', 'reply_to', 'data_format'): self.failIf(ertype in schema, ertype) @@ -394,7 +403,9 @@ finally: self.mh.cmd_add_cube('email') self.failUnless('email' in self.config.cubes()) + self.failUnless(self.config.cube_dir('email') in self.config.cubes_path()) self.failUnless('file' in self.config.cubes()) + self.failUnless(self.config.cube_dir('file') in self.config.cubes_path()) for ertype in ('Email', 'EmailThread', 'EmailPart', 'File', 'Image', 'sender', 'in_thread', 'reply_to', 'data_format'): self.failUnless(ertype in schema, ertype) diff -r 8184bec7414d -r edfe43ceaa35 server/test/unittest_querier.py --- a/server/test/unittest_querier.py Wed Sep 02 16:42:07 2009 +0200 +++ b/server/test/unittest_querier.py Thu Sep 10 08:13:22 2009 +0200 @@ -213,6 +213,11 @@ # should return an empty result set self.failIf(self.execute('Any X WHERE X eid 99999999')) + def test_typed_eid(self): + # should return an empty result set + rset = self.execute('Any X WHERE X eid %(x)s', {'x': '1'}, 'x') + self.assertIsInstance(rset[0][0], (int, long)) + def test_bytes_storage(self): feid = self.execute('INSERT File X: X name "foo.pdf", X data_format "text/plain", X data %(data)s', {'data': Binary("xxx")})[0][0] diff -r 8184bec7414d -r edfe43ceaa35 sobjects/notification.py --- a/sobjects/notification.py Wed Sep 02 16:42:07 2009 +0200 +++ b/sobjects/notification.py Thu Sep 10 08:13:22 2009 +0200 @@ -115,7 +115,7 @@ def subject(self): entity = self.rset.get_entity(self.row or 0, self.col or 0) return u'%s #%s (%s)' % (self.req.__('New %s' % entity.e_schema), - entity.eid, self.user_login()) + entity.eid, self.user_data['login']) from logilab.common.deprecation import class_renamed, class_moved, deprecated diff -r 8184bec7414d -r edfe43ceaa35 sobjects/supervising.py diff -r 8184bec7414d -r edfe43ceaa35 test/unittest_entity.py --- a/test/unittest_entity.py Wed Sep 02 16:42:07 2009 +0200 +++ b/test/unittest_entity.py Thu Sep 10 08:13:22 2009 +0200 @@ -182,15 +182,16 @@ from cubicweb.entities import fetch_config Personne = self.vreg['etypes'].etype_class('Personne') Note = self.vreg['etypes'].etype_class('Note') + self.failUnless(issubclass(self.vreg['etypes'].etype_class('SubNote'), Note)) Personne.fetch_attrs, Personne.fetch_order = fetch_config(('nom', 'type')) Note.fetch_attrs, Note.fetch_order = fetch_config(('type',)) - aff = self.add_entity('Personne', nom=u'pouet') - self.assertEquals(aff.related_rql('evaluee'), + p = self.add_entity('Personne', nom=u'pouet') + self.assertEquals(p.related_rql('evaluee'), 'Any X,AA,AB ORDERBY AA ASC WHERE E eid %(x)s, E evaluee X, ' 'X type AA, X modification_date AB') Personne.fetch_attrs, Personne.fetch_order = fetch_config(('nom', )) # XXX - self.assertEquals(aff.related_rql('evaluee'), + 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') def test_entity_unrelated(self): diff -r 8184bec7414d -r edfe43ceaa35 test/unittest_utils.py --- a/test/unittest_utils.py Wed Sep 02 16:42:07 2009 +0200 +++ b/test/unittest_utils.py Thu Sep 10 08:13:22 2009 +0200 @@ -17,12 +17,12 @@ self.assertNotEquals(make_uid('xyz'), make_uid('xyz')) def test_2(self): - d = {} + d = set() while len(d)<10000: uid = make_uid('xyz') - if d.has_key(uid): + if uid in d: self.fail(len(d)) - d[uid] = 1 + d.add(uid) class UStringIOTC(TestCase): diff -r 8184bec7414d -r edfe43ceaa35 toolsutils.py --- a/toolsutils.py Wed Sep 02 16:42:07 2009 +0200 +++ b/toolsutils.py Thu Sep 10 08:13:22 2009 +0200 @@ -10,9 +10,15 @@ # XXX move most of this in logilab.common (shellutils ?) import os, sys -from os import listdir, makedirs, symlink, environ, chmod, walk, remove +from os import listdir, makedirs, environ, chmod, walk, remove from os.path import exists, join, abspath, normpath +try: + from os import symlink +except ImportError: + def symlink(*args): + raise NotImplementedError + from logilab.common.clcommands import Command as BaseCommand, \ main_run as base_main_run from logilab.common.compat import any diff -r 8184bec7414d -r edfe43ceaa35 utils.py --- a/utils.py Wed Sep 02 16:42:07 2009 +0200 +++ b/utils.py Thu Sep 10 08:13:22 2009 +0200 @@ -7,8 +7,11 @@ """ __docformat__ = "restructuredtext en" +from logilab.mtconverter import xml_escape + import locale from md5 import md5 +import sys from datetime import datetime, timedelta, date from time import time, mktime from random import randint, seed @@ -101,11 +104,17 @@ encoding = locale.getpreferredencoding(do_setlocale=False) or 'UTF-8' return unicode(date.strftime(str(fmt)), encoding) -def make_uid(key): - """forge a unique identifier""" - msg = str(key) + "%.10f" % time() + str(randint(0, 1000000)) - return md5(msg).hexdigest() +if sys.version_info[:2] < (2, 5): + def make_uid(key): + """forge a unique identifier + not that unique on win32""" + msg = str(key) + "%.10f" % time() + str(randint(0, 1000000)) + return md5(msg).hexdigest() +else: + from uuid import uuid4 + def make_uid(key): + return str(key) + str(uuid4()) def dump_class(cls, clsname): """create copy of a class by creating an empty class inheriting @@ -262,17 +271,18 @@ # 2/ css files for cssfile, media in self.cssfiles: w(u'\n' % - (media, cssfile)) + (media, xml_escape(cssfile))) # 3/ ie css if necessary if self.ie_cssfiles: w(u' \n') # 4/ js files for jsfile in self.jsfiles: - w(u'\n' % jsfile) + w(u'\n' % + xml_escape(jsfile)) # 5/ post inlined scripts (i.e. scripts depending on other JS files) if self.post_inlined_scripts: w(u'