Initial implementation of blender benchmark addon

Just some glue logic to query progress and results from benchmark.

Needed to move files around, so oth standalone and addon are happy.
This commit is contained in:
2018-08-02 17:38:10 +02:00
parent de682f8e8e
commit 7f42fdf5e8
40 changed files with 391 additions and 8998 deletions

203
benchmark/__init__.py Normal file
View File

@@ -0,0 +1,203 @@
bl_info = {
"name": "Benchmark",
"author": "Sergey Sharybin",
"blender": (2, 80, 0),
"location": "",
"description": "Addon to provide benchmark functionality",
"warning": "",
"wiki_url": "",
"support": 'OFFICIAL',
"category": "Benchmark",
}
import datetime
import json
import os
import sys
import bpy
from threading import Thread
from bpy.props import IntProperty, FloatProperty
from .foundation import (benchrunner,
buildbot,
config,
context,
logger,
system_info,
util)
class ProgressProviderSink:
current_progress = 0.0
current_step = ''
def __init__(self):
self.current_progress = 0.0
self.current_step = ''
def progress(self, count, total, prefix="", suffix=""):
if total != 0:
self.current_progress = float(count) / float(total)
else:
self.current_progress = 0.0
def clear(self):
pass
def step(self, step_name):
self.current_step = step_name
class LoggerProviderSink:
def HEADER(self, *args):
pass
def WARNING(self, *args):
pass
def ERROR(self, *args):
pass
def OK(self, *args):
pass
def BOLD(self, *args):
pass
def INFO(self, *args):
pass
def DEBUG(self, *args):
pass
def FATAL(self, *args):
pass
def getResultJSONString(ctx, results):
# Convert custom classes to dictionaries for easier JSON dump.
json_results = results
stats = json_results['stats']
for scene in ctx.scenes:
if scene not in stats:
continue
if stats[scene]:
stats[scene] = stats[scene].asDict()
stats[scene]['result'] = 'OK'
else:
stats[scene] = {'result': 'CRASH'}
return json.dumps(json_results, sort_keys=True, indent=2)
def benchmark_thread(ctx):
# This is actual device configuration which is used to render the
# benchmark scene.
blender_device_info = benchrunner.benchmarkGetDeviceInfo(ctx)
if not blender_device_info['device_type']:
# TODO(sergey): Report an error somehow.
return
all_stats = benchrunner.benchmarkAll(ctx)
# Gather all information together.
timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat()
old_executable = sys.executable
sys.executable = bpy.app.binary_path_python
results = {
"timestamp": timestamp,
"blender_version": system_info.getBlenderVersion(ctx),
"system_info": system_info.gatherSystemInfo(ctx),
"device_info": blender_device_info,
"stats": all_stats if all_stats else {}
}
sys.executable = old_executable
json_string = getResultJSONString(ctx, results)
print(json_string)
class BenchmarkOperator(bpy.types.Operator):
bl_idname = "wm.benchmark"
bl_label = "benchmark Operator"
first_mouse_x: IntProperty()
first_value: FloatProperty()
benchmark_context = None
thread = None
timer = None
progress_provider = None
logger_provider = None
def setup_sink(self):
self.progress_provider = ProgressProviderSink()
self.logger_provider = LoggerProviderSink()
foundation.progress.setProvider(self.progress_provider)
foundation.logger.setProvider(self.logger_provider)
def update_status(self):
rounded = int(self.progress_provider.current_progress * 100) / 100
step = self.progress_provider.current_step
print(f"{step}: {rounded}")
def done(self, context):
wm = context.window_manager
wm.event_timer_remove(self.timer)
# Restore all modifications to the benchmark foundation.
foundation.progress.restoreDefaultProvider()
foundation.logger.restoreDefaultProvider()
# Destroy objects of sinks.
del self.progress_provider
del self.logger_provider
self.progress_provider = None
self.logger_provider = None
def modal(self, context, event):
if event.type == 'TIMER':
if self.thread.is_alive():
self.update_status()
return {'PASS_THROUGH'}
else:
self.done(context)
return {'FINISHED'}
return {'PASS_THROUGH'}
def invoke(self, context, event):
# Before doing anything, make sure we have all sinks set up, so we do
# not miss any progress report.
self.setup_sink()
wm = context.window_manager
script_directory = os.path.dirname(os.path.realpath(__file__))
configure_script = os.path.join(script_directory, "configure.py")
ctx = foundation.context.Context()
ctx.blender = "<blender>"
ctx.configure_script = configure_script
ctx.scenes = ["<monkey>"]
ctx.scenes_dir = "<scenes>"
ctx.device_type = 'CPU'
# Only applies for GPU, should match Cycles name
ctx.device_name = ""
# Set this to True when having multiple GPUs of same name and only
# one of the mis to be enabled. Or when requesting GPU render without
# specifying GPU name.
ctx.device_single = True
# ctx.image_output_dir = "/tmp/"
self.benchmark_context = ctx
# Create thread for the actual benchmark.
self.thread = Thread(target = benchmark_thread,
args = (self.benchmark_context, ))
self.thread.start()
# Create timer to query thread status
self.timer = wm.event_timer_add(0.1, context.window)
# Register self as modal.
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def register():
bpy.utils.register_class(BenchmarkOperator)
def unregister():
bpy.utils.unregister_class(BenchmarkOperator)
#if __name__ == "__main__":
# register()

View File

@@ -5,7 +5,6 @@ import sys
SCRIPT_PATH = os.path.realpath(__file__)
SCRIPT_DIR = os.path.dirname(SCRIPT_PATH)
sys.path.append(os.path.join(SCRIPT_DIR, "third_party"))
import argparse
import datetime
@@ -364,8 +363,8 @@ def main():
print("Using device name override: {}" .
format(farm_config["device_name_override"]))
# Print prelmiinary information.
blender_dvice_info = benchrunner.benchmarkGetDeviceInfo(ctx)
if not blender_dvice_info['device_type']:
blender_device_info = benchrunner.benchmarkGetDeviceInfo(ctx)
if not blender_device_info['device_type']:
logger.ERROR("Requested device can not be enabled in Blender.")
logger.INFO("Requested device details:")
logger.INFO(" Device type: {}" . format(ctx.device_type))
@@ -374,9 +373,9 @@ def main():
else:
logger.INFO("Configured device details:")
logger.INFO(" Device type: {}" .
format(blender_dvice_info["device_type"]))
format(blender_device_info["device_type"]))
logger.INFO(" Compute devices:")
for compute_device in blender_dvice_info["compute_devices"]:
for compute_device in blender_device_info["compute_devices"]:
logger.INFO(" {}" . format(compute_device))
# Run benchmark.
all_stats = benchrunner.benchmarkAll(ctx)
@@ -386,7 +385,7 @@ def main():
"timestamp": timestamp,
"blender_version": system_info.getBlenderVersion(ctx),
"system_info": system_info.gatherSystemInfo(ctx),
"device_info": blender_dvice_info,
"device_info": blender_device_info,
"stats": all_stats if all_stats else {}
}
if farm_config["device_name_override"]:

View File

