]>
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
)
86 def __new__(cls
, response
, headers
):
87 return response_typ
.__new
__(cls
, response
)
90 return WrappedTwitterResponse(response
, headers
)
94 class TwitterCall(object):
97 self
, auth
, format
, domain
, callable_cls
, uri
="",
98 uriparts
=None, secure
=True):
102 self
.callable_cls
= callable_cls
104 self
.uriparts
= uriparts
107 def __getattr__(self
, k
):
109 return object.__getattr
__(self
, k
)
110 except AttributeError:
111 def extend_call(arg
):
112 return self
.callable_cls(
113 auth
=self
.auth
, format
=self
.format
, domain
=self
.domain
,
114 callable_cls
=self
.callable_cls
, uriparts
=self
.uriparts \
120 return extend_call(k
)
122 def __call__(self
, **kwargs
):
125 for uripart
in self
.uriparts
:
126 # If this part matches a keyword argument, use the
127 # supplied value otherwise, just use the part.
128 uriparts
.append(str(kwargs
.pop(uripart
, uripart
)))
129 uri
= '/'.join(uriparts
)
131 method
= kwargs
.pop('_method', None)
134 for action
in POST_ACTIONS
:
135 if uri
.endswith(action
):
139 # If an id kwarg is present and there is no id to fill in in
140 # the list of uriparts, assume the id goes at the end.
141 id = kwargs
.pop('id', None)
151 uriBase
= "http%s://%s/%s%s%s" %(
152 secure_str
, self
.domain
, uri
, dot
, self
.format
)
156 headers
.update(self
.auth
.generate_headers())
157 arg_data
= self
.auth
.encode_params(uriBase
, method
, kwargs
)
159 uriBase
+= '?' + arg_data
162 body
= arg_data
.encode('utf8')
164 req
= urllib_request
.Request(uriBase
, body
, headers
)
165 return self
._handle
_response
(req
, uri
, arg_data
)
167 def _handle_response(self
, req
, uri
, arg_data
):
169 handle
= urllib_request
.urlopen(req
)
170 if "json" == self
.format
:
171 res
= json
.loads(handle
.read().decode('utf8'))
172 return wrap_response(res
, handle
.headers
)
174 return wrap_response(
175 handle
.read().decode('utf8'), handle
.headers
)
176 except urllib_error
.HTTPError
as e
:
180 raise TwitterHTTPError(e
, uri
, self
.format
, arg_data
)
182 class Twitter(TwitterCall
):
184 The minimalist yet fully featured Twitter API class.
186 Get RESTful data by accessing members of this class. The result
187 is decoded python objects (lists and dicts).
189 The Twitter API is documented here:
191 http://dev.twitter.com/doc
197 auth=OAuth(token, token_key, con_secret, con_secret_key)))
199 # Get the public timeline
200 twitter.statuses.public_timeline()
202 # Get a particular friend's timeline
203 twitter.statuses.friends_timeline(id="billybob")
205 # Also supported (but totally weird)
206 twitter.statuses.friends_timeline.billybob()
208 # Send a direct message
209 twitter.direct_messages.new(
211 text="I think yer swell!")
213 # Get the members of a particular list of a particular friend
214 twitter.user.listname.members(user="billybob", listname="billysbuds")
219 twitter_search = Twitter(domain="search.twitter.com")
221 # Find the latest search trends
222 twitter_search.trends()
224 # Search for the latest News on #gaza
225 twitter_search.search(q="#gaza")
228 Using the data returned
229 -----------------------
231 Twitter API calls return decoded JSON. This is converted into
232 a bunch of Python lists, dicts, ints, and strings. For example::
234 x = twitter.statuses.public_timeline()
236 # The first 'tweet' in the timeline
239 # The screen name of the user who wrote the first 'tweet'
240 x[0]['user']['screen_name']
246 If you prefer to get your Twitter data in XML format, pass
247 format="xml" to the Twitter object when you instantiate it::
249 twitter = Twitter(format="xml")
251 The output will not be parsed in any way. It will be a raw string
257 domain
="api.twitter.com", secure
=True, auth
=None,
258 api_version
=_DEFAULT
):
260 Create a new twitter API connector.
262 Pass an `auth` parameter to use the credentials of a specific
263 user. Generally you'll want to pass an `OAuth`
266 twitter = Twitter(auth=OAuth(
267 token, token_secret, consumer_key, consumer_secret))
270 `domain` lets you change the domain you are connecting. By
271 default it's `api.twitter.com` but `search.twitter.com` may be
274 If `secure` is False you will connect with HTTP instead of
277 `api_version` is used to set the base uri. By default it's
278 '1'. If you are using "search.twitter.com" set this to None.
283 if (format
not in ("json", "xml", "")):
284 raise ValueError("Unknown data format '%s'" %(format))
286 if api_version
is _DEFAULT
:
287 if domain
== 'api.twitter.com':
294 uriparts
+= (str(api_version
),)
296 TwitterCall
.__init
__(
297 self
, auth
=auth
, format
=format
, domain
=domain
,
298 callable_cls
=TwitterCall
,
299 secure
=secure
, uriparts
=uriparts
)
302 __all__
= ["Twitter", "TwitterError", "TwitterHTTPError", "TwitterResponse"]