Hi, GitHub API v3 is intentionally broken (see http://developer.github.com/v3/auth/):
> The main difference is that the RFC requires unauthenticated requests > to be answered with 401 Unauthorized responses. In many places, this > would disclose the existence of user data. Instead, the GitHub API > responds with 404 Not Found. This may cause problems for HTTP > libraries that assume a 401 Unauthorized response. The solution is to > manually craft the Authorization header. Unfortunately, urllib2.HTTPBasicAuthHandler relies on the standard-conformant behavior. So a naive programmer (like me) who wants to program against GitHub API using urllib2 (and foolishly ignores this comment about the API non-conformance, because he thinks GitHub wouldn't be that stupid and break all Python applications) writes something like the attached script, spends couple of hours hitting this issue, until he tries python-requests (which work) and his (mistaken) conclusion is that urllib2 is a piece of crap which should never be used again. I am not sure how widespread is this breaking of RFC, but it seems to me that quite a lot (e.g., http://stackoverflow.com/a/9698319/164233 which just en passant expects urllib2 authentication stuff to be useless), and the question is whether it shouldn't be documented somehow and/or urllib2.HTTPBasicAuthHandler shouldn't be modified to try add Authenticate header first. Any suggestions? Best, Matěj -- http://www.ceplovi.cz/matej/, Jabber: mc...@ceplovi.cz GPG Finger: 89EF 4BC6 288A BF43 1BAB 25C3 E09F EF25 D964 84AC For a successful technology, reality must take precedence over public relations, for nature cannot be fooled. -- R. P. Feynman's concluding sentence in his appendix to the Challenger Report
import urllib.request import base64 import json import os import logging from configparser import SafeConfigParser logging.basicConfig(format='%(levelname)s:%(funcName)s:%(message)s', level=logging.DEBUG) class HTTPBrokenBasicAuthHandler(urllib.request.AbstractHTTPHandler, urllib.request.AbstractBasicAuthHandler): auth_header = 'Authorization' def __init__(self, *args, **kwargs): urllib.request.AbstractHTTPHandler.__init__(self, *args, **kwargs) self.set_http_debuglevel(2) def do_open(self, http_class, req, **http_conn_args): user, password = self.passwd.find_user_password(None, req.full_url) b64str = base64.standard_b64encode( '{}:{}'.format(user, password)).decode('ascii') req.add_header(self.auth_header, 'Basic {}'.format(b64str)) urllib.request.AbstractHTTPHandler(self, http_class, req, http_conn_args) def http_error_401(self, req, fp, code, msg, headers): url = req.full_url response = self.http_error_auth_reqed('www-authenticate', url, req, headers) self.reset_retry_count() return response cp = SafeConfigParser() cp.read(os.path.expanduser('~/.githubrc')) # That configuration file should look something like # [github] # user=mylogin # password=myveryverysecretpassword gh_user = cp.get('github', 'user') gh_passw = cp.get('github', 'password') repo = 'odt2rst' pwd_manager = urllib.request.HTTPPasswordMgrWithDefaultRealm() pwd_manager.add_password(None, uri='https://api.github.com', user=gh_user, passwd=gh_passw) auth_handler = HTTPBrokenBasicAuthHandler(pwd_manager) opener = urllib.request.build_opener(auth_handler) urllib.request.install_opener(opener) API_URL = "https://api.github.com/repos/{0}/{1}/issues/" gh_url = API_URL.format(gh_user, repo, 1) logging.debug("gh_url = {0}".format(gh_url)) req = urllib.request.Request(gh_url) create_data = { 'title': 'Testing bug summary', 'body': '''This is a testing bug. I am writing here this stuff, just to have some text for body.''', 'labels': ['BEbug'] } handler = urllib.request.urlopen(req, json.dumps(create_data).encode('utf8')) print(handler.getcode()) print(handler)
signature.asc
Description: OpenPGP digital signature
_______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com