From d8c937673c7848afc3488ff8c69a32e068256ef9 Mon Sep 17 00:00:00 2001 From: florentx Date: Wed, 18 Aug 2010 16:38:45 +0200 Subject: [PATCH 1/7] Restore compatibility with Python 2.5 and fix URLs. --- gistapi/gistapi.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/gistapi/gistapi.py b/gistapi/gistapi.py index 97ce14d..0f28455 100644 --- a/gistapi/gistapi.py +++ b/gistapi/gistapi.py @@ -32,10 +32,8 @@ False 'My .bashrc configuration' """ - import urllib - try: import simplejson as json except ImportError: @@ -43,6 +41,9 @@ except ImportError: __all__ = ['Gist', 'Gists'] +GIST_BASE = 'http://gist.github.com/%s' +GIST_JSON = GIST_BASE % 'api/v1/json/%s' + class Gist(object): """Gist Object""" @@ -55,6 +56,10 @@ class Gist(object): if self._json: self.id = json['repo'] + self.url = url = GIST_BASE % self.id + self.embed_url = url + '.js' + self.json_url = url + '.json' + def __getattribute__(self, name): """Gets attributes, but only if needed""" @@ -74,12 +79,8 @@ class Gist(object): setattr(self, 'id', _meta['repo']) else: # Fetch Gist metadata - _meta_url = 'http://gist.github.com/api/v1/json/{0}'.format(self.id) - _meta = json.load(urllib.urlopen(_meta_url))['gists'][0] - - self.url = 'http://github.com/{0}'.format(self.id) - self.embed_url = 'http://github.com/{0}.js'.format(self.id) - self.json_url = 'http://github.com/{0}.json'.format(self.id) + _meta_url = GIST_JSON % self.id + _meta = json.load(urllib2.urlopen(_meta_url))['gists'][0] for key, value in _meta.iteritems(): @@ -99,8 +100,8 @@ class Gist(object): for fn in self.filenames: # Grab file contents - _file_url = 'http://gist.github.com/raw/{0}/{1}'.format(self.id, fn) - _files[fn] = (urllib.urlopen(_file_url).read()) + _file_url = GIST_BASE % 'raw/%s/%s' % (self.id, fn) + _files[fn] = urllib2.urlopen(_file_url).read() return _files @@ -117,7 +118,7 @@ class Gists(object): def fetch_by_user(name): """Returns a set of public Gist objects owned by the given GitHub username""" - _url = 'http://gist.github.com/api/v1/json/gists/{0}'.format(name) + _url = GIST_JSON % 'gists/%s' % name # Return a list of Gist objects return [Gist(json=g) for g in json.load(urllib.urlopen(_url))['gists']] From c002cc77f13cbca41a69cb2c980124496670795f Mon Sep 17 00:00:00 2001 From: florentx Date: Wed, 18 Aug 2010 16:42:21 +0200 Subject: [PATCH 2/7] PEP8 conformance --- gistapi/gistapi.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/gistapi/gistapi.py b/gistapi/gistapi.py index 0f28455..2bd57bf 100644 --- a/gistapi/gistapi.py +++ b/gistapi/gistapi.py @@ -64,7 +64,8 @@ class Gist(object): """Gets attributes, but only if needed""" # Only make external API calls if needed - if name in ['owner', 'description', 'created_at', 'public', 'files', 'filenames', 'repo']: + if name in ('owner', 'description', 'created_at', 'public', + 'files', 'filenames', 'repo'): if not hasattr(self, '_meta'): self._meta = self._get_meta() @@ -116,7 +117,8 @@ class Gists(object): @staticmethod def fetch_by_user(name): - """Returns a set of public Gist objects owned by the given GitHub username""" + """Returns a list of public Gist objects owned by + the given GitHub username""" _url = GIST_JSON % 'gists/%s' % name @@ -128,5 +130,5 @@ if __name__ == '__main__': import doctest print('hello') a = 'bob' - + doctest.testmod() From 5f09079556b36636fb2b63a3fb6cd3ad7af33b69 Mon Sep 17 00:00:00 2001 From: florentx Date: Wed, 18 Aug 2010 16:47:21 +0200 Subject: [PATCH 3/7] Switch to urllib2. --- gistapi/gistapi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gistapi/gistapi.py b/gistapi/gistapi.py index 2bd57bf..b776b37 100644 --- a/gistapi/gistapi.py +++ b/gistapi/gistapi.py @@ -32,7 +32,7 @@ False 'My .bashrc configuration' """ -import urllib +import urllib2 try: import simplejson as json @@ -123,7 +123,7 @@ class Gists(object): _url = GIST_JSON % 'gists/%s' % name # Return a list of Gist objects - return [Gist(json=g) for g in json.load(urllib.urlopen(_url))['gists']] + return [Gist(json=g) for g in json.load(urllib2.urlopen(_url))['gists']] if __name__ == '__main__': From 192b9e563444cf7a9aaf37794308f97e889730ae Mon Sep 17 00:00:00 2001 From: florentx Date: Wed, 18 Aug 2010 16:49:44 +0200 Subject: [PATCH 4/7] Cache the Gist.files attribute. --- gistapi/gistapi.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gistapi/gistapi.py b/gistapi/gistapi.py index b776b37..0bb7a8b 100644 --- a/gistapi/gistapi.py +++ b/gistapi/gistapi.py @@ -97,7 +97,10 @@ class Gist(object): @property def files(self): """Fetches a gists files and stores them in the 'files' property""" - _files = {} + try: + return self._files + except AttributeError: + self._files = _files = {} for fn in self.filenames: # Grab file contents From e4eb48088be3dac443506045c7126557f8ed9435 Mon Sep 17 00:00:00 2001 From: florentx Date: Wed, 18 Aug 2010 17:21:25 +0200 Subject: [PATCH 5/7] Allow to add, rename, modify and delete files in a Gist object. --- gistapi/gistapi.py | 91 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 4 deletions(-) diff --git a/gistapi/gistapi.py b/gistapi/gistapi.py index 0bb7a8b..5be1bce 100644 --- a/gistapi/gistapi.py +++ b/gistapi/gistapi.py @@ -32,6 +32,8 @@ False 'My .bashrc configuration' """ +import os.path +import urllib import urllib2 try: @@ -41,6 +43,10 @@ except ImportError: __all__ = ['Gist', 'Gists'] +# Set your own credentials +USERNAME = 'YOUR GITHUB USERNAME' +TOKEN = 'YOUR GITHUB TOKEN' + GIST_BASE = 'http://gist.github.com/%s' GIST_JSON = GIST_BASE % 'api/v1/json/%s' @@ -48,9 +54,11 @@ GIST_JSON = GIST_BASE % 'api/v1/json/%s' class Gist(object): """Gist Object""" - def __init__(self, id=None, json=None): + def __init__(self, id=None, json=None, username=None, token=None): self.id = id self._json = json + self._username = username + self._token = token # Map given repo id to gist id if none exists if self._json: @@ -59,6 +67,7 @@ class Gist(object): self.url = url = GIST_BASE % self.id self.embed_url = url + '.js' self.json_url = url + '.json' + self.post_url = GIST_BASE % 'gists/%s' % self.id def __getattribute__(self, name): """Gets attributes, but only if needed""" @@ -86,14 +95,87 @@ class Gist(object): for key, value in _meta.iteritems(): if key == 'files': - # Remap file key from API - setattr(self, 'filenames', value) + # Remap file key from API. This structure stores renames. + setattr(self, 'filenames', dict((fn, fn) for fn in value)) + # This attribute stores the {new_name: original_name} mapping. + setattr(self, '_renames', {}) else: # Attach properties to object setattr(self, key, value) return _meta + def _post(self, params, headers={}): + """POST to the web form (internal method).""" + request = urllib2.Request(self.post_url, + urllib.urlencode(params), + headers) + try: + response = urllib2.urlopen(request) + except IOError, exc: + response = exc + return response.code, response.msg + + def reset(self): + """Clear the local cache.""" + if hasattr(self, '_files'): + del self._files + if hasattr(self, '_meta'): + del self._meta + + def auth(self, username, token): + """Set credentials.""" + self._username = username + self._token = token + + def add(self, name, content=''): + """Add file to the Gist.""" + if name in self.filenames.values(): + raise KeyError('File %r already exist' % name) + defaultname = 'gistfile%s' % len(self.filenames) + self.files[name] = content + self.filenames[defaultname] = name + self._renames[name] = defaultname + + def rename(self, from_name, to_name): + """Rename a file.""" + filenames = self.filenames.values() + if from_name not in filenames: + raise KeyError('File %r does not exist' % from_name) + if to_name in filenames: + raise KeyError('File %r already exist' % to_name) + orig_name = self._renames.pop(from_name, from_name) + self.files[to_name] = self.files.pop(from_name) + self.filenames[orig_name] = to_name + self._renames[to_name] = orig_name + + def delete(self, name): + """Delete a file.""" + orig_name = self._renames.pop(name, name) + del self.files[name] + del self.filenames[orig_name] + + def save(self): + """Upload the changes to Github.""" + params = { + '_method': 'put', + 'login': self._username or USERNAME, + 'token': self._token or TOKEN, + } + for orig, fn in self.filenames.items(): + ext = os.path.splitext(fn)[1] or '.txt' + content = self.files[fn] + params.update({ + 'file_name[%s]' % orig: fn, + 'file_ext[%s]' % orig: ext, + 'file_contents[%s]' % orig: content, + }) + code, msg = self._post(params=params) + if code == 200: # OK + # If successful, clear the cache + self.reset() + return code, msg + @property def files(self): """Fetches a gists files and stores them in the 'files' property""" @@ -126,7 +208,8 @@ class Gists(object): _url = GIST_JSON % 'gists/%s' % name # Return a list of Gist objects - return [Gist(json=g) for g in json.load(urllib2.urlopen(_url))['gists']] + return [Gist(json=g, username=self._username, token=self._token) + for g in json.load(urllib2.urlopen(_url))['gists']] if __name__ == '__main__': From f29e1b6e26f0a3ca0b0fe69cdf3038dd4dc24351 Mon Sep 17 00:00:00 2001 From: florentx Date: Wed, 18 Aug 2010 17:24:53 +0200 Subject: [PATCH 6/7] Typo and PEP257 conformance. --- gistapi/gistapi.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gistapi/gistapi.py b/gistapi/gistapi.py index 5be1bce..0b9e4b8 100644 --- a/gistapi/gistapi.py +++ b/gistapi/gistapi.py @@ -70,7 +70,7 @@ class Gist(object): self.post_url = GIST_BASE % 'gists/%s' % self.id def __getattribute__(self, name): - """Gets attributes, but only if needed""" + """Get attributes, but only if needed.""" # Only make external API calls if needed if name in ('owner', 'description', 'created_at', 'public', @@ -81,7 +81,7 @@ class Gist(object): return object.__getattribute__(self, name) def _get_meta(self): - """Fetches Gist metadata""" + """Fetch Gist metadata.""" # Use json data provided if available if self._json: @@ -178,7 +178,7 @@ class Gist(object): @property def files(self): - """Fetches a gists files and stores them in the 'files' property""" + """Fetch Gist's files and store them in the 'files' property.""" try: return self._files except AttributeError: @@ -202,7 +202,7 @@ class Gists(object): @staticmethod def fetch_by_user(name): - """Returns a list of public Gist objects owned by + """Return a list of public Gist objects owned by the given GitHub username""" _url = GIST_JSON % 'gists/%s' % name From d3fe07f2b5f8eb6e864dafe0f242cf0dcfbe2638 Mon Sep 17 00:00:00 2001 From: florentx Date: Wed, 18 Aug 2010 17:38:42 +0200 Subject: [PATCH 7/7] Do not use self from a static method :-). --- gistapi/gistapi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gistapi/gistapi.py b/gistapi/gistapi.py index 0b9e4b8..35baf5a 100644 --- a/gistapi/gistapi.py +++ b/gistapi/gistapi.py @@ -196,19 +196,19 @@ class Gists(object): """Gist API wrapper""" def __init__(self, username=None, token=None): - # Token-based Authentication is unnecesary, gist api still in alpha + # Token-based Authentication is unnecessary, gist api still in alpha self._username = username self._token = token @staticmethod def fetch_by_user(name): """Return a list of public Gist objects owned by - the given GitHub username""" + the given GitHub username.""" _url = GIST_JSON % 'gists/%s' % name # Return a list of Gist objects - return [Gist(json=g, username=self._username, token=self._token) + return [Gist(json=g) for g in json.load(urllib2.urlopen(_url))['gists']]