eggs/zc.buildout-1.5.2-py2.6.egg/zc/buildout/testing.py
changeset 307 c6bca38c1cbf
equal deleted inserted replaced
306:5ff1fc726848 307:c6bca38c1cbf
       
     1 #############################################################################
       
     2 #
       
     3 # Copyright (c) 2004-2009 Zope Corporation and Contributors.
       
     4 # All Rights Reserved.
       
     5 #
       
     6 # This software is subject to the provisions of the Zope Public License,
       
     7 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
       
     8 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
       
     9 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
    10 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
       
    11 # FOR A PARTICULAR PURPOSE.
       
    12 #
       
    13 ##############################################################################
       
    14 """Various test-support utility functions
       
    15 
       
    16 $Id: testing.py 116197 2010-09-04 00:02:53Z gary $
       
    17 """
       
    18 
       
    19 import BaseHTTPServer
       
    20 import errno
       
    21 import logging
       
    22 import os
       
    23 import pkg_resources
       
    24 import random
       
    25 import re
       
    26 import shutil
       
    27 import socket
       
    28 import subprocess
       
    29 import sys
       
    30 import tempfile
       
    31 import textwrap
       
    32 import threading
       
    33 import time
       
    34 import urllib2
       
    35 
       
    36 import zc.buildout.buildout
       
    37 import zc.buildout.easy_install
       
    38 from zc.buildout.rmtree import rmtree
       
    39 
       
    40 fsync = getattr(os, 'fsync', lambda fileno: None)
       
    41 is_win32 = sys.platform == 'win32'
       
    42 
       
    43 setuptools_location = pkg_resources.working_set.find(
       
    44     pkg_resources.Requirement.parse('setuptools')).location
       
    45 
       
    46 def cat(dir, *names):
       
    47     path = os.path.join(dir, *names)
       
    48     if (not os.path.exists(path)
       
    49         and is_win32
       
    50         and os.path.exists(path+'-script.py')
       
    51         ):
       
    52         path = path+'-script.py'
       
    53     print open(path).read(),
       
    54 
       
    55 def ls(dir, *subs):
       
    56     if subs:
       
    57         dir = os.path.join(dir, *subs)
       
    58     names = os.listdir(dir)
       
    59     names.sort()
       
    60     for name in names:
       
    61         if os.path.isdir(os.path.join(dir, name)):
       
    62             print 'd ',
       
    63         elif os.path.islink(os.path.join(dir, name)):
       
    64             print 'l ',
       
    65         else:
       
    66             print '- ',
       
    67         print name
       
    68 
       
    69 def mkdir(*path):
       
    70     os.mkdir(os.path.join(*path))
       
    71 
       
    72 def remove(*path):
       
    73     path = os.path.join(*path)
       
    74     if os.path.isdir(path):
       
    75         shutil.rmtree(path)
       
    76     else:
       
    77         os.remove(path)
       
    78 
       
    79 def rmdir(*path):
       
    80     shutil.rmtree(os.path.join(*path))
       
    81 
       
    82 def write(dir, *args):
       
    83     path = os.path.join(dir, *(args[:-1]))
       
    84     f = open(path, 'w')
       
    85     f.write(args[-1])
       
    86     f.flush()
       
    87     fsync(f.fileno())
       
    88     f.close()
       
    89 
       
    90 ## FIXME - check for other platforms
       
    91 MUST_CLOSE_FDS = not sys.platform.startswith('win')
       
    92 
       
    93 def system(command, input=''):
       
    94     env = dict(os.environ)
       
    95     env['COLUMNS'] = '80'
       
    96     p = subprocess.Popen(command,
       
    97                          shell=True,
       
    98                          stdin=subprocess.PIPE,
       
    99                          stdout=subprocess.PIPE,
       
   100                          stderr=subprocess.PIPE,
       
   101                          close_fds=MUST_CLOSE_FDS,
       
   102                          env=env,
       
   103                          )
       
   104     i, o, e = (p.stdin, p.stdout, p.stderr)
       
   105     if input:
       
   106         i.write(input)
       
   107     i.close()
       
   108     result = o.read() + e.read()
       
   109     o.close()
       
   110     e.close()
       
   111     return result
       
   112 
       
   113 def call_py(interpreter, cmd, flags=None):
       
   114     if sys.platform == 'win32':
       
   115         args = ['"%s"' % arg for arg in (interpreter, flags, cmd) if arg]
       
   116         args.insert(-1, '"-c"')
       
   117         return system('"%s"' % ' '.join(args))
       
   118     else:
       
   119         cmd = repr(cmd)
       
   120         return system(
       
   121             ' '.join(arg for arg in (interpreter, flags, '-c', cmd) if arg))
       
   122 
       
   123 def get(url):
       
   124     return urllib2.urlopen(url).read()
       
   125 
       
   126 def _runsetup(setup, executable, *args):
       
   127     if os.path.isdir(setup):
       
   128         setup = os.path.join(setup, 'setup.py')
       
   129     d = os.path.dirname(setup)
       
   130 
       
   131     args = [zc.buildout.easy_install._safe_arg(arg)
       
   132             for arg in args]
       
   133     args.insert(0, '-q')
       
   134     env = dict(os.environ)
       
   135     if executable == sys.executable:
       
   136         env['PYTHONPATH'] = setuptools_location
       
   137     # else pass an executable that has setuptools! See testselectingpython.py.
       
   138     args.append(env)
       
   139 
       
   140     here = os.getcwd()
       
   141     try:
       
   142         os.chdir(d)
       
   143         os.spawnle(os.P_WAIT, executable,
       
   144                    zc.buildout.easy_install._safe_arg(executable),
       
   145                    setup, *args)
       
   146         if os.path.exists('build'):
       
   147             rmtree('build')
       
   148     finally:
       
   149         os.chdir(here)
       
   150 
       
   151 def sdist(setup, dest):
       
   152     _runsetup(setup, sys.executable, 'sdist', '-d', dest, '--formats=zip')
       
   153 
       
   154 def bdist_egg(setup, executable, dest):
       
   155     _runsetup(setup, executable, 'bdist_egg', '-d', dest)
       
   156 
       
   157 def sys_install(setup, dest):
       
   158     _runsetup(setup, sys.executable, 'install', '--install-purelib', dest,
       
   159               '--record', os.path.join(dest, '__added_files__'),
       
   160               '--single-version-externally-managed')
       
   161 
       
   162 def find_python(version):
       
   163     e = os.environ.get('PYTHON%s' % version)
       
   164     if e is not None:
       
   165         return e
       
   166     if is_win32:
       
   167         e = '\Python%s%s\python.exe' % tuple(version.split('.'))
       
   168         if os.path.exists(e):
       
   169             return e
       
   170     else:
       
   171         cmd = 'python%s -c "import sys; print sys.executable"' % version
       
   172         p = subprocess.Popen(cmd,
       
   173                              shell=True,
       
   174                              stdin=subprocess.PIPE,
       
   175                              stdout=subprocess.PIPE,
       
   176                              stderr=subprocess.STDOUT,
       
   177                              close_fds=MUST_CLOSE_FDS)
       
   178         i, o = (p.stdin, p.stdout)
       
   179         i.close()
       
   180         e = o.read().strip()
       
   181         o.close()
       
   182         if os.path.exists(e):
       
   183             return e
       
   184         cmd = 'python -c "import sys; print \'%s.%s\' % sys.version_info[:2]"'
       
   185         p = subprocess.Popen(cmd,
       
   186                              shell=True,
       
   187                              stdin=subprocess.PIPE,
       
   188                              stdout=subprocess.PIPE,
       
   189                              stderr=subprocess.STDOUT,
       
   190                              close_fds=MUST_CLOSE_FDS)
       
   191         i, o = (p.stdin, p.stdout)
       
   192         i.close()
       
   193         e = o.read().strip()
       
   194         o.close()
       
   195         if e == version:
       
   196             cmd = 'python -c "import sys; print sys.executable"'
       
   197             p = subprocess.Popen(cmd,
       
   198                                 shell=True,
       
   199                                 stdin=subprocess.PIPE,
       
   200                                 stdout=subprocess.PIPE,
       
   201                                 stderr=subprocess.STDOUT,
       
   202                                 close_fds=MUST_CLOSE_FDS)
       
   203             i, o = (p.stdin, p.stdout)
       
   204             i.close()
       
   205             e = o.read().strip()
       
   206             o.close()
       
   207             if os.path.exists(e):
       
   208                 return e
       
   209 
       
   210     raise ValueError(
       
   211         "Couldn't figure out the executable for Python %(version)s.\n"
       
   212         "Set the environment variable PYTHON%(version)s to the location\n"
       
   213         "of the Python %(version)s executable before running the tests."
       
   214         % {'version': version})
       
   215 
       
   216 def wait_until(label, func, *args, **kw):
       
   217     if 'timeout' in kw:
       
   218         kw = dict(kw)
       
   219         timeout = kw.pop('timeout')
       
   220     else:
       
   221         timeout = 30
       
   222     deadline = time.time()+timeout
       
   223     while time.time() < deadline:
       
   224         if func(*args, **kw):
       
   225             return
       
   226         time.sleep(0.01)
       
   227     raise ValueError('Timed out waiting for: '+label)
       
   228 
       
   229 def get_installer_values():
       
   230     """Get the current values for the easy_install module.
       
   231 
       
   232     This is necessary because instantiating a Buildout will force the
       
   233     Buildout's values on the installer.
       
   234 
       
   235     Returns a dict of names-values suitable for set_installer_values."""
       
   236     names = ('default_versions', 'download_cache', 'install_from_cache',
       
   237              'prefer_final', 'include_site_packages',
       
   238              'allowed_eggs_from_site_packages', 'use_dependency_links',
       
   239              'allow_picked_versions', 'always_unzip'
       
   240             )
       
   241     values = {}
       
   242     for name in names:
       
   243         values[name] = getattr(zc.buildout.easy_install, name)()
       
   244     return values
       
   245 
       
   246 def set_installer_values(values):
       
   247     """Set the given values on the installer."""
       
   248     for name, value in values.items():
       
   249         getattr(zc.buildout.easy_install, name)(value)
       
   250 
       
   251 def make_buildout(executable=None):
       
   252     """Make a buildout that uses this version of zc.buildout."""
       
   253     # Create a basic buildout.cfg to avoid a warning from buildout.
       
   254     open('buildout.cfg', 'w').write(
       
   255         "[buildout]\nparts =\n"
       
   256         )
       
   257     # Get state of installer defaults so we can reinstate them (instantiating
       
   258     # a Buildout will force the Buildout's defaults on the installer).
       
   259     installer_values = get_installer_values()
       
   260     # Use the buildout bootstrap command to create a buildout
       
   261     config = [
       
   262         ('buildout', 'log-level', 'WARNING'),
       
   263         # trick bootstrap into putting the buildout develop egg
       
   264         # in the eggs dir.
       
   265         ('buildout', 'develop-eggs-directory', 'eggs'),
       
   266         ]
       
   267     if executable is not None:
       
   268         config.append(('buildout', 'executable', executable))
       
   269     zc.buildout.buildout.Buildout(
       
   270         'buildout.cfg', config,
       
   271         user_defaults=False,
       
   272         ).bootstrap([])
       
   273     # Create the develop-eggs dir, which didn't get created the usual
       
   274     # way due to the trick above:
       
   275     os.mkdir('develop-eggs')
       
   276     # Reinstate the default values of the installer.
       
   277     set_installer_values(installer_values)
       
   278 
       
   279 def buildoutSetUp(test):
       
   280 
       
   281     test.globs['__tear_downs'] = __tear_downs = []
       
   282     test.globs['register_teardown'] = register_teardown = __tear_downs.append
       
   283 
       
   284     installer_values = get_installer_values()
       
   285     register_teardown(
       
   286         lambda: set_installer_values(installer_values)
       
   287         )
       
   288 
       
   289     here = os.getcwd()
       
   290     register_teardown(lambda: os.chdir(here))
       
   291 
       
   292     handlers_before_set_up = logging.getLogger().handlers[:]
       
   293     def restore_root_logger_handlers():
       
   294         root_logger = logging.getLogger()
       
   295         for handler in root_logger.handlers[:]:
       
   296             root_logger.removeHandler(handler)
       
   297         for handler in handlers_before_set_up:
       
   298             root_logger.addHandler(handler)
       
   299     register_teardown(restore_root_logger_handlers)
       
   300 
       
   301     base = tempfile.mkdtemp('buildoutSetUp')
       
   302     base = os.path.realpath(base)
       
   303     register_teardown(lambda base=base: rmtree(base))
       
   304 
       
   305     old_home = os.environ.get('HOME')
       
   306     os.environ['HOME'] = os.path.join(base, 'bbbBadHome')
       
   307     def restore_home():
       
   308         if old_home is None:
       
   309             del os.environ['HOME']
       
   310         else:
       
   311             os.environ['HOME'] = old_home
       
   312     register_teardown(restore_home)
       
   313 
       
   314     base = os.path.join(base, '_TEST_')
       
   315     os.mkdir(base)
       
   316 
       
   317     tmp = tempfile.mkdtemp('buildouttests')
       
   318     register_teardown(lambda: rmtree(tmp))
       
   319 
       
   320     zc.buildout.easy_install.default_index_url = 'file://'+tmp
       
   321     os.environ['buildout-testing-index-url'] = (
       
   322         zc.buildout.easy_install.default_index_url)
       
   323     os.environ.pop('PYTHONPATH', None)
       
   324 
       
   325     def tmpdir(name):
       
   326         path = os.path.join(base, name)
       
   327         mkdir(path)
       
   328         return path
       
   329 
       
   330     sample = tmpdir('sample-buildout')
       
   331 
       
   332     os.chdir(sample)
       
   333     make_buildout()
       
   334 
       
   335     def start_server(path):
       
   336         port, thread = _start_server(path, name=path)
       
   337         url = 'http://localhost:%s/' % port
       
   338         register_teardown(lambda: stop_server(url, thread))
       
   339         return url
       
   340 
       
   341     def make_py(initialization=''):
       
   342         """Returns paths to new executable and to its site-packages.
       
   343         """
       
   344         buildout = tmpdir('executable_buildout')
       
   345         site_packages_dir = os.path.join(buildout, 'site-packages')
       
   346         mkdir(site_packages_dir)
       
   347         old_wd = os.getcwd()
       
   348         os.chdir(buildout)
       
   349         make_buildout()
       
   350         # Normally we don't process .pth files in extra-paths.  We want to
       
   351         # in this case so that we can test with setuptools system installs
       
   352         # (--single-version-externally-managed), which use .pth files.
       
   353         initialization = (
       
   354             ('import sys\n'
       
   355              'import site\n'
       
   356              'known_paths = set(sys.path)\n'
       
   357              'site_packages_dir = %r\n'
       
   358              'site.addsitedir(site_packages_dir, known_paths)\n'
       
   359             ) % (site_packages_dir,)) + initialization
       
   360         initialization = '\n'.join(
       
   361             '  ' + line for line in initialization.split('\n'))
       
   362         install_develop(
       
   363             'zc.recipe.egg', os.path.join(buildout, 'develop-eggs'))
       
   364         install_develop(
       
   365             'z3c.recipe.scripts', os.path.join(buildout, 'develop-eggs'))
       
   366         write('buildout.cfg', textwrap.dedent('''\
       
   367             [buildout]
       
   368             parts = py
       
   369             include-site-packages = false
       
   370             exec-sitecustomize = false
       
   371 
       
   372             [py]
       
   373             recipe = z3c.recipe.scripts
       
   374             interpreter = py
       
   375             initialization =
       
   376             %(initialization)s
       
   377             extra-paths = %(site-packages)s
       
   378             eggs = setuptools
       
   379             ''') % {
       
   380                 'initialization': initialization,
       
   381                 'site-packages': site_packages_dir})
       
   382         system(os.path.join(buildout, 'bin', 'buildout'))
       
   383         os.chdir(old_wd)
       
   384         return (
       
   385             os.path.join(buildout, 'bin', 'py'), site_packages_dir)
       
   386 
       
   387     test.globs.update(dict(
       
   388         sample_buildout = sample,
       
   389         ls = ls,
       
   390         cat = cat,
       
   391         mkdir = mkdir,
       
   392         rmdir = rmdir,
       
   393         remove = remove,
       
   394         tmpdir = tmpdir,
       
   395         write = write,
       
   396         system = system,
       
   397         call_py = call_py,
       
   398         get = get,
       
   399         cd = (lambda *path: os.chdir(os.path.join(*path))),
       
   400         join = os.path.join,
       
   401         sdist = sdist,
       
   402         bdist_egg = bdist_egg,
       
   403         start_server = start_server,
       
   404         buildout = os.path.join(sample, 'bin', 'buildout'),
       
   405         wait_until = wait_until,
       
   406         make_py = make_py
       
   407         ))
       
   408 
       
   409 def buildoutTearDown(test):
       
   410     for f in test.globs['__tear_downs']:
       
   411         f()
       
   412 
       
   413 class Server(BaseHTTPServer.HTTPServer):
       
   414 
       
   415     def __init__(self, tree, *args):
       
   416         BaseHTTPServer.HTTPServer.__init__(self, *args)
       
   417         self.tree = os.path.abspath(tree)
       
   418 
       
   419     __run = True
       
   420     def serve_forever(self):
       
   421         while self.__run:
       
   422             self.handle_request()
       
   423 
       
   424     def handle_error(self, *_):
       
   425         self.__run = False
       
   426 
       
   427 class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
       
   428 
       
   429     Server.__log = False
       
   430 
       
   431     def __init__(self, request, address, server):
       
   432         self.__server = server
       
   433         self.tree = server.tree
       
   434         BaseHTTPServer.BaseHTTPRequestHandler.__init__(
       
   435             self, request, address, server)
       
   436 
       
   437     def do_GET(self):
       
   438         if '__stop__' in self.path:
       
   439             raise SystemExit
       
   440 
       
   441         if self.path == '/enable_server_logging':
       
   442             self.__server.__log = True
       
   443             self.send_response(200)
       
   444             return
       
   445 
       
   446         if self.path == '/disable_server_logging':
       
   447             self.__server.__log = False
       
   448             self.send_response(200)
       
   449             return
       
   450 
       
   451         path = os.path.abspath(os.path.join(self.tree, *self.path.split('/')))
       
   452         if not (
       
   453             ((path == self.tree) or path.startswith(self.tree+os.path.sep))
       
   454             and
       
   455             os.path.exists(path)
       
   456             ):
       
   457             self.send_response(404, 'Not Found')
       
   458             #self.send_response(200)
       
   459             out = '<html><body>Not Found</body></html>'
       
   460             #out = '\n'.join(self.tree, self.path, path)
       
   461             self.send_header('Content-Length', str(len(out)))
       
   462             self.send_header('Content-Type', 'text/html')
       
   463             self.end_headers()
       
   464             self.wfile.write(out)
       
   465             return
       
   466 
       
   467         self.send_response(200)
       
   468         if os.path.isdir(path):
       
   469             out = ['<html><body>\n']
       
   470             names = os.listdir(path)
       
   471             names.sort()
       
   472             for name in names:
       
   473                 if os.path.isdir(os.path.join(path, name)):
       
   474                     name += '/'
       
   475                 out.append('<a href="%s">%s</a><br>\n' % (name, name))
       
   476             out.append('</body></html>\n')
       
   477             out = ''.join(out)
       
   478             self.send_header('Content-Length', str(len(out)))
       
   479             self.send_header('Content-Type', 'text/html')
       
   480         else:
       
   481             out = open(path, 'rb').read()
       
   482             self.send_header('Content-Length', len(out))
       
   483             if path.endswith('.egg'):
       
   484                 self.send_header('Content-Type', 'application/zip')
       
   485             elif path.endswith('.gz'):
       
   486                 self.send_header('Content-Type', 'application/x-gzip')
       
   487             elif path.endswith('.zip'):
       
   488                 self.send_header('Content-Type', 'application/x-gzip')
       
   489             else:
       
   490                 self.send_header('Content-Type', 'text/html')
       
   491         self.end_headers()
       
   492 
       
   493         self.wfile.write(out)
       
   494 
       
   495     def log_request(self, code):
       
   496         if self.__server.__log:
       
   497             print '%s %s %s' % (self.command, code, self.path)
       
   498 
       
   499 def _run(tree, port):
       
   500     server_address = ('localhost', port)
       
   501     httpd = Server(tree, server_address, Handler)
       
   502     httpd.serve_forever()
       
   503 
       
   504 def get_port():
       
   505     for i in range(10):
       
   506         port = random.randrange(20000, 30000)
       
   507         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       
   508         try:
       
   509             try:
       
   510                 s.connect(('localhost', port))
       
   511             except socket.error:
       
   512                 return port
       
   513         finally:
       
   514             s.close()
       
   515     raise RuntimeError, "Can't find port"
       
   516 
       
   517 def _start_server(tree, name=''):
       
   518     port = get_port()
       
   519     thread = threading.Thread(target=_run, args=(tree, port), name=name)
       
   520     thread.setDaemon(True)
       
   521     thread.start()
       
   522     wait(port, up=True)
       
   523     return port, thread
       
   524 
       
   525 def start_server(tree):
       
   526     return _start_server(tree)[0]
       
   527 
       
   528 def stop_server(url, thread=None):
       
   529     try:
       
   530         urllib2.urlopen(url+'__stop__')
       
   531     except Exception:
       
   532         pass
       
   533     if thread is not None:
       
   534         thread.join() # wait for thread to stop
       
   535 
       
   536 def wait(port, up):
       
   537     addr = 'localhost', port
       
   538     for i in range(120):
       
   539         time.sleep(0.25)
       
   540         try:
       
   541             s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       
   542             s.connect(addr)
       
   543             s.close()
       
   544             if up:
       
   545                 break
       
   546         except socket.error, e:
       
   547             if e[0] not in (errno.ECONNREFUSED, errno.ECONNRESET):
       
   548                 raise
       
   549             s.close()
       
   550             if not up:
       
   551                 break
       
   552     else:
       
   553         if up:
       
   554             raise
       
   555         else:
       
   556             raise SystemError("Couln't stop server")
       
   557 
       
   558 def install(project, destination):
       
   559     if not isinstance(destination, basestring):
       
   560         destination = os.path.join(destination.globs['sample_buildout'],
       
   561                                    'eggs')
       
   562 
       
   563     dist = pkg_resources.working_set.find(
       
   564         pkg_resources.Requirement.parse(project))
       
   565     if dist.location.endswith('.egg'):
       
   566         destination = os.path.join(destination,
       
   567                                    os.path.basename(dist.location),
       
   568                                    )
       
   569         if os.path.isdir(dist.location):
       
   570             shutil.copytree(dist.location, destination)
       
   571         else:
       
   572             shutil.copyfile(dist.location, destination)
       
   573     else:
       
   574         # copy link
       
   575         open(os.path.join(destination, project+'.egg-link'), 'w'
       
   576              ).write(dist.location)
       
   577 
       
   578 def install_develop(project, destination):
       
   579     if not isinstance(destination, basestring):
       
   580         destination = os.path.join(destination.globs['sample_buildout'],
       
   581                                    'develop-eggs')
       
   582 
       
   583     dist = pkg_resources.working_set.find(
       
   584         pkg_resources.Requirement.parse(project))
       
   585     open(os.path.join(destination, project+'.egg-link'), 'w'
       
   586          ).write(dist.location)
       
   587 
       
   588 def _normalize_path(match):
       
   589     path = match.group(1)
       
   590     if os.path.sep == '\\':
       
   591         path = path.replace('\\\\', '/')
       
   592         if path.startswith('\\'):
       
   593             path = path[1:]
       
   594     return '/' + path.replace(os.path.sep, '/')
       
   595 
       
   596 if sys.platform == 'win32':
       
   597     sep = r'[\\/]' # Windows uses both sometimes.
       
   598 else:
       
   599     sep = re.escape(os.path.sep)
       
   600 normalize_path = (
       
   601     re.compile(
       
   602         r'''[^'" \t\n\r!]+%(sep)s_[Tt][Ee][Ss][Tt]_%(sep)s([^"' \t\n\r]+)'''
       
   603         % dict(sep=sep)),
       
   604     _normalize_path,
       
   605     )
       
   606 
       
   607 normalize_endings = re.compile('\r\n'), '\n'
       
   608 
       
   609 normalize_script = (
       
   610     re.compile('(\n?)-  ([a-zA-Z_.-]+)-script.py\n-  \\2.exe\n'),
       
   611     '\\1-  \\2\n')
       
   612 
       
   613 normalize_egg_py = (
       
   614     re.compile('-py\d[.]\d(-\S+)?.egg'),
       
   615     '-pyN.N.egg',
       
   616     )