]> jfr.im git - z_archive/twitter.git/blob - twitter/api.py
Basic auth is dead. Agent header is dead. R.I.P.
[z_archive/twitter.git] / twitter / api.py
1 import urllib2
2
3 from exceptions import Exception
4
5 from twitter.twitter_globals import POST_ACTIONS
6 from twitter.auth import NoAuth
7
8 def _py26OrGreater():
9 import sys
10 return sys.hexversion > 0x20600f0
11
12 if _py26OrGreater():
13 import json
14 else:
15 import simplejson as json
16
17 class TwitterError(Exception):
18 """
19 Base Exception thrown by the Twitter object when there is a
20 general error interacting with the API.
21 """
22 pass
23
24 class TwitterHTTPError(TwitterError):
25 """
26 Exception thrown by the Twitter object when there is an
27 HTTP error interacting with twitter.com.
28 """
29 def __init__(self, e, uri, format, uriparts):
30 self.e = e
31 self.uri = uri
32 self.format = format
33 self.uriparts = uriparts
34
35 def __str__(self):
36 return (
37 "Twitter sent status %i for URL: %s.%s using parameters: "
38 "(%s)\ndetails: %s" %(
39 self.e.code, self.uri, self.format, self.uriparts,
40 self.e.fp.read()))
41
42 class TwitterCall(object):
43 def __init__(
44 self, auth, format, domain, uri="", agent=None,
45 uriparts=None, secure=True):
46 self.auth = auth
47 self.format = format
48 self.domain = domain
49 self.uri = uri
50 self.agent = agent
51 self.uriparts = uriparts
52 self.secure = secure
53
54 def __getattr__(self, k):
55 try:
56 return object.__getattr__(self, k)
57 except AttributeError:
58 return TwitterCall(
59 auth=self.auth, format=self.format, domain=self.domain,
60 agent=self.agent, uriparts=self.uriparts + (k,),
61 secure=self.secure)
62
63 def __call__(self, **kwargs):
64 # Build the uri.
65 uriparts = []
66 for uripart in self.uriparts:
67 # If this part matches a keyword argument, use the
68 # supplied value otherwise, just use the part.
69 uriparts.append(kwargs.pop(uripart, uripart))
70 uri = '/'.join(uriparts)
71
72 method = "GET"
73 for action in POST_ACTIONS:
74 if uri.endswith(action):
75 method = "POST"
76 break
77
78 # If an id kwarg is present and there is no id to fill in in
79 # the list of uriparts, assume the id goes at the end.
80 id = kwargs.pop('id', None)
81 if id:
82 uri += "/%s" %(id)
83
84 secure_str = ''
85 if self.secure:
86 secure_str = 's'
87 dot = ""
88 if self.format:
89 dot = "."
90 uriBase = "http%s://%s/%s%s%s" %(
91 secure_str, self.domain, uri, dot, self.format)
92
93 headers = {}
94 if self.auth:
95 headers.update(self.auth.generate_headers())
96 arg_data = self.auth.encode_params(uriBase, method, kwargs)
97 if method == 'GET':
98 uriBase += '?' + arg_data
99 body = None
100 else:
101 body = arg_data
102
103 req = urllib2.Request(uriBase, body, headers)
104
105 try:
106 handle = urllib2.urlopen(req)
107 if "json" == self.format:
108 return json.loads(handle.read())
109 else:
110 return handle.read()
111 except urllib2.HTTPError, e:
112 if (e.code == 304):
113 return []
114 else:
115 raise TwitterHTTPError(e, uri, self.format, arg_data)
116
117 class Twitter(TwitterCall):
118 """
119 The minimalist yet fully featured Twitter API class.
120
121 Get RESTful data by accessing members of this class. The result
122 is decoded python objects (lists and dicts).
123
124 The Twitter API is documented here:
125
126 http://dev.twitter.com/doc
127
128
129 Examples::
130
131 twitter = Twitter(
132 auth=OAuth(token, token_key, con_secret, con_secret_key)))
133
134 # Get the public timeline
135 twitter.statuses.public_timeline()
136
137 # Get a particular friend's timeline
138 twitter.statuses.friends_timeline(id="billybob")
139
140 # Also supported (but totally weird)
141 twitter.statuses.friends_timeline.billybob()
142
143 # Send a direct message
144 twitter.direct_messages.new(
145 user="billybob",
146 text="I think yer swell!")
147
148 # Get the members of a particular list of a particular friend
149 twitter.user.listname.members(user="billybob", listname="billysbuds")
150
151
152 Searching Twitter::
153
154 twitter_search = Twitter(domain="search.twitter.com")
155
156 # Find the latest search trends
157 twitter_search.trends()
158
159 # Search for the latest News on #gaza
160 twitter_search.search(q="#gaza")
161
162
163 Using the data returned
164 -----------------------
165
166 Twitter API calls return decoded JSON. This is converted into
167 a bunch of Python lists, dicts, ints, and strings. For example::
168
169 x = twitter.statuses.public_timeline()
170
171 # The first 'tweet' in the timeline
172 x[0]
173
174 # The screen name of the user who wrote the first 'tweet'
175 x[0]['user']['screen_name']
176
177
178 Getting raw XML data
179 --------------------
180
181 If you prefer to get your Twitter data in XML format, pass
182 format="xml" to the Twitter object when you instantiate it::
183
184 twitter = Twitter(format="xml")
185
186 The output will not be parsed in any way. It will be a raw string
187 of XML.
188
189 """
190 def __init__(
191 self, format="json",
192 domain="twitter.com", secure=True, auth=None,
193 api_version=''):
194 """
195 Create a new twitter API connector.
196
197 Pass an `auth` parameter to use the credentials of a specific
198 user. Generally you'll want to pass an `OAuth`
199 instance::
200
201 twitter = Twitter(auth=OAuth(
202 token, token_secret, consumer_key, consumer_secret))
203
204
205 `domain` lets you change the domain you are connecting. By
206 default it's twitter.com but `search.twitter.com` may be
207 useful too.
208
209 If `secure` is False you will connect with HTTP instead of
210 HTTPS.
211
212 The value of `agent` is sent in the `X-Twitter-Client`
213 header. This is deprecated. Instead Twitter determines the
214 application using the OAuth Client Key and Client Key Secret
215 parameters.
216
217 `api_version` is used to set the base uri. By default it's
218 nothing, but if you set it to '1' your URI will start with
219 '1/'.
220 """
221 if not auth:
222 auth = NoAuth()
223
224 if (format not in ("json", "xml", "")):
225 raise ValueError("Unknown data format '%s'" %(format))
226
227 uriparts = ()
228 if api_version:
229 uriparts += (str(api_version),)
230
231 TwitterCall.__init__(
232 self, auth=auth, format=format, domain=domain,
233 secure=secure, uriparts=uriparts)
234
235
236 __all__ = ["Twitter", "TwitterError", "TwitterHTTPError"]