Skip to content

Commit

Permalink
support Python 3.9 (#17)
Browse files Browse the repository at this point in the history
* try Python 3.9 in workflow

* Update lint-and-test.yaml

* try from __future__ import annotations

* Update conftest.py

* add eval-type-backport

* more from __future__ import annotations

* remove TypeAlias

* replace zip with strict_zip

* ignore 3.9 mypy issues

* fix bug in backporting.py

* Update lint-and-test.yaml

* Update lint-and-test.yaml
  • Loading branch information
aclerc authored Sep 6, 2024
1 parent 79b6493 commit d96aea9
Show file tree
Hide file tree
Showing 33 changed files with 295 additions and 93 deletions.
77 changes: 38 additions & 39 deletions .github/workflows/lint-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,49 @@ name: Lint and test

on:
push:
branches: [ main ]
branches:
- main
pull_request:

env:
python-version: "3.10"
branches:
- main

permissions:
contents: read

jobs:
lint-and-test:
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]
os: [ubuntu-latest]
steps:
- name: "checkout repository"
uses: actions/checkout@v4

- uses: actions/setup-python@v5
name: 'set up Python ${{ inputs.python-version }}'
with:
python-version: ${{ env.python-version }}

- uses: actions/cache@v4
id: cache-venv
with:
path: ./.venv/
key: ${{ runner.os }}-venv-${{ hashFiles('**/dev-requirements.txt') }}
restore-keys: |
${{ runner.os }}-venv-
- name: 'create virtualenv and install dependencies'
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
python -m venv .venv
source .venv/bin/activate
pip install .[dev]
- name: 'update dependencies'
if: steps.cache-venv.outputs.cache-hit == 'true'
run: |
source .venv/bin/activate
pip install -U .[dev]
- name: "lint check & test"
run: |
source .venv/bin/activate
poe lint-check
poe test
- uses: actions/checkout@v4
- name: "set up Python ${{ matrix.python-version }}"
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- uses: actions/cache@v4
id: cache-venv
with:
path: ./.venv/
key: ${{ matrix.python-version }}-venv-${{ hashFiles('**/pyproject.toml') }}
restore-keys: |
${{ matrix.python-version }}-venv
- name: 'create virtualenv and install dependencies'
if: steps.cache-venv.outputs.cache-hit != 'true'
run: |
python -m venv .venv
source .venv/bin/activate
pip install .[dev]
- name: 'update dependencies'
if: steps.cache-venv.outputs.cache-hit == 'true'
run: |
source .venv/bin/activate
pip install -U .[dev]
- name: "lint check & test"
run: |
source .venv/bin/activate
poe lint-check
poe test
6 changes: 5 additions & 1 deletion examples/helpers.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from __future__ import annotations

import logging
import math
from collections.abc import Collection
from pathlib import Path
from typing import TYPE_CHECKING

import requests

if TYPE_CHECKING:
from collections.abc import Collection
logger = logging.getLogger(__name__)

