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