]>
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()
39 fmt
= ("." + self
.format
) if self
.format
else ""
41 "Twitter sent status %i for URL: %s%s using parameters: "
42 "(%s)\ndetails: %s" %(
43 self
.e
.code
, self
.uri
, fmt
, self
.uriparts
,
46 class TwitterResponse(object):
48 Response from a twitter request. Behaves like a list or a string
49 (depending on requested format) but it has a few other interesting
52 `headers` gives you access to the response headers as an
53 httplib.HTTPHeaders instance. You can do
54 `response.headers.getheader('h')` to retrieve a header.
56 def __init__(self
, headers
):
57 self
.headers
= headers
60 def rate_limit_remaining(self
):
62 Remaining requests in the current rate-limit.
64 return int(self
.headers
.getheader('X-RateLimit-Remaining'))
67 def rate_limit_reset(self
):
69 Time in UTC epoch seconds when the rate limit will reset.
71 return int(self
.headers
.getheader('X-RateLimit-Reset'))
74 def wrap_response(response
, headers
):
75 response_typ
= type(response
)
76 if response_typ
is bool:
77 # HURF DURF MY NAME IS PYTHON AND I CAN'T SUBCLASS bool.
80 class WrappedTwitterResponse(response_typ
, TwitterResponse
):
81 __doc__
= TwitterResponse
.__doc
__
83 def __init__(self
, response
, headers
):
84 response_typ
.__init
__(self
, response
)
85 TwitterResponse
.__init
__(self
, headers
)
87 return WrappedTwitterResponse(response
, headers
)
91 class TwitterCall(object):
94 self
, auth
, format
, domain
, callable_cls
, uri
="",
95 uriparts
=None, secure
=True):
99 self
.callable_cls
= callable_cls
101 self
.uriparts
= uriparts
104 def __getattr__(self
, k
):
106 return object.__getattr
__(self
, k
)
107 except AttributeError:
108 def extend_call(arg
):
109 return self
.callable_cls(
110 auth
=self
.auth
, format
=self
.format
, domain
=self
.domain
,
111 callable_cls
=self
.callable_cls
, uriparts
=self
.uriparts \
117 return extend_call(k
)
119 def __call__(self
, **kwargs
):
122 for uripart
in self
.uriparts
:
123 # If this part matches a keyword argument, use the
124 # supplied value otherwise, just use the part.
125 uriparts
.append(str(kwargs
.pop(uripart
, uripart
)))
126 uri
= '/'.join(uriparts
)
128 method
= kwargs
.pop('_method', None)
131 for action
in POST_ACTIONS
:
132 if uri
.endswith(action
):
136 # If an id kwarg is present and there is no id to fill in in
137 # the list of uriparts, assume the id goes at the end.
138 id = kwargs
.pop('id', None)
148 uriBase
= "http%s://%s/%s%s%s" %(
149 secure_str
, self
.domain
, uri
, dot
, self
.format
)
153 headers
.update(self
.auth
.generate_headers())
154 arg_data
= self
.auth
.encode_params(uriBase
, method
, kwargs
)
156 uriBase
+= '?' + arg_data
159 body
= arg_data
.encode('utf8')
161 req
= urllib_request
.Request(uriBase
, body
, headers
)
162 return self
._handle
_response
(req
, uri
, arg_data
)
164 def _handle_response(self
, req
, uri
, arg_data
):
166 handle
= urllib_request
.urlopen(req
)
167 if "json" == self
.format
:
168 res
= json
.loads(handle
.read().decode('utf8'))
169 return wrap_response(res
, handle
.headers
)
171 return wrap_response(
172 handle
.read().decode('utf8'), handle
.headers
)
173 except urllib_error
.HTTPError
as e
:
177 raise TwitterHTTPError(e
, uri
, self
.format
, arg_data
)
179 class Twitter(TwitterCall
):
181 The minimalist yet fully featured Twitter API class.
183 Get RESTful data by accessing members of this class. The result
184 is decoded python objects (lists and dicts).
186 The Twitter API is documented here:
188 http://dev.twitter.com/doc
194 auth=OAuth(token, token_key, con_secret, con_secret_key)))
196 # Get the public timeline
197 twitter.statuses.public_timeline()
199 # Get a particular friend's timeline
200 twitter.statuses.friends_timeline(id="billybob")
202 # Also supported (but totally weird)
203 twitter.statuses.friends_timeline.billybob()
205 # Send a direct message
206 twitter.direct_messages.new(
208 text="I think yer swell!")
210 # Get the members of a particular list of a particular friend
211 twitter.user.listname.members(user="billybob", listname="billysbuds")
216 twitter_search = Twitter(domain="search.twitter.com")
218 # Find the latest search trends
219 twitter_search.trends()
221 # Search for the latest News on #gaza
222 twitter_search.search(q="#gaza")
225 Using the data returned
226 -----------------------
228 Twitter API calls return decoded JSON. This is converted into
229 a bunch of Python lists, dicts, ints, and strings. For example::
231 x = twitter.statuses.public_timeline()
233 # The first 'tweet' in the timeline
236 # The screen name of the user who wrote the first 'tweet'
237 x[0]['user']['screen_name']
243 If you prefer to get your Twitter data in XML format, pass
244 format="xml" to the Twitter object when you instantiate it::
246 twitter = Twitter(format="xml")
248 The output will not be parsed in any way. It will be a raw string
254 domain
="api.twitter.com", secure
=True, auth
=None,
255 api_version
=_DEFAULT
):
257 Create a new twitter API connector.
259 Pass an `auth` parameter to use the credentials of a specific
260 user. Generally you'll want to pass an `OAuth`
263 twitter = Twitter(auth=OAuth(
264 token, token_secret, consumer_key, consumer_secret))
267 `domain` lets you change the domain you are connecting. By
268 default it's `api.twitter.com` but `search.twitter.com` may be
271 If `secure` is False you will connect with HTTP instead of
274 `api_version` is used to set the base uri. By default it's
275 '1'. If you are using "search.twitter.com" set this to None.
280 if (format
not in ("json", "xml", "")):
281 raise ValueError("Unknown data format '%s'" %(format))
283 if api_version
is _DEFAULT
:
284 if domain
== 'api.twitter.com':
291 uriparts
+= (str(api_version
),)
293 TwitterCall
.__init
__(
294 self
, auth
=auth
, format
=format
, domain
=domain
,
295 callable_cls
=TwitterCall
,
296 secure
=secure
, uriparts
=uriparts
)
299 __all__
= ["Twitter", "TwitterError", "TwitterHTTPError", "TwitterResponse"]