]>
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 TwitterError(Exception):
18 Base Exception thrown by the Twitter object when there is a
19 general error interacting with the API.
23 class TwitterHTTPError(TwitterError
):
25 Exception thrown by the Twitter object when there is an
26 HTTP error interacting with twitter.com.
28 def __init__(self
, e
, uri
, format
, uriparts
):
32 self
.uriparts
= uriparts
36 "Twitter sent status %i for URL: %s.%s using parameters: "
37 "(%s)\ndetails: %s" %(
38 self
.e
.code
, self
.uri
, self
.format
, self
.uriparts
,
41 class TwitterResponse(object):
43 Response from a twitter request. Behaves like a list or a string
44 (depending on requested format) but it has a few other interesting
47 `headers` gives you access to the response headers as an
48 httplib.HTTPHeaders instance. You can do
49 `response.headers.getheader('h')` to retrieve a header.
51 def __init__(self
, headers
):
52 self
.headers
= headers
55 def rate_limit_remaining(self
):
57 Remaining requests in the current rate-limit.
59 return int(self
.headers
.getheader('X-RateLimit-Remaining'))
62 def rate_limit_reset(self
):
64 Time in UTC epoch seconds when the rate limit will reset.
66 return int(self
.headers
.getheader('X-RateLimit-Reset'))
69 def wrap_response(response
, headers
):
70 response_typ
= type(response
)
71 if response_typ
is bool:
72 # HURF DURF MY NAME IS PYTHON AND I CAN'T SUBCLASS bool.
75 class WrappedTwitterResponse(response_typ
, TwitterResponse
):
76 __doc__
= TwitterResponse
.__doc
__
78 def __init__(self
, response
):
79 if response_typ
is not int:
80 response_typ
.__init
__(self
, response
)
81 TwitterResponse
.__init
__(self
, headers
)
83 return WrappedTwitterResponse(response
)
87 class TwitterCall(object):
90 self
, auth
, format
, domain
, callable_cls
, uri
="",
91 uriparts
=None, secure
=True):
95 self
.callable_cls
= callable_cls
97 self
.uriparts
= uriparts
100 def __getattr__(self
, k
):
102 return object.__getattr
__(self
, k
)
103 except AttributeError:
104 return self
.callable_cls(
105 auth
=self
.auth
, format
=self
.format
, domain
=self
.domain
,
106 callable_cls
=self
.callable_cls
, uriparts
=self
.uriparts
+ (k
,),
109 def __call__(self
, **kwargs
):
112 for uripart
in self
.uriparts
:
113 # If this part matches a keyword argument, use the
114 # supplied value otherwise, just use the part.
115 uriparts
.append(str(kwargs
.pop(uripart
, uripart
)))
116 uri
= '/'.join(uriparts
)
119 for action
in POST_ACTIONS
:
120 if uri
.endswith(action
):
124 # If an id kwarg is present and there is no id to fill in in
125 # the list of uriparts, assume the id goes at the end.
126 id = kwargs
.pop('id', None)
136 uriBase
= "http%s://%s/%s%s%s" %(
137 secure_str
, self
.domain
, uri
, dot
, self
.format
)
141 headers
.update(self
.auth
.generate_headers())
142 arg_data
= self
.auth
.encode_params(uriBase
, method
, kwargs
)
144 uriBase
+= '?' + arg_data
147 body
= arg_data
.encode('utf8')
149 req
= urllib_request
.Request(uriBase
, body
, headers
)
150 return self
._handle
_response
(req
, uri
, arg_data
)
152 def _handle_response(self
, req
, uri
, arg_data
):
154 handle
= urllib_request
.urlopen(req
)
155 if "json" == self
.format
:
156 res
= json
.loads(handle
.read().decode('utf8'))
157 return wrap_response(res
, handle
.headers
)
159 return wrap_response(
160 handle
.read().decode('utf8'), handle
.headers
)
161 except urllib_error
.HTTPError
as e
:
165 raise TwitterHTTPError(e
, uri
, self
.format
, arg_data
)
167 class Twitter(TwitterCall
):
169 The minimalist yet fully featured Twitter API class.
171 Get RESTful data by accessing members of this class. The result
172 is decoded python objects (lists and dicts).
174 The Twitter API is documented here:
176 http://dev.twitter.com/doc
182 auth=OAuth(token, token_key, con_secret, con_secret_key)))
184 # Get the public timeline
185 twitter.statuses.public_timeline()
187 # Get a particular friend's timeline
188 twitter.statuses.friends_timeline(id="billybob")
190 # Also supported (but totally weird)
191 twitter.statuses.friends_timeline.billybob()
193 # Send a direct message
194 twitter.direct_messages.new(
196 text="I think yer swell!")
198 # Get the members of a particular list of a particular friend
199 twitter.user.listname.members(user="billybob", listname="billysbuds")
204 twitter_search = Twitter(domain="search.twitter.com")
206 # Find the latest search trends
207 twitter_search.trends()
209 # Search for the latest News on #gaza
210 twitter_search.search(q="#gaza")
213 Using the data returned
214 -----------------------
216 Twitter API calls return decoded JSON. This is converted into
217 a bunch of Python lists, dicts, ints, and strings. For example::
219 x = twitter.statuses.public_timeline()
221 # The first 'tweet' in the timeline
224 # The screen name of the user who wrote the first 'tweet'
225 x[0]['user']['screen_name']
231 If you prefer to get your Twitter data in XML format, pass
232 format="xml" to the Twitter object when you instantiate it::
234 twitter = Twitter(format="xml")
236 The output will not be parsed in any way. It will be a raw string
242 domain
="api.twitter.com", secure
=True, auth
=None,
245 Create a new twitter API connector.
247 Pass an `auth` parameter to use the credentials of a specific
248 user. Generally you'll want to pass an `OAuth`
251 twitter = Twitter(auth=OAuth(
252 token, token_secret, consumer_key, consumer_secret))
255 `domain` lets you change the domain you are connecting. By
256 default it's `api.twitter.com` but `search.twitter.com` may be
259 If `secure` is False you will connect with HTTP instead of
262 `api_version` is used to set the base uri. By default it's
268 if (format
not in ("json", "xml", "")):
269 raise ValueError("Unknown data format '%s'" %(format))
273 uriparts
+= (str(api_version
),)
275 TwitterCall
.__init
__(
276 self
, auth
=auth
, format
=format
, domain
=domain
,
277 callable_cls
=TwitterCall
,
278 secure
=secure
, uriparts
=uriparts
)
281 __all__
= ["Twitter", "TwitterError", "TwitterHTTPError", "TwitterResponse"]