summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLance Stout <lancestout@gmail.com>2011-08-04 20:20:22 -0700
committerLance Stout <lancestout@gmail.com>2011-08-04 20:22:07 -0700
commit4d8933abdf4a190493f2762d423f469f6d8b30a9 (patch)
treee538d4b0c390b54e411e3d67f8fb09ff3ab28bd5
parent6eac0606cf5cd0b7777eb86b77ed1b061dd4b658 (diff)
downloadslixmpp-4d8933abdf4a190493f2762d423f469f6d8b30a9.tar.gz
slixmpp-4d8933abdf4a190493f2762d423f469f6d8b30a9.tar.bz2
slixmpp-4d8933abdf4a190493f2762d423f469f6d8b30a9.tar.xz
slixmpp-4d8933abdf4a190493f2762d423f469f6d8b30a9.zip
Actually, we can work around needing dateutil.
If dateutil is present, we'll use that. If not, we'll use some regexes from the fixed_datetime module.
-rw-r--r--LICENSE31
-rw-r--r--README3
-rw-r--r--sleekxmpp/plugins/__init__.py20
-rw-r--r--sleekxmpp/plugins/xep_0082.py13
-rw-r--r--sleekxmpp/plugins/xep_0202/__init__.py22
-rw-r--r--sleekxmpp/plugins/xep_0202/stanza.py7
-rw-r--r--sleekxmpp/thirdparty/__init__.py1
-rw-r--r--sleekxmpp/thirdparty/mini_dateutil.py267
8 files changed, 310 insertions, 54 deletions
diff --git a/LICENSE b/LICENSE
index b981b0ad..df302d00 100644
--- a/LICENSE
+++ b/LICENSE
@@ -25,6 +25,7 @@ Licences of Bundled Third Pary Code
-----------------------------------
dateutil - Extensions to the standard python 2.3+ datetime module.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copyright (c) 2003-2011 - Gustavo Niemeyer <gustavo@niemeyer.net>
@@ -55,9 +56,38 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+fixed_datetime
+~~~~~~~~~~~~~~
+
+Copyright (c) 2008, Red Innovation Ltd., Finland
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Red Innovation nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY RED INNOVATION ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL RED INNOVATION BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
OrderedDict - A port of the Python 2.7+ OrderedDict to Python 2.6
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copyright (c) 2009 Raymond Hettinger
@@ -85,6 +115,7 @@ subject to the following conditions:
SUELTA – A PURE-PYTHON SASL CLIENT LIBRARY
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This software is subject to "The MIT License"
diff --git a/README b/README
index aab5ae8c..8a85365c 100644
--- a/README
+++ b/README
@@ -9,9 +9,6 @@ We try to keep requirements to a minimum, but we suggest that you install http:/
If you do not install this library, you may need to specify the server/port for services that use SRV records (like GTalk).
"sudo pip install dnspython" on a *nix system with pip installed.
-Two time related plugins (XEP-0082 and XEP-0202) also require the dateutil package, but that is not a hard requirement if you
-don't need those plugins.
-
SleekXMPP has several design goals/philosophies:
- Low number of dependencies.
- Every XEP as a plugin.
diff --git a/sleekxmpp/plugins/__init__.py b/sleekxmpp/plugins/__init__.py
index 21a05fe0..3f90f059 100644
--- a/sleekxmpp/plugins/__init__.py
+++ b/sleekxmpp/plugins/__init__.py
@@ -6,20 +6,6 @@
See the file LICENSE for copying permission.
"""
__all__ = ['xep_0004', 'xep_0009', 'xep_0012', 'xep_0030', 'xep_0033',
- 'xep_0045', 'xep_0050', 'xep_0060', 'xep_0066', 'xep_0085',
- 'xep_0086', 'xep_0092', 'xep_0128', 'xep_0199', 'xep_0203',
- 'xep_0224', 'xep_0249', 'gmail_notify']
-
-# Some plugins may require external dependencies beyond what the
-# core SleekXMPP installation requires. Thus they should only by
-# imported automatically if those dependecies are met.
-
-HAVE_DATEUTIL = True
-try:
- import dateutil
-except:
- HAVE_DATEUTIL = False
-
-if HAVE_DATEUTIL:
- __all__.append('xep_0082')
- __all__.append('xep_0202')
+ 'xep_0045', 'xep_0050', 'xep_0060', 'xep_0066', 'xep_0082',
+ 'xep_0085', 'xep_0086', 'xep_0092', 'xep_0128', 'xep_0199',
+ 'xep_0082', 'xep_0203', 'xep_0224', 'xep_0249', 'gmail_notify']
diff --git a/sleekxmpp/plugins/xep_0082.py b/sleekxmpp/plugins/xep_0082.py
index e78a50ad..d3c4cc56 100644
--- a/sleekxmpp/plugins/xep_0082.py
+++ b/sleekxmpp/plugins/xep_0082.py
@@ -10,13 +10,7 @@ import logging
import datetime as dt
from sleekxmpp.plugins.base import base_plugin
-
-try:
- from dateutil import parser
- from dateutil.tz import tzoffset, tzutc
-except e:
- log = logging.getLogger(__name__)
- log.warning("XEP-0082 plugin requires dateutil")
+from sleekxmpp.thirdparty import tzutc, tzoffset, parse_iso
# =====================================================================
@@ -31,7 +25,8 @@ def parse(time_str):
Arguments:
time_str -- A formatted timestamp string.
"""
- return parser.parse(time_str)
+ return parse_iso(time_str)
+
def format_date(time_obj):
"""
@@ -52,7 +47,7 @@ def format_time(time_obj):
Return a formatted string version of a time object.
format:
- hh:mm:ss[.sss][TZD
+ hh:mm:ss[.sss][TZD]
arguments:
time_obj -- A time or datetime object.
diff --git a/sleekxmpp/plugins/xep_0202/__init__.py b/sleekxmpp/plugins/xep_0202/__init__.py
index 3fb4744d..a34b2376 100644
--- a/sleekxmpp/plugins/xep_0202/__init__.py
+++ b/sleekxmpp/plugins/xep_0202/__init__.py
@@ -6,23 +6,7 @@
See the file LICENSE for copying permission.
"""
-import logging
-import sleekxmpp
-
-log = logging.getLogger(__name__)
-
-
-HAVE_DATEUTIL = True
-try:
- import dateutil
-except:
- HAVE_DATEUTIL = False
-
-
-if HAVE_DATEUTIL:
- from sleekxmpp.plugins.xep_0202 import stanza
- from sleekxmpp.plugins.xep_0202.stanza import EntityTime
- from sleekxmpp.plugins.xep_0202.time import xep_0202
-else:
- log.warning("XEP-0202 requires the dateutil package")
+from sleekxmpp.plugins.xep_0202 import stanza
+from sleekxmpp.plugins.xep_0202.stanza import EntityTime
+from sleekxmpp.plugins.xep_0202.time import xep_0202
diff --git a/sleekxmpp/plugins/xep_0202/stanza.py b/sleekxmpp/plugins/xep_0202/stanza.py
index 72ab403d..b6ccc960 100644
--- a/sleekxmpp/plugins/xep_0202/stanza.py
+++ b/sleekxmpp/plugins/xep_0202/stanza.py
@@ -11,12 +11,7 @@ import datetime as dt
from sleekxmpp.xmlstream import ElementBase
from sleekxmpp.plugins import xep_0082
-
-try:
- from dateutil.tz import tzutc
-except:
- log = logging.getLogger(__name__)
- log.warning("XEP-0202 plugin requies dateutil package")
+from sleekxmpp.thirdparty import tzutc, tzoffset
class EntityTime(ElementBase):
diff --git a/sleekxmpp/thirdparty/__init__.py b/sleekxmpp/thirdparty/__init__.py
index 3eb6ad73..1c7bf651 100644
--- a/sleekxmpp/thirdparty/__init__.py
+++ b/sleekxmpp/thirdparty/__init__.py
@@ -4,3 +4,4 @@ except:
from sleekxmpp.thirdparty.ordereddict import OrderedDict
from sleekxmpp.thirdparty import suelta
+from sleekxmpp.thirdparty.mini_dateutil import tzutc, tzoffset, parse_iso
diff --git a/sleekxmpp/thirdparty/mini_dateutil.py b/sleekxmpp/thirdparty/mini_dateutil.py
new file mode 100644
index 00000000..bfb86305
--- /dev/null
+++ b/sleekxmpp/thirdparty/mini_dateutil.py
@@ -0,0 +1,267 @@
+# This module is a very stripped down version of the dateutil
+# package for when dateutil has not been installed. As a replacement
+# for dateutil.parser.parse, the parsing methods from
+# http://blog.mfabrik.com/2008/06/30/relativity-of-time-shortcomings-in-python-datetime-and-workaround/
+
+#As such, the following copyrights and licenses applies:
+
+
+# dateutil - Extensions to the standard python 2.3+ datetime module.
+#
+# Copyright (c) 2003-2011 - Gustavo Niemeyer <gustavo@niemeyer.net>
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+# fixed_dateime
+#
+# Copyright (c) 2008, Red Innovation Ltd., Finland
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Red Innovation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED INNOVATION ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL RED INNOVATION BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+import re
+import datetime
+
+
+ZERO = datetime.timedelta(0)
+
+
+try:
+ from dateutil.parser import parse as parse_iso
+ from dateutil.tz import tzoffset, tzutc
+except:
+ # As a stopgap, define the two timezones here based
+ # on the dateutil code.
+
+ class tzutc(datetime.tzinfo):
+
+ def utcoffset(self, dt):
+ return ZERO
+
+ def dst(self, dt):
+ return ZERO
+
+ def tzname(self, dt):
+ return "UTC"
+
+ def __eq__(self, other):
+ return (isinstance(other, tzutc) or
+ (isinstance(other, tzoffset) and other._offset == ZERO))
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __repr__(self):
+ return "%s()" % self.__class__.__name__
+
+ __reduce__ = object.__reduce__
+
+ class tzoffset(datetime.tzinfo):
+
+ def __init__(self, name, offset):
+ self._name = name
+ self._offset = datetime.timedelta(seconds=offset)
+
+ def utcoffset(self, dt):
+ return self._offset
+
+ def dst(self, dt):
+ return ZERO
+
+ def tzname(self, dt):
+ return self._name
+
+ def __eq__(self, other):
+ return (isinstance(other, tzoffset) and
+ self._offset == other._offset)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __repr__(self):
+ return "%s(%s, %s)" % (self.__class__.__name__,
+ repr(self._name),
+ self._offset.days*86400+self._offset.seconds)
+
+ __reduce__ = object.__reduce__
+
+
+ _fixed_offset_tzs = { }
+ UTC = tzutc()
+
+ def _get_fixed_offset_tz(offsetmins):
+ """For internal use only: Returns a tzinfo with
+ the given fixed offset. This creates only one instance
+ for each offset; the zones are kept in a dictionary"""
+
+ if offsetmins == 0:
+ return UTC
+
+ if not _fixed_offset_tzs.has_key(offsetmins):
+ if offsetmins < 0:
+ sign = '-'
+ absoff = -offsetmins
+ else:
+ sign = '+'
+ absoff = offsetmins
+
+ name = "UTC%s%02d:%02d" % (sign, int(absoff / 60), absoff % 60)
+ inst = tzoffset(offsetmins, name)
+ _fixed_offset_tzs[offsetmins] = inst
+
+ return _fixed_offset_tzs[offsetmins]
+
+
+ _iso8601_parser = re.compile("""
+ ^
+ (?P<year> [0-9]{4})?(?P<ymdsep>-?)?
+ (?P<month>[0-9]{2})?(?P=ymdsep)?
+ (?P<day> [0-9]{2})?
+
+ (?: # time part... optional... at least hour must be specified
+ (?:T|\s+)?
+ (?P<hour>[0-9]{2})
+ (?:
+ # minutes, separated with :, or none, from hours
+ (?P<hmssep>[:]?)
+ (?P<minute>[0-9]{2})
+ (?:
+ # same for seconds, separated with :, or none, from hours
+ (?P=hmssep)
+ (?P<second>[0-9]{2})
+ )?
+ )?
+
+ # fractions
+ (?: [,.] (?P<frac>[0-9]{1,10}))?
+
+ # timezone, Z, +-hh or +-hh:?mm. MUST BE, but complain if not there.
+ (
+ (?P<tzempty>Z)
+ |
+ (?P<tzh>[+-][0-9]{2})
+ (?: :? # optional separator
+ (?P<tzm>[0-9]{2})
+ )?
+ )?
+ )?
+ $
+ """, re.X) # """
+
+ def parse_iso(timestamp):
+ """Internal function for parsing a timestamp in
+ ISO 8601 format"""
+
+ timestamp = timestamp.strip()
+
+ m = _iso8601_parser.match(timestamp)
+ if not m:
+ raise ValueError("Not a proper ISO 8601 timestamp!: %s" % timestamp)
+
+ vals = m.groupdict()
+ def_vals = {'year': 1970, 'month': 1, 'day': 1}
+ for key in vals:
+ if vals[key] is None:
+ vals[key] = def_vals.get(key, 0)
+ elif key not in ['ymdsep', 'hmssep', 'tzempty']:
+ vals[key] = int(vals[key])
+
+ year = vals['year']
+ month = vals['month']
+ day = vals['day']
+
+ h, min, s, us = None, None, None, 0
+ frac = 0
+ if m.group('tzempty') == None and m.group('tzh') == None:
+ raise ValueError("Not a proper ISO 8601 timestamp: " +
+ "missing timezone (Z or +hh[:mm])!")
+
+ if m.group('frac'):
+ frac = m.group('frac')
+ power = len(frac)
+ frac = long(frac) / 10.0 ** power
+
+ if m.group('hour'):
+ h = vals['hour']
+
+ if m.group('minute'):
+ min = vals['minute']
+
+ if m.group('second'):
+ s = vals['second']
+
+ if frac != None:
+ # ok, fractions of hour?
+ if min == None:
+ frac, min = _math.modf(frac * 60.0)
+ min = int(min)
+
+ # fractions of second?
+ if s == None:
+ frac, s = _math.modf(frac * 60.0)
+ s = int(s)
+
+ # and extract microseconds...
+ us = int(frac * 1000000)
+
+ if m.group('tzempty') == 'Z':
+ offsetmins = 0
+ else:
+ # timezone: hour diff with sign
+ offsetmins = vals['tzh'] * 60
+ tzm = m.group('tzm')
+
+ # add optional minutes
+ if tzm != None:
+ tzm = long(tzm)
+ offsetmins += tzm if offsetmins > 0 else -tzm
+
+ tz = _get_fixed_offset_tz(offsetmins)
+ return datetime.datetime(year, month, day, h, min, s, us, tz)