3 # Copyright 2007 Google Inc. All Rights Reserved.
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 '''A library that provides a python interface to the Twitter API'''
19 __author__
= 'dewitt@google.com'
20 __version__
= '0.6-devel'
37 from hashlib
import md5
45 class TwitterError(Exception):
46 '''Base class for Twitter errors'''
50 '''Returns the first argument used to construct this error.'''
55 '''A class representing the Status structure used by the twitter API.
57 The Status structure exposes the following properties:
60 status.created_at_in_seconds # read only
62 status.in_reply_to_screen_name
63 status.in_reply_to_user_id
64 status.in_reply_to_status_id
69 status.relative_created_at # read only
78 in_reply_to_screen_name
=None,
79 in_reply_to_user_id
=None,
80 in_reply_to_status_id
=None,
84 '''An object to hold a Twitter status message.
86 This class is normally instantiated by the twitter.Api class and
87 returned in a sequence.
89 Note: Dates are posted in the form "Sat Jan 27 04:17:38 +0000 2007"
92 created_at: The time this status message was posted
93 favorited: Whether this is a favorite of the authenticated user
94 id: The unique id of this status message
95 text: The text of this status message
97 A human readable string representing the posting time
99 A twitter.User instance representing the person posting the message
101 The current time, if the client choses to set it. Defaults to the
104 self
.created_at
= created_at
105 self
.favorited
= favorited
110 self
.in_reply_to_screen_name
= in_reply_to_screen_name
111 self
.in_reply_to_user_id
= in_reply_to_user_id
112 self
.in_reply_to_status_id
= in_reply_to_status_id
113 self
.truncated
= truncated
116 def GetCreatedAt(self
):
117 '''Get the time this status message was posted.
120 The time this status message was posted
122 return self
._created
_at
124 def SetCreatedAt(self
, created_at
):
125 '''Set the time this status message was posted.
128 created_at: The time this status message was created
130 self
._created
_at
= created_at
132 created_at
= property(GetCreatedAt
, SetCreatedAt
,
133 doc
='The time this status message was posted.')
135 def GetCreatedAtInSeconds(self
):
136 '''Get the time this status message was posted, in seconds since the epoch.
139 The time this status message was posted, in seconds since the epoch.
141 return calendar
.timegm(rfc822
.parsedate(self
.created_at
))
143 created_at_in_seconds
= property(GetCreatedAtInSeconds
,
144 doc
="The time this status message was "
145 "posted, in seconds since the epoch")
147 def GetFavorited(self
):
148 '''Get the favorited setting of this status message.
151 True if this status message is favorited; False otherwise
153 return self
._favorited
155 def SetFavorited(self
, favorited
):
156 '''Set the favorited state of this status message.
159 favorited: boolean True/False favorited state of this status message
161 self
._favorited
= favorited
163 favorited
= property(GetFavorited
, SetFavorited
,
164 doc
='The favorited state of this status message.')
167 '''Get the unique id of this status message.
170 The unique id of this status message
175 '''Set the unique id of this status message.
178 id: The unique id of this status message
182 id = property(GetId
, SetId
,
183 doc
='The unique id of this status message.')
185 def GetInReplyToScreenName(self
):
186 return self
._in
_reply
_to
_screen
_name
188 def SetInReplyToScreenName(self
, in_reply_to_screen_name
):
189 self
._in
_reply
_to
_screen
_name
= in_reply_to_screen_name
191 in_reply_to_screen_name
= property(GetInReplyToScreenName
, SetInReplyToScreenName
,
194 def GetInReplyToUserId(self
):
195 return self
._in
_reply
_to
_user
_id
197 def SetInReplyToUserId(self
, in_reply_to_user_id
):
198 self
._in
_reply
_to
_user
_id
= in_reply_to_user_id
200 in_reply_to_user_id
= property(GetInReplyToUserId
, SetInReplyToUserId
,
203 def GetInReplyToStatusId(self
):
204 return self
._in
_reply
_to
_status
_id
206 def SetInReplyToStatusId(self
, in_reply_to_status_id
):
207 self
._in
_reply
_to
_status
_id
= in_reply_to_status_id
209 in_reply_to_status_id
= property(GetInReplyToStatusId
, SetInReplyToStatusId
,
212 def GetTruncated(self
):
213 return self
._truncated
215 def SetTruncated(self
, truncated
):
216 self
._truncated
= truncated
218 truncated
= property(GetTruncated
, SetTruncated
,
224 def SetSource(self
, source
):
225 self
._source
= source
227 source
= property(GetSource
, SetSource
,
231 '''Get the text of this status message.
234 The text of this status message.
238 def SetText(self
, text
):
239 '''Set the text of this status message.
242 text: The text of this status message
246 text
= property(GetText
, SetText
,
247 doc
='The text of this status message')
249 def GetRelativeCreatedAt(self
):
250 '''Get a human redable string representing the posting time
253 A human readable string representing the posting time
256 delta
= long(self
.now
) - long(self
.created_at_in_seconds
)
258 if delta
< (1 * fudge
):
259 return 'about a second ago'
260 elif delta
< (60 * (1/fudge
)):
261 return 'about %d seconds ago' % (delta
)
262 elif delta
< (60 * fudge
):
263 return 'about a minute ago'
264 elif delta
< (60 * 60 * (1/fudge
)):
265 return 'about %d minutes ago' % (delta
/ 60)
266 elif delta
< (60 * 60 * fudge
):
267 return 'about an hour ago'
268 elif delta
< (60 * 60 * 24 * (1/fudge
)):
269 return 'about %d hours ago' % (delta
/ (60 * 60))
270 elif delta
< (60 * 60 * 24 * fudge
):
271 return 'about a day ago'
273 return 'about %d days ago' % (delta
/ (60 * 60 * 24))
275 relative_created_at
= property(GetRelativeCreatedAt
,
276 doc
='Get a human readable string representing'
280 '''Get a twitter.User reprenting the entity posting this status message.
283 A twitter.User reprenting the entity posting this status message
287 def SetUser(self
, user
):
288 '''Set a twitter.User reprenting the entity posting this status message.
291 user: A twitter.User reprenting the entity posting this status message
295 user
= property(GetUser
, SetUser
,
296 doc
='A twitter.User reprenting the entity posting this '
300 '''Get the wallclock time for this status message.
302 Used to calculate relative_created_at. Defaults to the time
303 the object was instantiated.
306 Whatever the status instance believes the current time to be,
307 in seconds since the epoch.
309 if self
._now
is None:
310 self
._now
= time
.time()
313 def SetNow(self
, now
):
314 '''Set the wallclock time for this status message.
316 Used to calculate relative_created_at. Defaults to the time
317 the object was instantiated.
320 now: The wallclock time for this instance.
324 now
= property(GetNow
, SetNow
,
325 doc
='The wallclock time for this status instance.')
328 def __ne__(self
, other
):
329 return not self
.__eq
__(other
)
331 def __eq__(self
, other
):
334 self
.created_at
== other
.created_at
and \
335 self
.id == other
.id and \
336 self
.text
== other
.text
and \
337 self
.user
== other
.user
and \
338 self
.in_reply_to_screen_name
== other
.in_reply_to_screen_name
and \
339 self
.in_reply_to_user_id
== other
.in_reply_to_user_id
and \
340 self
.in_reply_to_status_id
== other
.in_reply_to_status_id
and \
341 self
.truncated
== other
.truncated
and \
342 self
.favorited
== other
.favorited
and \
343 self
.source
== other
.source
344 except AttributeError:
348 '''A string representation of this twitter.Status instance.
350 The return value is the same as the JSON string representation.
353 A string representation of this twitter.Status instance.
355 return self
.AsJsonString()
357 def AsJsonString(self
):
358 '''A JSON string representation of this twitter.Status instance.
361 A JSON string representation of this twitter.Status instance
363 return simplejson
.dumps(self
.AsDict(), sort_keys
=True)
366 '''A dict representation of this twitter.Status instance.
368 The return value uses the same key names as the JSON representation.
371 A dict representing this twitter.Status instance
375 data
['created_at'] = self
.created_at
377 data
['favorited'] = self
.favorited
381 data
['text'] = self
.text
383 data
['user'] = self
.user
.AsDict()
384 if self
.in_reply_to_screen_name
:
385 data
['in_reply_to_screen_name'] = self
.in_reply_to_screen_name
386 if self
.in_reply_to_user_id
:
387 data
['in_reply_to_user_id'] = self
.in_reply_to_user_id
388 if self
.in_reply_to_status_id
:
389 data
['in_reply_to_status_id'] = self
.in_reply_to_status_id
390 if self
.truncated
is not None:
391 data
['truncated'] = self
.truncated
392 if self
.favorited
is not None:
393 data
['favorited'] = self
.favorited
395 data
['source'] = self
.source
399 def NewFromJsonDict(data
):
400 '''Create a new instance based on a JSON dict.
403 data: A JSON dict, as converted from the JSON in the twitter API
405 A twitter.Status instance
408 user
= User
.NewFromJsonDict(data
['user'])
411 return Status(created_at
=data
.get('created_at', None),
412 favorited
=data
.get('favorited', None),
413 id=data
.get('id', None),
414 text
=data
.get('text', None),
415 in_reply_to_screen_name
=data
.get('in_reply_to_screen_name', None),
416 in_reply_to_user_id
=data
.get('in_reply_to_user_id', None),
417 in_reply_to_status_id
=data
.get('in_reply_to_status_id', None),
418 truncated
=data
.get('truncated', None),
419 source
=data
.get('source', None),
424 '''A class representing the User structure used by the twitter API.
426 The User structure exposes the following properties:
433 user.profile_image_url
434 user.profile_background_tile
435 user.profile_background_image_url
436 user.profile_sidebar_fill_color
437 user.profile_background_color
438 user.profile_link_color
439 user.profile_text_color
448 user.favourites_count
456 profile_image_url
=None,
457 profile_background_tile
=None,
458 profile_background_image_url
=None,
459 profile_sidebar_fill_color
=None,
460 profile_background_color
=None,
461 profile_link_color
=None,
462 profile_text_color
=None,
466 followers_count
=None,
469 favourites_count
=None,
474 self
.screen_name
= screen_name
475 self
.location
= location
476 self
.description
= description
477 self
.profile_image_url
= profile_image_url
478 self
.profile_background_tile
= profile_background_tile
479 self
.profile_background_image_url
= profile_background_image_url
480 self
.profile_sidebar_fill_color
= profile_sidebar_fill_color
481 self
.profile_background_color
= profile_background_color
482 self
.profile_link_color
= profile_link_color
483 self
.profile_text_color
= profile_text_color
484 self
.protected
= protected
485 self
.utc_offset
= utc_offset
486 self
.time_zone
= time_zone
487 self
.followers_count
= followers_count
488 self
.friends_count
= friends_count
489 self
.statuses_count
= statuses_count
490 self
.favourites_count
= favourites_count
496 '''Get the unique id of this user.
499 The unique id of this user
504 '''Set the unique id of this user.
507 id: The unique id of this user.
511 id = property(GetId
, SetId
,
512 doc
='The unique id of this user.')
515 '''Get the real name of this user.
518 The real name of this user
522 def SetName(self
, name
):
523 '''Set the real name of this user.
526 name: The real name of this user
530 name
= property(GetName
, SetName
,
531 doc
='The real name of this user.')
533 def GetScreenName(self
):
534 '''Get the short username of this user.
537 The short username of this user
539 return self
._screen
_name
541 def SetScreenName(self
, screen_name
):
542 '''Set the short username of this user.
545 screen_name: the short username of this user
547 self
._screen
_name
= screen_name
549 screen_name
= property(GetScreenName
, SetScreenName
,
550 doc
='The short username of this user.')
552 def GetLocation(self
):
553 '''Get the geographic location of this user.
556 The geographic location of this user
558 return self
._location
560 def SetLocation(self
, location
):
561 '''Set the geographic location of this user.
564 location: The geographic location of this user
566 self
._location
= location
568 location
= property(GetLocation
, SetLocation
,
569 doc
='The geographic location of this user.')
571 def GetDescription(self
):
572 '''Get the short text description of this user.
575 The short text description of this user
577 return self
._description
579 def SetDescription(self
, description
):
580 '''Set the short text description of this user.
583 description: The short text description of this user
585 self
._description
= description
587 description
= property(GetDescription
, SetDescription
,
588 doc
='The short text description of this user.')
591 '''Get the homepage url of this user.
594 The homepage url of this user
598 def SetUrl(self
, url
):
599 '''Set the homepage url of this user.
602 url: The homepage url of this user
606 url
= property(GetUrl
, SetUrl
,
607 doc
='The homepage url of this user.')
609 def GetProfileImageUrl(self
):
610 '''Get the url of the thumbnail of this user.
613 The url of the thumbnail of this user
615 return self
._profile
_image
_url
617 def SetProfileImageUrl(self
, profile_image_url
):
618 '''Set the url of the thumbnail of this user.
621 profile_image_url: The url of the thumbnail of this user
623 self
._profile
_image
_url
= profile_image_url
625 profile_image_url
= property(GetProfileImageUrl
, SetProfileImageUrl
,
626 doc
='The url of the thumbnail of this user.')
628 def GetProfileBackgroundTile(self
):
629 '''Boolean for whether to tile the profile background image.
632 True if the background is to be tiled, False if not, None if unset.
634 return self
._profile
_background
_tile
636 def SetProfileBackgroundTile(self
, profile_background_tile
):
637 '''Set the boolean flag for whether to tile the profile background image.
640 profile_background_tile: Boolean flag for whether to tile or not.
642 self
._profile
_background
_tile
= profile_background_tile
644 profile_background_tile
= property(GetProfileBackgroundTile
, SetProfileBackgroundTile
,
645 doc
='Boolean for whether to tile the background image.')
647 def GetProfileBackgroundImageUrl(self
):
648 return self
._profile
_background
_image
_url
650 def SetProfileBackgroundImageUrl(self
, profile_background_image_url
):
651 self
._profile
_background
_image
_url
= profile_background_image_url
653 profile_background_image_url
= property(GetProfileBackgroundImageUrl
, SetProfileBackgroundImageUrl
,
654 doc
='The url of the profile background of this user.')
656 def GetProfileSidebarFillColor(self
):
657 return self
._profile
_sidebar
_fill
_color
659 def SetProfileSidebarFillColor(self
, profile_sidebar_fill_color
):
660 self
._profile
_sidebar
_fill
_color
= profile_sidebar_fill_color
662 profile_sidebar_fill_color
= property(GetProfileSidebarFillColor
, SetProfileSidebarFillColor
)
664 def GetProfileBackgroundColor(self
):
665 return self
._profile
_background
_color
667 def SetProfileBackgroundColor(self
, profile_background_color
):
668 self
._profile
_background
_color
= profile_background_color
670 profile_background_color
= property(GetProfileBackgroundColor
, SetProfileBackgroundColor
)
672 def GetProfileLinkColor(self
):
673 return self
._profile
_link
_color
675 def SetProfileLinkColor(self
, profile_link_color
):
676 self
._profile
_link
_color
= profile_link_color
678 profile_link_color
= property(GetProfileLinkColor
, SetProfileLinkColor
)
680 def GetProfileTextColor(self
):
681 return self
._profile
_text
_color
683 def SetProfileTextColor(self
, profile_text_color
):
684 self
._profile
_text
_color
= profile_text_color
686 profile_text_color
= property(GetProfileTextColor
, SetProfileTextColor
)
688 def GetProtected(self
):
689 return self
._protected
691 def SetProtected(self
, protected
):
692 self
._protected
= protected
694 protected
= property(GetProtected
, SetProtected
)
696 def GetUtcOffset(self
):
697 return self
._utc
_offset
699 def SetUtcOffset(self
, utc_offset
):
700 self
._utc
_offset
= utc_offset
702 utc_offset
= property(GetUtcOffset
, SetUtcOffset
)
704 def GetTimeZone(self
):
705 '''Returns the current time zone string for the user.
708 The descriptive time zone string for the user.
710 return self
._time
_zone
712 def SetTimeZone(self
, time_zone
):
713 '''Sets the user's time zone string.
716 time_zone: The descriptive time zone to assign for the user.
718 self
._time
_zone
= time_zone
720 time_zone
= property(GetTimeZone
, SetTimeZone
)
723 '''Get the latest twitter.Status of this user.
726 The latest twitter.Status of this user
730 def SetStatus(self
, status
):
731 '''Set the latest twitter.Status of this user.
734 status: The latest twitter.Status of this user
736 self
._status
= status
738 status
= property(GetStatus
, SetStatus
,
739 doc
='The latest twitter.Status of this user.')
741 def GetFriendsCount(self
):
742 '''Get the friend count for this user.
745 The number of users this user has befriended.
747 return self
._friends
_count
749 def SetFriendsCount(self
, count
):
750 '''Set the friend count for this user.
753 count: The number of users this user has befriended.
755 self
._friends
_count
= count
757 friends_count
= property(GetFriendsCount
, SetFriendsCount
,
758 doc
='The number of friends for this user.')
760 def GetFollowersCount(self
):
761 '''Get the follower count for this user.
764 The number of users following this user.
766 return self
._followers
_count
768 def SetFollowersCount(self
, count
):
769 '''Set the follower count for this user.
772 count: The number of users following this user.
774 self
._followers
_count
= count
776 followers_count
= property(GetFollowersCount
, SetFollowersCount
,
777 doc
='The number of users following this user.')
779 def GetStatusesCount(self
):
780 '''Get the number of status updates for this user.
783 The number of status updates for this user.
785 return self
._statuses
_count
787 def SetStatusesCount(self
, count
):
788 '''Set the status update count for this user.
791 count: The number of updates for this user.
793 self
._statuses
_count
= count
795 statuses_count
= property(GetStatusesCount
, SetStatusesCount
,
796 doc
='The number of updates for this user.')
798 def GetFavouritesCount(self
):
799 '''Get the number of favourites for this user.
802 The number of favourites for this user.
804 return self
._favourites
_count
806 def SetFavouritesCount(self
, count
):
807 '''Set the favourite count for this user.
810 count: The number of favourites for this user.
812 self
._favourites
_count
= count
814 favourites_count
= property(GetFavouritesCount
, SetFavouritesCount
,
815 doc
='The number of favourites for this user.')
817 def __ne__(self
, other
):
818 return not self
.__eq
__(other
)
820 def __eq__(self
, other
):
823 self
.id == other
.id and \
824 self
.name
== other
.name
and \
825 self
.screen_name
== other
.screen_name
and \
826 self
.location
== other
.location
and \
827 self
.description
== other
.description
and \
828 self
.profile_image_url
== other
.profile_image_url
and \
829 self
.profile_background_tile
== other
.profile_background_tile
and \
830 self
.profile_background_image_url
== other
.profile_background_image_url
and \
831 self
.profile_sidebar_fill_color
== other
.profile_sidebar_fill_color
and \
832 self
.profile_background_color
== other
.profile_background_color
and \
833 self
.profile_link_color
== other
.profile_link_color
and \
834 self
.profile_text_color
== other
.profile_text_color
and \
835 self
.protected
== other
.protected
and \
836 self
.utc_offset
== other
.utc_offset
and \
837 self
.time_zone
== other
.time_zone
and \
838 self
.url
== other
.url
and \
839 self
.statuses_count
== other
.statuses_count
and \
840 self
.followers_count
== other
.followers_count
and \
841 self
.favourites_count
== other
.favourites_count
and \
842 self
.friends_count
== other
.friends_count
and \
843 self
.status
== other
.status
844 except AttributeError:
848 '''A string representation of this twitter.User instance.
850 The return value is the same as the JSON string representation.
853 A string representation of this twitter.User instance.
855 return self
.AsJsonString()
857 def AsJsonString(self
):
858 '''A JSON string representation of this twitter.User instance.
861 A JSON string representation of this twitter.User instance
863 return simplejson
.dumps(self
.AsDict(), sort_keys
=True)
866 '''A dict representation of this twitter.User instance.
868 The return value uses the same key names as the JSON representation.
871 A dict representing this twitter.User instance
877 data
['name'] = self
.name
879 data
['screen_name'] = self
.screen_name
881 data
['location'] = self
.location
883 data
['description'] = self
.description
884 if self
.profile_image_url
:
885 data
['profile_image_url'] = self
.profile_image_url
886 if self
.profile_background_tile
is not None:
887 data
['profile_background_tile'] = self
.profile_background_tile
888 if self
.profile_background_image_url
:
889 data
['profile_sidebar_fill_color'] = self
.profile_background_image_url
890 if self
.profile_background_color
:
891 data
['profile_background_color'] = self
.profile_background_color
892 if self
.profile_link_color
:
893 data
['profile_link_color'] = self
.profile_link_color
894 if self
.profile_text_color
:
895 data
['profile_text_color'] = self
.profile_text_color
896 if self
.protected
is not None:
897 data
['protected'] = self
.protected
899 data
['utc_offset'] = self
.utc_offset
901 data
['time_zone'] = self
.time_zone
903 data
['url'] = self
.url
905 data
['status'] = self
.status
.AsDict()
906 if self
.friends_count
:
907 data
['friends_count'] = self
.friends_count
908 if self
.followers_count
:
909 data
['followers_count'] = self
.followers_count
910 if self
.statuses_count
:
911 data
['statuses_count'] = self
.statuses_count
912 if self
.favourites_count
:
913 data
['favourites_count'] = self
.favourites_count
917 def NewFromJsonDict(data
):
918 '''Create a new instance based on a JSON dict.
921 data: A JSON dict, as converted from the JSON in the twitter API
923 A twitter.User instance
926 status
= Status
.NewFromJsonDict(data
['status'])
929 return User(id=data
.get('id', None),
930 name
=data
.get('name', None),
931 screen_name
=data
.get('screen_name', None),
932 location
=data
.get('location', None),
933 description
=data
.get('description', None),
934 statuses_count
=data
.get('statuses_count', None),
935 followers_count
=data
.get('followers_count', None),
936 favourites_count
=data
.get('favourites_count', None),
937 friends_count
=data
.get('friends_count', None),
938 profile_image_url
=data
.get('profile_image_url', None),
939 profile_background_tile
= data
.get('profile_background_tile', None),
940 profile_background_image_url
= data
.get('profile_background_image_url', None),
941 profile_sidebar_fill_color
= data
.get('profile_sidebar_fill_color', None),
942 profile_background_color
= data
.get('profile_background_color', None),
943 profile_link_color
= data
.get('profile_link_color', None),
944 profile_text_color
= data
.get('profile_text_color', None),
945 protected
= data
.get('protected', None),
946 utc_offset
= data
.get('utc_offset', None),
947 time_zone
= data
.get('time_zone', None),
948 url
=data
.get('url', None),
951 class DirectMessage(object):
952 '''A class representing the DirectMessage structure used by the twitter API.
954 The DirectMessage structure exposes the following properties:
957 direct_message.created_at
958 direct_message.created_at_in_seconds # read only
959 direct_message.sender_id
960 direct_message.sender_screen_name
961 direct_message.recipient_id
962 direct_message.recipient_screen_name
970 sender_screen_name
=None,
972 recipient_screen_name
=None,
974 '''An object to hold a Twitter direct message.
976 This class is normally instantiated by the twitter.Api class and
977 returned in a sequence.
979 Note: Dates are posted in the form "Sat Jan 27 04:17:38 +0000 2007"
982 id: The unique id of this direct message
983 created_at: The time this direct message was posted
984 sender_id: The id of the twitter user that sent this message
985 sender_screen_name: The name of the twitter user that sent this message
986 recipient_id: The id of the twitter that received this message
987 recipient_screen_name: The name of the twitter that received this message
988 text: The text of this direct message
991 self
.created_at
= created_at
992 self
.sender_id
= sender_id
993 self
.sender_screen_name
= sender_screen_name
994 self
.recipient_id
= recipient_id
995 self
.recipient_screen_name
= recipient_screen_name
999 '''Get the unique id of this direct message.
1002 The unique id of this direct message
1006 def SetId(self
, id):
1007 '''Set the unique id of this direct message.
1010 id: The unique id of this direct message
1014 id = property(GetId
, SetId
,
1015 doc
='The unique id of this direct message.')
1017 def GetCreatedAt(self
):
1018 '''Get the time this direct message was posted.
1021 The time this direct message was posted
1023 return self
._created
_at
1025 def SetCreatedAt(self
, created_at
):
1026 '''Set the time this direct message was posted.
1029 created_at: The time this direct message was created
1031 self
._created
_at
= created_at
1033 created_at
= property(GetCreatedAt
, SetCreatedAt
,
1034 doc
='The time this direct message was posted.')
1036 def GetCreatedAtInSeconds(self
):
1037 '''Get the time this direct message was posted, in seconds since the epoch.
1040 The time this direct message was posted, in seconds since the epoch.
1042 return calendar
.timegm(rfc822
.parsedate(self
.created_at
))
1044 created_at_in_seconds
= property(GetCreatedAtInSeconds
,
1045 doc
="The time this direct message was "
1046 "posted, in seconds since the epoch")
1048 def GetSenderId(self
):
1049 '''Get the unique sender id of this direct message.
1052 The unique sender id of this direct message
1054 return self
._sender
_id
1056 def SetSenderId(self
, sender_id
):
1057 '''Set the unique sender id of this direct message.
1060 sender id: The unique sender id of this direct message
1062 self
._sender
_id
= sender_id
1064 sender_id
= property(GetSenderId
, SetSenderId
,
1065 doc
='The unique sender id of this direct message.')
1067 def GetSenderScreenName(self
):
1068 '''Get the unique sender screen name of this direct message.
1071 The unique sender screen name of this direct message
1073 return self
._sender
_screen
_name
1075 def SetSenderScreenName(self
, sender_screen_name
):
1076 '''Set the unique sender screen name of this direct message.
1079 sender_screen_name: The unique sender screen name of this direct message
1081 self
._sender
_screen
_name
= sender_screen_name
1083 sender_screen_name
= property(GetSenderScreenName
, SetSenderScreenName
,
1084 doc
='The unique sender screen name of this direct message.')
1086 def GetRecipientId(self
):
1087 '''Get the unique recipient id of this direct message.
1090 The unique recipient id of this direct message
1092 return self
._recipient
_id
1094 def SetRecipientId(self
, recipient_id
):
1095 '''Set the unique recipient id of this direct message.
1098 recipient id: The unique recipient id of this direct message
1100 self
._recipient
_id
= recipient_id
1102 recipient_id
= property(GetRecipientId
, SetRecipientId
,
1103 doc
='The unique recipient id of this direct message.')
1105 def GetRecipientScreenName(self
):
1106 '''Get the unique recipient screen name of this direct message.
1109 The unique recipient screen name of this direct message
1111 return self
._recipient
_screen
_name
1113 def SetRecipientScreenName(self
, recipient_screen_name
):
1114 '''Set the unique recipient screen name of this direct message.
1117 recipient_screen_name: The unique recipient screen name of this direct message
1119 self
._recipient
_screen
_name
= recipient_screen_name
1121 recipient_screen_name
= property(GetRecipientScreenName
, SetRecipientScreenName
,
1122 doc
='The unique recipient screen name of this direct message.')
1125 '''Get the text of this direct message.
1128 The text of this direct message.
1132 def SetText(self
, text
):
1133 '''Set the text of this direct message.
1136 text: The text of this direct message
1140 text
= property(GetText
, SetText
,
1141 doc
='The text of this direct message')
1143 def __ne__(self
, other
):
1144 return not self
.__eq
__(other
)
1146 def __eq__(self
, other
):
1149 self
.id == other
.id and \
1150 self
.created_at
== other
.created_at
and \
1151 self
.sender_id
== other
.sender_id
and \
1152 self
.sender_screen_name
== other
.sender_screen_name
and \
1153 self
.recipient_id
== other
.recipient_id
and \
1154 self
.recipient_screen_name
== other
.recipient_screen_name
and \
1155 self
.text
== other
.text
1156 except AttributeError:
1160 '''A string representation of this twitter.DirectMessage instance.
1162 The return value is the same as the JSON string representation.
1165 A string representation of this twitter.DirectMessage instance.
1167 return self
.AsJsonString()
1169 def AsJsonString(self
):
1170 '''A JSON string representation of this twitter.DirectMessage instance.
1173 A JSON string representation of this twitter.DirectMessage instance
1175 return simplejson
.dumps(self
.AsDict(), sort_keys
=True)
1178 '''A dict representation of this twitter.DirectMessage instance.
1180 The return value uses the same key names as the JSON representation.
1183 A dict representing this twitter.DirectMessage instance
1187 data
['id'] = self
.id
1189 data
['created_at'] = self
.created_at
1191 data
['sender_id'] = self
.sender_id
1192 if self
.sender_screen_name
:
1193 data
['sender_screen_name'] = self
.sender_screen_name
1194 if self
.recipient_id
:
1195 data
['recipient_id'] = self
.recipient_id
1196 if self
.recipient_screen_name
:
1197 data
['recipient_screen_name'] = self
.recipient_screen_name
1199 data
['text'] = self
.text
1203 def NewFromJsonDict(data
):
1204 '''Create a new instance based on a JSON dict.
1207 data: A JSON dict, as converted from the JSON in the twitter API
1209 A twitter.DirectMessage instance
1211 return DirectMessage(created_at
=data
.get('created_at', None),
1212 recipient_id
=data
.get('recipient_id', None),
1213 sender_id
=data
.get('sender_id', None),
1214 text
=data
.get('text', None),
1215 sender_screen_name
=data
.get('sender_screen_name', None),
1216 id=data
.get('id', None),
1217 recipient_screen_name
=data
.get('recipient_screen_name', None))
1220 '''A python interface into the Twitter API
1222 By default, the Api caches results for 1 minute.
1226 To create an instance of the twitter.Api class, with no authentication:
1229 >>> api = twitter.Api()
1231 To fetch the most recently posted public twitter status messages:
1233 >>> statuses = api.GetPublicTimeline()
1234 >>> print [s.user.name for s in statuses]
1235 [u'DeWitt', u'Kesuke Miyagi', u'ev', u'Buzz Andersen', u'Biz Stone'] #...
1237 To fetch a single user's public status messages, where "user" is either
1238 a Twitter "short name" or their user id.
1240 >>> statuses = api.GetUserTimeline(user)
1241 >>> print [s.text for s in statuses]
1243 To use authentication, instantiate the twitter.Api class with a
1244 username and password:
1246 >>> api = twitter.Api(username='twitter user', password='twitter pass')
1248 To fetch your friends (after being authenticated):
1250 >>> users = api.GetFriends()
1251 >>> print [u.name for u in users]
1253 To post a twitter status message (after being authenticated):
1255 >>> status = api.PostUpdate('I love python-twitter!')
1256 >>> print status.text
1257 I love python-twitter!
1259 There are many other methods, including:
1261 >>> api.PostUpdates(status)
1262 >>> api.PostDirectMessage(user, text)
1263 >>> api.GetUser(user)
1264 >>> api.GetReplies()
1265 >>> api.GetUserTimeline(user)
1266 >>> api.GetStatus(id)
1267 >>> api.DestroyStatus(id)
1268 >>> api.GetFriendsTimeline(user)
1269 >>> api.GetFriends(user)
1270 >>> api.GetFollowers()
1271 >>> api.GetFeatured()
1272 >>> api.GetDirectMessages()
1273 >>> api.PostDirectMessage(user, text)
1274 >>> api.DestroyDirectMessage(id)
1275 >>> api.DestroyFriendship(user)
1276 >>> api.CreateFriendship(user)
1277 >>> api.GetUserByEmail(email)
1280 DEFAULT_CACHE_TIMEOUT
= 60 # cache for 1 minute
1282 _API_REALM
= 'Twitter API'
1287 input_encoding
=None,
1288 request_headers
=None,
1289 twitterserver
='twitter.com'):
1290 '''Instantiate a new twitter.Api object.
1293 username: The username of the twitter account. [optional]
1294 password: The password for the twitter account. [optional]
1295 input_encoding: The encoding used to encode input strings. [optional]
1296 request_header: A dictionary of additional HTTP request headers. [optional]
1297 twitterserver: you can use twitter.com or identi.ca [optional]
1299 self
._twitterserver
= twitterserver
1300 self
._cache
= _FileCache()
1301 self
._urllib
= urllib2
1302 self
._cache
_timeout
= Api
.DEFAULT_CACHE_TIMEOUT
1303 self
._InitializeRequestHeaders
(request_headers
)
1304 self
._InitializeUserAgent
()
1305 self
._InitializeDefaultParameters
()
1306 self
._input
_encoding
= input_encoding
1307 self
.SetCredentials(username
, password
)
1309 def GetPublicTimeline(self
, since_id
=None):
1310 '''Fetch the sequnce of public twitter.Status message for all users.
1314 Returns only public statuses with an ID greater than (that is,
1315 more recent than) the specified ID. [Optional]
1318 An sequence of twitter.Status instances, one for each message
1322 parameters
['since_id'] = since_id
1323 url
= 'http://%s/statuses/public_timeline.json' % self
._twitterserver
1324 json
= self
._FetchUrl
(url
, parameters
=parameters
)
1325 data
= simplejson
.loads(json
)
1326 self
._CheckForTwitterError
(data
)
1327 return [Status
.NewFromJsonDict(x
) for x
in data
]
1329 def GetFriendsTimeline(self
,
1334 '''Fetch the sequence of twitter.Status messages for a user's friends
1336 The twitter.Api instance must be authenticated if the user is private.
1340 Specifies the ID or screen name of the user for whom to return
1341 the friends_timeline. If unspecified, the username and password
1342 must be set in the twitter.Api instance. [Optional]
1344 Specifies the number of statuses to retrieve. May not be
1345 greater than 200. [Optional]
1347 Narrows the returned results to just those statuses created
1348 after the specified HTTP-formatted date. [Optional]
1350 Returns only public statuses with an ID greater than (that is,
1351 more recent than) the specified ID. [Optional]
1354 A sequence of twitter.Status instances, one for each message
1357 url
= 'http://%s/statuses/friends_timeline/%s.json' % (self
._twitterserver
,user
)
1358 elif not user
and not self
._username
:
1359 raise TwitterError("User must be specified if API is not authenticated.")
1361 url
= 'http://%s/statuses/friends_timeline.json' % self
._twitterserver
1363 if count
is not None:
1365 if int(count
) > 200:
1366 raise TwitterError("'count' may not be greater than 200")
1368 raise TwitterError("'count' must be an integer")
1369 parameters
['count'] = count
1371 parameters
['since'] = since
1373 parameters
['since_id'] = since_id
1374 json
= self
._FetchUrl
(url
, parameters
=parameters
)
1375 data
= simplejson
.loads(json
)
1376 self
._CheckForTwitterError
(data
)
1377 return [Status
.NewFromJsonDict(x
) for x
in data
]
1379 def GetUserTimeline(self
, user
=None, count
=None, since
=None, since_id
=None):
1380 '''Fetch the sequence of public twitter.Status messages for a single user.
1382 The twitter.Api instance must be authenticated if the user is private.
1386 either the username (short_name) or id of the user to retrieve. If
1387 not specified, then the current authenticated user is used. [optional]
1388 count: the number of status messages to retrieve [optional]
1390 Narrows the returned results to just those statuses created
1391 after the specified HTTP-formatted date. [optional]
1393 Returns only public statuses with an ID greater than (that is,
1394 more recent than) the specified ID. [Optional]
1397 A sequence of twitter.Status instances, one for each message up to count
1403 raise TwitterError("Count must be an integer")
1406 parameters
['count'] = count
1408 parameters
['since'] = since
1410 parameters
['since_id'] = since_id
1412 url
= 'http://%s/statuses/user_timeline/%s.json' % (self
._twitterserver
,user
)
1413 elif not user
and not self
._username
:
1414 raise TwitterError("User must be specified if API is not authenticated.")
1416 url
= 'http://%s/statuses/user_timeline.json' % self
._twitterserver
1417 json
= self
._FetchUrl
(url
, parameters
=parameters
)
1418 data
= simplejson
.loads(json
)
1419 self
._CheckForTwitterError
(data
)
1420 return [Status
.NewFromJsonDict(x
) for x
in data
]
1422 def GetStatus(self
, id):
1423 '''Returns a single status message.
1425 The twitter.Api instance must be authenticated if the status message is private.
1428 id: The numerical ID of the status you're trying to retrieve.
1431 A twitter.Status instance representing that status message
1437 raise TwitterError("id must be an long integer")
1438 url
= 'http://%s/statuses/show/%s.json' % (self
._twitterserver
,id)
1439 json
= self
._FetchUrl
(url
)
1440 data
= simplejson
.loads(json
)
1441 self
._CheckForTwitterError
(data
)
1442 return Status
.NewFromJsonDict(data
)
1444 def DestroyStatus(self
, id):
1445 '''Destroys the status specified by the required ID parameter.
1447 The twitter.Api instance must be authenticated and thee
1448 authenticating user must be the author of the specified status.
1451 id: The numerical ID of the status you're trying to destroy.
1454 A twitter.Status instance representing the destroyed status message
1460 raise TwitterError("id must be an integer")
1461 url
= 'http://%s/statuses/destroy/%s.json' % (self
._twitterserver
,id)
1462 json
= self
._FetchUrl
(url
, post_data
={})
1463 data
= simplejson
.loads(json
)
1464 self
._CheckForTwitterError
(data
)
1465 return Status
.NewFromJsonDict(data
)
1467 def PostUpdate(self
, status
, in_reply_to_status_id
=None):
1468 '''Post a twitter status message from the authenticated user.
1470 The twitter.Api instance must be authenticated.
1474 The message text to be posted. Must be less than or equal to
1476 in_reply_to_status_id:
1477 The ID of an existing status that the status to be posted is
1478 in reply to. This implicitly sets the in_reply_to_user_id
1479 attribute of the resulting status to the user ID of the
1480 message being replied to. Invalid/missing status IDs will be
1483 A twitter.Status instance representing the message posted.
1485 if not self
._username
:
1486 raise TwitterError("The twitter.Api instance must be authenticated.")
1488 url
= 'http://%s/statuses/update.json' % self
._twitterserver
1490 if len(status
) > CHARACTER_LIMIT
:
1491 raise TwitterError("Text must be less than or equal to %d characters. "
1492 "Consider using PostUpdates." % CHARACTER_LIMIT
)
1494 data
= {'status': status
}
1495 if in_reply_to_status_id
:
1496 data
['in_reply_to_status_id'] = in_reply_to_status_id
1497 json
= self
._FetchUrl
(url
, post_data
=data
)
1498 data
= simplejson
.loads(json
)
1499 self
._CheckForTwitterError
(data
)
1500 return Status
.NewFromJsonDict(data
)
1502 def PostUpdates(self
, status
, continuation
=None, **kwargs
):
1503 '''Post one or more twitter status messages from the authenticated user.
1505 Unlike api.PostUpdate, this method will post multiple status updates
1506 if the message is longer than 140 characters.
1508 The twitter.Api instance must be authenticated.
1512 The message text to be posted. May be longer than 140 characters.
1514 The character string, if any, to be appended to all but the
1515 last message. Note that Twitter strips trailing '...' strings
1516 from messages. Consider using the unicode \u2026 character
1517 (horizontal ellipsis) instead. [Defaults to None]
1519 See api.PostUpdate for a list of accepted parameters.
1521 A of list twitter.Status instance representing the messages posted.
1524 if continuation
is None:
1526 line_length
= CHARACTER_LIMIT
- len(continuation
)
1527 lines
= textwrap
.wrap(status
, line_length
)
1528 for line
in lines
[0:-1]:
1529 results
.append(self
.PostUpdate(line
+ continuation
, **kwargs
))
1530 results
.append(self
.PostUpdate(lines
[-1], **kwargs
))
1533 def GetReplies(self
, since
=None, since_id
=None, page
=None):
1534 '''Get a sequence of status messages representing the 20 most recent
1535 replies (status updates prefixed with @username) to the authenticating
1541 Narrows the returned results to just those statuses created
1542 after the specified HTTP-formatted date. [optional]
1544 Returns only public statuses with an ID greater than (that is,
1545 more recent than) the specified ID. [Optional]
1548 A sequence of twitter.Status instances, one for each reply to the user.
1550 url
= 'http://%s/statuses/replies.json' % self
._twitterserver
1551 if not self
._username
:
1552 raise TwitterError("The twitter.Api instance must be authenticated.")
1555 parameters
['since'] = since
1557 parameters
['since_id'] = since_id
1559 parameters
['page'] = page
1560 json
= self
._FetchUrl
(url
, parameters
=parameters
)
1561 data
= simplejson
.loads(json
)
1562 self
._CheckForTwitterError
(data
)
1563 return [Status
.NewFromJsonDict(x
) for x
in data
]
1565 def GetFriends(self
, user
=None, page
=None):
1566 '''Fetch the sequence of twitter.User instances, one for each friend.
1569 user: the username or id of the user whose friends you are fetching. If
1570 not specified, defaults to the authenticated user. [optional]
1572 The twitter.Api instance must be authenticated.
1575 A sequence of twitter.User instances, one for each friend
1577 if not self
._username
:
1578 raise TwitterError("twitter.Api instance must be authenticated")
1580 url
= 'http://%s/statuses/friends/%s.json' % (self
._twitterserver
,user
)
1582 url
= 'http://%s/statuses/friends.json' % self
._twitterserver
1585 parameters
['page'] = page
1586 json
= self
._FetchUrl
(url
, parameters
=parameters
)
1587 data
= simplejson
.loads(json
)
1588 self
._CheckForTwitterError
(data
)
1589 return [User
.NewFromJsonDict(x
) for x
in data
]
1591 def GetFollowers(self
, page
=None):
1592 '''Fetch the sequence of twitter.User instances, one for each follower
1594 The twitter.Api instance must be authenticated.
1597 A sequence of twitter.User instances, one for each follower
1599 if not self
._username
:
1600 raise TwitterError("twitter.Api instance must be authenticated")
1601 url
= 'http://%s/statuses/followers.json' % self
._twitterserver
1604 parameters
['page'] = page
1605 json
= self
._FetchUrl
(url
, parameters
=parameters
)
1606 data
= simplejson
.loads(json
)
1607 self
._CheckForTwitterError
(data
)
1608 return [User
.NewFromJsonDict(x
) for x
in data
]
1610 def GetFeatured(self
):
1611 '''Fetch the sequence of twitter.User instances featured on twitter.com
1613 The twitter.Api instance must be authenticated.
1616 A sequence of twitter.User instances
1618 url
= 'http://%s/statuses/featured.json' % self
._twitterserver
1619 json
= self
._FetchUrl
(url
)
1620 data
= simplejson
.loads(json
)
1621 self
._CheckForTwitterError
(data
)
1622 return [User
.NewFromJsonDict(x
) for x
in data
]
1624 def GetUser(self
, user
):
1625 '''Returns a single user.
1627 The twitter.Api instance must be authenticated.
1630 user: The username or id of the user to retrieve.
1633 A twitter.User instance representing that user
1635 url
= 'http://%s/users/show/%s.json' % (self
._twitterserver
,user
)
1636 json
= self
._FetchUrl
(url
)
1637 data
= simplejson
.loads(json
)
1638 self
._CheckForTwitterError
(data
)
1639 return User
.NewFromJsonDict(data
)
1641 def GetDirectMessages(self
, since
=None, since_id
=None, page
=None):
1642 '''Returns a list of the direct messages sent to the authenticating user.
1644 The twitter.Api instance must be authenticated.
1648 Narrows the returned results to just those statuses created
1649 after the specified HTTP-formatted date. [optional]
1651 Returns only public statuses with an ID greater than (that is,
1652 more recent than) the specified ID. [Optional]
1655 A sequence of twitter.DirectMessage instances
1657 url
= 'http://%s/direct_messages.json' % self
._twitterserver
1658 if not self
._username
:
1659 raise TwitterError("The twitter.Api instance must be authenticated.")
1662 parameters
['since'] = since
1664 parameters
['since_id'] = since_id
1666 parameters
['page'] = page
1667 json
= self
._FetchUrl
(url
, parameters
=parameters
)
1668 data
= simplejson
.loads(json
)
1669 self
._CheckForTwitterError
(data
)
1670 return [DirectMessage
.NewFromJsonDict(x
) for x
in data
]
1672 def PostDirectMessage(self
, user
, text
):
1673 '''Post a twitter direct message from the authenticated user
1675 The twitter.Api instance must be authenticated.
1678 user: The ID or screen name of the recipient user.
1679 text: The message text to be posted. Must be less than 140 characters.
1682 A twitter.DirectMessage instance representing the message posted
1684 if not self
._username
:
1685 raise TwitterError("The twitter.Api instance must be authenticated.")
1686 url
= 'http://%s/direct_messages/new.json' % self
._twitterserver
1687 data
= {'text': text
, 'user': user
}
1688 json
= self
._FetchUrl
(url
, post_data
=data
)
1689 data
= simplejson
.loads(json
)
1690 self
._CheckForTwitterError
(data
)
1691 return DirectMessage
.NewFromJsonDict(data
)
1693 def DestroyDirectMessage(self
, id):
1694 '''Destroys the direct message specified in the required ID parameter.
1696 The twitter.Api instance must be authenticated, and the
1697 authenticating user must be the recipient of the specified direct
1701 id: The id of the direct message to be destroyed
1704 A twitter.DirectMessage instance representing the message destroyed
1706 url
= 'http://%s/direct_messages/destroy/%s.json' % (self
._twitterserver
,id)
1707 json
= self
._FetchUrl
(url
, post_data
={})
1708 data
= simplejson
.loads(json
)
1709 self
._CheckForTwitterError
(data
)
1710 return DirectMessage
.NewFromJsonDict(data
)
1712 def CreateFriendship(self
, user
):
1713 '''Befriends the user specified in the user parameter as the authenticating user.
1715 The twitter.Api instance must be authenticated.
1718 The ID or screen name of the user to befriend.
1720 A twitter.User instance representing the befriended user.
1722 url
= 'http://%s/friendships/create/%s.json' % (self
._twitterserver
,user
)
1723 json
= self
._FetchUrl
(url
, post_data
={})
1724 data
= simplejson
.loads(json
)
1725 self
._CheckForTwitterError
(data
)
1726 return User
.NewFromJsonDict(data
)
1728 def DestroyFriendship(self
, user
):
1729 '''Discontinues friendship with the user specified in the user parameter.
1731 The twitter.Api instance must be authenticated.
1734 The ID or screen name of the user with whom to discontinue friendship.
1736 A twitter.User instance representing the discontinued friend.
1738 url
= 'http://%s/friendships/destroy/%s.json' % (self
._twitterserver
,user
)
1739 json
= self
._FetchUrl
(url
, post_data
={})
1740 data
= simplejson
.loads(json
)
1741 self
._CheckForTwitterError
(data
)
1742 return User
.NewFromJsonDict(data
)
1744 def CreateFavorite(self
, status
):
1745 '''Favorites the status specified in the status parameter as the authenticating user.
1746 Returns the favorite status when successful.
1748 The twitter.Api instance must be authenticated.
1751 The twitter.Status instance to mark as a favorite.
1753 A twitter.Status instance representing the newly-marked favorite.
1755 url
= 'http://%s/favorites/create/%s.json' % (self
._twitterserver
, status
.id)
1756 json
= self
._FetchUrl
(url
, post_data
={})
1757 data
= simplejson
.loads(json
)
1758 self
._CheckForTwitterError
(data
)
1759 return Status
.NewFromJsonDict(data
)
1761 def DestroyFavorite(self
, status
):
1762 '''Un-favorites the status specified in the ID parameter as the authenticating user.
1763 Returns the un-favorited status in the requested format when successful.
1765 The twitter.Api instance must be authenticated.
1768 The twitter.Status to unmark as a favorite.
1770 A twitter.Status instance representing the newly-unmarked favorite.
1772 url
= 'http://%s/favorites/destroy/%s.json' % (self
._twitterserver
, status
.id)
1773 json
= self
._FetchUrl
(url
, post_data
={})
1774 data
= simplejson
.loads(json
)
1775 self
._CheckForTwitterError
(data
)
1776 return Status
.NewFromJsonDict(data
)
1778 def GetUserByEmail(self
, email
):
1779 '''Returns a single user by email address.
1782 email: The email of the user to retrieve.
1784 A twitter.User instance representing that user
1786 url
= 'http://%s/users/show.json?email=%s' % (self
._twitterserver
, email
)
1787 json
= self
._FetchUrl
(url
)
1788 data
= simplejson
.loads(json
)
1789 self
._CheckForTwitterError
(data
)
1790 return User
.NewFromJsonDict(data
)
1792 def SetCredentials(self
, username
, password
):
1793 '''Set the username and password for this instance
1796 username: The twitter username.
1797 password: The twitter password.
1799 self
._username
= username
1800 self
._password
= password
1802 def ClearCredentials(self
):
1803 '''Clear the username and password for this instance
1805 self
._username
= None
1806 self
._password
= None
1808 def SetCache(self
, cache
):
1809 '''Override the default cache. Set to None to prevent caching.
1812 cache: an instance that supports the same API as the twitter._FileCache
1816 def SetUrllib(self
, urllib
):
1817 '''Override the default urllib implementation.
1820 urllib: an instance that supports the same API as the urllib2 module
1822 self
._urllib
= urllib
1824 def SetCacheTimeout(self
, cache_timeout
):
1825 '''Override the default cache timeout.
1828 cache_timeout: time, in seconds, that responses should be reused.
1830 self
._cache
_timeout
= cache_timeout
1832 def SetUserAgent(self
, user_agent
):
1833 '''Override the default user agent
1836 user_agent: a string that should be send to the server as the User-agent
1838 self
._request
_headers
['User-Agent'] = user_agent
1840 def SetXTwitterHeaders(self
, client
, url
, version
):
1841 '''Set the X-Twitter HTTP headers that will be sent to the server.
1845 The client name as a string. Will be sent to the server as
1846 the 'X-Twitter-Client' header.
1848 The URL of the meta.xml as a string. Will be sent to the server
1849 as the 'X-Twitter-Client-URL' header.
1851 The client version as a string. Will be sent to the server
1852 as the 'X-Twitter-Client-Version' header.
1854 self
._request
_headers
['X-Twitter-Client'] = client
1855 self
._request
_headers
['X-Twitter-Client-URL'] = url
1856 self
._request
_headers
['X-Twitter-Client-Version'] = version
1858 def SetSource(self
, source
):
1859 '''Suggest the "from source" value to be displayed on the Twitter web site.
1861 The value of the 'source' parameter must be first recognized by
1862 the Twitter server. New source values are authorized on a case by
1863 case basis by the Twitter development team.
1867 The source name as a string. Will be sent to the server as
1868 the 'source' parameter.
1870 self
._default
_params
['source'] = source
1872 def _BuildUrl(self
, url
, path_elements
=None, extra_params
=None):
1873 # Break url into consituent parts
1874 (scheme
, netloc
, path
, params
, query
, fragment
) = urlparse
.urlparse(url
)
1876 # Add any additional path elements to the path
1878 # Filter out the path elements that have a value of None
1879 p
= [i
for i
in path_elements
if i
]
1880 if not path
.endswith('/'):
1884 # Add any additional query parameters to the query string
1885 if extra_params
and len(extra_params
) > 0:
1886 extra_query
= self
._EncodeParameters
(extra_params
)
1887 # Add it to the existing query
1889 query
+= '&' + extra_query
1893 # Return the rebuilt URL
1894 return urlparse
.urlunparse((scheme
, netloc
, path
, params
, query
, fragment
))
1896 def _InitializeRequestHeaders(self
, request_headers
):
1898 self
._request
_headers
= request_headers
1900 self
._request
_headers
= {}
1902 def _InitializeUserAgent(self
):
1903 user_agent
= 'Python-urllib/%s (python-twitter/%s)' % \
1904 (self
._urllib
.__version
__, __version__
)
1905 self
.SetUserAgent(user_agent
)
1907 def _InitializeDefaultParameters(self
):
1908 self
._default
_params
= {}
1910 def _AddAuthorizationHeader(self
, username
, password
):
1911 if username
and password
:
1912 basic_auth
= base64
.encodestring('%s:%s' % (username
, password
))[:-1]
1913 self
._request
_headers
['Authorization'] = 'Basic %s' % basic_auth
1915 def _RemoveAuthorizationHeader(self
):
1916 if self
._request
_headers
and 'Authorization' in self
._request
_headers
:
1917 del self
._request
_headers
['Authorization']
1919 def _GetOpener(self
, url
, username
=None, password
=None):
1920 if username
and password
:
1921 self
._AddAuthorizationHeader
(username
, password
)
1922 handler
= self
._urllib
.HTTPBasicAuthHandler()
1923 (scheme
, netloc
, path
, params
, query
, fragment
) = urlparse
.urlparse(url
)
1924 handler
.add_password(Api
._API
_REALM
, netloc
, username
, password
)
1925 opener
= self
._urllib
.build_opener(handler
)
1927 opener
= self
._urllib
.build_opener()
1928 opener
.addheaders
= self
._request
_headers
.items()
1931 def _Encode(self
, s
):
1932 if self
._input
_encoding
:
1933 return unicode(s
, self
._input
_encoding
).encode('utf-8')
1935 return unicode(s
).encode('utf-8')
1937 def _EncodeParameters(self
, parameters
):
1938 '''Return a string in key=value&key=value form
1940 Values of None are not included in the output string.
1944 A dict of (key, value) tuples, where value is encoded as
1945 specified by self._encoding
1947 A URL-encoded string in "key=value&key=value" form
1949 if parameters
is None:
1952 return urllib
.urlencode(dict([(k
, self
._Encode
(v
)) for k
, v
in parameters
.items() if v
is not None]))
1954 def _EncodePostData(self
, post_data
):
1955 '''Return a string in key=value&key=value form
1957 Values are assumed to be encoded in the format specified by self._encoding,
1958 and are subsequently URL encoded.
1962 A dict of (key, value) tuples, where value is encoded as
1963 specified by self._encoding
1965 A URL-encoded string in "key=value&key=value" form
1967 if post_data
is None:
1970 return urllib
.urlencode(dict([(k
, self
._Encode
(v
)) for k
, v
in post_data
.items()]))
1972 def _CheckForTwitterError(self
, data
):
1973 """Raises a TwitterError if twitter returns an error message.
1976 data: A python dict created from the Twitter json response
1978 TwitterError wrapping the twitter error message if one exists.
1980 # Twitter errors are relatively unlikely, so it is faster
1981 # to check first, rather than try and catch the exception
1983 raise TwitterError(data
['error'])
1990 '''Fetch a URL, optionally caching for a specified time.
1993 url: The URL to retrieve
1995 A dict of (str, unicode) key/value pairs. If set, POST will be used.
1997 A dict whose key/value pairs should encoded and added
1998 to the query string. [OPTIONAL]
1999 no_cache: If true, overrides the cache on the current request
2002 A string containing the body of the response.
2004 # Build the extra parameters dict
2006 if self
._default
_params
:
2007 extra_params
.update(self
._default
_params
)
2009 extra_params
.update(parameters
)
2011 # Add key/value parameters to the query string of the url
2012 url
= self
._BuildUrl
(url
, extra_params
=extra_params
)
2014 # Get a url opener that can handle basic auth
2015 opener
= self
._GetOpener
(url
, username
=self
._username
, password
=self
._password
)
2017 encoded_post_data
= self
._EncodePostData
(post_data
)
2019 # Open and return the URL immediately if we're not going to cache
2020 if encoded_post_data
or no_cache
or not self
._cache
or not self
._cache
_timeout
:
2021 url_data
= opener
.open(url
, encoded_post_data
).read()
2024 # Unique keys are a combination of the url and the username
2026 key
= self
._username
+ ':' + url
2030 # See if it has been cached before
2031 last_cached
= self
._cache
.GetCachedTime(key
)
2033 # If the cached version is outdated then fetch another and store it
2034 if not last_cached
or time
.time() >= last_cached
+ self
._cache
_timeout
:
2035 url_data
= opener
.open(url
, encoded_post_data
).read()
2037 self
._cache
.Set(key
, url_data
)
2039 url_data
= self
._cache
.Get(key
)
2041 # Always return the latest version
2045 class _FileCacheError(Exception):
2046 '''Base exception class for FileCache related errors'''
2048 class _FileCache(object):
2052 def __init__(self
,root_directory
=None):
2053 self
._InitializeRootDirectory
(root_directory
)
2056 path
= self
._GetPath
(key
)
2057 if os
.path
.exists(path
):
2058 return open(path
).read()
2062 def Set(self
,key
,data
):
2063 path
= self
._GetPath
(key
)
2064 directory
= os
.path
.dirname(path
)
2065 if not os
.path
.exists(directory
):
2066 os
.makedirs(directory
)
2067 if not os
.path
.isdir(directory
):
2068 raise _FileCacheError('%s exists but is not a directory' % directory
)
2069 temp_fd
, temp_path
= tempfile
.mkstemp()
2070 temp_fp
= os
.fdopen(temp_fd
, 'w')
2073 if not path
.startswith(self
._root
_directory
):
2074 raise _FileCacheError('%s does not appear to live under %s' %
2075 (path
, self
._root
_directory
))
2076 if os
.path
.exists(path
):
2078 os
.rename(temp_path
, path
)
2080 def Remove(self
,key
):
2081 path
= self
._GetPath
(key
)
2082 if not path
.startswith(self
._root
_directory
):
2083 raise _FileCacheError('%s does not appear to live under %s' %
2084 (path
, self
._root
_directory
))
2085 if os
.path
.exists(path
):
2088 def GetCachedTime(self
,key
):
2089 path
= self
._GetPath
(key
)
2090 if os
.path
.exists(path
):
2091 return os
.path
.getmtime(path
)
2095 def _GetUsername(self
):
2096 '''Attempt to find the username in a cross-platform fashion.'''
2098 return os
.getenv('USER') or \
2099 os
.getenv('LOGNAME') or \
2100 os
.getenv('USERNAME') or \
2103 except (IOError, OSError), e
:
2106 def _GetTmpCachePath(self
):
2107 username
= self
._GetUsername
()
2108 cache_directory
= 'python.cache_' + username
2109 return os
.path
.join(tempfile
.gettempdir(), cache_directory
)
2111 def _InitializeRootDirectory(self
, root_directory
):
2112 if not root_directory
:
2113 root_directory
= self
._GetTmpCachePath
()
2114 root_directory
= os
.path
.abspath(root_directory
)
2115 if not os
.path
.exists(root_directory
):
2116 os
.mkdir(root_directory
)
2117 if not os
.path
.isdir(root_directory
):
2118 raise _FileCacheError('%s exists but is not a directory' %
2120 self
._root
_directory
= root_directory
2122 def _GetPath(self
,key
):
2124 hashed_key
= md5(key
).hexdigest()
2126 hashed_key
= md5
.new(key
).hexdigest()
2128 return os
.path
.join(self
._root
_directory
,
2129 self
._GetPrefix
(hashed_key
),
2132 def _GetPrefix(self
,hashed_key
):
2133 return os
.path
.sep
.join(hashed_key
[0:_FileCache
.DEPTH
])