]> jfr.im git - z_archive/twitter.git/blob - twitter/api.py
Apply patch by Jordan Gottlieb to handle URLs with ids in the middle.
[z_archive/twitter.git] / twitter / api.py
1 """
2 Attempting to patch to accommodate API like the list interface.
3 Note: Make sure not to use keyword substitutions that have the same name
4 as an argument that will get encoded.
5 """
6
7 from base64 import b64encode
8 from urllib import urlencode
9
10 import urllib2
11
12 from exceptions import Exception
13
14 from twitter.twitter_globals import POST_ACTIONS
15
16 def _py26OrGreater():
17 import sys
18 return sys.hexversion > 0x20600f0
19
20 if _py26OrGreater():
21 import json
22 else:
23 import simplejson as json
24
25 class TwitterError(Exception):
26 """
27 Exception thrown by the Twitter object when there is an
28 error interacting with twitter.com.
29 """
30 pass
31
32 class TwitterCall(object):
33 def __init__(
34 self, username, password, format, domain, uri="", agent=None, uriparts=()):
35 self.username = username
36 self.password = password
37 self.format = format
38 self.domain = domain
39 self.uri = uri
40 self.agent = agent
41 self.uriparts = uriparts
42
43 def __getattr__(self, k):
44 try:
45 return object.__getattr__(self, k)
46 except AttributeError:
47 """Instead of incrementally building the uri string, now we
48 just append to uriparts. We'll build the uri later."""
49 return TwitterCall(
50 self.username, self.password, self.format, self.domain,
51 self.uri, self.agent, self.uriparts + (k,))
52 def __call__(self, **kwargs):
53 #build the uri
54 uri = self.uri
55 for uripart in self.uriparts:
56 #if this part matches a keyword argument, use the supplied value
57 #otherwise, just use the part
58 uri = uri + "/" + kwargs.pop(uripart,uripart)
59
60 method = "GET"
61 for action in POST_ACTIONS:
62 if uri.endswith(action):
63 method = "POST"
64 if (self.agent):
65 kwargs["source"] = self.agent
66 break
67
68 """This handles a special case. It isn't really needed anymore because now
69 we can insert an id value (or any other value) at the end of the
70 uri (or anywhere else).
71 However we can leave it for backward compatibility."""
72 id = kwargs.pop('id', None)
73 if id:
74 uri += "/%s" %(id)
75
76 argStr = ""
77 argData = None
78 encoded_kwargs = urlencode(kwargs.items())
79 if (method == "GET"):
80 if kwargs:
81 argStr = "?%s" %(encoded_kwargs)
82 else:
83 argData = encoded_kwargs
84
85 headers = {}
86 if (self.agent):
87 headers["X-Twitter-Client"] = self.agent
88 if (self.username):
89 headers["Authorization"] = "Basic " + b64encode("%s:%s" %(
90 self.username, self.password))
91
92 req = urllib2.Request(
93 "http://%s/%s.%s%s" %(self.domain, uri, self.format, argStr),
94 argData, headers
95 )
96 try:
97 handle = urllib2.urlopen(req)
98 if "json" == self.format:
99 return json.loads(handle.read())
100 else:
101 return handle.read()
102 except urllib2.HTTPError, e:
103 if (e.code == 304):
104 return []
105 else:
106 raise TwitterError(
107 "Twitter sent status %i for URL: %s.%s using parameters: (%s)\ndetails: %s" %(
108 e.code, uri, self.format, encoded_kwargs, e.fp.read()))
109
110 class Twitter(TwitterCall):
111 """
112 The minimalist yet fully featured Twitter API class.
113
114 Get RESTful data by accessing members of this class. The result
115 is decoded python objects (lists and dicts).
116
117 The Twitter API is documented here:
118
119 http://apiwiki.twitter.com/
120 http://groups.google.com/group/twitter-development-talk/web/api-documentation
121
122 Examples::
123
124 twitter = Twitter("hello@foo.com", "password123")
125
126 # Get the public timeline
127 twitter.statuses.public_timeline()
128
129 # Get a particular friend's timeline
130 twitter.statuses.friends_timeline(id="billybob")
131
132 # Also supported (but totally weird)
133 twitter.statuses.friends_timeline.billybob()
134
135 # Send a direct message
136 twitter.direct_messages.new(
137 user="billybob",
138 text="I think yer swell!")
139
140 # Get the members of a particular list of a particular friend
141 twitter.user.listname.members(user="billybob", listname="billysbuds")
142
143 Searching Twitter::
144
145 twitter_search = Twitter(domain="search.twitter.com")
146
147 # Find the latest search trends
148 twitter_search.trends()
149
150 # Search for the latest News on #gaza
151 twitter_search.search(q="#gaza")
152
153 Using the data returned::
154
155 Twitter API calls return decoded JSON. This is converted into
156 a bunch of Python lists, dicts, ints, and strings. For example,
157
158 x = twitter.statuses.public_timeline()
159
160 # The first 'tweet' in the timeline
161 x[0]
162
163 # The screen name of the user who wrote the first 'tweet'
164 x[0]['user']['screen_name']
165
166 Getting raw XML data::
167
168 If you prefer to get your Twitter data in XML format, pass
169 format="xml" to the Twitter object when you instantiate it:
170
171 twitter = Twitter(format="xml")
172
173 The output will not be parsed in any way. It will be a raw string
174 of XML.
175 """
176 def __init__(
177 self, email=None, password=None, format="json", domain="twitter.com",
178 agent=None):
179 """
180 Create a new twitter API connector using the specified
181 credentials (email and password). Format specifies the output
182 format ("json" (default) or "xml").
183 """
184 if (format not in ("json", "xml")):
185 raise TwitterError("Unknown data format '%s'" %(format))
186 TwitterCall.__init__(self, email, password, format, domain, "", agent, ())
187
188 __all__ = ["Twitter", "TwitterError"]