Repository: HumanCompatibleAI/imitation (language: python)

Issue:
Automate version bump on release
Create workflow/action to trigger both a change in version and a new GitHub release together (which in turn triggers a deploy to PyPI).


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml
new file mode 100644
index 000000000..9edcd1a44
--- /dev/null
+++ b/.github/workflows/publish-to-pypi.yml
@@ -0,0 +1,52 @@
+# Adapted from https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/
+
+name: Publish imitation distributions 📦 to PyPI and TestPyPI
+
+on: push
+
+jobs:
+  build-n-publish:
+    name: Build and publish imitation distributions 📦 to PyPI and TestPyPI
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v3
+      with:
+        # Fetch tags needed by setuptools_scm to infer version number
+        # See https://github.com/pypa/setuptools_scm/issues/414
+        fetch-depth: 0
+    - name: Set up Python 3.10
+      uses: actions/setup-python@v3
+      with:
+        python-version: "3.10"
+
+    - name: Install pypa/build
+      run: >-
+        python -m
+        pip install
+        build
+        --user
+    - name: Build a binary wheel and a source tarball
+      run: >-
+        python -m
+        build
+        --sdist
+        --wheel
+        --outdir dist/
+        .
+
+    # Publish new distribution to Test PyPi on every push.
+    # This ensures the workflow stays healthy, and will also serve
+    # as a source of alpha builds.
+    - name: Publish distribution 📦 to Test PyPI
+      uses: pypa/gh-action-pypi-publish@release/v1
+      with:
+        password: ${{ secrets.TEST_PYPI_API_TOKEN }}
+        repository_url: https://test.pypi.org/legacy/
+
+    # Publish new distribution to production PyPi on releases.
+    - name: Publish distribution 📦 to PyPI
+      if: startsWith(github.ref, 'refs/tags/v')
+      uses: pypa/gh-action-pypi-publish@release/v1
+      with:
+        password: ${{ secrets.PYPI_API_TOKEN }}
diff --git a/docs/conf.py b/docs/conf.py
index c4e504bfe..80c2baa54 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -19,14 +19,14 @@
 
 # -- Project information -----------------------------------------------------
 
-import imitation  # pytype: disable=import-error
+from importlib import metadata
 
 project = "imitation"
-copyright = "2019-2021, Center for Human-Compatible AI"  # noqa: A001
+copyright = "2019-2022, Center for Human-Compatible AI"  # noqa: A001
 author = "Center for Human-Compatible AI"
 
 # The full version, including alpha/beta/rc tags
-release = imitation.__version__
+version = metadata.version("imitation")
 
 
 # -- General configuration ---------------------------------------------------
diff --git a/pyproject.toml b/pyproject.toml
index 5a8e741b0..19b4ae3ea 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,2 +1,11 @@
+[build-system]
+requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"]
+build-backend = "setuptools.build_meta"
+
+[tools.setuptools_scm]
+# Disable local scheme to allow uploads to Test PyPI.
+# See https://github.com/pypa/setuptools_scm/issues/342
+local_scheme = "no-local-version"
+
 [tool.black]
-target-version = ["py37"]
+target-version = ["py38"]
diff --git a/readthedocs.yml b/readthedocs.yml
index dee2f3e49..1efe11cb7 100644
--- a/readthedocs.yml
+++ b/readthedocs.yml
@@ -6,7 +6,7 @@ sphinx:
 formats: all
 
 python:
-  version: 3.7
+  version: 3.8
   install:
     - method: pip
       path: .
diff --git a/setup.py b/setup.py
index 2f6097c05..5b01ca5db 100644
--- a/setup.py
+++ b/setup.py
@@ -7,8 +7,6 @@
 from setuptools import find_packages, setup
 from setuptools.command.install import install
 
-import src.imitation  # pytype: disable=import-error
-
 IS_NOT_WINDOWS = os.name != "nt"
 
 PARALLEL_REQUIRE = ["ray[debug,tune]>=1.13.0"]
