mirror of
https://github.com/saymrwulf/prophet.git
synced 2026-05-14 20:48:08 +00:00
Simplify setup.py and tests (#2336)
This commit is contained in:
parent
8b3d09caf7
commit
2f84620a28
12 changed files with 123 additions and 159 deletions
43
.github/workflows/build-and-test.yml
vendored
43
.github/workflows/build-and-test.yml
vendored
|
|
@ -9,37 +9,40 @@ on:
|
|||
jobs:
|
||||
build-and-test-python:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.7, 3.8]
|
||||
python-version: ["3.8"]
|
||||
os: ["macos-latest", "ubuntu-latest", "windows-latest"]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- name: "Get OS version (Linux)"
|
||||
if: startsWith(runner.os, 'Linux')
|
||||
- name: "Set environment variables (Windows)"
|
||||
if: startsWith(runner.os, 'Windows')
|
||||
shell: pwsh
|
||||
run: |
|
||||
echo "OS_VERSION=`lsb_release -sr`" >> $GITHUB_ENV
|
||||
echo "PIP_DEFAULT_CACHE=$HOME/.cache/pip" >> $GITHUB_ENV
|
||||
echo "DEFAULT_HOME=$HOME" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
(Get-ItemProperty "HKLM:System\CurrentControlSet\Control\FileSystem").LongPathsEnabled
|
||||
$os_version = (Get-CimInstance Win32_OperatingSystem).version
|
||||
Echo "OS_VERSION=$os_version" >> $env:GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: "Restore pip cache"
|
||||
id: cache-pip
|
||||
uses: actions/cache@v2
|
||||
cache: pip
|
||||
cache-dependency-path: "**/python/pyproject.toml"
|
||||
- name: "Restore RTools40"
|
||||
if: startsWith(runner.os, 'Windows')
|
||||
id: cache-rtools
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ env.PIP_DEFAULT_CACHE }}
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/python/requirements.txt') }}-v1
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
path: C:/rtools40
|
||||
key: ${{ runner.os }}-${{ env.OS_VERSION }}-rtools-v1
|
||||
- name: Install and test
|
||||
run: |
|
||||
pip install -U -r python/requirements.txt dask[dataframe] distributed
|
||||
cd python
|
||||
python setup.py develop test
|
||||
python -m pip install -U --editable ".[dev,parallel]"
|
||||
python -m pytest prophet/tests/
|
||||
|
||||
build-and-test-r:
|
||||
|
||||
|
|
@ -55,7 +58,7 @@ jobs:
|
|||
RSPM: ${{ matrix.config.rspm }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up R
|
||||
uses: r-lib/actions/setup-r@v2
|
||||
with:
|
||||
|
|
@ -71,7 +74,7 @@ jobs:
|
|||
shell: Rscript {0}
|
||||
- name: Restore R package cache
|
||||
if: runner.os != 'Windows'
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ env.R_LIBS_USER }}
|
||||
key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,12 @@ FROM python:3.7-stretch
|
|||
|
||||
RUN apt-get -y install libc-dev
|
||||
|
||||
RUN pip install pip==19.1.1
|
||||
|
||||
COPY python/requirements.txt .
|
||||
RUN pip install -r requirements.txt
|
||||
RUN pip install ipython==7.5.0
|
||||
RUN pip install pip==22.3.1
|
||||
|
||||
COPY . .
|
||||
|
||||
WORKDIR python
|
||||
|
||||
RUN python setup.py install
|
||||
RUN python -m pip install -e \".[dev, parallel]\"
|
||||
|
||||
WORKDIR /
|
||||
|
||||
|
|
|
|||
|
|
@ -88,14 +88,13 @@ To get the latest code changes as they are merged, you can clone this repo and b
|
|||
```bash
|
||||
git clone https://github.com/facebook/prophet.git
|
||||
cd prophet/python
|
||||
python -m pip install -r requirements.txt
|
||||
python setup.py develop
|
||||
python -m pip install -e .
|
||||
```
|
||||
|
||||
By default, Prophet will use a fixed version of `cmdstan` (downloading and installing it if necessary) to compile the model executables. If this is undesired and you would like to use your own existing `cmdstan` installation, you can set the environment variable `PROPHET_REPACKAGE_CMDSTAN` to `False`:
|
||||
|
||||
```bash
|
||||
export PROPHET_REPACKAGE_CMDSTAN=False; python setup.py develop
|
||||
export PROPHET_REPACKAGE_CMDSTAN=False; python -m pip install -e .
|
||||
```
|
||||
|
||||
### Linux
|
||||
|
|
|
|||
|
|
@ -46,12 +46,10 @@ $ cd python
|
|||
# with Anaconda
|
||||
$ conda create -n prophet
|
||||
$ conda activate prophet
|
||||
$ pip install -r requirements.txt
|
||||
|
||||
# with venv
|
||||
$ python3 -m venv prophet
|
||||
$ source prophet/bin/activate
|
||||
$ pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### R
|
||||
|
|
@ -86,7 +84,7 @@ The next step is to build and install the development version of prophet in the
|
|||
### Python
|
||||
|
||||
```bash
|
||||
$ python setup.py develop
|
||||
$ python -m pip install -e ".[dev,parallel]"
|
||||
```
|
||||
|
||||
You should be able to import *prophet* from your locally built version:
|
||||
|
|
@ -95,7 +93,7 @@ You should be able to import *prophet* from your locally built version:
|
|||
$ python # start an interpreter
|
||||
>>> import prophet
|
||||
>>> prophet.__version__
|
||||
'1.0' # whatever the current github version is
|
||||
'1.1.2' # whatever the current github version is
|
||||
```
|
||||
|
||||
This will create the new environment, and not touch any of your existing environments,
|
||||
|
|
@ -154,11 +152,11 @@ Adding tests is one of the most common requests after code is pushed to prophet.
|
|||
|
||||
### Python
|
||||
|
||||
Prophet uses the ``unittest`` package for running tests in Python and ``testthat`` package for testing in R. All tests should go into the tests subdirectory in either the Python or R folders.
|
||||
Prophet uses the ``pytest`` package for running tests in Python and ``testthat`` package for testing in R. All tests should go into the tests subdirectory in either the Python or R folders.
|
||||
|
||||
The entire test suite can be run by typing:
|
||||
```bash
|
||||
$ python setup.py test
|
||||
$ python -m pytest prophet/tests/
|
||||
```
|
||||
|
||||
### R
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
include stan/*.stan
|
||||
include LICENSE
|
||||
include requirements.txt
|
||||
include pyproject.toml
|
||||
|
||||
# Ensure in-place built models do not get included in the source dist.
|
||||
prune prophet/stan_model
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ See [Installation in Python - Development version](https://github.com/facebook/p
|
|||
|
||||
Simply type `make build` and if everything is fine you should be able to `make shell` or alternative jump directly to `make py-shell`.
|
||||
|
||||
To run the tests, inside the container `cd python/prophet` and then `python -m unittest`
|
||||
To run the tests, inside the container `cd python/prophet` and then `python -m pytest prophet/tests/`
|
||||
|
||||
### Example usage
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1 @@
|
|||
__title__ = "prophet"
|
||||
__description__ = "Automatic Forecasting Procedure"
|
||||
__url__ = "https://facebook.github.io/prophet/"
|
||||
__version__ = "1.1.1"
|
||||
__author__ = "Sean J. Taylor <sjtz@pm.me>, Ben Letham <bletham@fb.com>"
|
||||
__author_email__ = "sjtz@pm.me"
|
||||
__license__ = "MIT"
|
||||
__version__ = "1.1.2"
|
||||
|
|
|
|||
18
python/prophet/tests/conftest.py
Normal file
18
python/prophet/tests/conftest.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import pytest
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
config.addinivalue_line("markers", "slow: mark tests as slow (include in run with --test-slow)")
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--test-slow", action="store_true", default=False, help="Run slow tests")
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
if config.getoption("--test-slow"):
|
||||
return
|
||||
skip_slow = pytest.mark.skip(reason="Skipped due to the lack of '--test-slow' argument")
|
||||
for item in items:
|
||||
if "slow" in item.keywords:
|
||||
item.add_marker(skip_slow)
|
||||
|
|
@ -9,11 +9,11 @@ from __future__ import print_function
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import sys
|
||||
from unittest import TestCase, skipUnless
|
||||
from unittest import TestCase
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from prophet import Prophet
|
||||
from prophet.utilities import warm_start_params
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ class TestProphet(TestCase):
|
|||
res = self.rmse(future['yhat'], test['y'])
|
||||
self.assertAlmostEqual(res, 23.44, places=2, msg="backend: {}".format(forecaster.stan_backend))
|
||||
|
||||
@skipUnless("--test-slow" in sys.argv, "Skipped due to the lack of '--test-slow' argument")
|
||||
@pytest.mark.slow
|
||||
def test_fit_sampling_predict(self):
|
||||
days = 30
|
||||
N = DATA.shape[0]
|
||||
|
|
@ -106,7 +106,7 @@ class TestProphet(TestCase):
|
|||
forecaster.fit(train)
|
||||
forecaster.predict(future)
|
||||
|
||||
@skipUnless("--test-slow" in sys.argv, "Skipped due to the lack of '--test-slow' argument")
|
||||
@pytest.mark.slow
|
||||
def test_fit_predict_no_changepoints_mcmc(self):
|
||||
N = DATA.shape[0]
|
||||
train = DATA.head(N // 2)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,61 @@
|
|||
[build-system]
|
||||
requires = [
|
||||
"setuptools>=42",
|
||||
"wheel",
|
||||
"cmdstanpy>=1.0.4",
|
||||
"setuptools>=64",
|
||||
"wheel",
|
||||
"cmdstanpy>=1.0.4",
|
||||
]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "prophet"
|
||||
dynamic = ["version"]
|
||||
description = "Automatic Forecasting Procedure"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.7"
|
||||
dependencies = [
|
||||
"cmdstanpy>=1.0.4",
|
||||
"numpy>=1.15.4",
|
||||
"matplotlib>=2.0.0",
|
||||
"pandas>=1.0.4",
|
||||
"LunarCalendar>=0.0.9",
|
||||
"convertdate>=2.1.2",
|
||||
"holidays>=0.14.2",
|
||||
"python-dateutil>=2.8.0",
|
||||
"tqdm>=4.36.1",
|
||||
]
|
||||
authors = [
|
||||
{name = "Sean J. Taylor", email = "sjtz@pm.me"},
|
||||
{name = "Ben Letham", email = "bletham@fb.com"},
|
||||
]
|
||||
maintainers = [
|
||||
{name = "Cuong Duong", email = "cuong.duong242@gmail.com"},
|
||||
]
|
||||
license = {text = "MIT"}
|
||||
classifiers = [
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"setuptools>=64",
|
||||
"wheel",
|
||||
"pytest",
|
||||
"jupyterlab",
|
||||
"nbconvert",
|
||||
"plotly",
|
||||
]
|
||||
parallel = [
|
||||
"dask[dataframe]",
|
||||
"distributed",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
homepage = "https://facebook.github.io/prophet/"
|
||||
documentation = "https://facebook.github.io/prophet/"
|
||||
repository = "https://github.com/facebook/prophet"
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
cmdstanpy>=1.0.4
|
||||
numpy>=1.15.4
|
||||
pandas>=1.0.4
|
||||
matplotlib>=2.0.0
|
||||
LunarCalendar>=0.0.9
|
||||
convertdate>=2.1.2
|
||||
holidays>=0.14.2
|
||||
setuptools>=42
|
||||
setuptools-git>=1.2
|
||||
python-dateutil>=2.8.0
|
||||
tqdm>=4.36.1
|
||||
wheel>=0.37.0
|
||||
105
python/setup.py
105
python/setup.py
|
|
@ -4,18 +4,15 @@
|
|||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
from pathlib import Path
|
||||
from shutil import copy, copytree, rmtree
|
||||
from typing import List
|
||||
|
||||
from pkg_resources import add_activation_listener, normalize_path, require, working_set
|
||||
from setuptools import find_packages, setup, Extension
|
||||
from setuptools.command.build_ext import build_ext
|
||||
from setuptools.command.build_py import build_py
|
||||
from setuptools.command.develop import develop
|
||||
from setuptools.command.test import test as test_command
|
||||
from setuptools.command.editable_wheel import editable_wheel
|
||||
|
||||
|
||||
MODEL_DIR = "stan"
|
||||
|
|
@ -52,6 +49,7 @@ def prune_cmdstan(cmdstan_dir: str) -> None:
|
|||
rmtree(original_dir)
|
||||
temp_dir.rename(original_dir)
|
||||
|
||||
|
||||
def repackage_cmdstan():
|
||||
return os.environ.get("PROPHET_REPACKAGE_CMDSTAN", "").lower() not in ["false", "0"]
|
||||
|
||||
|
|
@ -59,6 +57,7 @@ def repackage_cmdstan():
|
|||
def maybe_install_cmdstan_toolchain():
|
||||
"""Install C++ compilers required to build stan models on Windows machines."""
|
||||
import cmdstanpy
|
||||
|
||||
try:
|
||||
cmdstanpy.utils.cxx_toolchain_path()
|
||||
except Exception:
|
||||
|
|
@ -122,7 +121,6 @@ def build_cmdstan_model(target_dir):
|
|||
prune_cmdstan(cmdstan_dir)
|
||||
|
||||
|
||||
|
||||
def get_backends_from_env() -> List[str]:
|
||||
return os.environ.get("STAN_BACKEND", "CMDSTANPY").split(",")
|
||||
|
||||
|
|
@ -131,9 +129,10 @@ def build_models(target_dir):
|
|||
print("Compiling cmdstanpy model")
|
||||
build_cmdstan_model(target_dir)
|
||||
|
||||
if 'PYSTAN' in get_backends_from_env():
|
||||
if "PYSTAN" in get_backends_from_env():
|
||||
raise ValueError("PyStan backend is not supported for Prophet >= 1.1")
|
||||
|
||||
|
||||
class BuildPyCommand(build_py):
|
||||
"""Custom build command to pre-compile Stan models."""
|
||||
|
||||
|
|
@ -153,117 +152,33 @@ class BuildExtCommand(build_ext):
|
|||
pass
|
||||
|
||||
|
||||
class DevelopCommand(develop):
|
||||
class EditableWheel(editable_wheel):
|
||||
"""Custom develop command to pre-compile Stan models in-place."""
|
||||
|
||||
def run(self):
|
||||
if not self.dry_run:
|
||||
target_dir = os.path.join(self.setup_path, MODEL_TARGET_DIR)
|
||||
target_dir = os.path.join(self.project_dir, MODEL_TARGET_DIR)
|
||||
self.mkpath(target_dir)
|
||||
build_models(target_dir)
|
||||
|
||||
develop.run(self)
|
||||
editable_wheel.run(self)
|
||||
|
||||
|
||||
class TestCommand(test_command):
|
||||
user_options = [
|
||||
("test-module=", "m", "Run 'test_suite' in specified module"),
|
||||
(
|
||||
"test-suite=",
|
||||
"s",
|
||||
"Run single test, case or suite (e.g. 'module.test_suite')",
|
||||
),
|
||||
("test-runner=", "r", "Test runner to use"),
|
||||
("test-slow", "w", "Test slow suites (default off)"),
|
||||
]
|
||||
test_slow = None
|
||||
|
||||
def initialize_options(self):
|
||||
super(TestCommand, self).initialize_options()
|
||||
self.test_slow = False
|
||||
|
||||
def finalize_options(self):
|
||||
super(TestCommand, self).finalize_options()
|
||||
if self.test_slow is None:
|
||||
self.test_slow = getattr(self.distribution, "test_slow", False)
|
||||
|
||||
"""We must run tests on the build directory, not source."""
|
||||
|
||||
def with_project_on_sys_path(self, func):
|
||||
# Ensure metadata is up-to-date
|
||||
self.reinitialize_command("build_py", inplace=0)
|
||||
self.run_command("build_py")
|
||||
bpy_cmd = self.get_finalized_command("build_py")
|
||||
build_path = normalize_path(bpy_cmd.build_lib)
|
||||
|
||||
# Build extensions
|
||||
self.reinitialize_command("egg_info", egg_base=build_path)
|
||||
self.run_command("egg_info")
|
||||
|
||||
self.reinitialize_command("build_ext", inplace=0)
|
||||
self.run_command("build_ext")
|
||||
|
||||
ei_cmd = self.get_finalized_command("egg_info")
|
||||
|
||||
old_path = sys.path[:]
|
||||
old_modules = sys.modules.copy()
|
||||
|
||||
try:
|
||||
sys.path.insert(0, normalize_path(ei_cmd.egg_base))
|
||||
working_set.__init__()
|
||||
add_activation_listener(lambda dist: dist.activate())
|
||||
require("%s==%s" % (ei_cmd.egg_name, ei_cmd.egg_version))
|
||||
func()
|
||||
finally:
|
||||
sys.path[:] = old_path
|
||||
sys.modules.clear()
|
||||
sys.modules.update(old_modules)
|
||||
working_set.__init__()
|
||||
|
||||
|
||||
with open("README.md", "r", encoding="utf-8") as f:
|
||||
long_description = f.read()
|
||||
|
||||
with open("requirements.txt", "r") as f:
|
||||
install_requires = f.read().splitlines()
|
||||
|
||||
about = {}
|
||||
here = Path(__file__).parent.resolve()
|
||||
with open(here / "prophet" / "__version__.py", "r") as f:
|
||||
with open(here / "prophet" / "__version__.py", "r") as f:
|
||||
exec(f.read(), about)
|
||||
|
||||
setup(
|
||||
name=about["__title__"],
|
||||
version=about["__version__"],
|
||||
description=about["__description__"],
|
||||
url=about["__url__"],
|
||||
project_urls={
|
||||
"Source": "https://github.com/facebook/prophet",
|
||||
},
|
||||
author=about["__author__"],
|
||||
author_email=about["__author_email__"],
|
||||
license=about["__license__"],
|
||||
packages=find_packages(),
|
||||
install_requires=install_requires,
|
||||
python_requires=">=3.7",
|
||||
zip_safe=False,
|
||||
include_package_data=True,
|
||||
ext_modules=[Extension("prophet.stan_model", [])],
|
||||
cmdclass={
|
||||
"build_ext": BuildExtCommand,
|
||||
"build_py": BuildPyCommand,
|
||||
"develop": DevelopCommand,
|
||||
"test": TestCommand,
|
||||
"editable_wheel": EditableWheel,
|
||||
},
|
||||
test_suite="prophet.tests",
|
||||
classifiers=[
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
],
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue