[Python-checkins] r88997 - in sandbox/branches/setuptools-0.6: EasyInstall.txt api_tests.txt pkg_resources.py setup.py setuptools.egg-info/dependency_links.txt setuptools.egg-info/entry_points.txt setuptools.egg-info/requires.txt setuptools.txt setuptools/command/easy_install.py setuptools/dist.py setuptools/package_index.py setuptools/sandbox.py setuptools/ssl_support.py setuptools/tests/test_resources.py

phillip.eby python-checkins at python.org
Thu May 16 00:04:59 CEST 2013


Author: phillip.eby
Date: Thu May 16 00:04:59 2013
New Revision: 88997

Log:
Snapshotting pre-merge changes, mostly SSL support and a few bug fixes


Added:
   sandbox/branches/setuptools-0.6/setuptools.egg-info/dependency_links.txt   (contents, props changed)
   sandbox/branches/setuptools-0.6/setuptools.egg-info/requires.txt   (contents, props changed)
   sandbox/branches/setuptools-0.6/setuptools/ssl_support.py   (contents, props changed)
Modified:
   sandbox/branches/setuptools-0.6/EasyInstall.txt
   sandbox/branches/setuptools-0.6/api_tests.txt
   sandbox/branches/setuptools-0.6/pkg_resources.py
   sandbox/branches/setuptools-0.6/setup.py
   sandbox/branches/setuptools-0.6/setuptools.egg-info/entry_points.txt
   sandbox/branches/setuptools-0.6/setuptools.txt
   sandbox/branches/setuptools-0.6/setuptools/command/easy_install.py
   sandbox/branches/setuptools-0.6/setuptools/dist.py
   sandbox/branches/setuptools-0.6/setuptools/package_index.py
   sandbox/branches/setuptools-0.6/setuptools/sandbox.py
   sandbox/branches/setuptools-0.6/setuptools/tests/test_resources.py

Modified: sandbox/branches/setuptools-0.6/EasyInstall.txt
==============================================================================
--- sandbox/branches/setuptools-0.6/EasyInstall.txt	(original)
+++ sandbox/branches/setuptools-0.6/EasyInstall.txt	Thu May 16 00:04:59 2013
@@ -1217,7 +1217,9 @@
 Release Notes/Change History
 ============================
 
-0.6final
+0.6c12
+ * Default --index-URL to SSL version of PyPI
+
  * Fixed AttributeError under Python 2.3 when processing "HTTP authentication"
    URLs (i.e., ones with a ``user:password at host``).
 

Modified: sandbox/branches/setuptools-0.6/api_tests.txt
==============================================================================
--- sandbox/branches/setuptools-0.6/api_tests.txt	(original)
+++ sandbox/branches/setuptools-0.6/api_tests.txt	Thu May 16 00:04:59 2013
@@ -328,3 +328,94 @@
     >>> cp("darwin-8.2.0-Power_Macintosh", "macosx-10.3-ppc")
     False
 
+
+Environment Markers
+-------------------
+
+    >>> from pkg_resources import invalid_marker as im, evaluate_marker as em
+    >>> import os
+
+    >>> print(im("sys_platform"))
+    Comparison or logical expression expected
+
+    >>> print(im("sys_platform=="))
+    unexpected EOF while parsing (line 1)
+    
+    >>> print(im("sys_platform=='win32'"))
+    False
+
+    >>> print(im("sys=='x'"))
+    Unknown name 'sys'
+
+    >>> print(im("(extra)"))
+    Comparison or logical expression expected
+
+    >>> print(im("(extra"))
+    unexpected EOF while parsing (line 1)
+
+    >>> print(im("os.open('foo')=='y'"))
+    Language feature not supported in environment markers
+    
+    >>> print(im("'x'=='y' and os.open('foo')=='y'"))   # no short-circuit!
+    Language feature not supported in environment markers
+
+    >>> print(im("'x'=='x' or os.open('foo')=='y'"))   # no short-circuit!
+    Language feature not supported in environment markers
+
+    >>> print(im("'x' < 'y'"))
+    '<' operator not allowed in environment markers
+    
+    >>> print(im("'x' < 'y' < 'z'"))
+    Chained comparison not allowed in environment markers
+
+    >>> print(im("r'x'=='x'"))
+    Only plain strings allowed in environment markers
+    
+    >>> print(im("'''x'''=='x'"))
+    Only plain strings allowed in environment markers
+    
+    >>> print(im('"""x"""=="x"'))
+    Only plain strings allowed in environment markers
+    
+    >>> print(im(r"'x\n'=='x'"))
+    Only plain strings allowed in environment markers
+
+    >>> print(im("os.open=='y'"))
+    Language feature not supported in environment markers
+    
+    >>> em('"x"=="x"')
+    True
+
+    >>> em('"x"=="y"')
+    False
+
+    >>> em('"x"=="y" and "x"=="x"')
+    False
+
+    >>> em('"x"=="y" or "x"=="x"')
+    True
+
+    >>> em('"x"=="y" and "x"=="q" or "z"=="z"')
+    True
+
+    >>> em('"x"=="y" and ("x"=="q" or "z"=="z")')
+    False
+
+    >>> em('"x"=="y" and "z"=="z" or "x"=="q"')
+    False
+
+    >>> em('"x"=="x" and "z"=="z" or "x"=="q"')
+    True
+
+    >>> em("sys_platform=='win32'") == (sys.platform=='win32')
+    True
+
+    >>> em("'x' in 'yx'")
+    True
+
+    >>> em("'yx' in 'x'")
+    False
+
+
+
+

