]>
jfr.im git - z_archive/twitter.git/blob - twitter/api.py
2 import urllib
.request
as urllib_request
3 import urllib
.error
as urllib_error
5 import urllib2
as urllib_request
6 import urllib2
as urllib_error
8 from twitter
.twitter_globals
import POST_ACTIONS
9 from twitter
.auth
import NoAuth
14 import simplejson
as json
16 class _DEFAULT(object):
19 class TwitterError(Exception):
21 Base Exception thrown by the Twitter object when there is a
22 general error interacting with the API.
26 class TwitterHTTPError(TwitterError
):
28 Exception thrown by the Twitter object when there is an
29 HTTP error interacting with twitter.com.
31 def __init__(self
, e
, uri
, format
, uriparts
):
35 self
.uriparts
= uriparts
36 self
.response_data
= self
.e
.fp
.read()
40 "Twitter sent status %i for URL: %s.%s using parameters: "
41 "(%s)\ndetails: %s" %(
42 self
.e
.code
, self
.uri
, self
.format
, self
.uriparts
,
45 class TwitterResponse(object):
47 Response from a twitter request. Behaves like a list or a string
48 (depending on requested format) but it has a few other interesting
51 `headers` gives you access to the response headers as an
52 httplib.HTTPHeaders instance. You can do
53 `response.headers.getheader('h')` to retrieve a header.
55 def __init__(self
, headers
):
56 self
.headers
= headers
59 def rate_limit_remaining(self
):
61 Remaining requests in the current rate-limit.
63 return int(self
.headers
.getheader('X-RateLimit-Remaining'))
66 def rate_limit_reset(self
):
68 Time in UTC epoch seconds when the rate limit will reset.
70 return int(self
.headers
.getheader('X-RateLimit-Reset'))
73 def wrap_response(response
, headers
):
74 response_typ
= type(response
)
75 if response_typ
is bool:
76 # HURF DURF MY NAME IS PYTHON AND I CAN'T SUBCLASS bool.
79 class WrappedTwitterResponse(response_typ
, TwitterResponse
):
80 __doc__
= TwitterResponse
.__doc
__
82 def __init__(self
, response
, headers
):
83 response_typ
.__init
__(self
, response
)
84 TwitterResponse
.__init
__(self
, headers
)
86 return WrappedTwitterResponse(response
, headers
)
90 class TwitterCall(object):
93 self
, auth
, format
, domain
, callable_cls
, uri
="",
94 uriparts
=None, secure
=True):
98 self
.callable_cls
= callable_cls
100 self
.uriparts
= uriparts
103 def __getattr__(self
, k
):
105 return object.__getattr
__(self
, k
)
106 except AttributeError:
107 def extend_call(arg
):
108 return self
.callable_cls(
109 auth
=self
.auth
, format
=self
.format
, domain
=self
.domain
,
110 callable_cls
=self
.callable_cls
, uriparts
=self
.uriparts \
116 return extend_call(k
)
118 def __call__(self
, **kwargs
):
121 for uripart
in self
.uriparts
:
122 # If this part matches a keyword argument, use the
123 # supplied value otherwise, just use the part.
124 uriparts
.append(str(kwargs
.pop(uripart
, uripart
)))
125 uri
= '/'.join(uriparts
)
128 for action
in POST_ACTIONS
:
129 if uri
.endswith(action
):
133 # If an id kwarg is present and there is no id to fill in in
134 # the list of uriparts, assume the id goes at the end.
135 id = kwargs
.pop('id', None)
145 uriBase
= "http%s://%s/%s%s%s" %(
146 secure_str
, self
.domain
, uri
, dot
, self
.format
)
150 headers
.update(self
.auth
.generate_headers())
151 arg_data
= self
.auth
.encode_params(uriBase
, method
, kwargs
)
153 uriBase
+= '?' + arg_data
156 body
= arg_data
.encode('utf8')
158 req
= urllib_request
.Request(uriBase
, body
, headers
)
159 return self
._handle
_response
(req
, uri
, arg_data
)
161 def _handle_response(self
, req
, uri
, arg_data
):
163 handle
= urllib_request
.urlopen(req
)
164 if "json" == self
.format
:
165 res
= json
.loads(handle
.read().decode('utf8'))
166 return wrap_response(res
, handle
.headers
)
168 return wrap_response(
169 handle
.read().decode('utf8'), handle
.headers
)
170 except urllib_error
.HTTPError
as e
:
174 raise TwitterHTTPError(e
, uri
, self
.format
, arg_data
)
176 class Twitter(TwitterCall
):
178 The minimalist yet fully featured Twitter API class.
180 Get RESTful data by accessing members of this class. The result
181 is decoded python objects (lists and dicts).
183 The Twitter API is documented here:
185 http://dev.twitter.com/doc
191 auth=OAuth(token, token_key, con_secret, con_secret_key)))
193 # Get the public timeline
194 twitter.statuses.public_timeline()
196 # Get a particular friend's timeline
197 twitter.statuses.friends_timeline(id="billybob")
199 # Also supported (but totally weird)
200 twitter.statuses.friends_timeline.billybob()
202 # Send a direct message
203 twitter.direct_messages.new(
205 text="I think yer swell!")
207 # Get the members of a particular list of a particular friend
208 twitter.user.listname.members(user="billybob", listname="billysbuds")
213 twitter_search = Twitter(domain="search.twitter.com")
215 # Find the latest search trends
216 twitter_search.trends()
218 # Search for the latest News on #gaza
219 twitter_search.search(q="#gaza")
222 Using the data returned
223 -----------------------
225 Twitter API calls return decoded JSON. This is converted into
226 a bunch of Python lists, dicts, ints, and strings. For example::
228 x = twitter.statuses.public_timeline()
230 # The first 'tweet' in the timeline
233 # The screen name of the user who wrote the first 'tweet'
234 x[0]['user']['screen_name']
240 If you prefer to get your Twitter data in XML format, pass
241 format="xml" to the Twitter object when you instantiate it::
243 twitter = Twitter(format="xml")
245 The output will not be parsed in any way. It will be a raw string
251 domain
="api.twitter.com", secure
=True, auth
=None,
252 api_version
=_DEFAULT
):
254 Create a new twitter API connector.
256 Pass an `auth` parameter to use the credentials of a specific
257 user. Generally you'll want to pass an `OAuth`
260 twitter = Twitter(auth=OAuth(
261 token, token_secret, consumer_key, consumer_secret))
264 `domain` lets you change the domain you are connecting. By
265 default it's `api.twitter.com` but `search.twitter.com` may be
268 If `secure` is False you will connect with HTTP instead of
271 `api_version` is used to set the base uri. By default it's
272 '1'. If you are using "search.twitter.com" set this to None.
277 if (format
not in ("json", "xml", "")):
278 raise ValueError("Unknown data format '%s'" %(format))
280 if api_version
is _DEFAULT
:
281 if domain
== 'api.twitter.com':
288 uriparts
+= (str(api_version
),)
290 TwitterCall
.__init
__(
291 self
, auth
=auth
, format
=format
, domain
=domain
,
292 callable_cls
=TwitterCall
,
293 secure
=secure
, uriparts
=uriparts
)
296 __all__
= ["Twitter", "TwitterError", "TwitterHTTPError", "TwitterResponse"]