@@ -2,10 +2,10 @@ import os
import time
import subprocess
from foundation import (logger,
progress,
stats,
util)
from . import (logger,
progress,
stats,
util)
def constructBenchmarkCommand(ctx, scene, blendfile, output_folder, cfra):
@@ -36,7 +36,7 @@ def constructBenchmarkCommand(ctx, scene, blendfile, output_folder, cfra):
def benchmarkBlenderWatched(command):
# Run Blender with configured command line.
logger.DEBUG("About to execuet command: {}" . format(command))
logger.DEBUG("About to execute command: {}" . format(command))
start_time = time.time()
process = subprocess.Popen(command,
stdout=subprocess.PIPE,
@@ -85,7 +85,7 @@ def benchmarkScene(ctx, scene):
cfra = util.queryCurrentFrame(blendfile)
command = constructBenchmarkCommand(ctx, scene, blendfile, "/tmp/", cfra)
logger.DEBUG("Command for rendering: {}" . format(command))
progress.step('WARM_UP')
logger.INFO("> Warm-up round, making sure everything is ready " +
"(this might take several minutes).")
warmup_command = command + ['--benchmark-warmup']
@@ -97,6 +97,7 @@ def benchmarkScene(ctx, scene):
if os.path.exists(full_image_output):
os.remove(full_image_output)
# TODO(sergey): Consider doing several passes.
progress.step('RUN')
logger.INFO("> Doing real benchmark pass now.")
stats = benchmarkBlenderWatched(command)
# Rename file to more sensible name.
@@ -109,6 +110,7 @@ def benchmarkScene(ctx, scene):
logger.INFO("Total render time: {}" . format(
util.humanReadableTimeDifference(
stats.pipeline_render_time)))
progress.step('')
return stats

View File

@@ -1,6 +1,6 @@
import configparser
import foundation
from foundation import util
from .. import foundation
from ..foundation import util
import os

View File

@@ -1,6 +1,6 @@
import os
from foundation import logger
from . import logger
class Context:

View File

@@ -23,6 +23,86 @@ class COLORS_ANSI:
VERBOSE = False
COLORS = COLORS_DUMMY
class DefaultLoggerProvider:
def HEADER(self, *args):
print(COLORS.HEADER + COLORS.BOLD, end="")
print(*args, end="")
print(COLORS.ENDC)
def WARNING(self, *args):
print(COLORS.WARNING + COLORS.BOLD, end="")
print(*args, end="")
print(COLORS.ENDC)
def ERROR(self, *args):
print(COLORS.FAIL + COLORS.BOLD, end="")
print(*args, end="")
print(COLORS.ENDC)
def OK(self, *args):
print(COLORS.OKGREEN + COLORS.BOLD, end="")
print(*args, end="")
print(COLORS.ENDC)
def BOLD(self, *args):
print(COLORS.BOLD, end="")
print(*args, end="")
print(COLORS.ENDC)
def INFO(self, *args):
print(*args)
def DEBUG(self, *args):
# TODO(sergey): Add check that debug is enabled.
if False:
print(*args)
def FATAL(self, *args):
import sys
ERROR(*args)
sys.exit(1)
LOGGER_PROVIDER = DefaultLoggerProvider()
def HEADER(*args):
LOGGER_PROVIDER.HEADER(*args)
def WARNING(*args):
LOGGER_PROVIDER.WARNING(*args)
def ERROR(*args):
LOGGER_PROVIDER.ERROR(*args)
def OK(*args):
LOGGER_PROVIDER.OK(*args)
def BOLD(*args):
LOGGER_PROVIDER.BOLD(*args)
def INFO(*args):
LOGGER_PROVIDER.INFO(*args)
def DEBUG(*args):
LOGGER_PROVIDER.DEBUG(*args)
def FATAL(*args):
LOGGER_PROVIDER.FATAL(*args)
def supportsColor():
"""
@@ -43,53 +123,17 @@ def supportsColor():
return True
def HEADER(*args):
print(COLORS.HEADER + COLORS.BOLD, end="")
print(*args, end="")
print(COLORS.ENDC)
def WARNING(*args):
print(COLORS.WARNING + COLORS.BOLD, end="")
print(*args, end="")
print(COLORS.ENDC)
def ERROR(*args):
print(COLORS.FAIL + COLORS.BOLD, end="")
print(*args, end="")
print(COLORS.ENDC)
def OK(*args):
print(COLORS.OKGREEN + COLORS.BOLD, end="")
print(*args, end="")
print(COLORS.ENDC)
def BOLD(*args):
print(COLORS.BOLD, end="")
print(*args, end="")
print(COLORS.ENDC)
def INFO(*args):
print(*args)
def DEBUG(*args):
# TODO(sergey): Add check that debug is enabled.
if False:
print(*args)
def FATAL(*args):
import sys
ERROR(*args)
sys.exit(1)
def init():
if not VERBOSE and supportsColor():
global COLORS
COLORS = COLORS_ANSI
def setProvider(provider):
global LOGGER_PROVIDER
LOGGER_PROVIDER = provider
def restoreDefaultProvider():
global LOGGER_PROVIDER
LOGGER_PROVIDER = DefaultLoggerProvider()

View File

@@ -1,34 +1,77 @@
import shutil
import sys
from foundation import logger
from . import logger
class DefaultProgressProvider:
"""
Default progress provider implementation, which draws progress
bar in the console, unless current logging is set to evrbose mode.
"""
def progress(self, count, total, prefix="", suffix=""):
if logger.VERBOSE:
return
size = shutil.get_terminal_size((80, 20))
if prefix != "":
prefix = prefix + " "
if suffix != "":
suffix = " " + suffix
bar_len = size.columns - len(prefix) - len(suffix) - 10
filled_len = int(round(bar_len * count / float(total)))
percents = round(100.0 * count / float(total), 1)
bar = '=' * filled_len + '-' * (bar_len - filled_len)
sys.stdout.write('%s[%s] %s%%%s\r' % (prefix, bar, percents, suffix))
sys.stdout.flush()
def clear(self):
if logger.VERBOSE:
return
size = shutil.get_terminal_size((80, 20))
sys.stdout.write(" " * size.columns + "\r")
sys.stdout.flush()
def step(self, step_name):
pass
PROGRESS_PROVIDER = DefaultProgressProvider()
def progress(count, total, prefix="", suffix=""):
if logger.VERBOSE:
return
size = shutil.get_terminal_size((80, 20))
if prefix != "":
prefix = prefix + " "
if suffix != "":
suffix = " " + suffix
bar_len = size.columns - len(prefix) - len(suffix) - 10
filled_len = int(round(bar_len * count / float(total)))
percents = round(100.0 * count / float(total), 1)
bar = '=' * filled_len + '-' * (bar_len - filled_len)
sys.stdout.write('%s[%s] %s%%%s\r' % (prefix, bar, percents, suffix))
sys.stdout.flush()
"""
Report new progress status of the current task.
"""
PROGRESS_PROVIDER.progress(count, total, prefix, suffix)
def progressClear():
if logger.VERBOSE:
return
"""
Clear all possible progress bar lines in the ocnsole.
"""
PROGRESS_PROVIDER.clear()
size = shutil.get_terminal_size((80, 20))
sys.stdout.write(" " * size.columns + "\r")
sys.stdout.flush()
def step(step_name):
PROGRESS_PROVIDER.step(step_name)
def setProvider(provider):
"""
Override progress provider with a given name.
Is used by glue logic to hijack progress and do smart things.
"""
global PROGRESS_PROVIDER
PROGRESS_PROVIDER = provider
def restoreDefaultProvider():
"""
Restore default progress provider.
"""
global PROGRESS_PROVIDER
PROGRESS_PROVIDER = DefaultProgressProvider()

View File

@@ -1,13 +1,13 @@
import re
from foundation import util
from . import util
class Stats:
def __init__(self):
# Pepare some regex for parsing
self.re_path_tracing = re.compile(
".*Rendered ([0-9]+)/([0-9]+) Tiles" +
".*(Rendered|Path Tracing Tile) ([0-9]+)/([0-9]+)( Tiles)?" +
"(, Sample ([0-9]+)\/([0-9]+))?$")
self.re_total_render_time = re.compile(
".*Total render time: ([0-9]+(\.[0-9]+)?)")
@@ -35,8 +35,8 @@ class Stats:
# Current tile progress.
match = self.re_path_tracing.match(line)
if match:
self.current_tiles = int(match.group(1))
self.total_tiles = int(match.group(2))
self.current_tiles = int(match.group(2))
self.total_tiles = int(match.group(3))
# Total render time.
match = self.re_total_render_time.match(line)
if match:

View File

@@ -6,8 +6,8 @@ import subprocess
import sys
# Usually comes from third_party
import cpuinfo
import cpu_cores
from .third_party import cpuinfo
from .third_party import cpu_cores
def _getBlenderDeviceInfo(ctx):
@@ -38,7 +38,7 @@ def getBlenderVersion(ctx):
"build_commit_date",
"build_commit_time",
"build_hash")
command = [ctx.blender, "--version"]
command = [ctx.blender, "--background", "--version"]
process = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)

View File

@@ -4,6 +4,6 @@
version_info = (0, 1, 3)
__version__ = ".".join([str(x) for x in version_info])
from cpu_cores.common import CPUCoresCounter
from .common import CPUCoresCounter
__all__ = ['CPUCoresCounter']

View File

@@ -20,10 +20,10 @@ class CPUCoresCounter(object):
else:
cls.platform = sys.platform
if cls.platform.startswith('darwin'):
from cpu_cores.darwin import DarwinCPUCoresCounter
from .darwin import DarwinCPUCoresCounter
return DarwinCPUCoresCounter()
elif cls.platform.startswith('linux'):
from cpu_cores.linux import LinuxCPUCoresCounter
from .linux import LinuxCPUCoresCounter
return LinuxCPUCoresCounter()
else:
raise NotImplementedError("unsupported platform type [%s]" %

View File

@@ -4,7 +4,7 @@
import shlex
import subprocess
from cpu_cores.common import CPUCoresCounter
from .common import CPUCoresCounter
CPUINFO_COMMAND = "/usr/sbin/system_profiler" \
" -detailLevel full SPHardwareDataType"

View File

@@ -1,7 +1,7 @@
# This file is part of cpu_cores released under the MIT license.
# See the LICENSE file for more information.
from cpu_cores.common import CPUCoresCounter
from .common import CPUCoresCounter
CPUINFO_FILEPATH = "/proc/cpuinfo"

View File

@@ -1,6 +1,5 @@
import foundation
from foundation import progress
import dateutil
from . import progress
from .third_party import dateutil
import os
import requests
import tarfile
@@ -45,7 +44,7 @@ def queryMainScene(filepath, callbacks):
Return the equivalent to bpy.context.scene
"""
from blendfile import blendfile
from .blendfile import blendfile
with blendfile.open_blend(filepath) as blend:
# There is no bpy.context.scene, we get it from the main window

View File

@@ -5,7 +5,6 @@ import sys
SCRIPT_PATH = os.path.realpath(__file__)
SCRIPT_DIR = os.path.dirname(SCRIPT_PATH)
sys.path.append(os.path.join(SCRIPT_DIR, "third_party"))
import argparse
import foundation

View File

@@ -1,292 +0,0 @@
from __future__ import unicode_literals
try:
import unittest2 as unittest
except ImportError:
import unittest
import os
import datetime
import time
import subprocess
import warnings
import tempfile
import pickle
class WarningTestMixin(object):
# Based on https://stackoverflow.com/a/12935176/467366
class _AssertWarnsContext(warnings.catch_warnings):
def __init__(self, expected_warnings, parent, **kwargs):
super(WarningTestMixin._AssertWarnsContext, self).__init__(**kwargs)
self.parent = parent
try:
self.expected_warnings = list(expected_warnings)
except TypeError:
self.expected_warnings = [expected_warnings]
self._warning_log = []
def __enter__(self, *args, **kwargs):
rv = super(WarningTestMixin._AssertWarnsContext, self).__enter__(*args, **kwargs)
if self._showwarning is not self._module.showwarning:
super_showwarning = self._module.showwarning
else:
super_showwarning = None
def showwarning(*args, **kwargs):
if super_showwarning is not None:
super_showwarning(*args, **kwargs)
self._warning_log.append(warnings.WarningMessage(*args, **kwargs))
self._module.showwarning = showwarning
return rv
def __exit__(self, *args, **kwargs):
super(WarningTestMixin._AssertWarnsContext, self).__exit__(self, *args, **kwargs)
self.parent.assertTrue(any(issubclass(item.category, warning)
for warning in self.expected_warnings
for item in self._warning_log))
def assertWarns(self, warning, callable=None, *args, **kwargs):
warnings.simplefilter('always')
context = self.__class__._AssertWarnsContext(warning, self)
if callable is None:
return context
else:
with context:
callable(*args, **kwargs)
class PicklableMixin(object):
def _get_nobj_bytes(self, obj, dump_kwargs, load_kwargs):
"""
Pickle and unpickle an object using ``pickle.dumps`` / ``pickle.loads``
"""
pkl = pickle.dumps(obj, **dump_kwargs)
return pickle.loads(pkl, **load_kwargs)
def _get_nobj_file(self, obj, dump_kwargs, load_kwargs):
"""
Pickle and unpickle an object using ``pickle.dump`` / ``pickle.load`` on
a temporary file.
"""
with tempfile.TemporaryFile('w+b') as pkl:
pickle.dump(obj, pkl, **dump_kwargs)
pkl.seek(0) # Reset the file to the beginning to read it
nobj = pickle.load(pkl, **load_kwargs)
return nobj
def assertPicklable(self, obj, asfile=False,
dump_kwargs=None, load_kwargs=None):
"""
Assert that an object can be pickled and unpickled. This assertion
assumes that the desired behavior is that the unpickled object compares
equal to the original object, but is not the same object.
"""
get_nobj = self._get_nobj_file if asfile else self._get_nobj_bytes
dump_kwargs = dump_kwargs or {}
load_kwargs = load_kwargs or {}
nobj = get_nobj(obj, dump_kwargs, load_kwargs)
self.assertIsNot(obj, nobj)
self.assertEqual(obj, nobj)
class TZContextBase(object):
"""
Base class for a context manager which allows changing of time zones.
Subclasses may define a guard variable to either block or or allow time
zone changes by redefining ``_guard_var_name`` and ``_guard_allows_change``.
The default is that the guard variable must be affirmatively set.
Subclasses must define ``get_current_tz`` and ``set_current_tz``.
"""
_guard_var_name = "DATEUTIL_MAY_CHANGE_TZ"
_guard_allows_change = True
def __init__(self, tzval):
self.tzval = tzval
self._old_tz = None
@classmethod
def tz_change_allowed(cls):
"""
Class method used to query whether or not this class allows time zone
changes.
"""
guard = bool(os.environ.get(cls._guard_var_name, False))
# _guard_allows_change gives the "default" behavior - if True, the
# guard is overcoming a block. If false, the guard is causing a block.
# Whether tz_change is allowed is therefore the XNOR of the two.
return guard == cls._guard_allows_change
@classmethod
def tz_change_disallowed_message(cls):
""" Generate instructions on how to allow tz changes """
msg = ('Changing time zone not allowed. Set {envar} to {gval} '
'if you would like to allow this behavior')
return msg.format(envar=cls._guard_var_name,
gval=cls._guard_allows_change)
def __enter__(self):
if not self.tz_change_allowed():
raise ValueError(self.tz_change_disallowed_message())
self._old_tz = self.get_current_tz()
self.set_current_tz(self.tzval)
def __exit__(self, type, value, traceback):
if self._old_tz is not None:
self.set_current_tz(self._old_tz)
self._old_tz = None
def get_current_tz(self):
raise NotImplementedError
def set_current_tz(self):
raise NotImplementedError
class TZEnvContext(TZContextBase):
"""
Context manager that temporarily sets the `TZ` variable (for use on
*nix-like systems). Because the effect is local to the shell anyway, this
will apply *unless* a guard is set.
If you do not want the TZ environment variable set, you may set the
``DATEUTIL_MAY_NOT_CHANGE_TZ_VAR`` variable to a truthy value.
"""
_guard_var_name = "DATEUTIL_MAY_NOT_CHANGE_TZ_VAR"
_guard_allows_change = False
def get_current_tz(self):
return os.environ.get('TZ', UnsetTz)
def set_current_tz(self, tzval):
if tzval is UnsetTz and 'TZ' in os.environ:
del os.environ['TZ']
else:
os.environ['TZ'] = tzval
time.tzset()
class TZWinContext(TZContextBase):
"""
Context manager for changing local time zone on Windows.
Because the effect of this is system-wide and global, it may have
unintended side effect. Set the ``DATEUTIL_MAY_CHANGE_TZ`` environment
variable to a truthy value before using this context manager.
"""
def get_current_tz(self):
p = subprocess.Popen(['tzutil', '/g'], stdout=subprocess.PIPE)
ctzname, err = p.communicate()
ctzname = ctzname.decode() # Popen returns
if p.returncode:
raise OSError('Failed to get current time zone: ' + err)
return ctzname
def set_current_tz(self, tzname):
p = subprocess.Popen('tzutil /s "' + tzname + '"')
out, err = p.communicate()
if p.returncode:
raise OSError('Failed to set current time zone: ' +
(err or 'Unknown error.'))
###
# Compatibility functions
def _total_seconds(td):
# Python 2.6 doesn't have a total_seconds() method on timedelta objects
return ((td.seconds + td.days * 86400) * 1000000 +
td.microseconds) // 1000000
total_seconds = getattr(datetime.timedelta, 'total_seconds', _total_seconds)
###
# Utility classes
class NotAValueClass(object):
"""
A class analogous to NaN that has operations defined for any type.
"""
def _op(self, other):
return self # Operation with NotAValue returns NotAValue
def _cmp(self, other):
return False
__add__ = __radd__ = _op
__sub__ = __rsub__ = _op
__mul__ = __rmul__ = _op
__div__ = __rdiv__ = _op
__truediv__ = __rtruediv__ = _op
__floordiv__ = __rfloordiv__ = _op
__lt__ = __rlt__ = _op
__gt__ = __rgt__ = _op
__eq__ = __req__ = _op
__le__ = __rle__ = _op
__ge__ = __rge__ = _op
NotAValue = NotAValueClass()
class ComparesEqualClass(object):
"""
A class that is always equal to whatever you compare it to.
"""
def __eq__(self, other):
return True
def __ne__(self, other):
return False
def __le__(self, other):
return True
def __ge__(self, other):
return True
def __lt__(self, other):
return False
def __gt__(self, other):
return False
__req__ = __eq__
__rne__ = __ne__
__rle__ = __le__
__rge__ = __ge__
__rlt__ = __lt__
__rgt__ = __gt__
ComparesEqual = ComparesEqualClass()
class UnsetTzClass(object):
""" Sentinel class for unset time zone variable """
pass
UnsetTz = UnsetTzClass()

View File

@@ -1,99 +0,0 @@
from dateutil.easter import easter
from dateutil.easter import EASTER_WESTERN, EASTER_ORTHODOX, EASTER_JULIAN
from datetime import date
try:
import unittest2 as unittest
except ImportError:
import unittest
# List of easters between 1990 and 2050
western_easter_dates = [
date(1990, 4, 15), date(1991, 3, 31), date(1992, 4, 19), date(1993, 4, 11),
date(1994, 4, 3), date(1995, 4, 16), date(1996, 4, 7), date(1997, 3, 30),
date(1998, 4, 12), date(1999, 4, 4),
date(2000, 4, 23), date(2001, 4, 15), date(2002, 3, 31), date(2003, 4, 20),
date(2004, 4, 11), date(2005, 3, 27), date(2006, 4, 16), date(2007, 4, 8),
date(2008, 3, 23), date(2009, 4, 12),
date(2010, 4, 4), date(2011, 4, 24), date(2012, 4, 8), date(2013, 3, 31),
date(2014, 4, 20), date(2015, 4, 5), date(2016, 3, 27), date(2017, 4, 16),
date(2018, 4, 1), date(2019, 4, 21),
date(2020, 4, 12), date(2021, 4, 4), date(2022, 4, 17), date(2023, 4, 9),
date(2024, 3, 31), date(2025, 4, 20), date(2026, 4, 5), date(2027, 3, 28),
date(2028, 4, 16), date(2029, 4, 1),
date(2030, 4, 21), date(2031, 4, 13), date(2032, 3, 28), date(2033, 4, 17),
date(2034, 4, 9), date(2035, 3, 25), date(2036, 4, 13), date(2037, 4, 5),
date(2038, 4, 25), date(2039, 4, 10),
date(2040, 4, 1), date(2041, 4, 21), date(2042, 4, 6), date(2043, 3, 29),
date(2044, 4, 17), date(2045, 4, 9), date(2046, 3, 25), date(2047, 4, 14),
date(2048, 4, 5), date(2049, 4, 18), date(2050, 4, 10)
]
orthodox_easter_dates = [
date(1990, 4, 15), date(1991, 4, 7), date(1992, 4, 26), date(1993, 4, 18),
date(1994, 5, 1), date(1995, 4, 23), date(1996, 4, 14), date(1997, 4, 27),
date(1998, 4, 19), date(1999, 4, 11),
date(2000, 4, 30), date(2001, 4, 15), date(2002, 5, 5), date(2003, 4, 27),
date(2004, 4, 11), date(2005, 5, 1), date(2006, 4, 23), date(2007, 4, 8),
date(2008, 4, 27), date(2009, 4, 19),
date(2010, 4, 4), date(2011, 4, 24), date(2012, 4, 15), date(2013, 5, 5),
date(2014, 4, 20), date(2015, 4, 12), date(2016, 5, 1), date(2017, 4, 16),
date(2018, 4, 8), date(2019, 4, 28),
date(2020, 4, 19), date(2021, 5, 2), date(2022, 4, 24), date(2023, 4, 16),
date(2024, 5, 5), date(2025, 4, 20), date(2026, 4, 12), date(2027, 5, 2),
date(2028, 4, 16), date(2029, 4, 8),
date(2030, 4, 28), date(2031, 4, 13), date(2032, 5, 2), date(2033, 4, 24),
date(2034, 4, 9), date(2035, 4, 29), date(2036, 4, 20), date(2037, 4, 5),
date(2038, 4, 25), date(2039, 4, 17),
date(2040, 5, 6), date(2041, 4, 21), date(2042, 4, 13), date(2043, 5, 3),
date(2044, 4, 24), date(2045, 4, 9), date(2046, 4, 29), date(2047, 4, 21),
date(2048, 4, 5), date(2049, 4, 25), date(2050, 4, 17)
]
# A random smattering of Julian dates.
# Pulled values from http://www.kevinlaughery.com/east4099.html
julian_easter_dates = [
date( 326, 4, 3), date( 375, 4, 5), date( 492, 4, 5), date( 552, 3, 31),
date( 562, 4, 9), date( 569, 4, 21), date( 597, 4, 14), date( 621, 4, 19),
date( 636, 3, 31), date( 655, 3, 29), date( 700, 4, 11), date( 725, 4, 8),
date( 750, 3, 29), date( 782, 4, 7), date( 835, 4, 18), date( 849, 4, 14),
date( 867, 3, 30), date( 890, 4, 12), date( 922, 4, 21), date( 934, 4, 6),
date(1049, 3, 26), date(1058, 4, 19), date(1113, 4, 6), date(1119, 3, 30),
date(1242, 4, 20), date(1255, 3, 28), date(1257, 4, 8), date(1258, 3, 24),
date(1261, 4, 24), date(1278, 4, 17), date(1333, 4, 4), date(1351, 4, 17),
date(1371, 4, 6), date(1391, 3, 26), date(1402, 3, 26), date(1412, 4, 3),
date(1439, 4, 5), date(1445, 3, 28), date(1531, 4, 9), date(1555, 4, 14)
]
class EasterTest(unittest.TestCase):
def testEasterWestern(self):
for easter_date in western_easter_dates:
self.assertEqual(easter_date,
easter(easter_date.year, EASTER_WESTERN))
def testEasterOrthodox(self):
for easter_date in orthodox_easter_dates:
self.assertEqual(easter_date,
easter(easter_date.year, EASTER_ORTHODOX))
def testEasterJulian(self):
for easter_date in julian_easter_dates:
self.assertEqual(easter_date,
easter(easter_date.year, EASTER_JULIAN))
def testEasterBadMethod(self):
# Invalid methods raise ValueError
with self.assertRaises(ValueError):
easter(1975, 4)

View File

@@ -1,166 +0,0 @@
import sys
try:
import unittest2 as unittest
except ImportError:
import unittest
class ImportVersionTest(unittest.TestCase):
""" Test that dateutil.__version__ can be imported"""
def testImportVersionStr(self):
from dateutil import __version__
def testImportRoot(self):
import dateutil
self.assertTrue(hasattr(dateutil, '__version__'))
class ImportEasterTest(unittest.TestCase):
""" Test that dateutil.easter-related imports work properly """
def testEasterDirect(self):
import dateutil.easter
def testEasterFrom(self):
from dateutil import easter
def testEasterStar(self):
from dateutil.easter import easter
class ImportParserTest(unittest.TestCase):
""" Test that dateutil.parser-related imports work properly """
def testParserDirect(self):
import dateutil.parser
def testParserFrom(self):
from dateutil import parser
def testParserAll(self):
# All interface
from dateutil.parser import parse
from dateutil.parser import parserinfo
# Other public classes
from dateutil.parser import parser
for var in (parse, parserinfo, parser):
self.assertIsNot(var, None)
class ImportRelativeDeltaTest(unittest.TestCase):
""" Test that dateutil.relativedelta-related imports work properly """
def testRelativeDeltaDirect(self):
import dateutil.relativedelta
def testRelativeDeltaFrom(self):
from dateutil import relativedelta
def testRelativeDeltaAll(self):
from dateutil.relativedelta import relativedelta
from dateutil.relativedelta import MO, TU, WE, TH, FR, SA, SU
for var in (relativedelta, MO, TU, WE, TH, FR, SA, SU):
self.assertIsNot(var, None)
# In the public interface but not in all
from dateutil.relativedelta import weekday
self.assertIsNot(weekday, None)
class ImportRRuleTest(unittest.TestCase):
""" Test that dateutil.rrule related imports work properly """
def testRRuleDirect(self):
import dateutil.rrule
def testRRuleFrom(self):
from dateutil import rrule
def testRRuleAll(self):
from dateutil.rrule import rrule
from dateutil.rrule import rruleset
from dateutil.rrule import rrulestr
from dateutil.rrule import YEARLY, MONTHLY, WEEKLY, DAILY
from dateutil.rrule import HOURLY, MINUTELY, SECONDLY
from dateutil.rrule import MO, TU, WE, TH, FR, SA, SU
rr_all = (rrule, rruleset, rrulestr,
YEARLY, MONTHLY, WEEKLY, DAILY,
HOURLY, MINUTELY, SECONDLY,
MO, TU, WE, TH, FR, SA, SU)
for var in rr_all:
self.assertIsNot(var, None)
# In the public interface but not in all
from dateutil.rrule import weekday
self.assertIsNot(weekday, None)
class ImportTZTest(unittest.TestCase):
""" Test that dateutil.tz related imports work properly """
def testTzDirect(self):
import dateutil.tz
def testTzFrom(self):
from dateutil import tz
def testTzAll(self):
from dateutil.tz import tzutc
from dateutil.tz import tzoffset
from dateutil.tz import tzlocal
from dateutil.tz import tzfile
from dateutil.tz import tzrange
from dateutil.tz import tzstr
from dateutil.tz import tzical
from dateutil.tz import gettz
from dateutil.tz import tzwin
from dateutil.tz import tzwinlocal
tz_all = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange",
"tzstr", "tzical", "gettz"]
tz_all += ["tzwin", "tzwinlocal"] if sys.platform.startswith("win") else []
lvars = locals()
for var in tz_all:
self.assertIsNot(lvars[var], None)
@unittest.skipUnless(sys.platform.startswith('win'), "Requires Windows")
class ImportTZWinTest(unittest.TestCase):
""" Test that dateutil.tzwin related imports work properly """
def testTzwinDirect(self):
import dateutil.tzwin
def testTzwinFrom(self):
from dateutil import tzwin
def testTzwinStar(self):
from dateutil.tzwin import tzwin
from dateutil.tzwin import tzwinlocal
tzwin_all = [tzwin, tzwinlocal]
for var in tzwin_all:
self.assertIsNot(var, None)
class ImportZoneInfoTest(unittest.TestCase):
def testZoneinfoDirect(self):
import dateutil.zoneinfo
def testZoneinfoFrom(self):
from dateutil import zoneinfo
def testZoneinfoStar(self):
from dateutil.zoneinfo import gettz
from dateutil.zoneinfo import gettz_db_metadata
from dateutil.zoneinfo import rebuild
zi_all = (gettz, gettz_db_metadata, rebuild)
for var in zi_all:
self.assertIsNot(var, None)

