From d7575dc45657046f4d5a3a4e2c84a42aa4c541ed Mon Sep 17 00:00:00 2001 From: Gene Wood Date: Fri, 13 Apr 2018 15:21:48 -0700 Subject: [PATCH 1/7] agithub:Spellcheck errror diagnostic and docstrings From 54b4b08d49c0e472b9a51f308a1ea0309d3bb691 --- agithub/GitHub.py | 2 +- agithub/base.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agithub/GitHub.py b/agithub/GitHub.py index 54454af..dfb7e94 100644 --- a/agithub/GitHub.py +++ b/agithub/GitHub.py @@ -46,7 +46,7 @@ def __init__(self, username=None, password=None, token=None, *args, **kwargs): def generateAuthHeader(self, username=None, password=None, token=None): if token is not None: if password is not None: - raise TypeError("You cannot use both password and oauth token authenication") + raise TypeError("You cannot use both password and OAuth token authentication") return 'Token %s' % token elif username is not None: if password is None: diff --git a/agithub/base.py b/agithub/base.py index 8e2b12e..31da8da 100644 --- a/agithub/base.py +++ b/agithub/base.py @@ -169,7 +169,7 @@ def delete(self, url, headers={}, **params): def patch(self, url, body=None, headers={}, **params): """ Do a http patch request on the given url with given body, headers and parameters - Parameters is a dictionary that will will be urlencoded + Parameters is a dictionary that will will be url-encoded """ url += self.urlencode(params) if not 'content-type' in headers: From 2ed2abe134868f2575d193531e4c129ca583a0a4 Mon Sep 17 00:00:00 2001 From: Gene Wood Date: Fri, 13 Apr 2018 15:39:43 -0700 Subject: [PATCH 2/7] Remove duplicate code In the Client class: setConnectionProperties() - Rely on _fix_headers to fix the case of default headers at the time of request instead of duplicating its code _fix_headers() - When we addin default headers, ensure the case is correct This is a reworking of 079976b5aca01747aa79e2fd3a3d1f915d183f7e from the `next` branch which accomplishes this differently --- agithub/base.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/agithub/base.py b/agithub/base.py index 31da8da..e5df7de 100644 --- a/agithub/base.py +++ b/agithub/base.py @@ -136,12 +136,6 @@ def setConnectionProperties(self, prop): self.default_headers.update(prop.extra_headers) self.prop = prop - # Enforce case restrictions on self.default_headers - tmp_dict = {} - for k,v in self.default_headers.items(): - tmp_dict[k.lower()] = v - self.default_headers = tmp_dict - def head(self, url, headers={}, **params): url += self.urlencode(params) return self.request('HEAD', url, None, headers) @@ -210,7 +204,7 @@ def _fix_headers(self, headers): # Add default headers (if unspecified) for k,v in self.default_headers.items(): if k not in headers: - headers[k] = v + headers[k.lower()] = v return headers def urlencode(self, params): From 90c69b85ac7efb15d8c3f535b8d64ece32d3489a Mon Sep 17 00:00:00 2001 From: Gene Wood Date: Fri, 13 Apr 2018 15:54:56 -0700 Subject: [PATCH 3/7] Docstring and comment fixes This is a reworking of fad02faaf393c54d23a65b1a0220840669af9011 from the `next` branch. It removes a docstring change that moves away from PEP008 --- agithub/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agithub/base.py b/agithub/base.py index e5df7de..693b7c8 100644 --- a/agithub/base.py +++ b/agithub/base.py @@ -292,7 +292,7 @@ def decode_body(self): def processBody(self): ''' - Retrieve the body of the response, encoding it into a usuable + Retrieve the body of the response, encoding it into a usable form based on the media-type (mime-type) ''' handlerName = self.mangled_mtype() @@ -323,7 +323,7 @@ def application_json(self): # XXX: This isn't technically correct, but we'll hope for the best. # Patches welcome! - # Insert new media-type handlers here + # Insert new Response media-type handlers here class RequestBody(Body): ''' From 2db5876edde8d7579ca6634cd86ee552d55bd7b2 Mon Sep 17 00:00:00 2001 From: Gene Wood Date: Fri, 13 Apr 2018 15:56:42 -0700 Subject: [PATCH 4/7] test: Make it directly executable by Unix systems This is a from 51f5b78a08b650b12d6dffacce3d1b27a5c8d650 from the `next` branch. --- agithub/test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agithub/test.py b/agithub/test.py index 4b382f5..c152391 100644 --- a/agithub/test.py +++ b/agithub/test.py @@ -1,4 +1,5 @@ -# Copyright 2012-2016 Jonathan Paugh and contributors +#!/usr/bin/env python3 +# Copyright 2012-2016 Jonathan Paugh and contributors # See COPYING for license details from __future__ import print_function from agithub.GitHub import GitHub From 5f61c3ece21d4e2e8f7799af6edc703f886a0a84 Mon Sep 17 00:00:00 2001 From: Gene Wood Date: Fri, 13 Apr 2018 16:01:28 -0700 Subject: [PATCH 5/7] Update ChangeLog - TODO: Reuse TCP connections - Update "Parse Content-Type" bullet This adds new content to v2.0 about parsing Content-Type headers correctly. v2.0 came out in the past but I see the ctypeParameters dict it mentions. This is a from 6a44c59509ecb9486b14f73551c66a4dfc2f07d6 from the `next` branch. --- ChangeLog.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 6ec5e57..f6b88ab 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -24,6 +24,23 @@ Upcoming Unscheduled ----------- + +* Support reusing TCP connections, and "pipelining" of requests, a la + RFC 2068, Sect 8.1, L2377 + + - The user must ask for pipelining, and supply a callback function + to be called after a response is received. + - Rename Client.request() -> Client.addRequest() (or such) + - Have Client.addRequest() check whether a persistent connection is + wanted, and save the entire request in a Client.pendingRequests + list in that case + - Create a new Client.sendRequests() method which the user may call + to send all requests up the pipeline. (It should work even if the + server does not support pipelining) + - Call the user-supplied callback whenever a request is received. + There are some concurrency issues here, and we may elect to call + the callback only after *all* requests are received. + * Create a script to pack the basic module and any given set of service-specific classes as one file. I still like the idea that a given API (e.g. Facebook) could be packed into a single file, and @@ -82,6 +99,10 @@ v2.0 - Use `application/octet-stream` for unknown media type - Spell 'GitHub' correctly +* Parse Content-Type header correctly; make a dict of the + parameters (Content.ctypeParameters) available to the media-type + handlers + v1.3 ---- A stable branch, with a lot of bug fixes! (Thanks to all who From 74b49d20fc24b54b07094a2c086e1e758dcc73ab Mon Sep 17 00:00:00 2001 From: Gene Wood Date: Fri, 13 Apr 2018 16:06:18 -0700 Subject: [PATCH 6/7] ResponseBody: New media-types application/xml, text/xml We use xml.dom.minidom to de-serialize the results. This is a from 3d373435c8110612cad061e9a9b31a7a1abd752c from the `next` branch. --- agithub/base.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/agithub/base.py b/agithub/base.py index 693b7c8..95ebcbe 100644 --- a/agithub/base.py +++ b/agithub/base.py @@ -5,6 +5,7 @@ import re from functools import partial, update_wrapper +import xml.dom.minidom import sys if sys.version_info[0:2] > (3,0): @@ -323,6 +324,22 @@ def application_json(self): # XXX: This isn't technically correct, but we'll hope for the best. # Patches welcome! + def application_xml(self): + self.decode_body() + + try: + pybody = xml.dom.minidom.parseString(self.body) + except Exception: #TODO: What kind of exceptions? + pybody = self.body + + return pybody + + + text_xml = application_xml + # The difference between text/xml and application/xml is whether it + # is human-readable or not. For our purposes, there is no + # difference. RFC 3023, L270. + # Insert new Response media-type handlers here class RequestBody(Body): From 0706cfe0199ba54b0acc0f46c280a56f96797cb3 Mon Sep 17 00:00:00 2001 From: Gene Wood Date: Fri, 13 Apr 2018 16:11:01 -0700 Subject: [PATCH 7/7] Fix Client.setConnectionProperties: unconditionally set self.default_headers In case the API does not supply extra_headers, self.request() will fail trying to delete the 'content-type' header from them. Including the _default_headers (always) ensures that 'content-type' is defined This is a from e1143ed97638f18ccb17f7c409641100ffa58f92 from the `next` branch. --- agithub/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agithub/base.py b/agithub/base.py index 95ebcbe..115d83f 100644 --- a/agithub/base.py +++ b/agithub/base.py @@ -131,9 +131,9 @@ def setConnectionProperties(self, prop): if type(prop) is not ConnectionProperties: raise TypeError("Client.setConnectionProperties: Expected ConnectionProperties object") + self.default_headers = _default_headers.copy() if prop.extra_headers is not None: prop.filterEmptyHeaders() - self.default_headers = _default_headers.copy() self.default_headers.update(prop.extra_headers) self.prop = prop