Modified: sandbox/branches/setuptools-0.6/pkg_resources.py
==============================================================================
--- sandbox/branches/setuptools-0.6/pkg_resources.py	(original)
+++ sandbox/branches/setuptools-0.6/pkg_resources.py	Thu May 16 00:04:59 2013
@@ -144,7 +144,7 @@
     # Parsing functions and string utilities
     'parse_requirements', 'parse_version', 'safe_name', 'safe_version',
     'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections',
-    'safe_extra', 'to_filename',
+    'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker',
 
     # filesystem utilities
     'ensure_directory', 'normalize_path',
@@ -1146,6 +1146,129 @@
 
 
 
+_marker_names = {
+    'os': ['name'], 'sys': ['platform'],
+    'platform': ['version','machine','python_implementation'],
+    'python_version': [], 'python_full_version': [], 'extra':[],
+}
+
+_marker_values = {
+    'os_name': lambda: os.name,
+    'sys_platform': lambda: sys.platform,
+    'python_full_version': lambda: sys.version.split()[0],
+    'python_version': lambda:'%s.%s' % (sys.version_info[0], sys.version_info[1]),
+    'platform_version': lambda: _platinfo('version'),
+    'platform_machine': lambda: _platinfo('machine'),
+    'python_implementation': lambda: _platinfo('python_implementation') or _pyimp(),
+}
+
+def _platinfo(attr):
+    try:
+        import platform
+    except ImportError:
+        return ''
+    return getattr(platform, attr, lambda:'')()
+
+def _pyimp():
+    if sys.platform=='cli':
+        return 'IronPython'
+    elif sys.platform.startswith('java'):
+        return 'Jython'
+    elif '__pypy__' in sys.builtin_module_names:
+        return 'PyPy'
+    else:
+        return 'CPython'                
+
+def invalid_marker(text):
+    """Validate text as a PEP 426 environment marker; return exception or False"""
+    try:
+        evaluate_marker(text)
+    except SyntaxError:
+        return sys.exc_info()[1]            
+    return False
+
+def evaluate_marker(text, extra=None, _ops={}):
+    """Evaluate a PEP 426 environment marker; SyntaxError if marker is invalid"""
+
+    if not _ops:
+
+        from token import NAME, STRING
+        import token, symbol, operator
+        
+        def and_test(nodelist):
+            # MUST NOT short-circuit evaluation, or invalid syntax can be skipped!
+            return reduce(operator.and_, [interpret(nodelist[i]) for i in range(1,len(nodelist),2)])
+        
+        def test(nodelist):
+            # MUST NOT short-circuit evaluation, or invalid syntax can be skipped!
+            return reduce(operator.or_, [interpret(nodelist[i]) for i in range(1,len(nodelist),2)])
+        
+        def atom(nodelist):
+            t = nodelist[1][0]
+            if t == token.LPAR:
+                if nodelist[2][0] == token.RPAR:
+                    raise SyntaxError("Empty parentheses")
+                return interpret(nodelist[2])
+            raise SyntaxError("Language feature not supported in environment markers")
+        
+        def comparison(nodelist):
+            if len(nodelist)>4:
+                raise SyntaxError("Chained comparison not allowed in environment markers")
+            comp = nodelist[2][1]
+            cop = comp[1]
+            if comp[0] == NAME:
+                if len(nodelist[2]) == 3:
+                    if cop == 'not':
+                        cop = 'not in'
+                    else:
+                        cop = 'is not'
+            try:
+                cop = _ops[cop]
+            except KeyError:
+                raise SyntaxError(repr(cop)+" operator not allowed in environment markers")
+            return cop(evaluate(nodelist[1]), evaluate(nodelist[3]))
+        
+        _ops.update({
+            symbol.test: test, symbol.and_test: and_test, symbol.atom: atom,
+            symbol.comparison: comparison, 'not in': lambda x,y: x not in y,
+            'in': lambda x,y: x in y, '==': operator.eq, '!=': operator.ne,
+        })
+        if hasattr(symbol,'or_test'):
+            _ops[symbol.or_test] = test
+
+    def interpret(nodelist):
+        while len(nodelist)==2: nodelist = nodelist[1]
+        try:
+            op = _ops[nodelist[0]]
+        except KeyError:
+            raise SyntaxError("Comparison or logical expression expected")
+            raise SyntaxError("Language feature not supported in environment markers: "+symbol.sym_name[nodelist[0]])
+        return op(nodelist)
+        
+    def evaluate(nodelist):
+        while len(nodelist)==2: nodelist = nodelist[1]
+        kind = nodelist[0]
+        name = nodelist[1]
+        #while len(name)==2: name = name[1]
+        if kind==NAME:
+            try:
+                op = _marker_values[name]
+            except KeyError:
+                raise SyntaxError("Unknown name %r" % name)
+            return op()
+        if kind==STRING:
+            s = nodelist[1]
+            if s[:1] not in "'\"" or s.startswith('"""') or s.startswith("'''") \
+            or '\\' in s:
+                raise SyntaxError(
+                    "Only plain strings allowed in environment markers")
+            return s[1:-1]
+        raise SyntaxError("Language feature not supported in environment markers")
+
+    import parser        
+    return interpret(parser.expr(text).totuple(1)[1])
+
+
 class NullProvider:
     """Try to implement resources and metadata for arbitrary PEP 302 loaders"""
 
