commit python-croniter for openSUSE:Factory
Hello community, here is the log from the commit of package python-croniter for openSUSE:Factory checked in at 2017-08-28 15:15:57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-croniter (Old) and /work/SRC/openSUSE:Factory/.python-croniter.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python-croniter" Mon Aug 28 15:15:57 2017 rev:6 rq:518656 version:0.3.17 Changes: -------- --- /work/SRC/openSUSE:Factory/python-croniter/python-croniter.changes 2016-12-08 00:29:35.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.python-croniter.new/python-croniter.changes 2017-08-28 15:17:19.223417956 +0200 @@ -1,0 +2,19 @@ +Fri Aug 25 07:05:44 UTC 2017 - tbechtold@suse.com + +- update to 0.3.17: + - DOW occurence sharp style support. + - Better test suite + - DST support + - fix bug around multiple conditions and range_val in + _get_prev_nearest_diff. + - issue #69: added day_or option to change behavior when day-of-month and + day-of-week is given + - `Real fix for #34 + - `Modernize test infra + - `Release as a universal wheel + - `Raise ValueError on negative numbers + - `Compare types using "issubclass" instead of exact match + - `Implement step cron with a variable base +- convert to singlespec + +------------------------------------------------------------------- Old: ---- croniter-0.3.12.tar.gz New: ---- croniter-0.3.17.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-croniter.spec ++++++ --- /var/tmp/diff_new_pack.BVEWaj/_old 2017-08-28 15:17:22.171003769 +0200 +++ /var/tmp/diff_new_pack.BVEWaj/_new 2017-08-28 15:17:22.171003769 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-croniter # -# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -16,28 +16,28 @@ # +%{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-croniter -Version: 0.3.12 +Version: 0.3.17 Release: 0 Summary: Croniter provides iteration for datetime object with cron like format License: MIT Group: Development/Languages/Python Url: http://github.com/kiorky/croniter -Source: https://pypi.io/packages/source/c/croniter/croniter-%{version}.tar.gz -BuildRequires: python-devel -BuildRequires: python-setuptools +Source: https://files.pythonhosted.org/packages/source/c/croniter/croniter-%{version}.tar.gz +BuildRequires: %{python_module devel} +BuildRequires: %{python_module setuptools} +BuildRequires: fdupes +BuildRequires: python-rpm-macros BuildRequires: unzip # Test requirements: -BuildRequires: python-nose -BuildRequires: python-python-dateutil -BuildRequires: python-pytz +BuildRequires: %{python_module nose} +BuildRequires: %{python_module python-dateutil} +BuildRequires: %{python_module pytz} Requires: python-python-dateutil -BuildRoot: %{_tmppath}/%{name}-%{version}-build -%if 0%{?suse_version} && 0%{?suse_version} <= 1110 -%{!?python_sitelib: %global python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} -%else BuildArch: noarch -%endif + +%python_subpackages %description croniter provides iteration for datetime object with cron like format. @@ -46,15 +46,16 @@ %setup -q -n croniter-%{version} %build -python setup.py build +%python_build %install -python setup.py install --prefix=%{_prefix} --root=%{buildroot} +%python_install +%python_expand %fdupes %{buildroot}%{$python_sitelib} %check -nosetests +%python_exec %{_bindir}/nosetests -%files +%files %{python_files} %defattr(-,root,root,-) %doc README.rst docs/LICENSE %{python_sitelib}/* ++++++ croniter-0.3.12.tar.gz -> croniter-0.3.17.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/MANIFEST.in new/croniter-0.3.17/MANIFEST.in --- old/croniter-0.3.12/MANIFEST.in 2016-03-10 21:31:22.000000000 +0100 +++ new/croniter-0.3.17/MANIFEST.in 2017-05-22 12:11:46.000000000 +0200 @@ -1,6 +1,7 @@ -include *.txt *.cfg *.rst +include *.txt *.cfg *.rst *.ini recursive-include docs * +recursive-include requirements * recursive-include src * global-exclude *pyc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/PKG-INFO new/croniter-0.3.17/PKG-INFO --- old/croniter-0.3.12/PKG-INFO 2016-03-10 21:31:22.000000000 +0100 +++ new/croniter-0.3.17/PKG-INFO 2017-05-22 12:11:46.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: croniter -Version: 0.3.12 +Version: 0.3.17 Summary: croniter provides iteration for datetime object with cron like format Home-page: http://github.com/kiorky/croniter Author: Matsumoto Taichi, kiorky @@ -12,7 +12,7 @@ .. contents:: - croniter provides iteration for datetime object with cron like format. + croniter provides iteration for the datetime object with a cron like format. :: @@ -33,12 +33,12 @@ Usage ============ - Simple example of usage is followings:: + A simple example:: >>> from croniter import croniter >>> from datetime import datetime >>> base = datetime(2010, 1, 25, 4, 46) - >>> iter = croniter('*/5 * * * *', base) # every 5 minites + >>> iter = croniter('*/5 * * * *', base) # every 5 minutes >>> 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 @@ -47,21 +47,42 @@ >>> 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.:: + >>> + >>> iter = croniter('2 4 1 * wed', base) # 04:02 on every Wednesday OR on 1st day of month + >>> print iter.get_next(datetime) # 2010-01-27 04:02:00 + >>> print iter.get_next(datetime) # 2010-02-01 04:02:00 + >>> print iter.get_next(datetime) # 2010-02-03 04:02:00 + >>> + >>> iter = croniter('2 4 1 * wed', base, day_or=False) # 04:02 on every 1st day of the month if it is a Wednesday + >>> print iter.get_next(datetime) # 2010-09-01 04:02:00 + >>> print iter.get_next(datetime) # 2010-12-01 04:02:00 + >>> print iter.get_next(datetime) # 2011-06-01 04:02:00 + >>> iter = croniter('0 0 * * sat#1,sun#2', base) + >>> print iter.get_next(datetime) # datetime.datetime(2010, 2, 6, 0, 0) + + All you need to know is how to use the constructor and the ``get_next`` + method, the signature of these methods are listed below:: + + >>> def __init__(self, cron_format, start_time=time.time(), day_or=True) + + croniter iterates along with ``cron_format`` from ``start_time``. + ``cron_format`` is **min hour day month day_of_week**, you can refer to + http://en.wikipedia.org/wiki/Cron for more details. The ``day_or`` + switch is used to control how croniter handles **day** and **day_of_week** + entries. Default option is the cron behaviour, which connects those + values using **OR**. If the switch is set to False, the values are connected + using **AND**. This behaves like fcron and enables you to e.g. define a job that + executes each 2nd friday of a month by setting the days of month and the + weekday. + :: >>> 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'. + get_next calculates the next value according to the cron expression and + returns an object of type ``ret_type``. ``ret_type`` should be a ``float`` or a + ``datetime`` object. - Now, supported get_prev method. (>= 0.2.0):: + Supported added for ``get_prev`` method. (>= 0.2.0):: >>> base = datetime(2010, 8, 25) >>> itr = croniter('0 0 1 * *', base) @@ -69,6 +90,12 @@ >>> print itr.get_prev(datetime) # 2010-07-01 00:00:00 >>> print itr.get_prev(datetime) # 2010-06-01 00:00:00 + About DST + ========= + Be sure to init your croniter instance with a TZ aware datetime for this to work !:: + + >>> local_date = tz.localize(datetime(2017, 3, 26)) + >>> val = croniter('0 0 * * *', local_date).get_next(datetime) Develop this package ==================== @@ -77,25 +104,28 @@ git clone https://github.com/kiorky/croniter.git cd croniter - python bootstrap.py -d - bin/buildout -vvvvvvN - bin/test + virtualenv --no-site-packages venv + . venv/bin/activate + pip install --upgrade -r requirements/test.txt + py.test src Make a new release ==================== - We use zest.fullreleaser, a great releaser infrastructure. + We use zest.fullreleaser, a great release infrastructure. - Do and follow the instructions + Do and follow these instructions :: - bin/fullrelease + . venv/bin/activate + pip install --upgrade -r requirements/release.txt + fullrelease Contributors =============== - Thank you to all who have contributed to this project! - If you contributed and not listed below please let me know. + Thanks to all who have contributed to this project! + If you have contributed and your name is not listed below please let me know. - mrmachine - Hinnack @@ -114,6 +144,50 @@ Changelog ============== + 0.3.17 (2017-05-22) + ------------------- + - DOW occurence sharp style support. + [kiorky, Kengo Seki <sekikn@apache.org>] + + + 0.3.16 (2017-03-15) + ------------------- + + - Better test suite [mrcrilly@github] + - DST support [kiorky] + + 0.3.15 (2017-02-16) + ------------------- + + - fix bug around multiple conditions and range_val in + _get_prev_nearest_diff. + [abeja-yuki@github] + + 0.3.14 (2017-01-25) + ------------------- + + - issue #69: added day_or option to change behavior when day-of-month and + day-of-week is given + [Andreas Vogl <a.vogl@hackner-security.com>] + + + + 0.3.13 (2016-11-01) + ------------------- + + - `Real fix for #34 <https://github.com/taichino/croniter/pull/73>`_ + [kiorky@github] + - `Modernize test infra <https://github.com/taichino/croniter/pull/72>`_ + [kiorky@github] + - `Release as a universal wheel <https://github.com/kiorky/croniter/pull/16>`_ + [adamchainz@github] + - `Raise ValueError on negative numbers <https://github.com/taichino/croniter/pull/63>`_ + [josegonzalez@github] + - `Compare types using "issubclass" instead of exact match <https://github.com/taichino/croniter/pull/70>`_ + [darkk@github] + - `Implement step cron with a variable base <https://github.com/taichino/croniter/pull/60>`_ + [josegonzalez@github] + 0.3.12 (2016-03-10) ------------------- - support setting ret_type in __init__ [Brent Tubbs <brent.tubbs@gmail.com>] @@ -185,4 +259,6 @@ Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: POSIX Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Software Development :: Libraries :: Python Modules diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/README.rst new/croniter-0.3.17/README.rst --- old/croniter-0.3.12/README.rst 2016-03-10 21:31:22.000000000 +0100 +++ new/croniter-0.3.17/README.rst 2017-05-22 12:11:46.000000000 +0200 @@ -4,7 +4,7 @@ .. contents:: -croniter provides iteration for datetime object with cron like format. +croniter provides iteration for the datetime object with a cron like format. :: @@ -25,12 +25,12 @@ Usage ============ -Simple example of usage is followings:: +A simple example:: >>> from croniter import croniter >>> from datetime import datetime >>> base = datetime(2010, 1, 25, 4, 46) - >>> iter = croniter('*/5 * * * *', base) # every 5 minites + >>> iter = croniter('*/5 * * * *', base) # every 5 minutes >>> 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 @@ -39,21 +39,42 @@ >>> 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.:: + >>> + >>> iter = croniter('2 4 1 * wed', base) # 04:02 on every Wednesday OR on 1st day of month + >>> print iter.get_next(datetime) # 2010-01-27 04:02:00 + >>> print iter.get_next(datetime) # 2010-02-01 04:02:00 + >>> print iter.get_next(datetime) # 2010-02-03 04:02:00 + >>> + >>> iter = croniter('2 4 1 * wed', base, day_or=False) # 04:02 on every 1st day of the month if it is a Wednesday + >>> print iter.get_next(datetime) # 2010-09-01 04:02:00 + >>> print iter.get_next(datetime) # 2010-12-01 04:02:00 + >>> print iter.get_next(datetime) # 2011-06-01 04:02:00 + >>> iter = croniter('0 0 * * sat#1,sun#2', base) + >>> print iter.get_next(datetime) # datetime.datetime(2010, 2, 6, 0, 0) + +All you need to know is how to use the constructor and the ``get_next`` +method, the signature of these methods are listed below:: + + >>> def __init__(self, cron_format, start_time=time.time(), day_or=True) + +croniter iterates along with ``cron_format`` from ``start_time``. +``cron_format`` is **min hour day month day_of_week**, you can refer to +http://en.wikipedia.org/wiki/Cron for more details. The ``day_or`` +switch is used to control how croniter handles **day** and **day_of_week** +entries. Default option is the cron behaviour, which connects those +values using **OR**. If the switch is set to False, the values are connected +using **AND**. This behaves like fcron and enables you to e.g. define a job that +executes each 2nd friday of a month by setting the days of month and the +weekday. +:: >>> 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'. +get_next calculates the next value according to the cron expression and +returns an object of type ``ret_type``. ``ret_type`` should be a ``float`` or a +``datetime`` object. -Now, supported get_prev method. (>= 0.2.0):: +Supported added for ``get_prev`` method. (>= 0.2.0):: >>> base = datetime(2010, 8, 25) >>> itr = croniter('0 0 1 * *', base) @@ -61,6 +82,12 @@ >>> print itr.get_prev(datetime) # 2010-07-01 00:00:00 >>> print itr.get_prev(datetime) # 2010-06-01 00:00:00 +About DST +========= +Be sure to init your croniter instance with a TZ aware datetime for this to work !:: + + >>> local_date = tz.localize(datetime(2017, 3, 26)) + >>> val = croniter('0 0 * * *', local_date).get_next(datetime) Develop this package ==================== @@ -69,25 +96,28 @@ git clone https://github.com/kiorky/croniter.git cd croniter - python bootstrap.py -d - bin/buildout -vvvvvvN - bin/test + virtualenv --no-site-packages venv + . venv/bin/activate + pip install --upgrade -r requirements/test.txt + py.test src Make a new release ==================== -We use zest.fullreleaser, a great releaser infrastructure. +We use zest.fullreleaser, a great release infrastructure. -Do and follow the instructions +Do and follow these instructions :: - bin/fullrelease + . venv/bin/activate + pip install --upgrade -r requirements/release.txt + fullrelease Contributors =============== -Thank you to all who have contributed to this project! -If you contributed and not listed below please let me know. +Thanks to all who have contributed to this project! +If you have contributed and your name is not listed below please let me know. - mrmachine - Hinnack diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/buildout.cfg new/croniter-0.3.17/buildout.cfg --- old/croniter-0.3.12/buildout.cfg 2016-03-10 21:31:22.000000000 +0100 +++ new/croniter-0.3.17/buildout.cfg 1970-01-01 01:00:00.000000000 +0100 @@ -1,118 +0,0 @@ -[buildout] -package-name = croniter -develop = . -versions=versions -parts = - scripts - omelette - coverage - report - test - report-xml - code-analysis -extensions = - mr.developer -auto-checkout = -test-eggs= ${buildout:package-name} [test] -eggs = ${buildout:package-name} - -[sources] - -[code-analysis] -recipe = plone.recipe.codeanalysis -directory = ${buildout:directory}/src -flake8-ignore=C901 - -[scripts] -recipe=zc.recipe.egg -eggs = ${buildout:eggs} - zest.releaser - ipython - tox - nose -interpreter = scripts - -[test] -recipe=zc.recipe.egg -eggs = ${scripts:eggs} - croniter[test] -arguments= ['croniter'] -scripts=test -entry-points= - test=nose:run_exit - -[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] -pytz = 2013.9 -python-dateutil = 2.2 - -# buildout & test infra -zc.buildout = 2.2.1 -zc.recipe.egg = 2.0.1 -buildout-versions = 1.7 -collective.recipe.omelette = 0.16 -coverage = 3.7.1 -ipython = 1.1.0 -mr.developer = 1.28 -plone.testing = 4.0.9 -zc.recipe.testrunner = 2.0.0 -zest.releaser = 3.50 -six = 1.5.2 -unittest2 = 0.5.1 -zope.exceptions = 4.0.6 -zope.interface = 4.0.5 -zope.testing = 4.1.2 -zope.testrunner = 4.4.1 -zope.contentprovider = 4.0.0a1 -zope.event = 4.0.2 -zope.i18n = 4.0.0a4 -zope.pagetemplate = 4.0.4 -zope.proxy = 4.1.3 -zope.publisher = 4.0.0a4 -zope.schema = 4.4.0 -zope.security = 4.0.0 -zope.tales = 4.0.2 -zope.traversing = 4.0.0a3 -zptlint = 0.2.4 -mccabe = 0.2.1 -pep8 = 1.4.6 -plone.recipe.codeanalysis = 1.0b6 -pyflakes = 0.7.3 -zope.contenttype = 4.0.1 -zope.i18nmessageid = 4.0.2 -zope.location = 4.0.2 -zope.tal = 4.0.0 -Unidecode = 0.04.14 -flake8 = 2.1.0 -i18ndude = 3.3.3 -ordereddict = 1.1 -plone.i18n = 2.0.9 -transaction = 1.4.1 -zope.browser = 2.0.2 -zope.component = 4.1.0 -zope.configuration = 4.0.2 - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/docs/CHANGES.rst new/croniter-0.3.17/docs/CHANGES.rst --- old/croniter-0.3.12/docs/CHANGES.rst 2016-03-10 21:31:22.000000000 +0100 +++ new/croniter-0.3.17/docs/CHANGES.rst 2017-05-22 12:11:46.000000000 +0200 @@ -1,6 +1,50 @@ Changelog ============== +0.3.17 (2017-05-22) +------------------- +- DOW occurence sharp style support. + [kiorky, Kengo Seki <sekikn@apache.org>] + + +0.3.16 (2017-03-15) +------------------- + +- Better test suite [mrcrilly@github] +- DST support [kiorky] + +0.3.15 (2017-02-16) +------------------- + +- fix bug around multiple conditions and range_val in + _get_prev_nearest_diff. + [abeja-yuki@github] + +0.3.14 (2017-01-25) +------------------- + +- issue #69: added day_or option to change behavior when day-of-month and + day-of-week is given + [Andreas Vogl <a.vogl@hackner-security.com>] + + + +0.3.13 (2016-11-01) +------------------- + +- `Real fix for #34 <https://github.com/taichino/croniter/pull/73>`_ + [kiorky@github] +- `Modernize test infra <https://github.com/taichino/croniter/pull/72>`_ + [kiorky@github] +- `Release as a universal wheel <https://github.com/kiorky/croniter/pull/16>`_ + [adamchainz@github] +- `Raise ValueError on negative numbers <https://github.com/taichino/croniter/pull/63>`_ + [josegonzalez@github] +- `Compare types using "issubclass" instead of exact match <https://github.com/taichino/croniter/pull/70>`_ + [darkk@github] +- `Implement step cron with a variable base <https://github.com/taichino/croniter/pull/60>`_ + [josegonzalez@github] + 0.3.12 (2016-03-10) ------------------- - support setting ret_type in __init__ [Brent Tubbs <brent.tubbs@gmail.com>] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/py3.cfg new/croniter-0.3.17/py3.cfg --- old/croniter-0.3.12/py3.cfg 2016-03-10 21:31:22.000000000 +0100 +++ new/croniter-0.3.17/py3.cfg 1970-01-01 01:00:00.000000000 +0100 @@ -1,9 +0,0 @@ -[buildout] -extends=buildout.cfg -parts-= - omelette - code-analysis -[scripts] -eggs-= - ipython - zest.releaser diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/requirements/base.txt new/croniter-0.3.17/requirements/base.txt --- old/croniter-0.3.12/requirements/base.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/croniter-0.3.17/requirements/base.txt 2017-05-22 12:11:46.000000000 +0200 @@ -0,0 +1,2 @@ +python_dateutil +-e . diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/requirements/release.txt new/croniter-0.3.17/requirements/release.txt --- old/croniter-0.3.12/requirements/release.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/croniter-0.3.17/requirements/release.txt 2017-05-22 12:11:46.000000000 +0200 @@ -0,0 +1,2 @@ +-r base.txt +zest.releaser[recommended]>=6.7 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/requirements/test.txt new/croniter-0.3.17/requirements/test.txt --- old/croniter-0.3.12/requirements/test.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/croniter-0.3.17/requirements/test.txt 2017-05-22 12:11:46.000000000 +0200 @@ -0,0 +1,8 @@ +-r base.txt +pytz +pytest>=3.0.3 +tox>=2.4.1 +coverage>=4.2 +mock>=2.0.0 # For Python 2 +coveralls +flake8 \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/setup.cfg new/croniter-0.3.17/setup.cfg --- old/croniter-0.3.12/setup.cfg 2016-03-10 21:31:22.000000000 +0100 +++ new/croniter-0.3.17/setup.cfg 2017-05-22 12:11:46.000000000 +0200 @@ -1,3 +1,21 @@ +[bdist_wheel] +universal = 1 + +[coverage:run] +source = . +branch = True +omit = + .tox/* + setup.py + **/**/tests* + **/**/test_*.py + +[coverage:report] +exclude_lines = + pragma: no cover + raise AssertionError + raise NotImplementedError + [egg_info] tag_build = tag_date = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/setup.py new/croniter-0.3.17/setup.py --- old/croniter-0.3.12/setup.py 2016-03-10 21:31:22.000000000 +0100 +++ new/croniter-0.3.17/setup.py 2017-05-22 12:11:46.000000000 +0200 @@ -8,6 +8,12 @@ os.path.join('.', *rnames) ).read() +install_requires = [ + a.strip() + for a in read('requirements/base.txt').splitlines() + if a.strip() and not a.startswith(('#', '-')) +] + long_description = "\n\n".join( [ read('README.rst'), @@ -17,7 +23,7 @@ setup( name='croniter', - version='0.3.12', + version='0.3.17', py_modules=['croniter', ], description=( 'croniter provides iteration for datetime ' @@ -28,10 +34,7 @@ author_email='taichino@gmail.com, kiorky@cryptelium.net', url='http://github.com/kiorky/croniter', keywords='datetime, iterator, cron', - install_requires=[ - "python-dateutil", - "setuptools", - ], + install_requires=install_requires, license="MIT License", classifiers=[ "Development Status :: 4 - Beta", @@ -39,13 +42,10 @@ "License :: OSI Approved :: MIT License", "Operating System :: POSIX", "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries :: Python Modules"], packages=find_packages('src'), package_dir={'': 'src'}, include_package_data=True, - extras_require={ - 'test': [ - "pytz", - ], - }, ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/src/croniter/__init__.py new/croniter-0.3.17/src/croniter/__init__.py --- old/croniter-0.3.12/src/croniter/__init__.py 2016-03-10 21:31:22.000000000 +0100 +++ new/croniter-0.3.17/src/croniter/__init__.py 2017-05-22 12:11:46.000000000 +0200 @@ -1,4 +1,9 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import -from .croniter import croniter +from .croniter import ( + croniter, + CroniterBadDateError, # noqa + CroniterBadCronError, # noqa + CroniterNotAlphaError # noqa +) # noqa croniter.__name__ # make flake8 happy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/src/croniter/croniter.py new/croniter-0.3.17/src/croniter/croniter.py --- old/croniter-0.3.12/src/croniter/croniter.py 2016-03-10 21:31:22.000000000 +0100 +++ new/croniter-0.3.17/src/croniter/croniter.py 2017-05-22 12:11:46.000000000 +0200 @@ -7,13 +7,25 @@ import datetime from dateutil.relativedelta import relativedelta from dateutil.tz import tzutc +import calendar +step_search_re = re.compile(r'^([^-]+)-([^-/]+)(/(.*))?$') search_re = re.compile(r'^([^-]+)-([^-/]+)(/(.*))?$') only_int_re = re.compile(r'^\d+$') any_int_re = re.compile(r'^\d+') star_or_int_re = re.compile(r'^(\d+|\*)$') -__all__ = ('croniter',) + +class CroniterBadCronError(ValueError): + '''.''' + + +class CroniterBadDateError(ValueError): + '''.''' + + +class CroniterNotAlphaError(ValueError): + '''.''' class croniter(object): @@ -52,8 +64,11 @@ bad_length = 'Exactly 5 or 6 columns has to be specified for iterator' \ 'expression.' - def __init__(self, expr_format, start_time=None, ret_type=float): + def __init__(self, expr_format, start_time=None, ret_type=float, + day_or=True): self._ret_type = ret_type + self._day_or = day_or + if start_time is None: start_time = time() @@ -62,13 +77,15 @@ self.tzinfo = start_time.tzinfo start_time = self._datetime_to_timestamp(start_time) + self.start_time = start_time self.cur = start_time self.exprs = expr_format.split() if len(self.exprs) != 5 and len(self.exprs) != 6: - raise ValueError(self.bad_length) + raise CroniterBadCronError(self.bad_length) expanded = [] + nth_weekday_of_month = {} for i, expr in enumerate(self.exprs): e_list = expr.split(',') @@ -76,42 +93,53 @@ while len(e_list) > 0: e = e_list.pop() - t = re.sub(r'^\*(/.+)$', r'%d-%d\1' % ( + + if i == 4: + e, sep, nth = str(e).partition('#') + if nth and not re.match(r'[1-5]', nth): + raise CroniterBadDateError( + "[{0}] is not acceptable".format(expr_format)) + + t = re.sub(r'^\*(\/.+)$', r'%d-%d\1' % ( self.RANGES[i][0], self.RANGES[i][1]), str(e)) m = search_re.search(t) + if not m: + t = re.sub(r'^(.+)\/(.+)$', r'\1-%d/\2' % ( + self.RANGES[i][1]), + str(e)) + m = step_search_re.search(t) + if m: (low, high, step) = m.group(1), m.group(2), m.group(4) or 1 if not any_int_re.search(low): - low = "{0}".format(self.ALPHACONV[i][low.lower()]) + low = "{0}".format(self._alphaconv(i, low)) if not any_int_re.search(high): - high = "{0}".format(self.ALPHACONV[i][high.lower()]) + high = "{0}".format(self._alphaconv(i, high)) if ( not low or not high or int(low) > int(high) or not only_int_re.search(str(step)) ): - raise ValueError( + raise CroniterBadDateError( "[{0}] is not acceptable".format(expr_format)) low, high, step = map(int, [low, high, step]) - e_list += range(low, high + 1, step) - # other solution - #try: - # for j in xrange(int(low), int(high) + 1): - # if j % int(step) == 0: - # e_list.append(j) - #except NameError: - # for j in range(int(low), int(high) + 1): - # if j % int(step) == 0: - # e_list.append(j) + rng = range(low, high + 1, step) + e_list += (["{0}#{1}".format(item, nth) for item in rng] + if i == 4 and nth else rng) else: + if t.startswith('-'): + raise CroniterBadCronError( + "[{0}] is not acceptable,\ + negative numbers not allowed".format( + expr_format)) if not star_or_int_re.search(t): - t = self.ALPHACONV[i][t.lower()] + t = self._alphaconv(i, t) try: t = int(t) @@ -126,17 +154,31 @@ and (int(t) < self.RANGES[i][0] or int(t) > self.RANGES[i][1]) ): - raise ValueError( + raise CroniterBadCronError( "[{0}] is not acceptable, out of range".format( expr_format)) res.append(t) + if i == 4 and nth: + if t not in nth_weekday_of_month: + nth_weekday_of_month[t] = set() + nth_weekday_of_month[t].add(int(nth)) + res.sort() expanded.append(['*'] if (len(res) == 1 and res[0] == '*') else res) + self.expanded = expanded + self.nth_weekday_of_month = nth_weekday_of_month + + def _alphaconv(self, index, key): + try: + return self.ALPHACONV[index][key.lower()] + except KeyError: + raise CroniterNotAlphaError( + "[{0}] is not acceptable".format(" ".join(self.exprs))) def get_next(self, ret_type=None): return self._get_next(ret_type or self._ret_type, is_prev=False) @@ -146,7 +188,7 @@ def get_current(self, ret_type=None): ret_type = ret_type or self._ret_type - if ret_type == datetime.datetime: + if issubclass(ret_type, datetime.datetime): return self._timestamp_to_datetime(self.cur) return self.cur @@ -205,35 +247,57 @@ def _get_next(self, ret_type=None, is_prev=False): expanded = self.expanded[:] + nth_weekday_of_month = self.nth_weekday_of_month.copy() ret_type = ret_type or self._ret_type - if ret_type not in (float, datetime.datetime): + if not issubclass(ret_type, (float, datetime.datetime)): raise TypeError("Invalid ret_type, only 'float' or 'datetime' " "is acceptable.") - if expanded[2][0] != '*' and expanded[4][0] != '*': + # exception to support day of month and day of week as defined in cron + if (expanded[2][0] != '*' and expanded[4][0] != '*') and self._day_or: bak = expanded[4] expanded[4] = ['*'] - t1 = self._calc(self.cur, expanded, is_prev) + t1 = self._calc(self.cur, expanded, nth_weekday_of_month, is_prev) expanded[4] = bak expanded[2] = ['*'] - t2 = self._calc(self.cur, expanded, is_prev) + t2 = self._calc(self.cur, expanded, nth_weekday_of_month, is_prev) if not is_prev: result = t1 if t1 < t2 else t2 else: result = t1 if t1 > t2 else t2 else: - result = self._calc(self.cur, expanded, is_prev) - self.cur = result + result = self._calc(self.cur, expanded, nth_weekday_of_month, is_prev) - if ret_type == datetime.datetime: - result = self._timestamp_to_datetime(result) + # DST Handling for cron job spanning accross days + dtstarttime = self._timestamp_to_datetime(self.start_time) + dtresult = self._timestamp_to_datetime(result) + dtresult_utcoffset = dtresult.utcoffset() or datetime.timedelta(0) + dtstarttime_utcoffset = ( + dtstarttime.utcoffset() or datetime.timedelta(0)) + hours_before_midnight = 24 - dtstarttime.hour + lag_hours = ( + self._timedelta_to_seconds(dtresult - dtstarttime) / (60*60) + ) + if ( + lag_hours >= hours_before_midnight and + (dtresult_utcoffset or dtstarttime_utcoffset) and + (dtresult_utcoffset != dtstarttime_utcoffset) + ): + lag = self._timedelta_to_seconds( + dtresult_utcoffset - dtstarttime_utcoffset + ) + dtresult = dtresult - datetime.timedelta(seconds=lag) + result = self._datetime_to_timestamp(dtresult) + self.cur = result + if issubclass(ret_type, datetime.datetime): + result = dtresult return result - def _calc(self, now, expanded, is_prev): + def _calc(self, now, expanded, nth_weekday_of_month, is_prev): if is_prev: nearest_diff_method = self._get_prev_nearest_diff sign = -1 @@ -244,7 +308,7 @@ offset = len(expanded) == 6 and 1 or 60 dst = now = self._timestamp_to_datetime(now + sign * offset) - day, month, year = dst.day, dst.month, dst.year + month, year = dst.month, dst.year current_year = now.year DAYS = self.DAYS @@ -275,7 +339,7 @@ days = DAYS[month - 1] if month == 2 and self.is_leap(year) is True: days += 1 - if 'l' in expanded[2] and days==d.day: + if 'l' in expanded[2] and days == d.day: return False, d if is_prev: @@ -310,6 +374,55 @@ return True, d return False, d + def proc_day_of_week_nth(d): + if '*' in nth_weekday_of_month: + s = nth_weekday_of_month['*'] + for i in range(0, 7): + if i in nth_weekday_of_month: + nth_weekday_of_month[i].update(s) + else: + nth_weekday_of_month[i] = s + del nth_weekday_of_month['*'] + + candidates = [] + for wday, nth in nth_weekday_of_month.items(): + w = (wday + 6) % 7 + c = calendar.Calendar(w).monthdayscalendar(d.year, d.month) + if c[0][0] == 0: c.pop(0) + for n in nth: + if len(c) < n: + continue + candidate = c[n - 1][0] + if ( + (is_prev and candidate <= d.day) or + (not is_prev and d.day <= candidate) + ): + candidates.append(candidate) + + if not candidates: + if is_prev: + d += relativedelta(days=-d.day, + hour=23, minute=59, second=59) + else: + days = DAYS[month - 1] + if month == 2 and self.is_leap(year) is True: + days += 1 + d += relativedelta(days=(days - d.day + 1), + hour=0, minute=0, second=0) + return True, d + + candidates.sort() + diff_day = (candidates[-1] if is_prev else candidates[0]) - d.day + if diff_day != 0: + if is_prev: + d += relativedelta(days=diff_day, + hour=23, minute=59, second=59) + else: + d += relativedelta(days=diff_day, + hour=0, minute=0, second=0) + return True, d + return False, d + def proc_hour(d): if expanded[1][0] != '*': diff_hour = nearest_diff_method(d.hour, expanded[1], 24) @@ -346,7 +459,8 @@ procs = [proc_month, proc_day_of_month, - proc_day_of_week, + (proc_day_of_week_nth if nth_weekday_of_month + else proc_day_of_week), proc_hour, proc_minute, proc_second] @@ -356,14 +470,16 @@ for proc in procs: (changed, dst) = proc(dst) if changed: - day, month, year = dst.day, dst.month, dst.year + month, year = dst.month, dst.year next = True break if next: continue return self._datetime_to_timestamp(dst.replace(microsecond=0)) - raise Exception("failed to find prev date") + if is_prev: + raise CroniterBadDateError("failed to find prev date") + raise CroniterBadDateError("failed to find next date") def _get_next_nearest(self, x, to_check): small = [item for item in to_check if item < x] @@ -399,7 +515,13 @@ return -x candidate = candidates[0] for c in candidates: - if c < range_val: + # fixed: c < range_val + # this code will reject all 31 day of month, 12 month, 59 second, + # 23 hour and so on. + # if candidates has just a element, this will not harmful. + # but candidates have multiple elements, then values equal to + # range_val will rejected. + if c <= range_val: candidate = c break @@ -410,10 +532,3 @@ return True else: return False - -if __name__ == '__main__': - - base = datetime.datetime(2010, 1, 25) - itr = croniter('0 0 1 * *', base) - n1 = itr.get_next(datetime.datetime) - print(n1) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/src/croniter/tests/test_croniter.py new/croniter-0.3.17/src/croniter/tests/test_croniter.py --- old/croniter-0.3.12/src/croniter/tests/test_croniter.py 2016-03-10 21:31:22.000000000 +0100 +++ new/croniter-0.3.17/src/croniter/tests/test_croniter.py 2017-05-22 12:11:46.000000000 +0200 @@ -1,12 +1,11 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: utf-8 -*- import unittest -from datetime import datetime +from datetime import datetime, timedelta from time import sleep import pytz -from croniter import croniter - +from croniter import croniter, CroniterBadDateError from croniter.tests import base @@ -146,6 +145,49 @@ self.assertEqual(n3.day, 3) self.assertEqual(n3.year, 2010) + def testNthWeekDay(self): + base = datetime(2010, 2, 25) + itr = croniter('0 0 * * sat#1', base) + n1 = itr.get_next(datetime) + self.assertEqual(n1.isoweekday(), 6) + self.assertEqual(n1.day, 6) + self.assertEqual(n1.month, 3) + n2 = itr.get_next(datetime) + self.assertEqual(n2.isoweekday(), 6) + self.assertEqual(n2.day, 3) + self.assertEqual(n2.month, 4) + + base = datetime(2010, 1, 25) + itr = croniter('0 0 * * wed#5', base) + n1 = itr.get_next(datetime) + self.assertEqual(n1.month, 3) + self.assertEqual(n1.day, 31) + self.assertEqual(n1.year, 2010) + n2 = itr.get_next(datetime) + self.assertEqual(n2.month, 6) + self.assertEqual(n2.day, 30) + self.assertEqual(n2.year, 2010) + n3 = itr.get_next(datetime) + self.assertEqual(n3.month, 9) + self.assertEqual(n3.day, 29) + self.assertEqual(n3.year, 2010) + + def testWeekDayDayAnd(self): + base = datetime(2010, 1, 25) + itr = croniter('0 0 1 * mon', base, day_or=False) + n1 = itr.get_next(datetime) + self.assertEqual(n1.month, 2) + self.assertEqual(n1.day, 1) + self.assertEqual(n1.year, 2010) + n2 = itr.get_next(datetime) + self.assertEqual(n2.month, 3) + self.assertEqual(n2.day, 1) + self.assertEqual(n2.year, 2010) + n3 = itr.get_next(datetime) + self.assertEqual(n3.month, 11) + self.assertEqual(n3.day, 1) + self.assertEqual(n3.year, 2010) + def testMonth(self): base = datetime(2010, 1, 25) itr = croniter('0 0 1 * *', base) @@ -228,10 +270,12 @@ self.assertRaises(TypeError, itr.get_next, str) self.assertRaises(ValueError, croniter, '* * * *') self.assertRaises(ValueError, croniter, '* * 5-1 * *') - self.assertRaises(KeyError, croniter, '* * * janu-jun *') + self.assertRaises(ValueError, croniter, '-90 * * * *') + self.assertRaises(ValueError, croniter, 'a * * * *') + self.assertRaises(ValueError, croniter, '* * * janu-jun *') def testSundayToThursdayWithAlphaConversion(self): - base = datetime(2010, 8, 25, 15, 56) #wednesday + base = datetime(2010, 8, 25, 15, 56) # wednesday itr = croniter("30 22 * * sun-thu", base) next = itr.get_next(datetime) @@ -306,6 +350,30 @@ self.assertEqual(prev3.hour, 0) self.assertEqual(prev3.minute, 0) + def testPrevNthWeekDay(self): + base = datetime(2010, 8, 25, 15, 56) + itr = croniter('0 0 * * sat#1,sun#2', base) + prev1 = itr.get_prev(datetime) + self.assertEqual(prev1.year, base.year) + self.assertEqual(prev1.month, base.month) + self.assertEqual(prev1.day, 8) + 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, 7) + 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, 7) + self.assertEqual(prev3.day, 11) + 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) @@ -388,6 +456,112 @@ self.assertEqual(n6.month, 2) self.assertEqual(n6.day, 16) + def test_bug34(self): + base = datetime(2012, 2, 24, 0, 0, 0) + itr = croniter('* * 31 2 *', base) + try: + itr.get_next(datetime) + except (CroniterBadDateError,) as ex: + self.assertEqual("{0}".format(ex), + 'failed to find next date') + + def testBug57(self): + base = datetime(2012, 2, 24, 0, 0, 0) + itr = croniter('0 4/6 * * *', base) + n1 = itr.get_next(datetime) + self.assertEqual(n1.hour, 4) + self.assertEqual(n1.minute, 0) + self.assertEqual(n1.month, 2) + self.assertEqual(n1.day, 24) + + n1 = itr.get_prev(datetime) + self.assertEqual(n1.hour, 22) + self.assertEqual(n1.minute, 0) + self.assertEqual(n1.month, 2) + self.assertEqual(n1.day, 23) + + itr = croniter('0 0/6 * * *', base) + n1 = itr.get_next(datetime) + self.assertEqual(n1.hour, 6) + self.assertEqual(n1.minute, 0) + self.assertEqual(n1.month, 2) + self.assertEqual(n1.day, 24) + + n1 = itr.get_prev(datetime) + self.assertEqual(n1.hour, 0) + self.assertEqual(n1.minute, 0) + self.assertEqual(n1.month, 2) + self.assertEqual(n1.day, 24) + + def test_multiple_months(self): + base = datetime(2016, 3, 1, 0, 0, 0) + itr = croniter('0 0 1 3,6,9,12 *', base) + n1 = itr.get_next(datetime) + self.assertEqual(n1.hour, 0) + self.assertEqual(n1.month, 6) + self.assertEqual(n1.day, 1) + self.assertEqual(n1.year, 2016) + + base = datetime(2016, 2, 15, 0, 0, 0) + itr = croniter('0 0 1 3,6,9,12 *', base) + n1 = itr.get_next(datetime) + self.assertEqual(n1.hour, 0) + self.assertEqual(n1.month, 3) + self.assertEqual(n1.day, 1) + self.assertEqual(n1.year, 2016) + + base = datetime(2016, 12, 3, 10, 0, 0) + itr = croniter('0 0 1 3,6,9,12 *', base) + n1 = itr.get_next(datetime) + self.assertEqual(n1.hour, 0) + self.assertEqual(n1.month, 3) + self.assertEqual(n1.day, 1) + self.assertEqual(n1.year, 2017) + + # The result with this parameters was incorrect. + # self.assertEqual(p1.month, 12 + # AssertionError: 9 != 12 + base = datetime(2016, 3, 1, 0, 0, 0) + itr = croniter('0 0 1 3,6,9,12 *', base) + p1 = itr.get_prev(datetime) + self.assertEqual(p1.hour, 0) + self.assertEqual(p1.month, 12) + self.assertEqual(p1.day, 1) + self.assertEqual(p1.year, 2015) + + # check my change resolves another hidden bug. + base = datetime(2016, 2, 1, 0, 0, 0) + itr = croniter('0 0 1,15,31 * *', base) + p1 = itr.get_prev(datetime) + self.assertEqual(p1.hour, 0) + self.assertEqual(p1.month, 1) + self.assertEqual(p1.day, 31) + self.assertEqual(p1.year, 2016) + + base = datetime(2016, 6, 1, 0, 0, 0) + itr = croniter('0 0 1 3,6,9,12 *', base) + p1 = itr.get_prev(datetime) + self.assertEqual(p1.hour, 0) + self.assertEqual(p1.month, 3) + self.assertEqual(p1.day, 1) + self.assertEqual(p1.year, 2016) + + base = datetime(2016, 3, 1, 0, 0, 0) + itr = croniter('0 0 1 1,3,6,9,12 *', base) + p1 = itr.get_prev(datetime) + self.assertEqual(p1.hour, 0) + self.assertEqual(p1.month, 1) + self.assertEqual(p1.day, 1) + self.assertEqual(p1.year, 2016) + + base = datetime(2016, 3, 1, 0, 0, 0) + itr = croniter('0 0 1 1,3,6,9,12 *', base) + p1 = itr.get_prev(datetime) + self.assertEqual(p1.hour, 0) + self.assertEqual(p1.month, 1) + self.assertEqual(p1.day, 1) + self.assertEqual(p1.year, 2016) + def test_rangeGenerator(self): base = datetime(2013, 3, 4, 0, 0) itr = croniter('1-9/2 0 1 * *', base) @@ -467,13 +641,15 @@ itr = croniter('* * * * *') sleep(.01) itr2 = croniter('* * * * *') - self.assertGreater(itr2.cur, itr.cur) + # Greater dosnt exists in py26 + self.assertTrue(itr2.cur > itr.cur) def assertScheduleTimezone(self, callback, expected_schedule): for expected_date, expected_offset in expected_schedule: d = callback() self.assertEqual(expected_date, d.replace(tzinfo=None)) - self.assertEqual(expected_offset, croniter._timedelta_to_seconds(d.utcoffset())) + self.assertEqual(expected_offset, + croniter._timedelta_to_seconds(d.utcoffset())) def testTimezoneWinterTime(self): tz = pytz.timezone('Europe/Athens') @@ -515,6 +691,47 @@ ct = croniter('*/30 * * * *', tz.localize(start)) self.assertScheduleTimezone(lambda: ct.get_prev(datetime), reversed(expected_schedule)) + def test_std_dst(self): + """ + DST tests + + This fixes https://github.com/taichino/croniter/issues/82 + + """ + tz = pytz.timezone('Europe/Warsaw') + # -> 2017-03-26 01:59+1:00 -> 03:00+2:00 + local_date = tz.localize(datetime(2017, 3, 26)) + val = croniter('0 0 * * *', local_date).get_next(datetime) + self.assertEqual(val, tz.localize(datetime(2017, 3, 27))) + # + local_date = tz.localize(datetime(2017, 3, 26, 1)) + cr = croniter('0 * * * *', local_date) + val = cr.get_next(datetime) + self.assertEqual(val, tz.localize(datetime(2017, 3, 26, 3))) + val = cr.get_current(datetime) + self.assertEqual(val, tz.localize(datetime(2017, 3, 26, 3))) + + # -> 2017-10-29 02:59+2:00 -> 02:00+1:00 + local_date = tz.localize(datetime(2017, 10, 29)) + val = croniter('0 0 * * *', local_date).get_next(datetime) + self.assertEqual(val, tz.localize(datetime(2017, 10, 30))) + local_date = tz.localize(datetime(2017, 10, 29, 1, 59)) + val = croniter('0 * * * *', local_date).get_next(datetime) + self.assertEqual( + val.replace(tzinfo=None), + tz.localize(datetime(2017, 10, 29, 2)).replace(tzinfo=None)) + local_date = tz.localize(datetime(2017, 10, 29, 2)) + val = croniter('0 * * * *', local_date).get_next(datetime) + self.assertEqual(val, tz.localize(datetime(2017, 10, 29, 3))) + local_date = tz.localize(datetime(2017, 10, 29, 3)) + val = croniter('0 * * * *', local_date).get_next(datetime) + self.assertEqual(val, tz.localize(datetime(2017, 10, 29, 4))) + local_date = tz.localize(datetime(2017, 10, 29, 4)) + val = croniter('0 * * * *', local_date).get_next(datetime) + self.assertEqual(val, tz.localize(datetime(2017, 10, 29, 5))) + local_date = tz.localize(datetime(2017, 10, 29, 5)) + val = croniter('0 * * * *', local_date).get_next(datetime) + self.assertEqual(val, tz.localize(datetime(2017, 10, 29, 6))) if __name__ == '__main__': unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/src/croniter.egg-info/PKG-INFO new/croniter-0.3.17/src/croniter.egg-info/PKG-INFO --- old/croniter-0.3.12/src/croniter.egg-info/PKG-INFO 2016-03-10 21:31:22.000000000 +0100 +++ new/croniter-0.3.17/src/croniter.egg-info/PKG-INFO 2017-05-22 12:11:46.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: croniter -Version: 0.3.12 +Version: 0.3.17 Summary: croniter provides iteration for datetime object with cron like format Home-page: http://github.com/kiorky/croniter Author: Matsumoto Taichi, kiorky @@ -12,7 +12,7 @@ .. contents:: - croniter provides iteration for datetime object with cron like format. + croniter provides iteration for the datetime object with a cron like format. :: @@ -33,12 +33,12 @@ Usage ============ - Simple example of usage is followings:: + A simple example:: >>> from croniter import croniter >>> from datetime import datetime >>> base = datetime(2010, 1, 25, 4, 46) - >>> iter = croniter('*/5 * * * *', base) # every 5 minites + >>> iter = croniter('*/5 * * * *', base) # every 5 minutes >>> 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 @@ -47,21 +47,42 @@ >>> 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.:: + >>> + >>> iter = croniter('2 4 1 * wed', base) # 04:02 on every Wednesday OR on 1st day of month + >>> print iter.get_next(datetime) # 2010-01-27 04:02:00 + >>> print iter.get_next(datetime) # 2010-02-01 04:02:00 + >>> print iter.get_next(datetime) # 2010-02-03 04:02:00 + >>> + >>> iter = croniter('2 4 1 * wed', base, day_or=False) # 04:02 on every 1st day of the month if it is a Wednesday + >>> print iter.get_next(datetime) # 2010-09-01 04:02:00 + >>> print iter.get_next(datetime) # 2010-12-01 04:02:00 + >>> print iter.get_next(datetime) # 2011-06-01 04:02:00 + >>> iter = croniter('0 0 * * sat#1,sun#2', base) + >>> print iter.get_next(datetime) # datetime.datetime(2010, 2, 6, 0, 0) + + All you need to know is how to use the constructor and the ``get_next`` + method, the signature of these methods are listed below:: + + >>> def __init__(self, cron_format, start_time=time.time(), day_or=True) + + croniter iterates along with ``cron_format`` from ``start_time``. + ``cron_format`` is **min hour day month day_of_week**, you can refer to + http://en.wikipedia.org/wiki/Cron for more details. The ``day_or`` + switch is used to control how croniter handles **day** and **day_of_week** + entries. Default option is the cron behaviour, which connects those + values using **OR**. If the switch is set to False, the values are connected + using **AND**. This behaves like fcron and enables you to e.g. define a job that + executes each 2nd friday of a month by setting the days of month and the + weekday. + :: >>> 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'. + get_next calculates the next value according to the cron expression and + returns an object of type ``ret_type``. ``ret_type`` should be a ``float`` or a + ``datetime`` object. - Now, supported get_prev method. (>= 0.2.0):: + Supported added for ``get_prev`` method. (>= 0.2.0):: >>> base = datetime(2010, 8, 25) >>> itr = croniter('0 0 1 * *', base) @@ -69,6 +90,12 @@ >>> print itr.get_prev(datetime) # 2010-07-01 00:00:00 >>> print itr.get_prev(datetime) # 2010-06-01 00:00:00 + About DST + ========= + Be sure to init your croniter instance with a TZ aware datetime for this to work !:: + + >>> local_date = tz.localize(datetime(2017, 3, 26)) + >>> val = croniter('0 0 * * *', local_date).get_next(datetime) Develop this package ==================== @@ -77,25 +104,28 @@ git clone https://github.com/kiorky/croniter.git cd croniter - python bootstrap.py -d - bin/buildout -vvvvvvN - bin/test + virtualenv --no-site-packages venv + . venv/bin/activate + pip install --upgrade -r requirements/test.txt + py.test src Make a new release ==================== - We use zest.fullreleaser, a great releaser infrastructure. + We use zest.fullreleaser, a great release infrastructure. - Do and follow the instructions + Do and follow these instructions :: - bin/fullrelease + . venv/bin/activate + pip install --upgrade -r requirements/release.txt + fullrelease Contributors =============== - Thank you to all who have contributed to this project! - If you contributed and not listed below please let me know. + Thanks to all who have contributed to this project! + If you have contributed and your name is not listed below please let me know. - mrmachine - Hinnack @@ -114,6 +144,50 @@ Changelog ============== + 0.3.17 (2017-05-22) + ------------------- + - DOW occurence sharp style support. + [kiorky, Kengo Seki <sekikn@apache.org>] + + + 0.3.16 (2017-03-15) + ------------------- + + - Better test suite [mrcrilly@github] + - DST support [kiorky] + + 0.3.15 (2017-02-16) + ------------------- + + - fix bug around multiple conditions and range_val in + _get_prev_nearest_diff. + [abeja-yuki@github] + + 0.3.14 (2017-01-25) + ------------------- + + - issue #69: added day_or option to change behavior when day-of-month and + day-of-week is given + [Andreas Vogl <a.vogl@hackner-security.com>] + + + + 0.3.13 (2016-11-01) + ------------------- + + - `Real fix for #34 <https://github.com/taichino/croniter/pull/73>`_ + [kiorky@github] + - `Modernize test infra <https://github.com/taichino/croniter/pull/72>`_ + [kiorky@github] + - `Release as a universal wheel <https://github.com/kiorky/croniter/pull/16>`_ + [adamchainz@github] + - `Raise ValueError on negative numbers <https://github.com/taichino/croniter/pull/63>`_ + [josegonzalez@github] + - `Compare types using "issubclass" instead of exact match <https://github.com/taichino/croniter/pull/70>`_ + [darkk@github] + - `Implement step cron with a variable base <https://github.com/taichino/croniter/pull/60>`_ + [josegonzalez@github] + 0.3.12 (2016-03-10) ------------------- - support setting ret_type in __init__ [Brent Tubbs <brent.tubbs@gmail.com>] @@ -185,4 +259,6 @@ Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: POSIX Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Software Development :: Libraries :: Python Modules diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/src/croniter.egg-info/SOURCES.txt new/croniter-0.3.17/src/croniter.egg-info/SOURCES.txt --- old/croniter-0.3.12/src/croniter.egg-info/SOURCES.txt 2016-03-10 21:31:22.000000000 +0100 +++ new/croniter-0.3.17/src/croniter.egg-info/SOURCES.txt 2017-05-22 12:11:46.000000000 +0200 @@ -1,10 +1,13 @@ MANIFEST.in README.rst -buildout.cfg -py3.cfg +setup.cfg setup.py +tox.ini docs/CHANGES.rst docs/LICENSE +requirements/base.txt +requirements/release.txt +requirements/test.txt src/croniter/__init__.py src/croniter/croniter.py src/croniter.egg-info/PKG-INFO diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/src/croniter.egg-info/requires.txt new/croniter-0.3.17/src/croniter.egg-info/requires.txt --- old/croniter-0.3.12/src/croniter.egg-info/requires.txt 2016-03-10 21:31:22.000000000 +0100 +++ new/croniter-0.3.17/src/croniter.egg-info/requires.txt 2017-05-22 12:11:46.000000000 +0200 @@ -1,5 +1 @@ -python-dateutil -setuptools - -[test] -pytz +python_dateutil diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/croniter-0.3.12/tox.ini new/croniter-0.3.17/tox.ini --- old/croniter-0.3.12/tox.ini 1970-01-01 01:00:00.000000000 +0100 +++ new/croniter-0.3.17/tox.ini 2017-05-22 12:11:46.000000000 +0200 @@ -0,0 +1,22 @@ +[tox] +minversion = 2.3 +envlist = + {py26,py27,py34,py35}-std + py27-coverage +skipsdist = true + +[testenv] +usedevelop = true +deps = + -r{toxinidir}/requirements/test.txt +whitelist_externals = /bin/sh +setenv = + COVERAGE_FILE={envdir}/coverage_report +changedir = src +commands = + {py26,py27,py34,py35,py36}-std: py.test -v . + {py27,py34,py35,py36}-std: flake8 src/croniter/croniter.py + py27-coverage: coverage erase + py27-coverage: sh -c 'cd .. && coverage run $(which py.test) -v src' + py27-coverage: coverage report +
participants (1)
-
root@hilbert.suse.de