From b3b09686a5d9d8ed743c57c5da97a6d77db66895 Mon Sep 17 00:00:00 2001 From: Bruno Bord Date: Fri, 4 Oct 2019 14:32:59 +0200 Subject: [PATCH] Enables to shift the expiration date limit by "n" days in the ``get_skyfield_data_path`` function * Added ``expiration_limit`` argument. Default value is ``0``, so you're only checking for *already expired* files, * Updated documentation in README. * Updated docstrings --- CHANGELOG.md | 2 +- README.md | 16 +++++++++++++ skyfield_data/__init__.py | 8 +++++-- skyfield_data/expirations.py | 37 +++++++++++++++++++++------- tests/test_expiration_date.py | 45 +++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88b7cdd..8666e84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## master (unreleased) -Nothing here yet. +* Added ``expiration_limit`` argument for ``get_skyfield_data_path`` function. Enables to shift the expiration date limit by "n" days. ## 0.1.0 (2019-10-04) diff --git a/README.md b/README.md index 9ae75a6..26477b9 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,22 @@ Whenever a file contained in the catalog has expired, you're going to receive a By default, the loading isn't blocked, but it's strongly recommended to upgrade to a more recent version, to make sure you're not going to make wrong astronomical computations. +### Custom limit + +By default, the ``RuntimeWarning`` is raised when the file **has** expired. You may want to be aware of this warning **in advance**, that is to say a few days or weeks before, in order to eventually upgrade your version of ``skyfield-data``. + +In order to trigger this warning, you can use the ``expiration_limit`` argument, like this: + +```python +>>> from skyfield_data import get_skyfield_data_path +>>> from skyfield.api import Loader +>>> load = Loader(get_skyfield_data_path(expiration_limit=30)) +/home/[redacted]/skyfield_data/expirations.py:25: RuntimeWarning: The file de421.bsp would expire in less than 30 days. Please upgrade your version of `skyfield-data` or expect computation errors + RuntimeWarning +``` + +**Note:** The ``expiration_limit`` argument should be a positive integer (or zero). + ## Developers We're providing a ``Makefile`` with basic targets to play around with the toolkit. use ``make help`` to get more details. diff --git a/skyfield_data/__init__.py b/skyfield_data/__init__.py index cc8242b..587ab44 100644 --- a/skyfield_data/__init__.py +++ b/skyfield_data/__init__.py @@ -4,7 +4,7 @@ from .expirations import check_expirations __DATA_PATH = abspath(join(dirname(__file__), "data")) -def get_skyfield_data_path(): +def get_skyfield_data_path(expiration_limit=0): """ Return the data path to be used along with Skyfield loader. @@ -14,8 +14,12 @@ def get_skyfield_data_path(): from skyfield.api import Loader load = Loader(get_skyfield_data_path()) planets = load('de421.bsp') + + :param expiration_limit: Limit in days for expiration calculation. + Default is zero (only check for expired files) + :type expiration_limit: int """ - check_expirations() + check_expirations(expiration_limit) return __DATA_PATH diff --git a/skyfield_data/expirations.py b/skyfield_data/expirations.py index 949e30e..025faf2 100644 --- a/skyfield_data/expirations.py +++ b/skyfield_data/expirations.py @@ -1,5 +1,5 @@ import warnings -from datetime import date +from datetime import date, timedelta from os.path import dirname, join, abspath from os import listdir from .expiration_data import EXPIRATIONS @@ -12,15 +12,34 @@ def get_all(): return EXPIRATIONS -def check_expirations(): +def check_expirations(expiration_limit=0): + """ + Check for expiration dates on each file in the catalog. + + :param expiration_limit: Limit in days for expiration calculation. + Default is zero (only check for expired files) + :type expiration_limit: int + """ + if not isinstance(expiration_limit, int) or expiration_limit < 0: + raise ValueError( + "Argument `expiration_limit` should be a positive integer" + ) + expirations = get_all() files = listdir(__DATA_PATH) for filename in files: expiration_date = expirations.get(filename) - if expiration_date and date.today() >= expiration_date: - warnings.warn( - ("The file {} has expired." - " Please upgrade your version of `skyfield-data` or expect" - " computation errors").format(filename), - RuntimeWarning - ) + if expiration_date: + message = ( + "The file {} has expired." + " Please upgrade your version of `skyfield-data` or expect" + " computation errors").format(filename) + if expiration_limit: + expiration_date -= timedelta(days=expiration_limit) + message = ( + "The file {} would expire in less than {} days." + " Please upgrade your version of `skyfield-data` or expect" + " computation errors").format(filename, expiration_limit) + + if date.today() >= expiration_date: + warnings.warn(message, RuntimeWarning) diff --git a/tests/test_expiration_date.py b/tests/test_expiration_date.py index 98ecebe..010bff2 100644 --- a/tests/test_expiration_date.py +++ b/tests/test_expiration_date.py @@ -1,7 +1,9 @@ +import pytest import mock from datetime import date, timedelta from skyfield_data import get_skyfield_data_path from skyfield_data.expiration_data import EXPIRATIONS +from skyfield_data.expirations import check_expirations @mock.patch('skyfield_data.expirations.get_all') @@ -30,6 +32,49 @@ def test_expiration_deltat_yesterday(mocked_exp): with mock.patch('warnings.warn') as mocked_warn: get_skyfield_data_path() assert mocked_warn.call_count == 1 + message = mocked_warn.call_args[0][0] + assert "The file deltat.data has expired." in message + + +@mock.patch('skyfield_data.expirations.get_all') +def test_expiration_deltat_custom_limit(mocked_exp): + # It expires in 20 days + mocked_exp.return_value = { + 'deltat.data': date.today() + timedelta(days=20) + } + with mock.patch('warnings.warn') as mocked_warn: + # Limit is 40 days, the limit is reached + get_skyfield_data_path(expiration_limit=40) + assert mocked_warn.call_count == 1 + message = mocked_warn.call_args[0][0] + assert "The file deltat.data would expire in less than 40 days." in message + + with mock.patch('warnings.warn') as mocked_warn: + # Limit is 15 days, the limit is not reached + get_skyfield_data_path(expiration_limit=15) + assert mocked_warn.call_count == 0 + + +def test_wrong_custom_expiration_limit_get_path(): + with pytest.raises(ValueError): + get_skyfield_data_path(expiration_limit=-1) + + with pytest.raises(ValueError): + get_skyfield_data_path(expiration_limit=None) + + with pytest.raises(ValueError): + get_skyfield_data_path(expiration_limit="bad") + + +def test_wrong_custom_expiration_limit_check_expirations(): + with pytest.raises(ValueError): + check_expirations(expiration_limit=-1) + + with pytest.raises(ValueError): + check_expirations(expiration_limit=None) + + with pytest.raises(ValueError): + check_expirations(expiration_limit="bad") def test_current_expiration_date():