From 50b58a990c4a20b284181438920432ce122530b0 Mon Sep 17 00:00:00 2001 From: theOehrly <23384863+theOehrly@users.noreply.github.com> Date: Fri, 15 Dec 2023 23:55:04 +0100 Subject: [PATCH] MNT: switch to pyproject.toml, flake8 -> ruff --- .github/workflows/release.yml | 6 +- .github/workflows/tests.yml | 8 +- .pre-commit-config.yaml | 14 +- pyproject.toml | 55 ++++++++ requirements-dev.txt | 4 +- scripts/flake8_line_length.py | 18 --- scripts/visualization.py | 234 ---------------------------------- setup.cfg | 49 ------- setup.py | 15 --- 9 files changed, 68 insertions(+), 335 deletions(-) create mode 100644 pyproject.toml delete mode 100644 scripts/flake8_line_length.py delete mode 100644 scripts/visualization.py delete mode 100644 setup.cfg delete mode 100644 setup.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 439f5edc0..81bfbd96b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install setuptools wheel twine + pip install --upgrade build twine # if this is a release, upload to PyPI - name: Build and publish release @@ -26,7 +26,7 @@ jobs: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} run: | - python setup.py sdist bdist_wheel + python -m build twine upload dist/* # if this is a manual dispatch, upload to TestPyPI @@ -36,5 +36,5 @@ jobs: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.TEST_PYPI_TOKEN}} run: | - python setup.py sdist bdist_wheel + python -m build twine upload --repository testpypi dist/* \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a321f1be1..b357ed725 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -59,7 +59,7 @@ jobs: run-lint-checks: runs-on: ubuntu-latest - name: Flake8 lint checks + name: Linting Ruff steps: - name: Setup python uses: actions/setup-python@v4 @@ -91,10 +91,8 @@ jobs: run: | mkdir test_cache # make sure cache dir exists git fetch origin --quiet - # flake8 with default config - flake8 fastf1 examples scripts - # flake8 check new shorter line length only on diff - git diff origin/master -U0 --relative | flake8 --max-line-length 79 --diff --select E501 fastf1 examples scripts + # ruff with default config + ruff check . - name: Run tests (master push) if: ${{ github.ref == 'refs/heads/master' && env.GITHUB_EVENT_NAME == 'push'}} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fa2ea7052..f8f7dd1d8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,7 @@ repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.0.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.1.8 hooks: - - id: flake8 - - repo: local - hooks: - - id: flake8-line-length - name: Flake8 line length - entry: python ./scripts/flake8_line_length.py - language: python + # Run the linter. + - id: ruff diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..797eea4ad --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,55 @@ +[project] +dynamic = ["version"] + +name = "fastf1" +authors = [ + {email = "oehrly@mailbox.org"}, + {name = "Philipp Schaefer"} +] +description = "Python package for accessing and analyzing Formula 1 results, schedules, timing data and telemetry." +readme = "README.md" + +license = { file = "LICENSE" } + +requires-python = ">=3.8" +dependencies = [ + "matplotlib>=3.4.2,<4.0.0", + "numpy>=1.20.3,<2.0.0", + "pandas>=1.2.4,<2.1.0", + "python-dateutil", + "requests>=2.28.0", + "requests-cache>=0.8.0", + "scipy>=1.6.3,<2.0.0", + "thefuzz", + "timple>=0.1.6", + "websockets>=8.1", +] + +[project.urls] +"Source Code" = "https://github.com/theOehrly/Fast-F1" +"Documentation" = "https://docs.fastf1.dev" + +[build-system] +requires = ["setuptools", "setuptools-scm"] +build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] + +[tool.ruff] +line-length = 79 +select = [ +# "D", TODO: select and fix docstrings + "E", + "F", + "W", +] +exclude = [ + "scripts", + "fastf1/tests", + "fastf1/testing", + "fastf1/signalr_aio", + "fastf1/legacy.py", +] + +[tool.ruff.per-file-ignores] +"fastf1/_api.py" = ["E501"] diff --git a/requirements-dev.txt b/requirements-dev.txt index 0cd0eeb82..90caf806c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,7 +7,6 @@ sphinx-gallery>=0.10.0 autodocsumm>=0.2.10 # support for sphinx v6 sphinx-autodoc-typehints wheel -flake8<5.0.0 readme-renderer[md] xdoctest pre-commit @@ -16,4 +15,5 @@ websockets>=10.3 seaborn<0.13.0 plotly seaborn -kaleido \ No newline at end of file +kaleido +ruff \ No newline at end of file diff --git a/scripts/flake8_line_length.py b/scripts/flake8_line_length.py deleted file mode 100644 index c8d19b4c7..000000000 --- a/scripts/flake8_line_length.py +++ /dev/null @@ -1,18 +0,0 @@ -"""This script will run flake8 but only check for then new line length limit of -79 characters. The checks are limited to the currently staged changes.""" -import os -import subprocess -import sys - -print(os.getcwd()) - - -p_diff = subprocess.Popen(["git", "diff", "--cached", "-U0", "--relative"], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - -p_flake8 = subprocess.Popen(["flake8", "--max-line-length", "79", - "--select", "E501", "--diff", - "fastf1 examples scripts"], - stdin=p_diff.stdout) -p_flake8.wait() -sys.exit(p_flake8.returncode) diff --git a/scripts/visualization.py b/scripts/visualization.py deleted file mode 100644 index ecfdf5ff3..000000000 --- a/scripts/visualization.py +++ /dev/null @@ -1,234 +0,0 @@ -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1 import make_axes_locatable -import numpy as np -import pandas as pd -import os -import fastf1 as ff1 - - -def show_deviation_minima_on_track(res, track, sector_number): - mad_x_stats = res['mad_x{}'.format(sector_number)] - mad_y_stats = res['mad_y{}'.format(sector_number)] - mean_x_stats = res['mean_x{}'.format(sector_number)] - mean_y_stats = res['mean_y{}'.format(sector_number)] - - mean_x_stats = np.array(mean_x_stats) - mean_y_stats = np.array(mean_y_stats) - mad_x_stats = np.array(mad_x_stats) - mad_y_stats = np.array(mad_y_stats) - - x_minima = np.r_[True, mad_x_stats[1:] < mad_x_stats[:-1]] & np.r_[mad_x_stats[:-1] < mad_x_stats[1:], True] - y_minima = np.r_[True, mad_y_stats[1:] < mad_y_stats[:-1]] & np.r_[mad_y_stats[:-1] < mad_y_stats[1:], True] - - ax_main = plt.subplot(label='Track Map') - plt.plot(track.sorted_x, track.sorted_y) - ax_main.set_aspect('equal') - ax_main.set_xlabel('X') - ax_main.set_ylabel('Y') - ax_main.yaxis.set_tick_params(labelleft=False, labelright=True) - ax_main.yaxis.set_label_position("right") - - # x deviation minima - for x_min in mean_x_stats[x_minima]: - ax_main.axvline(x_min, color='r') - - # y deviation minima - for y_min in mean_y_stats[y_minima]: - ax_main.axhline(y_min, color='r') - - divider = make_axes_locatable(ax_main) - ax_mad_x = divider.append_axes("top", 1.2, pad=0.1, sharex=ax_main) - ax_mad_y = divider.append_axes("left", 1.2, pad=0.1, sharey=ax_main) - - ax_mad_x.plot(mean_x_stats, mad_x_stats) - ax_mad_x.grid(which='both') - ax_mad_x.set_ylabel('Y MAD {}'.format(sector_number)) - ax_mad_x.xaxis.set_tick_params(labelbottom=False) - - ax_mad_y.plot(mad_y_stats, mean_y_stats) - ax_mad_y.grid(which='both') - ax_mad_y.invert_xaxis() - ax_mad_y.set_xlabel('X MAD {}'.format(sector_number)) - ax_mad_y.yaxis.set_tick_params(labelleft=False) - - -def plot_lap_time_integrity(laps_data, suffix=''): - drivers_series = laps_data.DriverResult - drivers = list(drivers_series.drop_duplicates()) - - deltas = list() - ref = list() # scatter plots need an x and y value therefore ref is created by counting up - n = 0 - - if 'Date' in laps_data.columns: - for driver in drivers: - i_max = len(laps_data[laps_data.DriverResult == driver]) - for i in range(1, i_max): - delta = (laps_data.iloc[i].Date - laps_data.iloc[i].LapTime - laps_data.iloc[i-1].Date).total_seconds() - deltas.append(delta) - ref.append(n) - n += 1 - else: - for driver in drivers: - i_max = len(laps_data[laps_data.DriverResult == driver]) - for i in range(1, i_max): - delta = (laps_data.iloc[i].Time - laps_data.iloc[i].LapTime - laps_data.iloc[i-1].Time).total_seconds() - deltas.append(delta) - ref.append(n) - n += 1 - - fig1 = plt.figure() - fig1.suptitle("Lap Time Scatter {}".format(suffix)) - plt.scatter(ref, deltas) - - fig2 = plt.figure() - fig2.suptitle("Lap Time Histogram {}".format(suffix)) - plt.hist(deltas, bins=50) - - -def plot_lap_position_integrity(laps_data, pos_data, suffix=''): - vals = dict() - - for _, lap in laps_data.pick_wo_box().pick_track_status('1').iterlaps(require=['LapStartDate', 'DriverNumber']): - if not lap['IsAccurate']: - continue - if lap['LapStartDate'] not in pos_data[lap['DriverNumber']]['Date']: - tmp_add = pd.DataFrame({}, index=[lap['LapStartDate'], ]) - tmp_df = pos_data[lap['DriverNumber']].set_index('Date').append(tmp_add).sort_index() - tmp_df.loc[:, ('X', 'Y')] = tmp_df.loc[:, ('X', 'Y')].interpolate(method='quadratic') - - row = tmp_df[tmp_df.index == lap['LapStartDate']].iloc[0] - - else: - row = pos_data[lap['DriverNumber']][pos_data[lap['DriverNumber']]['Date'] == lap['LapStartDate']].iloc[0] - - if lap['DriverNumber'] not in vals.keys(): - vals[lap['DriverNumber']] = {'x': list(), 'y': list(), 't': list()} - - vals[lap['DriverNumber']]['x'].append(row['X']) - vals[lap['DriverNumber']]['y'].append(row['Y']) - vals[lap['DriverNumber']]['t'].append(lap['Time'].total_seconds()) - - x_vals, y_vals = list(), list() - for drv in vals.keys(): - x_vals.extend(vals[drv]['x']) - y_vals.extend(vals[drv]['y']) - - y_mean_tot = np.mean(y_vals) - - fig0y = plt.figure() - fig0y.suptitle("Time Scatter {}".format(suffix)) - for drv in vals.keys(): - plt.plot(vals[drv]['t'], vals[drv]['y'], label=drv) - plt.legend() - - fig0cy = plt.figure() - fig0cy.suptitle("Time Scatter Mean correction {}".format(suffix)) - for drv in vals.keys(): - plt.plot(vals[drv]['t'], np.array(vals[drv]['y']) + (y_mean_tot - np.mean(vals[drv]['y'])), label=drv) - plt.legend() - - fig1 = plt.figure() - fig1.suptitle("Position Scatter {}".format(suffix)) - for drv in vals.keys(): - plt.scatter(vals[drv]['x'], vals[drv]['y'], label=drv) - plt.axis('equal') - plt.legend() - - fig2 = plt.figure() - fig2 .suptitle("Position X Histogram {}".format(suffix)) - plt.hist(x_vals, bins=30) - - fig3 = plt.figure() - fig3.suptitle("Position Y Histogram {}".format(suffix)) - plt.hist(y_vals, bins=30) - - -def all_sectors_result_plots(result, track, suffix, workdir=None): - r = result - - # mean absolute deviation x - plt.figure(figsize=(15, 8)) - plt.suptitle('MAD X | {}'.format(suffix)) - plt.plot(r['mad_x1'], label="mad_x1") - plt.plot(r['mad_x2'], label="mad_x2") - plt.plot(r['mad_x3'], label="mad_x3") - plt.legend() - plt.grid(which='both') - if workdir: - plt.savefig(os.path.join(workdir, 'mad x {}.png'.format(suffix)), dpi=300) - - # mean absolute deviation y - plt.figure(figsize=(15, 8)) - plt.suptitle('MAD Y | {}'.format(suffix)) - plt.plot(r['mad_y1'], label="mad_y1") - plt.plot(r['mad_y2'], label="mad_y2") - plt.plot(r['mad_y3'], label="mad_y3") - plt.legend() - plt.grid(which='both') - if workdir: - plt.savefig(os.path.join(workdir, 'mad y {}.png'.format(suffix)), dpi=300) - - # track + mad plot one for each sector - plt.figure(figsize=(15, 8)) - plt.suptitle('Track + MAD 1 | {}'.format(suffix)) - show_deviation_minima_on_track(r, track, 1) - if workdir: - plt.savefig(os.path.join(workdir, 'track plus mad 1 {}.png'.format(suffix)), dpi=300) - - plt.figure(figsize=(15, 8)) - plt.suptitle('Track + MAD 2 | {}'.format(suffix)) - show_deviation_minima_on_track(r, track, 2) - if workdir: - plt.savefig(os.path.join(workdir, 'track plus mad 2 {}.png'.format(suffix)), dpi=300) - - plt.figure(figsize=(15, 8)) - plt.suptitle('Track + MAD 3 | {}'.format(suffix)) - show_deviation_minima_on_track(r, track, 3) - if workdir: - plt.savefig(os.path.join(workdir, 'track plus mad 3 {}.png'.format(suffix)), dpi=300) - - # delta between test value and mean result - dx = list() - for test, tres in zip(r['tx'], r['mean_x1']): - dx.append(test-tres) - - dy = list() - for test, tres in zip(r['ty'], r['mean_y1']): - dy.append(test-tres) - - plt.figure(figsize=(15, 8)) - plt.suptitle('Difference In - Out Coordinate | {}'.format(suffix)) - plt.plot(dx, label='dx') - plt.plot(dy, label='dy') - plt.legend() - plt.grid(which='both') - if workdir: - plt.savefig(os.path.join(workdir, 'coord diff {}.png'.format(suffix)), dpi=300) - - plt.show() - - -def delta_time_validation(ref_lap, comp_lap): - from fastf1 import plotting - plotting.setup_mpl() - plt.figure() - - dt, ref_tel, compare_tel = ff1.utils.delta_time(ref_lap, comp_lap) - - sec12 = ref_tel['Distance'].iloc[np.argmin(abs(ref_tel['SessionTime'] - ref_lap['Sector1SessionTime']))] - sec23 = ref_tel['Distance'].iloc[np.argmin(abs(ref_tel['SessionTime'] - ref_lap['Sector2SessionTime']))] - plt.vlines(sec12, min(dt), max(dt), colors='yellow') - plt.vlines(sec23, min(dt), max(dt), colors='yellow') - plt.vlines(1.0, min(dt), max(dt), colors='yellow') - - plt.plot(ref_tel['Distance'], dt, label='Gap') - - dts1 = comp_lap['Sector1Time'] - ref_lap['Sector1Time'] - dts2 = (comp_lap['Sector1Time'] + comp_lap['Sector2Time']) - (ref_lap['Sector1Time'] + ref_lap['Sector2Time']) - dtsL = comp_lap['LapTime'] - ref_lap['LapTime'] - plt.hlines(dts1.total_seconds(), 0, max(ref_tel['Distance']), label='Sec1', colors='blue') - plt.hlines(dts2.total_seconds(), 0, max(ref_tel['Distance']), label='Sec2', colors='pink') - plt.hlines(dtsL.total_seconds(), 0, max(ref_tel['Distance']), label='Sec3', colors='grey') - - plt.legend() diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 47ff695a4..000000000 --- a/setup.cfg +++ /dev/null @@ -1,49 +0,0 @@ -[metadata] -name = fastf1 -license = MIT -license_files = LICENSE -author = Oehrly -author_email = oehrly@mailbox.org -home_page = https://github.com/theOehrly/Fast-F1 -description = Wrapper library for F1 data and telemetry API with additional data processing capabilities. -long_description = file: README.md -long_description_content_type = text/markdown - - -[options] -zip_safe = False -packages = - fastf1 - fastf1.ergast - fastf1.internals - fastf1.livetiming - fastf1.mvapi - fastf1.signalr_aio - fastf1.signalr_aio.events - fastf1.signalr_aio.hubs - fastf1.signalr_aio.transports - -python_requires = >= 3.8 -install_requires = - requests-cache>=0.8.0 - pandas>=1.2.4,<2.1.0 - numpy>=1.20.3,<2.0.0 - scipy>=1.6.3,<2.0.0 - thefuzz - matplotlib>=3.4.2,<4.0.0 - python-dateutil - timple>=0.1.6 - requests>=2.28.0 - websockets>=8.1 - - -[build_sphinx] -project = Fast F1 -source-dir = ./docs -build-dir = ./docs/_build - -[flake8] -max-line-length = 120 -extend-ignore = W503 -extend-select = W504 -exclude = fastf1/signalr_aio diff --git a/setup.py b/setup.py deleted file mode 100644 index a0a0edeff..000000000 --- a/setup.py +++ /dev/null @@ -1,15 +0,0 @@ -from setuptools import setup - - -if __name__ == '__main__': - import sys - import re - if sys.version_info < (3, 8): - sys.exit('Sorry, Python < 3.8 is not supported,' - + ' please update to install.') - else: - # load version number from file in sources dir without importing - with open('fastf1/version.py') as vfobj: - vstring = str(vfobj.read()) - version = re.search(r"(\d+.\d+.\d+[-\w]*)", vstring)[0] - setup(version=version)