-
Notifications
You must be signed in to change notification settings - Fork 393
migrate tests from nbs to pytest #1029
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
Merged
Merged
Changes from all commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
beb8ead
scripts to extract tests
deven367 b029356
cleaned tests for `utils`
deven367 fd5407d
find `nbs` recursively
deven367 bc66a3a
extracted tests
deven367 44b4091
Merge branch 'main' into rm-nbdev
deven367 66b81e9
cleaned tests for `feature_engineering`
deven367 ff41ab4
cleaned tests for `ets`
deven367 14a4150
cleaned tests for `tbats`
deven367 6c91661
cleaned tests for `theta`
deven367 92490ce
partially cleaned tests for core
deven367 f129571
tests for `garch`
deven367 ac77624
cleaned tests for `core`
deven367 6dbc426
rename and clean
deven367 ac193f1
rename and clean
deven367 ec509c4
workflow for `pytest`
deven367 89e0a86
clone submodule to install from source
deven367 fc40da4
format tests for `mstl`
deven367 a2e8716
Merge branch 'main' into rm-nbdev
deven367 80c7ecf
add missing imports
deven367 def6dd1
rename and clean
deven367 c34ba8b
add missing imports
deven367 30035ba
Merge branch 'rm-nbdev' of https://github.com/Nixtla/statsforecast in…
deven367 c851e02
cleaned tests for `models`
deven367 2137a17
rename and clean tests for `prophet` adapter
deven367 2cb050a
clean remaining tests for ARIMA
deven367 4127531
clean remaining tests for CES
deven367 c12f37b
clean tests for `core`
deven367 51a3569
clean tests for `mfles`
deven367 c250124
clean tests for `mstl`
deven367 1a0ce31
clean tests for `tbats`
deven367 1a17072
missing import?
deven367 2f53a04
pytest mark skip tests for `ray`
deven367 9537370
skip test on py3.12+
deven367 dbfca17
conditional import
deven367 92c718f
skip tests in distributed_fugue
deven367 9827047
pinning pyarrow for `PyExtensionType` error
deven367 623179d
add tests for `garch` vs `arch`
deven367 f8bd26b
rm redundant print statements
deven367 18df258
clean `scripts` folder
deven367 949ba20
remove `initstate`
deven367 b6ddd57
merge test
deven367 52ed641
move common functionality to `conftest`
deven367 b686d91
move common logic to fixtures
deven367 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| name: pytest | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
| branches: [main] | ||
| workflow_dispatch: | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| all-tests: | ||
| runs-on: ${{ matrix.os }} | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| os: [ubuntu-latest, macos-latest, windows-latest] | ||
| python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] | ||
| steps: | ||
| - name: Clone repo | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: ${{ matrix.python-version }} | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| git submodule status | ||
| git submodule update --init --recursive | ||
| pip install uv && uv pip install --system ".[all]" | ||
|
|
||
| - name: Run pytest | ||
| run: pytest tests --ignore tests/test_distributed_fugue.py |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| # AUTOGENERATED! DO NOT EDIT! File to edit: cli.ipynb. | ||
|
|
||
| # %% auto 0 | ||
| __all__ = ['tst_flags', 'to_skip', 'mapper', 'print_execs', 'print_hide', 'other_tests', 'get_markdown', 'get_code', | ||
| 'extract_dir', 'no_dir_and_dir', 'get_all_tests', 'print_dir_in_nb'] | ||
|
|
||
| # %% cli.ipynb 1 | ||
| from functools import partial | ||
|
|
||
| from fastcore.script import call_parse | ||
| from nbdev.processors import NBProcessor | ||
|
|
||
| # %% cli.ipynb 5 | ||
| tst_flags = 'datasets distributed matplotlib polars pyarrow scipy'.split() | ||
| to_skip = [ | ||
| 'showdoc', | ||
| 'load_ext', | ||
| 'from nbdev' | ||
| ] | ||
|
|
||
|
|
||
| def print_execs(cell): | ||
| if 'exec' in cell.source: print(cell.source) | ||
|
|
||
| def print_hide(cell): | ||
| if 'hide' in cell.directives_: print(cell.source) | ||
|
|
||
| def other_tests(cell): | ||
| if len(cell.directives_) == 0: | ||
| print(cell.source) | ||
|
|
||
| def get_markdown(cell): | ||
| if cell.cell_type == "markdown": | ||
| print(cell.source) | ||
|
|
||
| def get_code(cell): | ||
| if cell.cell_type == "code": | ||
| print(cell.source) | ||
|
|
||
| def extract_dir(cell, dirs): | ||
| for directive in dirs.split(','): | ||
| if directive in cell.directives_: | ||
| print(cell.source) | ||
|
|
||
| def no_dir_and_dir(cell, dir): | ||
| if len(cell.directives_) == 0: | ||
| print(cell.source) | ||
|
|
||
| if dir in cell.directives_: | ||
| print(cell.source) | ||
|
|
||
| def get_all_tests(cell): | ||
| if cell.cell_type == "code": | ||
|
|
||
| if len(cell.directives_) == 0: | ||
| print(cell.source) | ||
|
|
||
|
|
||
| elif any(x in tst_flags + ['hide'] for x in cell.directives_): | ||
| if not (x in cell.source for x in to_skip): | ||
| print(cell.source) | ||
|
|
||
|
|
||
| # %% cli.ipynb 9 | ||
| mapper = { | ||
| 'print_execs': print_execs, | ||
| 'print_hide': print_hide, | ||
| 'other_tests': other_tests, | ||
| 'get_markdown': get_markdown, | ||
| 'extract_dir': extract_dir, | ||
| 'no_dir_and_dir': no_dir_and_dir, | ||
| 'get_all_tests':get_all_tests | ||
| } | ||
|
|
||
| # %% cli.ipynb 10 | ||
| @call_parse | ||
| def print_dir_in_nb(nb_path:str, | ||
| dir:str=None, | ||
| dir_name:str=None, | ||
| ): | ||
| if dir_name not in mapper.keys(): | ||
| raise ValueError(f'Choose processor from the the following: {mapper.keys()}') | ||
|
|
||
| if dir_name == 'extract_dir': | ||
| processor = NBProcessor(nb_path, partial(extract_dir, dir=dir)) | ||
| processor.process() | ||
| return | ||
| elif dir_name == 'no_dir_and_dir': | ||
| processor = NBProcessor(nb_path, partial(no_dir_and_dir, dir=dir)) | ||
| processor.process() | ||
| return | ||
|
|
||
| processor = NBProcessor(nb_path, mapper[dir_name]) | ||
| processor.process() | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| from nbdev.export import nb_export | ||
| nb_export('cli.ipynb', lib_path='.', name='cli') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| #!/bin/bash | ||
|
|
||
| echo Python: $(which python) | ||
| source /Users/deven367/miniforge3/bin/activate | ||
| conda activate nixtla | ||
|
|
||
| echo Activated env: $(which python) | ||
|
|
||
|
|
||
| mkdir -p ../tests | ||
|
|
||
| tst_flags='datasets distributed matplotlib polars pyarrow scipy' | ||
|
|
||
| nbs=$(find ../nbs/src -type f -iname '*.ipynb') | ||
| # echo "Available notebooks: $nbs" | ||
|
|
||
|
|
||
| # this approach3 that was discussed on Slack | ||
| # mkdir -p ../tests3 | ||
| # for flag in $tst_flags; do | ||
| # # echo "Extracting $flag" | ||
|
|
||
| # for nb in $nbs; do | ||
| # # get name of notebook without extension | ||
| # nb_name=$(basename "$nb" .ipynb) | ||
|
|
||
| # # echo "Processing notebook: $nb" | ||
| # # print_dir_in_nb "$nb" --dir_name no_dir_and_dir --dir "$flag" >> "../tests/test_$flag_$nb_name.py" | ||
| # print_dir_in_nb "$nb" --dir_name no_dir_and_dir --dir "$flag" >> "../tests3/test_${flag}_$nb_name.py" | ||
| # done | ||
| # done | ||
|
|
||
| for nb in $nbs; do | ||
| # get name of notebook without extension | ||
| nb_name=$(basename "$nb" .ipynb) | ||
|
|
||
| # echo "Processing notebook: $nb" | ||
| # print_dir_in_nb "$nb" --dir_name no_dir_and_dir --dir "$flag" >> "../tests/test_$flag_$nb_name.py" | ||
| python cli.py "$nb" --dir_name get_all_tests2 >> "../tests/test_$nb_name.py" | ||
| done |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import pandas as pd | ||
| import pytest | ||
| from statsforecast.utils import AirPassengers as ap | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module") | ||
| def sample_data_prophet(): | ||
| """Load sample data for Prophet testing.""" | ||
| df = pd.read_csv( | ||
| "https://raw.githubusercontent.com/facebook/prophet/main/examples/example_wp_log_peyton_manning.csv" | ||
| ) | ||
| return df | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def air_passengers(): | ||
| """AirPassengers dataset.""" | ||
| return ap |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| import warnings | ||
|
|
||
| import pandas as pd | ||
| import pytest | ||
| from statsforecast.adapters.prophet import Prophet | ||
|
|
||
| warnings.simplefilter(action="ignore", category=FutureWarning) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def prophet_model(): | ||
| """Create a Prophet model instance.""" | ||
| return Prophet(daily_seasonality=False) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def holidays_data(): | ||
| """Create sample holidays data.""" | ||
| playoffs = pd.DataFrame( | ||
| { | ||
| "holiday": "playoff", | ||
| "ds": pd.to_datetime( | ||
| [ | ||
| "2008-01-13", | ||
| "2009-01-03", | ||
| "2010-01-16", | ||
| "2010-01-24", | ||
| "2010-02-07", | ||
| "2011-01-08", | ||
| "2013-01-12", | ||
| "2014-01-12", | ||
| "2014-01-19", | ||
| "2014-02-02", | ||
| "2015-01-11", | ||
| "2016-01-17", | ||
| "2016-01-24", | ||
| "2016-02-07", | ||
| ] | ||
| ), | ||
| "lower_window": 0, | ||
| "upper_window": 1, | ||
| } | ||
| ) | ||
| superbowls = pd.DataFrame( | ||
| { | ||
| "holiday": "superbowl", | ||
| "ds": pd.to_datetime(["2010-02-07", "2014-02-02", "2016-02-07"]), | ||
| "lower_window": 0, | ||
| "upper_window": 1, | ||
| } | ||
| ) | ||
| return pd.concat((playoffs, superbowls)) | ||
|
|
||
|
|
||
| def test_prophet_initialization(): | ||
| """Test Prophet model initialization.""" | ||
| model = Prophet(daily_seasonality=False) | ||
| assert model is not None | ||
| assert hasattr(model, "fit") | ||
| assert hasattr(model, "predict") | ||
|
|
||
|
|
||
| def test_prophet_fit(sample_data_prophet, prophet_model): | ||
| """Test Prophet model fitting.""" | ||
| prophet_model.fit(sample_data_prophet) | ||
| # Check that the model has been fitted by verifying it has the necessary attributes | ||
| assert hasattr(prophet_model, "history") | ||
| assert prophet_model.history is not None | ||
|
|
||
|
|
||
| def test_prophet_predict(sample_data_prophet, prophet_model): | ||
| """Test Prophet model prediction.""" | ||
| prophet_model.fit(sample_data_prophet) | ||
| future = prophet_model.make_future_dataframe(365) | ||
| forecast = prophet_model.predict(future) | ||
|
|
||
| assert forecast is not None | ||
| assert isinstance(forecast, pd.DataFrame) | ||
| assert len(forecast) > len(sample_data_prophet) | ||
|
|
||
|
|
||
| def test_prophet_make_future_dataframe(sample_data_prophet, prophet_model): | ||
| """Test making future dataframe.""" | ||
| prophet_model.fit(sample_data_prophet) | ||
| future = prophet_model.make_future_dataframe(365) | ||
|
|
||
| assert isinstance(future, pd.DataFrame) | ||
| assert len(future) == len(sample_data_prophet) + 365 | ||
| assert "ds" in future.columns | ||
|
|
||
|
|
||
| def test_prophet_plot(sample_data_prophet, prophet_model): | ||
| """Test Prophet plotting functionality.""" | ||
| prophet_model.fit(sample_data_prophet) | ||
| future = prophet_model.make_future_dataframe(365) | ||
| forecast = prophet_model.predict(future) | ||
|
|
||
| # Test that plot method exists and can be called | ||
| fig = prophet_model.plot(forecast) | ||
| assert fig is not None | ||
|
|
||
|
|
||
| def test_prophet_with_holidays(sample_data_prophet, holidays_data): | ||
| """Test Prophet model with holidays.""" | ||
| model = Prophet(holidays=holidays_data, daily_seasonality=False) | ||
| model.fit(sample_data_prophet) | ||
| future = model.make_future_dataframe(365) | ||
| forecast = model.predict(future) | ||
|
|
||
| assert forecast is not None | ||
| assert isinstance(forecast, pd.DataFrame) | ||
|
|
||
|
|
||
| def test_prophet_forecast_components(sample_data_prophet, prophet_model): | ||
| """Test Prophet forecast components.""" | ||
| prophet_model.fit(sample_data_prophet) | ||
| future = prophet_model.make_future_dataframe(10) | ||
| forecast = prophet_model.predict(future) | ||
|
|
||
| # Check that the forecast contains expected columns | ||
| expected_columns = ["yhat", "yhat_lower", "yhat_upper", "trend"] | ||
| for col in expected_columns: | ||
| assert col in forecast.columns, f"Expected column '{col}' not found in forecast" | ||
|
|
||
|
|
||
| def test_prophet_empty_dataframe(): | ||
| """Test Prophet behavior with empty dataframe.""" | ||
| model = Prophet(daily_seasonality=False) | ||
| empty_df = pd.DataFrame(columns=["ds", "y"]) | ||
|
|
||
| with pytest.raises(Exception): # Prophet should raise an error with empty data | ||
| model.fit(empty_df) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.