@@ -80,7 +78,10 @@ def run(self):
 setup(
     cmdclass={"install": InstallCommand},
     name="imitation",
-    version=src.imitation.__version__,
+    # Disable local scheme to allow uploads to Test PyPI.
+    # See https://github.com/pypa/setuptools_scm/issues/342
+    use_scm_version={"local_scheme": "no-local-version"},
+    setup_requires=["setuptools_scm"],
     description="Implementation of modern reward and imitation learning algorithms.",
     long_description=get_readme(),
     long_description_content_type="text/markdown",
@@ -99,13 +100,10 @@ def run(self):
         "torch>=1.4.0",
         "tqdm",
         "scikit-learn>=0.21.2",
-        # TODO(adam): switch to master once stable-baselines3 PR#979 merged
-        #  https://github.com/DLR-RM/stable-baselines3/pull/979
-        #  (and then switch to PyPi once it makes it to release)
-        "stable-baselines3@git+https://github.com/DLR-RM/stable-baselines3.git@"
-        "ff4bc96afa6336c5f4d0ebd8a40aec398fe648ba",
-        # TODO(nora) switch back to PyPi once 0.8.3 makes it to release:
-        "sacred@git+https://github.com/IDSIA/sacred.git@0.8.3",
+        "stable-baselines3>=1.5.0",
+        # TODO(adam) switch to upstream release if they make it
+        #  See https://github.com/IDSIA/sacred/issues/879
+        "chai-sacred>=0.8.3",
         "tensorboard>=1.14",
     ],
     tests_require=TESTS_REQUIRE,
diff --git a/src/imitation/__init__.py b/src/imitation/__init__.py
index 28d210820..39ba165da 100644
--- a/src/imitation/__init__.py
+++ b/src/imitation/__init__.py
@@ -1,3 +1,9 @@
 """imitation: implementations of imitation and reward learning algorithms."""
 
-__version__ = "0.2.0"
+from importlib import metadata
+
+try:
+    __version__ = metadata.version("imitation")
+except metadata.PackageNotFoundError:  # pragma: no cover
+    # package is not installed
+    pass


Test patch:
diff --git a/tests/algorithms/test_preference_comparisons.py b/tests/algorithms/test_preference_comparisons.py
index 2e6358dfc..a86e130fc 100644
--- a/tests/algorithms/test_preference_comparisons.py
+++ b/tests/algorithms/test_preference_comparisons.py
@@ -1,5 +1,6 @@
 """Tests for the preference comparisons reward learning implementation."""
 
+import os
 import re
 from typing import Sequence
 
@@ -176,11 +177,13 @@ def test_trainer_no_crash(
         custom_logger=custom_logger,
         query_schedule=schedule,
     )
-    result = main_trainer.train(100, 10)
-    # We don't expect good performance after training for 10 (!) timesteps,
-    # but check stats are within the bounds they should lie in.
-    assert result["reward_loss"] > 0.0
-    assert 0.0 < result["reward_accuracy"] <= 1.0
+    # TODO(GH494): remove this skip once SB3 fixed
+    if os.name != "nt":
+        result = main_trainer.train(100, 10)
+        # We don't expect good performance after training for 10 (!) timesteps,
+        # but check stats are within the bounds they should lie in.
+        assert result["reward_loss"] > 0.0
+        assert 0.0 < result["reward_accuracy"] <= 1.0
 
 
 def test_discount_rate_no_crash(agent_trainer, reward_net, fragmenter, custom_logger):
@@ -200,7 +203,9 @@ def test_discount_rate_no_crash(agent_trainer, reward_net, fragmenter, custom_lo
         reward_trainer=reward_trainer,
         custom_logger=custom_logger,
     )
-    main_trainer.train(100, 10)
+    # TODO(GH494): remove this skip once SB3 fixed
+    if os.name != "nt":
+        main_trainer.train(100, 10)
 
 
 def test_synthetic_gatherer_deterministic(agent_trainer, fragmenter):
@@ -316,4 +321,6 @@ def test_exploration_no_crash(agent, reward_net, fragmenter, custom_logger):
         fragmenter=fragmenter,
         custom_logger=custom_logger,
     )
-    main_trainer.train(100, 10)
+    # TODO(GH494): remove this skip once SB3 fixed
+    if os.name != "nt":
+        main_trainer.train(100, 10)