Hello,

I needed support in easy_install for installing from HTTP
digest and basic authenticated sites so I wrote the included
patch. It currently supports specifying username and password
information at the command line (however misguided that may be)
and prompting the user interactively. It should also be easy to
integrate with graphical interfaces. Please consider it for
inclusion in the next release.

Thanks,
-- 
  David D. Smith
Index: setuptools/package_index.py
===================================================================
--- setuptools/package_index.py (リビジョン 53164)
+++ setuptools/package_index.py (作業コピー)
@@ -20,9 +20,27 @@
 
 __all__ = [
     'PackageIndex', 'distros_for_url', 'parse_bdist_wininst',
-    'interpret_distro_name',
+    'interpret_distro_name', 'detect_auth_scheme',
 ]
 
+def detect_auth_scheme(url):
+    """Return (scheme, realm) or (None, None) for a URL"""
+    scheme, realm = None, None
+    try:
+        f = urllib2.urlopen(url)
+        f.close()
+    except urllib2.HTTPError, e:
+        if e.code == 401:
+            import re
+            realm = re.findall('realm="([^"]*)"', 
e.headers['WWW-Authenticate'])
+            if realm:
+                realm = realm[0]
+            else:
+                realm = None
+            scheme = e.headers['WWW-Authenticate'].split()[0].lower()
+        else:
+            raise e
+    return (scheme, realm)
 
 def parse_bdist_wininst(name):
     """Return (base,pyversion) or (None,None) for possible .exe name"""
@@ -166,7 +184,7 @@
     """A distribution index that scans web pages for download URLs"""
 
     def __init__(self, index_url="http://www.python.org/pypi";, hosts=('*',),
-        sf_mirrors=None, *args, **kw
+        sf_mirrors=None, username=None, password=None, *args, **kw
     ):
         Environment.__init__(self,*args,**kw)
         self.index_url = index_url + "/"[:not index_url.endswith('/')]
@@ -182,6 +200,8 @@
                 self.sf_mirrors = map(str.strip, sf_mirrors)
         else:
             self.sf_mirrors = ()
+        self.username = username
+        self.password = password
 
 
     def _get_mirrors(self):
@@ -654,13 +674,42 @@
 
 
 
-    def open_url(self, url):
+    def open_url(self, url, username=None, password=None):
+        """username and/or password can be a string or a callable.
+        If a callable, they are called with the host, scheme, and realm."""
+
+        username = username or self.username
+        password = password or self.password
+
         if url.startswith('file:'):
             return local_open(url)
         try:
+            scheme, realm = detect_auth_scheme(url)
+            host = ""
+            if isinstance(url, urllib2.Request):
+                host = url.get_host()
+            else:
+                host = urlparse.urlparse(url)[1]
+            if not scheme:
+                return urllib2.urlopen(url)
+            else:
+                password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
+                if scheme.lower() == 'digest':
+                    auth = urllib2.HTTPDigestAuthHandler(password_mgr)
+                elif scheme.lower() == 'basic':
+                    auth = urllib2.HTTPBasicAuthHandler(password_mgr)
+                else:
+                    auth = None
+                if auth:
+                    if callable(username):
+                        username = username(host, scheme, realm)
+                    if callable(password):
+                        password = password(host, scheme, realm)
+                    password_mgr.add_password(realm, host, username, password)
+                opener = urllib2.build_opener(auth)
             request = urllib2.Request(url)
             request.add_header('User-Agent', user_agent)
-            return urllib2.urlopen(request)
+            return opener.open(request)
         except urllib2.HTTPError, v:
             return v
         except urllib2.URLError, v:
Index: setuptools/command/easy_install.py
===================================================================
--- setuptools/command/easy_install.py  (リビジョン 53164)
+++ setuptools/command/easy_install.py  (作業コピー)
@@ -9,7 +9,8 @@
 
 __ http://peak.telecommunity.com/DevCenter/EasyInstall
 """
-import sys, os.path, zipimport, shutil, tempfile, zipfile, re, stat, random
+import sys, os.path, zipimport, shutil, tempfile, zipfile, re, stat, \
+    random, getpass
 from glob import glob
 from setuptools import Command
 from setuptools.sandbox import run_setup
@@ -71,6 +72,8 @@
         ('no-deps', 'N', "don't install dependencies"),
         ('allow-hosts=', 'H', "pattern(s) that hostnames must match"),
         ('sf-mirrors=', None, "Sourceforge mirror(s) to use"),
+        ('username', 'u', "username for HTTP authentication"),
+        ('password', 'p', "password for HTTP authentication"),
     ]
     boolean_options = [
         'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy',
@@ -110,7 +113,17 @@
         self.distribution._set_command_options(
             self, self.distribution.get_option_dict('easy_install')
         )
+        self.username = self.prompt_username
+        self.password = self.prompt_password
 
+    def prompt_username(self, host, scheme, realm):
+        self.announce("Host %s requires %s authentication." % (host, scheme),
+                      level=9)
+        return raw_input("Username for [EMAIL PROTECTED]: " % (realm, host))
+
+    def prompt_password(self, host, scheme, realm):
+        return getpass.getpass("Password for [EMAIL PROTECTED]: " % (realm, 
host))
+
     def delete_blockers(self, blockers):
         for filename in blockers:
             if os.path.exists(filename) or os.path.islink(filename):
@@ -169,7 +182,8 @@
         if self.package_index is None:
             self.package_index = self.create_index(
                 self.index_url, search_path = self.shadow_path, hosts=hosts,
-                sf_mirrors = self.sf_mirrors
+                sf_mirrors = self.sf_mirrors, username=self.username,
+                password=self.password
             )
         self.local_index = Environment(self.shadow_path+sys.path)
 
_______________________________________________
Distutils-SIG maillist  -  [email protected]
http://mail.python.org/mailman/listinfo/distutils-sig

Reply via email to