~nmu-sscheel/gtg/rework-task-editor

« back to all changes in this revision

Viewing changes to GTG/backends/twitter.py

  • Committer: Luca Invernizzi
  • Date: 2010-09-08 14:15:11 UTC
  • mfrom: (825.1.227 liblarch_rebased)
  • Revision ID: invernizzi.l@gmail.com-20100908141511-vsctgw74dj1xp0wi
Liblarch is now in trunk.
Note that performances are still bad and it misses DnD and multi-select
support, but its state is better that the current trunk.
Backends are added in this merge too.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python2.4
 
2
#
 
3
# Copyright 2007 Google Inc. All Rights Reserved.
 
4
#
 
5
# Licensed under the Apache License, Version 2.0 (the "License");
 
6
# you may not use this file except in compliance with the License.
 
7
# You may obtain a copy of the License at
 
8
#
 
9
#     http://www.apache.org/licenses/LICENSE-2.0
 
10
#
 
11
# Unless required by applicable law or agreed to in writing, software
 
12
# distributed under the License is distributed on an "AS IS" BASIS,
 
13
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
14
# See the License for the specific language governing permissions and
 
15
# limitations under the License.
 
16
 
 
17
'''A library that provides a python interface to the Twitter API'''
 
18
 
 
19
__author__ = 'dewitt@google.com'
 
20
__version__ = '0.7-devel'
 
21
 
 
22
 
 
23
import base64
 
24
import calendar
 
25
import httplib
 
26
import os
 
27
import rfc822
 
28
import simplejson
 
29
import sys
 
30
import tempfile
 
31
import textwrap
 
32
import time
 
33
import urllib
 
34
import urllib2
 
35
import urlparse
 
36
import gzip
 
37
import StringIO
 
38
 
 
39
try:
 
40
  from hashlib import md5
 
41
except ImportError:
 
42
  from md5 import md5
 
43
 
 
44
 
 
45
CHARACTER_LIMIT = 140
 
46
 
 
47
# A singleton representing a lazily instantiated FileCache.
 
48
DEFAULT_CACHE = object()
 
49
 
 
50
 
 
51
class TwitterError(Exception):
 
52
  '''Base class for Twitter errors'''
 
53
  
 
54
  @property
 
55
  def message(self):
 
56
    '''Returns the first argument used to construct this error.'''
 
57
    return self.args[0]
 
58
 
 
59
 
 
60
class Status(object):
 
61
  '''A class representing the Status structure used by the twitter API.
 
62
 
 
63
  The Status structure exposes the following properties:
 
64
 
 
65
    status.created_at
 
66
    status.created_at_in_seconds # read only
 
67
    status.favorited
 
68
    status.in_reply_to_screen_name
 
69
    status.in_reply_to_user_id
 
70
    status.in_reply_to_status_id
 
71
    status.truncated
 
72
    status.source
 
73
    status.id
 
74
    status.text
 
75
    status.location
 
76
    status.relative_created_at # read only
 
77
    status.user
 
78
  '''
 
79
  def __init__(self,
 
80
               created_at=None,
 
81
               favorited=None,
 
82
               id=None,
 
83
               text=None,
 
84
               location=None,
 
85
               user=None,
 
86
               in_reply_to_screen_name=None,
 
87
               in_reply_to_user_id=None,
 
88
               in_reply_to_status_id=None,
 
89
               truncated=None,
 
90
               source=None,
 
91
               now=None):
 
92
    '''An object to hold a Twitter status message.
 
93
 
 
94
    This class is normally instantiated by the twitter.Api class and
 
95
    returned in a sequence.
 
96
 
 
97
    Note: Dates are posted in the form "Sat Jan 27 04:17:38 +0000 2007"
 
98
 
 
99
    Args:
 
100
      created_at: The time this status message was posted
 
101
      favorited: Whether this is a favorite of the authenticated user
 
102
      id: The unique id of this status message
 
103
      text: The text of this status message
 
104
      location: the geolocation string associated with this message
 
105
      relative_created_at:
 
106
        A human readable string representing the posting time
 
107
      user:
 
108
        A twitter.User instance representing the person posting the message
 
109
      now:
 
110
        The current time, if the client choses to set it.  Defaults to the
 
111
        wall clock time.
 
112
    '''
 
113
    self.created_at = created_at
 
114
    self.favorited = favorited
 
115
    self.id = id
 
116
    self.text = text
 
117
    self.location = location
 
118
    self.user = user
 
119
    self.now = now
 
120
    self.in_reply_to_screen_name = in_reply_to_screen_name
 
121
    self.in_reply_to_user_id = in_reply_to_user_id
 
122
    self.in_reply_to_status_id = in_reply_to_status_id
 
123
    self.truncated = truncated
 
124
    self.source = source
 
125
 
 
126
  def GetCreatedAt(self):
 
127
    '''Get the time this status message was posted.
 
128
 
 
129
    Returns:
 
130
      The time this status message was posted
 
131
    '''
 
132
    return self._created_at
 
133
 
 
134
  def SetCreatedAt(self, created_at):
 
135
    '''Set the time this status message was posted.
 
136
 
 
137
    Args:
 
138
      created_at: The time this status message was created
 
139
    '''
 
140
    self._created_at = created_at
 
141
 
 
142
  created_at = property(GetCreatedAt, SetCreatedAt,
 
143
                        doc='The time this status message was posted.')
 
144
 
 
145
  def GetCreatedAtInSeconds(self):
 
146
    '''Get the time this status message was posted, in seconds since the epoch.
 
147
 
 
148
    Returns:
 
149
      The time this status message was posted, in seconds since the epoch.
 
150
    '''
 
151
    return calendar.timegm(rfc822.parsedate(self.created_at))
 
152
 
 
153
  created_at_in_seconds = property(GetCreatedAtInSeconds,
 
154
                                   doc="The time this status message was "
 
155
                                       "posted, in seconds since the epoch")
 
156
 
 
157
  def GetFavorited(self):
 
158
    '''Get the favorited setting of this status message.
 
159
 
 
160
    Returns:
 
161
      True if this status message is favorited; False otherwise
 
162
    '''
 
163
    return self._favorited
 
164
 
 
165
  def SetFavorited(self, favorited):
 
166
    '''Set the favorited state of this status message.
 
167
 
 
168
    Args:
 
169
      favorited: boolean True/False favorited state of this status message
 
170
    '''
 
171
    self._favorited = favorited
 
172
 
 
173
  favorited = property(GetFavorited, SetFavorited,
 
174
                       doc='The favorited state of this status message.')
 
175
 
 
176
  def GetId(self):
 
177
    '''Get the unique id of this status message.
 
178
 
 
179
    Returns:
 
180
      The unique id of this status message
 
181
    '''
 
182
    return self._id
 
183
 
 
184
  def SetId(self, id):
 
185
    '''Set the unique id of this status message.
 
186
 
 
187
    Args:
 
188
      id: The unique id of this status message
 
189
    '''
 
190
    self._id = id
 
191
 
 
192
  id = property(GetId, SetId,
 
193
                doc='The unique id of this status message.')
 
194
 
 
195
  def GetInReplyToScreenName(self):
 
196
    return self._in_reply_to_screen_name
 
197
 
 
198
  def SetInReplyToScreenName(self, in_reply_to_screen_name):
 
199
    self._in_reply_to_screen_name = in_reply_to_screen_name
 
200
 
 
201
  in_reply_to_screen_name = property(GetInReplyToScreenName, SetInReplyToScreenName,
 
202
                doc='')
 
203
 
 
204
  def GetInReplyToUserId(self):
 
205
    return self._in_reply_to_user_id
 
206
 
 
207
  def SetInReplyToUserId(self, in_reply_to_user_id):
 
208
    self._in_reply_to_user_id = in_reply_to_user_id
 
209
 
 
210
  in_reply_to_user_id = property(GetInReplyToUserId, SetInReplyToUserId,
 
211
                doc='')
 
212
 
 
213
  def GetInReplyToStatusId(self):
 
214
    return self._in_reply_to_status_id
 
215
 
 
216
  def SetInReplyToStatusId(self, in_reply_to_status_id):
 
217
    self._in_reply_to_status_id = in_reply_to_status_id
 
218
 
 
219
  in_reply_to_status_id = property(GetInReplyToStatusId, SetInReplyToStatusId,
 
220
                doc='')
 
221
 
 
222
  def GetTruncated(self):
 
223
    return self._truncated
 
224
 
 
225
  def SetTruncated(self, truncated):
 
226
    self._truncated = truncated
 
227
 
 
228
  truncated = property(GetTruncated, SetTruncated,
 
229
                doc='')
 
230
 
 
231
  def GetSource(self):
 
232
    return self._source
 
233
 
 
234
  def SetSource(self, source):
 
235
    self._source = source
 
236
 
 
237
  source = property(GetSource, SetSource,
 
238
                doc='')
 
239
 
 
240
  def GetText(self):
 
241
    '''Get the text of this status message.
 
242
 
 
243
    Returns:
 
244
      The text of this status message.
 
245
    '''
 
246
    return self._text
 
247
 
 
248
  def SetText(self, text):
 
249
    '''Set the text of this status message.
 
250
 
 
251
    Args:
 
252
      text: The text of this status message
 
253
    '''
 
254
    self._text = text
 
255
 
 
256
  text = property(GetText, SetText,
 
257
                  doc='The text of this status message')
 
258
 
 
259
  def GetLocation(self):
 
260
    '''Get the geolocation associated with this status message
 
261
 
 
262
    Returns:
 
263
      The geolocation string of this status message.
 
264
    '''
 
265
    return self._location
 
266
 
 
267
  def SetLocation(self, location):
 
268
    '''Set the geolocation associated with this status message
 
269
 
 
270
    Args:
 
271
      location: The geolocation string of this status message
 
272
    '''
 
273
    self._location = location
 
274
 
 
275
  location = property(GetLocation, SetLocation,
 
276
                      doc='The geolocation string of this status message')
 
277
 
 
278
  def GetRelativeCreatedAt(self):
 
279
    '''Get a human redable string representing the posting time
 
280
 
 
281
    Returns:
 
282
      A human readable string representing the posting time
 
283
    '''
 
284
    fudge = 1.25
 
285
    delta  = long(self.now) - long(self.created_at_in_seconds)
 
286
 
 
287
    if delta < (1 * fudge):
 
288
      return 'about a second ago'
 
289
    elif delta < (60 * (1/fudge)):
 
290
      return 'about %d seconds ago' % (delta)
 
291
    elif delta < (60 * fudge):
 
292
      return 'about a minute ago'
 
293
    elif delta < (60 * 60 * (1/fudge)):
 
294
      return 'about %d minutes ago' % (delta / 60)
 
295
    elif delta < (60 * 60 * fudge) or delta / (60 * 60) == 1:
 
296
      return 'about an hour ago'
 
297
    elif delta < (60 * 60 * 24 * (1/fudge)):
 
298
      return 'about %d hours ago' % (delta / (60 * 60))
 
299
    elif delta < (60 * 60 * 24 * fudge) or delta / (60 * 60 * 24) == 1:
 
300
      return 'about a day ago'
 
301
    else:
 
302
      return 'about %d days ago' % (delta / (60 * 60 * 24))
 
303
 
 
304
  relative_created_at = property(GetRelativeCreatedAt,
 
305
                                 doc='Get a human readable string representing'
 
306
                                     'the posting time')
 
307
 
 
308
  def GetUser(self):
 
309
    '''Get a twitter.User reprenting the entity posting this status message.
 
310
 
 
311
    Returns:
 
312
      A twitter.User reprenting the entity posting this status message
 
313
    '''
 
314
    return self._user
 
315
 
 
316
  def SetUser(self, user):
 
317
    '''Set a twitter.User reprenting the entity posting this status message.
 
318
 
 
319
    Args:
 
320
      user: A twitter.User reprenting the entity posting this status message
 
321
    '''
 
322
    self._user = user
 
323
 
 
324
  user = property(GetUser, SetUser,
 
325
                  doc='A twitter.User reprenting the entity posting this '
 
326
                      'status message')
 
327
 
 
328
  def GetNow(self):
 
329
    '''Get the wallclock time for this status message.
 
330
 
 
331
    Used to calculate relative_created_at.  Defaults to the time
 
332
    the object was instantiated.
 
333
 
 
334
    Returns:
 
335
      Whatever the status instance believes the current time to be,
 
336
      in seconds since the epoch.
 
337
    '''
 
338
    if self._now is None:
 
339
      self._now = time.time()
 
340
    return self._now
 
341
 
 
342
  def SetNow(self, now):
 
343
    '''Set the wallclock time for this status message.
 
344
 
 
345
    Used to calculate relative_created_at.  Defaults to the time
 
346
    the object was instantiated.
 
347
 
 
348
    Args:
 
349
      now: The wallclock time for this instance.
 
350
    '''
 
