diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml deleted file mode 100644 index 6f47e98..0000000 --- a/.github/workflows/black.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: Check black formatting - -on: [push] - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: psf/black@stable diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml deleted file mode 100644 index 621b03d..0000000 --- a/.github/workflows/flake8.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Python flake8 check - -on: [push] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v4 - with: - python-version: "3.10" - - name: Lint with flake8 - run: | - pip install flake8 - flake8 . --exclude=docs --count --ignore=W503,W504,F541,E203 --max-line-length=100 --show-source --statistics diff --git a/.github/workflows/python-formatting.yml b/.github/workflows/python-formatting.yml new file mode 100644 index 0000000..4dc79f7 --- /dev/null +++ b/.github/workflows/python-formatting.yml @@ -0,0 +1,10 @@ +name: check format using ruff +on: [push] +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: chartboost/ruff-action@v1 + with: + args: format --check diff --git a/.github/workflows/python-linting.yml b/.github/workflows/python-linting.yml new file mode 100644 index 0000000..5b842f0 --- /dev/null +++ b/.github/workflows/python-linting.yml @@ -0,0 +1,8 @@ +name: lint code using ruff +on: [push] +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: chartboost/ruff-action@v1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e3e5bcb..f055986 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,9 @@ repos: -- repo: https://github.com/psf/black - rev: 23.3.0 - hooks: - - id: black - language_version: python3.10 - -- repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort - name: isort (python) - language_version: python3.10 +- repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.1.5 + hooks: + # Run the linter. + - id: ruff + # Run the formatter. + - id: ruff-format diff --git a/cxotime/__init__.py b/cxotime/__init__.py index ab702a8..966a80a 100644 --- a/cxotime/__init__.py +++ b/cxotime/__init__.py @@ -1,10 +1,10 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import ska_helpers -from astropy import units # noqa -from astropy.time import TimeDelta # noqa +from astropy import units +from astropy.time import TimeDelta -from .convert import * # noqa -from .cxotime import CxoTime, CxoTimeLike # noqa: F401 +from .convert import * +from .cxotime import CxoTime, CxoTimeLike __version__ = ska_helpers.get_version(__package__) diff --git a/cxotime/convert.py b/cxotime/convert.py index 5bcd10d..c838b44 100644 --- a/cxotime/convert.py +++ b/cxotime/convert.py @@ -289,7 +289,10 @@ def make_docstring(fmt_in, fmt_out): doc_in = fmt_in_cls.convert_doc fmt_out_cls = TIME_FORMATS[fmt_out] doc_out = fmt_out_cls.convert_doc - equiv = f"CxoTime({doc_in['input_name']}, format='{fmt_in_cls.name}').{fmt_out_cls.name}" + equiv = ( + f"CxoTime({doc_in['input_name']}," + f" format='{fmt_in_cls.name}').{fmt_out_cls.name}" + ) out = f"""\ Convert {doc_in['descr_short']} to {doc_out['descr_short']}. @@ -329,6 +332,6 @@ def make_docstring(fmt_in, fmt_out): f"lambda {input_name}: " f"convert_time_format({input_name}, fmt_in='{fmt1}', fmt_out='{fmt2}')" ) - func = globals()[name] = eval(func_str) + func = globals()[name] = eval(func_str) # noqa: PGH001 func.__doc__ = make_docstring(fmt1, fmt2) - __all__.append(name) + __all__.append(name) # noqa: PYI056 diff --git a/cxotime/cxotime.py b/cxotime/cxotime.py index b09ca7f..372e663 100644 --- a/cxotime/cxotime.py +++ b/cxotime/cxotime.py @@ -246,33 +246,33 @@ def get_conversions(self): return out -TimeJD.convert_doc = dict( - input_name="jd", - descr_short="Julian Date", - input_format="Julian Date (numeric)", - output_format="Julian Date (numeric)", - input_type="float, int, list, ndarray", - output_type="float, ndarray[float]", -) +TimeJD.convert_doc = { + "input_name": "jd", + "descr_short": "Julian Date", + "input_format": "Julian Date (numeric)", + "output_format": "Julian Date (numeric)", + "input_type": "float, int, list, ndarray", + "output_type": "float, ndarray[float]", +} class TimeSecs(TimeCxcSec): - """ - Chandra X-ray Center seconds from 1998-01-01 00:00:00 TT. + """Chandra X-ray Center seconds from 1998-01-01 00:00:00 TT. + For example, 63072064.184 is midnight on January 1, 2000. """ name = "secs" # Documentation inputs for convert functions - convert_doc = dict( - input_name="time", - descr_short="CXC seconds", - input_format="CXC seconds (numeric)", - output_format="CXC seconds (numeric)", - input_type="float, int, list, ndarray", - output_type="float, ndarray[float]", - ) + convert_doc = { + "input_name": "time", + "descr_short": "CXC seconds", + "input_format": "CXC seconds (numeric)", + "output_format": "CXC seconds (numeric)", + "input_type": "float, int, list, ndarray", + "output_type": "float, ndarray[float]", + } class TimeDate(TimeYearDayTime): @@ -301,18 +301,18 @@ class TimeDate(TimeYearDayTime): name = "date" # Documentation inputs for convert functions - convert_doc = dict( - input_name="date", - descr_short="Date (Year, day-of-year, time)", - input_format=""" + convert_doc = { + "input_name": "date", + "descr_short": "Date (Year, day-of-year, time)", + "input_format": """ - YYYY:DDD:HH:MM:SS.sss - YYYY:DDD:HH:MM:SS - YYYY:DDD:HH:MM - YYYY:DDD""", - output_format="YYYY:DDD:HH:MM:SS.sss", - input_type="str, bytes, float, list, ndarray", - output_type="str, ndarray[str]", - ) + "output_format": "YYYY:DDD:HH:MM:SS.sss", + "input_type": "str, bytes, float, list, ndarray", + "output_type": "str, ndarray[str]", + } def to_value(self, parent=None, **kwargs): if self.scale == "utc": @@ -330,10 +330,10 @@ def set_jds(self, val1, val2): class TimeFracYear(TimeDecimalYear): - """ - Time as a decimal year, with integer values corresponding to midnight - of the first day of each year. For example 2000.5 corresponds to the - ISO time '2000-07-02 00:00:00'. + """Time as a decimal year. + + Integer values corresponding to midnight of the first day of each year. For example + 2000.5 corresponds to the ISO time '2000-07-02 00:00:00'. Time value is always in UTC regardless of time object scale. """ @@ -350,12 +350,11 @@ def to_value(self, parent=None, **kwargs): class TimeGreta(TimeDate): - """ - Date as a string in format 'YYYYDDD.hhmmsssss', where sssss is number of - milliseconds. + """Date as string in format 'YYYYDDD.hhmmsssss'. - This can be input as a float, integer or string, but the output is always - string. + Here sssss is the number of millisec. + + This can be input as a float, integer or string, but the output is always string. Time value is always in UTC regardless of time object scale. """ @@ -363,14 +362,14 @@ class TimeGreta(TimeDate): name = "greta" # Documentation inputs for convert functions - convert_doc = dict( - input_name="date", - descr_short="GRETA date", - input_format="YYYYDDD.HHMMSSsss (str or float)", - output_format="YYYYDDD.HHMMSSsss (str)", - input_type="str, bytes, float, list, np.ndarray", - output_type="str, np.ndarray[str]", - ) + convert_doc = { + "input_name": "date", + "descr_short": "GRETA date", + "input_format": "YYYYDDD.HHMMSSsss (str or float)", + "output_format": "YYYYDDD.HHMMSSsss (str)", + "input_type": "str, bytes, float, list, np.ndarray", + "output_type": "str, np.ndarray[str]", + } subfmts = ( ("date_hms", "%Y%j%H%M%S", "{year:d}{yday:03d}{hour:02d}{min:02d}{sec:02d}"), @@ -391,14 +390,14 @@ class TimeGreta(TimeDate): # Before: yr mon doy hour minute second frac - fast_parser_pars = dict( - delims=(0, 0, 0, ord("."), 0, 0, 0), - starts=(0, -1, 4, 7, 10, 12, 14), - stops=(3, -1, 6, 9, 11, 13, -1), + fast_parser_pars = { + "delims": (0, 0, 0, ord("."), 0, 0, 0), + "starts": (0, -1, 4, 7, 10, 12, 14), + "stops": (3, -1, 6, 9, 11, 13, -1), # Break before: y m d h m s f - break_allowed=(0, 0, 0, 1, 0, 1, 1), - has_day_of_year=1, - ) + "break_allowed": (0, 0, 0, 1, 0, 1, 1), + "has_day_of_year": 1, + } def _check_val_type(self, val1, val2): if val2 is not None: @@ -427,9 +426,9 @@ def to_value(self, parent=None, **kwargs): class TimeMaude(TimeDate): - """ - Date as a 64-bit integer in format YYYYDDDHHMMSSsss, where sss is number of - milliseconds. + """Date as a 64-bit integer in format YYYYDDDHHMMSSsss. + + Here sss is number of milliseconds. This can be input as an integer or string, but the output is always integer. @@ -437,14 +436,14 @@ class TimeMaude(TimeDate): """ name = "maude" - convert_doc = dict( - input_name="date", - descr_short="MAUDE date", - input_format="YYYYDDDHHMMSSsss (str or int)", - output_format="YYYYDDD.HHMMSSsss (int)", - input_type="str, bytes, int, list, ndarray", - output_type="int, ndarray[int]", - ) + convert_doc = { + "input_name": "date", + "descr_short": "MAUDE date", + "input_format": "YYYYDDDHHMMSSsss (str or int)", + "output_format": "YYYYDDD.HHMMSSsss (int)", + "input_type": "str, bytes, int, list, ndarray", + "output_type": "int, ndarray[int]", + } subfmts = ( ("date_hms", "%Y%j%H%M%S", "{year:d}{yday:03d}{hour:02d}{min:02d}{sec:02d}"), @@ -464,15 +463,15 @@ class TimeMaude(TimeDate): # stops: position where component ends (-1 => continue to end of string) # Before: yr mon doy hour minute second frac - fast_parser_pars = dict( - use_fast_parser=True, - delims=(0, 0, 0, 0, 0, 0, 0), - starts=(0, -1, 4, 7, 9, 11, 13), - stops=(3, -1, 6, 8, 10, 12, -1), + fast_parser_pars = { + "use_fast_parser": True, + "delims": (0, 0, 0, 0, 0, 0, 0), + "starts": (0, -1, 4, 7, 9, 11, 13), + "stops": (3, -1, 6, 8, 10, 12, -1), # Break before: y m d h m s f, - break_allowed=(0, 0, 0, 1, 0, 1, 1), - has_day_of_year=1, - ) + "break_allowed": (0, 0, 0, 1, 0, 1, 1), + "has_day_of_year": 1, + } def _check_val_type(self, val1, val2): if val2 is not None: diff --git a/cxotime/tests/test_cxotime.py b/cxotime/tests/test_cxotime.py index 25eff58..e4e3860 100644 --- a/cxotime/tests/test_cxotime.py +++ b/cxotime/tests/test_cxotime.py @@ -118,7 +118,12 @@ def test_cxotime_vs_datetime(): np.array(["2000:001", "2015:181:23:59:60.500", "2015:180:01:02:03.456"]) ).secs dts = DateTime(secs) - vals = dict(date=dts.date, secs=dts.secs, greta=dts.greta, frac_year=dts.frac_year) + vals = { + "date": dts.date, + "secs": dts.secs, + "greta": dts.greta, + "frac_year": dts.frac_year, + } fmts = list(vals.keys()) for in_fmt in fmts: @@ -396,7 +401,7 @@ def test_print_time_conversions(date): ("iso", "2001-01-01 02:03:04.123", "U"), ] -test_fmts = set([fmt_name for fmt_name, val, fmt_kind in inputs]) +test_fmts = {fmt_name for fmt_name, val, fmt_kind in inputs} @pytest.mark.parametrize("fmt_val", inputs) diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index fe6a87f..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,27 +0,0 @@ -[tool.black] -include = '\.pyi?$' -exclude = ''' -/( - \.git - | \.mypy_cache - | \.tox - | \.venv - | \.vscode - | \.eggs - | _build - | buck-out - | build - | dist - | docs -)/ -''' - -[tool.isort] -profile = "black" - -[tool.flake8] -exclude = ''' -/( - docs -)/ -''' diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000..7fef666 --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,8 @@ +{ + "ignore": [ + "**/node_modules", + "**/__pycache__", + "**/*.ipynb", + ".git" + ] +} \ No newline at end of file diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..f724a90 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,58 @@ +# Copied originally from pandas +target-version = "py310" + +# fix = true +unfixable = [] + +select = [ + "I", # isort + "F", # pyflakes + "E", "W", # pycodestyle + "YTT", # flake8-2020 + "B", # flake8-bugbear + "Q", # flake8-quotes + "T10", # flake8-debugger + "INT", # flake8-gettext + "PLC", "PLE", "PLR", "PLW", # pylint + "PIE", # misc lints + "PYI", # flake8-pyi + "TID", # tidy imports + "ISC", # implicit string concatenation + "TCH", # type-checking imports + "C4", # comprehensions + "PGH" # pygrep-hooks +] + +# Some additional rules that are useful +extend-select = [ +"UP009", # UTF-8 encoding declaration is unnecessary +"SIM118", # Use `key in dict` instead of `key in dict.keys()` +"D205", # One blank line required between summary line and description +"ARG001", # Unused function argument +"RSE102", # Unnecessary parentheses on raised exception +"PERF401", # Use a list comprehension to create a transformed list +] + +ignore = [ + "ISC001", # Disable this for compatibility with ruff format + "B028", # No explicit `stacklevel` keyword argument found + "B905", # `zip()` without an explicit `strict=` parameter + "E731", # do not assign a lambda expression, use a def + "PLC1901", # compare-to-empty-string + "PLR0912", # Too many branches + "PLR2004", # Magic number +] + +extend-exclude = [ + "docs", +] + +[pycodestyle] +max-line-length = 100 # E501 reports lines that exceed the length of 100. + +[lint.extend-per-file-ignores] +"__init__.py" = ["E402", "F401", "F403"] +# For tests: +# - D205: Don't worry about test docstrings +# - ARG001: Unused function argument false positives for some fixtures +"**/tests/test_*.py" = ["D205", "ARG001"]