bump python-irclib to 0.6.4, the latest i care to merge right now
This commit is contained in:
		
							parent
							
								
									db0cfbc997
								
							
						
					
					
						commit
						17040c1cde
					
				| @ -1,6 +1,6 @@ | ||||
| Python IRC library (python-irclib) | ||||
| * http://python-irclib.sourceforge.net/ | ||||
| * 0.4.8 | ||||
| * 0.6.4 | ||||
| * LGPLv2 | ||||
| 
 | ||||
| python-weather-api | ||||
|  | ||||
							
								
								
									
										321
									
								
								extlib/irclib.py
									
									
									
									
									
								
							
							
						
						
									
										321
									
								
								extlib/irclib.py
									
									
									
									
									
								
							| @ -1,4 +1,7 @@ | ||||
| # Copyright (C) 1999--2002  Joel Rosdahl | ||||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| # Copyright (C) 1999-2002  Joel Rosdahl | ||||
| # Portions Copyright © 2011 Jason R. Coombs | ||||
| # | ||||
| # This library is free software; you can redistribute it and/or | ||||
| # modify it under the terms of the GNU Lesser General Public | ||||
| @ -15,10 +18,9 @@ | ||||
| # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA | ||||
| # | ||||
| # keltus <keltus@users.sourceforge.net> | ||||
| # | ||||
| # $Id: irclib.py,v 1.47 2008/09/25 22:00:59 keltus Exp $ | ||||
| 
 | ||||
| """irclib -- Internet Relay Chat (IRC) protocol client library. | ||||
| """ | ||||
| irclib -- Internet Relay Chat (IRC) protocol client library. | ||||
| 
 | ||||
| This library is intended to encapsulate the IRC protocol at a quite | ||||
| low level.  It provides an event-driven IRC client framework.  It has | ||||
| @ -66,12 +68,19 @@ import re | ||||
| import select | ||||
| import socket | ||||
| import string | ||||
| import sys | ||||
| import time | ||||
| import types | ||||
| import ssl as ssl_mod | ||||
| import datetime | ||||
| 
 | ||||
| VERSION = 0, 4, 8 | ||||
| DEBUG = 0 | ||||
| try: | ||||
|     import pkg_resources | ||||
|     _pkg = pkg_resources.require('python-irclib')[0] | ||||
|     VERSION = tuple(int(res) for res in re.findall('\d+', _pkg.version)) | ||||
| except Exception: | ||||
|     VERSION = () | ||||
| 
 | ||||
| DEBUG = False | ||||
| 
 | ||||
| # TODO | ||||
| # ---- | ||||
| @ -93,7 +102,7 @@ class IRCError(Exception): | ||||
|     pass | ||||
| 
 | ||||
| 
 | ||||
| class IRC: | ||||
| class IRC(object): | ||||
|     """Class that handles one or several IRC server connections. | ||||
| 
 | ||||
|     When an IRC object has been instantiated, it can be used to create | ||||
| @ -112,12 +121,12 @@ class IRC: | ||||
| 
 | ||||
|         irc = irclib.IRC() | ||||
|         server = irc.server() | ||||
|         server.connect(\"irc.some.where\", 6667, \"my_nickname\") | ||||
|         server.privmsg(\"a_nickname\", \"Hi there!\") | ||||
|         server.connect("irc.some.where", 6667, "my_nickname") | ||||
|         server.privmsg("a_nickname", "Hi there!") | ||||
|         irc.process_forever() | ||||
| 
 | ||||
|     This will connect to the IRC server irc.some.where on port 6667 | ||||
|     using the nickname my_nickname and send the message \"Hi there!\" | ||||
|     using the nickname my_nickname and send the message "Hi there!" | ||||
|     to the nickname a_nickname. | ||||
|     """ | ||||
| 
 | ||||
| @ -157,7 +166,7 @@ class IRC: | ||||
|         self.fn_to_add_timeout = fn_to_add_timeout | ||||
|         self.connections = [] | ||||
|         self.handlers = {} | ||||
|         self.delayed_commands = [] # list of tuples in the format (time, function, arguments) | ||||
|         self.delayed_commands = []  # list of DelayedCommands | ||||
| 
 | ||||
|         self.add_global_handler("ping", _ping_ponger, -42) | ||||
| 
 | ||||
| @ -187,13 +196,14 @@ class IRC: | ||||
| 
 | ||||
|         See documentation for IRC.__init__. | ||||
|         """ | ||||
|         t = time.time() | ||||
|         while self.delayed_commands: | ||||
|             if t >= self.delayed_commands[0][0]: | ||||
|                 self.delayed_commands[0][1](*self.delayed_commands[0][2]) | ||||
|                 del self.delayed_commands[0] | ||||
|             else: | ||||
|             command = self.delayed_commands[0] | ||||
|             if not command.due(): | ||||
|                 break | ||||
|             command.function(*command.arguments) | ||||
|             if isinstance(command, PeriodicCommand): | ||||
|                 self._schedule_command(command.next()) | ||||
|             del self.delayed_commands[0] | ||||
| 
 | ||||
|     def process_once(self, timeout=0): | ||||
|         """Process data from connections once. | ||||
| @ -252,7 +262,7 @@ class IRC: | ||||
| 
 | ||||
|         The handler functions are called in priority order (lowest | ||||
|         number is highest priority).  If a handler function returns | ||||
|         \"NO MORE\", no more handlers will be called. | ||||
|         "NO MORE", no more handlers will be called. | ||||
|         """ | ||||
|         if not event in self.handlers: | ||||
|             self.handlers[event] = [] | ||||
| @ -264,7 +274,6 @@ class IRC: | ||||
|         Arguments: | ||||
| 
 | ||||
|             event -- Event type (a string). | ||||
| 
 | ||||
|             handler -- Callback function. | ||||
| 
 | ||||
|         Returns 1 on success, otherwise 0. | ||||
| @ -281,28 +290,39 @@ class IRC: | ||||
| 
 | ||||
|         Arguments: | ||||
| 
 | ||||
|             at -- Execute at this time (standard \"time_t\" time). | ||||
| 
 | ||||
|             at -- Execute at this time (standard "time_t" time). | ||||
|             function -- Function to call. | ||||
| 
 | ||||
|             arguments -- Arguments to give the function. | ||||
|         """ | ||||
|         self.execute_delayed(at-time.time(), function, arguments) | ||||
|         command = DelayedCommand.at_time(at, function, arguments) | ||||
|         self._schedule_command(command) | ||||
| 
 | ||||
|     def execute_delayed(self, delay, function, arguments=()): | ||||
|         """Execute a function after a specified time. | ||||
| 
 | ||||
|         Arguments: | ||||
| 
 | ||||
|             delay -- How many seconds to wait. | ||||
| 
 | ||||
|             function -- Function to call. | ||||
| 
 | ||||
|             arguments -- Arguments to give the function. | ||||
|         """ | ||||
|         bisect.insort(self.delayed_commands, (delay+time.time(), function, arguments)) | ||||
|         Execute a function after a specified time. | ||||
| 
 | ||||
|         delay -- How many seconds to wait. | ||||
|         function -- Function to call. | ||||
|         arguments -- Arguments to give the function. | ||||
|         """ | ||||
|         command = DelayedCommand(delay, function, arguments) | ||||
|         self._schedule_command(command) | ||||
| 
 | ||||
|     def execute_every(self, period, function, arguments=()): | ||||
|         """ | ||||
|         Execute a function every 'period' seconds. | ||||
| 
 | ||||
|         period -- How often to run (always waits this long for first). | ||||
|         function -- Function to call. | ||||
|         arguments -- Arguments to give the function. | ||||
|         """ | ||||
|         command = PeriodicCommand(period, function, arguments) | ||||
|         self._schedule_command(command) | ||||
| 
 | ||||
|     def _schedule_command(self, command): | ||||
|         bisect.insort(self.delayed_commands, command) | ||||
|         if self.fn_to_add_timeout: | ||||
|             self.fn_to_add_timeout(delay) | ||||
|             self.fn_to_add_timeout(total_seconds(command.delay)) | ||||
| 
 | ||||
|     def dcc(self, dcctype="chat"): | ||||
|         """Creates and returns a DCCConnection object. | ||||
| @ -321,7 +341,8 @@ class IRC: | ||||
|     def _handle_event(self, connection, event): | ||||
|         """[Internal]""" | ||||
|         h = self.handlers | ||||
|         for handler in h.get("all_events", []) + h.get(event.eventtype(), []): | ||||
|         th = sorted(h.get("all_events", []) + h.get(event.eventtype(), [])) | ||||
|         for handler in th: | ||||
|             if handler[1](connection, event) == "NO MORE": | ||||
|                 return | ||||
| 
 | ||||
| @ -331,9 +352,48 @@ class IRC: | ||||
|         if self.fn_to_remove_socket: | ||||
|             self.fn_to_remove_socket(connection._get_socket()) | ||||
| 
 | ||||
| class DelayedCommand(datetime.datetime): | ||||
|     """ | ||||
|     A command to be executed after some delay (seconds or timedelta). | ||||
|     """ | ||||
|     def __new__(cls, delay, function, arguments): | ||||
|         if not isinstance(delay, datetime.timedelta): | ||||
|             delay = datetime.timedelta(seconds=delay) | ||||
|         at = datetime.datetime.utcnow() + delay | ||||
|         cmd = datetime.datetime.__new__(DelayedCommand, at.year, | ||||
|             at.month, at.day, at.hour, at.minute, at.second, | ||||
|             at.microsecond, at.tzinfo) | ||||
|         cmd.delay = delay | ||||
|         cmd.function = function | ||||
|         cmd.arguments = arguments | ||||
|         return cmd | ||||
| 
 | ||||
|     def at_time(cls, at, function, arguments): | ||||
|         """ | ||||
|         Construct a DelayedCommand to come due at `at`, where `at` may be | ||||
|         a datetime or timestamp. | ||||
|         """ | ||||
|         if isinstance(at, int): | ||||
|             at = datetime.datetime.utcfromtimestamp(at) | ||||
|         delay = at - datetime.datetime.utcnow() | ||||
|         return cls(delay, function, arguments) | ||||
|     at_time = classmethod(at_time) | ||||
| 
 | ||||
|     def due(self): | ||||
|         return datetime.datetime.utcnow() >= self | ||||
| 
 | ||||
| class PeriodicCommand(DelayedCommand): | ||||
|     """ | ||||
|     Like a deferred command, but expect this command to run every delay | ||||
|     seconds. | ||||
|     """ | ||||
|     def next(self): | ||||
|         return PeriodicCommand(self.delay, self.function, | ||||
|             self.arguments) | ||||
| 
 | ||||
| _rfc_1459_command_regexp = re.compile("^(:(?P<prefix>[^ ]+) +)?(?P<command>[^ ]+)( *(?P<argument> .+))?") | ||||
| 
 | ||||
| class Connection: | ||||
| class Connection(object): | ||||
|     """Base class for IRC connections. | ||||
| 
 | ||||
|     Must be overridden. | ||||
| @ -342,7 +402,7 @@ class Connection: | ||||
|         self.irclibobj = irclibobj | ||||
| 
 | ||||
|     def _get_socket(): | ||||
|         raise IRCError, "Not overridden" | ||||
|         raise IRCError("Not overridden") | ||||
| 
 | ||||
|     ############################## | ||||
|     ### Convenience wrappers. | ||||
| @ -353,6 +413,8 @@ class Connection: | ||||
|     def execute_delayed(self, delay, function, arguments=()): | ||||
|         self.irclibobj.execute_delayed(delay, function, arguments) | ||||
| 
 | ||||
|     def execute_every(self, period, function, arguments=()): | ||||
|         self.irclibobj.execute_every(period, function, arguments) | ||||
| 
 | ||||
| class ServerConnectionError(IRCError): | ||||
|     pass | ||||
| @ -373,7 +435,7 @@ class ServerConnection(Connection): | ||||
|     """ | ||||
| 
 | ||||