351
    self._now = now
 
352
 
 
353
  now = property(GetNow, SetNow,
 
354
                 doc='The wallclock time for this status instance.')
 
355
 
 
356
 
 
357
  def __ne__(self, other):
 
358
    return not self.__eq__(other)
 
359
 
 
360
  def __eq__(self, other):
 
361
    try:
 
362
      return other and \
 
363
             self.created_at == other.created_at and \
 
364
             self.id == other.id and \
 
365
             self.text == other.text and \
 
366
             self.location == other.location and \
 
367
             self.user == other.user and \
 
368
             self.in_reply_to_screen_name == other.in_reply_to_screen_name and \
 
369
             self.in_reply_to_user_id == other.in_reply_to_user_id and \
 
370
             self.in_reply_to_status_id == other.in_reply_to_status_id and \
 
371
             self.truncated == other.truncated and \
 
372
             self.favorited == other.favorited and \
 
373
             self.source == other.source
 
374
    except AttributeError:
 
375
      return False
 
376
 
 
377
  def __str__(self):
 
378
    '''A string representation of this twitter.Status instance.
 
379
 
 
380
    The return value is the same as the JSON string representation.
 
381
 
 
382
    Returns:
 
383
      A string representation of this twitter.Status instance.
 
384
    '''
 
385
    return self.AsJsonString()
 
386
 
 
387
  def AsJsonString(self):
 
388
    '''A JSON string representation of this twitter.Status instance.
 
389
 
 
390
    Returns:
 
391
      A JSON string representation of this twitter.Status instance
 
392
   '''
 
393
    return simplejson.dumps(self.AsDict(), sort_keys=True)
 
394
 
 
395
  def AsDict(self):
 
396
    '''A dict representation of this twitter.Status instance.
 
397
 
 
398
    The return value uses the same key names as the JSON representation.
 
399
 
 
400
    Return:
 
401
      A dict representing this twitter.Status instance
 
402
    '''
 
403
    data = {}
 
404
    if self.created_at:
 
405
      data['created_at'] = self.created_at
 
406
    if self.favorited:
 
407
      data['favorited'] = self.favorited
 
408
    if self.id:
 
409
      data['id'] = self.id
 
410
    if self.text:
 
411
      data['text'] = self.text
 
412
    if self.location:
 
413
      data['location'] = self.location  
 
414
    if self.user:
 
415
      data['user'] = self.user.AsDict()
 
416
    if self.in_reply_to_screen_name:
 
417
      data['in_reply_to_screen_name'] = self.in_reply_to_screen_name
 
418
    if self.in_reply_to_user_id:
 
419
      data['in_reply_to_user_id'] = self.in_reply_to_user_id
 
420
    if self.in_reply_to_status_id:
 
421
      data['in_reply_to_status_id'] = self.in_reply_to_status_id
 
422
    if self.truncated is not None:
 
423
      data['truncated'] = self.truncated
 
424
    if self.favorited is not None:
 
425
      data['favorited'] = self.favorited
 
426
    if self.source:
 
427
      data['source'] = self.source
 
428
    return data
 
429
 
 
430
  @staticmethod
 
431
  def NewFromJsonDict(data):
 
432
    '''Create a new instance based on a JSON dict.
 
433
 
 
434
    Args:
 
435
      data: A JSON dict, as converted from the JSON in the twitter API
 
436
    Returns:
 
437
      A twitter.Status instance
 
438
    '''
 
439
    if 'user' in data:
 
440
      user = User.NewFromJsonDict(data['user'])
 
441
    else:
 
442
      user = None
 
443
    return Status(created_at=data.get('created_at', None),
 
444
                  favorited=data.get('favorited', None),
 
445
                  id=data.get('id', None),
 
446
                  text=data.get('text', None),
 
447
                  location=data.get('location', None),
 
448
                  in_reply_to_screen_name=data.get('in_reply_to_screen_name', None),
 
449
                  in_reply_to_user_id=data.get('in_reply_to_user_id', None),
 
450
                  in_reply_to_status_id=data.get('in_reply_to_status_id', None),
 
451
                  truncated=data.get('truncated', None),
 
452
                  source=data.get('source', None),
 
453
                  user=user)
 
454
 
 
455
 
 
456
class User(object):
 
457
  '''A class representing the User structure used by the twitter API.
 
458
 
 
459
  The User structure exposes the following properties:
 
460
 
 
461
    user.id
 
462
    user.name
 
463
    user.screen_name
 
464
    user.location
 
465
    user.description
 
466
    user.profile_image_url
 
467
    user.profile_background_tile
 
468
    user.profile_background_image_url
 
469
    user.profile_sidebar_fill_color
 
470
    user.profile_background_color
 
471
    user.profile_link_color
 
472
    user.profile_text_color
 
473
    user.protected
 
474
    user.utc_offset
 
475
    user.time_zone
 
476
    user.url
 
477
    user.status
 
478
    user.statuses_count
 
479
    user.followers_count
 
480
    user.friends_count
 
481
    user.favourites_count
 
482
  '''
 
483
  def __init__(self,
 
484
               id=None,
 
485
               name=None,
 
486
               screen_name=None,
 
487
               location=None,
 
488
               description=None,
 
489
               profile_image_url=None,
 
490
               profile_background_tile=None,
 
491
               profile_background_image_url=None,
 
492
               profile_sidebar_fill_color=None,
 
493
               profile_background_color=None,
 
494
               profile_link_color=None,
 
495
               profile_text_color=None,
 
496
               protected=None,
 
497
               utc_offset=None,
 
498
               time_zone=None,
 
499
               followers_count=None,
 
500
               friends_count=None,
 
501
               statuses_count=None,
 
502
               favourites_count=None,
 
503
               url=None,
 
504
               status=None):
 
505
    self.id = id
 
506
    self.name = name
 
507
    self.screen_name = screen_name
 
508
    self.location = location
 
509
    self.description = description
 
510
    self.profile_image_url = profile_image_url
 
511
    self.profile_background_tile = profile_background_tile
 
512
    self.profile_background_image_url = profile_background_image_url
 
513
    self.profile_sidebar_fill_color = profile_sidebar_fill_color
 
514
    self.profile_background_color = profile_background_color
 
515
    self.profile_link_color = profile_link_color
 
516
    self.profile_text_color = profile_text_color
 
517
    self.protected = protected
 
518
    self.utc_offset = utc_offset
 
519
    self.time_zone = time_zone
 
520
    self.followers_count = followers_count
 
521
    self.friends_count = friends_count
 
522
    self.statuses_count = statuses_count
 
523
    self.favourites_count = favourites_count
 
524
    self.url = url
 
525
    self.status = status
 
526
 
 
527
 
 
528
  def GetId(self):
 
529
    '''Get the unique id of this user.
 
530
 
 
531
    Returns:
 
532
      The unique id of this user
 
533
    '''
 
534
    return self._id
 
535
 
 
536
  def SetId(self, id):
 
537
    '''Set the unique id of this user.
 
538
 
 
539
    Args:
 
540
      id: The unique id of this user.
 
541
    '''
 
542
    self._id = id
 
543
 
 
544
  id = property(GetId, SetId,
 
545
                doc='The unique id of this user.')
 
546
 
 
547
  def GetName(self):
 
548
    '''Get the real name of this user.
 
549
 
 
550
    Returns:
 
551
      The real name of this user
 
552
    '''
 
553
    return self._name
 
554
 
 
555
  def SetName(self, name):
 
556
    '''Set the real name of this user.
 
557
 
 
558
    Args:
 
559
      name: The real name of this user
 
560
    '''
 
561
    self._name = name
 
562
 
 
563
  name = property(GetName, SetName,
 
564
                  doc='The real name of this user.')
 
565
 
 
566
  def GetScreenName(self):
 
567
    '''Get the short username of this user.
 
568
 
 
569
    Returns:
 
570
      The short username of this user
 
571
    '''
 
572
    return self._screen_name
 
573
 
 
574
  def SetScreenName(self, screen_name):
 
575
    '''Set the short username of this user.
 
576
 
 
577
    Args:
 
578
      screen_name: the short username of this user
 
579
    '''
 
580
    self._screen_name = screen_name
 
581
 
 
582
  screen_name = property(GetScreenName, SetScreenName,
 
583
                         doc='The short username of this user.')
 
584
 
 
585
  def GetLocation(self):
 
586
    '''Get the geographic location of this user.
 
587
 
 
588
    Returns:
 
589
      The geographic location of this user
 
590
    '''
 
591
    return self._location
 
592
 
 
593
  def SetLocation(self, location):
 
594
    '''Set the geographic location of this user.
 
595
 
 
596
    Args:
 
597
      location: The geographic location of this user
 
598
    '''
 
599
    self._location = location
 
600
 
 
601
  location = property(GetLocation, SetLocation,
 
602
                      doc='The geographic location of this user.')
 
603
 
 
604
  def GetDescription(self):
 
605
    '''Get the short text description of this user.
 
606
 
 
607
    Returns:
 
608
      The short text description of this user
 
609
    '''
 
610
    return self._description
 
611
 
 
612
  def SetDescription(self, description):
 
613
    '''Set the short text description of this user.
 
614
 
 
615
    Args:
 
616
      description: The short text description of this user
 
617
    '''
 
618
    self._description = description
 
619
 
 
620
  description = property(GetDescription, SetDescription,
 
621
                         doc='The short text description of this user.')
 
622
 
 
623
  def GetUrl(self):
 
624
    '''Get the homepage url of this user.
 
625
 
 
626
    Returns:
 
627
      The homepage url of this user
 
628
    '''
 
629
    return self._url
 
630
 
 
631
  def SetUrl(self, url):
 
632
    '''Set the homepage url of this user.
 
633
 
 
634
    Args:
 
635
      url: The homepage url of this user
 
636
    '''
 
637
    self._url = url
 
638
 
 
639
  url = property(GetUrl, SetUrl,
 
640
                 doc='The homepage url of this user.')
 
641
 
 
642
  def GetProfileImageUrl(self):
 
643
    '''Get the url of the thumbnail of this user.
 
644
 
 
645
    Returns:
 
646
      The url of the thumbnail of this user
 
647
    '''
 
648
    return self._profile_image_url
 
649
 
 
650
  def SetProfileImageUrl(self, profile_image_url):
 
651
    '''Set the url of the thumbnail of this user.
 
652
 
 
653
    Args:
 
654
      profile_image_url: The url of the thumbnail of this user
 
655
    '''
 
656
    self._profile_image_url = profile_image_url
 
657
 
 
658
  profile_image_url= property(GetProfileImageUrl, SetProfileImageUrl,
 
659
                              doc='The url of the thumbnail of this user.')
 
660
 
 
661
  def GetProfileBackgroundTile(self):
 
662
    '''Boolean for whether to tile the profile background image.
 
663
 
 
664
    Returns:
 
665
      True if the background is to be tiled, False if not, None if unset.
 
666
    '''
 
667
    return self._profile_background_tile
 
668
 
 
669
  def SetProfileBackgroundTile(self, profile_background_tile):
 
670
    '''Set the boolean flag for whether to tile the profile background image.
 
671
 
 
672
    Args:
 
673
      profile_background_tile: Boolean flag for whether to tile or not.
 
674
    '''
 
675
    self._profile_background_tile = profile_background_tile
 
676
 
 
677
  profile_background_tile = property(GetProfileBackgroundTile, SetProfileBackgroundTile,
 
678
                                     doc='Boolean for whether to tile the background image.')
 
679
 
 
680
  def GetProfileBackgroundImageUrl(self):
 
681
    return self._profile_background_image_url
 
682
 
 
683
  def SetProfileBackgroundImageUrl(self, profile_background_image_url):
 
684
    self._profile_background_image_url = profile_background_image_url
 
685
 
 
686
  profile_background_image_url = property(GetProfileBackgroundImageUrl, SetProfileBackgroundImageUrl,
 
687
                                          doc='The url of the profile background of this user.')
 
688
 
 
689
  def GetProfileSidebarFillColor(self):
 
690
    return self._profile_sidebar_fill_color
 
691
 
 
692
  def SetProfileSidebarFillColor(self, profile_sidebar_fill_color):
 
693
    self._profile_sidebar_fill_color = profile_sidebar_fill_color
 
694
 
 
695
  profile_sidebar_fill_color = property(GetProfileSidebarFillColor, SetProfileSidebarFillColor)
 
696
 
 
697
  def GetProfileBackgroundColor(self):
 
698
    return self._profile_background_color
 
699
 
 
700
  def SetProfileBackgroundColor(self, profile_background_color):
 
701
    self._profile_background_color = profile_background_color
 
