prepare new version
authorymh <ymh.work@gmail.com>
Mon, 02 May 2011 17:47:03 +0200
changeset 74 c18c0211348b
parent 73 acba3bceebf3
child 75 cd12d9d72ca2
prepare new version
sbin/create_python_env.py
sbin/res/lib/patch.py
sbin/res/patch/pyxml.patch
sbin/res/src/4Suite-XML-1.0.2.tar.bz2
sbin/res/src/Imaging-1.1.7.tar.gz
sbin/res/src/PyXML-0.8.4.tar.gz
sbin/res/src/distribute-0.6.13.tar.gz
sbin/res/src/psycopg2-2.2.1.tar.gz
sbin/res/src/pylucene-3.0.1-1-src.tar.gz
sbin/sync_blinkster_venv
virtualenv/res/src/ldt-0.4.tar.gz
web/blinkster/__init__.py
--- a/sbin/create_python_env.py	Mon May 02 12:30:49 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,286 +0,0 @@
-"""
-Call this like ``python create_python_env.py``; it will
-refresh the project-boot.py script
-
--prerequisite:
-
-- virtualenv
-- distribute
-- psycopg2 requires the PostgreSQL libpq libraries and the pg_config utility
-
-- virtualenv --distribute --no-site-packages venv
-- python project-boot.py --distribute --no-site-packages --index-url=http://pypi.websushi.org/ --clear bvenv
-
-"""
-
-import os
-import subprocess
-import re
-import sys
-
-
-here = os.path.dirname(os.path.abspath(__file__))
-base_dir = here
-script_name = os.path.join(base_dir, 'project-boot.py')
-
-import virtualenv
-
-# things to install
-# - psycopg2 -> pip
-# - PIL -> pip
-# - pyxml -> pip
-# - 4Suite-xml - easy_install ftp://ftp.4suite.org/pub/4Suite/4Suite-XML-1.0.2.tar.bz2
-# - pylucene  - script
-
-src_base = os.path.join(here,"res","src")
-lib_path = os.path.abspath(os.path.join(here,"res","lib"))
-patch_path = os.path.abspath(os.path.join(here,"res","patch"))
-
-EXTRA_TEXT  = "URLS = { \n"
-
-EXTRA_TEXT += "    'DISTRIBUTE' : { 'setup': 'distribute', 'url': 'http://pypi.python.org/packages/source/d/distribute/distribute-0.6.13.tar.gz', 'local': '"+ os.path.abspath(os.path.join(src_base,"distribute-0.6.13.tar.gz"))+"'},\n"
-EXTRA_TEXT += "    'PSYCOPG2' : { 'setup': 'psycopg2','url': 'http://initd.org/pub/software/psycopg/psycopg2-2.2.1.tar.gz', 'local': '"+ os.path.abspath(os.path.join(src_base,"psycopg2-2.2.1.tar.gz"))+"'},\n"
-EXTRA_TEXT += "    'FOURSUITE_XML' : { 'setup': '4Suite-XML', 'url': 'ftp://ftp.4suite.org/pub/4Suite/4Suite-XML-1.0.2.tar.bz2', 'local': '"+ os.path.abspath(os.path.join(src_base,"4Suite-XML-1.0.2.tar.bz2"))+"'},\n"
-EXTRA_TEXT += "    'PYLUCENE' : { 'setup': 'http://apache.crihan.fr/dist/lucene/pylucene/pylucene-3.0.1-1-src.tar.gz', 'url': 'http://apache.crihan.fr/dist/lucene/pylucene/pylucene-3.0.1-1-src.tar.gz', 'local': '"+ os.path.abspath(os.path.join(src_base,"pylucene-3.0.1-1-src.tar.gz"))+"'},\n"
-EXTRA_TEXT += "    'PIL' : { 'setup': 'pil', 'url': 'http://effbot.org/downloads/Imaging-1.1.7.tar.gz', 'local': '"+ os.path.abspath(os.path.join(src_base,"Imaging-1.1.7.tar.gz"))+"'},\n"
-EXTRA_TEXT += "    'PYXML' : { 'setup': 'http://sourceforge.net/projects/pyxml/files/pyxml/0.8.4/PyXML-0.8.4.tar.gz/download', 'url': 'http://sourceforge.net/projects/pyxml/files/pyxml/0.8.4/PyXML-0.8.4.tar.gz/download', 'local': '"+ os.path.abspath(os.path.join(src_base,"PyXML-0.8.4.tar.gz"))+"', 'patch': '"+os.path.join(patch_path,"pyxml.patch")+"'},\n"
-
-EXTRA_TEXT += "}\n"
-
-EXTRA_TEXT += "import sys\n"
-EXTRA_TEXT += "sys.path.append('"+lib_path+"')\n"
-
-EXTRA_TEXT += """
-
-import shutil
-import tarfile
-import urllib
-import platform
-import patch
-import zipfile
-from distutils.sysconfig import get_python_lib
-
-
-INDEX_URL = 'http://pypi.python.org/simple/'
-
-
-def extend_parser(parser):
-    parser.add_option(
-        '--index-url',
-        metavar='INDEX_URL',
-        dest='index_url',
-        default='',
-        help='base URL of Python Package Index')
-    parser.add_option(
-        '--type-install',
-        metavar='type_install',
-        dest='type_install',
-        default='local',
-        help='type install : local, url, setup')
-
-
-def adjust_options(options, args):
-    pass
-
-
-def after_install(options, home_dir):
-    home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir)
-    base_dir = os.path.dirname(home_dir)
-    src_dir = join(home_dir, 'src')
-    tmp_dir = join(home_dir, 'tmp')
-    ensure_dir(src_dir)
-    ensure_dir(tmp_dir)
-    system_str = platform.system()
-    python_lib_dir = get_python_lib()
-    
-    res_source_key = options.type_install
-    
-    logger.indent += 2
-    try:
-        
-        #get pylucene
-        logger.notify("Get Pylucene from %s " % URLS['PYLUCENE'][res_source_key])
-        pylucene_src = os.path.join(src_dir,"pylucene.tar.gz")
-        urllib.urlretrieve(URLS['PYLUCENE'][res_source_key], pylucene_src)
-        tf = tarfile.open(pylucene_src,'r:gz')
-        pylucene_base_path = os.path.join(src_dir,"pylucene") 
-        logger.notify("Extract Pylucene to %s " % pylucene_base_path)
-        tf.extractall(pylucene_base_path)
-        tf.close()
-        
-        pylucene_src_path = os.path.join(pylucene_base_path, os.listdir(pylucene_base_path)[0])
-        jcc_src_path = os.path.abspath(os.path.join(pylucene_src_path,"jcc"))
-        
-        #install jcc
-
-        #patch for linux
-        if system_str == 'Linux' :
-            olddir = os.getcwd()
-            patch_dest_path = os.path.join(lib_dir, 'site-packages','setuptools-0.6c11-py'+'%s.%s' % (sys.version_info[0], sys.version_info[1])+'.egg')
-            
-            if os.path.isfile(patch_dest_path):
-                # must unzip egg
-                # rename file and etract all
-                shutil.move(patch_dest_path, patch_dest_path + ".zip")
-                zf = zipfile.ZipFile(patch_dest_path + ".zip",'r')
-                zf.extractall(patch_dest_path)
-                os.remove(patch_dest_path + ".zip")
-            logger.notify("Patch jcc : %s " % (patch_dest_path))
-            os.chdir(patch_dest_path)
-            p = patch.fromfile(os.path.join(jcc_src_path,"jcc","patches","patch.43.0.6c11"))
-            p.apply()
-            os.chdir(olddir)
-
-        logger.notify("Install jcc")
-        call_subprocess([os.path.abspath(os.path.join(home_dir, 'bin', 'python')), 'setup.py', 'install'],
-                        cwd=jcc_src_path,
-                        filter_stdout=filter_python_develop,
-                        show_stdout=True)
-        #install pylucene
-        
-        logger.notify("Install pylucene")
-        #modify makefile
-        makefile_path = os.path.join(pylucene_src_path,"Makefile")
-        logger.notify("Modify makefile %s " % makefile_path)
-        shutil.move( makefile_path, makefile_path+"~" )
-
-        destination= open( makefile_path, "w" )
-        source= open( makefile_path+"~", "r" )
-        destination.write("PREFIX_PYTHON="+os.path.abspath(home_dir)+"\\n")
-        destination.write("ANT=ant\\n")
-        destination.write("PYTHON=$(PREFIX_PYTHON)/bin/python\\n")
-        
-        if system_str == "Darwin":
-            if sys.version_info >= (2,6):
-                destination.write("JCC=$(PYTHON) -m jcc.__main__ --shared --arch x86_64 --arch i386\\n")
-            else:
-                destination.write("JCC=$(PYTHON) -m jcc --shared\\n")
-            destination.write("NUM_FILES=2\\n")
-        elif system_str == "Windows":
-            destination.write("JCC=$(PYTHON) -m jcc.__main__ --shared --arch x86_64 --arch i386\\n")
-            destination.write("NUM_FILES=2\\n")
-        else:
-            if sys.version_info >= (2,6) and sys.version_info < (2,7):
-                destination.write("JCC=$(PYTHON) -m jcc.__main__ --shared\\n")
-            else:
-                destination.write("JCC=$(PYTHON) -m jcc --shared\\n")
-            destination.write("NUM_FILES=2\\n")
-        for line in source:
-            destination.write( line )
-        source.close()
-        destination.close()
-        os.remove(makefile_path+"~" )
-
-        logger.notify("pylucene make")
-        call_subprocess(['make'],
-                        cwd=os.path.abspath(pylucene_src_path),
-                        filter_stdout=filter_python_develop,
-                        show_stdout=True)
-
-        logger.notify("pylucene make install")
-        call_subprocess(['make', 'install'],
-                        cwd=os.path.abspath(pylucene_src_path),
-                        filter_stdout=filter_python_develop,
-                        show_stdout=True)
-
-        logger.notify("PyXML install : %s " % URLS['PYXML'][res_source_key])
-        if sys.version_info >= (2,6):
-            logger.notify("PyXML -> python version >= 2.6 : patching")
-            pyxml_src = os.path.join(src_dir,"pyxml.tar.gz")
-            urllib.urlretrieve(URLS['PYXML'][res_source_key], pyxml_src)
-            logger.notify("PyXML -> python version >= 2.6 : extract archive")
-            tf = tarfile.open(pyxml_src,'r:gz')
-            pyxml_base_path = os.path.join(src_dir,"pyxml") 
-            tf.extractall(pyxml_base_path)
-            tf.close()
-
-            #patch
-            pyxml_version = os.listdir(pyxml_base_path)[0]            
-            pyxml_path = os.path.join(pyxml_base_path, pyxml_version)
-            olddir = os.getcwd()
-            os.chdir(pyxml_path)
-            logger.notify("PyXML -> python version >= 2.6 : do patch %s : %s " % (pyxml_path, URLS['PYXML']['patch']))
-            p = patch.fromfile(URLS['PYXML']['patch'])
-            p.apply()
-            os.chdir(olddir)
-            logger.notify("PyXML -> python version >= 2.6 : install")
-            call_subprocess([os.path.abspath(os.path.join(home_dir, 'bin', 'pip')), 'install', '-E', os.path.abspath(home_dir), '--build='+os.path.abspath(pyxml_base_path), '--no-download', pyxml_version],
-                    cwd=os.path.abspath(tmp_dir),
-                    filter_stdout=filter_python_develop,
-                    show_stdout=True)
-        else:
-            call_subprocess([os.path.abspath(os.path.join(home_dir, 'bin', 'pip')), 'install', '-E', os.path.abspath(home_dir), URLS['PYXML'][res_source_key]],
-                    cwd=os.path.abspath(tmp_dir),
-                    filter_stdout=filter_python_develop,
-                    show_stdout=True)
-        
-        logger.notify("Install Distribute from %s" % URLS['DISTRIBUTE'][res_source_key])
-        call_subprocess([os.path.abspath(os.path.join(home_dir, 'bin', 'pip')), 'install', '-E', os.path.abspath(home_dir), URLS['DISTRIBUTE'][res_source_key]],
-                        cwd=os.path.abspath(tmp_dir),
-                        filter_stdout=filter_python_develop,
-                        show_stdout=True)
-                        
-        logger.notify("Install Psycopg2 from %s" % URLS['PSYCOPG2'][res_source_key])
-        call_subprocess([os.path.abspath(os.path.join(home_dir, 'bin', 'pip')), 'install', '-E', os.path.abspath(home_dir), URLS['PSYCOPG2'][res_source_key]],
-                        cwd=os.path.abspath(tmp_dir),
-                        filter_stdout=filter_python_develop,
-                        show_stdout=True)
-
-        logger.notify("Install PIL from %s" % URLS['PIL'][res_source_key])
-        call_subprocess([os.path.abspath(os.path.join(home_dir, 'bin', 'pip')), 'install', '-E', os.path.abspath(home_dir), URLS['PIL'][res_source_key]],
-                        cwd=os.path.abspath(tmp_dir),
-                        filter_stdout=filter_python_develop,
-                        show_stdout=True)
-                        
-        logger.notify("Install 4Suite-XML from %s" % URLS['FOURSUITE_XML'][res_source_key])
-        call_subprocess([os.path.abspath(os.path.join(home_dir, 'bin', 'easy_install')), URLS['FOURSUITE_XML'][res_source_key]],
-                        cwd=os.path.abspath(tmp_dir),
-                        filter_stdout=filter_python_develop,
-                        show_stdout=True)
-                        
-        logger.notify("Clear source dir")
-        shutil.rmtree(src_dir)
-
-    finally:
-        logger.indent -= 2
-    script_dir = join(base_dir, 'bin')
-    logger.notify('Run "%s Package" to install new packages that provide builds'
-                  % join(script_dir, 'easy_install'))
-
-def ensure_dir(dir):
-    if not os.path.exists(dir):
-        logger.notify('Creating directory %s' % dir)
-        os.makedirs(dir)
-
-def filter_python_develop(line):
-    if not line.strip():
-        return Logger.DEBUG
-    for prefix in ['Searching for', 'Reading ', 'Best match: ', 'Processing ',
-                   'Moving ', 'Adding ', 'running ', 'writing ', 'Creating ',
-                   'creating ', 'Copying ']:
-        if line.startswith(prefix):
-            return Logger.DEBUG
-    return Logger.NOTIFY
-"""
-
-def main():
-    python_version = ".".join(map(str,sys.version_info[0:2]))
-    text = virtualenv.create_bootstrap_script(EXTRA_TEXT, python_version=python_version)
-    if os.path.exists(script_name):
-        f = open(script_name)
-        cur_text = f.read()
-        f.close()
-    else:
-        cur_text = ''
-    print 'Updating %s' % script_name
-    if cur_text == 'text':
-        print 'No update'
-    else:
-        print 'Script changed; updating...'
-        f = open(script_name, 'w')
-        f.write(text)
-        f.close()
-
-if __name__ == '__main__':
-    main()
-
--- a/sbin/res/lib/patch.py	Mon May 02 12:30:49 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,589 +0,0 @@
-""" Patch utility to apply unified diffs
-
-    Brute-force line-by-line non-recursive parsing 
-
-    Copyright (c) 2008-2010 anatoly techtonik
-    Available under the terms of MIT license
-
-    Project home: http://code.google.com/p/python-patch/
-
-
-    $Id: patch.py 76 2010-04-08 19:10:21Z techtonik $
-    $HeadURL: https://python-patch.googlecode.com/svn/trunk/patch.py $
-"""
-
-__author__ = "techtonik.rainforce.org"
-__version__ = "10.04"
-
-import copy
-import logging
-import re
-# cStringIO doesn't support unicode in 2.5
-from StringIO import StringIO
-from logging import debug, info, warning
-
-from os.path import exists, isfile, abspath
-from os import unlink
-
-
-#------------------------------------------------
-# Logging is controlled by "python_patch" logger
-
-debugmode = False
-
-logger = logging.getLogger("python_patch")
-loghandler = logging.StreamHandler()
-logger.addHandler(loghandler)
-
-debug = logger.debug
-info = logger.info
-warning = logger.warning
-
-#: disable library logging by default
-logger.setLevel(logging.CRITICAL)
-
-#------------------------------------------------
-
-
-def fromfile(filename):
-  """ Parse patch file and return Patch() object
-  """
-
-  info("reading patch from file %s" % filename)
-  fp = open(filename, "rb")
-  patch = Patch(fp)
-  fp.close()
-  return patch
-
-
-def fromstring(s):
-  """ Parse text string and return Patch() object
-  """
-
-  return Patch(
-           StringIO.StringIO(s)    
-         )
-
-
-
-class HunkInfo(object):
-  """ Parsed hunk data container (hunk starts with @@ -R +R @@) """
-
-  def __init__(self):
-    self.startsrc=None #: line count starts with 1
-    self.linessrc=None
-    self.starttgt=None
-    self.linestgt=None
-    self.invalid=False
-    self.text=[]
-
-  def copy(self):
-    return copy.copy(self)
-
-#  def apply(self, estream):
-#    """ write hunk data into enumerable stream
-#        return strings one by one until hunk is
-#        over
-#
-#        enumerable stream are tuples (lineno, line)
-#        where lineno starts with 0
-#    """
-#    pass
-
-
-
-class Patch(object):
-
-  def __init__(self, stream=None):
-
-    # define Patch data members
-    # table with a row for every source file
-
-    #: list of source filenames
-    self.source=None
-    self.target=None
-    #: list of lists of hunks
-    self.hunks=None
-    #: file endings statistics for every hunk
-    self.hunkends=None
-
-    if stream:
-      self.parse(stream)
-
-  def copy(self):
-    return copy.copy(self)
-
-  def parse(self, stream):
-    """ parse unified diff """
-    self.source = []
-    self.target = []
-    self.hunks = []
-    self.hunkends = []
-
-    # define possible file regions that will direct the parser flow
-    header = False    # comments before the patch body
-    filenames = False # lines starting with --- and +++
-
-    hunkhead = False  # @@ -R +R @@ sequence
-    hunkbody = False  #
-    hunkskip = False  # skipping invalid hunk mode
-
-    header = True
-    lineends = dict(lf=0, crlf=0, cr=0)
-    nextfileno = 0
-    nexthunkno = 0    #: even if index starts with 0 user messages number hunks from 1
-
-    # hunkinfo holds parsed values, hunkactual - calculated
-    hunkinfo = HunkInfo()
-    hunkactual = dict(linessrc=None, linestgt=None)
-
-    fe = enumerate(stream)
-    for lineno, line in fe:
-
-      # analyze state
-      if header and line.startswith("--- "):
-        header = False
-        # switch to filenames state
-        filenames = True
-      #: skip hunkskip and hunkbody code until you read definition of hunkhead
-      if hunkbody:
-        # process line first
-        if re.match(r"^[- \+\\]", line):
-            # gather stats about line endings
-            if line.endswith("\r\n"):
-              self.hunkends[nextfileno-1]["crlf"] += 1
-            elif line.endswith("\n"):
-              self.hunkends[nextfileno-1]["lf"] += 1
-            elif line.endswith("\r"):
-              self.hunkends[nextfileno-1]["cr"] += 1
-              
-            if line.startswith("-"):
-              hunkactual["linessrc"] += 1
-            elif line.startswith("+"):
-              hunkactual["linestgt"] += 1
-            elif not line.startswith("\\"):
-              hunkactual["linessrc"] += 1
-              hunkactual["linestgt"] += 1
-            hunkinfo.text.append(line)
-            # todo: handle \ No newline cases
-        else:
-            warning("invalid hunk no.%d at %d for target file %s" % (nexthunkno, lineno+1, self.target[nextfileno-1]))
-            # add hunk status node
-            self.hunks[nextfileno-1].append(hunkinfo.copy())
-            self.hunks[nextfileno-1][nexthunkno-1]["invalid"] = True
-            # switch to hunkskip state
-            hunkbody = False
-            hunkskip = True
-
-        # check exit conditions
-        if hunkactual["linessrc"] > hunkinfo.linessrc or hunkactual["linestgt"] > hunkinfo.linestgt:
-            warning("extra hunk no.%d lines at %d for target %s" % (nexthunkno, lineno+1, self.target[nextfileno-1]))
-            # add hunk status node
-            self.hunks[nextfileno-1].append(hunkinfo.copy())
-            self.hunks[nextfileno-1][nexthunkno-1]["invalid"] = True
-            # switch to hunkskip state
-            hunkbody = False
-            hunkskip = True
-        elif hunkinfo.linessrc == hunkactual["linessrc"] and hunkinfo.linestgt == hunkactual["linestgt"]:
-            self.hunks[nextfileno-1].append(hunkinfo.copy())
-            # switch to hunkskip state
-            hunkbody = False
-            hunkskip = True
-
-            # detect mixed window/unix line ends
-            ends = self.hunkends[nextfileno-1]
-            if ((ends["cr"]!=0) + (ends["crlf"]!=0) + (ends["lf"]!=0)) > 1:
-              warning("inconsistent line ends in patch hunks for %s" % self.source[nextfileno-1])
-            if debugmode:
-              debuglines = dict(ends)
-              debuglines.update(file=self.target[nextfileno-1], hunk=nexthunkno)
-              debug("crlf: %(crlf)d  lf: %(lf)d  cr: %(cr)d\t - file: %(file)s hunk: %(hunk)d" % debuglines)
-
-      if hunkskip:
-        match = re.match("^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))?", line)
-        if match:
-          # switch to hunkhead state
-          hunkskip = False
-          hunkhead = True
-        elif line.startswith("--- "):
-          # switch to filenames state
-          hunkskip = False
-          filenames = True
-          if debugmode and len(self.source) > 0:
-            debug("- %2d hunks for %s" % (len(self.hunks[nextfileno-1]), self.source[nextfileno-1]))
-
-      if filenames:
-        if line.startswith("--- "):
-          if nextfileno in self.source:
-            warning("skipping invalid patch for %s" % self.source[nextfileno])
-            del self.source[nextfileno]
-            # double source filename line is encountered
-            # attempt to restart from this second line
-          re_filename = "^--- ([^\t]+)"
-          match = re.match(re_filename, line)
-          # todo: support spaces in filenames
-          if match:
-            self.source.append(match.group(1).strip())
-          else:
-            warning("skipping invalid filename at line %d" % lineno)
-            # switch back to header state
-            filenames = False
-            header = True
-        elif not line.startswith("+++ "):
-          if nextfileno in self.source:
-            warning("skipping invalid patch with no target for %s" % self.source[nextfileno])
-            del self.source[nextfileno]
-          else:
-            # this should be unreachable
-            warning("skipping invalid target patch")
-          filenames = False
-          header = True
-        else:
-          if nextfileno in self.target:
-            warning("skipping invalid patch - double target at line %d" % lineno)
-            del self.source[nextfileno]
-            del self.target[nextfileno]
-            nextfileno -= 1
-            # double target filename line is encountered
-            # switch back to header state
-            filenames = False
-            header = True
-          else:
-            re_filename = "^\+\+\+ ([^\t]+)"
-            match = re.match(re_filename, line)
-            if not match:
-              warning("skipping invalid patch - no target filename at line %d" % lineno)
-              # switch back to header state
-              filenames = False
-              header = True
-            else:
-              self.target.append(match.group(1).strip())
-              nextfileno += 1
-              # switch to hunkhead state
-              filenames = False
-              hunkhead = True
-              nexthunkno = 0
-              self.hunks.append([])
-              self.hunkends.append(lineends.copy())
-              continue
-
-      if hunkhead:
-        match = re.match("^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))?", line)
-        if not match:
-          if nextfileno-1 not in self.hunks:
-            warning("skipping invalid patch with no hunks for file %s" % self.target[nextfileno-1])
-            # switch to header state
-            hunkhead = False
-            header = True
-            continue
-          else:
-            # switch to header state
-            hunkhead = False
-            header = True
-        else:
-          hunkinfo.startsrc = int(match.group(1))
-          hunkinfo.linessrc = 1
-          if match.group(3): hunkinfo.linessrc = int(match.group(3))
-          hunkinfo.starttgt = int(match.group(4))
-          hunkinfo.linestgt = 1
-          if match.group(6): hunkinfo.linestgt = int(match.group(6))
-          hunkinfo.invalid = False
-          hunkinfo.text = []
-
-          hunkactual["linessrc"] = hunkactual["linestgt"] = 0
-
-          # switch to hunkbody state
-          hunkhead = False
-          hunkbody = True
-          nexthunkno += 1
-          continue
-    else:
-      if not hunkskip:
-        warning("patch file incomplete - %s" % filename)
-        # sys.exit(?)
-      else:
-        # duplicated message when an eof is reached
-        if debugmode and len(self.source) > 0:
-            debug("- %2d hunks for %s" % (len(self.hunks[nextfileno-1]), self.source[nextfileno-1]))
-
-    info("total files: %d  total hunks: %d" % (len(self.source), sum(len(hset) for hset in self.hunks)))
-
-
-  def apply(self):
-    """ apply parsed patch """
-
-    total = len(self.source)
-    for fileno, filename in enumerate(self.source):
-
-      f2patch = filename
-      if not exists(f2patch):
-        f2patch = self.target[fileno]
-        if not exists(f2patch):
-          warning("source/target file does not exist\n--- %s\n+++ %s" % (filename, f2patch))
-          continue
-      if not isfile(f2patch):
-        warning("not a file - %s" % f2patch)
-        continue
-      filename = f2patch
-
-      info("processing %d/%d:\t %s" % (fileno+1, total, filename))
-
-      # validate before patching
-      f2fp = open(filename)
-      hunkno = 0
-      hunk = self.hunks[fileno][hunkno]
-      hunkfind = []
-      hunkreplace = []
-      validhunks = 0
-      canpatch = False
-      for lineno, line in enumerate(f2fp):
-        if lineno+1 < hunk.startsrc:
-          continue
-        elif lineno+1 == hunk.startsrc:
-          hunkfind = [x[1:].rstrip("\r\n") for x in hunk.text if x[0] in " -"]
-          hunkreplace = [x[1:].rstrip("\r\n") for x in hunk.text if x[0] in " +"]
-          #pprint(hunkreplace)
-          hunklineno = 0
-
-          # todo \ No newline at end of file
-
-        # check hunks in source file
-        if lineno+1 < hunk.startsrc+len(hunkfind)-1:
-          if line.rstrip("\r\n") == hunkfind[hunklineno]:
-            hunklineno+=1
-          else:
-            debug("hunk no.%d doesn't match source file %s" % (hunkno+1, filename))
-            # file may be already patched, but we will check other hunks anyway
-            hunkno += 1
-            if hunkno < len(self.hunks[fileno]):
-              hunk = self.hunks[fileno][hunkno]
-              continue
-            else:
-              break
-
-        # check if processed line is the last line
-        if lineno+1 == hunk.startsrc+len(hunkfind)-1:
-          debug("file %s hunk no.%d -- is ready to be patched" % (filename, hunkno+1))
-          hunkno+=1
-          validhunks+=1
-          if hunkno < len(self.hunks[fileno]):
-            hunk = self.hunks[fileno][hunkno]
-          else:
-            if validhunks == len(self.hunks[fileno]):
-              # patch file
-              canpatch = True
-              break
-      else:
-        if hunkno < len(self.hunks[fileno]):
-          warning("premature end of source file %s at hunk %d" % (filename, hunkno+1))
-
-      f2fp.close()
-
-      if validhunks < len(self.hunks[fileno]):
-        if self._match_file_hunks(filename, self.hunks[fileno]):
-          warning("already patched  %s" % filename)
-        else:
-          warning("source file is different - %s" % filename)
-      if canpatch:
-        backupname = filename+".orig"
-        if exists(backupname):
-          warning("can't backup original file to %s - aborting" % backupname)
-        else:
-          import shutil
-          shutil.move(filename, backupname)
-          if self.write_hunks(backupname, filename, self.hunks[fileno]):
-            warning("successfully patched %s" % filename)
-            unlink(backupname)
-          else:
-            warning("error patching file %s" % filename)
-            shutil.copy(filename, filename+".invalid")
-            warning("invalid version is saved to %s" % filename+".invalid")
-            # todo: proper rejects
-            shutil.move(backupname, filename)
-
-    # todo: check for premature eof
-
-
-  def can_patch(self, filename):
-    """ Check if specified filename can be patched. Returns None if file can
-    not be found among source filenames. False if patch can not be applied
-    clearly. True otherwise.
-
-    :returns: True, False or None
-    """
-    idx = self._get_file_idx(filename, source=True)
-    if idx == None:
-      return None
-    return self._match_file_hunks(filename, self.hunks[idx])
-    
-
-  def _match_file_hunks(self, filepath, hunks):
-    matched = True
-    fp = open(abspath(filepath))
-
-    class NoMatch(Exception):
-      pass
-
-    lineno = 1
-    line = fp.readline()
-    hno = None
-    try:
-      for hno, h in enumerate(hunks):
-        # skip to first line of the hunk
-        while lineno < h.starttgt:
-          if not len(line): # eof
-            debug("check failed - premature eof before hunk: %d" % (hno+1))
-            raise NoMatch
-          line = fp.readline()
-          lineno += 1
-        for hline in h.text:
-          if hline.startswith("-"):
-            continue
-          if not len(line):
-            debug("check failed - premature eof on hunk: %d" % (hno+1))
-            # todo: \ No newline at the end of file
-            raise NoMatch
-          if line.rstrip("\r\n") != hline[1:].rstrip("\r\n"):
-            debug("file is not patched - failed hunk: %d" % (hno+1))
-            raise NoMatch
-          line = fp.readline()
-          lineno += 1
-
-    except NoMatch:
-      matched = False
-      # todo: display failed hunk, i.e. expected/found
-
-    fp.close()
-    return matched
-
-
-  def patch_stream(self, instream, hunks):
-    """ Generator that yields stream patched with hunks iterable
-    
-        Converts lineends in hunk lines to the best suitable format
-        autodetected from input
-    """
-
-    # todo: At the moment substituted lineends may not be the same
-    #       at the start and at the end of patching. Also issue a
-    #       warning/throw about mixed lineends (is it really needed?)
-
-    hunks = iter(hunks)
-
-    srclineno = 1
-
-    lineends = {'\n':0, '\r\n':0, '\r':0}
-    def get_line():
-      """
-      local utility function - return line from source stream
-      collecting line end statistics on the way
-      """
-      line = instream.readline()
-        # 'U' mode works only with text files
-      if line.endswith("\r\n"):
-        lineends["\r\n"] += 1
-      elif line.endswith("\n"):
-        lineends["\n"] += 1
-      elif line.endswith("\r"):
-        lineends["\r"] += 1
-      return line
-
-    for hno, h in enumerate(hunks):
-      debug("hunk %d" % (hno+1))
-      # skip to line just before hunk starts
-      while srclineno < h.startsrc:
-        yield get_line()
-        srclineno += 1
-
-      for hline in h.text:
-        # todo: check \ No newline at the end of file
-        if hline.startswith("-") or hline.startswith("\\"):
-          get_line()
-          srclineno += 1
-          continue
-        else:
-          if not hline.startswith("+"):
-            get_line()
-            srclineno += 1
-          line2write = hline[1:]
-          # detect if line ends are consistent in source file
-          if sum([bool(lineends[x]) for x in lineends]) == 1:
-            newline = [x for x in lineends if lineends[x] != 0][0]
-            yield line2write.rstrip("\r\n")+newline
-          else: # newlines are mixed
-            yield line2write
-     
-    for line in instream:
-      yield line
-
-
-  def write_hunks(self, srcname, tgtname, hunks):
-    src = open(srcname, "rb")
-    tgt = open(tgtname, "wb")
-
-    debug("processing target file %s" % tgtname)
-
-    tgt.writelines(self.patch_stream(src, hunks))
-
-    tgt.close()
-    src.close()
-    return True
-  
-
-  def _get_file_idx(self, filename, source=None):
-    """ Detect index of given filename within patch.
-
-        :param filename:
-        :param source: search filename among sources (True),
-                       targets (False), or both (None)
-        :returns: int or None
-    """
-    filename = abspath(filename)
-    if source == True or source == None:
-      for i,fnm in enumerate(self.source):
-        if filename == abspath(fnm):
-          return i  
-    if source == False or source == None:
-      for i,fnm in enumerate(self.target):
-        if filename == abspath(fnm):
-          return i  
-
-
-
-
-from optparse import OptionParser
-from os.path import exists
-import sys
-
-if __name__ == "__main__":
-  opt = OptionParser(usage="%prog [options] unipatch-file", version="python-patch %s" % __version__)
-  opt.add_option("--debug", action="store_true", dest="debugmode", help="debug mode")
-  (options, args) = opt.parse_args()
-
-  if not args:
-    opt.print_version()
-    opt.print_help()
-    sys.exit()
-  debugmode = options.debugmode
-  patchfile = args[0]
-  if not exists(patchfile) or not isfile(patchfile):
-    sys.exit("patch file does not exist - %s" % patchfile)
-
-
-  if debugmode:
-    loglevel = logging.DEBUG
-    logformat = "%(levelname)8s %(message)s"
-  else:
-    loglevel = logging.INFO
-    logformat = "%(message)s"
-  logger.setLevel(loglevel)
-  loghandler.setFormatter(logging.Formatter(logformat))
-
-
-
-  patch = fromfile(patchfile)
-  #pprint(patch)
-  patch.apply()
-
-  # todo: document and test line ends handling logic - patch.py detects proper line-endings
-  #       for inserted hunks and issues a warning if patched file has incosistent line ends
--- a/sbin/res/patch/pyxml.patch	Mon May 02 12:30:49 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-diff -ur xml/xpath/ParsedAbbreviatedAbsoluteLocationPath.py b/xml/xpath/ParsedAbbreviatedAbsoluteLocationPath.py
---- xml/xpath/ParsedAbbreviatedAbsoluteLocationPath.py	2003-03-11 15:01:34.000000000 +0100
-+++ xml/xpath/ParsedAbbreviatedAbsoluteLocationPath.py	2009-05-25 16:32:26.000000000 +0200
-@@ -24,8 +24,8 @@
-         self._rel = rel
-         nt = ParsedNodeTest.ParsedNodeTest('node', '')
-         ppl = ParsedPredicateList.ParsedPredicateList([])
--        as = ParsedAxisSpecifier.ParsedAxisSpecifier('descendant-or-self')
--        self._step = ParsedStep.ParsedStep(as, nt, ppl)
-+        asp = ParsedAxisSpecifier.ParsedAxisSpecifier('descendant-or-self')
-+        self._step = ParsedStep.ParsedStep(asp, nt, ppl)
-         return
- 
-     def evaluate(self, context):
-diff -ur a/xml/xpath/ParsedAbbreviatedRelativeLocationPath.py b/xml/xpath/ParsedAbbreviatedRelativeLocationPath.py
---- xml/xpath/ParsedAbbreviatedRelativeLocationPath.py	2003-03-11 15:01:34.000000000 +0100
-+++ xml/xpath/ParsedAbbreviatedRelativeLocationPath.py	2009-05-25 16:27:55.000000000 +0200
-@@ -28,8 +28,8 @@
-         self._right = right
-         nt = ParsedNodeTest.ParsedNodeTest('node','')
-         ppl = ParsedPredicateList.ParsedPredicateList([])
--        as = ParsedAxisSpecifier.ParsedAxisSpecifier('descendant-or-self')
--        self._middle = ParsedStep.ParsedStep(as, nt, ppl)
-+        asp = ParsedAxisSpecifier.ParsedAxisSpecifier('descendant-or-self')
-+        self._middle = ParsedStep.ParsedStep(asp, nt, ppl)
- 
-     def evaluate(self, context):
-         res = []
Binary file sbin/res/src/4Suite-XML-1.0.2.tar.bz2 has changed
Binary file sbin/res/src/Imaging-1.1.7.tar.gz has changed
Binary file sbin/res/src/PyXML-0.8.4.tar.gz has changed
Binary file sbin/res/src/distribute-0.6.13.tar.gz has changed
Binary file sbin/res/src/psycopg2-2.2.1.tar.gz has changed
Binary file sbin/res/src/pylucene-3.0.1-1-src.tar.gz has changed
--- a/sbin/sync_blinkster_venv	Mon May 02 12:30:49 2011 +0200
+++ b/sbin/sync_blinkster_venv	Mon May 02 17:47:03 2011 +0200
@@ -9,7 +9,7 @@
 #text2unix ~/tmp/blinkster_V$1
 
 if [ -d ~/tmp/blinkster_V$1 ]; then
-    cat <<EOT | rsync -Cvrlz --delete --filter=". -" ~/tmp/blinkster_V$1/sbin/ web.iri.centrepompidou.fr:project/blinkster/sbin
+    cat <<EOT | rsync -Cvrlz --delete --filter=". -" ~/tmp/blinkster_V$1/virtualenv/ web.iri.centrepompidou.fr:project/blinkster/virtualenv
 + core
 EOT
 fi
Binary file virtualenv/res/src/ldt-0.4.tar.gz has changed
--- a/web/blinkster/__init__.py	Mon May 02 12:30:49 2011 +0200
+++ b/web/blinkster/__init__.py	Mon May 02 17:47:03 2011 +0200
@@ -1,3 +1,3 @@
-VERSION = (0, 19)
+VERSION = (0, 20)
 
 VERSION_STR = unicode(".".join(map(lambda i:"%02d" % (i,), VERSION)))