Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DEV: Drop black and flake8 and use ruff instead #371

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ It's recommended that whilst working through the process reference be made to ex
- [ ] Name subclass `{Exchange MIC}ExchangeCalendar`.
- [ ] Override methods and properties as required, being guided by ExchangeCalendar documentation and in-code comments. All abstract properties must be overriden.
- [ ] Include references / links for holidays and timings (either to class documentation or as comments).
- [ ] Import calendar class to `exchange_calendars/calendar_utils.py` and add class to `_default_calendar_factories`.
- [ ] Import calendar class to `exchange_calendars/calendar_utils.py` and add class to `_default_calendar_factories`.
- [ ] Add calendar test module `tests/test_{Exchange MIC}_calendar.py` Module should contain a subclass of the test base class `ExchangeCalendarTestBase` (in `tests/test_exchange_calendars.py`).
- [ ] Name subclass `Test{Exchange MIC}Calendar`.
- [ ] Override fixtures as required, being guided by ExchangeCalendarTestBase documentation and in-code comments. The `calendar_cls` and `max_session_hours` fixtures must be overriden.
Expand All @@ -16,7 +16,7 @@ It's recommended that whilst working through the process reference be made to ex

**Workflow to modify an existing Exchange Calendar**

- [ ] Modify calendar class as required.
- [ ] Modify calendar class as required.
- [ ] Modify the test resources file (e.g `tests/resources/{Exchange MIC}.csv`), either manually or by executing `python etc/make_exchange_calendar_test_csv.py {Exchange MIC}`.
- [ ] Check if any of the fixtures in `tests/test_{Exchange MIC}_calendar.py` need updating to reflect your changes.
- [ ] Add references to any new/modified holidays in `exchange_calendars/exchange_calendar_{Exchange MIC}.py`.
10 changes: 7 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ jobs:
python -m pip install --upgrade pip
pip install -r etc/${{matrix.requirements_file}}
pip install -e .
- name: Lint with flake8
run: |
flake8
herebebeasties marked this conversation as resolved.
Show resolved Hide resolved
- name: Test with pytest
run: |
pytest tests -n auto --dist loadscope

pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: pre-commit/action@v3.0.0
herebebeasties marked this conversation as resolved.
Show resolved Hide resolved
15 changes: 6 additions & 9 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.2.0
rev: v4.5.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pycqa/flake8
rev: 6.1.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.2.1
herebebeasties marked this conversation as resolved.
Show resolved Hide resolved
hooks:
- id: flake8
- repo: https://github.com/psf/black
rev: 23.7.0
hooks:
- id: black
language_version: python
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry why do we need --exit-non-zero-on-fix?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise it'll exit status code zero, which means pre-commit will merrily let you land the changes with outstanding linting issues. This way the commit fails, you review & add the auto-fixes that ruff has made, and you try the commit again.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merrily let you land the changes with outstanding linting issues.

But in this case the changes are fixed, no?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out that pre-commit fails if a hook changes any of the files that the user has staged (without then doing a git add) so I agree that this looks superfluous. I'll remove it.

Copy link
Author

@herebebeasties herebebeasties Feb 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm. I say that, but...

We're running this in two ways:

  • In the local developer's git repo, only if they have pre-commit set up.
  • In GitHub Actions, which enforces linting regardless of a dev's local setup.

So we do need this to exit non-zero really, for GitHub Actions to fail the build if there are linting fixes. We don't want them to silently get applied within the CI env but never committed back and for it to pretend it's all fine.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the easiest thing to do here would be to:

  1. Keep the precommit with just args: [--fix]
  2. Instead of running the precommit GHA, run ruff directly via any of https://docs.astral.sh/ruff/integrations/#github-actions

- id: ruff-format
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# exchange_calendars

[![PyPI](https://img.shields.io/pypi/v/exchange-calendars)](https://pypi.org/project/exchange-calendars/) ![Python Support](https://img.shields.io/pypi/pyversions/exchange_calendars) ![PyPI Downloads](https://img.shields.io/pypi/dd/exchange-calendars) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![PyPI](https://img.shields.io/pypi/v/exchange-calendars)](https://pypi.org/project/exchange-calendars/) ![Python Support](https://img.shields.io/pypi/pyversions/exchange_calendars) ![PyPI Downloads](https://img.shields.io/pypi/dd/exchange-calendars) [![Code style: black](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)

A Python library for defining and querying calendars for security exchanges.

Expand Down
15 changes: 7 additions & 8 deletions etc/lunisolar
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ import novas.compat as novas
import numpy as np
import pandas as pd
import toolz
from novas.compat import eph_manager

from exchange_calendars.calendar_helpers import UTC
from novas.compat import eph_manager


@contextlib.contextmanager
Expand Down Expand Up @@ -878,11 +877,11 @@ def lunar_ecliptic_longitude(root_dir, verbose, start, stop):

with ephemeris():
minutes = np.arange(start, stop, dtype="M8[m]")
for minutes in toolz.partition_all(60 * 24 * 365, minutes):
for mins_in_year in toolz.partition_all(60 * 24 * 365, minutes):
if verbose:
print(f"start={minutes[0]}; stop={minutes[-1]}")
print(f"start={mins_in_year[0]}; stop={mins_in_year[-1]}")

lunar_ecliptic_longitude_block(root_dir, np.array(minutes))
lunar_ecliptic_longitude_block(root_dir, np.array(mins_in_year))


def calculate_new_moon(solar_ecliptic_longitude, lunar_ecliptic_longitude, minutes):
Expand Down Expand Up @@ -1025,11 +1024,11 @@ def solar_solar_ecliptic_longitude(root_dir, verbose, start, stop):

with ephemeris():
minutes = np.arange(start, stop, dtype="M8[m]")
for minutes in toolz.partition_all(60 * 24 * 365, minutes):
for mins_in_year in toolz.partition_all(60 * 24 * 365, minutes):
if verbose:
print(f"start={minutes[0]}; stop={minutes[-1]}")
print(f"start={mins_in_year[0]}; stop={mins_in_year[-1]}")

solar_ecliptic_longitude_block(root_dir, np.array(minutes))
solar_ecliptic_longitude_block(root_dir, np.array(mins_in_year))


def minutes_at_phase(longitude, minutes, phase):
Expand Down
8 changes: 5 additions & 3 deletions exchange_calendars/exchange_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
WEEKDAYS = (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY)
WEEKENDS = (SATURDAY, SUNDAY)

ONE_MINUTE = pd.Timedelta(1, "min")


def selection(
arr: pd.DatetimeIndex, start: pd.Timestamp, end: pd.Timestamp
Expand Down Expand Up @@ -119,7 +121,7 @@ def __init__(
def __call__(self, f: Callable) -> Callable:
@functools.wraps(f)
def wrapped_f(*args, **kwargs):
warnings.warn(self._message(f), FutureWarning)
warnings.warn(self._message(f), FutureWarning, stacklevel=2)
return f(*args, **kwargs)

return wrapped_f
Expand Down Expand Up @@ -2010,7 +2012,7 @@ def minute_offset_by_sessions(
return last_minute
elif self.is_break_minute(minute, _parse=False):
return self.session_last_am_minute(target_session, _parse=False)
assert False, "offset minute should have resolved!"
raise AssertionError("offset minute should have resolved!")

# Methods that evaluate or interrogate a range of minutes.

Expand Down Expand Up @@ -2344,7 +2346,7 @@ def trading_index(
force: bool | None = None,
curtail_overlaps: bool = False,
ignore_breaks: bool = False,
align: pd.Timedelta | str = pd.Timedelta(1, "min"),
align: pd.Timedelta | str = ONE_MINUTE,
herebebeasties marked this conversation as resolved.
Show resolved Hide resolved
align_pm: pd.Timedelta | bool = True,
parse: bool = True,
) -> pd.DatetimeIndex | pd.IntervalIndex:
Expand Down
2 changes: 1 addition & 1 deletion exchange_calendars/exchange_calendar_xbom.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@
"2024-10-02",
"2024-11-01",
"2024-11-15",
"2024-12-25"
"2024-12-25",
]
)

Expand Down
4 changes: 3 additions & 1 deletion exchange_calendars/exchange_calendar_xhkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,9 @@ def process_queen_birthday(dt):
# on the Monday (2022-09-12). In the past they don't seem to have followed
# this pattern. We'll have to wait and see before we generalise this into a rule.
pd.Timestamp("2022-09-12"),
pd.Timestamp("2023-07-17"), # 8号台风泰利, 全天休市 https://www.hkex.com.hk/News/Market-Communications/2023/2307172news?sc_lang=en
pd.Timestamp(
"2023-07-17"
), # 8号台风泰利, 全天休市 https://www.hkex.com.hk/News/Market-Communications/2023/2307172news?sc_lang=en
]


Expand Down
3 changes: 1 addition & 2 deletions exchange_calendars/exchange_calendar_xidx.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@ def regular_holidays(self):
chinese_new_year = chinese_lunar_new_year_dates[
# The Indonesia Stock Exchange did not close for Chinese New
# Year in 1998, 1999, or 2001. (It fell on a Saturday in 2000.)
chinese_lunar_new_year_dates.year
>= 2002
chinese_lunar_new_year_dates.year >= 2002
]

common_leave = pd.to_datetime(
Expand Down
2 changes: 1 addition & 1 deletion exchange_calendars/pandas_extensions/holiday.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def _apply_observance(self, dates):
offset = observance

def f(d):
return d + offset
return d + offset # noqa:B023

observance = f
dates = dates.map(observance)
Expand Down
2 changes: 0 additions & 2 deletions exchange_calendars/pandas_extensions/offsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@


class CompositeCustomBusinessDay(CustomBusinessDay):

_prefix = "C"
_attributes = tuple(
[
Expand Down Expand Up @@ -245,7 +244,6 @@ def _get_calendar(weekmask, holidays, calendar):


class MultipleWeekmaskCustomBusinessDay(CompositeCustomBusinessDay):

_prefix = "C"
_attributes = tuple(
[
Expand Down
10 changes: 5 additions & 5 deletions exchange_calendars/tase_holidays.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,35 +360,35 @@ def holiday(self):
month=1,
day=1,
offset=[_Sukkoth(), Day(1)],
days_of_week=(0, 1, 2, 3, 6)
days_of_week=(0, 1, 2, 3, 6),
)
SukkothInterimDay2 = Holiday(
"Sukkoth Interim Day",
month=1,
day=1,
offset=[_Sukkoth(), Day(2)],
days_of_week=(0, 1, 2, 3, 6)
days_of_week=(0, 1, 2, 3, 6),
)
SukkothInterimDay3 = Holiday(
"Sukkoth Interim Day",
month=1,
day=1,
offset=[_Sukkoth(), Day(3)],
days_of_week=(0, 1, 2, 3, 6)
days_of_week=(0, 1, 2, 3, 6),
)
SukkothInterimDay4 = Holiday(
"Sukkoth Interim Day",
month=1,
day=1,
offset=[_Sukkoth(), Day(4)],
days_of_week=(0, 1, 2, 3, 6)
days_of_week=(0, 1, 2, 3, 6),
)
SukkothInterimDay5 = Holiday(
"Sukkoth Interim Day",
month=1,
day=1,
offset=[_Sukkoth(), Day(5)],
days_of_week=(0, 1, 2, 3, 6)
days_of_week=(0, 1, 2, 3, 6),
)

# Passover interim days are the days between beginning and end of passover. Any otherwise regular business day in that
Expand Down
2 changes: 1 addition & 1 deletion exchange_calendars/us_holidays.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def following_tuesday_every_four_years_observance(dt):
month=6,
day=19,
start_date=Timestamp("2022-01-01"),
observance=nearest_workday
observance=nearest_workday,
)
USIndependenceDayBefore1954 = Holiday(
"July 4th",
Expand Down
2 changes: 1 addition & 1 deletion exchange_calendars/xkls_holidays.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@
"2021-05-26",
"2022-05-15",
"2023-05-04",
"2024-05-22"
"2024-05-22",
]
)

Expand Down
13 changes: 10 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ dynamic = ["version"]

[project.optional-dependencies]
dev = [
"flake8",
"hypothesis",
"pytest",
"pytest-benchmark",
"pytest-xdist",
"pip-tools",
"ruff",
]

[project.scripts]
Expand All @@ -61,6 +61,13 @@ include = ["exchange_calendars", "exchange_calendars.*"]
[tool.setuptools_scm]
write_to = "exchange_calendars/_version.py"

[tool.black]
[tool.ruff]
target-version = "py39"
line-length = 88
target-version = ['py39', 'py310', 'py311']
src = ["etc", "exchange_calendars", "tests"]

[tool.ruff.lint]
# TODO - decide if we want sorted imports - "I" if so
select = ["E", "F", "B"]
fixable = ["ALL"]
ignore = ["E501", "B019"]
herebebeasties marked this conversation as resolved.
Show resolved Hide resolved
7 changes: 5 additions & 2 deletions tests/test_calendar_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@

# TODO tests for next_divider_idx, previous_divider_idx, compute_minutes (#15)

ONE_MINUTE = pd.Timedelta(1, "min")
ONE_DAY = pd.Timedelta(1, "D")


def test_constants():
# Just to make sure they aren't inadvertently changed
Expand Down Expand Up @@ -502,8 +505,8 @@ def st_start_end(
@st.composite
def st_periods(
draw,
minimum: pd.Timedelta = pd.Timedelta(1, "min"),
maximum: pd.Timedelta = pd.Timedelta(1, "D") - pd.Timedelta(1, "min"),
minimum: pd.Timedelta = ONE_MINUTE,
maximum: pd.Timedelta = ONE_DAY - ONE_MINUTE,
) -> st.SearchStrategy[pd.Timedelta]:
"""SearchStrategy for a period between a `minimum` and `maximum`."""
period = draw(st.integers(minimum.seconds // 60, maximum.seconds // 60))
Expand Down
23 changes: 14 additions & 9 deletions tests/test_exchange_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -1869,7 +1869,7 @@ def early_closes_sample_time(self) -> abc.Iterator[pd.Timedelta | None]:
yield None

@pytest.fixture
def early_closes_weekdays(self) -> abc.Iterator[tuple(int)]:
def early_closes_weekdays(self) -> abc.Iterator[tuple[int]]:
"""Weekdays with non-standard close times.

`test_early_closes_weekdays` will check that all sessions on these
Expand Down Expand Up @@ -2842,9 +2842,7 @@ def test_date_to_session(self, default_calendar_with_answers):

# direction as "next"
last_session = None
for date, is_session in zip(
dates.sort_values(ascending=False), date_is_session[::-1]
):
for date, _ in zip(dates.sort_values(ascending=False), date_is_session[::-1]):
session_label = f(date, "next")
if date in sessions:
assert session_label == date
Expand Down Expand Up @@ -3313,9 +3311,9 @@ def test_minute_to_trading_minute(self, all_calendars_with_answers, all_directio
for minutes, session in ans.break_minutes[:1]:
for minute in minutes:
if direction == "previous":
f(minute, direction) == ans.last_am_minutes[session]
assert f(minute, direction) == ans.last_am_minutes[session]
elif direction == "next":
f(minute, direction) == ans.first_pm_minutes[session]
assert f(minute, direction) == ans.first_pm_minutes[session]
else:
error_msg = (
f"`minute` '{minute}' is not a trading minute. Consider passing"
Expand Down Expand Up @@ -3887,9 +3885,14 @@ def unite(dtis: list[pd.DatetimeIndex]) -> pd.DatetimeIndex:
break

def get_index(closed: str, intervals: bool):
start, end = sessions[0], sessions[-1]
start, end = sessions[0], sessions[-1] # noqa: B023
herebebeasties marked this conversation as resolved.
Show resolved Hide resolved
return cal.trading_index(
start, end, period, intervals, closed, parse=False
start,
end,
period, # noqa: B023
intervals,
closed,
parse=False, # noqa: B023
)

def tst_indices_index(
Expand All @@ -3911,7 +3914,9 @@ def tst_intervals_index(closed: str, overlaps: bool):
if not overlaps:
rtrn = get_index(closed, True)
expected = pd.IntervalIndex.from_arrays(
left_index, right_index, closed
left_index, # noqa: B023
right_index, # noqa: B023
closed, # noqa: B023
)
pd.testing.assert_index_equal(expected, rtrn)
else:
Expand Down
4 changes: 2 additions & 2 deletions tests/test_xfra_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def regular_holidays_sample(self):
# Whit Monday
"2015-05-25", # regularly observed from 2015
"2016-05-16",
"2021-05-24"
"2021-05-24",
]

@pytest.fixture
Expand All @@ -58,7 +58,7 @@ def non_holidays_sample(self):
"2016-10-31",
"2018-10-31",
# German Unity Day was not observed in 2022
"2022-10-03"
"2022-10-03",
]

@pytest.fixture
Expand Down
Loading
Loading