702
 
 
703
  profile_background_color = property(GetProfileBackgroundColor, SetProfileBackgroundColor)
 
704
 
 
705
  def GetProfileLinkColor(self):
 
706
    return self._profile_link_color
 
707
 
 
708
  def SetProfileLinkColor(self, profile_link_color):
 
709
    self._profile_link_color = profile_link_color
 
710
 
 
711
  profile_link_color = property(GetProfileLinkColor, SetProfileLinkColor)
 
712
 
 
713
  def GetProfileTextColor(self):
 
714
    return self._profile_text_color
 
715
 
 
716
  def SetProfileTextColor(self, profile_text_color):
 
717
    self._profile_text_color = profile_text_color
 
718
 
 
719
  profile_text_color = property(GetProfileTextColor, SetProfileTextColor)
 
720
 
 
721
  def GetProtected(self):
 
722
    return self._protected
 
723
 
 
724
  def SetProtected(self, protected):
 
725
    self._protected = protected
 
726
 
 
727
  protected = property(GetProtected, SetProtected)
 
728
 
 
729
  def GetUtcOffset(self):
 
730
    return self._utc_offset
 
731
 
 
732
  def SetUtcOffset(self, utc_offset):
 
733
    self._utc_offset = utc_offset
 
734
 
 
735
  utc_offset = property(GetUtcOffset, SetUtcOffset)
 
736
 
 
737
  def GetTimeZone(self):
 
738
    '''Returns the current time zone string for the user.
 
739
 
 
740
    Returns:
 
741
      The descriptive time zone string for the user.
 
742
    '''
 
743
    return self._time_zone
 
744
 
 
745
  def SetTimeZone(self, time_zone):
 
746
    '''Sets the user's time zone string.
 
747
 
 
748
    Args:
 
749
      time_zone: The descriptive time zone to assign for the user.
 
750
    '''
 
751
    self._time_zone = time_zone
 
752
 
 
753
  time_zone = property(GetTimeZone, SetTimeZone)
 
754
 
 
755
  def GetStatus(self):
 
756
    '''Get the latest twitter.Status of this user.
 
757
 
 
758
    Returns:
 
759
      The latest twitter.Status of this user
 
760
    '''
 
761
    return self._status
 
762
 
 
763
  def SetStatus(self, status):
 
764
    '''Set the latest twitter.Status of this user.
 
765
 
 
766
    Args:
 
767
      status: The latest twitter.Status of this user
 
768
    '''
 
769
    self._status = status
 
770
 
 
771
  status = property(GetStatus, SetStatus,
 
772
                  doc='The latest twitter.Status of this user.')
 
773
 
 
774
  def GetFriendsCount(self):
 
775
    '''Get the friend count for this user.
 
776
    
 
777
    Returns:
 
778
      The number of users this user has befriended.
 
779
    '''
 
780
    return self._friends_count
 
781
 
 
782
  def SetFriendsCount(self, count):
 
783
    '''Set the friend count for this user.
 
784
 
 
785
    Args:
 
786
      count: The number of users this user has befriended.
 
787
    '''
 
788
    self._friends_count = count
 
789
 
 
790
  friends_count = property(GetFriendsCount, SetFriendsCount,
 
791
                  doc='The number of friends for this user.')
 
792
 
 
793
  def GetFollowersCount(self):
 
794
    '''Get the follower count for this user.
 
795
    
 
796
    Returns:
 
797
      The number of users following this user.
 
798
    '''
 
799
    return self._followers_count
 
800
 
 
801
  def SetFollowersCount(self, count):
 
802
    '''Set the follower count for this user.
 
803
 
 
804
    Args:
 
805
      count: The number of users following this user.
 
806
    '''
 
807
    self._followers_count = count
 
808
 
 
809
  followers_count = property(GetFollowersCount, SetFollowersCount,
 
810
                  doc='The number of users following this user.')
 
811
 
 
812
  def GetStatusesCount(self):
 
813
    '''Get the number of status updates for this user.
 
814
    
 
815
    Returns:
 
816
      The number of status updates for this user.
 
817
    '''
 
818
    return self._statuses_count
 
819
 
 
820
  def SetStatusesCount(self, count):
 
821
    '''Set the status update count for this user.
 
822
 
 
823
    Args:
 
824
      count: The number of updates for this user.
 
825
    '''
 
826
    self._statuses_count = count
 
827
 
 
828
  statuses_count = property(GetStatusesCount, SetStatusesCount,
 
829
                  doc='The number of updates for this user.')
 
830
 
 
831
  def GetFavouritesCount(self):
 
832
    '''Get the number of favourites for this user.
 
833
    
 
834
    Returns:
 
835
      The number of favourites for this user.
 
836
    '''
 
837
    return self._favourites_count
 
838
 
 
839
  def SetFavouritesCount(self, count):
 
840
    '''Set the favourite count for this user.
 
841
 
 
842
    Args:
 
843
      count: The number of favourites for this user.
 
844
    '''
 
845
    self._favourites_count = count
 
846
 
 
847
  favourites_count = property(GetFavouritesCount, SetFavouritesCount,
 
848
                  doc='The number of favourites for this user.')
 
849
 
 
850
  def __ne__(self, other):
 
851
    return not self.__eq__(other)
 
852
 
 
853
  def __eq__(self, other):
 
854
    try:
 
855
      return other and \
 
856
             self.id == other.id and \
 
857
             self.name == other.name and \
 
858
             self.screen_name == other.screen_name and \
 
859
             self.location == other.location and \
 
860
             self.description == other.description and \
 
861
             self.profile_image_url == other.profile_image_url and \
 
862
             self.profile_background_tile == other.profile_background_tile and \
 
863
             self.profile_background_image_url == other.profile_background_image_url and \
 
864
             self.profile_sidebar_fill_color == other.profile_sidebar_fill_color and \
 
865
             self.profile_background_color == other.profile_background_color and \
 
866
             self.profile_link_color == other.profile_link_color and \
 
867
             self.profile_text_color == other.profile_text_color and \
 
868
             self.protected == other.protected and \
 
869
             self.utc_offset == other.utc_offset and \
 
870
             self.time_zone == other.time_zone and \
 
871
             self.url == other.url and \
 
872
             self.statuses_count == other.statuses_count and \
 
873
             self.followers_count == other.followers_count and \
 
874
             self.favourites_count == other.favourites_count and \
 
875
             self.friends_count == other.friends_count and \
 
876
             self.status == other.status
 
877
    except AttributeError:
 
878
      return False
 
879
 
 
880
  def __str__(self):
 
881
    '''A string representation of this twitter.User instance.
 
882
 
 
883
    The return value is the same as the JSON string representation.
 
884
 
 
885
    Returns:
 
886
      A string representation of this twitter.User instance.
 
887
    '''
 
888
    return self.AsJsonString()
 
889
 
 
890
  def AsJsonString(self):
 
891
    '''A JSON string representation of this twitter.User instance.
 
892
 
 
893
    Returns:
 
894
      A JSON string representation of this twitter.User instance
 
895
   '''
 
896
    return simplejson.dumps(self.AsDict(), sort_keys=True)
 
897
 
 
898
  def AsDict(self):
 
899
    '''A dict representation of this twitter.User instance.
 
900
 
 
901
    The return value uses the same key names as the JSON representation.
 
902
 
 
903
    Return:
 
904
      A dict representing this twitter.User instance
 
905
    '''
 
906
    data = {}
 
907
    if self.id:
 
908
      data['id'] = self.id
 
909
    if self.name:
 
910
      data['name'] = self.name
 
911
    if self.screen_name:
 
912
      data['screen_name'] = self.screen_name
 
913
    if self.location:
 
914
      data['location'] = self.location
 
915
    if self.description:
 
916
      data['description'] = self.description
 
917
    if self.profile_image_url:
 
918
      data['profile_image_url'] = self.profile_image_url
 
919
    if self.profile_background_tile is not None:
 
920
      data['profile_background_tile'] = self.profile_background_tile
 
921
    if self.profile_background_image_url:
 
922
      data['profile_sidebar_fill_color'] = self.profile_background_image_url
 
923
    if self.profile_background_color:
 
924
      data['profile_background_color'] = self.profile_background_color
 
925
    if self.profile_link_color:
 
926
      data['profile_link_color'] = self.profile_link_color
 
927
    if self.profile_text_color:
 
928
      data['profile_text_color'] = self.profile_text_color
 
929
    if self.protected is not None:
 
930
      data['protected'] = self.protected
 
931
    if self.utc_offset:
 
932
      data['utc_offset'] = self.utc_offset
 
933
    if self.time_zone:
 
934
      data['time_zone'] = self.time_zone
 
935
    if self.url:
 
936
      data['url'] = self.url
 
937
    if self.status:
 
938
      data['status'] = self.status.AsDict()
 
939
    if self.friends_count:
 
940
      data['friends_count'] = self.friends_count
 
941
    if self.followers_count:
 
942
      data['followers_count'] = self.followers_count
 
943
    if self.statuses_count:
 
944
      data['statuses_count'] = self.statuses_count
 
945
    if self.favourites_count:
 
946
      data['favourites_count'] = self.favourites_count
 
947
    return data
 
948
 
 
949
  @staticmethod
 
950
  def NewFromJsonDict(data):
 
951
    '''Create a new instance based on a JSON dict.
 
952
 
 
953
    Args:
 
954
      data: A JSON dict, as converted from the JSON in the twitter API
 
955
    Returns:
 
956
      A twitter.User instance
 
957
    '''
 
958
    if 'status' in data:
 
959
      status = Status.NewFromJsonDict(data['status'])
 
960
    else:
 
961
      status = None
 
962
    return User(id=data.get('id', None),
 
963
                name=data.get('name', None),
 
964
                screen_name=data.get('screen_name', None),
 
965
                location=data.get('location', None),
 
966
                description=data.get('description', None),
 
967
                statuses_count=data.get('statuses_count', None),
 
968
                followers_count=data.get('followers_count', None),
 
969
                favourites_count=data.get('favourites_count', None),
 
970
                friends_count=data.get('friends_count', None),
 
971
                profile_image_url=data.get('profile_image_url', None),
 
972
                profile_background_tile = data.get('profile_background_tile', None),
 
973
                profile_background_image_url = data.get('profile_background_image_url', None),
 
974
                profile_sidebar_fill_color = data.get('profile_sidebar_fill_color', None),
 
975
                profile_background_color = data.get('profile_background_color', None),
 
976
                profile_link_color = data.get('profile_link_color', None),
 
977
                profile_text_color = data.get('profile_text_color', None),
 
978
                protected = data.get('protected', None),
 
979
                utc_offset = data.get('utc_offset', None),
 
980
                time_zone = data.get('time_zone', None),
 
981
                url=data.get('url', None),
 
982
                status=status)
 
983
 
 
984
class DirectMessage(object):
 
985
  '''A class representing the DirectMessage structure used by the twitter API.
 
986
 
 
987
  The DirectMessage structure exposes the following properties:
 
988
 
 
989
    direct_message.id
 
990
    direct_message.created_at
 
991
    direct_message.created_at_in_seconds # read only
 
992
    direct_message.sender_id
 
993
    direct_message.sender_screen_name
 
994
    direct_message.recipient_id
 
995
    direct_message.recipient_screen_name
 
996
    direct_message.text
 
997
  '''
 
998
 
 
999
  def __init__(self,
 
1000
               id=None,
 
1001
               created_at=None,
 
1002
               sender_id=None,
 
1003
               sender_screen_name=None,
 
1004
               recipient_id=None,
 
1005
               recipient_screen_name=None,
 
1006
               text=None):
 
1007
    '''An object to hold a Twitter direct message.
 
1008
 
 
1009
    This class is normally instantiated by the twitter.Api class and
 
1010
    returned in a sequence.
 
1011
 
 
1012
    Note: Dates are posted in the form "Sat Jan 27 04:17:38 +0000 2007"
 
1013
 
 
1014
    Args:
 
1015
      id: The unique id of this direct message
 
1016
      created_at: The time this direct message was posted
 
1017
      sender_id: The id of the twitter user that sent this message
 
1018
      sender_screen_name: The name of the twitter user that sent this message
 
1019
      recipient_id: The id of the twitter that received this message
 
1020
      recipient_screen_name: The name of the twitter that received this message
 
1021
      text: The text of this direct message
 
1022
    '''
 
1023
    self.id = id
 
1024
    self.created_at = created_at
 
1025
    self.sender_id = sender_id
 
1026
    self.sender_screen_name = sender_screen_name
 
1027
    self.recipient_id = recipient_id
 
1028
    self.recipient_screen_name = recipient_screen_name
 
1029
    self.text = text
 
1030
 
 
1031
  def GetId(self):
 
