#!/usr/bin/env python # # Copyright (c) 2005-2008 Dustin Sallings # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # # """ Interface to github's API (v2). Basic usage: g = GitHub() for r in g.user.search('dustin'): print r.name See the GitHub docs or README.markdown for more usage. Copyright (c) 2007 Dustin Sallings """ # GAE friendly URL detection (theoretically) try: import urllib2 default_fetcher = urllib2.urlopen except LoadError: pass import urllib import xml import xml.dom.minidom def _string_parser(x): """Extract the data from the first child of the input.""" return x.firstChild.data _types = { 'string': _string_parser, 'integer': lambda x: int(_string_parser(x)), 'float': lambda x: float(_string_parser(x)), 'datetime': _string_parser, 'boolean': lambda x: _string_parser(x) == 'true' } def _parse(el): """Generic response parser.""" type = 'string' if el.attributes and 'type' in el.attributes.keys(): type = el.attributes['type'].value elif el.localName in _types: type = el.localName elif len(el.childNodes) > 1: # This is a container, find the child type type = None ch = el.firstChild while ch and not type: if ch.localName == 'type': type = ch.firstChild.data ch = ch.nextSibling if not type: raise Exception("Can't parse %s, known: %s" % (el.toxml(), repr(_types.keys()))) return _types[type](el) def parses(t): """Parser for a specific type in the github response.""" def f(orig): orig.parses = t return orig return f def with_temporary_mappings(m): """Allow temporary localized altering of type mappings.""" def f(orig): def every(self, *args): global _types o = _types.copy() for k,v in m.items(): if v: _types[k] = v else: del _types[k] try: return orig(self, *args) finally: _types = o return every return f @parses('array') def _parseArray(el): rv = [] ch = el.firstChild while ch: if ch.nodeType != xml.dom.Node.TEXT_NODE and ch.firstChild: rv.append(_parse(ch)) ch=ch.nextSibling return rv class BaseResponse(object): """Base class for XML Response Handling.""" def __init__(self, el): ch = el.firstChild while ch: if ch.nodeType != xml.dom.Node.TEXT_NODE and ch.firstChild: ln = ch.localName.replace('-', '_') self.__dict__[ln] = _parse(ch) ch=ch.nextSibling def __repr__(self): return "<<%s>>" % str(self.__class__) class User(BaseResponse): """A github user.""" parses = 'user' def __repr__(self): return "<>" % self.name class Plan(BaseResponse): """A github plan.""" parses = 'plan' def __repr__(self): return "<>" % self.name class Repository(BaseResponse): """A repository.""" parses = 'repository' @property def owner_name(self): if hasattr(self, 'owner'): return self.owner else: return self.username def __repr__(self): return "<>" % (self.owner_name, self.name) class PublicKey(BaseResponse): """A public key.""" parses = 'public-key' title = 'untitled' def __repr__(self): return "<>" % self.title class Commit(BaseResponse): """A commit.""" parses = 'commit' def __repr__(self): return "<>" % self.id class Parent(Commit): """A commit parent.""" parses = 'parent' class Author(User): """A commit author.""" parses = 'author' class Committer(User): """A commit committer.""" parses = 'committer' class Issue(BaseResponse): """An issue within the issue tracker.""" parses = 'issue' def __repr__(self): return "<>" % self.number class Label(BaseResponse): """A Label within the issue tracker.""" parses = 'label' def __repr__(self): return "<