|     def __init__(self, irclibobj): | ||||
|         Connection.__init__(self, irclibobj) | ||||
|         super(ServerConnection, self).__init__(irclibobj) | ||||
|         self.connected = 0  # Not connected yet. | ||||
|         self.socket = None | ||||
|         self.ssl = None | ||||
| @ -432,11 +494,11 @@ class ServerConnection(Connection): | ||||
|             self.socket.bind((self.localaddress, self.localport)) | ||||
|             self.socket.connect((self.server, self.port)) | ||||
|             if ssl: | ||||
|                 self.ssl = socket.ssl(self.socket) | ||||
|                 self.ssl = ssl_mod.wrap_socket(self.socket) | ||||
|         except socket.error, x: | ||||
|             self.socket.close() | ||||
|             self.socket = None | ||||
|             raise ServerConnectionError, "Couldn't connect to socket: %s" % x | ||||
|             raise ServerConnectionError("Couldn't connect to socket: %s" % x) | ||||
|         self.connected = 1 | ||||
|         if self.irclibobj.fn_to_add_socket: | ||||
|             self.irclibobj.fn_to_add_socket(self.socket) | ||||
| @ -488,10 +550,10 @@ class ServerConnection(Connection): | ||||
| 
 | ||||
|         try: | ||||
|             if self.ssl: | ||||
|                 new_data = self.ssl.read(2**14) | ||||
|                 new_data = self.ssl.read(2 ** 14) | ||||
|             else: | ||||
|                 new_data = self.socket.recv(2**14) | ||||
|         except socket.error, x: | ||||
|                 new_data = self.socket.recv(2 ** 14) | ||||
|         except socket.error: | ||||
|             # The server hung up. | ||||
|             self.disconnect("Connection reset by peer") | ||||
|             return | ||||
| @ -660,7 +722,7 @@ class ServerConnection(Connection): | ||||
| 
 | ||||
|         try: | ||||
|             self.socket.close() | ||||
|         except socket.error, x: | ||||
|         except socket.error: | ||||
|             pass | ||||
|         self.socket = None | ||||
|         self._handle_event(Event("disconnect", self.server, "", [message])) | ||||
| @ -743,10 +805,13 @@ class ServerConnection(Connection): | ||||
| 
 | ||||
|     def part(self, channels, message=""): | ||||
|         """Send a PART command.""" | ||||
|         if type(channels) == types.StringType: | ||||
|             self.send_raw("PART " + channels + (message and (" " + message))) | ||||
|         else: | ||||
|             self.send_raw("PART " + ",".join(channels) + (message and (" " + message))) | ||||
|         channels = always_iterable(channels) | ||||
|         cmd_parts = [ | ||||
|             'PART', | ||||
|             ','.join(channels), | ||||
|         ] | ||||
|         if message: cmd_parts.append(message) | ||||
|         self.send_raw(' '.join(cmd_parts)) | ||||
| 
 | ||||
|     def pass_(self, password): | ||||
|         """Send a PASS command.""" | ||||
| @ -782,7 +847,7 @@ class ServerConnection(Connection): | ||||
|         The string will be padded with appropriate CR LF. | ||||
|         """ | ||||
|         if self.socket is None: | ||||
|             raise ServerNotConnectedError, "Not connected." | ||||
|             raise ServerNotConnectedError("Not connected.") | ||||
|         try: | ||||
|             if self.ssl: | ||||
|                 self.ssl.write(string + "\r\n") | ||||
| @ -790,7 +855,7 @@ class ServerConnection(Connection): | ||||
|                 self.socket.send(string + "\r\n") | ||||
|             if DEBUG: | ||||
|                 print "TO SERVER:", string | ||||
|         except socket.error, x: | ||||
|         except socket.error: | ||||
|             # Ouch! | ||||
|             self.disconnect("Connection reset by peer.") | ||||
| 
 | ||||
| @ -862,7 +927,7 @@ class DCCConnection(Connection): | ||||
|     method on an IRC object. | ||||
|     """ | ||||
|     def __init__(self, irclibobj, dcctype): | ||||
|         Connection.__init__(self, irclibobj) | ||||
|         super(DCCConnection, self).__init__(irclibobj) | ||||
|         self.connected = 0 | ||||
|         self.passive = 0 | ||||
|         self.dcctype = dcctype | ||||
| @ -889,7 +954,7 @@ class DCCConnection(Connection): | ||||
|         try: | ||||
|             self.socket.connect((self.peeraddress, self.peerport)) | ||||
|         except socket.error, x: | ||||
|             raise DCCConnectionError, "Couldn't connect to socket: %s" % x | ||||
|             raise DCCConnectionError("Couldn't connect to socket: %s" % x) | ||||
|         self.connected = 1 | ||||
|         if self.irclibobj.fn_to_add_socket: | ||||
|             self.irclibobj.fn_to_add_socket(self.socket) | ||||
| @ -914,7 +979,7 @@ class DCCConnection(Connection): | ||||
|             self.localaddress, self.localport = self.socket.getsockname() | ||||
|             self.socket.listen(10) | ||||
|         except socket.error, x: | ||||
|             raise DCCConnectionError, "Couldn't bind socket: %s" % x | ||||
|             raise DCCConnectionError("Couldn't bind socket: %s" % x) | ||||
|         return self | ||||
| 
 | ||||