1032
    '''Get the unique id of this direct message.
 
1033
 
 
1034
    Returns:
 
1035
      The unique id of this direct message
 
1036
    '''
 
1037
    return self._id
 
1038
 
 
1039
  def SetId(self, id):
 
1040
    '''Set the unique id of this direct message.
 
1041
 
 
1042
    Args:
 
1043
      id: The unique id of this direct message
 
1044
    '''
 
1045
    self._id = id
 
1046
 
 
1047
  id = property(GetId, SetId,
 
1048
                doc='The unique id of this direct message.')
 
1049
 
 
1050
  def GetCreatedAt(self):
 
1051
    '''Get the time this direct message was posted.
 
1052
 
 
1053
    Returns:
 
1054
      The time this direct message was posted
 
1055
    '''
 
1056
    return self._created_at
 
1057
 
 
1058
  def SetCreatedAt(self, created_at):
 
1059
    '''Set the time this direct message was posted.
 
1060
 
 
1061
    Args:
 
1062
      created_at: The time this direct message was created
 
1063
    '''
 
1064
    self._created_at = created_at
 
1065
 
 
1066
  created_at = property(GetCreatedAt, SetCreatedAt,
 
1067
                        doc='The time this direct message was posted.')
 
1068
 
 
1069
  def GetCreatedAtInSeconds(self):
 
1070
    '''Get the time this direct message was posted, in seconds since the epoch.
 
1071
 
 
1072
    Returns:
 
1073
      The time this direct message was posted, in seconds since the epoch.
 
1074
    '''
 
1075
    return calendar.timegm(rfc822.parsedate(self.created_at))
 
1076
 
 
1077
  created_at_in_seconds = property(GetCreatedAtInSeconds,
 
1078
                                   doc="The time this direct message was "
 
1079
                                       "posted, in seconds since the epoch")
 
1080
 
 
1081
  def GetSenderId(self):
 
1082
    '''Get the unique sender id of this direct message.
 
1083
 
 
1084
    Returns:
 
1085
      The unique sender id of this direct message
 
1086
    '''
 
1087
    return self._sender_id
 
1088
 
 
1089
  def SetSenderId(self, sender_id):
 
1090
    '''Set the unique sender id of this direct message.
 
1091
 
 
1092
    Args:
 
1093
      sender id: The unique sender id of this direct message
 
1094
    '''
 
1095
    self._sender_id = sender_id
 
1096
 
 
1097
  sender_id = property(GetSenderId, SetSenderId,
 
1098
                doc='The unique sender id of this direct message.')
 
1099
 
 
1100
  def GetSenderScreenName(self):
 
1101
    '''Get the unique sender screen name of this direct message.
 
1102
 
 
1103
    Returns:
 
1104
      The unique sender screen name of this direct message
 
1105
    '''
 
1106
    return self._sender_screen_name
 
1107
 
 
1108
  def SetSenderScreenName(self, sender_screen_name):
 
1109
    '''Set the unique sender screen name of this direct message.
 
1110
 
 
1111
    Args:
 
1112
      sender_screen_name: The unique sender screen name of this direct message
 
1113
    '''
 
1114
    self._sender_screen_name = sender_screen_name
 
1115
 
 
1116
  sender_screen_name = property(GetSenderScreenName, SetSenderScreenName,
 
1117
                doc='The unique sender screen name of this direct message.')
 
1118
 
 
1119
  def GetRecipientId(self):
 
1120
    '''Get the unique recipient id of this direct message.
 
1121
 
 
1122
    Returns:
 
1123
      The unique recipient id of this direct message
 
1124
    '''
 
1125
    return self._recipient_id
 
1126
 
 
1127
  def SetRecipientId(self, recipient_id):
 
1128
    '''Set the unique recipient id of this direct message.
 
1129
 
 
1130
    Args:
 
1131
      recipient id: The unique recipient id of this direct message
 
1132
    '''
 
1133
    self._recipient_id = recipient_id
 
1134
 
 
1135
  recipient_id = property(GetRecipientId, SetRecipientId,
 
1136
                doc='The unique recipient id of this direct message.')
 
1137
 
 
1138
  def GetRecipientScreenName(self):
 
1139
    '''Get the unique recipient screen name of this direct message.
 
1140
 
 
1141
    Returns:
 
1142
      The unique recipient screen name of this direct message
 
1143
    '''
 
1144
    return self._recipient_screen_name
 
1145
 
 
1146
  def SetRecipientScreenName(self, recipient_screen_name):
 
1147
    '''Set the unique recipient screen name of this direct message.
 
1148
 
 
1149
    Args:
 
1150
      recipient_screen_name: The unique recipient screen name of this direct message
 
1151
    '''
 
1152
    self._recipient_screen_name = recipient_screen_name
 
1153
 
 
1154
  recipient_screen_name = property(GetRecipientScreenName, SetRecipientScreenName,
 
1155
                doc='The unique recipient screen name of this direct message.')
 
1156
 
 
1157
  def GetText(self):
 
1158
    '''Get the text of this direct message.
 
1159
 
 
1160
    Returns:
 
1161
      The text of this direct message.
 
1162
    '''
 
1163
    return self._text
 
1164
 
 
1165
  def SetText(self, text):
 
1166
    '''Set the text of this direct message.
 
1167
 
 
1168
    Args:
 
1169
      text: The text of this direct message
 
1170
    '''
 
1171
    self._text = text
 
1172
 
 
1173
  text = property(GetText, SetText,
 
1174
                  doc='The text of this direct message')
 
1175
 
 
1176
  def __ne__(self, other):
 
1177
    return not self.__eq__(other)
 
1178
 
 
1179
  def __eq__(self, other):
 
1180
    try:
 
1181
      return other and \
 
1182
          self.id == other.id and \
 
1183
          self.created_at == other.created_at and \
 
1184
          self.sender_id == other.sender_id and \
 
1185
          self.sender_screen_name == other.sender_screen_name and \
 
1186
          self.recipient_id == other.recipient_id and \
 
1187
          self.recipient_screen_name == other.recipient_screen_name and \
 
1188
          self.text == other.text
 
1189
    except AttributeError:
 
1190
      return False
 
1191
 
 
1192
  def __str__(self):
 
1193
    '''A string representation of this twitter.DirectMessage instance.
 
1194
 
 
1195
    The return value is the same as the JSON string representation.
 
1196
 
 
1197
    Returns:
 
1198
      A string representation of this twitter.DirectMessage instance.
 
1199
    '''
 
1200
    return self.AsJsonString()
 
1201
 
 
1202
  def AsJsonString(self):
 
1203
    '''A JSON string representation of this twitter.DirectMessage instance.
 
1204
 
 
1205
    Returns:
 
1206
      A JSON string representation of this twitter.DirectMessage instance
 
1207
   '''
 
1208
    return simplejson.dumps(self.AsDict(), sort_keys=True)
 
1209
 
 
1210
  def AsDict(self):
 
1211
    '''A dict representation of this twitter.DirectMessage instance.
 
1212
 
 
1213
    The return value uses the same key names as the JSON representation.
 
1214
 
 
1215
    Return:
 
1216
      A dict representing this twitter.DirectMessage instance
 
1217
    '''
 
1218
    data = {}
 
1219
    if self.id:
 
1220
      data['id'] = self.id
 
1221
    if self.created_at:
 
1222
      data['created_at'] = self.created_at
 
1223
    if self.sender_id:
 
1224
      data['sender_id'] = self.sender_id
 
1225
    if self.sender_screen_name:
 
1226
      data['sender_screen_name'] = self.sender_screen_name
 
1227
    if self.recipient_id:
 
1228
      data['recipient_id'] = self.recipient_id
 
1229
    if self.recipient_screen_name:
 
1230
      data['recipient_screen_name'] = self.recipient_screen_name
 
1231
    if self.text:
 
1232
      data['text'] = self.text
 
1233
    return data
 
1234
 
 
1235
  @staticmethod
 
1236
  def NewFromJsonDict(data):
 
1237
    '''Create a new instance based on a JSON dict.
 
1238
 
 
1239
    Args:
 
1240
      data: A JSON dict, as converted from the JSON in the twitter API
 
1241
    Returns:
 
1242
      A twitter.DirectMessage instance
 
1243
    '''
 
1244
    return DirectMessage(created_at=data.get('created_at', None),
 
1245
                         recipient_id=data.get('recipient_id', None),
 
1246
                         sender_id=data.get('sender_id', None),
 
1247
                         text=data.get('text', None),
 
1248
                         sender_screen_name=data.get('sender_screen_name', None),
 
1249
                         id=data.get('id', None),
 
1250
                         recipient_screen_name=data.get('recipient_screen_name', None))
 
1251
 
 
1252
class Api(object):
 
1253
  '''A python interface into the Twitter API
 
1254
 
 
1255
  By default, the Api caches results for 1 minute.
 
1256
 
 
1257
  Example usage:
 
1258
 
 
1259
    To create an instance of the twitter.Api class, with no authentication:
 
1260
 
 
1261
      >>> import twitter
 
1262
      >>> api = twitter.Api()
 
1263
 
 
1264
    To fetch the most recently posted public twitter status messages:
 
1265
 
 
1266
      >>> statuses = api.GetPublicTimeline()
 
1267
      >>> print [s.user.name for s in statuses]
 
1268
      [u'DeWitt', u'Kesuke Miyagi', u'ev', u'Buzz Andersen', u'Biz Stone'] #...
 
1269
 
 
1270
    To fetch a single user's public status messages, where "user" is either
 
1271
    a Twitter "short name" or their user id.
 
1272
 
 
1273
      >>> statuses = api.GetUserTimeline(user)
 
1274
      >>> print [s.text for s in statuses]
 
1275
 
 
1276
    To use authentication, instantiate the twitter.Api class with a
 
1277
    username and password:
 
1278
 
 
1279
      >>> api = twitter.Api(username='twitter user', password='twitter pass')
 
1280
 
 
1281
    To fetch your friends (after being authenticated):
 
1282
 
 
1283
      >>> users = api.GetFriends()
 
1284
      >>> print [u.name for u in users]
 
1285
 
 
1286
    To post a twitter status message (after being authenticated):
 
1287
 
 
1288
      >>> status = api.PostUpdate('I love python-twitter!')
 
1289
      >>> print status.text
 
1290
      I love python-twitter!
 
1291
 
 
1292
    There are many other methods, including:
 
1293
 
 
1294
      >>> api.PostUpdates(status)
 
1295
      >>> api.PostDirectMessage(user, text)
 
1296
      >>> api.GetUser(user)
 
1297
      >>> api.GetReplies()
 
1298
      >>> api.GetUserTimeline(user)
 
1299
      >>> api.GetStatus(id)
 
1300
      >>> api.DestroyStatus(id)
 
1301
      >>> api.GetFriendsTimeline(user)
 
1302
      >>> api.GetFriends(user)
 
1303
      >>> api.GetFollowers()
 
1304
      >>> api.GetFeatured()
 
1305
      >>> api.GetDirectMessages()
 
1306
      >>> api.PostDirectMessage(user, text)
 
1307
      >>> api.DestroyDirectMessage(id)
 
1308
      >>> api.DestroyFriendship(user)
 
1309
      >>> api.CreateFriendship(user)
 
1310
      >>> api.GetUserByEmail(email)
 
1311
      >>> api.VerifyCredentials()
 
1312
  '''
 
1313
 
 
1314
  DEFAULT_CACHE_TIMEOUT = 60 # cache for 1 minute
 
1315
 
 
1316
  _API_REALM = 'Twitter API'
 
1317
 
 
1318
  def __init__(self,
 
1319
               username=None,
 
1320
               password=None,
 
1321
               input_encoding=None,
 
1322
               request_headers=None,
 
1323
               cache=DEFAULT_CACHE,
 
1324
               shortner=None,
 
1325
               base_url=None,
 
1326
               use_gzip_compression=False):
 
1327
    '''Instantiate a new twitter.Api object.
 
1328
 
 
1329
    Args:
 
1330
      username:
 
1331
        The username of the twitter account.  [optional]
 
1332
      password:
 
1333
        The password for the twitter account. [optional]
 
1334
      input_encoding:
 
1335
        The encoding used to encode input strings. [optional]
 
1336
      request_header:
 
1337
        A dictionary of additional HTTP request headers. [optional]
 
1338
      cache:
 
1339
        The cache instance to use. Defaults to DEFAULT_CACHE.
 
1340
        Use None to disable caching. [optional]
 
1341
      shortner:
 
1342
        The shortner instance to use.  Defaults to None.
 
1343
        See shorten_url.py for an example shortner. [optional]
 
1344
      base_url:
 
1345
        The base URL to use to contact the Twitter API.
 
1346
        Defaults to https://twitter.com. [optional]
 
1347
      use_gzip_compression:
 
1348
        Set to True to tell enable gzip compression for any call
 
1349
        made to Twitter.  Defaults to False. [optional]
 
1350
    '''
 
