diff --git a/.gitignore b/.gitignore index f9265d7..ba2998e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,13 @@ -*.pyc +*app.pyc +t/ +eggs +\.*.cfg +b.sh +develop-eggs +parts +bin + +*minitage* *.egg-info *.pyc *.swp diff --git a/CONTRIBUTORS b/CONTRIBUTORS deleted file mode 100644 index 479f357..0000000 --- a/CONTRIBUTORS +++ /dev/null @@ -1,12 +0,0 @@ -Thank you to all who have contributed to this project! -If you contributed and not listed below please let me know. - -mrmachine -Hinnack -shazow -kiorky -jlsandell -mag009 -djmitche -GreatCombinator -chris-baynes diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..7ea4bae --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,6 @@ +include *.txt *.cfg *.rst + +recursive-include docs * +recursive-include src * + +global-exclude *pyc diff --git a/README b/README deleted file mode 100644 index 871d2de..0000000 --- a/README +++ /dev/null @@ -1,8 +0,0 @@ - _ _ - ___ _ __ ___ _ __ (_) |_ ___ _ __ - / __| '__/ _ \| '_ \| | __/ _ \ '__| - | (__| | | (_) | | | | | || __/ | - \___|_| \___/|_| |_|_|\__\___|_| - -croniter provides iteration for datetime object with cron like format. -Website: https://github.com/taichino/croniter diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..eed36da --- /dev/null +++ b/README.rst @@ -0,0 +1,96 @@ +Introduction +============ + +.. contents:: + + +croniter provides iteration for datetime object with cron like format. + +:: + + _ _ + ___ _ __ ___ _ __ (_) |_ ___ _ __ + / __| '__/ _ \| '_ \| | __/ _ \ '__| + | (__| | | (_) | | | | | || __/ | + \___|_| \___/|_| |_|_|\__\___|_| + + +Website: https://github.com/taichino/croniter + +Usage +============ + +Simple example of usage is followings:: + + >>> from croniter import croniter + >>> from datetime import datetime + >>> base = datetime(2010, 1, 25, 4, 46) + >>> iter = croniter('*/5 * * * *', base) # every 5 minites + >>> print iter.get_next(datetime) # 2010-01-25 04:50:00 + >>> print iter.get_next(datetime) # 2010-01-25 04:55:00 + >>> print iter.get_next(datetime) # 2010-01-25 05:00:00 + >>> + >>> iter = croniter('2 4 * * mon,fri', base) # 04:02 on every Monday and Friday + >>> print iter.get_next(datetime) # 2010-01-26 04:02:00 + >>> print iter.get_next(datetime) # 2010-01-30 04:02:00 + >>> print iter.get_next(datetime) # 2010-02-02 04:02:00 + +All you need to know is constructor and get_next, these signature are following:: + + >>> def __init__(self, cron_format, start_time=time.time()) + +croniter iterate along with 'cron_format' from 'start_time'. +cron_format is 'min hour day month day_of_week', and please refer to +http://en.wikipedia.org/wiki/Cron for details.:: + + >>> def get_next(self, ret_type=float) + +get_next return next time in iteration with 'ret_type'. +And ret_type accept only 'float' or 'datetime'. + +Now, supported get_prev method. (>= 0.2.0):: + + >>> base = datetime(2010, 8, 25) + >>> itr = croniter('0 0 1 * *', base) + >>> print itr.get_prev(datetime) # 2010-08-01 00:00:00 + >>> print itr.get_prev(datetime) # 2010-07-01 00:00:00 + >>> print itr.get_prev(datetime) # 2010-06-01 00:00:00 + + +Develop this package +==================== + +:: + + git clone https://github.com/taichino/croniter.git + cd croniter + python bootstrap.py -d + bin/buildout -vvvvvvN + bin/test + + +Make a new release +==================== +We use zest.fullreleaser, a great releaser infrastructure. + +Do and follow the instructions +:: + + bin/fullrelease + + +Contributors +=============== +Thank you to all who have contributed to this project! +If you contributed and not listed below please let me know. + + - mrmachine + - Hinnack + - shazow + - kiorky + - jlsandell + - mag009 + - djmitche + - GreatCombinator + - chris-baynes + diff --git a/bootstrap.py b/bootstrap.py new file mode 100644 index 0000000..1980d6c --- /dev/null +++ b/bootstrap.py @@ -0,0 +1,121 @@ +############################################################################## +# +# Copyright (c) 2006 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Bootstrap a buildout-based project + +Simply run this script in a directory containing a buildout.cfg. +The script accepts buildout command-line options, so you can +use the -c option to specify an alternate configuration file. +""" + +import os, shutil, sys, tempfile, urllib2 +from optparse import OptionParser + +tmpeggs = tempfile.mkdtemp() + +is_jython = sys.platform.startswith('java') + +# parsing arguments +parser = OptionParser() +parser.add_option("-v", "--version", dest="version", + help="use a specific zc.buildout version") +parser.add_option("-d", "--distribute", + action="store_true", dest="distribute", default=False, + help="Use Disribute rather than Setuptools.") + +parser.add_option("-c", None, action="store", dest="config_file", + help=("Specify the path to the buildout configuration " + "file to be used.")) + +options, args = parser.parse_args() + +# if -c was provided, we push it back into args for buildout' main function +if options.config_file is not None: + args += ['-c', options.config_file] + +if options.version is not None: + VERSION = '==%s' % options.version +else: + VERSION = '' + +# We decided to always use distribute, make sure this is the default for us +# USE_DISTRIBUTE = options.distribute +USE_DISTRIBUTE = True +args = args + ['bootstrap'] + +to_reload = False +try: + import pkg_resources + if not hasattr(pkg_resources, '_distribute'): + to_reload = True + raise ImportError +except ImportError: + ez = {} + if USE_DISTRIBUTE: + exec urllib2.urlopen('http://python-distribute.org/distribute_setup.py' + ).read() in ez + ez['use_setuptools'](to_dir=tmpeggs, download_delay=0, no_fake=True) + else: + exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py' + ).read() in ez + ez['use_setuptools'](to_dir=tmpeggs, download_delay=0) + + if to_reload: + reload(pkg_resources) + else: + import pkg_resources + +if sys.platform == 'win32': + def quote(c): + if ' ' in c: + return '"%s"' % c # work around spawn lamosity on windows + else: + return c +else: + def quote (c): + return c + +cmd = 'from setuptools.command.easy_install import main; main()' +ws = pkg_resources.working_set + +if USE_DISTRIBUTE: + requirement = 'distribute' +else: + requirement = 'setuptools' + +if is_jython: + import subprocess + + assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd', + quote(tmpeggs), 'zc.buildout' + VERSION], + env=dict(os.environ, + PYTHONPATH= + ws.find(pkg_resources.Requirement.parse(requirement)).location + ), + ).wait() == 0 + +else: + assert os.spawnle( + os.P_WAIT, sys.executable, quote (sys.executable), + '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION, + dict(os.environ, + PYTHONPATH= + ws.find(pkg_resources.Requirement.parse(requirement)).location + ), + ) == 0 + +ws.add_entry(tmpeggs) +ws.require('zc.buildout' + VERSION) +import zc.buildout.buildout +zc.buildout.buildout.main(args) +shutil.rmtree(tmpeggs) diff --git a/buildout.cfg b/buildout.cfg new file mode 100644 index 0000000..e613877 --- /dev/null +++ b/buildout.cfg @@ -0,0 +1,84 @@ +[buildout] +package-name = croniter +develop = . +parts = + scripts + omelette + test + coverage + report + report-xml +extensions = + mr.developer + buildout.minitagificator + buildout.dumppickedversions +auto-checkout = +sources-dir=${buildout:directory}/src.mrdeveloper +test-eggs= ${buildout:package-name} [test] +eggs = ${buildout:package-name} + +[sources] + +[scripts] +recipe=zc.recipe.egg +eggs = ${buildout:eggs} + zest.releaser + ipython +interpreter = scripts +scripts = + ipython + croniter + fullrelease + paster + nose + nosetests + fullrelease + lasttagdiff + lasttaglog + longtest + postrelease + prerelease + release + rst2html.py + rst2latex.py + rst2man.py + rst2odt_prepstyles.py + rst2odt.py + rst2pseudoxml.py + rst2s5.py + rst2xetex.py + rst2xml.py + rstpep2html.py + +[test] +recipe = zc.recipe.testrunner +defaults = ['-v', '-s', '${buildout:package-name}'] +eggs = ${buildout:test-eggs} + +[coverage] +recipe = zc.recipe.egg +eggs = coverage +initialization = +include = '--source=${buildout:directory}/src' +sys.argv = sys.argv[:] + ['run', include, 'bin/test', '--xml'] + +[report] +recipe = zc.recipe.egg +eggs = coverage +scripts = coverage=report +initialization = +sys.argv = sys.argv[:] + ['html', '-i'] + +[report-xml] +recipe = zc.recipe.egg +eggs = coverage +scripts = coverage=report-xml +initialization = +sys.argv = sys.argv[:] + ['xml', '-i'] + +[omelette] +recipe = collective.recipe.omelette +eggs = ${scripts:eggs} + +[versions] + diff --git a/croniter/__init__.py b/croniter/__init__.py deleted file mode 100644 index e749a57..0000000 --- a/croniter/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- -# defer imports to be accesible in setup.py -from _release import ( - __doc__, - __author__, - __version__, - __license__, -) -from croniter import croniter diff --git a/croniter/_release.py b/croniter/_release.py deleted file mode 100644 index 7180b80..0000000 --- a/croniter/_release.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -__author__ = "Matsumoto Taichi" -__version__ = "0.3.2" -__license__ = "MIT License" -__doc__ = """ - Simple example of usage is followings - - >>> from croniter import croniter - >>> from datetime import datetime - >>> base = datetime(2010, 1, 25, 4, 46) - >>> iter = croniter('*/5 * * * *', base) # every 5 minites - >>> print iter.get_next(datetime) # 2010-01-25 04:50:00 - >>> print iter.get_next(datetime) # 2010-01-25 04:55:00 - >>> print iter.get_next(datetime) # 2010-01-25 05:00:00 - >>> - >>> iter = croniter('2 4 * * mon,fri', base) # 04:02 on every Monday and Friday - >>> print iter.get_next(datetime) # 2010-01-26 04:02:00 - >>> print iter.get_next(datetime) # 2010-01-30 04:02:00 - >>> print iter.get_next(datetime) # 2010-02-02 04:02:00 - - All you need to know is constructor and get_next, these signature are following. - - >>> def __init__(self, cron_format, start_time=time.time()) - - croniter iterate along with 'cron_format' from 'start_time'. - cron_format is 'min hour day month day_of_week', and please refer to - http://en.wikipedia.org/wiki/Cron for details. - - >>> def get_next(self, ret_type=float) - - get_next return next time in iteration with 'ret_type'. - And ret_type accept only 'float' or 'datetime'. - - Now, supported get_prev method. (>= 0.2.0) - - >>> base = datetime(2010, 8, 25) - >>> itr = croniter('0 0 1 * *', base) - >>> print itr.get_prev(datetime) # 2010-08-01 00:00:00 - >>> print itr.get_prev(datetime) # 2010-07-01 00:00:00 - >>> print itr.get_prev(datetime) # 2010-06-01 00:00:00 -""" -# vim:set et sts=4 ts=4 tw=80: diff --git a/croniter/croniter_test.py b/croniter/croniter_test.py deleted file mode 100644 index 9522350..0000000 --- a/croniter/croniter_test.py +++ /dev/null @@ -1,305 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -import unittest -from datetime import datetime -from croniter import croniter - -class CroniterTest(unittest.TestCase): - def testSecond(self): - base = datetime(2012, 4, 6, 13, 26, 10) - itr = croniter('*/1 * * * * *', base) - n1 = itr.get_next(datetime) - self.assertEqual(base.year, n1.year) - self.assertEqual(base.month, n1.month) - self.assertEqual(base.day, n1.day) - self.assertEqual(base.hour, n1.hour) - self.assertEqual(base.minute, n1.minute) - self.assertEqual(base.second + 1, n1.second) - - def testMinute(self): - # minute asterisk - base = datetime(2010, 1, 23, 12, 18) - itr = croniter('*/1 * * * *', base) - n1 = itr.get_next(datetime) # 19 - self.assertEqual(base.year, n1.year) - self.assertEqual(base.month, n1.month) - self.assertEqual(base.day, n1.day) - self.assertEqual(base.hour, n1.hour) - self.assertEqual(base.minute, n1.minute - 1) - for i in range(39): # ~ 58 - itr.get_next() - n2 = itr.get_next(datetime) - self.assertEqual(n2.minute, 59) - n3 = itr.get_next(datetime) - self.assertEqual(n3.minute, 0) - self.assertEqual(n3.hour, 13) - - itr = croniter('*/5 * * * *', base) - n4 = itr.get_next(datetime) - self.assertEqual(n4.minute, 20) - for i in range(6): - itr.get_next() - n5 = itr.get_next(datetime) - self.assertEqual(n5.minute, 55) - n6 = itr.get_next(datetime) - self.assertEqual(n6.minute, 0) - self.assertEqual(n6.hour, 13) - - def testHour(self): - base = datetime(2010, 1, 24, 12, 2) - itr = croniter('0 */3 * * *', base) - n1 = itr.get_next(datetime) - self.assertEqual(n1.hour, 15) - self.assertEqual(n1.minute, 0) - for i in range(2): - itr.get_next() - n2 = itr.get_next(datetime) - self.assertEqual(n2.hour, 0) - self.assertEqual(n2.day, 25) - - def testDay(self): - base = datetime(2010, 2, 24, 12, 9) - itr = croniter('0 0 */3 * *', base) - n1 = itr.get_next(datetime) - self.assertEqual(n1.day, 27) - n2 = itr.get_next(datetime) - self.assertEqual(n2.day, 3) - - # test leap year - base = datetime(1996, 2, 27) - itr = croniter('0 0 * * *', base) - n1 = itr.get_next(datetime) - self.assertEqual(n1.day, 28) - self.assertEqual(n1.month, 2) - n2 = itr.get_next(datetime) - self.assertEqual(n2.day, 29) - self.assertEqual(n2.month, 2) - - base2 = datetime(2000, 2, 27) - itr2 = croniter('0 0 * * *', base2) - n3 = itr2.get_next(datetime) - self.assertEqual(n3.day, 28) - self.assertEqual(n3.month, 2) - n4 = itr2.get_next(datetime) - self.assertEqual(n4.day, 29) - self.assertEqual(n4.month, 2) - - def testWeekDay(self): - base = datetime(2010, 2, 25) - itr = croniter('0 0 * * sat', base) - n1 = itr.get_next(datetime) - self.assertEqual(n1.isoweekday(), 6) - self.assertEqual(n1.day, 27) - n2 = itr.get_next(datetime) - self.assertEqual(n2.isoweekday(), 6) - self.assertEqual(n2.day, 6) - self.assertEqual(n2.month, 3) - - base = datetime(2010, 1, 25) - itr = croniter('0 0 1 * wed', base) - n1 = itr.get_next(datetime) - self.assertEqual(n1.month, 1) - self.assertEqual(n1.day, 27) - self.assertEqual(n1.year, 2010) - n2 = itr.get_next(datetime) - self.assertEqual(n2.month, 2) - self.assertEqual(n2.day, 1) - self.assertEqual(n2.year, 2010) - n3 = itr.get_next(datetime) - self.assertEqual(n3.month, 2) - self.assertEqual(n3.day, 3) - self.assertEqual(n3.year, 2010) - - def testMonth(self): - base = datetime(2010, 1, 25) - itr = croniter('0 0 1 * *', base) - n1 = itr.get_next(datetime) - self.assertEqual(n1.month, 2) - self.assertEqual(n1.day, 1) - n2 = itr.get_next(datetime) - self.assertEqual(n2.month, 3) - self.assertEqual(n2.day, 1) - for i in range(8): - itr.get_next() - n3 = itr.get_next(datetime) - self.assertEqual(n3.month, 12) - self.assertEqual(n3.year, 2010) - n4 = itr.get_next(datetime) - self.assertEqual(n4.month, 1) - self.assertEqual(n4.year, 2011) - - def testError(self): - itr = croniter('* * * * *') - self.assertRaises(TypeError, itr.get_next, str) - self.assertRaises(ValueError, croniter, '* * * *') - self.assertRaises(ValueError, croniter, '* * 5-1 * *') - self.assertRaises(KeyError, croniter, '* * * janu-jun *') - - def testPrevMinute(self): - base = datetime(2010, 8, 25, 15, 56) - itr = croniter('*/1 * * * *', base) - prev = itr.get_prev(datetime) - self.assertEqual(base.year, prev.year) - self.assertEqual(base.month, prev.month) - self.assertEqual(base.day, prev.day) - self.assertEqual(base.hour, prev.hour) - self.assertEqual(base.minute, prev.minute+1) - - base = datetime(2010, 8, 25, 15, 0) - itr = croniter('*/1 * * * *', base) - prev = itr.get_prev(datetime) - self.assertEqual(base.year, prev.year) - self.assertEqual(base.month, prev.month) - self.assertEqual(base.day, prev.day) - self.assertEqual(base.hour, prev.hour+1) - self.assertEqual(59, prev.minute) - - base = datetime(2010, 8, 25, 0, 0) - itr = croniter('*/1 * * * *', base) - prev = itr.get_prev(datetime) - self.assertEqual(base.year, prev.year) - self.assertEqual(base.month, prev.month) - self.assertEqual(base.day, prev.day+1) - self.assertEqual(23, prev.hour) - self.assertEqual(59, prev.minute) - - def testPrevDayOfMonthWithCrossing(self): - """Test getting previous occurrence that crosses into previous month.""" - base = datetime(2012, 3, 15, 0, 0) - itr = croniter('0 0 22 * *', base) - prev = itr.get_prev(datetime) - self.assertEqual(prev.year, 2012) - self.assertEqual(prev.month, 2) - self.assertEqual(prev.day, 22) - self.assertEqual(prev.hour, 0) - self.assertEqual(prev.minute, 0) - - def testPrevWeekDay(self): - base = datetime(2010, 8, 25, 15, 56) - itr = croniter('0 0 * * sat,sun', base) - prev1 = itr.get_prev(datetime) - self.assertEqual(prev1.year, base.year) - self.assertEqual(prev1.month, base.month) - self.assertEqual(prev1.day, 22) - self.assertEqual(prev1.hour, 0) - self.assertEqual(prev1.minute, 0) - - prev2 = itr.get_prev(datetime) - self.assertEqual(prev2.year, base.year) - self.assertEqual(prev2.month, base.month) - self.assertEqual(prev2.day, 21) - self.assertEqual(prev2.hour, 0) - self.assertEqual(prev2.minute, 0) - - prev3 = itr.get_prev(datetime) - self.assertEqual(prev3.year, base.year) - self.assertEqual(prev3.month, base.month) - self.assertEqual(prev3.day, 15) - self.assertEqual(prev3.hour, 0) - self.assertEqual(prev3.minute, 0) - - def testPrevWeekDay2(self): - base = datetime(2010, 8, 25, 15, 56) - itr = croniter('10 0 * * 0', base) - prev = itr.get_prev(datetime) - self.assertEqual(prev.day, 22) - self.assertEqual(prev.hour, 0) - self.assertEqual(prev.minute, 10) - - def testISOWeekday(self): - base = datetime(2010, 2, 25) - itr = croniter('0 0 * * 7', base) - n1 = itr.get_next(datetime) - self.assertEqual(n1.isoweekday(), 7) - self.assertEqual(n1.day, 28) - n2 = itr.get_next(datetime) - self.assertEqual(n2.isoweekday(), 7) - self.assertEqual(n2.day, 7) - self.assertEqual(n2.month, 3) - - def testBug1(self): - base = datetime(2012, 2, 24) - itr = croniter('5 0 */2 * *', base) - n1 = itr.get_prev(datetime) - self.assertEqual(n1.day, 22) - self.assertEqual(n1.hour, 0) - self.assertEqual(n1.minute, 5) - - def testBug2(self): - base = datetime(2012, 01, 01, 00, 00) - iter = croniter('0 * * 3 *', base) - n1 = iter.get_next(datetime) - self.assertEqual(n1.year, base.year) - self.assertEqual(n1.month, 3) - self.assertEqual(n1.day, base.day) - self.assertEqual(n1.hour, base.hour) - self.assertEqual(n1.minute, base.minute) - - n2 = iter.get_next(datetime) - self.assertEqual(n2.year, base.year) - self.assertEqual(n2.month, 3) - self.assertEqual(n2.day, base.day) - self.assertEqual(n2.hour, base.hour + 1) - self.assertEqual(n2.minute, base.minute) - - n3 = iter.get_next(datetime) - self.assertEqual(n3.year, base.year) - self.assertEqual(n3.month, 3) - self.assertEqual(n3.day, base.day) - self.assertEqual(n3.hour, base.hour + 2) - self.assertEqual(n3.minute, base.minute) - - def testPreviousHour(self): - base = datetime(2012, 6, 23, 17, 41) - itr = croniter('* 10 * * *', base) - prev1 = itr.get_prev(datetime) - self.assertEqual(prev1.year, base.year) - self.assertEqual(prev1.month, base.month) - self.assertEqual(prev1.day, base.day) - self.assertEqual(prev1.hour, 10) - self.assertEqual(prev1.minute, 59) - - def testPreviousDay(self): - base = datetime(2012, 6, 27, 00, 15) - itr = croniter('* * 26 * *', base) - prev1 = itr.get_prev(datetime) - self.assertEqual(prev1.year, base.year) - self.assertEqual(prev1.month, base.month) - self.assertEqual(prev1.day, 26) - self.assertEqual(prev1.hour, 23) - self.assertEqual(prev1.minute, 59) - - def testPreviousMonth(self): - base = datetime(2012, 6, 18, 00, 15) - itr = croniter('* * * 5 *', base) - prev1 = itr.get_prev(datetime) - self.assertEqual(prev1.year, base.year) - self.assertEqual(prev1.month, 5) - self.assertEqual(prev1.day, 31) - self.assertEqual(prev1.hour, 23) - self.assertEqual(prev1.minute, 59) - - def testPreviousDow(self): - base = datetime(2012, 5, 13, 18, 48) - itr = croniter('* * * * sat', base) - prev1 = itr.get_prev(datetime) - self.assertEqual(prev1.year, base.year) - self.assertEqual(prev1.month, base.month) - self.assertEqual(prev1.day, 12) - self.assertEqual(prev1.hour, 23) - self.assertEqual(prev1.minute, 59) - - def testGetCurrent(self): - base = datetime(2012, 9, 25, 11, 24) - itr = croniter('* * * * *', base) - res = itr.get_current(datetime) - self.assertEqual(base.year, res.year) - self.assertEqual(base.month, res.month) - self.assertEqual(base.day, res.day) - self.assertEqual(base.hour, res.hour) - self.assertEqual(base.minute, res.minute) - - -if __name__ == '__main__': - unittest.main() diff --git a/docs/CHANGES.rst b/docs/CHANGES.rst new file mode 100644 index 0000000..232519b --- /dev/null +++ b/docs/CHANGES.rst @@ -0,0 +1,7 @@ +Changelog +============== + +0.3.3 (unreleased) +------------------- +- proper packaging + diff --git a/LICENSE b/docs/LICENSE similarity index 100% rename from LICENSE rename to docs/LICENSE diff --git a/setup.py b/setup.py index c2222b5..6e79425 100644 --- a/setup.py +++ b/setup.py @@ -1,25 +1,42 @@ # -*- coding: utf-8 -*- -from setuptools import setup -__version__, __doc__, __license__, __author__ = None, None, None, None -# get __version__, __doc__, __license__, __author__ -execfile("croniter/_release.py") +import os +from setuptools import setup, find_packages +def read(*rnames): + return open( + os.path.join('.', *rnames) + ).read() + +long_description = "\n\n".join( + [read('README.rst'), + read('docs', 'CHANGES.rst'), + ] +) + setup( - packages = ('croniter',), name = 'croniter', - version = __version__, - py_modules = ['croniter', 'croniter_test'], + version = "0.3.3dev", + py_modules = ['croniter', ], description = 'croniter provides iteration for datetime object with cron like format', - long_description = __doc__, - author = __author__, - author_email = 'taichino@gmail.com', + long_description = long_description, + author = "Matsumoto Taichi, kiorky", + author_email = 'taichino@gmail.com, kiorky@cryptelium.net', url = 'http://github.com/taichino/croniter', keywords = 'datetime, iterator, cron', - install_requires = ["python-dateutil", "setuptools",], - license = __license__, + install_requires = ["python-dateutil", "zope.interface", + "setuptools",], + license = "MIT License", classifiers = ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", "Programming Language :: Python", - "Topic :: Software Development :: Libraries :: Python Modules"] + "Topic :: Software Development :: Libraries :: Python Modules"], + packages=find_packages('src'), + package_dir = {'': 'src'}, + include_package_data=True, + extras_require = { + 'test': [ + 'plone.testing', + ], + }, ) diff --git a/src/croniter/__init__.py b/src/croniter/__init__.py new file mode 100644 index 0000000..4e33e7d --- /dev/null +++ b/src/croniter/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from croniter import croniter diff --git a/croniter/croniter.py b/src/croniter/croniter.py similarity index 100% rename from croniter/croniter.py rename to src/croniter/croniter.py diff --git a/src/croniter/testing.py b/src/croniter/testing.py new file mode 100644 index 0000000..fd6a44f --- /dev/null +++ b/src/croniter/testing.py @@ -0,0 +1,16 @@ +from plone.testing.layer import Layer as Base + +class Layer(Base): + + defaultBases = tuple() + +class IntegrationLayer(Layer): + """.""" + +class FunctionnalLayer(IntegrationLayer): + """.""" + + +CRONITER_FIXTURE = Layer() +CRONITER_INTEGRATION_TESTING = IntegrationLayer() +CRONITER_FUNCTIONAL_TESTING = FunctionnalLayer() diff --git a/src/croniter/tests/__init__.py b/src/croniter/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/croniter/tests/base.py b/src/croniter/tests/base.py new file mode 100644 index 0000000..ec192b9 --- /dev/null +++ b/src/croniter/tests/base.py @@ -0,0 +1,44 @@ +import unittest2 as unittest + +from croniter.testing import ( + CRONITER_FIXTURE as UNIT_TESTING, + CRONITER_INTEGRATION_TESTING as INTEGRATION_TESTING, + CRONITER_FUNCTIONAL_TESTING as FUNCTIONAL_TESTING, +) +from croniter.tests.globals import * + +""" + + +Lot of files generated by the collective.generic packages will try to load user defined objects in user specific files. +The final goal is to regenerate easyly the test infrastructure on templates updates without impacting +user-specific test boilerplate. +We do not use paster local commands (insert/update) as it cannot determine witch is specific or not and we prefer to totally +separe generated stuff and what is user specific + + +""" + + +class TestCase(unittest.TestCase): + """We use this base class for all the tests in this package. + If necessary, we can put common utility or setup code in here. + """ + layer = UNIT_TESTING + + def setUp(self): + super(TestCase, self).setUp() + + +class IntegrationTestCase(TestCase): + """Integration base TestCase.""" + layer = INTEGRATION_TESTING + + +class FunctionalTestCase(TestCase): + """Functionnal base TestCase.""" + layer = FUNCTIONAL_TESTING + + + +# vim:set ft=python: diff --git a/src/croniter/tests/globals.py b/src/croniter/tests/globals.py new file mode 100644 index 0000000..bdaf9e9 --- /dev/null +++ b/src/croniter/tests/globals.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +__docformat__ = 'restructuredtext en' + +from pprint import pprint +from copy import deepcopy as dc + + +# vim:set et sts=4 ts=4 tw=80: diff --git a/src/croniter/tests/test_croniter.py b/src/croniter/tests/test_croniter.py new file mode 100644 index 0000000..bdfd461 --- /dev/null +++ b/src/croniter/tests/test_croniter.py @@ -0,0 +1,308 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import unittest +from datetime import datetime +from croniter import croniter + +from croniter.tests import base + + +class CroniterTest(base.TestCase): + def testSecond(self): + base = datetime(2012, 4, 6, 13, 26, 10) + itr = croniter('*/1 * * * * *', base) + n1 = itr.get_next(datetime) + self.assertEqual(base.year, n1.year) + self.assertEqual(base.month, n1.month) + self.assertEqual(base.day, n1.day) + self.assertEqual(base.hour, n1.hour) + self.assertEqual(base.minute, n1.minute) + self.assertEqual(base.second + 1, n1.second) + + def testMinute(self): + # minute asterisk + base = datetime(2010, 1, 23, 12, 18) + itr = croniter('*/1 * * * *', base) + n1 = itr.get_next(datetime) # 19 + self.assertEqual(base.year, n1.year) + self.assertEqual(base.month, n1.month) + self.assertEqual(base.day, n1.day) + self.assertEqual(base.hour, n1.hour) + self.assertEqual(base.minute, n1.minute - 1) + for i in range(39): # ~ 58 + itr.get_next() + n2 = itr.get_next(datetime) + self.assertEqual(n2.minute, 59) + n3 = itr.get_next(datetime) + self.assertEqual(n3.minute, 0) + self.assertEqual(n3.hour, 13) + + itr = croniter('*/5 * * * *', base) + n4 = itr.get_next(datetime) + self.assertEqual(n4.minute, 20) + for i in range(6): + itr.get_next() + n5 = itr.get_next(datetime) + self.assertEqual(n5.minute, 55) + n6 = itr.get_next(datetime) + self.assertEqual(n6.minute, 0) + self.assertEqual(n6.hour, 13) + + def testHour(self): + base = datetime(2010, 1, 24, 12, 2) + itr = croniter('0 */3 * * *', base) + n1 = itr.get_next(datetime) + self.assertEqual(n1.hour, 15) + self.assertEqual(n1.minute, 0) + for i in range(2): + itr.get_next() + n2 = itr.get_next(datetime) + self.assertEqual(n2.hour, 0) + self.assertEqual(n2.day, 25) + + def testDay(self): + base = datetime(2010, 2, 24, 12, 9) + itr = croniter('0 0 */3 * *', base) + n1 = itr.get_next(datetime) + self.assertEqual(n1.day, 27) + n2 = itr.get_next(datetime) + self.assertEqual(n2.day, 3) + + # test leap year + base = datetime(1996, 2, 27) + itr = croniter('0 0 * * *', base) + n1 = itr.get_next(datetime) + self.assertEqual(n1.day, 28) + self.assertEqual(n1.month, 2) + n2 = itr.get_next(datetime) + self.assertEqual(n2.day, 29) + self.assertEqual(n2.month, 2) + + base2 = datetime(2000, 2, 27) + itr2 = croniter('0 0 * * *', base2) + n3 = itr2.get_next(datetime) + self.assertEqual(n3.day, 28) + self.assertEqual(n3.month, 2) + n4 = itr2.get_next(datetime) + self.assertEqual(n4.day, 29) + self.assertEqual(n4.month, 2) + + def testWeekDay(self): + base = datetime(2010, 2, 25) + itr = croniter('0 0 * * sat', base) + n1 = itr.get_next(datetime) + self.assertEqual(n1.isoweekday(), 6) + self.assertEqual(n1.day, 27) + n2 = itr.get_next(datetime) + self.assertEqual(n2.isoweekday(), 6) + self.assertEqual(n2.day, 6) + self.assertEqual(n2.month, 3) + + base = datetime(2010, 1, 25) + itr = croniter('0 0 1 * wed', base) + n1 = itr.get_next(datetime) + self.assertEqual(n1.month, 1) + self.assertEqual(n1.day, 27) + self.assertEqual(n1.year, 2010) + n2 = itr.get_next(datetime) + self.assertEqual(n2.month, 2) + self.assertEqual(n2.day, 1) + self.assertEqual(n2.year, 2010) + n3 = itr.get_next(datetime) + self.assertEqual(n3.month, 2) + self.assertEqual(n3.day, 3) + self.assertEqual(n3.year, 2010) + + def testMonth(self): + base = datetime(2010, 1, 25) + itr = croniter('0 0 1 * *', base) + n1 = itr.get_next(datetime) + self.assertEqual(n1.month, 2) + self.assertEqual(n1.day, 1) + n2 = itr.get_next(datetime) + self.assertEqual(n2.month, 3) + self.assertEqual(n2.day, 1) + for i in range(8): + itr.get_next() + n3 = itr.get_next(datetime) + self.assertEqual(n3.month, 12) + self.assertEqual(n3.year, 2010) + n4 = itr.get_next(datetime) + self.assertEqual(n4.month, 1) + self.assertEqual(n4.year, 2011) + + def testError(self): + itr = croniter('* * * * *') + self.assertRaises(TypeError, itr.get_next, str) + self.assertRaises(ValueError, croniter, '* * * *') + self.assertRaises(ValueError, croniter, '* * 5-1 * *') + self.assertRaises(KeyError, croniter, '* * * janu-jun *') + + def testPrevMinute(self): + base = datetime(2010, 8, 25, 15, 56) + itr = croniter('*/1 * * * *', base) + prev = itr.get_prev(datetime) + self.assertEqual(base.year, prev.year) + self.assertEqual(base.month, prev.month) + self.assertEqual(base.day, prev.day) + self.assertEqual(base.hour, prev.hour) + self.assertEqual(base.minute, prev.minute+1) + + base = datetime(2010, 8, 25, 15, 0) + itr = croniter('*/1 * * * *', base) + prev = itr.get_prev(datetime) + self.assertEqual(base.year, prev.year) + self.assertEqual(base.month, prev.month) + self.assertEqual(base.day, prev.day) + self.assertEqual(base.hour, prev.hour+1) + self.assertEqual(59, prev.minute) + + base = datetime(2010, 8, 25, 0, 0) + itr = croniter('*/1 * * * *', base) + prev = itr.get_prev(datetime) + self.assertEqual(base.year, prev.year) + self.assertEqual(base.month, prev.month) + self.assertEqual(base.day, prev.day+1) + self.assertEqual(23, prev.hour) + self.assertEqual(59, prev.minute) + + def testPrevDayOfMonthWithCrossing(self): + """Test getting previous occurrence that crosses into previous month.""" + base = datetime(2012, 3, 15, 0, 0) + itr = croniter('0 0 22 * *', base) + prev = itr.get_prev(datetime) + self.assertEqual(prev.year, 2012) + self.assertEqual(prev.month, 2) + self.assertEqual(prev.day, 22) + self.assertEqual(prev.hour, 0) + self.assertEqual(prev.minute, 0) + + def testPrevWeekDay(self): + base = datetime(2010, 8, 25, 15, 56) + itr = croniter('0 0 * * sat,sun', base) + prev1 = itr.get_prev(datetime) + self.assertEqual(prev1.year, base.year) + self.assertEqual(prev1.month, base.month) + self.assertEqual(prev1.day, 22) + self.assertEqual(prev1.hour, 0) + self.assertEqual(prev1.minute, 0) + + prev2 = itr.get_prev(datetime) + self.assertEqual(prev2.year, base.year) + self.assertEqual(prev2.month, base.month) + self.assertEqual(prev2.day, 21) + self.assertEqual(prev2.hour, 0) + self.assertEqual(prev2.minute, 0) + + prev3 = itr.get_prev(datetime) + self.assertEqual(prev3.year, base.year) + self.assertEqual(prev3.month, base.month) + self.assertEqual(prev3.day, 15) + self.assertEqual(prev3.hour, 0) + self.assertEqual(prev3.minute, 0) + + def testPrevWeekDay2(self): + base = datetime(2010, 8, 25, 15, 56) + itr = croniter('10 0 * * 0', base) + prev = itr.get_prev(datetime) + self.assertEqual(prev.day, 22) + self.assertEqual(prev.hour, 0) + self.assertEqual(prev.minute, 10) + + def testISOWeekday(self): + base = datetime(2010, 2, 25) + itr = croniter('0 0 * * 7', base) + n1 = itr.get_next(datetime) + self.assertEqual(n1.isoweekday(), 7) + self.assertEqual(n1.day, 28) + n2 = itr.get_next(datetime) + self.assertEqual(n2.isoweekday(), 7) + self.assertEqual(n2.day, 7) + self.assertEqual(n2.month, 3) + + def testBug1(self): + base = datetime(2012, 2, 24) + itr = croniter('5 0 */2 * *', base) + n1 = itr.get_prev(datetime) + self.assertEqual(n1.day, 22) + self.assertEqual(n1.hour, 0) + self.assertEqual(n1.minute, 5) + + def testBug2(self): + base = datetime(2012, 01, 01, 00, 00) + iter = croniter('0 * * 3 *', base) + n1 = iter.get_next(datetime) + self.assertEqual(n1.year, base.year) + self.assertEqual(n1.month, 3) + self.assertEqual(n1.day, base.day) + self.assertEqual(n1.hour, base.hour) + self.assertEqual(n1.minute, base.minute) + + n2 = iter.get_next(datetime) + self.assertEqual(n2.year, base.year) + self.assertEqual(n2.month, 3) + self.assertEqual(n2.day, base.day) + self.assertEqual(n2.hour, base.hour + 1) + self.assertEqual(n2.minute, base.minute) + + n3 = iter.get_next(datetime) + self.assertEqual(n3.year, base.year) + self.assertEqual(n3.month, 3) + self.assertEqual(n3.day, base.day) + self.assertEqual(n3.hour, base.hour + 2) + self.assertEqual(n3.minute, base.minute) + + def testPreviousHour(self): + base = datetime(2012, 6, 23, 17, 41) + itr = croniter('* 10 * * *', base) + prev1 = itr.get_prev(datetime) + self.assertEqual(prev1.year, base.year) + self.assertEqual(prev1.month, base.month) + self.assertEqual(prev1.day, base.day) + self.assertEqual(prev1.hour, 10) + self.assertEqual(prev1.minute, 59) + + def testPreviousDay(self): + base = datetime(2012, 6, 27, 00, 15) + itr = croniter('* * 26 * *', base) + prev1 = itr.get_prev(datetime) + self.assertEqual(prev1.year, base.year) + self.assertEqual(prev1.month, base.month) + self.assertEqual(prev1.day, 26) + self.assertEqual(prev1.hour, 23) + self.assertEqual(prev1.minute, 59) + + def testPreviousMonth(self): + base = datetime(2012, 6, 18, 00, 15) + itr = croniter('* * * 5 *', base) + prev1 = itr.get_prev(datetime) + self.assertEqual(prev1.year, base.year) + self.assertEqual(prev1.month, 5) + self.assertEqual(prev1.day, 31) + self.assertEqual(prev1.hour, 23) + self.assertEqual(prev1.minute, 59) + + def testPreviousDow(self): + base = datetime(2012, 5, 13, 18, 48) + itr = croniter('* * * * sat', base) + prev1 = itr.get_prev(datetime) + self.assertEqual(prev1.year, base.year) + self.assertEqual(prev1.month, base.month) + self.assertEqual(prev1.day, 12) + self.assertEqual(prev1.hour, 23) + self.assertEqual(prev1.minute, 59) + + def testGetCurrent(self): + base = datetime(2012, 9, 25, 11, 24) + itr = croniter('* * * * *', base) + res = itr.get_current(datetime) + self.assertEqual(base.year, res.year) + self.assertEqual(base.month, res.month) + self.assertEqual(base.day, res.day) + self.assertEqual(base.hour, res.hour) + self.assertEqual(base.minute, res.minute) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/croniter/tests/test_doctests.py b/src/croniter/tests/test_doctests.py new file mode 100644 index 0000000..3fe63fe --- /dev/null +++ b/src/croniter/tests/test_doctests.py @@ -0,0 +1,56 @@ +""" +Launching all doctests in the tests directory using: + + - the base layer in testing.py + +""" + +from croniter.tests.base import FunctionalTestCase + +# GLOBALS avalaible in doctests +# IMPORT/DEFINE objects there or inside ./user_globals.py (better) +# globals from the testing product are also available. +# example: +# from for import bar +# and in your doctests, you can do: +# >>> bar.something +from croniter.tests.globals import * +from croniter.testing import CRONITER_FUNCTIONAL_TESTING as FUNCTIONAL_TESTING + + +import unittest2 as unittest +import glob +import os +import logging +import doctest +from plone.testing import layered + +optionflags = (doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE| doctest.REPORT_ONLY_FIRST_FAILURE) + +def test_suite(): + """.""" + logger = logging.getLogger('croniter') + cwd = os.path.dirname(__file__) + files = [] + try: + files = glob.glob(os.path.join(cwd, '*txt')) + files += glob.glob(os.path.join(cwd, '*rst')) + except Exception,e: + logger.warn('No doctests for croniter') + suite = unittest.TestSuite() + globs = globals() + for s in files: + suite.addTests([ + layered( + doctest.DocFileSuite( + s, + globs = globs, + module_relative=False, + optionflags=optionflags, + ), + layer=FUNCTIONAL_TESTING + ), + ]) + return suite + + diff --git a/croniter/speed_test.py b/src/croniter/tests/test_speed.py similarity index 96% rename from croniter/speed_test.py rename to src/croniter/tests/test_speed.py index f0302c5..9e1d926 100644 --- a/croniter/speed_test.py +++ b/src/croniter/tests/test_speed.py @@ -1,10 +1,13 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +import unittest import time from datetime import datetime, date from croniter import croniter +from croniter.tests import base + class timerTest(object): def __init__(self): self.tests = tuple(getattr(self, m) for m in dir(self) if m.lower().startswith('test')) @@ -12,7 +15,6 @@ class timerTest(object): def run(self): for test in self.tests: test() - class CroniterTest(timerTest): def testMinute(self): @@ -205,8 +207,14 @@ class CroniterTest(timerTest): n2 = itr.get_next(datetime) n2.isoweekday() == 7 n2.day == 7 - n2.month == 3 - + n2.month == 3 + + +class TestCase(base.TestCase): + """make zope.testrunner happy""" + def test_Noop(self): + self.assertEqual(1,1) + if __name__ == '__main__': from timeit import Timer t = Timer('c=CroniterTest();c.run()', 'from __main__ import CroniterTest')