]> jfr.im git - z_archive/twitter.git/blob - twitter/api.py
Update patch to work with new code.
[z_archive/twitter.git] / twitter / api.py
1 import urllib2
2
3 from exceptions import Exception
4
5 from twitter.twitter_globals import POST_ACTIONS
6 from twitter.auth import UserPassAuth, NoAuth
7
8 def _py26OrGreater():
9 import sys
10 return sys.hexversion > 0x20600f0
11
12 if _py26OrGreater():
13 import json
14 else:
15 import simplejson as json
16
17 class TwitterError(Exception):
18 """
19 Base Exception thrown by the Twitter object when there is a
20 general error interacting with the API.
21 """
22 pass
23
24 class TwitterHTTPError(TwitterError):
25 """
26 Exception thrown by the Twitter object when there is an
27 HTTP error interacting with twitter.com.
28 """
29 def __init__(self, e, uri, format, uriparts):
30 self.e = e
31 self.uri = uri
32 self.format = format
33 self.uriparts = uriparts
34
35 def __str__(self):
36 return (
37 "Twitter sent status %i for URL: %s.%s using parameters: "
38 "(%s)\ndetails: %s" %(
39 self.e.code, self.uri, self.format, self.uriparts,
40 self.e.fp.read()))
41
42 class TwitterCall(object):
43 def __init__(
44 self, auth, format, domain, uri="", agent=None,
45 uriparts=None, secure=True):
46 self.auth = auth
47 self.format = format
48 self.domain = domain
49 self.uri = uri
50 self.agent = agent
51 self.uriparts = uriparts
52 self.secure = secure
53
54 def __getattr__(self, k):
55 try:
56 return object.__getattr__(self, k)
57 except AttributeError:
58 return TwitterCall(
59 auth=self.auth, format=self.format, domain=self.domain,
60 agent=self.agent, uriparts=self.uriparts + (k,),
61 secure=self.secure)
62
63 def __call__(self, **kwargs):
64 #build the uri
65 uriparts = []
66 for uripart in self.uriparts:
67 #if this part matches a keyword argument, use the supplied value
68 #otherwise, just use the part
69 uriparts.append(kwargs.pop(uripart,uripart))
70 uri = '/'.join(uriparts)
71
72 method = "GET"
73 for action in POST_ACTIONS:
74 if uri.endswith(action):
75 method = "POST"
76 if (self.agent):
77 kwargs["source"] = self.agent
78 break
79
80 """This handles a special case. It isn't really needed anymore because now
81 we can insert an id value (or any other value) at the end of the
82 uri (or anywhere else).
83 However we can leave it for backward compatibility."""
84 id = kwargs.pop('id', None)
85 if id:
86 uri += "/%s" %(id)
87
88 secure_str = ''
89 if self.secure:
90 secure_str = 's'
91 dot = ""
92 if self.format:
93 dot = "."
94 uriBase = "http%s://%s/%s%s%s" %(
95 secure_str, self.domain, uri, dot, self.format)
96
97 headers = {}
98 if (self.agent):
99 headers["X-Twitter-Client"] = self.agent
100 if self.auth:
101 headers.update(self.auth.generate_headers())
102 arg_data = self.auth.encode_params(uriBase, method, kwargs)
103 if method == 'GET':
104 uriBase += '?' + arg_data
105 body = None
106 else:
107 body = arg_data
108
109 req = urllib2.Request(uriBase, body, headers)
110
111 try:
112 handle = urllib2.urlopen(req)
113 if "json" == self.format:
114 return json.loads(handle.read())
115 else:
116 return handle.read()
117 except urllib2.HTTPError, e:
118 if (e.code == 304):
119 return []
120 else:
121 raise TwitterHTTPError(e, uriBase, self.format, self.uriparts)
122
123 class Twitter(TwitterCall):
124 """
125 The minimalist yet fully featured Twitter API class.
126
127 Get RESTful data by accessing members of this class. The result
128 is decoded python objects (lists and dicts).
129
130 The Twitter API is documented here:
131
132 http://apiwiki.twitter.com/
133 http://groups.google.com/group/twitter-development-talk/web/api-documentation
134
135 Examples::
136
137 twitter = Twitter(
138 auth=OAuth(token, token_key, con_secret, con_secret_key)))
139
140 # Get the public timeline
141 twitter.statuses.public_timeline()
142
143 # Get a particular friend's timeline
144 twitter.statuses.friends_timeline(id="billybob")
145
146 # Also supported (but totally weird)
147 twitter.statuses.friends_timeline.billybob()
148
149 # Send a direct message
150 twitter.direct_messages.new(
151 user="billybob",
152 text="I think yer swell!")
153
154 # Get the members of a particular list of a particular friend
155 twitter.user.listname.members(user="billybob", listname="billysbuds")
156
157
158 Searching Twitter::
159
160 twitter_search = Twitter(domain="search.twitter.com")
161
162 # Find the latest search trends
163 twitter_search.trends()
164
165 # Search for the latest News on #gaza
166 twitter_search.search(q="#gaza")
167
168
169 Using the data returned
170 -----------------------
171
172 Twitter API calls return decoded JSON. This is converted into
173 a bunch of Python lists, dicts, ints, and strings. For example::
174
175 x = twitter.statuses.public_timeline()
176
177 # The first 'tweet' in the timeline
178 x[0]
179
180 # The screen name of the user who wrote the first 'tweet'
181 x[0]['user']['screen_name']
182
183
184 Getting raw XML data
185 --------------------
186
187 If you prefer to get your Twitter data in XML format, pass
188 format="xml" to the Twitter object when you instantiate it::
189
190 twitter = Twitter(format="xml")
191
192 The output will not be parsed in any way. It will be a raw string
193 of XML.
194
195 """
196 def __init__(
197 self, email=None, password=None, format="json",
198 domain="twitter.com", agent=None, secure=True, auth=None,
199 api_version=''):
200 """
201 Create a new twitter API connector.
202
203 Pass an `auth` parameter to use the credentials of a specific
204 user. Generally you'll want to pass an `OAuth`
205 instance::
206
207 twitter = Twitter(auth=OAuth(
208 token, token_secret, consumer_key, consumer_secret))
209
210
211 Alternately you can pass `email` and `password` parameters but
212 this authentication mode will be deactive by Twitter very soon
213 and is not recommended::
214
215 twitter = Twitter(email="blah@blah.com", password="foobar")
216
217
218 `domain` lets you change the domain you are connecting. By
219 default it's twitter.com but `search.twitter.com` may be
220 useful too.
221
222 If `secure` is False you will connect with HTTP instead of
223 HTTPS.
224
225 The value of `agent` is sent in the `X-Twitter-Client`
226 header. This is deprecated. Instead Twitter determines the
227 application using the OAuth Client Key and Client Key Secret
228 parameters.
229
230 `api_version` is used to set the base uri. By default it's
231 nothing, but if you set it to '1' your URI will start with
232 '1/'.
233 """
234 if email is not None or password is not None:
235 if auth:
236 raise ValueError(
237 "Can't specify 'email'/'password' and 'auth' params"
238 " simultaneously.")
239 auth = UserPassAuth(email, password)
240
241 if not auth:
242 auth = NoAuth()
243
244 if (format not in ("json", "xml", "")):
245 raise ValueError("Unknown data format '%s'" %(format))
246
247 uriparts = ()
248 if api_version:
249 uriparts += (str(api_version),)
250
251 TwitterCall.__init__(
252 self, auth=auth, format=format, domain=domain, agent=agent,
253 secure=secure, uriparts=uriparts)
254
255
256 __all__ = ["Twitter", "TwitterError", "TwitterHTTPError"]