View File

@@ -1,897 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from ._common import unittest
from datetime import datetime, timedelta
from dateutil.tz import tzoffset
from dateutil.parser import parse, parserinfo
from six import assertRaisesRegex, PY3
from six.moves import StringIO
class ParserTest(unittest.TestCase):
def setUp(self):
self.tzinfos = {"BRST": -10800}
self.brsttz = tzoffset("BRST", -10800)
self.default = datetime(2003, 9, 25)
# Parser should be able to handle bytestring and unicode
self.uni_str = '2014-05-01 08:00:00'
self.str_str = self.uni_str.encode()
def testEmptyString(self):
with self.assertRaises(ValueError):
parse('')
def testNone(self):
with self.assertRaises(TypeError):
parse(None)
def testInvalidType(self):
with self.assertRaises(TypeError):
parse(13)
def testDuckTyping(self):
# We want to support arbitrary classes that implement the stream
# interface.
class StringPassThrough(object):
def __init__(self, stream):
self.stream = stream
def read(self, *args, **kwargs):
return self.stream.read(*args, **kwargs)
dstr = StringPassThrough(StringIO('2014 January 19'))
self.assertEqual(parse(dstr), datetime(2014, 1, 19))
def testParseStream(self):
dstr = StringIO('2014 January 19')
self.assertEqual(parse(dstr), datetime(2014, 1, 19))
def testParseStr(self):
self.assertEqual(parse(self.str_str),
parse(self.uni_str))
def testParserParseStr(self):
from dateutil.parser import parser
self.assertEqual(parser().parse(self.str_str),
parser().parse(self.uni_str))
def testParseUnicodeWords(self):
class rus_parserinfo(parserinfo):
MONTHS = [("янв", "Январь"),
("фев", "Февраль"),
("мар", "Март"),
("апр", "Апрель"),
("май", "Май"),
("июн", "Июнь"),
("июл", "Июль"),
("авг", "Август"),
("сен", "Сентябрь"),
("окт", "Октябрь"),
("ноя", "Ноябрь"),
("дек", "Декабрь")]
self.assertEqual(parse('10 Сентябрь 2015 10:20',
parserinfo=rus_parserinfo()),
datetime(2015, 9, 10, 10, 20))
def testParseWithNulls(self):
# This relies on the from __future__ import unicode_literals, because
# explicitly specifying a unicode literal is a syntax error in Py 3.2
# May want to switch to u'...' if we ever drop Python 3.2 support.
pstring = '\x00\x00August 29, 1924'
self.assertEqual(parse(pstring),
datetime(1924, 8, 29))
def testDateCommandFormat(self):
self.assertEqual(parse("Thu Sep 25 10:36:28 BRST 2003",
tzinfos=self.tzinfos),
datetime(2003, 9, 25, 10, 36, 28,
tzinfo=self.brsttz))
def testDateCommandFormatUnicode(self):
self.assertEqual(parse("Thu Sep 25 10:36:28 BRST 2003",
tzinfos=self.tzinfos),
datetime(2003, 9, 25, 10, 36, 28,
tzinfo=self.brsttz))
def testDateCommandFormatReversed(self):
self.assertEqual(parse("2003 10:36:28 BRST 25 Sep Thu",
tzinfos=self.tzinfos),
datetime(2003, 9, 25, 10, 36, 28,
tzinfo=self.brsttz))
def testDateCommandFormatWithLong(self):
if not PY3:
self.assertEqual(parse("Thu Sep 25 10:36:28 BRST 2003",
tzinfos={"BRST": long(-10800)}),
datetime(2003, 9, 25, 10, 36, 28,
tzinfo=self.brsttz))
def testDateCommandFormatIgnoreTz(self):
self.assertEqual(parse("Thu Sep 25 10:36:28 BRST 2003",
ignoretz=True),
datetime(2003, 9, 25, 10, 36, 28))
def testDateCommandFormatStrip1(self):
self.assertEqual(parse("Thu Sep 25 10:36:28 2003"),
datetime(2003, 9, 25, 10, 36, 28))
def testDateCommandFormatStrip2(self):
self.assertEqual(parse("Thu Sep 25 10:36:28", default=self.default),
datetime(2003, 9, 25, 10, 36, 28))
def testDateCommandFormatStrip3(self):
self.assertEqual(parse("Thu Sep 10:36:28", default=self.default),
datetime(2003, 9, 25, 10, 36, 28))
def testDateCommandFormatStrip4(self):
self.assertEqual(parse("Thu 10:36:28", default=self.default),
datetime(2003, 9, 25, 10, 36, 28))
def testDateCommandFormatStrip5(self):
self.assertEqual(parse("Sep 10:36:28", default=self.default),
datetime(2003, 9, 25, 10, 36, 28))
def testDateCommandFormatStrip6(self):
self.assertEqual(parse("10:36:28", default=self.default),
datetime(2003, 9, 25, 10, 36, 28))
def testDateCommandFormatStrip7(self):
self.assertEqual(parse("10:36", default=self.default),
datetime(2003, 9, 25, 10, 36))
def testDateCommandFormatStrip8(self):
self.assertEqual(parse("Thu Sep 25 2003"),
datetime(2003, 9, 25))
def testDateCommandFormatStrip9(self):
self.assertEqual(parse("Sep 25 2003"),
datetime(2003, 9, 25))
def testDateCommandFormatStrip10(self):
self.assertEqual(parse("Sep 2003", default=self.default),
datetime(2003, 9, 25))
def testDateCommandFormatStrip11(self):
self.assertEqual(parse("Sep", default=self.default),
datetime(2003, 9, 25))
def testDateCommandFormatStrip12(self):
self.assertEqual(parse("2003", default=self.default),
datetime(2003, 9, 25))
def testDateRCommandFormat(self):
self.assertEqual(parse("Thu, 25 Sep 2003 10:49:41 -0300"),
datetime(2003, 9, 25, 10, 49, 41,
tzinfo=self.brsttz))
def testISOFormat(self):
self.assertEqual(parse("2003-09-25T10:49:41.5-03:00"),
datetime(2003, 9, 25, 10, 49, 41, 500000,
tzinfo=self.brsttz))
def testISOFormatStrip1(self):
self.assertEqual(parse("2003-09-25T10:49:41-03:00"),
datetime(2003, 9, 25, 10, 49, 41,
tzinfo=self.brsttz))
def testISOFormatStrip2(self):
self.assertEqual(parse("2003-09-25T10:49:41"),
datetime(2003, 9, 25, 10, 49, 41))
def testISOFormatStrip3(self):
self.assertEqual(parse("2003-09-25T10:49"),
datetime(2003, 9, 25, 10, 49))
def testISOFormatStrip4(self):
self.assertEqual(parse("2003-09-25T10"),
datetime(2003, 9, 25, 10))
def testISOFormatStrip5(self):
self.assertEqual(parse("2003-09-25"),
datetime(2003, 9, 25))
def testISOStrippedFormat(self):
self.assertEqual(parse("20030925T104941.5-0300"),
datetime(2003, 9, 25, 10, 49, 41, 500000,
tzinfo=self.brsttz))
def testISOStrippedFormatStrip1(self):
self.assertEqual(parse("20030925T104941-0300"),
datetime(2003, 9, 25, 10, 49, 41,
tzinfo=self.brsttz))
def testISOStrippedFormatStrip2(self):
self.assertEqual(parse("20030925T104941"),
datetime(2003, 9, 25, 10, 49, 41))
def testISOStrippedFormatStrip3(self):
self.assertEqual(parse("20030925T1049"),
datetime(2003, 9, 25, 10, 49, 0))
def testISOStrippedFormatStrip4(self):
self.assertEqual(parse("20030925T10"),
datetime(2003, 9, 25, 10))
def testISOStrippedFormatStrip5(self):
self.assertEqual(parse("20030925"),
datetime(2003, 9, 25))
def testPythonLoggerFormat(self):
self.assertEqual(parse("2003-09-25 10:49:41,502"),
datetime(2003, 9, 25, 10, 49, 41, 502000))
def testNoSeparator1(self):
self.assertEqual(parse("199709020908"),
datetime(1997, 9, 2, 9, 8))
def testNoSeparator2(self):
self.assertEqual(parse("19970902090807"),
datetime(1997, 9, 2, 9, 8, 7))
def testDateWithDash1(self):
self.assertEqual(parse("2003-09-25"),
datetime(2003, 9, 25))
def testDateWithDash2(self):
self.assertEqual(parse("2003-Sep-25"),
datetime(2003, 9, 25))
def testDateWithDash3(self):
self.assertEqual(parse("25-Sep-2003"),
datetime(2003, 9, 25))
def testDateWithDash4(self):
self.assertEqual(parse("25-Sep-2003"),
datetime(2003, 9, 25))
def testDateWithDash5(self):
self.assertEqual(parse("Sep-25-2003"),
datetime(2003, 9, 25))
def testDateWithDash6(self):
self.assertEqual(parse("09-25-2003"),
datetime(2003, 9, 25))
def testDateWithDash7(self):
self.assertEqual(parse("25-09-2003"),
datetime(2003, 9, 25))
def testDateWithDash8(self):
self.assertEqual(parse("10-09-2003", dayfirst=True),
datetime(2003, 9, 10))
def testDateWithDash9(self):
self.assertEqual(parse("10-09-2003"),
datetime(2003, 10, 9))
def testDateWithDash10(self):
self.assertEqual(parse("10-09-03"),
datetime(2003, 10, 9))
def testDateWithDash11(self):
self.assertEqual(parse("10-09-03", yearfirst=True),
datetime(2010, 9, 3))
def testDateWithDot1(self):
self.assertEqual(parse("2003.09.25"),
datetime(2003, 9, 25))
def testDateWithDot2(self):
self.assertEqual(parse("2003.Sep.25"),
datetime(2003, 9, 25))
def testDateWithDot3(self):
self.assertEqual(parse("25.Sep.2003"),
datetime(2003, 9, 25))
def testDateWithDot4(self):
self.assertEqual(parse("25.Sep.2003"),
datetime(2003, 9, 25))
def testDateWithDot5(self):
self.assertEqual(parse("Sep.25.2003"),
datetime(2003, 9, 25))
def testDateWithDot6(self):
self.assertEqual(parse("09.25.2003"),
datetime(2003, 9, 25))
def testDateWithDot7(self):
self.assertEqual(parse("25.09.2003"),
datetime(2003, 9, 25))
def testDateWithDot8(self):
self.assertEqual(parse("10.09.2003", dayfirst=True),
datetime(2003, 9, 10))
def testDateWithDot9(self):
self.assertEqual(parse("10.09.2003"),
datetime(2003, 10, 9))
def testDateWithDot10(self):
self.assertEqual(parse("10.09.03"),
datetime(2003, 10, 9))
def testDateWithDot11(self):
self.assertEqual(parse("10.09.03", yearfirst=True),
datetime(2010, 9, 3))
def testDateWithSlash1(self):
self.assertEqual(parse("2003/09/25"),
datetime(2003, 9, 25))
def testDateWithSlash2(self):
self.assertEqual(parse("2003/Sep/25"),
datetime(2003, 9, 25))
def testDateWithSlash3(self):
self.assertEqual(parse("25/Sep/2003"),
datetime(2003, 9, 25))
def testDateWithSlash4(self):
self.assertEqual(parse("25/Sep/2003"),
datetime(2003, 9, 25))
def testDateWithSlash5(self):
self.assertEqual(parse("Sep/25/2003"),
datetime(2003, 9, 25))
def testDateWithSlash6(self):
self.assertEqual(parse("09/25/2003"),
datetime(2003, 9, 25))
def testDateWithSlash7(self):
self.assertEqual(parse("25/09/2003"),
datetime(2003, 9, 25))
def testDateWithSlash8(self):
self.assertEqual(parse("10/09/2003", dayfirst=True),
datetime(2003, 9, 10))
def testDateWithSlash9(self):
self.assertEqual(parse("10/09/2003"),
datetime(2003, 10, 9))
def testDateWithSlash10(self):
self.assertEqual(parse("10/09/03"),
datetime(2003, 10, 9))
def testDateWithSlash11(self):
self.assertEqual(parse("10/09/03", yearfirst=True),
datetime(2010, 9, 3))
def testDateWithSpace1(self):
self.assertEqual(parse("2003 09 25"),
datetime(2003, 9, 25))
def testDateWithSpace2(self):
self.assertEqual(parse("2003 Sep 25"),
datetime(2003, 9, 25))
def testDateWithSpace3(self):
self.assertEqual(parse("25 Sep 2003"),
datetime(2003, 9, 25))
def testDateWithSpace4(self):
self.assertEqual(parse("25 Sep 2003"),
datetime(2003, 9, 25))
def testDateWithSpace5(self):
self.assertEqual(parse("Sep 25 2003"),
datetime(2003, 9, 25))
def testDateWithSpace6(self):
self.assertEqual(parse("09 25 2003"),
datetime(2003, 9, 25))
def testDateWithSpace7(self):
self.assertEqual(parse("25 09 2003"),
datetime(2003, 9, 25))
def testDateWithSpace8(self):
self.assertEqual(parse("10 09 2003", dayfirst=True),
datetime(2003, 9, 10))
def testDateWithSpace9(self):
self.assertEqual(parse("10 09 2003"),
datetime(2003, 10, 9))
def testDateWithSpace10(self):
self.assertEqual(parse("10 09 03"),
datetime(2003, 10, 9))
def testDateWithSpace11(self):
self.assertEqual(parse("10 09 03", yearfirst=True),
datetime(2010, 9, 3))
def testDateWithSpace12(self):
self.assertEqual(parse("25 09 03"),
datetime(2003, 9, 25))
def testStrangelyOrderedDate1(self):
self.assertEqual(parse("03 25 Sep"),
datetime(2003, 9, 25))
def testStrangelyOrderedDate2(self):
self.assertEqual(parse("2003 25 Sep"),
datetime(2003, 9, 25))
def testStrangelyOrderedDate3(self):
self.assertEqual(parse("25 03 Sep"),
datetime(2025, 9, 3))
def testHourWithLetters(self):
self.assertEqual(parse("10h36m28.5s", default=self.default),
datetime(2003, 9, 25, 10, 36, 28, 500000))
def testHourWithLettersStrip1(self):
self.assertEqual(parse("10h36m28s", default=self.default),
datetime(2003, 9, 25, 10, 36, 28))
def testHourWithLettersStrip2(self):
self.assertEqual(parse("10h36m", default=self.default),
datetime(2003, 9, 25, 10, 36))
def testHourWithLettersStrip3(self):
self.assertEqual(parse("10h", default=self.default),
datetime(2003, 9, 25, 10))
def testHourWithLettersStrip4(self):
self.assertEqual(parse("10 h 36", default=self.default),
datetime(2003, 9, 25, 10, 36))
def testHourWithLetterStrip5(self):
self.assertEqual(parse("10 h 36.5", default=self.default),
datetime(2003, 9, 25, 10, 36, 30))
def testMinuteWithLettersSpaces1(self):
self.assertEqual(parse("36 m 5", default=self.default),
datetime(2003, 9, 25, 0, 36, 5))
def testMinuteWithLettersSpaces2(self):
self.assertEqual(parse("36 m 5 s", default=self.default),
datetime(2003, 9, 25, 0, 36, 5))
def testMinuteWithLettersSpaces3(self):
self.assertEqual(parse("36 m 05", default=self.default),
datetime(2003, 9, 25, 0, 36, 5))
def testMinuteWithLettersSpaces4(self):
self.assertEqual(parse("36 m 05 s", default=self.default),
datetime(2003, 9, 25, 0, 36, 5))
def testAMPMNoHour(self):
with self.assertRaises(ValueError):
parse("AM")
with self.assertRaises(ValueError):
parse("Jan 20, 2015 PM")
def testHourAmPm1(self):
self.assertEqual(parse("10h am", default=self.default),
datetime(2003, 9, 25, 10))
def testHourAmPm2(self):
self.assertEqual(parse("10h pm", default=self.default),
datetime(2003, 9, 25, 22))
def testHourAmPm3(self):
self.assertEqual(parse("10am", default=self.default),
datetime(2003, 9, 25, 10))
def testHourAmPm4(self):
self.assertEqual(parse("10pm", default=self.default),
datetime(2003, 9, 25, 22))
def testHourAmPm5(self):
self.assertEqual(parse("10:00 am", default=self.default),
datetime(2003, 9, 25, 10))
def testHourAmPm6(self):
self.assertEqual(parse("10:00 pm", default=self.default),
datetime(2003, 9, 25, 22))
def testHourAmPm7(self):
self.assertEqual(parse("10:00am", default=self.default),
datetime(2003, 9, 25, 10))
def testHourAmPm8(self):
self.assertEqual(parse("10:00pm", default=self.default),
datetime(2003, 9, 25, 22))
def testHourAmPm9(self):
self.assertEqual(parse("10:00a.m", default=self.default),
datetime(2003, 9, 25, 10))
def testHourAmPm10(self):
self.assertEqual(parse("10:00p.m", default=self.default),
datetime(2003, 9, 25, 22))
def testHourAmPm11(self):
self.assertEqual(parse("10:00a.m.", default=self.default),
datetime(2003, 9, 25, 10))
def testHourAmPm12(self):
self.assertEqual(parse("10:00p.m.", default=self.default),
datetime(2003, 9, 25, 22))
def testAMPMRange(self):
with self.assertRaises(ValueError):
parse("13:44 AM")
with self.assertRaises(ValueError):
parse("January 25, 1921 23:13 PM")
def testPertain(self):
self.assertEqual(parse("Sep 03", default=self.default),
datetime(2003, 9, 3))
self.assertEqual(parse("Sep of 03", default=self.default),
datetime(2003, 9, 25))
def testWeekdayAlone(self):
self.assertEqual(parse("Wed", default=self.default),
datetime(2003, 10, 1))
def testLongWeekday(self):
self.assertEqual(parse("Wednesday", default=self.default),
datetime(2003, 10, 1))
def testLongMonth(self):
self.assertEqual(parse("October", default=self.default),
datetime(2003, 10, 25))
def testZeroYear(self):
self.assertEqual(parse("31-Dec-00", default=self.default),
datetime(2000, 12, 31))
def testFuzzy(self):
s = "Today is 25 of September of 2003, exactly " \
"at 10:49:41 with timezone -03:00."
self.assertEqual(parse(s, fuzzy=True),
datetime(2003, 9, 25, 10, 49, 41,
tzinfo=self.brsttz))
def testFuzzyWithTokens(self):
s1 = "Today is 25 of September of 2003, exactly " \
"at 10:49:41 with timezone -03:00."
self.assertEqual(parse(s1, fuzzy_with_tokens=True),
(datetime(2003, 9, 25, 10, 49, 41,
tzinfo=self.brsttz),
('Today is ', 'of ', ', exactly at ',
' with timezone ', '.')))
s2 = "http://biz.yahoo.com/ipo/p/600221.html"
self.assertEqual(parse(s2, fuzzy_with_tokens=True),
(datetime(2060, 2, 21, 0, 0, 0),
('http://biz.yahoo.com/ipo/p/', '.html')))
def testFuzzyAMPMProblem(self):
# Sometimes fuzzy parsing results in AM/PM flag being set without
# hours - if it's fuzzy it should ignore that.
s1 = "I have a meeting on March 1, 1974."
s2 = "On June 8th, 2020, I am going to be the first man on Mars"
# Also don't want any erroneous AM or PMs changing the parsed time
s3 = "Meet me at the AM/PM on Sunset at 3:00 AM on December 3rd, 2003"
s4 = "Meet me at 3:00AM on December 3rd, 2003 at the AM/PM on Sunset"
self.assertEqual(parse(s1, fuzzy=True), datetime(1974, 3, 1))
self.assertEqual(parse(s2, fuzzy=True), datetime(2020, 6, 8))
self.assertEqual(parse(s3, fuzzy=True), datetime(2003, 12, 3, 3))
self.assertEqual(parse(s4, fuzzy=True), datetime(2003, 12, 3, 3))
def testFuzzyIgnoreAMPM(self):
s1 = "Jan 29, 1945 14:45 AM I going to see you there?"
self.assertEqual(parse(s1, fuzzy=True), datetime(1945, 1, 29, 14, 45))
def testExtraSpace(self):
self.assertEqual(parse(" July 4 , 1976 12:01:02 am "),
datetime(1976, 7, 4, 0, 1, 2))
def testRandomFormat1(self):
self.assertEqual(parse("Wed, July 10, '96"),
datetime(1996, 7, 10, 0, 0))
def testRandomFormat2(self):
self.assertEqual(parse("1996.07.10 AD at 15:08:56 PDT",
ignoretz=True),
datetime(1996, 7, 10, 15, 8, 56))
def testRandomFormat3(self):
self.assertEqual(parse("1996.July.10 AD 12:08 PM"),
datetime(1996, 7, 10, 12, 8))
def testRandomFormat4(self):
self.assertEqual(parse("Tuesday, April 12, 1952 AD 3:30:42pm PST",
ignoretz=True),
datetime(1952, 4, 12, 15, 30, 42))
def testRandomFormat5(self):
self.assertEqual(parse("November 5, 1994, 8:15:30 am EST",
ignoretz=True),
datetime(1994, 11, 5, 8, 15, 30))
def testRandomFormat6(self):
self.assertEqual(parse("1994-11-05T08:15:30-05:00",
ignoretz=True),
datetime(1994, 11, 5, 8, 15, 30))
def testRandomFormat7(self):
self.assertEqual(parse("1994-11-05T08:15:30Z",
ignoretz=True),
datetime(1994, 11, 5, 8, 15, 30))
def testRandomFormat8(self):
self.assertEqual(parse("July 4, 1976"), datetime(1976, 7, 4))
def testRandomFormat9(self):
self.assertEqual(parse("7 4 1976"), datetime(1976, 7, 4))
def testRandomFormat10(self):
self.assertEqual(parse("4 jul 1976"), datetime(1976, 7, 4))
def testRandomFormat11(self):
self.assertEqual(parse("7-4-76"), datetime(1976, 7, 4))
def testRandomFormat12(self):
self.assertEqual(parse("19760704"), datetime(1976, 7, 4))
def testRandomFormat13(self):
self.assertEqual(parse("0:01:02", default=self.default),
datetime(2003, 9, 25, 0, 1, 2))
def testRandomFormat14(self):
self.assertEqual(parse("12h 01m02s am", default=self.default),
datetime(2003, 9, 25, 0, 1, 2))
def testRandomFormat15(self):
self.assertEqual(parse("0:01:02 on July 4, 1976"),
datetime(1976, 7, 4, 0, 1, 2))
def testRandomFormat16(self):
self.assertEqual(parse("0:01:02 on July 4, 1976"),
datetime(1976, 7, 4, 0, 1, 2))
def testRandomFormat17(self):
self.assertEqual(parse("1976-07-04T00:01:02Z", ignoretz=True),
datetime(1976, 7, 4, 0, 1, 2))
def testRandomFormat18(self):
self.assertEqual(parse("July 4, 1976 12:01:02 am"),
datetime(1976, 7, 4, 0, 1, 2))
def testRandomFormat19(self):
self.assertEqual(parse("Mon Jan 2 04:24:27 1995"),
datetime(1995, 1, 2, 4, 24, 27))
def testRandomFormat20(self):
self.assertEqual(parse("Tue Apr 4 00:22:12 PDT 1995", ignoretz=True),
datetime(1995, 4, 4, 0, 22, 12))
def testRandomFormat21(self):
self.assertEqual(parse("04.04.95 00:22"),
datetime(1995, 4, 4, 0, 22))
def testRandomFormat22(self):
self.assertEqual(parse("Jan 1 1999 11:23:34.578"),
datetime(1999, 1, 1, 11, 23, 34, 578000))
def testRandomFormat23(self):
self.assertEqual(parse("950404 122212"),
datetime(1995, 4, 4, 12, 22, 12))
def testRandomFormat24(self):
self.assertEqual(parse("0:00 PM, PST", default=self.default,
ignoretz=True),
datetime(2003, 9, 25, 12, 0))
def testRandomFormat25(self):
self.assertEqual(parse("12:08 PM", default=self.default),
datetime(2003, 9, 25, 12, 8))
def testRandomFormat26(self):
self.assertEqual(parse("5:50 A.M. on June 13, 1990"),
datetime(1990, 6, 13, 5, 50))
def testRandomFormat27(self):
self.assertEqual(parse("3rd of May 2001"), datetime(2001, 5, 3))
def testRandomFormat28(self):
self.assertEqual(parse("5th of March 2001"), datetime(2001, 3, 5))
def testRandomFormat29(self):
self.assertEqual(parse("1st of May 2003"), datetime(2003, 5, 1))
def testRandomFormat30(self):
self.assertEqual(parse("01h02m03", default=self.default),
datetime(2003, 9, 25, 1, 2, 3))
def testRandomFormat31(self):
self.assertEqual(parse("01h02", default=self.default),
datetime(2003, 9, 25, 1, 2))
def testRandomFormat32(self):
self.assertEqual(parse("01h02s", default=self.default),
datetime(2003, 9, 25, 1, 0, 2))
def testRandomFormat33(self):
self.assertEqual(parse("01m02", default=self.default),
datetime(2003, 9, 25, 0, 1, 2))
def testRandomFormat34(self):
self.assertEqual(parse("01m02h", default=self.default),
datetime(2003, 9, 25, 2, 1))
def testRandomFormat35(self):
self.assertEqual(parse("2004 10 Apr 11h30m", default=self.default),
datetime(2004, 4, 10, 11, 30))
def test_99_ad(self):
self.assertEqual(parse('0099-01-01T00:00:00'),
datetime(99, 1, 1, 0, 0))
def test_31_ad(self):
self.assertEqual(parse('0031-01-01T00:00:00'),
datetime(31, 1, 1, 0, 0))
def testInvalidDay(self):
with self.assertRaises(ValueError):
parse("Feb 30, 2007")
def testUnspecifiedDayFallback(self):
# Test that for an unspecified day, the fallback behavior is correct.
self.assertEqual(parse("April 2009", default=datetime(2010, 1, 31)),
datetime(2009, 4, 30))
def testUnspecifiedDayFallbackFebNoLeapYear(self):
self.assertEqual(parse("Feb 2007", default=datetime(2010, 1, 31)),
datetime(2007, 2, 28))
def testUnspecifiedDayFallbackFebLeapYear(self):
self.assertEqual(parse("Feb 2008", default=datetime(2010, 1, 31)),
datetime(2008, 2, 29))
def testErrorType01(self):
self.assertRaises(ValueError,
parse, 'shouldfail')
def testCorrectErrorOnFuzzyWithTokens(self):
assertRaisesRegex(self, ValueError, 'Unknown string format',
parse, '04/04/32/423', fuzzy_with_tokens=True)
assertRaisesRegex(self, ValueError, 'Unknown string format',
parse, '04/04/04 +32423', fuzzy_with_tokens=True)
assertRaisesRegex(self, ValueError, 'Unknown string format',
parse, '04/04/0d4', fuzzy_with_tokens=True)
def testIncreasingCTime(self):
# This test will check 200 different years, every month, every day,
# every hour, every minute, every second, and every weekday, using
# a delta of more or less 1 year, 1 month, 1 day, 1 minute and
# 1 second.
delta = timedelta(days=365+31+1, seconds=1+60+60*60)
dt = datetime(1900, 1, 1, 0, 0, 0, 0)
for i in range(200):
self.assertEqual(parse(dt.ctime()), dt)
dt += delta
def testIncreasingISOFormat(self):
delta = timedelta(days=365+31+1, seconds=1+60+60*60)
dt = datetime(1900, 1, 1, 0, 0, 0, 0)
for i in range(200):
self.assertEqual(parse(dt.isoformat()), dt)
dt += delta
def testMicrosecondsPrecisionError(self):
# Skip found out that sad precision problem. :-(
dt1 = parse("00:11:25.01")
dt2 = parse("00:12:10.01")
self.assertEqual(dt1.microsecond, 10000)
self.assertEqual(dt2.microsecond, 10000)
def testMicrosecondPrecisionErrorReturns(self):
# One more precision issue, discovered by Eric Brown. This should
# be the last one, as we're no longer using floating points.
for ms in [100001, 100000, 99999, 99998,
10001, 10000, 9999, 9998,
1001, 1000, 999, 998,
101, 100, 99, 98]:
dt = datetime(2008, 2, 27, 21, 26, 1, ms)
self.assertEqual(parse(dt.isoformat()), dt)
def testHighPrecisionSeconds(self):
self.assertEqual(parse("20080227T21:26:01.123456789"),
datetime(2008, 2, 27, 21, 26, 1, 123456))
def testCustomParserInfo(self):
# Custom parser info wasn't working, as Michael Elsdörfer discovered.
from dateutil.parser import parserinfo, parser
class myparserinfo(parserinfo):
MONTHS = parserinfo.MONTHS[:]
MONTHS[0] = ("Foo", "Foo")
myparser = parser(myparserinfo())
dt = myparser.parse("01/Foo/2007")
self.assertEqual(dt, datetime(2007, 1, 1))
def testCustomParserShortDaynames(self):
# Horacio Hoyos discovered that day names shorter than 3 characters,
# for example two letter German day name abbreviations, don't work:
# https://github.com/dateutil/dateutil/issues/343
from dateutil.parser import parserinfo, parser
class GermanParserInfo(parserinfo):
WEEKDAYS = [("Mo", "Montag"),
("Di", "Dienstag"),
("Mi", "Mittwoch"),
("Do", "Donnerstag"),
("Fr", "Freitag"),
("Sa", "Samstag"),
("So", "Sonntag")]
myparser = parser(GermanParserInfo())
dt = myparser.parse("Sa 21. Jan 2017")
self.assertEqual(dt, datetime(2017, 1, 21))
def testNoYearFirstNoDayFirst(self):
dtstr = '090107'
# Should be MMDDYY
self.assertEqual(parse(dtstr),
datetime(2007, 9, 1))
self.assertEqual(parse(dtstr, yearfirst=False, dayfirst=False),
datetime(2007, 9, 1))
def testYearFirst(self):
dtstr = '090107'
# Should be MMDDYY
self.assertEqual(parse(dtstr, yearfirst=True),
datetime(2009, 1, 7))
self.assertEqual(parse(dtstr, yearfirst=True, dayfirst=False),
datetime(2009, 1, 7))
def testDayFirst(self):
dtstr = '090107'
# Should be DDMMYY
self.assertEqual(parse(dtstr, dayfirst=True),
datetime(2007, 1, 9))
self.assertEqual(parse(dtstr, yearfirst=False, dayfirst=True),
datetime(2007, 1, 9))
def testDayFirstYearFirst(self):
dtstr = '090107'
# Should be YYDDMM
self.assertEqual(parse(dtstr, yearfirst=True, dayfirst=True),
datetime(2009, 7, 1))
def testUnambiguousYearFirst(self):
dtstr = '2015 09 25'
self.assertEqual(parse(dtstr, yearfirst=True),
datetime(2015, 9, 25))
def testUnambiguousDayFirst(self):
dtstr = '2015 09 25'
self.assertEqual(parse(dtstr, dayfirst=True),
datetime(2015, 9, 25))
def testUnambiguousDayFirstYearFirst(self):
dtstr = '2015 09 25'
self.assertEqual(parse(dtstr, dayfirst=True, yearfirst=True),
datetime(2015, 9, 25))