1351
    self.SetCache(cache)
 
1352
    self._urllib = urllib2
 
1353
    self._cache_timeout = Api.DEFAULT_CACHE_TIMEOUT
 
1354
    self._InitializeRequestHeaders(request_headers)
 
1355
    self._InitializeUserAgent()
 
1356
    self._InitializeDefaultParameters()
 
1357
    self._input_encoding = input_encoding
 
1358
    self._use_gzip = use_gzip_compression
 
1359
    self.SetCredentials(username, password)
 
1360
    if base_url is None:
 
1361
      self.base_url = 'https://twitter.com'
 
1362
    else:
 
1363
      self.base_url = base_url
 
1364
 
 
1365
  def GetPublicTimeline(self,
 
1366
                        since_id=None):
 
1367
    '''Fetch the sequnce of public twitter.Status message for all users.
 
1368
 
 
1369
    Args:
 
1370
      since_id:
 
1371
        Returns only public statuses with an ID greater than
 
1372
        (that is, more recent than) the specified ID. [optional]
 
1373
 
 
1374
    Returns:
 
1375
      An sequence of twitter.Status instances, one for each message
 
1376
    '''
 
1377
    parameters = {}
 
1378
 
 
1379
    if since_id:
 
1380
      parameters['since_id'] = since_id
 
1381
 
 
1382
    url  = '%s/statuses/public_timeline.json' % self.base_url
 
1383
    json = self._FetchUrl(url,  parameters=parameters)
 
1384
    data = simplejson.loads(json)
 
1385
 
 
1386
    self._CheckForTwitterError(data)
 
1387
 
 
1388
    return [Status.NewFromJsonDict(x) for x in data]
 
1389
 
 
1390
  def FilterPublicTimeline(self,
 
1391
                           term,
 
1392
                           since_id=None):
 
1393
    '''Filter the public twitter timeline by a given search term on
 
1394
    the local machine.
 
1395
 
 
1396
    Args:
 
1397
      term:
 
1398
        term to search by.
 
1399
      since_id:
 
1400
        Returns only public statuses with an ID greater than
 
1401
        (that is, more recent than) the specified ID. [optional]
 
1402
 
 
1403
    Returns:
 
1404
      A sequence of twitter.Status instances, one for each message
 
1405
      containing the term
 
1406
    '''
 
1407
    statuses = self.GetPublicTimeline(since_id)
 
1408
    results  = []
 
1409
 
 
1410
    for s in statuses:
 
1411
      if s.text.lower().find(term.lower()) != -1:
 
1412
        results.append(s)
 
1413
 
 
1414
    return results
 
1415
 
 
1416
  def GetSearch(self,
 
1417
                term,
 
1418
                geocode=None,
 
1419
                since_id=None,
 
1420
                per_page=15,
 
1421
                page=1,
 
1422
                lang="en",
 
1423
                show_user="true",
 
1424
                query_users=False):
 
1425
    '''Return twitter search results for a given term.
 
1426
 
 
1427
    Args:
 
1428
      term:
 
1429
        term to search by.
 
1430
      since_id:
 
1431
        Returns only public statuses with an ID greater than
 
1432
        (that is, more recent than) the specified ID. [optional]
 
1433
      geocode:
 
1434
        geolocation information in the form (latitude, longitude, radius)
 
1435
        [optional]
 
1436
      per_page:
 
1437
        number of results to return.  Default is 15 [optional]
 
1438
      page:
 
1439
        which page of search results to return
 
1440
      lang:
 
1441
        language for results.  Default is English [optional]
 
1442
      show_user:
 
1443
        prefixes screen name in status
 
1444
      query_users:
 
1445
        If set to False, then all users only have screen_name and
 
1446
        profile_image_url available.
 
1447
        If set to True, all information of users are available,
 
1448
        but it uses lots of request quota, one per status.
 
1449
    Returns:
 
1450
      A sequence of twitter.Status instances, one for each message containing
 
1451
      the term
 
1452
    '''
 
1453
    # Build request parameters
 
1454
    parameters = {}
 
1455
 
 
1456
    if since_id:
 
1457
      parameters['since_id'] = since_id
 
1458
 
 
1459
    if not term:
 
1460
      return []
 
1461
 
 
1462
    parameters['q'] = urllib.quote_plus(term)
 
1463
    parameters['show_user'] = show_user
 
1464
    parameters['lang'] = lang
 
1465
    parameters['rpp'] = per_page
 
1466
    parameters['page'] = page
 
1467
 
 
1468
    if geocode is not None:
 
1469
      parameters['geocode'] = ','.join(map(str, geocode))
 
1470
 
 
1471
    # Make and send requests
 
1472
    url  = 'http://search.twitter.com/search.json'
 
1473
    json = self._FetchUrl(url, parameters=parameters)
 
1474
    data = simplejson.loads(json)
 
1475
 
 
1476
    self._CheckForTwitterError(data)
 
1477
 
 
1478
    results = []
 
1479
 
 
1480
    for x in data['results']:
 
1481
      temp = Status.NewFromJsonDict(x)
 
1482
 
 
1483
      if query_users:
 
1484
        # Build user object with new request
 
1485
        temp.user = self.GetUser(urllib.quote(x['from_user']))
 
1486
      else:
 
1487
        temp.user = User(screen_name=x['from_user'], profile_image_url=x['profile_image_url'])
 
1488
 
 
1489
      results.append(temp)
 
1490
 
 
1491
    # Return built list of statuses
 
1492
    return results # [Status.NewFromJsonDict(x) for x in data['results']]
 
1493
 
 
1494
  def GetFriendsTimeline(self,
 
1495
                         user=None,
 
1496
                         count=None,
 
1497
                         since=None,
 
1498
                         since_id=None):
 
1499
    '''Fetch the sequence of twitter.Status messages for a user's friends
 
1500
 
 
1501
    The twitter.Api instance must be authenticated if the user is private.
 
1502
 
 
1503
    Args:
 
1504
      user:
 
1505
        Specifies the ID or screen name of the user for whom to return
 
1506
        the friends_timeline.  If unspecified, the username and password
 
1507
        must be set in the twitter.Api instance.  [Optional]
 
1508
      count: 
 
1509
        Specifies the number of statuses to retrieve. May not be
 
1510
        greater than 200. [Optional]
 
1511
      since:
 
1512
        Narrows the returned results to just those statuses created
 
1513
        after the specified HTTP-formatted date. [Optional]
 
1514
      since_id:
 
1515
        Returns only public statuses with an ID greater than (that is,
 
1516
        more recent than) the specified ID. [Optional]
 
1517
 
 
1518
    Returns:
 
1519
      A sequence of twitter.Status instances, one for each message
 
1520
    '''
 
1521
    if not user and not self._username:
 
1522
      raise TwitterError("User must be specified if API is not authenticated.")
 
1523
    if user:
 
1524
      url = '%s/statuses/friends_timeline/%s.json' % (self.base_url, user)
 
1525
    else:
 
1526
      url = '%s/statuses/friends_timeline.json' % self.base_url
 
1527
    parameters = {}
 
1528
    if count is not None:
 
1529
      try:
 
1530
        if int(count) > 200:
 
1531
          raise TwitterError("'count' may not be greater than 200")
 
1532
      except ValueError:
 
1533
        raise TwitterError("'count' must be an integer")
 
1534
      parameters['count'] = count
 
1535
    if since:
 
1536
      parameters['since'] = since
 
1537
    if since_id:
 
1538
      parameters['since_id'] = since_id
 
1539
    json = self._FetchUrl(url, parameters=parameters)
 
1540
    data = simplejson.loads(json)
 
1541
    self._CheckForTwitterError(data)
 
1542
    return [Status.NewFromJsonDict(x) for x in data]
 
1543
 
 
1544
  def GetUserTimeline(self,
 
1545
                      id=None,
 
1546
                      user_id=None,
 
1547
                      screen_name=None,
 
1548
                      since_id=None,
 
1549
                      max_id=None,
 
1550
                      count=None,
 
1551
                      page=None):
 
1552
    '''Fetch the sequence of public Status messages for a single user.
 
1553
 
 
1554
    The twitter.Api instance must be authenticated if the user is private.
 
1555
 
 
1556
    Args:
 
1557
      id:
 
1558
        Specifies the ID or screen name of the user for whom to return
 
1559
        the user_timeline. [optional]
 
1560
      user_id:
 
1561
        Specfies the ID of the user for whom to return the
 
1562
        user_timeline. Helpful for disambiguating when a valid user ID
 
1563
        is also a valid screen name. [optional]
 
1564
      screen_name:
 
1565
        Specfies the screen name of the user for whom to return the
 
1566
        user_timeline. Helpful for disambiguating when a valid screen
 
1567
        name is also a user ID. [optional]
 
1568
      since_id:
 
1569
        Returns only public statuses with an ID greater than (that is,
 
1570
        more recent than) the specified ID. [optional]
 
1571
      max_id:
 
1572
        Returns only statuses with an ID less than (that is, older
 
1573
        than) or equal to the specified ID. [optional]
 
1574
      count:
 
1575
        Specifies the number of statuses to retrieve. May not be
 
1576
        greater than 200.  [optional]
 
1577
      page:
 
1578
         Specifies the page of results to retrieve. Note: there are
 
1579
         pagination limits. [optional]
 
1580
 
 
1581
    Returns:
 
1582
      A sequence of Status instances, one for each message up to count
 
1583
    '''
 
1584
    parameters = {}
 
1585
 
 
1586
    if id:
 
1587
      url = '%s/statuses/user_timeline/%s.json' % (self.base_url, id)
 
1588
    elif user_id:
 
1589
      url = '%s/statuses/user_timeline.json?user_id=%d' % (self.base_url, user_id)
 
1590
    elif screen_name:
 
1591
      url = ('%s/statuses/user_timeline.json?screen_name=%s' % (self.base_url,
 
1592
             screen_name))
 
1593
    elif not self._username:
 
1594
      raise TwitterError("User must be specified if API is not authenticated.")
 
1595
    else:
 
1596
      url = '%s/statuses/user_timeline.json' % self.base_url
 
1597
 
 
1598
    if since_id:
 
1599
      try:
 
1600
        parameters['since_id'] = long(since_id)
 
1601
      except:
 
1602
        raise TwitterError("since_id must be an integer")
 
1603
 
 
1604
    if max_id:
 
1605
      try:
 
1606
        parameters['max_id'] = long(max_id)
 
1607
      except:
 
1608
        raise TwitterError("max_id must be an integer")
 
1609
 
 
1610
    if count:
 
1611
      try:
 
1612
        parameters['count'] = int(count)
 
1613
      except:
 
1614
        raise TwitterError("count must be an integer")
 
1615
 
 
1616
    if page:
 
1617
      try:
 
1618
        parameters['page'] = int(page)
 
1619
      except:
 
1620
        raise TwitterError("page must be an integer")
 
1621
 
 
1622
    json = self._FetchUrl(url, parameters=parameters)
 
1623
    data = simplejson.loads(json)
 
1624
    self._CheckForTwitterError(data)
 
1625
    return [Status.NewFromJsonDict(x) for x in data]
 
1626
 
 
1627
  def GetStatus(self, id):
 
1628
    '''Returns a single status message.
 
1629
 
 
1630
    The twitter.Api instance must be authenticated if the status message is private.
 
1631
 
 
1632
    Args:
 
1633
      id: The numerical ID of the status you're trying to retrieve.
 
1634
 
 
1635
    Returns:
 
1636
      A twitter.Status instance representing that status message
 
1637
    '''
 
1638
    try:
 
1639
      if id:
 
1640
        long(id)
 
1641
    except:
 
1642
      raise TwitterError("id must be an long integer")
 
1643
    url = '%s/statuses/show/%s.json' % (self.base_url, id)
 
1644
    json = self._FetchUrl(url)
 
1645
    data = simplejson.loads(json)
 
1646
    self._CheckForTwitterError(data)
 
1647
    return Status.NewFromJsonDict(data)
 
1648
 
 
1649
  def DestroyStatus(self, id):
 
1650
    '''Destroys the status specified by the required ID parameter.
 
1651
 
 
1652
    The twitter.Api instance must be authenticated and thee
 
1653
    authenticating user must be the author of the specified status.
 
1654
 
 
1655
    Args:
 
1656
      id: The numerical ID of the status you're trying to destroy.
 
1657
 
 
1658
    Returns:
 
1659
      A twitter.Status instance representing the destroyed status message
 
1660
    '''
 
1661
    try:
 
1662
      if id:
 
1663
        long(id)
 
1664
    except:
 
1665
      raise TwitterError("id must be an integer")
 
1666
    url = '%s/statuses/destroy/%s.json' % (self.base_url, id)
 
1667
    json = self._FetchUrl(url, post_data={})
 
1668
    data = simplejson.loads(json)
 
1669
    self._CheckForTwitterError(data)
 
1670
    return Status.NewFromJsonDict(data)
 
1671
 
 
1672
  def PostUpdate(self, status, in_reply_to_status_id=None):
 
1673
    '''Post a twitter status message from the authenticated user.
 
1674
 
 
1675
    The twitter.Api instance must be authenticated.
 
1676
 
 
1677
    Args:
 
1678
      status:
 
1679
        The message text to be posted.  Must be less than or equal to
 
1680
        140 characters.
 
1681
      in_reply_to_status_id:
 
1682
        The ID of an existing status that the status to be posted is
 
1683
        in reply to.  This implicitly sets the in_reply_to_user_id
 
1684
        attribute of the resulting status to the user ID of the
 
1685
        message being replied to.  Invalid/missing status IDs will be
 
1686
        ignored. [Optional]
 
1687
    Returns:
 
1688
      A twitter.Status instance representing the message posted.
 
1689
    '''
 
1690
    if not self._username:
 
1691
      raise TwitterError("The twitter.Api instance must be authenticated.")
 
1692
 
 
1693
    url = '%s/statuses/update.json' % self.base_url
 
1694
 
 
1695
    if len(status) > CHARACTER_LIMIT:
 
1696
      raise TwitterError("Text must be less than or equal to %d characters. "
 
1697
                         "Consider using PostUpdates." % CHARACTER_LIMIT)
 
1698
 
 
1699
    data = {'status': status}
 
1700
    if in_reply_to_status_id:
 
1701
      data['in_reply_to_status_id'] = in_reply_to_status_id
 
1702
    json = self._FetchUrl(url, post_data=data)
 
1703
    data = simplejson.loads(json)
 
1704
    self._CheckForTwitterError(data)
 
1705
    return Status.NewFromJsonDict(data)
 
1706
 
 
1707
  def PostUpdates(self, status, continuation=None, **kwargs):
 
1708
    '''Post one or more twitter status messages from the authenticated user.
 
1709
 
 
1710
    Unlike api.PostUpdate, this method will post multiple status updates
 
1711
    if the message is longer than 140 characters.
 
1712
 
 
1713
    The twitter.Api instance must be authenticated.
 
1714
 
 
1715
    Args:
 
1716
      status:
 
1717
        The message text to be posted.  May be longer than 140 characters.
 
1718
      continuation:
 
1719
        The character string, if any, to be appended to all but the
 
1720
        last message.  Note that Twitter strips trailing '...' strings
 
1721
        from messages.  Consider using the unicode \u2026 character
 
1722
        (horizontal ellipsis) instead. [Defaults to None]
 
1723
      **kwargs:
 
1724
        See api.PostUpdate for a list of accepted parameters.
 
1725
    Returns:
 
1726
      A of list twitter.Status instance representing the messages posted.
 
1727
    '''
 
1728
    results = list()
 
1729
    if continuation is None:
 
1730
      continuation = ''
 
1731
    line_length = CHARACTER_LIMIT - len(continuation)
 
1732
    lines = textwrap.wrap(status, line_length)
 
1733
    for line in lines[0:-1]:
 
1734
      results.append(self.PostUpdate(line + continuation, **kwargs))
 
1735
    results.append(self.PostUpdate(lines[-1], **kwargs))
 
1736
    return results
 
1737
 
 
1738
  def GetReplies(self, since=None, since_id=None, page=None): 
 
1739
    '''Get a sequence of status messages representing the 20 most recent
 
1740
    replies (status updates prefixed with @username) to the authenticating
 
1741
    user.
 
1742
 
 
1743
    Args:
 
1744
      page: 
 
1745
      since:
 
1746
        Narrows the returned results to just those statuses created
 
1747
        after the specified HTTP-formatted date. [optional]
 
1748
      since_id:
 
1749
        Returns only public statuses with an ID greater than (that is,
 
1750
        more recent than) the specified ID. [Optional]
 
1751
 
 
1752
    Returns:
 
1753
      A sequence of twitter.Status instances, one for each reply to the user.
 
1754
    '''
 
1755
    url = '%s/statuses/replies.json' % self.base_url
 
1756
    if not self._username:
 
1757
      raise TwitterError("The twitter.Api instance must be authenticated.")
 
1758
    parameters = {}
 
1759
    if since:
 
1760
      parameters['since'] = since
 
1761
    if since_id:
 
1762
      parameters['since_id'] = since_id
 
1763
    if page:
 
1764
      parameters['page'] = page
 
1765
    json = self._FetchUrl(url, parameters=parameters)
 
1766
    data = simplejson.loads(json)
 
1767
    self._CheckForTwitterError(data)
 
1768
    return [Status.NewFromJsonDict(x) for x in data]
 
1769
 
 
1770
  def GetFriends(self, user=None, page=None):
 
1771
    '''Fetch the sequence of twitter.User instances, one for each friend.
 
1772
 
 
1773
    Args:
 
1774
      user: the username or id of the user whose friends you are fetching.  If
 
1775
      not specified, defaults to the authenticated user. [optional]
 
1776
 
 
1777
    The twitter.Api instance must be authenticated.
 
1778
 
 
1779
    Returns:
 
1780
      A sequence of twitter.User instances, one for each friend
 
1781
    '''
 
1782
    if not user and not self._username:
 
1783
      raise TwitterError("twitter.Api instance must be authenticated")
 
1784
    if user:
 
1785
      url = '%s/statuses/friends/%s.json' % (self.base_url, user)
 
1786
    else:
 
1787
      url = '%s/statuses/friends.json' % self.base_url
 
1788
    parameters = {}
 
1789
    if page:
 
1790
      parameters['page'] = page
 
1791
    json = self._FetchUrl(url, parameters=parameters)
 
1792
    data = simplejson.loads(json)
 
1793
    self._CheckForTwitterError(data)
 
1794
    return [User.NewFromJsonDict(x) for x in data]
 
1795
 
 
1796
  def GetFriendIDs(self, user=None, page=None):
 
1797
      '''Returns a list of twitter user id's for every person
 
1798
      the specified user is following.
 
1799
 
 
1800
      Args:
 
1801
        user:
 
1802
          The id or screen_name of the user to retrieve the id list for
 
1803
          [optional]
 
1804
        page:
 
1805
          Specifies the page number of the results beginning at 1.
 
1806
          A single page contains 5000 ids. This is recommended for users
 
1807
          with large id lists. If not provided all id's are returned. 
 
1808
          (Please note that the result set isn't guaranteed to be 5000 
 
1809
          every time as suspended users will be filtered.)
 
1810
          [optional]
 
1811
 
 
1812
      Returns:
 
1813
        A list of integers, one for each user id.
 
1814
      '''
 
1815
      if not user and not self._username:
 
1816
          raise TwitterError("twitter.Api instance must be authenticated")
 
1817
      if user:
 
1818
          url = '%s/friends/ids/%s.json' % (self.base_url, user)
 
1819
      else:
 
1820
          url = '%s/friends/ids.json' % self.base_url
 
1821
      parameters = {}
 
1822
      if page:
 
1823
          parameters['page'] = page
 
1824
      json = self._FetchUrl(url, parameters=parameters)
 
1825
      data = simplejson.loads(json)
 
1826
      self._CheckForTwitterError(data)
 
1827
      return data
 
1828
 
 
1829
  def GetFollowers(self, page=None):
 
1830
    '''Fetch the sequence of twitter.User instances, one for each follower
 
1831
 
 
1832
    The twitter.Api instance must be authenticated.
 
1833
 
 
1834
    Returns:
 
1835
      A sequence of twitter.User instances, one for each follower
 
1836
    '''
 
1837
    if not self._username:
 
1838
      raise TwitterError("twitter.Api instance must be authenticated")
 
1839
    url = '%s/statuses/followers.json' % self.base_url
 
1840
    parameters = {}
 
1841
    if page:
 
1842
      parameters['page'] = page
 
1843
    json = self._FetchUrl(url, parameters=parameters)
 
1844
    data = simplejson.loads(json)
 
1845
    self._CheckForTwitterError(data)
 
1846
    return [User.NewFromJsonDict(x) for x in data]
 
1847
 
 
1848
  def GetFeatured(self):
 
1849
    '''Fetch the sequence of twitter.User instances featured on twitter.com
 
1850
 
 
1851
    The twitter.Api instance must be authenticated.
 
1852
 
 
1853
    Returns:
 
1854
      A sequence of twitter.User instances
 
1855
    '''
 
1856
    url = '%s/statuses/featured.json' % self.base_url
 
1857
    json = self._FetchUrl(url)
 
1858
    data = simplejson.loads(json)
 
1859
    self._CheckForTwitterError(data)
 
1860
    return [User.NewFromJsonDict(x) for x in data]
 
1861
 
 
1862
  def GetUser(self, user):
 
1863
    '''Returns a single user.
 
1864
 
 
1865
    The twitter.Api instance must be authenticated.
 
1866
 
 
1867
    Args:
 
1868
      user: The username or id of the user to retrieve.
 
1869
 
 
1870
    Returns:
 
1871
      A twitter.User instance representing that user
 
1872
    '''
 
1873
    url = '%s/users/show/%s.json' % (self.base_url, user)
 
1874
    json = self._FetchUrl(url)
 
1875
    data = simplejson.loads(json)
 
1876
    self._CheckForTwitterError(data)
 
1877
    return User.NewFromJsonDict(data)
 
1878
 
 
1879
  def GetDirectMessages(self, since=None, since_id=None, page=None):
 
1880
    '''Returns a list of the direct messages sent to the authenticating user.
 
1881
 
 
1882
    The twitter.Api instance must be authenticated.
 
1883
 
 
1884
    Args:
 
1885
      since:
 
1886
        Narrows the returned results to just those statuses created
 
1887
        after the specified HTTP-formatted date. [optional]
 
1888
      since_id:
 
1889
        Returns only public statuses with an ID greater than (that is,
 
1890
        more recent than) the specified ID. [Optional]
 
1891
 
 
1892
    Returns:
 
1893
      A sequence of twitter.DirectMessage instances
 
1894
    '''
 
1895
    url = '%s/direct_messages.json' % self.base_url
 
1896
    if not self._username:
 
1897
      raise TwitterError("The twitter.Api instance must be authenticated.")
 
1898
    parameters = {}
 
1899
    if since:
 
1900
      parameters['since'] = since
 
1901
    if since_id:
 
1902
      parameters['since_id'] = since_id
 
1903
    if page:
 
1904
      parameters['page'] = page 
 
1905
    json = self._FetchUrl(url, parameters=parameters)
 
1906
    data = simplejson.loads(json)
 
1907
    self._CheckForTwitterError(data)
 
1908
    return [DirectMessage.NewFromJsonDict(x) for x in data]
 
1909
 
 
1910
  def PostDirectMessage(self, user, text):
 
1911
    '''Post a twitter direct message from the authenticated user
 
1912
 
 
1913
    The twitter.Api instance must be authenticated.
 
1914
 
 
1915
    Args:
 
1916
      user: The ID or screen name of the recipient user.
 
1917
      text: The message text to be posted.  Must be less than 140 characters.
 
1918
 
 
1919
    Returns:
 
1920
      A twitter.DirectMessage instance representing the message posted
 
1921
    '''
 
1922
    if not self._username:
 
1923
      raise TwitterError("The twitter.Api instance must be authenticated.")
 
1924
    url = '%s/direct_messages/new.json' % self.base_url
 
1925
    data = {'text': text, 'user': user}
 
1926
    json = self._FetchUrl(url, post_data=data)
 
1927
    data = simplejson.loads(json)
 
1928
    self._CheckForTwitterError(data)
 
1929
    return DirectMessage.NewFromJsonDict(data)
 
1930
 
 
1931
  def DestroyDirectMessage(self, id):
 
1932
    '''Destroys the direct message specified in the required ID parameter.
 
1933
 
 
1934
    The twitter.Api instance must be authenticated, and the
 
1935
    authenticating user must be the recipient of the specified direct
 
1936
    message.
 
1937
 
 
1938
    Args:
 
1939
      id: The id of the direct message to be destroyed
 
1940
 
 
1941
    Returns:
 
1942
      A twitter.DirectMessage instance representing the message destroyed
 
1943
    '''
 
1944
    url = '%s/direct_messages/destroy/%s.json' % (self.base_url, id)
 
1945
    json = self._FetchUrl(url, post_data={})
 
1946
    data = simplejson.loads(json)
 
1947
    self._CheckForTwitterError(data)
 
1948
    return DirectMessage.NewFromJsonDict(data)
 
1949
 
 
1950
  def CreateFriendship(self, user):
 
1951
    '''Befriends the user specified in the user parameter as the authenticating user.
 
1952
 
 
1953
    The twitter.Api instance must be authenticated.
 
1954
 
 
1955
    Args:
 
1956
      The ID or screen name of the user to befriend.
 
1957
    Returns:
 
1958
      A twitter.User instance representing the befriended user.
 
1959
    '''
 
1960
    url = '%s/friendships/create/%s.json' % (self.base_url, user)
 
1961
    json = self._FetchUrl(url, post_data={})
 
1962
    data = simplejson.loads(json)
 
1963
    self._CheckForTwitterError(data)
 
1964
    return User.NewFromJsonDict(data)
 
1965
 
 
1966
  def DestroyFriendship(self, user):
 
1967
    '''Discontinues friendship with the user specified in the user parameter.
 
1968
 
 
1969
    The twitter.Api instance must be authenticated.
 
1970
 
 
1971
    Args:
 
1972
      The ID or screen name of the user  with whom to discontinue friendship.
 
1973
    Returns:
 
1974
      A twitter.User instance representing the discontinued friend.
 
1975
    '''
 
1976
    url = '%s/friendships/destroy/%s.json' % (self.base_url, user)
 
1977
    json = self._FetchUrl(url, post_data={})
 
1978
    data = simplejson.loads(json)
 
1979
    self._CheckForTwitterError(data)
 
1980
    return User.NewFromJsonDict(data)
 
1981
 
 
1982
  def CreateFavorite(self, status):
 
1983
    '''Favorites the status specified in the status parameter as the authenticating user.
 
1984
    Returns the favorite status when successful.
 
1985
 
 
1986
    The twitter.Api instance must be authenticated.
 
1987
 
 
1988
    Args:
 
1989
      The twitter.Status instance to mark as a favorite.
 
1990
    Returns:
 
1991
      A twitter.Status instance representing the newly-marked favorite.
 
1992
    '''
 
1993
    url = '%s/favorites/create/%s.json' % (self.base_url, status.id)
 
1994
    json = self._FetchUrl(url, post_data={})
 
1995
    data = simplejson.loads(json)
 
1996
    self._CheckForTwitterError(data)
 
1997
    return Status.NewFromJsonDict(data)
 
1998
 
 
1999
  def DestroyFavorite(self, status):
 
2000
    '''Un-favorites the status specified in the ID parameter as the authenticating user.
 
2001
    Returns the un-favorited status in the requested format when successful.
 
2002
 
 
2003
    The twitter.Api instance must be authenticated.
 
2004
 
 
2005
    Args:
 
2006
      The twitter.Status to unmark as a favorite.
 
2007
    Returns:
 
2008
      A twitter.Status instance representing the newly-unmarked favorite.
 
2009
    '''
 
2010
    url = '%s/favorites/destroy/%s.json' % (self.base_url, status.id)
 
2011
    json = self._FetchUrl(url, post_data={})
 
2012
    data = simplejson.loads(json)
 
2013
    self._CheckForTwitterError(data)
 
2014
    return Status.NewFromJsonDict(data)
 
2015
 
 
2016
  def GetFavorites(self,
 
2017
                   user=None,
 
2018
                   page=None):
 
2019
    '''Return a list of Status objects representing favorited tweets.
 
2020
    By default, returns the (up to) 20 most recent tweets for the
 
2021
    authenticated user.
 
2022
    
 
2023
    Args:
 
2024
      user:
 
2025
        The username or id of the user whose favorites you are fetching.
 
2026
        If not specified, defaults to the authenticated user. [optional]
 
2027
    
 
2028
      page:
 
2029
        Retrieves the 20 next most recent favorite statuses. [optional]
 
2030
    '''
 
2031
    parameters = {}
 
2032
 
 
2033
    if page:
 
2034
      parameters['page'] = page
 
2035
 
 
2036
    if user:
 
2037
      url = '%s/favorites/%s.json' % (self.base_url, user)
 
2038
    elif not user and not self._username:
 
2039
      raise TwitterError("User must be specified if API is not authenticated.")
 
2040
    else:
 
2041
      url = '%s/favorites.json' % self.base_url
 
2042
 
 
2043
    json = self._FetchUrl(url, parameters=parameters)
 
2044
    data = simplejson.loads(json)
 
2045
 
 
2046
    self._CheckForTwitterError(data)
 
2047
 
 
2048
    return [Status.NewFromJsonDict(x) for x in data]
 
2049
 
 
2050
  def GetMentions(self,
 
2051
                  since_id=None,
 
2052
                  max_id=None,
 
2053
                  page=None):
 
2054
    '''Returns the 20 most recent mentions (status containing @username)
 
2055
    for the authenticating user.
 
2056
    
 
2057
    Args:
 
2058
      since_id:
 
2059
        Returns only public statuses with an ID greater than
 
2060
        (that is, more recent than) the specified ID. [optional]
 
2061
    
 
2062
      max_id:
 
2063
        Returns only statuses with an ID less than
 
2064
        (that is, older than) the specified ID.  [optional]
 
2065
    
 
2066
      page:
 
2067
        Retrieves the 20 next most recent replies. [optional]
 
2068
    
 
2069
    Returns:
 
2070
      A sequence of twitter.Status instances, one for each mention of the user.
 
2071
      see: http://apiwiki.twitter.com/REST-API-Documentation#statuses/mentions
 
2072
    '''
 
2073
 
 
2074
    url = '%s/statuses/mentions.json' % self.base_url
 
2075
 
 
2076
    if not self._username:
 
2077
      raise TwitterError("The twitter.Api instance must be authenticated.")
 
2078
 
 
2079
    parameters = {}
 
2080
 
 
2081
    if since_id:
 
2082
      parameters['since_id'] = since_id
 
2083
    if max_id:
 
2084
      parameters['max_id'] = max_id
 
2085
    if page:
 
2086
      parameters['page'] = page
 
2087
 
 
2088
    json = self._FetchUrl(url, parameters=parameters)
 
2089
    data = simplejson.loads(json)
 
2090
 
 
2091
    self._CheckForTwitterError(data)
 
2092
 
 
2093
    return [Status.NewFromJsonDict(x) for x in data]
 
2094
 
 
2095
  def GetUserByEmail(self, email):
 
2096
    '''Returns a single user by email address.
 
2097
 
 
2098
    Args:
 
2099
      email: The email of the user to retrieve.
 
2100
    Returns:
 
2101
      A twitter.User instance representing that user
 
2102
    '''
 
2103
    url = '%s/users/show.json?email=%s' % (self.base_url, email)
 
2104
    json = self._FetchUrl(url)
 
2105
    data = simplejson.loads(json)
 
2106
    self._CheckForTwitterError(data)
 
2107
    return User.NewFromJsonDict(data)
 
2108
 
 
2109
  def VerifyCredentials(self):
 
2110
    '''Returns a twitter.User instance if the authenticating user is valid.
 
2111
 
 
2112
    Returns: 
 
2113
      A twitter.User instance representing that user if the
 
2114
      credentials are valid, None otherwise.
 
2115
    '''
 
2116
    if not self._username:
 
2117
      raise TwitterError("Api instance must first be given user credentials.")
 
2118
    url = '%s/account/verify_credentials.json' % self.base_url
 
2119
    try:
 
2120
      json = self._FetchUrl(url, no_cache=True)
 
2121
    except urllib2.HTTPError, http_error:
 
2122
      if http_error.code == httplib.UNAUTHORIZED:
 
2123
        return None
 
2124
      else:
 
2125
        raise http_error
 
2126
    data = simplejson.loads(json)
 
2127
    self._CheckForTwitterError(data)
 
2128
    return User.NewFromJsonDict(data)
 
2129
 
 
2130
  def SetCredentials(self, username, password):
 
2131
    '''Set the username and password for this instance
 
2132
 
 
2133
    Args:
 
2134
      username: The twitter username.
 
2135
      password: The twitter password.
 
2136
    '''
 
2137
    self._username = username
 
2138
    self._password = password
 
2139
 
 
2140
  def ClearCredentials(self):
 
2141
    '''Clear the username and password for this instance
 
2142
    '''
 
2143
    self._username = None
 
2144
    self._password = None
 
2145
 
 
2146
  def SetCache(self, cache):
 
2147
    '''Override the default cache.  Set to None to prevent caching.
 
2148
 
 
2149
    Args:
 
2150
      cache: an instance that supports the same API as the twitter._FileCache
 
2151
    '''
 
2152
    if cache == DEFAULT_CACHE:
 
2153
      self._cache = _FileCache()
 
2154
    else:
 
2155
      self._cache = cache
 
2156
 
 
2157
  def SetUrllib(self, urllib):
 
2158
    '''Override the default urllib implementation.
 
2159
 
 
2160
    Args:
 
2161
      urllib: an instance that supports the same API as the urllib2 module
 
2162
    '''
 
2163
    self._urllib = urllib
 
2164
 
 
2165
  def SetCacheTimeout(self, cache_timeout):
 
2166
    '''Override the default cache timeout.
 
2167
 
 
2168
    Args:
 
2169
      cache_timeout: time, in seconds, that responses should be reused.
 
2170
    '''
 
2171
    self._cache_timeout = cache_timeout
 
2172
 
 
2173
  def SetUserAgent(self, user_agent):
 
2174
    '''Override the default user agent
 
2175
 
 
2176
    Args:
 
2177
      user_agent: a string that should be send to the server as the User-agent
 
2178
    '''
 
2179
    self._request_headers['User-Agent'] = user_agent
 
2180
 
 
2181
  def SetXTwitterHeaders(self, client, url, version):
 
2182
    '''Set the X-Twitter HTTP headers that will be sent to the server.
 
2183
 
 
2184
    Args:
 
2185
      client:
 
2186
         The client name as a string.  Will be sent to the server as
 
2187
         the 'X-Twitter-Client' header.
 
2188
      url:
 
2189
         The URL of the meta.xml as a string.  Will be sent to the server
 
2190
         as the 'X-Twitter-Client-URL' header.
 
2191
      version:
 
2192
         The client version as a string.  Will be sent to the server
 
2193
         as the 'X-Twitter-Client-Version' header.
 
2194
    '''
 
2195
    self._request_headers['X-Twitter-Client'] = client
 
2196
    self._request_headers['X-Twitter-Client-URL'] = url
 
2197
    self._request_headers['X-Twitter-Client-Version'] = version
 
2198
 
 
2199
  def SetSource(self, source):
 
2200
    '''Suggest the "from source" value to be displayed on the Twitter web site.
 
2201
 
 
2202
    The value of the 'source' parameter must be first recognized by
 
2203
    the Twitter server.  New source values are authorized on a case by
 
2204
    case basis by the Twitter development team.
 
2205
 
 
2206
    Args:
 
2207
      source:
 
2208
        The source name as a string.  Will be sent to the server as
 
2209
        the 'source' parameter.
 
2210
    '''
 
2211
    self._default_params['source'] = source
 
2212
 
 
2213
  def GetRateLimitStatus(self):
 
2214
    '''Fetch the rate limit status for the currently authorized user.
 
2215
    
 
2216
    Returns:
 
2217
      A dictionary containing the time the limit will reset (reset_time),
 
2218
      the number of remaining hits allowed before the reset (remaining_hits),
 
2219
      the number of hits allowed in a 60-minute period (hourly_limit), and the
 
2220
      time of the reset in seconds since The Epoch (reset_time_in_seconds).
 
2221
    '''
 
2222
    url  = '%s/account/rate_limit_status.json' % self.base_url
 
2223
    json = self._FetchUrl(url, no_cache=True)
 
2224
    data = simplejson.loads(json)
 
2225
 
 
2226
    self._CheckForTwitterError(data)
 
2227
 
 
2228
    return data
 
2229
 
 
2230
  def MaximumHitFrequency(self):
 
2231
    '''Determines the minimum number of seconds that a program must wait before
 
2232
    hitting the server again without exceeding the rate_limit imposed for the
 
2233
    currently authenticated user.
 
2234
    
 
2235
    Returns:
 
2236
      The minimum second interval that a program must use so as to not exceed
 
2237
      the rate_limit imposed for the user.
 
2238
    '''
 
2239
    rate_status = self.GetRateLimitStatus()
 
2240
    reset_time  = rate_status.get('reset_time', None)
 
2241
    limit       = rate_status.get('remaining_hits', None)
 
2242
 
 
2243
    if reset_time and limit:
 
2244
      # put the reset time into a datetime object
 
2245
      reset = datetime.datetime(*rfc822.parsedate(reset_time)[:7])
 
2246
 
 
2247
      # find the difference in time between now and the reset time + 1 hour
 
