From 8cfe3540b77dba4083e42aa91bb32942e1e26a04 Mon Sep 17 00:00:00 2001 From: Michael Hirsch Date: Tue, 28 Dec 2021 23:51:05 -0500 Subject: [PATCH] Python >= 3.7 anno, src layout --- .gitattributes | 20 ----------- .mypy.ini | 6 +--- archive/.appveyor.yml | 19 ---------- archive/.travis.yml | 35 ------------------- date2doy.py | 17 ++++----- findnearest.py | 11 ++---- pyproject.toml | 2 +- randomdate.py | 15 +++----- setup.cfg | 15 ++++---- .../sciencedates}/__init__.py | 0 {sciencedates => src/sciencedates}/dec.py | 12 ++++--- {sciencedates => src/sciencedates}/doy.py | 26 ++++++++------ .../sciencedates}/findnearest.py | 14 +++++--- {sciencedates => src/sciencedates}/random.py | 2 +- .../sciencedates}/tests/test_conv.py | 29 +++------------ .../sciencedates}/tests/test_msis.py | 24 +++++++------ .../sciencedates}/tests/test_time.py | 10 ++---- {sciencedates => src/sciencedates}/ticks.py | 0 {sciencedates => src/sciencedates}/tz.py | 11 +++--- 19 files changed, 88 insertions(+), 180 deletions(-) delete mode 100644 .gitattributes delete mode 100644 archive/.appveyor.yml delete mode 100644 archive/.travis.yml rename {sciencedates => src/sciencedates}/__init__.py (100%) rename {sciencedates => src/sciencedates}/dec.py (89%) rename {sciencedates => src/sciencedates}/doy.py (81%) rename {sciencedates => src/sciencedates}/findnearest.py (75%) rename {sciencedates => src/sciencedates}/random.py (88%) rename {sciencedates => src/sciencedates}/tests/test_conv.py (74%) rename {sciencedates => src/sciencedates}/tests/test_msis.py (82%) rename {sciencedates => src/sciencedates}/tests/test_time.py (76%) rename {sciencedates => src/sciencedates}/ticks.py (100%) rename {sciencedates => src/sciencedates}/tz.py (79%) diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index aafaecf..0000000 --- a/.gitattributes +++ /dev/null @@ -1,20 +0,0 @@ -.gitattributes text eol=lf -.gitignore text eol=lf -Makefile text eol=lf -*.yml text eol=lf -LICENSE text eol=lf -*.ipynb text eol=lf -*.txt text eol=lf -*.py text eol=lf -*.sh text eol=lf -*.c text eol=lf -*.cpp text eol=lf -*.f text eol=lf -*.for text eol=lf -*.f90 text eol=lf -*.md text eol=lf -*.rst text eol=lf -*.csv text eol=lf -*.m text eol=lf -*.grc text eol=lf -*.pas text eol=lf diff --git a/.mypy.ini b/.mypy.ini index 4191213..6c4e626 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -1,12 +1,8 @@ [mypy] -files = sciencedates/ +files = src/ ignore_missing_imports = True -strict_optional = False allow_redefinition = True show_error_context = False show_column_numbers = True warn_unreachable = False - -[mypy-xarray] -follow_imports = skip diff --git a/archive/.appveyor.yml b/archive/.appveyor.yml deleted file mode 100644 index e328379..0000000 --- a/archive/.appveyor.yml +++ /dev/null @@ -1,19 +0,0 @@ -image: -- Visual Studio 2017 -- ubuntu1804 - -stack: python 3 - -environment: - PY_DIR: C:\Python37-x64 - -clone_depth: 25 - -build: off - -init: -- cmd: set PATH=%PY_DIR%;%PY_DIR%\Scripts;%PATH% - -install: pip install -e .[tests] - -test_script: pytest diff --git a/archive/.travis.yml b/archive/.travis.yml deleted file mode 100644 index d4dceed..0000000 --- a/archive/.travis.yml +++ /dev/null @@ -1,35 +0,0 @@ -language: python -group: travis_latest - -git: - depth: 25 - quiet: true - -python: -- 3.5 -- 3.6 -- 3.7 - -matrix: - include: - - os: linux - python: 3.7 - install: pip install -e .[tests,cov] - script: - - flake8 - - mypy . - after_success: - - pytest --cov - - coveralls - - os: osx - language: minimal - install: pip3 install -e .[tests] - - os: windows - language: minimal - before_install: - - choco install python3 - - export PATH="/c/Python37:/c/Python37/Scripts:$PATH" - -install: pip install -e .[tests] - -script: pytest diff --git a/date2doy.py b/date2doy.py index fdee6d2..3a93da9 100755 --- a/date2doy.py +++ b/date2doy.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ command-line utility to convert date to day of year """ @@ -6,15 +6,10 @@ from sciencedates import date2doy -def main(): - p = ArgumentParser(description="convert date to day of year") - p.add_argument("date", help="yyyy-mm-dd") - P = p.parse_args() +p = ArgumentParser(description="convert date to day of year") +p.add_argument("date", help="yyyy-mm-dd") +P = p.parse_args() - doy, year = date2doy(P.date) +doy, year = date2doy(P.date) - print(doy.item()) - - -if __name__ == "__main__": - main() +print(doy.item()) diff --git a/findnearest.py b/findnearest.py index d128c65..967c9af 100644 --- a/findnearest.py +++ b/findnearest.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import numpy as np from bisect import bisect import sciencedates as sd @@ -13,11 +13,6 @@ def INCORRECTRESULT_using_bisect(x, X0): # pragma: no cover return np.asanyarray(ind), x[ind] -def main(): - print(sd.find_nearest([10, 15, 12, 20, 14, 33], [32, 12.01])) +print(sd.find_nearest([10, 15, 12, 20, 14, 33], [32, 12.01])) - print(INCORRECTRESULT_using_bisect([10, 15, 12, 20, 14, 33], [32, 12.01])) - - -if __name__ == "__main__": # pragma: no cover - main() +print(INCORRECTRESULT_using_bisect([10, 15, 12, 20, 14, 33], [32, 12.01])) diff --git a/pyproject.toml b/pyproject.toml index 2f2c683..90ccf9c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,4 +2,4 @@ requires = ["setuptools", "wheel"] [tool.black] -line-length = 132 +line-length = 100 diff --git a/randomdate.py b/randomdate.py index 7cf3542..bec137c 100755 --- a/randomdate.py +++ b/randomdate.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ generate a random date (month and day) in a year. Michael Hirsch, Ph.D. @@ -8,13 +8,8 @@ from sciencedates import randomdate -def main(): - p = ArgumentParser(description="generate random date in year") - p.add_argument("year", type=int, nargs="?", default=date.today().year) - P = p.parse_args() +p = ArgumentParser(description="generate random date in year") +p.add_argument("year", type=int, nargs="?", default=date.today().year) +P = p.parse_args() - print(randomdate(P.year)) - - -if __name__ == "__main__": - main() +print(randomdate(P.year)) diff --git a/setup.cfg b/setup.cfg index fcd82c1..fdfa229 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = sciencedates -version = 1.5.0 +version = 1.5.1 author = Michael Hirsch, Ph.D. author_email = scivision@users.noreply.github.com url = https://github.com/geospace-code/sciencedates @@ -21,15 +21,16 @@ long_description = file: README.md long_description_content_type = text/markdown [options] -python_requires = >= 3.5 +python_requires = >= 3.7 packages = find: -scripts = - date2doy.py - findnearest.py - randomdate.py install_requires = numpy python-dateutil +package_dir= + =src + +[options.packages.find] +where=src [options.extras_require] tests = @@ -37,6 +38,8 @@ tests = lint = flake8 mypy + types-pytz + types-python-dateutil plot = xarray matplotlib diff --git a/sciencedates/__init__.py b/src/sciencedates/__init__.py similarity index 100% rename from sciencedates/__init__.py rename to src/sciencedates/__init__.py diff --git a/sciencedates/dec.py b/src/sciencedates/dec.py similarity index 89% rename from sciencedates/dec.py rename to src/sciencedates/dec.py index 914df80..80ff527 100644 --- a/sciencedates/dec.py +++ b/src/sciencedates/dec.py @@ -1,12 +1,14 @@ +from __future__ import annotations import typing as T -import numpy as np import datetime import dateutil.parser +import numpy as np + __all__ = ["datetime2utsec", "datetime2yeardec", "yeardec2datetime"] -def datetime2utsec(t: T.Union[str, datetime.date, datetime.datetime, np.datetime64]) -> float: +def datetime2utsec(t: T.Any) -> float | np.ndarray: """ input: datetime output: float utc seconds since THIS DAY'S MIDNIGHT @@ -20,7 +22,9 @@ def datetime2utsec(t: T.Union[str, datetime.date, datetime.datetime, np.datetime elif isinstance(t, str): t = dateutil.parser.parse(t) - return datetime.timedelta.total_seconds(t - datetime.datetime.combine(t.date(), datetime.datetime.min.time())) + return datetime.timedelta.total_seconds( + t - datetime.datetime.combine(t.date(), datetime.datetime.min.time()) + ) def yeardec2datetime(atime: float) -> datetime.datetime: @@ -54,7 +58,7 @@ def yeardec2datetime(atime: float) -> datetime.datetime: return time -def datetime2yeardec(time: T.Union[str, datetime.datetime, datetime.date]) -> float: +def datetime2yeardec(time: str | datetime.datetime | datetime.date) -> float: """ Convert a datetime into a float. The integer part of the float should represent the year. diff --git a/sciencedates/doy.py b/src/sciencedates/doy.py similarity index 81% rename from sciencedates/doy.py rename to src/sciencedates/doy.py index df9fc8d..869299c 100644 --- a/sciencedates/doy.py +++ b/src/sciencedates/doy.py @@ -1,3 +1,4 @@ +from __future__ import annotations import typing as T import datetime import numpy as np @@ -8,7 +9,7 @@ __all__ = ["datetime2yeardoy", "yeardoy2datetime", "date2doy", "datetime2gtd"] -def datetime2yeardoy(time: T.Union[str, datetime.datetime]) -> T.Tuple[int, float]: +def datetime2yeardoy(time: T.Any) -> tuple[int, float]: """ Inputs: T: Numpy 1-D array of datetime.datetime OR string for dateutil.parser.parse @@ -33,7 +34,7 @@ def datetime2yeardoy(time: T.Union[str, datetime.datetime]) -> T.Tuple[int, floa return yd.squeeze()[()], utsec.squeeze()[()] -def yeardoy2datetime(yeardate: int, utsec: T.Union[float, int] = None) -> datetime.datetime: +def yeardoy2datetime(yeardate: int, utsec: float | int = 0) -> datetime.datetime: """ Parameters @@ -51,10 +52,10 @@ def yeardoy2datetime(yeardate: int, utsec: T.Union[float, int] = None) -> dateti http://stackoverflow.com/questions/2427555/python-question-year-and-day-of-year-to-date """ if isinstance(yeardate, (tuple, list, np.ndarray)): - if utsec is None: - return np.asarray([yeardoy2datetime(y) for y in yeardate]) - elif isinstance(utsec, (tuple, list, np.ndarray)): + if isinstance(utsec, (tuple, list, np.ndarray)): return np.asarray([yeardoy2datetime(y, s) for y, s in zip(yeardate, utsec)]) + else: + return np.asarray([yeardoy2datetime(y, utsec) for y in yeardate]) yeardate = int(yeardate) @@ -65,15 +66,16 @@ def yeardoy2datetime(yeardate: int, utsec: T.Union[float, int] = None) -> dateti year = int(yd[:4]) assert 0 < year < 3000, "year not in expected format" - dt = datetime.datetime(year, 1, 1) + datetime.timedelta(days=int(yd[4:]) - 1) - - if utsec is not None: - dt += datetime.timedelta(seconds=utsec) + dt = ( + datetime.datetime(year, 1, 1) + + datetime.timedelta(days=int(yd[4:]) - 1) + + datetime.timedelta(seconds=utsec) + ) return dt -def date2doy(time: T.Union[str, datetime.datetime]) -> T.Tuple[int, int]: +def date2doy(time: T.Any) -> tuple[np.ndarray, np.ndarray]: """ < 366 for leap year too. normal year 0..364. Leap 0..365. """ @@ -94,7 +96,9 @@ def date2doy(time: T.Union[str, datetime.datetime]) -> T.Tuple[int, int]: return doy, year -def datetime2gtd(time: T.Union[str, datetime.datetime, np.datetime64], glon: np.ndarray = np.nan) -> T.Tuple[int, float, float]: +def datetime2gtd( + time: T.Any, glon: float | np.ndarray = np.nan +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """ Parameters diff --git a/sciencedates/findnearest.py b/src/sciencedates/findnearest.py similarity index 75% rename from sciencedates/findnearest.py rename to src/sciencedates/findnearest.py index f0404c5..9a36b65 100644 --- a/sciencedates/findnearest.py +++ b/src/sciencedates/findnearest.py @@ -1,9 +1,10 @@ -from typing import Tuple, Any +from __future__ import annotations +import typing as T import numpy as np import datetime -def find_nearest(x, x0) -> Tuple[int, Any]: +def find_nearest(x, x0) -> tuple[int, T.Any]: """ This find_nearest function does NOT assume sorted input @@ -34,9 +35,14 @@ def find_nearest(x, x0) -> Tuple[int, Any]: # NOTE: not trapping IndexError (all-nan) becaues returning None can surprise with slice indexing for i, xi in enumerate(x0): - if xi is not None and (isinstance(xi, (datetime.datetime, datetime.date, np.datetime64)) or np.isfinite(xi)): + if xi is not None and ( + isinstance(xi, (datetime.datetime, datetime.date, np.datetime64)) or np.isfinite(xi) + ): ind[i] = np.nanargmin(abs(x - xi)) else: raise ValueError("x0 must NOT be None or NaN to avoid surprising None return value") - return ind.squeeze()[()], x[ind].squeeze()[()] # [()] to pop scalar from 0d array while being OK with ndim>0 + return ( + ind.squeeze()[()], + x[ind].squeeze()[()], + ) # [()] to pop scalar from 0d array while being OK with ndim>0 diff --git a/sciencedates/random.py b/src/sciencedates/random.py similarity index 88% rename from sciencedates/random.py rename to src/sciencedates/random.py index 147ebfe..93c57ef 100644 --- a/sciencedates/random.py +++ b/src/sciencedates/random.py @@ -4,7 +4,7 @@ def randomdate(year: int) -> datetime.date: - """ gives random date in year""" + """gives random date in year""" if calendar.isleap(year): doy = random.randrange(366) else: diff --git a/sciencedates/tests/test_conv.py b/src/sciencedates/tests/test_conv.py similarity index 74% rename from sciencedates/tests/test_conv.py rename to src/sciencedates/tests/test_conv.py index acf1d07..c55a663 100755 --- a/sciencedates/tests/test_conv.py +++ b/src/sciencedates/tests/test_conv.py @@ -1,20 +1,19 @@ -#!/usr/bin/env python +from __future__ import annotations import datetime from dateutil.parser import parse +import typing + import numpy as np from pytest import approx -import pytest -import sys -import typing + import sciencedates as sd -T: typing.List[typing.Any] = [datetime.datetime(2013, 7, 2, 12, 0, 0)] +T: list[typing.Any] = [datetime.datetime(2013, 7, 2, 12, 0, 0)] T.append(T[0].date()) T.append(np.datetime64(T[0])) T.append(str(T[0])) Tdt = (T[0],) * 3 -OLDPY = sys.version_info < (3, 6) def test_yearint(): @@ -62,21 +61,3 @@ def test_yeardec(): assert sd.yeardec2datetime(yeardec).date() == t # %% array assert (sd.yeardec2datetime(sd.datetime2yeardec(Tdt)) == T[0]).all() - - -@pytest.mark.skip(OLDPY, reason="This test for Python < 3.6") -def test_utc(): - pytz = pytest.importorskip("pytz") - - estdt = T[0].astimezone(pytz.timezone("EST")) - utcdt = sd.forceutc(estdt) - - assert utcdt == estdt - assert utcdt.tzname() == "UTC" - - d = T[0].date() - assert sd.forceutc(d) == d - - -if __name__ == "__main__": - pytest.main([__file__]) diff --git a/sciencedates/tests/test_msis.py b/src/sciencedates/tests/test_msis.py similarity index 82% rename from sciencedates/tests/test_msis.py rename to src/sciencedates/tests/test_msis.py index 37e2cdd..b9cba7b 100644 --- a/sciencedates/tests/test_msis.py +++ b/src/sciencedates/tests/test_msis.py @@ -1,16 +1,17 @@ -#!/usr/bin/env python """ tests for time conversions relevant to MSISE00 """ -import numpy as np + +from __future__ import annotations import datetime -import pytest -from pytest import approx import typing +import numpy as np +from pytest import approx + import sciencedates as sd -T: typing.List[typing.Any] = [datetime.datetime(2013, 7, 2, 12, 0, 0)] +T: list[typing.Any] = [datetime.datetime(2013, 7, 2, 12, 0, 0)] T.append(T[0].date()) T.append(np.datetime64(T[0])) T.append(str(T[0])) @@ -74,11 +75,14 @@ def test_glon(): iyd, utsec, stl = sd.datetime2gtd(T, glon) - Estl = np.array([np.arange(0, 24 + 3, 3), np.arange(-12, 12 + 3, 3), np.arange(0, 24 + 3, 3), np.arange(0, 24 + 3, 3)]) + Estl = np.array( + [ + np.arange(0, 24 + 3, 3), + np.arange(-12, 12 + 3, 3), + np.arange(0, 24 + 3, 3), + np.arange(0, 24 + 3, 3), + ] + ) assert utsec == approx((43200, 0, 43200, 43200)) assert stl == approx(Estl) - - -if __name__ == "__main__": - pytest.main([__file__]) diff --git a/sciencedates/tests/test_time.py b/src/sciencedates/tests/test_time.py similarity index 76% rename from sciencedates/tests/test_time.py rename to src/sciencedates/tests/test_time.py index a6fbc03..521f546 100644 --- a/sciencedates/tests/test_time.py +++ b/src/sciencedates/tests/test_time.py @@ -1,6 +1,5 @@ -#!/usr/bin/env python3 import datetime -import pytest + from pytest import approx import sciencedates as sd @@ -13,7 +12,8 @@ def test_findnearest(): assert xf == approx([33.0, 12.0]) indf, xf = sd.find_nearest( - (datetime.datetime(2012, 1, 1, 12), datetime.datetime(2012, 1, 1, 11)), datetime.datetime(2012, 1, 1, 11, 30) + (datetime.datetime(2012, 1, 1, 12), datetime.datetime(2012, 1, 1, 11)), + datetime.datetime(2012, 1, 1, 11, 30), ) assert indf == 0 assert xf == datetime.datetime(2012, 1, 1, 12) @@ -21,7 +21,3 @@ def test_findnearest(): def test_randomdate(): assert sd.randomdate(2018).year == 2018 - - -if __name__ == "__main__": - pytest.main([__file__]) diff --git a/sciencedates/ticks.py b/src/sciencedates/ticks.py similarity index 100% rename from sciencedates/ticks.py rename to src/sciencedates/ticks.py diff --git a/sciencedates/tz.py b/src/sciencedates/tz.py similarity index 79% rename from sciencedates/tz.py rename to src/sciencedates/tz.py index 7126cb2..626d766 100644 --- a/sciencedates/tz.py +++ b/src/sciencedates/tz.py @@ -1,11 +1,13 @@ -from typing import Union -import numpy as np +from __future__ import annotations +import typing as T import datetime from pytz import UTC from dateutil.parser import parse +import numpy as np + -def forceutc(t: Union[str, datetime.datetime, datetime.date, np.datetime64]) -> Union[datetime.datetime, datetime.date]: +def forceutc(t: T.Any) -> datetime.datetime | datetime.date | np.ndarray: """ Add UTC to datetime-naive and convert to UTC for datetime aware @@ -30,6 +32,7 @@ def forceutc(t: Union[str, datetime.datetime, datetime.date, np.datetime64]) -> if t.tzinfo is None: # datetime-naive t = t.replace(tzinfo=UTC) else: # datetime-aware - t = t.astimezone(UTC) # changes timezone, preserving absolute time. E.g. noon EST = 5PM UTC + t = t.astimezone(UTC) + # changes timezone, preserving absolute time. E.g. noon EST = 5PM UTC return t