|     def disconnect(self, message=""): | ||||
| @ -930,7 +995,7 @@ class DCCConnection(Connection): | ||||
|         self.connected = 0 | ||||
|         try: | ||||
|             self.socket.close() | ||||
|         except socket.error, x: | ||||
|         except socket.error: | ||||
|             pass | ||||
|         self.socket = None | ||||
|         self.irclibobj._handle_event( | ||||
| @ -955,8 +1020,8 @@ class DCCConnection(Connection): | ||||
|             return | ||||
| 
 | ||||
|         try: | ||||
|             new_data = self.socket.recv(2**14) | ||||
|         except socket.error, x: | ||||
|             new_data = self.socket.recv(2 ** 14) | ||||
|         except socket.error: | ||||
|             # The server hung up. | ||||
|             self.disconnect("Connection reset by peer") | ||||
|             return | ||||
| @ -972,7 +1037,7 @@ class DCCConnection(Connection): | ||||
| 
 | ||||
|             # Save the last, unfinished line. | ||||
|             self.previous_buffer = chunks[-1] | ||||
|             if len(self.previous_buffer) > 2**14: | ||||
|             if len(self.previous_buffer) > 2 ** 14: | ||||
|                 # Bad peer! Naughty peer! | ||||
|                 self.disconnect() | ||||
|                 return | ||||
| @ -1010,11 +1075,11 @@ class DCCConnection(Connection): | ||||
|                 self.socket.send("\n") | ||||
|             if DEBUG: | ||||
|                 print "TO PEER: %s\n" % string | ||||
|         except socket.error, x: | ||||
|         except socket.error: | ||||
|             # Ouch! | ||||
|             self.disconnect("Connection reset by peer.") | ||||
| 
 | ||||
| class SimpleIRCClient: | ||||
| class SimpleIRCClient(object): | ||||
|     """A simple single-server IRC client class. | ||||
| 
 | ||||
|     This is an example of an object-oriented wrapper of the IRC | ||||
| @ -1044,6 +1109,9 @@ class SimpleIRCClient: | ||||
| 
 | ||||
|     def _dispatcher(self, c, e): | ||||
|         """[Internal]""" | ||||
|         if DEBUG: | ||||
|             print("irclib.py:_dispatcher:%s" % e.eventtype()) | ||||
| 
 | ||||
|         m = "on_" + e.eventtype() | ||||
|         if hasattr(self, m): | ||||
|             getattr(self, m)(c, e) | ||||
| @ -1114,7 +1182,7 @@ class SimpleIRCClient: | ||||
|         self.ircobj.process_forever() | ||||
| 
 | ||||
| 
 | ||||
| class Event: | ||||
| class Event(object): | ||||
|     """Class representing an IRC event.""" | ||||
|     def __init__(self, eventtype, source, target, arguments=None): | ||||
|         """Constructor of Event objects. | ||||
| @ -1184,16 +1252,6 @@ def mask_matches(nick, mask): | ||||
| 
 | ||||
| _special = "-[]\\`^{}" | ||||
| nick_characters = string.ascii_letters + string.digits + _special | ||||
| _ircstring_translation = string.maketrans(string.ascii_uppercase + "[]\\^", | ||||
|                                           string.ascii_lowercase + "{}|~") | ||||
| 
 | ||||
| def irc_lower(s): | ||||
|     """Returns a lowercased string. | ||||
| 
 | ||||
|     The definition of lowercased comes from the IRC specification (RFC | ||||
|     1459). | ||||
|     """ | ||||
|     return s.translate(_ircstring_translation) | ||||
| 
 | ||||
| def _ctcp_dequote(message): | ||||
|     """[Internal] Dequote a message according to CTCP specifications. | ||||
| @ -1229,14 +1287,14 @@ def _ctcp_dequote(message): | ||||
| 
 | ||||
|         messages = [] | ||||
|         i = 0 | ||||
|         while i < len(chunks)-1: | ||||
|         while i < len(chunks) - 1: | ||||
|             # Add message if it's non-empty. | ||||
|             if len(chunks[i]) > 0: | ||||
|                 messages.append(chunks[i]) | ||||
| 
 | ||||
|             if i < len(chunks)-2: | ||||
|             if i < len(chunks) - 2: | ||||
|                 # Aye!  CTCP tagged data ahead! | ||||
|                 messages.append(tuple(chunks[i+1].split(" ", 1))) | ||||
|                 messages.append(tuple(chunks[i + 1].split(" ", 1))) | ||||
| 
 | ||||
|             i = i + 2 | ||||
| 
 | ||||
| @ -1308,12 +1366,12 @@ def parse_nick_modes(mode_string): | ||||
|     """Parse a nick mode string. | ||||
| 
 | ||||
|     The function returns a list of lists with three members: sign, | ||||
|     mode and argument.  The sign is \"+\" or \"-\".  The argument is | ||||
|     mode and argument.  The sign is "+" or "-".  The argument is | ||||
|     always None. | ||||
| 
 | ||||
|     Example: | ||||
| 
 | ||||
|     >>> irclib.parse_nick_modes(\"+ab-c\") | ||||
|     >>> parse_nick_modes("+ab-c") | ||||
|     [['+', 'a', None], ['+', 'b', None], ['-', 'c', None]] | ||||
|     """ | ||||
| 
 | ||||
| @ -1323,12 +1381,12 @@ def parse_channel_modes(mode_string): | ||||
|     """Parse a channel mode string. | ||||
| 
 | ||||
|     The function returns a list of lists with three members: sign, | ||||
|     mode and argument.  The sign is \"+\" or \"-\".  The argument is | ||||
|     None if mode isn't one of \"b\", \"k\", \"l\", \"v\" or \"o\". | ||||
|     mode and argument.  The sign is "+" or "-".  The argument is | ||||
|     None if mode isn't one of "b", "k", "l", "v" or "o". | ||||
| 
 | ||||
|     Example: | ||||
| 
 | ||||
|     >>> irclib.parse_channel_modes(\"+ab-c foo\") | ||||
|     >>> parse_channel_modes("+ab-c foo") | ||||
|     [['+', 'a', None], ['+', 'b', 'foo'], ['-', 'c', None]] | ||||
|     """ | ||||
| 
 | ||||
| @ -1354,7 +1412,7 @@ def _parse_modes(mode_string, unary_modes=""): | ||||
|         if ch in "+-": | ||||
|             sign = ch | ||||
|         elif ch == " ": | ||||
|             collecting_arguments = 1 | ||||
|             pass | ||||
|         elif ch in unary_modes: | ||||
|             if len(args) >= arg_count + 1: | ||||
|                 modes.append([sign, ch, args[arg_count]]) | ||||
| @ -1494,7 +1552,7 @@ numeric_events = { | ||||
|     "423": "noadmininfo", | ||||
|     "424": "fileerror", | ||||
|     "431": "nonicknamegiven", | ||||
|     "432": "erroneusnickname", # Thiss iz how its speld in thee RFC. | ||||
|     "432": "erroneusnickname",  # Thiss iz how its speld in thee RFC. | ||||
|     "433": "nicknameinuse", | ||||
|     "436": "nickcollision", | ||||
|     "437": "unavailresource",  # "Nick temporally unavailable" | ||||
| @ -1509,7 +1567,7 @@ numeric_events = { | ||||
|     "462": "alreadyregistered", | ||||
|     "463": "nopermforhost", | ||||
|     "464": "passwdmismatch", | ||||
|     "465": "yourebannedcreep", # I love this one... | ||||
|     "465": "yourebannedcreep",  # I love this one... | ||||
|     "466": "youwillbebanned", | ||||
|     "467": "keyset", | ||||
|     "471": "channelisfull", | ||||
| @ -1559,3 +1617,104 @@ protocol_events = [ | ||||
| ] | ||||
| 
 | ||||
| all_events = generated_events + protocol_events + numeric_events.values() | ||||
| 
 | ||||
| # from jaraco.util.itertools | ||||
| def always_iterable(item): | ||||
|     """ | ||||
|     Given an object, always return an iterable. If the item is not | ||||
|     already iterable, return a tuple containing only the item. | ||||
| 
 | ||||
|     >>> always_iterable([1,2,3]) | ||||
|     [1, 2, 3] | ||||
|     >>> always_iterable('foo') | ||||
|     ('foo',) | ||||
|     >>> always_iterable(None) | ||||
|     (None,) | ||||
|     >>> always_iterable(xrange(10)) | ||||
|     xrange(10) | ||||
|     """ | ||||
|     if isinstance(item, basestring) or not hasattr(item, '__iter__'): | ||||
|         item = item, | ||||
|     return item | ||||
| 
 | ||||
| # from jaraco.util.string | ||||
| class FoldedCase(str): | ||||
|     """ | ||||
|     A case insensitive string class; behaves just like str | ||||
|     except compares equal when the only variation is case. | ||||
|     >>> s = FoldedCase('hello world') | ||||
| 
 | ||||
|     >>> s == 'Hello World' | ||||
|     True | ||||
| 
 | ||||
|     >>> 'Hello World' == s | ||||
|     True | ||||
| 
 | ||||
|     >>> s.index('O') | ||||
|     4 | ||||
| 
 | ||||
|     >>> s.split('O') | ||||
|     ['hell', ' w', 'rld'] | ||||
| 
 | ||||
|     >>> names = map(FoldedCase, ['GAMMA', 'alpha', 'Beta']) | ||||
|     >>> names.sort() | ||||
|     >>> names | ||||
|     ['alpha', 'Beta', 'GAMMA'] | ||||
|     """ | ||||
|     def __lt__(self, other): | ||||
|         return self.lower() < other.lower() | ||||
| 
 | ||||
|     def __gt__(self, other): | ||||
|         return self.lower() > other.lower() | ||||
| 
 | ||||
|     def __eq__(self, other): | ||||
|         return self.lower() == other.lower() | ||||
| 
 | ||||
|     def __hash__(self): | ||||
|         return hash(self.lower()) | ||||
| 
 | ||||
|     # cache lower since it's likely to be called frequently. | ||||
|     def lower(self): | ||||
|         self._lower = super(FoldedCase, self).lower() | ||||
|         self.lower = lambda: self._lower | ||||
|         return self._lower | ||||
| 
 | ||||
|     def index(self, sub): | ||||
|         return self.lower().index(sub.lower()) | ||||
| 
 | ||||
|     def split(self, splitter=' ', maxsplit=0): | ||||
|         pattern = re.compile(re.escape(splitter), re.I) | ||||
|         return pattern.split(self, maxsplit) | ||||
| 
 | ||||
| class IRCFoldedCase(FoldedCase): | ||||
|     """ | ||||
|     A version of FoldedCase that honors the IRC specification for lowercased | ||||
|     strings (RFC 1459). | ||||
| 
 | ||||
|     >>> IRCFoldedCase('Foo^').lower() | ||||
|     'foo~' | ||||
|     >>> IRCFoldedCase('[this]') == IRCFoldedCase('{THIS}') | ||||
|     True | ||||
|     """ | ||||
|     translation = string.maketrans( | ||||
|         string.ascii_uppercase + r"[]\^", | ||||
|         string.ascii_lowercase + r"{}|~", | ||||
|     ) | ||||
| 
 | ||||
|     def lower(self): | ||||
|         return self.translate(self.translation) | ||||
| 
 | ||||
| # for compatibility | ||||
| def irc_lower(str): | ||||
|     return IRCFoldedCase(str).lower() | ||||
| 
 | ||||
| def total_seconds(td): | ||||
|     """ | ||||
|     Python 2.7 adds a total_seconds method to timedelta objects. | ||||
|     See http://docs.python.org/library/datetime.html#datetime.timedelta.total_seconds | ||||
|     """ | ||||
|     try: | ||||
|         result = td.total_seconds() | ||||
|     except AttributeError: | ||||
|         result = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 | ||||
|     return result | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user