View File

@@ -1,577 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from ._common import unittest, WarningTestMixin, NotAValue
import calendar
from datetime import datetime, date, timedelta
from dateutil.relativedelta import relativedelta, MO, TU, WE, FR, SU
class RelativeDeltaTest(WarningTestMixin, unittest.TestCase):
now = datetime(2003, 9, 17, 20, 54, 47, 282310)
today = date(2003, 9, 17)
def testInheritance(self):
# Ensure that relativedelta is inheritance-friendly.
class rdChildClass(relativedelta):
pass
ccRD = rdChildClass(years=1, months=1, days=1, leapdays=1, weeks=1,
hours=1, minutes=1, seconds=1, microseconds=1)
rd = relativedelta(years=1, months=1, days=1, leapdays=1, weeks=1,
hours=1, minutes=1, seconds=1, microseconds=1)
self.assertEqual(type(ccRD + rd), type(ccRD),
msg='Addition does not inherit type.')
self.assertEqual(type(ccRD - rd), type(ccRD),
msg='Subtraction does not inherit type.')
self.assertEqual(type(-ccRD), type(ccRD),
msg='Negation does not inherit type.')
self.assertEqual(type(ccRD * 5.0), type(ccRD),
msg='Multiplication does not inherit type.')
self.assertEqual(type(ccRD / 5.0), type(ccRD),
msg='Division does not inherit type.')
def testMonthEndMonthBeginning(self):
self.assertEqual(relativedelta(datetime(2003, 1, 31, 23, 59, 59),
datetime(2003, 3, 1, 0, 0, 0)),
relativedelta(months=-1, seconds=-1))
self.assertEqual(relativedelta(datetime(2003, 3, 1, 0, 0, 0),
datetime(2003, 1, 31, 23, 59, 59)),
relativedelta(months=1, seconds=1))
def testMonthEndMonthBeginningLeapYear(self):
self.assertEqual(relativedelta(datetime(2012, 1, 31, 23, 59, 59),
datetime(2012, 3, 1, 0, 0, 0)),
relativedelta(months=-1, seconds=-1))
self.assertEqual(relativedelta(datetime(2003, 3, 1, 0, 0, 0),
datetime(2003, 1, 31, 23, 59, 59)),
relativedelta(months=1, seconds=1))
def testNextMonth(self):
self.assertEqual(self.now+relativedelta(months=+1),
datetime(2003, 10, 17, 20, 54, 47, 282310))
def testNextMonthPlusOneWeek(self):
self.assertEqual(self.now+relativedelta(months=+1, weeks=+1),
datetime(2003, 10, 24, 20, 54, 47, 282310))
def testNextMonthPlusOneWeek10am(self):
self.assertEqual(self.today +
relativedelta(months=+1, weeks=+1, hour=10),
datetime(2003, 10, 24, 10, 0))
def testNextMonthPlusOneWeek10amDiff(self):
self.assertEqual(relativedelta(datetime(2003, 10, 24, 10, 0),
self.today),
relativedelta(months=+1, days=+7, hours=+10))
def testOneMonthBeforeOneYear(self):
self.assertEqual(self.now+relativedelta(years=+1, months=-1),
datetime(2004, 8, 17, 20, 54, 47, 282310))
def testMonthsOfDiffNumOfDays(self):
self.assertEqual(date(2003, 1, 27)+relativedelta(months=+1),
date(2003, 2, 27))
self.assertEqual(date(2003, 1, 31)+relativedelta(months=+1),
date(2003, 2, 28))
self.assertEqual(date(2003, 1, 31)+relativedelta(months=+2),
date(2003, 3, 31))
def testMonthsOfDiffNumOfDaysWithYears(self):
self.assertEqual(date(2000, 2, 28)+relativedelta(years=+1),
date(2001, 2, 28))
self.assertEqual(date(2000, 2, 29)+relativedelta(years=+1),
date(2001, 2, 28))
self.assertEqual(date(1999, 2, 28)+relativedelta(years=+1),
date(2000, 2, 28))
self.assertEqual(date(1999, 3, 1)+relativedelta(years=+1),
date(2000, 3, 1))
self.assertEqual(date(1999, 3, 1)+relativedelta(years=+1),
date(2000, 3, 1))
self.assertEqual(date(2001, 2, 28)+relativedelta(years=-1),
date(2000, 2, 28))
self.assertEqual(date(2001, 3, 1)+relativedelta(years=-1),
date(2000, 3, 1))
def testNextFriday(self):
self.assertEqual(self.today+relativedelta(weekday=FR),
date(2003, 9, 19))
def testNextFridayInt(self):
self.assertEqual(self.today+relativedelta(weekday=calendar.FRIDAY),
date(2003, 9, 19))
def testLastFridayInThisMonth(self):
self.assertEqual(self.today+relativedelta(day=31, weekday=FR(-1)),
date(2003, 9, 26))
def testNextWednesdayIsToday(self):
self.assertEqual(self.today+relativedelta(weekday=WE),
date(2003, 9, 17))
def testNextWenesdayNotToday(self):
self.assertEqual(self.today+relativedelta(days=+1, weekday=WE),
date(2003, 9, 24))
def test15thISOYearWeek(self):
self.assertEqual(date(2003, 1, 1) +
relativedelta(day=4, weeks=+14, weekday=MO(-1)),
date(2003, 4, 7))
def testMillenniumAge(self):
self.assertEqual(relativedelta(self.now, date(2001, 1, 1)),
relativedelta(years=+2, months=+8, days=+16,
hours=+20, minutes=+54, seconds=+47,
microseconds=+282310))
def testJohnAge(self):
self.assertEqual(relativedelta(self.now,
datetime(1978, 4, 5, 12, 0)),
relativedelta(years=+25, months=+5, days=+12,
hours=+8, minutes=+54, seconds=+47,
microseconds=+282310))
def testJohnAgeWithDate(self):
self.assertEqual(relativedelta(self.today,
datetime(1978, 4, 5, 12, 0)),
relativedelta(years=+25, months=+5, days=+11,
hours=+12))
def testYearDay(self):
self.assertEqual(date(2003, 1, 1)+relativedelta(yearday=260),
date(2003, 9, 17))
self.assertEqual(date(2002, 1, 1)+relativedelta(yearday=260),
date(2002, 9, 17))
self.assertEqual(date(2000, 1, 1)+relativedelta(yearday=260),
date(2000, 9, 16))
self.assertEqual(self.today+relativedelta(yearday=261),
date(2003, 9, 18))
def testYearDayBug(self):
# Tests a problem reported by Adam Ryan.
self.assertEqual(date(2010, 1, 1)+relativedelta(yearday=15),
date(2010, 1, 15))
def testNonLeapYearDay(self):
self.assertEqual(date(2003, 1, 1)+relativedelta(nlyearday=260),
date(2003, 9, 17))
self.assertEqual(date(2002, 1, 1)+relativedelta(nlyearday=260),
date(2002, 9, 17))
self.assertEqual(date(2000, 1, 1)+relativedelta(nlyearday=260),
date(2000, 9, 17))
self.assertEqual(self.today+relativedelta(yearday=261),
date(2003, 9, 18))
def testAddition(self):
self.assertEqual(relativedelta(days=10) +
relativedelta(years=1, months=2, days=3, hours=4,
minutes=5, microseconds=6),
relativedelta(years=1, months=2, days=13, hours=4,
minutes=5, microseconds=6))
def testAbsoluteAddition(self):
self.assertEqual(relativedelta() + relativedelta(day=0, hour=0),
relativedelta(day=0, hour=0))
self.assertEqual(relativedelta(day=0, hour=0) + relativedelta(),
relativedelta(day=0, hour=0))
def testAdditionToDatetime(self):
self.assertEqual(datetime(2000, 1, 1) + relativedelta(days=1),
datetime(2000, 1, 2))
def testRightAdditionToDatetime(self):
self.assertEqual(relativedelta(days=1) + datetime(2000, 1, 1),
datetime(2000, 1, 2))
def testAdditionInvalidType(self):
with self.assertRaises(TypeError):
relativedelta(days=3) + 9
def testAdditionUnsupportedType(self):
# For unsupported types that define their own comparators, etc.
self.assertIs(relativedelta(days=1) + NotAValue, NotAValue)
def testSubtraction(self):
self.assertEqual(relativedelta(days=10) -
relativedelta(years=1, months=2, days=3, hours=4,
minutes=5, microseconds=6),
relativedelta(years=-1, months=-2, days=7, hours=-4,
minutes=-5, microseconds=-6))
def testRightSubtractionFromDatetime(self):
self.assertEqual(datetime(2000, 1, 2) - relativedelta(days=1),
datetime(2000, 1, 1))
def testSubractionWithDatetime(self):
self.assertRaises(TypeError, lambda x, y: x - y,
(relativedelta(days=1), datetime(2000, 1, 1)))
def testSubtractionInvalidType(self):
with self.assertRaises(TypeError):
relativedelta(hours=12) - 14
def testSubtractionUnsupportedType(self):
self.assertIs(relativedelta(days=1) + NotAValue, NotAValue)
def testMultiplication(self):
self.assertEqual(datetime(2000, 1, 1) + relativedelta(days=1) * 28,
datetime(2000, 1, 29))
self.assertEqual(datetime(2000, 1, 1) + 28 * relativedelta(days=1),
datetime(2000, 1, 29))
def testMultiplicationUnsupportedType(self):
self.assertIs(relativedelta(days=1) * NotAValue, NotAValue)
def testDivision(self):
self.assertEqual(datetime(2000, 1, 1) + relativedelta(days=28) / 28,
datetime(2000, 1, 2))
def testDivisionUnsupportedType(self):
self.assertIs(relativedelta(days=1) / NotAValue, NotAValue)
def testBoolean(self):
self.assertFalse(relativedelta(days=0))
self.assertTrue(relativedelta(days=1))
def testComparison(self):
d1 = relativedelta(years=1, months=1, days=1, leapdays=0, hours=1,
minutes=1, seconds=1, microseconds=1)
d2 = relativedelta(years=1, months=1, days=1, leapdays=0, hours=1,
minutes=1, seconds=1, microseconds=1)
d3 = relativedelta(years=1, months=1, days=1, leapdays=0, hours=1,
minutes=1, seconds=1, microseconds=2)
self.assertEqual(d1, d2)
self.assertNotEqual(d1, d3)
def testInequalityTypeMismatch(self):
# Different type
self.assertFalse(relativedelta(year=1) == 19)
def testInequalityUnsupportedType(self):
self.assertIs(relativedelta(hours=3) == NotAValue, NotAValue)
def testInequalityWeekdays(self):
# Different weekdays
no_wday = relativedelta(year=1997, month=4)
wday_mo_1 = relativedelta(year=1997, month=4, weekday=MO(+1))
wday_mo_2 = relativedelta(year=1997, month=4, weekday=MO(+2))
wday_tu = relativedelta(year=1997, month=4, weekday=TU)
self.assertTrue(wday_mo_1 == wday_mo_1)
self.assertFalse(no_wday == wday_mo_1)
self.assertFalse(wday_mo_1 == no_wday)
self.assertFalse(wday_mo_1 == wday_mo_2)
self.assertFalse(wday_mo_2 == wday_mo_1)
self.assertFalse(wday_mo_1 == wday_tu)
self.assertFalse(wday_tu == wday_mo_1)
def testMonthOverflow(self):
self.assertEqual(relativedelta(months=273),
relativedelta(years=22, months=9))
def testWeeks(self):
# Test that the weeks property is working properly.
rd = relativedelta(years=4, months=2, weeks=8, days=6)
self.assertEqual((rd.weeks, rd.days), (8, 8 * 7 + 6))
rd.weeks = 3
self.assertEqual((rd.weeks, rd.days), (3, 3 * 7 + 6))
def testRelativeDeltaRepr(self):
self.assertEqual(repr(relativedelta(years=1, months=-1, days=15)),
'relativedelta(years=+1, months=-1, days=+15)')
self.assertEqual(repr(relativedelta(months=14, seconds=-25)),
'relativedelta(years=+1, months=+2, seconds=-25)')
self.assertEqual(repr(relativedelta(month=3, hour=3, weekday=SU(3))),
'relativedelta(month=3, weekday=SU(+3), hour=3)')
def testRelativeDeltaFractionalYear(self):
with self.assertRaises(ValueError):
relativedelta(years=1.5)
def testRelativeDeltaFractionalMonth(self):
with self.assertRaises(ValueError):
relativedelta(months=1.5)
def testRelativeDeltaFractionalAbsolutes(self):
# Fractional absolute values will soon be unsupported,
# check for the deprecation warning.
with self.assertWarns(DeprecationWarning):
relativedelta(year=2.86)
with self.assertWarns(DeprecationWarning):
relativedelta(month=1.29)
with self.assertWarns(DeprecationWarning):
relativedelta(day=0.44)
with self.assertWarns(DeprecationWarning):
relativedelta(hour=23.98)
with self.assertWarns(DeprecationWarning):
relativedelta(minute=45.21)
with self.assertWarns(DeprecationWarning):
relativedelta(second=13.2)
with self.assertWarns(DeprecationWarning):
relativedelta(microsecond=157221.93)
def testRelativeDeltaFractionalRepr(self):
rd = relativedelta(years=3, months=-2, days=1.25)
self.assertEqual(repr(rd),
'relativedelta(years=+3, months=-2, days=+1.25)')
rd = relativedelta(hours=0.5, seconds=9.22)
self.assertEqual(repr(rd),
'relativedelta(hours=+0.5, seconds=+9.22)')
def testRelativeDeltaFractionalWeeks(self):
# Equivalent to days=8, hours=18
rd = relativedelta(weeks=1.25)
d1 = datetime(2009, 9, 3, 0, 0)
self.assertEqual(d1 + rd,
datetime(2009, 9, 11, 18))
def testRelativeDeltaFractionalDays(self):
rd1 = relativedelta(days=1.48)
d1 = datetime(2009, 9, 3, 0, 0)
self.assertEqual(d1 + rd1,
datetime(2009, 9, 4, 11, 31, 12))
rd2 = relativedelta(days=1.5)
self.assertEqual(d1 + rd2,
datetime(2009, 9, 4, 12, 0, 0))
def testRelativeDeltaFractionalHours(self):
rd = relativedelta(days=1, hours=12.5)
d1 = datetime(2009, 9, 3, 0, 0)
self.assertEqual(d1 + rd,
datetime(2009, 9, 4, 12, 30, 0))
def testRelativeDeltaFractionalMinutes(self):
rd = relativedelta(hours=1, minutes=30.5)
d1 = datetime(2009, 9, 3, 0, 0)
self.assertEqual(d1 + rd,
datetime(2009, 9, 3, 1, 30, 30))
def testRelativeDeltaFractionalSeconds(self):
rd = relativedelta(hours=5, minutes=30, seconds=30.5)
d1 = datetime(2009, 9, 3, 0, 0)
self.assertEqual(d1 + rd,
datetime(2009, 9, 3, 5, 30, 30, 500000))
def testRelativeDeltaFractionalPositiveOverflow(self):
# Equivalent to (days=1, hours=14)
rd1 = relativedelta(days=1.5, hours=2)
d1 = datetime(2009, 9, 3, 0, 0)
self.assertEqual(d1 + rd1,
datetime(2009, 9, 4, 14, 0, 0))
# Equivalent to (days=1, hours=14, minutes=45)
rd2 = relativedelta(days=1.5, hours=2.5, minutes=15)
d1 = datetime(2009, 9, 3, 0, 0)
self.assertEqual(d1 + rd2,
datetime(2009, 9, 4, 14, 45))
# Carry back up - equivalent to (days=2, hours=2, minutes=0, seconds=1)
rd3 = relativedelta(days=1.5, hours=13, minutes=59.5, seconds=31)
self.assertEqual(d1 + rd3,
datetime(2009, 9, 5, 2, 0, 1))
def testRelativeDeltaFractionalNegativeDays(self):
# Equivalent to (days=-1, hours=-1)
rd1 = relativedelta(days=-1.5, hours=11)
d1 = datetime(2009, 9, 3, 12, 0)
self.assertEqual(d1 + rd1,
datetime(2009, 9, 2, 11, 0, 0))
# Equivalent to (days=-1, hours=-9)
rd2 = relativedelta(days=-1.25, hours=-3)
self.assertEqual(d1 + rd2,
datetime(2009, 9, 2, 3))
def testRelativeDeltaNormalizeFractionalDays(self):
# Equivalent to (days=2, hours=18)
rd1 = relativedelta(days=2.75)
self.assertEqual(rd1.normalized(), relativedelta(days=2, hours=18))
# Equvalent to (days=1, hours=11, minutes=31, seconds=12)
rd2 = relativedelta(days=1.48)
self.assertEqual(rd2.normalized(),
relativedelta(days=1, hours=11, minutes=31, seconds=12))
def testRelativeDeltaNormalizeFractionalDays2(self):
# Equivalent to (hours=1, minutes=30)
rd1 = relativedelta(hours=1.5)
self.assertEqual(rd1.normalized(), relativedelta(hours=1, minutes=30))
# Equivalent to (hours=3, minutes=17, seconds=5, microseconds=100)
rd2 = relativedelta(hours=3.28472225)
self.assertEqual(rd2.normalized(),
relativedelta(hours=3, minutes=17, seconds=5, microseconds=100))
def testRelativeDeltaNormalizeFractionalMinutes(self):
# Equivalent to (minutes=15, seconds=36)
rd1 = relativedelta(minutes=15.6)
self.assertEqual(rd1.normalized(),
relativedelta(minutes=15, seconds=36))
# Equivalent to (minutes=25, seconds=20, microseconds=25000)
rd2 = relativedelta(minutes=25.33375)
self.assertEqual(rd2.normalized(),
relativedelta(minutes=25, seconds=20, microseconds=25000))
def testRelativeDeltaNormalizeFractionalSeconds(self):
# Equivalent to (seconds=45, microseconds=25000)
rd1 = relativedelta(seconds=45.025)
self.assertEqual(rd1.normalized(),
relativedelta(seconds=45, microseconds=25000))
def testRelativeDeltaFractionalPositiveOverflow2(self):
# Equivalent to (days=1, hours=14)
rd1 = relativedelta(days=1.5, hours=2)
self.assertEqual(rd1.normalized(),
relativedelta(days=1, hours=14))
# Equivalent to (days=1, hours=14, minutes=45)
rd2 = relativedelta(days=1.5, hours=2.5, minutes=15)
self.assertEqual(rd2.normalized(),
relativedelta(days=1, hours=14, minutes=45))
# Carry back up - equivalent to:
# (days=2, hours=2, minutes=0, seconds=2, microseconds=3)
rd3 = relativedelta(days=1.5, hours=13, minutes=59.50045,
seconds=31.473, microseconds=500003)
self.assertEqual(rd3.normalized(),
relativedelta(days=2, hours=2, minutes=0,
seconds=2, microseconds=3))
def testRelativeDeltaFractionalNegativeOverflow(self):
# Equivalent to (days=-1)
rd1 = relativedelta(days=-0.5, hours=-12)
self.assertEqual(rd1.normalized(),
relativedelta(days=-1))
# Equivalent to (days=-1)
rd2 = relativedelta(days=-1.5, hours=12)
self.assertEqual(rd2.normalized(),
relativedelta(days=-1))
# Equivalent to (days=-1, hours=-14, minutes=-45)
rd3 = relativedelta(days=-1.5, hours=-2.5, minutes=-15)
self.assertEqual(rd3.normalized(),
relativedelta(days=-1, hours=-14, minutes=-45))
# Equivalent to (days=-1, hours=-14, minutes=+15)
rd4 = relativedelta(days=-1.5, hours=-2.5, minutes=45)
self.assertEqual(rd4.normalized(),
relativedelta(days=-1, hours=-14, minutes=+15))
# Carry back up - equivalent to:
# (days=-2, hours=-2, minutes=0, seconds=-2, microseconds=-3)
rd3 = relativedelta(days=-1.5, hours=-13, minutes=-59.50045,
seconds=-31.473, microseconds=-500003)
self.assertEqual(rd3.normalized(),
relativedelta(days=-2, hours=-2, minutes=0,
seconds=-2, microseconds=-3))
def testInvalidYearDay(self):
with self.assertRaises(ValueError):
relativedelta(yearday=367)
def testAddTimedeltaToUnpopulatedRelativedelta(self):
td = timedelta(
days=1,
seconds=1,
microseconds=1,
milliseconds=1,
minutes=1,
hours=1,
weeks=1
)
expected = relativedelta(
weeks=1,
days=1,
hours=1,
minutes=1,
seconds=1,
microseconds=1001
)
self.assertEqual(expected, relativedelta() + td)
def testAddTimedeltaToPopulatedRelativeDelta(self):
td = timedelta(
days=1,
seconds=1,
microseconds=1,
milliseconds=1,
minutes=1,
hours=1,
weeks=1
)
rd = relativedelta(
year=1,
month=1,
day=1,
hour=1,
minute=1,
second=1,
microsecond=1,
years=1,
months=1,
days=1,
weeks=1,
hours=1,
minutes=1,
seconds=1,
microseconds=1
)
expected = relativedelta(
year=1,
month=1,
day=1,
hour=1,
minute=1,
second=1,
microsecond=1,
years=1,
months=1,
weeks=2,
days=2,
hours=2,
minutes=2,
seconds=2,
microseconds=1002,
)
self.assertEqual(expected, rd + td)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff