]>
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
39 "Twitter sent status %i for URL: %s.%s using parameters: "
40 "(%s)\ndetails: %s" %(
41 self
.e
.code
, self
.uri
, self
.format
, self
.uriparts
,
44 class TwitterResponse(object):
46 Response from a twitter request. Behaves like a list or a string
47 (depending on requested format) but it has a few other interesting
50 `headers` gives you access to the response headers as an
51 httplib.HTTPHeaders instance. You can do
52 `response.headers.getheader('h')` to retrieve a header.
54 def __init__(self
, headers
):
55 self
.headers
= headers
58 def rate_limit_remaining(self
):
60 Remaining requests in the current rate-limit.
62 return int(self
.headers
.getheader('X-RateLimit-Remaining'))
65 def rate_limit_reset(self
):
67 Time in UTC epoch seconds when the rate limit will reset.
69 return int(self
.headers
.getheader('X-RateLimit-Reset'))
72 def wrap_response(response
, headers
):
73 response_typ
= type(response
)
74 if response_typ
is bool:
75 # HURF DURF MY NAME IS PYTHON AND I CAN'T SUBCLASS bool.
78 class WrappedTwitterResponse(response_typ
, TwitterResponse
):
79 __doc__
= TwitterResponse
.__doc
__
81 def __init__(self
, response
):
82 if response_typ
is not int:
83 response_typ
.__init
__(self
, response
)
84 TwitterResponse
.__init
__(self
, headers
)
86 return WrappedTwitterResponse(response
)
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 return self
.callable_cls(
108 auth
=self
.auth
, format
=self
.format
, domain
=self
.domain
,
109 callable_cls
=self
.callable_cls
, uriparts
=self
.uriparts
+ (k
,),
112 def __call__(self
, **kwargs
):
115 for uripart
in self
.uriparts
:
116 # If this part matches a keyword argument, use the
117 # supplied value otherwise, just use the part.
118 uriparts
.append(str(kwargs
.pop(uripart
, uripart
)))
119 uri
= '/'.join(uriparts
)
122 for action
in POST_ACTIONS
:
123 if uri
.endswith(action
):
127 # If an id kwarg is present and there is no id to fill in in
128 # the list of uriparts, assume the id goes at the end.
129 id = kwargs
.pop('id', None)
139 uriBase
= "http%s://%s/%s%s%s" %(
140 secure_str
, self
.domain
, uri
, dot
, self
.format
)
144 headers
.update(self
.auth
.generate_headers())
145 arg_data
= self
.auth
.encode_params(uriBase
, method
, kwargs
)
147 uriBase
+= '?' + arg_data
150 body
= arg_data
.encode('utf8')
152 req
= urllib_request
.Request(uriBase
, body
, headers
)
153 return self
._handle
_response
(req
, uri
, arg_data
)
155 def _handle_response(self
, req
, uri
, arg_data
):
157 handle
= urllib_request
.urlopen(req
)
158 if "json" == self
.format
:
159 res
= json
.loads(handle
.read().decode('utf8'))
160 return wrap_response(res
, handle
.headers
)
162 return wrap_response(
163 handle
.read().decode('utf8'), handle
.headers
)
164 except urllib_error
.HTTPError
as e
:
168 raise TwitterHTTPError(e
, uri
, self
.format
, arg_data
)
170 class Twitter(TwitterCall
):
172 The minimalist yet fully featured Twitter API class.
174 Get RESTful data by accessing members of this class. The result
175 is decoded python objects (lists and dicts).
177 The Twitter API is documented here:
179 http://dev.twitter.com/doc
185 auth=OAuth(token, token_key, con_secret, con_secret_key)))
187 # Get the public timeline
188 twitter.statuses.public_timeline()
190 # Get a particular friend's timeline
191 twitter.statuses.friends_timeline(id="billybob")
193 # Also supported (but totally weird)
194 twitter.statuses.friends_timeline.billybob()
196 # Send a direct message
197 twitter.direct_messages.new(
199 text="I think yer swell!")
201 # Get the members of a particular list of a particular friend
202 twitter.user.listname.members(user="billybob", listname="billysbuds")
207 twitter_search = Twitter(domain="search.twitter.com")
209 # Find the latest search trends
210 twitter_search.trends()
212 # Search for the latest News on #gaza
213 twitter_search.search(q="#gaza")
216 Using the data returned
217 -----------------------
219 Twitter API calls return decoded JSON. This is converted into
220 a bunch of Python lists, dicts, ints, and strings. For example::
222 x = twitter.statuses.public_timeline()
224 # The first 'tweet' in the timeline
227 # The screen name of the user who wrote the first 'tweet'
228 x[0]['user']['screen_name']
234 If you prefer to get your Twitter data in XML format, pass
235 format="xml" to the Twitter object when you instantiate it::
237 twitter = Twitter(format="xml")
239 The output will not be parsed in any way. It will be a raw string
245 domain
="api.twitter.com", secure
=True, auth
=None,
246 api_version
=_DEFAULT
):
248 Create a new twitter API connector.
250 Pass an `auth` parameter to use the credentials of a specific
251 user. Generally you'll want to pass an `OAuth`
254 twitter = Twitter(auth=OAuth(
255 token, token_secret, consumer_key, consumer_secret))
258 `domain` lets you change the domain you are connecting. By
259 default it's `api.twitter.com` but `search.twitter.com` may be
262 If `secure` is False you will connect with HTTP instead of
265 `api_version` is used to set the base uri. By default it's
266 '1'. If you are using "search.twitter.com" set this to None.
271 if (format
not in ("json", "xml", "")):
272 raise ValueError("Unknown data format '%s'" %(format))
274 if api_version
is _DEFAULT
:
275 if domain
== 'api.twitter.com':
282 uriparts
+= (str(api_version
),)
284 TwitterCall
.__init
__(
285 self
, auth
=auth
, format
=format
, domain
=domain
,
286 callable_cls
=TwitterCall
,
287 secure
=secure
, uriparts
=uriparts
)
290 __all__
= ["Twitter", "TwitterError", "TwitterHTTPError", "TwitterResponse"]