]> jfr.im git - z_archive/twitter.git/blobdiff - twitter/api.py
typo
[z_archive/twitter.git] / twitter / api.py
index 52db9739be8821be4b651f7ea93b5f62cb49290b..42d9e53a62aee3f00c8959ac9822805688023c1f 100644 (file)
@@ -1,11 +1,11 @@
 
 
-from base64 import b64encode
-from urllib import urlencode
-
-import httplib
+import urllib2
 
 from exceptions import Exception
 
 
 from exceptions import Exception
 
+from twitter.twitter_globals import POST_ACTIONS
+from twitter.auth import UserPassAuth, NoAuth
+
 def _py26OrGreater():
     import sys
     return sys.hexversion > 0x20600f0
 def _py26OrGreater():
     import sys
     return sys.hexversion > 0x20600f0
@@ -17,89 +17,103 @@ else:
 
 class TwitterError(Exception):
     """
 
 class TwitterError(Exception):
     """
-    Exception thrown by the Twitter object when there is an
-    error interacting with twitter.com.
+    Base Exception thrown by the Twitter object when there is a
+    general error interacting with the API.
     """
     pass
 
     """
     pass
 
-# These actions require POST http requests instead of GET
-_POST_ACTIONS = [
-    "create", "update", "destroy", "new", "follow", "leave",
-    ]
+class TwitterHTTPError(TwitterError):
+    """
+    Exception thrown by the Twitter object when there is an
+    HTTP error interacting with twitter.com.
+    """
+    def __init__(self, e, uri, format, encoded_args):
+      self.e = e
+      self.uri = uri
+      self.format = format
+      self.encoded_args = encoded_args
+
+    def __str__(self):
+        return "Twitter sent status %i for URL: %s.%s using parameters: (%s)\ndetails: %s" %(
+                    self.e.code, self.uri, self.format, self.encoded_args, self.e.fp.read())
 
 class TwitterCall(object):
     def __init__(
 
 class TwitterCall(object):
     def __init__(
-        self, username, password, format, domain, uri="", agent=None):
-        self.username = username
-        self.password = password
+        self, auth, format, domain, uri="", agent=None,
+        encoded_args=None, secure=True):
+        self.auth = auth
         self.format = format
         self.domain = domain
         self.uri = uri
         self.agent = agent
         self.format = format
         self.domain = domain
         self.uri = uri
         self.agent = agent
+        self.encoded_args = encoded_args
+        self.secure = secure
+
     def __getattr__(self, k):
         try:
             return object.__getattr__(self, k)
         except AttributeError:
             return TwitterCall(
     def __getattr__(self, k):
         try:
             return object.__getattr__(self, k)
         except AttributeError:
             return TwitterCall(
-                self.username, self.password, self.format, self.domain,
-                self.uri + "/" + k, self.agent)
+                self.auth, self.format, self.domain,
+                self.uri + "/" + k, self.agent, self.encoded_args, self.secure)
+
     def __call__(self, **kwargs):
     def __call__(self, **kwargs):
-        uri = self.uri
+        uri = self.uri.strip("/")
         method = "GET"
         method = "GET"
-        for action in _POST_ACTIONS:
+        for action in POST_ACTIONS:
             if self.uri.endswith(action):
                 method = "POST"
             if self.uri.endswith(action):
                 method = "POST"
+                if (self.agent):
+                    kwargs["source"] = self.agent
                 break
                 break
-            if (self.agent):
-                kwargs["source"] = self.agent
-        
-        id = kwargs.pop('id', None)
-        if id:
-            uri += "/%s" %(id)
-            
-        encoded_kwargs = urlencode(kwargs.items())
+
+        secure_str = ''
+        if self.secure:
+            secure_str = 's'
+        dot = ""
+        if self.format != '':
+            dot = "."
+        uriBase = "http%s://%s/%s%s%s" %(
+                    secure_str, self.domain, uri, dot, self.format)
+
+        if (not self.encoded_args):
+            if kwargs.has_key('id'):
+                uri += "/%s" %(kwargs['id'])
+
+            self.encoded_args = self.auth.encode_params(uriBase, method, kwargs)
+
         argStr = ""
         argStr = ""
-        if kwargs and (method == "GET"):
-            argStr = "?" + encoded_kwargs
+        argData = None
+        if (method == "GET"):
+            if self.encoded_args:
+                argStr = "?%s" %(self.encoded_args)
+        else:
+            argData = self.encoded_args
 
         headers = {}
         if (self.agent):
             headers["X-Twitter-Client"] = self.agent
 
         headers = {}
         if (self.agent):
             headers["X-Twitter-Client"] = self.agent
-        if (self.username):
-            headers["Authorization"] = "Basic " + b64encode("%s:%s" %(
-                self.username, self.password))
-        if method == "POST":
-            headers["Content-type"] = "application/x-www-form-urlencoded"
-            headers["Content-length"] = len(encoded_kwargs)
+        if self.auth is not None:
+            headers.update(self.auth.generate_headers())
+
+        req = urllib2.Request(uriBase+argStr, argData, headers)
         
         
-        c = httplib.HTTPConnection(self.domain)
         try:
         try:
-            c.putrequest(method, "%s.%s%s" %(
-                uri, self.format, argStr))
-            for item in headers.iteritems():
-                c.putheader(*item)
-            c.endheaders()
-            if method == "POST":
-                c.send(encoded_kwargs)
-            r = c.getresponse()
-
-            if (r.status == 304):
-                return []
-            elif (r.status != 200):
-                raise TwitterError(
-                    "Twitter sent status %i for URL: %s.%s using parameters: (%s)\ndetails: %s" %(
-                        r.status, uri, self.format, encoded_kwargs, r.read()))
+            handle = urllib2.urlopen(req)
             if "json" == self.format:
             if "json" == self.format:
-                return json.loads(r.read())
+                return json.loads(handle.read())
+            else:
+                return handle.read()
+        except urllib2.HTTPError, e:
+            if (e.code == 304):
+                return []
             else:
             else:
-                return r.read()
-        finally:
-            c.close()
+                raise TwitterHTTPError(e, uri, self.format, self.encoded_args)
 
 class Twitter(TwitterCall):
     """
     The minimalist yet fully featured Twitter API class.
 
 class Twitter(TwitterCall):
     """
     The minimalist yet fully featured Twitter API class.
-    
+
     Get RESTful data by accessing members of this class. The result
     is decoded python objects (lists and dicts).
 
     Get RESTful data by accessing members of this class. The result
     is decoded python objects (lists and dicts).
 
@@ -107,27 +121,27 @@ class Twitter(TwitterCall):
 
       http://apiwiki.twitter.com/
       http://groups.google.com/group/twitter-development-talk/web/api-documentation
 
       http://apiwiki.twitter.com/
       http://groups.google.com/group/twitter-development-talk/web/api-documentation
-    
+
     Examples::
     Examples::
-    
+
       twitter = Twitter("hello@foo.com", "password123")
       twitter = Twitter("hello@foo.com", "password123")
-      
+
       # Get the public timeline
       twitter.statuses.public_timeline()
       # Get the public timeline
       twitter.statuses.public_timeline()
-      
+
       # Get a particular friend's timeline
       twitter.statuses.friends_timeline(id="billybob")
       # Get a particular friend's timeline
       twitter.statuses.friends_timeline(id="billybob")
-      
+
       # Also supported (but totally weird)
       twitter.statuses.friends_timeline.billybob()
       # Also supported (but totally weird)
       twitter.statuses.friends_timeline.billybob()
-      
+
       # Send a direct message
       twitter.direct_messages.new(
           user="billybob",
           text="I think yer swell!")
 
     Searching Twitter::
       # Send a direct message
       twitter.direct_messages.new(
           user="billybob",
           text="I think yer swell!")
 
     Searching Twitter::
-        
+
       twitter_search = Twitter(domain="search.twitter.com")
 
       # Find the latest search trends
       twitter_search = Twitter(domain="search.twitter.com")
 
       # Find the latest search trends
@@ -148,27 +162,38 @@ class Twitter(TwitterCall):
 
       # The screen name of the user who wrote the first 'tweet'
       x[0]['user']['screen_name']
 
       # The screen name of the user who wrote the first 'tweet'
       x[0]['user']['screen_name']
-    
+
     Getting raw XML data::
     Getting raw XML data::
-    
+
       If you prefer to get your Twitter data in XML format, pass
       format="xml" to the Twitter object when you instantiate it:
       If you prefer to get your Twitter data in XML format, pass
       format="xml" to the Twitter object when you instantiate it:
-      
+
       twitter = Twitter(format="xml")
       twitter = Twitter(format="xml")
-      
+
       The output will not be parsed in any way. It will be a raw string
       of XML.
     """
     def __init__(
         self, email=None, password=None, format="json", domain="twitter.com",
       The output will not be parsed in any way. It will be a raw string
       of XML.
     """
     def __init__(
         self, email=None, password=None, format="json", domain="twitter.com",
-        agent=None):
+        agent=None, secure=True, auth=None):
         """
         Create a new twitter API connector using the specified
         credentials (email and password). Format specifies the output
         format ("json" (default) or "xml").
         """
         """
         Create a new twitter API connector using the specified
         credentials (email and password). Format specifies the output
         format ("json" (default) or "xml").
         """
-        if (format not in ("json", "xml")):
+        
+        if email is not None or password is not None:
+            if auth is not None:
+                raise ValueError, "can't specify 'email' or 'password' and 'auth' params"
+            auth = UserPassAuth(email, password)
+
+        if not auth:
+            auth = NoAuth()
+
+        if (format not in ("json", "xml", "")):
             raise TwitterError("Unknown data format '%s'" %(format))
             raise TwitterError("Unknown data format '%s'" %(format))
-        TwitterCall.__init__(self, email, password, format, domain)
+        TwitterCall.__init__(
+            self, auth, format, domain, "", agent, 
+            secure=secure)
 
 
-__all__ = ["Twitter", "TwitterError"]
+__all__ = ["Twitter", "TwitterError", "TwitterHTTPError"]