@@ -1925,7 +2048,6 @@
                 parts.pop()
         parts.append(part)
     return tuple(parts)
-
 class EntryPoint(object):
     """Object representing an advertised importable object"""
 
@@ -2139,7 +2261,14 @@
             dm = self.__dep_map = {None: []}
             for name in 'requires.txt', 'depends.txt':
                 for extra,reqs in split_sections(self._get_metadata(name)):
-                    if extra: extra = safe_extra(extra)
+                    if extra:
+                        if ':' in extra:
+                            extra, marker = extra.split(':',1)
+                            if invalid_marker(marker):
+                                reqs=[] # XXX warn
+                            elif not evaluate_marker(marker):
+                                reqs=[]
+                        extra = safe_extra(extra) or None                                                            
                     dm.setdefault(extra,[]).extend(parse_requirements(reqs))
             return dm
     _dep_map = property(_dep_map)
@@ -2163,6 +2292,8 @@
             for line in self.get_metadata_lines(name):
                 yield line
 
+
+
     def activate(self,path=None):
         """Ensure distribution is importable on `path` (default=sys.path)"""
         if path is None: path = sys.path
@@ -2201,6 +2332,9 @@
             raise AttributeError,attr
         return getattr(self._provider, attr)
 
+
+
+
     #@classmethod
     def from_filename(cls,filename,metadata=None, **kw):
         return cls.from_location(
@@ -2238,18 +2372,6 @@
 
 
 
-
-
-
-
-
-
-
-
-
-
-
-
 
 
 

Modified: sandbox/branches/setuptools-0.6/setup.py
==============================================================================
--- sandbox/branches/setuptools-0.6/setup.py	(original)
+++ sandbox/branches/setuptools-0.6/setup.py	Thu May 16 00:04:59 2013
@@ -91,31 +91,31 @@
     Topic :: System :: Archiving :: Packaging
     Topic :: System :: Systems Administration
     Topic :: Utilities""".splitlines() if f.strip()],
-    scripts = scripts,
+    extras_require = {
+        "ssl:sys_platform=='win32'": "wincertstore==0.1",
+        "ssl:sys_platform=='win32' and python_version in '2.3, 2.4'": "ctypes==1.0.2",
+        "ssl:python_version in '2.3, 2.4, 2.5'":"ssl==1.16",
+        "certs": "certifi==0.0.8",
+    },
+    dependency_links = [
+        'http://pypi.python.org/packages/source/c/certifi/certifi-0.0.8.tar.gz#md5=dc5f5e7f0b5fc08d27654b17daa6ecec',
+        'http://pypi.python.org/packages/source/s/ssl/ssl-1.16.tar.gz#md5=fb12d335d56f3c8c7c1fefc1c06c4bfb',
+        'http://pypi.python.org/packages/source/w/wincertstore/wincertstore-0.1.zip#md5=2f9accbebe8f7b4c06ac7aa83879b81c',
+        'http://sourceforge.net/projects/ctypes/files/ctypes/1.0.2/ctypes-1.0.2.win32-py2.3.exe/download#md5=9afe4b75240a8808a24df7a76b6081e3',
+        'http://sourceforge.net/projects/ctypes/files/ctypes/1.0.2/ctypes-1.0.2.win32-py2.4.exe/download#md5=9092a0ad5a3d79fa2d980f1ddc5e9dbc',
+        'http://peak.telecommunity.com/dist/ssl-1.16-py2.3-win32.egg#md5=658f74b3eb6f32050e8531bb73de8e74',
+        'http://peak.telecommunity.com/dist/ssl-1.16-py2.4-win32.egg#md5=3cfa2c526dc66e318e8520b6f1aadce5',
+        'http://peak.telecommunity.com/dist/ssl-1.16-py2.5-win32.egg#md5=85ad1cda806d639743121c0bbcb5f39b',
+    ],
+    scripts = [],
 
     # uncomment for testing
     # setup_requires = ['setuptools>=0.6a0'],
+    # tests_require = "setuptools[ssl]",
 )
 
 
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 
 
 

Added: sandbox/branches/setuptools-0.6/setuptools.egg-info/dependency_links.txt
==============================================================================
--- (empty file)
+++ sandbox/branches/setuptools-0.6/setuptools.egg-info/dependency_links.txt	Thu May 16 00:04:59 2013
@@ -0,0 +1,8 @@
+http://pypi.python.org/packages/source/c/certifi/certifi-0.0.8.tar.gz#md5=dc5f5e7f0b5fc08d27654b17daa6ecec
+http://pypi.python.org/packages/source/s/ssl/ssl-1.16.tar.gz#md5=fb12d335d56f3c8c7c1fefc1c06c4bfb
+http://pypi.python.org/packages/source/w/wincertstore/wincertstore-0.1.zip#md5=2f9accbebe8f7b4c06ac7aa83879b81c
+http://sourceforge.net/projects/ctypes/files/ctypes/1.0.2/ctypes-1.0.2.win32-py2.3.exe/download#md5=9afe4b75240a8808a24df7a76b6081e3
+http://sourceforge.net/projects/ctypes/files/ctypes/1.0.2/ctypes-1.0.2.win32-py2.4.exe/download#md5=9092a0ad5a3d79fa2d980f1ddc5e9dbc
+http://peak.telecommunity.com/dist/ssl-1.16-py2.3-win32.egg#md5=658f74b3eb6f32050e8531bb73de8e74
+http://peak.telecommunity.com/dist/ssl-1.16-py2.4-win32.egg#md5=3cfa2c526dc66e318e8520b6f1aadce5
+http://peak.telecommunity.com/dist/ssl-1.16-py2.5-win32.egg#md5=85ad1cda806d639743121c0bbcb5f39b

Modified: sandbox/branches/setuptools-0.6/setuptools.egg-info/entry_points.txt
==============================================================================
--- sandbox/branches/setuptools-0.6/setuptools.egg-info/entry_points.txt	(original)
+++ sandbox/branches/setuptools-0.6/setuptools.egg-info/entry_points.txt	Thu May 16 00:04:59 2013
@@ -7,6 +7,7 @@
 saveopts = setuptools.command.saveopts:saveopts
 egg_info = setuptools.command.egg_info:egg_info
 register = setuptools.command.register:register
+upload = setuptools.command.upload:upload
 install_egg_info = setuptools.command.install_egg_info:install_egg_info
 alias = setuptools.command.alias:alias
 easy_install = setuptools.command.easy_install:easy_install

Added: sandbox/branches/setuptools-0.6/setuptools.egg-info/requires.txt
==============================================================================
--- (empty file)
+++ sandbox/branches/setuptools-0.6/setuptools.egg-info/requires.txt	Thu May 16 00:04:59 2013
@@ -0,0 +1,13 @@
+
+
+[ssl:sys_platform=='win32']
+wincertstore==0.1
+
+[certs]
+certifi==0.0.8
+
+[ssl:sys_platform=='win32' and python_version in '2.3, 2.4']
+ctypes==1.0.2
+
+[ssl:python_version in '2.3, 2.4, 2.5']
+ssl==1.16
\ No newline at end of file

Modified: sandbox/branches/setuptools-0.6/setuptools.txt
==============================================================================
--- sandbox/branches/setuptools-0.6/setuptools.txt	(original)
+++ sandbox/branches/setuptools-0.6/setuptools.txt	Thu May 16 00:04:59 2013
@@ -217,11 +217,7 @@
 but here are a few tips that will keep you out of trouble in the corner cases:
 
 * Don't use ``-`` or any other character than ``.`` as a separator, unless you
-  really want a post-release.  Remember that ``2.1-rc2`` means you've
-  *already* released ``2.1``, whereas ``2.1rc2`` and ``2.1.c2`` are candidates
-  you're putting out *before* ``2.1``.  If you accidentally distribute copies
-  of a post-release that you meant to be a pre-release, the only safe fix is to
-  bump your main release number (e.g. to ``2.1.1``) and re-release the project.
+  really want a post-release.
 
 * Don't stick adjoining pre-release tags together without a dot or number
   between them.  Version ``1.9adev`` is the ``adev`` prerelease of ``1.9``,
@@ -239,7 +235,7 @@
     >>> parse_version('1.9.a.dev') == parse_version('1.9a0dev')
     True
     >>> parse_version('2.1-rc2') < parse_version('2.1')
-    False
+    True
     >>> parse_version('0.6a9dev-r41475') < parse_version('0.6a9')
     True
 
@@ -2632,6 +2628,10 @@
 Release Notes/Change History
 ----------------------------
 
+0.6c12
+ * Fix documentation describing incorrect pre/post release version comparison
+   logic.
+ 
 0.6c11
  * Fix "bdist_wininst upload" trying to upload same file twice
 

Modified: sandbox/branches/setuptools-0.6/setuptools/command/easy_install.py
==============================================================================
--- sandbox/branches/setuptools-0.6/setuptools/command/easy_install.py	(original)
+++ sandbox/branches/setuptools-0.6/setuptools/command/easy_install.py	Thu May 16 00:04:59 2013
@@ -156,7 +156,7 @@
                 else:
                     self.all_site_dirs.append(normalize_path(d))
         if not self.editable: self.check_site_dir()
-        self.index_url = self.index_url or "http://pypi.python.org/simple"
+        self.index_url = self.index_url or "https://pypi.python.org/simple"
         self.shadow_path = self.all_site_dirs[:]
         for path_item in self.install_dir, normalize_path(self.script_dir):
             if path_item not in self.shadow_path:

Modified: sandbox/branches/setuptools-0.6/setuptools/dist.py
==============================================================================
--- sandbox/branches/setuptools-0.6/setuptools/dist.py	(original)
+++ sandbox/branches/setuptools-0.6/setuptools/dist.py	Thu May 16 00:04:59 2013
@@ -47,7 +47,6 @@
         raise DistutilsSetupError(
             "%r must be a list of strings (got %r)" % (attr,value)
         )
-
 def check_nsp(dist, attr, value):
     """Verify that namespace packages are valid"""
     assert_string_list(dist,attr,value)
@@ -69,6 +68,10 @@
     """Verify that extras_require mapping is valid"""
     try:
         for k,v in value.items():
+            if ':' in k:
+                k,m = k.split(':',1)
+                if pkg_resources.invalid_marker(m):
+                    raise DistutilsSetupError("Invalid environment marker: "+m)
             list(pkg_resources.parse_requirements(v))
     except (TypeError,ValueError,AttributeError):
         raise DistutilsSetupError(
@@ -77,9 +80,6 @@
             "requirement specifiers."
         )
 
-
-
-
 def assert_bool(dist, attr, value):
     """Verify that value is True, False, 0, or 1"""
     if bool(value) != value:

Modified: sandbox/branches/setuptools-0.6/setuptools/package_index.py
==============================================================================
--- sandbox/branches/setuptools-0.6/setuptools/package_index.py	(original)
+++ sandbox/branches/setuptools-0.6/setuptools/package_index.py	Thu May 16 00:04:59 2013
@@ -1,6 +1,6 @@
 """PyPI and direct package downloading"""
 import sys, os.path, re, urlparse, urllib2, shutil, random, socket, cStringIO
-import httplib, urllib
+import httplib, urllib; from setuptools import ssl_support
 from pkg_resources import *
 from distutils import log
 from distutils.errors import DistutilsError
@@ -145,12 +145,11 @@
     urllib2.__version__, require('setuptools')[0].version
 )
 
-
 class PackageIndex(Environment):
     """A distribution index that scans web pages for download URLs"""
 
-    def __init__(self, index_url="http://pypi.python.org/simple", hosts=('*',),
-        *args, **kw
+    def __init__(self, index_url="https://pypi.python.org/simple", hosts=('*',),
+        ca_bundle=None, verify_ssl=True, *args, **kw
     ):
         Environment.__init__(self,*args,**kw)
         self.index_url = index_url + "/"[:not index_url.endswith('/')]
@@ -159,8 +158,9 @@
         self.package_pages = {}
         self.allows = re.compile('|'.join(map(translate,hosts))).match
         self.to_scan = []
-
-
+        if verify_ssl and ssl_support.is_available and (ca_bundle or ssl_support.find_ca_bundle()):
+            self.opener = ssl_support.opener_for(ca_bundle)
+        else: self.opener = urllib2.urlopen
 
     def process_url(self, url, retrieve=False):
         """Evaluate a URL as a possible download, and maybe retrieve it"""
@@ -575,12 +575,13 @@
     def open_url(self, url, warning=None):
         if url.startswith('file:'): return local_open(url)
         try:
-            return open_with_auth(url)
-        except urllib2.HTTPError, v:
-            return v
-        except urllib2.URLError, v:
-            reason = v.reason
-        except httplib.HTTPException, v: 
+            return open_with_auth(url, self.opener)
+        except urllib2.HTTPError:
+            return sys.exc_info()[1]
+        except urllib2.URLError:
+            reason = sys.exc_info()[1].reason
+        except httplib.HTTPException:
+            v = sys.exc_info()[1]
             reason = "%s: %s" % (v.__doc__ or v.__class__.__name__, v)
         if warning:
             self.warn(warning, reason)
@@ -612,7 +613,6 @@
             self.url_ok(url, True)   # raises error if not allowed
             return self._attempt_download(url, filename)
 
-
     def scan_url(self, url):
         self.process_url(url, True)
 
@@ -736,7 +736,7 @@
 
 
 
-def open_with_auth(url):
+def open_with_auth(url, opener=urllib2.urlopen):
     """Open a urllib2 request, handling HTTP authentication"""
 
     scheme, netloc, path, params, query, frag = urlparse.urlparse(url)
@@ -755,7 +755,7 @@
         request = urllib2.Request(url)
 
     request.add_header('User-Agent', user_agent)
-    fp = urllib2.urlopen(request)
+    fp = opener(request)
 
     if auth:
         # Put authentication info back into request URL if same host,

Modified: sandbox/branches/setuptools-0.6/setuptools/sandbox.py
==============================================================================
--- sandbox/branches/setuptools-0.6/setuptools/sandbox.py	(original)
+++ sandbox/branches/setuptools-0.6/setuptools/sandbox.py	Thu May 16 00:04:59 2013
@@ -206,7 +206,7 @@
     def tmpnam(self): self._violation("tmpnam")
 
     def _ok(self,path):
-        if hasattr(_os,'devnull') and path==_os.devnull: return True
+        if hasattr(os,'devnull') and path==os.devnull: return True
         active = self._active
         try:
             self._active = False

Added: sandbox/branches/setuptools-0.6/setuptools/ssl_support.py
==============================================================================
--- (empty file)
+++ sandbox/branches/setuptools-0.6/setuptools/ssl_support.py	Thu May 16 00:04:59 2013
@@ -0,0 +1,246 @@
+import sys, os, socket, urllib2, atexit, re
+from pkg_resources import ResolutionError, ExtractionError
+
+try:
+    import ssl
+except ImportError:
+    ssl = None
+
+__all__ = [
+    'VerifyingHTTPSHandler', 'find_ca_bundle', 'is_available', 'cert_paths',
+    'opener_for'
+]
+
+cert_paths = """
+/etc/pki/tls/certs/ca-bundle.crt
+/etc/ssl/certs/ca-certificates.crt
+/usr/share/ssl/certs/ca-bundle.crt
+/usr/local/share/certs/ca-root.crt
+/etc/ssl/cert.pem
+/System/Library/OpenSSL/certs/cert.pem
+""".strip().split()
+
+
+HTTPSHandler = HTTPSConnection = object
+
+for what, where in (
+    ('HTTPSHandler', ['urllib2','urllib.request']),
+    ('HTTPSConnection', ['httplib', 'http.client']),
+):
+    for module in where:
+        try:
+            exec("from %s import %s" % (module, what))
+        except ImportError:
+            pass
+
+is_available = ssl is not None and object not in (HTTPSHandler, HTTPSConnection)
+
+
+
+
+
+try:
+    from socket import create_connection
+except ImportError:
+    _GLOBAL_DEFAULT_TIMEOUT = getattr(socket, '_GLOBAL_DEFAULT_TIMEOUT', object())
+    def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
+                          source_address=None):
+        """Connect to *address* and return the socket object.
+    
+        Convenience function.  Connect to *address* (a 2-tuple ``(host,
+        port)``) and return the socket object.  Passing the optional
+        *timeout* parameter will set the timeout on the socket instance
+        before attempting to connect.  If no *timeout* is supplied, the
+        global default timeout setting returned by :func:`getdefaulttimeout`
+        is used.  If *source_address* is set it must be a tuple of (host, port)
+        for the socket to bind as a source address before making the connection.
+        An host of '' or port 0 tells the OS to use the default.
+        """   
+        host, port = address
+        err = None
+        for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
+            af, socktype, proto, canonname, sa = res
+            sock = None
+            try:
+                sock = socket.socket(af, socktype, proto)
+                if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
+                    sock.settimeout(timeout)
+                if source_address:
+                    sock.bind(source_address)
+                sock.connect(sa)
+                return sock
+
+            except error:
+                err = True
+                if sock is not None:
+                    sock.close()    
+        if err:
+            raise
+        else:
+            raise error("getaddrinfo returns an empty list")
+
+
+try:
+    from ssl import CertificateError, match_hostname
+except ImportError:
+    class CertificateError(ValueError):
+        pass
+       
+    def _dnsname_to_pat(dn):
+        pats = []
+        for frag in dn.split(r'.'):
+            if frag == '*':
+                # When '*' is a fragment by itself, it matches a non-empty dotless
+                # fragment.
+                pats.append('[^.]+')
+            else:
+                # Otherwise, '*' matches any dotless fragment.
+                frag = re.escape(frag)
+                pats.append(frag.replace(r'\*', '[^.]*'))
+        return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
+       
+    def match_hostname(cert, hostname):
+        """Verify that *cert* (in decoded format as returned by
+        SSLSocket.getpeercert()) matches the *hostname*.  RFC 2818 rules
+        are mostly followed, but IP addresses are not accepted for *hostname*.
+    
+        CertificateError is raised on failure. On success, the function
+        returns nothing.
+        """
+        if not cert:
+            raise ValueError("empty or no certificate")
+        dnsnames = []
+        san = cert.get('subjectAltName', ())
+        for key, value in san:
+            if key == 'DNS':
+                if _dnsname_to_pat(value).match(hostname):
+                    return
+                dnsnames.append(value)
+        if not dnsnames:
+            # The subject is only checked when there is no dNSName entry
+            # in subjectAltName
+            for sub in cert.get('subject', ()):
+                for key, value in sub:
+                    # XXX according to RFC 2818, the most specific Common Name
+                    # must be used.
+                    if key == 'commonName':
+                        if _dnsname_to_pat(value).match(hostname):
+                            return
+                        dnsnames.append(value)
+        if len(dnsnames) > 1:
+            raise CertificateError("hostname %r "
+                "doesn't match either of %s"
+                % (hostname, ', '.join(map(repr, dnsnames))))
+        elif len(dnsnames) == 1:
+            raise CertificateError("hostname %r "
+                "doesn't match %r"
+                % (hostname, dnsnames[0]))
+        else:
+            raise CertificateError("no appropriate commonName or "
+                "subjectAltName fields were found")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class VerifyingHTTPSHandler(HTTPSHandler):
+    """Simple verifying handler: no auth, subclasses, timeouts, etc."""
+
+    def __init__(self, ca_bundle):
+        self.ca_bundle = ca_bundle
+        HTTPSHandler.__init__(self)
+
+    def https_open(self, req):
+        return self.do_open(
+            lambda host, **kw: VerifyingHTTPSConn(host, self.ca_bundle, **kw), req
+        )
+
+
+class VerifyingHTTPSConn(HTTPSConnection):
+    """Simple verifying connection: no auth, subclasses, timeouts, etc."""
+    def __init__(self, host, ca_bundle, **kw):         
+        HTTPSConnection.__init__(self, host, **kw)
+        self.ca_bundle = ca_bundle
+
+    def connect(self):
+        sock = create_connection(
+            (self.host, self.port), getattr(self,'source_address',None)
+        )
+        self.sock = ssl.wrap_socket(
+            sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=self.ca_bundle
+        )               
+        try:
+            match_hostname(self.sock.getpeercert(), self.host)
+        except CertificateError:
+            self.sock.shutdown(socket.SHUT_RDWR)
+            self.sock.close()
+            raise
+
+def opener_for(ca_bundle=None):
+    """Get a urlopen() replacement that uses ca_bundle for verification"""
+    return urllib2.build_opener(
+        VerifyingHTTPSHandler(ca_bundle or find_ca_bundle())
+    ).open
+
+        
+
+_wincerts = None
+
+def get_win_certfile():
+    global _wincerts
+    if _wincerts is not None:
+        return _wincerts.name
+
+    try:        
+        from wincertstore import CertFile
+    except ImportError:
+        return None
+
+    class MyCertFile(CertFile):
+        def __init__(self, stores=(), certs=()):
+            CertFile.__init__(self)
+            for store in stores:
+                self.addstore(store)
+            self.addcerts(certs)
+            atexit.register(self.close)            
+
+    _wincerts = MyCertFile(stores=['CA', 'ROOT'])
+    return _wincerts.name
+
+
+def find_ca_bundle():
+    """Return an existing CA bundle path, or None"""
+    if os.name=='nt':
+        return get_win_certfile()
+    else:
+        for cert_path in cert_paths:
+            if os.path.isfile(cert_path):
+                return cert_path
+    try:
+        return pkg_resources.resource_filename('certifi', 'cacert.pem')
+    except (ImportError, ResolutionError, ExtractionError):
+        return None
+
+
+
+
+

Modified: sandbox/branches/setuptools-0.6/setuptools/tests/test_resources.py
==============================================================================
--- sandbox/branches/setuptools-0.6/setuptools/tests/test_resources.py	(original)
+++ sandbox/branches/setuptools-0.6/setuptools/tests/test_resources.py	Thu May 16 00:04:59 2013
@@ -507,7 +507,7 @@
     def test_get_script_header_jython_workaround(self):
         platform = sys.platform
         sys.platform = 'java1.5.0_13'
-        stdout = sys.stdout
+        stdout, stderr = sys.stdout, sys.stderr
         try:
             # A mock sys.executable that uses a shebang line (this file)
             exe = os.path.normpath(os.path.splitext(__file__)[0] + '.py')
@@ -517,17 +517,17 @@
 
             # Ensure we generate what is basically a broken shebang line
             # when there's options, with a warning emitted
-            sys.stdout = StringIO.StringIO()
+            sys.stdout = sys.stderr = StringIO.StringIO()
             self.assertEqual(get_script_header('#!/usr/bin/python -x',
                                                executable=exe),
                              '#!%s  -x\n' % exe)
             self.assert_('Unable to adapt shebang line' in sys.stdout.getvalue())
-            sys.stdout = StringIO.StringIO()
+            sys.stdout = sys.stderr = StringIO.StringIO()
             self.assertEqual(get_script_header('#!/usr/bin/python',
                                                executable=self.non_ascii_exe),
                              '#!%s -x\n' % self.non_ascii_exe)
             self.assert_('Unable to adapt shebang line' in sys.stdout.getvalue())
         finally:
             sys.platform = platform
-            sys.stdout = stdout
+            sys.stdout, sys.stderr = stdout, stderr
 


More information about the Python-checkins mailing list