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.
This commit is contained in:
Lance Stout 2011-08-04 20:20:22 -07:00
parent 6eac0606cf
commit 4d8933abdf
8 changed files with 310 additions and 54 deletions

31
LICENSE
View file

@ -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"

3
README
View file

@ -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.

View file

@ -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']

View file

@ -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.

View file

@ -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

View file

@ -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):

View file

@ -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

267
sleekxmpp/thirdparty/mini_dateutil.py vendored Normal file
View file

@ -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)