[Distutils] HTTP authentication support
David Smith
davidsmith at acm.org
Thu Dec 28 05:25:50 CET 2006
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 %s@%s: " % (realm, host))
+
+ def prompt_password(self, host, scheme, realm):
+ return getpass.getpass("Password for %s@%s: " % (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)
More information about the Distutils-SIG
mailing list