2248
      delta = reset + datetime.timedelta(hours=1) - datetime.datetime.utcnow()
 
2249
 
 
2250
      # determine the minimum number of seconds allowed as a regular interval
 
2251
      max_frequency = int(delta.seconds / limit)
 
2252
 
 
2253
      # return the number of seconds
 
2254
      return max_frequency
 
2255
 
 
2256
    return 0
 
2257
 
 
2258
  def _BuildUrl(self, url, path_elements=None, extra_params=None):
 
2259
    # Break url into consituent parts
 
2260
    (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url)
 
2261
 
 
2262
    # Add any additional path elements to the path
 
2263
    if path_elements:
 
2264
      # Filter out the path elements that have a value of None
 
2265
      p = [i for i in path_elements if i]
 
2266
      if not path.endswith('/'):
 
2267
        path += '/'
 
2268
      path += '/'.join(p)
 
2269
 
 
2270
    # Add any additional query parameters to the query string
 
2271
    if extra_params and len(extra_params) > 0:
 
2272
      extra_query = self._EncodeParameters(extra_params)
 
2273
      # Add it to the existing query
 
2274
      if query:
 
2275
        query += '&' + extra_query
 
2276
      else:
 
2277
        query = extra_query
 
2278
 
 
2279
    # Return the rebuilt URL
 
2280
    return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
 
2281
 
 
2282
  def _InitializeRequestHeaders(self, request_headers):
 
2283
    if request_headers:
 
2284
      self._request_headers = request_headers
 
2285
    else:
 
2286
      self._request_headers = {}
 
2287
 
 
2288
  def _InitializeUserAgent(self):
 
2289
    user_agent = 'Python-urllib/%s (python-twitter/%s)' % \
 
2290
                 (self._urllib.__version__, __version__)
 
2291
    self.SetUserAgent(user_agent)
 
2292
 
 
2293
  def _InitializeDefaultParameters(self):
 
2294
    self._default_params = {}
 
2295
 
 
2296
  def _AddAuthorizationHeader(self, username, password):
 
2297
    if username and password:
 
2298
      basic_auth = base64.encodestring('%s:%s' % (username, password))[:-1]
 
2299
      self._request_headers['Authorization'] = 'Basic %s' % basic_auth
 
2300
 
 
2301
  def _RemoveAuthorizationHeader(self):
 
2302
    if self._request_headers and 'Authorization' in self._request_headers:
 
2303
      del self._request_headers['Authorization']
 
2304
 
 
2305
  def _DecompressGzippedResponse(self, response):
 
2306
    raw_data = response.read()
 
2307
    if response.headers.get('content-encoding', None) == 'gzip':
 
2308
      url_data = gzip.GzipFile(fileobj=StringIO.StringIO(raw_data)).read()
 
2309
    else:
 
2310
      url_data = raw_data
 
2311
    return url_data
 
2312
 
 
2313
  def _GetOpener(self, url, username=None, password=None):
 
2314
    if username and password:
 
2315
      self._AddAuthorizationHeader(username, password)
 
2316
      handler = self._urllib.HTTPBasicAuthHandler()
 
2317
      (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url)
 
2318
      handler.add_password(Api._API_REALM, netloc, username, password)
 
2319
      opener = self._urllib.build_opener(handler)
 
2320
    else:
 
2321
      opener = self._urllib.build_opener()
 
2322
    opener.addheaders = self._request_headers.items()
 
2323
    return opener
 
2324
 
 
2325
  def _Encode(self, s):
 
2326
    if self._input_encoding:
 
2327
      return unicode(s, self._input_encoding).encode('utf-8')
 
2328
    else:
 
2329
      return unicode(s).encode('utf-8')
 
2330
 
 
2331
  def _EncodeParameters(self, parameters):
 
2332
    '''Return a string in key=value&key=value form
 
2333
 
 
2334
    Values of None are not included in the output string.
 
2335
 
 
2336
    Args:
 
2337
      parameters:
 
2338
        A dict of (key, value) tuples, where value is encoded as
 
2339
        specified by self._encoding
 
2340
    Returns:
 
2341
      A URL-encoded string in "key=value&key=value" form
 
2342
    '''
 
2343
    if parameters is None:
 
2344
      return None
 
2345
    else:
 
2346
      return urllib.urlencode(dict([(k, self._Encode(v)) for k, v in parameters.items() if v is not None]))
 
2347
 
 
2348
  def _EncodePostData(self, post_data):
 
2349
    '''Return a string in key=value&key=value form
 
2350
 
 
2351
    Values are assumed to be encoded in the format specified by self._encoding,
 
2352
    and are subsequently URL encoded.
 
2353
 
 
2354
    Args:
 
2355
      post_data:
 
2356
        A dict of (key, value) tuples, where value is encoded as
 
2357
        specified by self._encoding
 
2358
    Returns:
 
2359
      A URL-encoded string in "key=value&key=value" form
 
2360
    '''
 
2361
    if post_data is None:
 
2362
      return None
 
2363
    else:
 
2364
      return urllib.urlencode(dict([(k, self._Encode(v)) for k, v in post_data.items()]))
 
2365
 
 
2366
  def _CheckForTwitterError(self, data):
 
2367
    """Raises a TwitterError if twitter returns an error message.
 
2368
 
 
2369
    Args:
 
2370
      data: A python dict created from the Twitter json response
 
2371
    Raises:
 
2372
      TwitterError wrapping the twitter error message if one exists.
 
2373
    """
 
2374
    # Twitter errors are relatively unlikely, so it is faster
 
2375
    # to check first, rather than try and catch the exception
 
2376
    if 'error' in data:
 
2377
      raise TwitterError(data['error'])
 
2378
 
 
2379
  def _FetchUrl(self,
 
2380
                url,
 
2381
                post_data=None,
 
2382
                parameters=None,
 
2383
                no_cache=None,
 
2384
                use_gzip_compression=None):
 
2385
    '''Fetch a URL, optionally caching for a specified time.
 
2386
 
 
2387
    Args:
 
2388
      url:
 
2389
        The URL to retrieve
 
2390
      post_data:
 
2391
        A dict of (str, unicode) key/value pairs.
 
2392
        If set, POST will be used.
 
2393
      parameters:
 
2394
        A dict whose key/value pairs should encoded and added
 
2395
        to the query string. [optional]
 
2396
      no_cache:
 
2397
        If true, overrides the cache on the current request
 
2398
      use_gzip_compression:
 
2399
        If True, tells the server to gzip-compress the response.
 
2400
        It does not apply to POST requests.
 
2401
        Defaults to None, which will get the value to use from
 
2402
        the instance variable self._use_gzip [optional]
 
2403
 
 
2404
    Returns:
 
2405
      A string containing the body of the response.
 
2406
    '''
 
2407
    # Build the extra parameters dict
 
2408
    extra_params = {}
 
2409
    if self._default_params:
 
2410
      extra_params.update(self._default_params)
 
2411
    if parameters:
 
2412
      extra_params.update(parameters)
 
2413
 
 
2414
    # Add key/value parameters to the query string of the url
 
2415
    url = self._BuildUrl(url, extra_params=extra_params)
 
2416
 
 
2417
    # Get a url opener that can handle basic auth
 
2418
    opener = self._GetOpener(url, username=self._username, password=self._password)
 
2419
 
 
2420
    if use_gzip_compression is None:
 
2421
      use_gzip = self._use_gzip
 
2422
    else:
 
2423
      use_gzip = use_gzip_compression
 
2424
      
 
2425
    # Set up compression
 
2426
    if use_gzip and not post_data:
 
2427
      opener.addheaders.append(('Accept-Encoding', 'gzip'))
 
2428
 
 
2429
    encoded_post_data = self._EncodePostData(post_data)
 
2430
 
 
2431
    # Open and return the URL immediately if we're not going to cache
 
2432
    if encoded_post_data or no_cache or not self._cache or not self._cache_timeout:
 
2433
      response = opener.open(url, encoded_post_data)
 
2434
      url_data = self._DecompressGzippedResponse(response)
 
2435
      opener.close()
 
2436
    else:
 
2437
      # Unique keys are a combination of the url and the username
 
2438
      if self._username:
 
2439
        key = self._username + ':' + url
 
2440
      else:
 
2441
        key = url
 
2442
 
 
2443
      # See if it has been cached before
 
2444
      last_cached = self._cache.GetCachedTime(key)
 
2445
 
 
2446
      # If the cached version is outdated then fetch another and store it
 
2447
      if not last_cached or time.time() >= last_cached + self._cache_timeout:
 
2448
        response = opener.open(url, encoded_post_data)
 
2449
        url_data = self._DecompressGzippedResponse(response)
 
2450
        opener.close()
 
2451
        self._cache.Set(key, url_data)
 
2452
      else:
 
2453
        url_data = self._cache.Get(key)
 
2454
 
 
2455
    # Always return the latest version
 
2456
    return url_data
 
2457
 
 
2458
 
 
2459
class _FileCacheError(Exception):
 
2460
  '''Base exception class for FileCache related errors'''
 
2461
 
 
2462
class _FileCache(object):
 
2463
 
 
2464
  DEPTH = 3
 
2465
 
 
2466
  def __init__(self,root_directory=None):
 
2467
    self._InitializeRootDirectory(root_directory)
 
2468
 
 
2469
  def Get(self,key):
 
2470
    path = self._GetPath(key)
 
2471
    if os.path.exists(path):
 
2472
      return open(path).read()
 
2473
    else:
 
2474
      return None
 
2475
 
 
2476
  def Set(self,key,data):
 
2477
    path = self._GetPath(key)
 
2478
    directory = os.path.dirname(path)
 
2479
    if not os.path.exists(directory):
 
2480
      os.makedirs(directory)
 
2481
    if not os.path.isdir(directory):
 
2482
      raise _FileCacheError('%s exists but is not a directory' % directory)
 
2483
    temp_fd, temp_path = tempfile.mkstemp()
 
2484
    temp_fp = os.fdopen(temp_fd, 'w')
 
2485
    temp_fp.write(data)
 
2486
    temp_fp.close()
 
2487
    if not path.startswith(self._root_directory):
 
2488
      raise _FileCacheError('%s does not appear to live under %s' %
 
2489
                            (path, self._root_directory))
 
2490
    if os.path.exists(path):
 
2491
      os.remove(path)
 
2492
    os.rename(temp_path, path)
 
2493
 
 
2494
  def Remove(self,key):
 
2495
    path = self._GetPath(key)
 
2496
    if not path.startswith(self._root_directory):
 
2497
      raise _FileCacheError('%s does not appear to live under %s' %
 
2498
                            (path, self._root_directory ))
 
2499
    if os.path.exists(path):
 
2500
      os.remove(path)
 
2501
 
 
2502
  def GetCachedTime(self,key):
 
2503
    path = self._GetPath(key)
 
2504
    if os.path.exists(path):
 
2505
      return os.path.getmtime(path)
 
2506
    else:
 
2507
      return None
 
2508
 
 
2509
  def _GetUsername(self):
 
2510
    '''Attempt to find the username in a cross-platform fashion.'''
 
2511
    try:
 
2512
      return os.getenv('USER') or \
 
2513
             os.getenv('LOGNAME') or \
 
2514
             os.getenv('USERNAME') or \
 
2515
             os.getlogin() or \
 
2516
             'nobody'
 
2517
    except (IOError, OSError), e:
 
2518
      return 'nobody'
 
2519
 
 
2520
  def _GetTmpCachePath(self):
 
2521
    username = self._GetUsername()
 
2522
    cache_directory = 'python.cache_' + username
 
2523
    return os.path.join(tempfile.gettempdir(), cache_directory)
 
2524
 
 
2525
  def _InitializeRootDirectory(self, root_directory):
 
2526
    if not root_directory:
 
2527
      root_directory = self._GetTmpCachePath()
 
2528
    root_directory = os.path.abspath(root_directory)
 
2529
    if not os.path.exists(root_directory):
 
2530
      os.mkdir(root_directory)
 
2531
    if not os.path.isdir(root_directory):
 
2532
      raise _FileCacheError('%s exists but is not a directory' %
 
2533
                            root_directory)
 
2534
    self._root_directory = root_directory
 
2535
 
 
2536
  def _GetPath(self,key):
 
2537
    try:
 
2538
        hashed_key = md5(key).hexdigest()
 
2539
    except TypeError:
 
2540
        hashed_key = md5.new(key).hexdigest()
 
2541
        
 
2542
    return os.path.join(self._root_directory,
 
2543
                        self._GetPrefix(hashed_key),
 
2544
                        hashed_key)
 
2545
 
 
2546
  def _GetPrefix(self,hashed_key):
 
2547
    return os.path.sep.join(hashed_key[0:_FileCache.DEPTH])