BYTES_IN_MB = 1024 * 1024
Expand Down
7 changes: 3 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ authors = [
]
description = "A tool to assess yield uplift of wind turbines"
readme = "README.md"
requires-python = ">=3.10"
requires-python = ">=3.9"
license = { file = "LICENSE.txt" }
classifiers = [
"Development Status :: 4 - Beta",
Expand All @@ -15,6 +15,7 @@ classifiers = [
"Operating System :: OS Independent",
]
dependencies = [
'eval-type-backport',
'geographiclib',
'matplotlib',
'pandas >= 2.0.0',
Expand All @@ -27,8 +28,8 @@ dependencies = [
'seaborn',
'tabulate',
'toml',
'utm',
'tqdm',
'utm',
]

[project.urls]
Expand Down Expand Up @@ -65,7 +66,6 @@ include = ["wind_up*"]

[tool.ruff]
line-length = 120
target-version = "py310"
show-fixes = true

[tool.ruff.lint]
Expand Down Expand Up @@ -99,7 +99,6 @@ max-args = 17 # try to bring this down to 5

[tool.mypy]
plugins = ["pydantic.mypy"]
python_version = "3.10"
exclude = "build|tests|venv|.venv|__ignore__"
disallow_untyped_defs = true

Expand Down
2 changes: 2 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

from pathlib import Path

import pytest
Expand Down
74 changes: 74 additions & 0 deletions tests/test_backporting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import pytest

from wind_up.backporting import strict_zip


def test_equal_length_iterables() -> None:
a = [1, 2, 3]
b = ["a", "b", "c"]
result = list(strict_zip(a, b, strict=True))
assert result == [(1, "a"), (2, "b"), (3, "c")]


def test_unequal_length_raises_value_error() -> None:
a = [1, 2, 3]
b = ["a", "b"]

with pytest.raises(ValueError): # noqa PT011
list(strict_zip(a, b, strict=True))


def test_unequal_length_non_strict() -> None:
a = [1, 2, 3]
b = ["a", "b"]

result = list(strict_zip(a, b, strict=False))
assert result == [(1, "a"), (2, "b")] # Shorter iterable determines length


def test_empty_iterables() -> None:
a = []
b = []

result = list(strict_zip(a, b, strict=True))
assert result == []


def test_single_iterable() -> None:
a = [1, 2, 3]

result = list(strict_zip(a, strict=True))
assert result == [(1,), (2,), (3,)]


def test_multiple_iterables() -> None:
a = [1, 2, 3]
b = ["a", "b", "c"]
c = [True, False, True]

result = list(strict_zip(a, b, c, strict=True))
assert result == [(1, "a", True), (2, "b", False), (3, "c", True)]


def test_non_iterable_argument() -> None:
a = [1, 2, 3]
b = 5 # Not iterable

with pytest.raises(TypeError):
list(strict_zip(a, b, strict=True))


def test_tuple_and_generator() -> None:
a = (1, 2, 3)
b = (x for x in ["a", "b", "c"]) # Generator

result = list(strict_zip(a, b, strict=True))
assert result == [(1, "a"), (2, "b"), (3, "c")]


def test_nested_iterables() -> None:
a = [[1], [2], [3]]
b = [["a"], ["b"], ["c"]]

result = list(strict_zip(a, b, strict=True))
assert result == [([1], ["a"]), ([2], ["b"]), ([3], ["c"])]
2 changes: 2 additions & 0 deletions tests/test_math_funcs.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import numpy as np
import pandas as pd
import pytest
Expand Down
3 changes: 2 additions & 1 deletion tests/test_smart_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pytest

from tests.conftest import TEST_DATA_FLD
from wind_up.backporting import strict_zip
from wind_up.constants import TIMESTAMP_COL
from wind_up.models import WindUpConfig
from wind_up.smart_data import (
Expand Down Expand Up @@ -32,7 +33,7 @@ def test_calc_last_xmin_datetime_in_month() -> None:
dt.datetime(2020, 2, 29, 23, 50),
dt.datetime(2020, 2, 29, 23, 50),
]
for i, e in zip(inputs, expected, strict=True):
for i, e in strict_zip(inputs, expected):
assert calc_last_xmin_datetime_in_month(i, TIMEBASE_PD_TIMEDELTA) == pd.Timestamp(e)


Expand Down
2 changes: 2 additions & 0 deletions tests/test_wind_funcs.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import pandas as pd
import pytest
from pandas.testing import assert_series_equal
Expand Down
16 changes: 16 additions & 0 deletions wind_up/backporting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import sys
from collections.abc import Iterable, Iterator

if sys.version_info >= (3, 10):

def strict_zip(*iterables: Iterable, strict: bool = False) -> Iterator:
return zip(*iterables, strict=strict)
else:

def strict_zip(*iterables: Iterable, strict: bool = False) -> Iterator:
if strict:
iterables = [list(it) for it in iterables] # type: ignore[assignment]
if not all(len(it) == len(iterables[0]) for it in iterables): # type: ignore[arg-type]
msg = "All iterables must have the same length"
raise ValueError(msg)
return zip(*iterables) # type: ignore[call-overload]
6 changes: 5 additions & 1 deletion wind_up/combine_results.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
from __future__ import annotations

import itertools
import logging
import math
from typing import TYPE_CHECKING

import numpy as np
import pandas as pd
from scipy.stats import norm

from wind_up.models import PlotConfig
from wind_up.plots.combine_results_plots import plot_combine_results
from wind_up.result_manager import result_manager

if TYPE_CHECKING:
from wind_up.models import PlotConfig
logger = logging.getLogger(__name__)


Expand Down
6 changes: 5 additions & 1 deletion wind_up/detrend.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from __future__ import annotations

import logging
from typing import TYPE_CHECKING

import numpy as np
import pandas as pd

from wind_up.math_funcs import circ_diff
from wind_up.models import PlotConfig, WindUpConfig
from wind_up.plots.detrend_plots import (
plot_check_applied_detrend,
plot_detrend_ws_scatter,
Expand All @@ -13,6 +15,8 @@
from wind_up.result_manager import result_manager
from wind_up.waking_state import get_iec_upwind_turbines, lat_long_is_valid, list_wtgs_offline_in_scen

if TYPE_CHECKING:
from wind_up.models import PlotConfig, WindUpConfig
logger = logging.getLogger(__name__)


Expand Down
11 changes: 8 additions & 3 deletions wind_up/interface.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from __future__ import annotations

import logging
import math
from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING

import pandas as pd

from wind_up.caching import with_pickle_cache
from wind_up.constants import REANALYSIS_WD_COL
from wind_up.models import PlotConfig, WindUpConfig
from wind_up.northing import add_wf_yawdir, apply_northing_corrections
from wind_up.optimize_northing import auto_northing_corrections
from wind_up.reanalysis_data import ReanalysisDataset, add_reanalysis_data
Expand All @@ -17,6 +18,10 @@
from wind_up.waking_state import add_waking_state
from wind_up.ws_est import add_ws_est

if TYPE_CHECKING:
from pathlib import Path

from wind_up.models import PlotConfig, WindUpConfig
logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -121,7 +126,7 @@ def from_cfg(
toggle_df: pd.DataFrame | None = None,
reanalysis_datasets: list[ReanalysisDataset],
cache_dir: Path | None = None,
) -> "AssessmentInputs":
) -> AssessmentInputs:
func = preprocess if cache_dir is None else with_pickle_cache(cache_dir / "preprocess.pickle")(preprocess)
wf_df, pc_per_ttype = func(
cfg=cfg,
Expand Down
6 changes: 5 additions & 1 deletion wind_up/long_term.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
from __future__ import annotations

import logging
from typing import TYPE_CHECKING

import numpy as np
import pandas as pd

from wind_up.constants import HOURS_PER_YEAR, RAW_POWER_COL, RAW_WINDSPEED_COL
from wind_up.models import PlotConfig, WindUpConfig
from wind_up.plots.long_term_plots import plot_lt_ws, plot_lt_ws_raw_filt
from wind_up.result_manager import result_manager

if TYPE_CHECKING:
from wind_up.models import PlotConfig, WindUpConfig
logger = logging.getLogger(__name__)


Expand Down
2 changes: 2 additions & 0 deletions wind_up/main_analysis.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import logging
import math

Expand Down
2 changes: 2 additions & 0 deletions wind_up/math_funcs.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import numpy as np
import numpy.typing as npt

Expand Down
Loading

0 comments on commit d96aea9

Please sign in to comment.