Repository: rlworkgroup/garage

Issue:
Remove conda environment from CI/Dockerfiles
Now that we use setup.py for tracking dependencies, there's no need be creating conda environments inside the Dockerfile and CI. We can replace them with a vanilla virtualenv for greatest compatibility.

I suggest we add tests which ensure that the package can still be successfully solved and installed by conda and pipenv. If they don't take too long (<2min) then we can run them in the main CI, otherwise they will be cron_job tests (which are run only nightly).


Discussion:
This issue could also update the setup scripts. They don't necessarily need to setup a conda/pipenv for garage (perhaps they could, optionally?).

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,15 @@
 from setuptools import find_packages
 from setuptools import setup
 
+try:
+    # pylint: disable=unused-import
+    import numpy  # noqa: F401
+except ImportError:
+    raise RuntimeError(
+        'garage requires numpy in the environment to install. '
+        'Please install numpy==1.14.5 and try again. See '
+        'https://github.com/rlworkgroup/garage/issues/800 for more info.')
+
 # Required dependencies
 required = [
     # Please keep alphabetized
@@ -58,6 +67,7 @@
     'pylint==1.9.2',
     'pytest>=3.6',  # Required for pytest-cov on Python 3.6
     'pytest-cov',
+    'pytest-xdist',
     'sphinx',
     'sphinx_rtd_theme',
     'yapf',
@@ -79,6 +89,7 @@
     packages=find_packages(where='src'),
     package_dir={'': 'src'},
     scripts=['scripts/garage'],
+    python_requires='>=3.5',
     install_requires=required,
     extras_require=extras,
     license='MIT',
diff --git a/src/garage/np/algos/cem.py b/src/garage/np/algos/cem.py
--- a/src/garage/np/algos/cem.py
+++ b/src/garage/np/algos/cem.py
@@ -68,7 +68,7 @@ def __init__(self,
         # fixed
         self.n_best = int(n_samples * best_frac)
         assert self.n_best >= 1, (
-            f'n_samples is too low. Make sure that n_samples * best_frac >= 1')
+            'n_samples is too low. Make sure that n_samples * best_frac >= 1')
         self.n_params = len(self.cur_mean)
 
     def sample_params(self, epoch):


Test patch:
diff --git a/tests/fixtures/experiment/fixture_experiment.py b/tests/fixtures/experiment/fixture_experiment.py
--- a/tests/fixtures/experiment/fixture_experiment.py
+++ b/tests/fixtures/experiment/fixture_experiment.py
@@ -5,8 +5,8 @@
 from garage.tf.policies import CategoricalMLPPolicy
 
 
-def fixture_exp(snapshot_config):
-    with LocalRunner(snapshot_config=snapshot_config) as runner:
+def fixture_exp(snapshot_config, sess):
+    with LocalRunner(snapshot_config=snapshot_config, sess=sess) as runner:
         env = TfEnv(env_name='CartPole-v1')
 
         policy = CategoricalMLPPolicy(
diff --git a/tests/fixtures/fixtures.py b/tests/fixtures/fixtures.py
--- a/tests/fixtures/fixtures.py
+++ b/tests/fixtures/fixtures.py
@@ -13,8 +13,7 @@ def setup_method(self):
         self.sess.__enter__()
 
     def teardown_method(self):
-        if tf.get_default_session() is self.sess:
-            self.sess.__exit__(None, None, None)
+        self.sess.__exit__(None, None, None)
         self.sess.close()
         del self.sess
         gc.collect()
@@ -22,8 +21,15 @@ def teardown_method(self):
 
 class TfGraphTestCase:
     def setup_method(self):
+        tf.reset_default_graph()
         self.graph = tf.Graph()
+        for c in self.graph.collections:
+            self.graph.clear_collection(c)
+        self.graph_manager = self.graph.as_default()
+        self.graph_manager.__enter__()
         self.sess = tf.Session(graph=self.graph)
+        self.sess_manager = self.sess.as_default()
+        self.sess_manager.__enter__()
         self.sess.__enter__()
         logger.add_output(NullOutput())
         deterministic.set_seed(1)
@@ -34,8 +40,9 @@ def setup_method(self):
 
     def teardown_method(self):
         logger.remove_all()
-        if tf.get_default_session() is self.sess:
-            self.sess.__exit__(None, None, None)
+        self.sess.__exit__(None, None, None)
+        self.sess_manager.__exit__(None, None, None)
+        self.graph_manager.__exit__(None, None, None)
         self.sess.close()
 
         # These del are crucial to prevent ENOMEM in the CI
diff --git a/tests/garage/experiment/test_resume.py b/tests/garage/experiment/test_resume.py
--- a/tests/garage/experiment/test_resume.py
+++ b/tests/garage/experiment/test_resume.py
@@ -8,25 +8,27 @@
 
 
 class TestResume(TfGraphTestCase):
-    temp_dir = tempfile.TemporaryDirectory()
-    snapshot_config = SnapshotConfig(
-        snapshot_dir=temp_dir.name, snapshot_mode='last', snapshot_gap=1)
-    policy_params = None
-
-    @classmethod
-    def teardown_class(cls):
-        cls.temp_dir.cleanup()
-
-    def test_before_resume(self):
-        self.__class__.policy_params = fixture_exp(
-            self.__class__.snapshot_config)
+    def setup_method(self):
+        super().setup_method()
+        self.temp_dir = tempfile.TemporaryDirectory()
+        self.snapshot_config = SnapshotConfig(
+            snapshot_dir=self.temp_dir.name,
+            snapshot_mode='last',
+            snapshot_gap=1)
+        self.policy_params = fixture_exp(self.snapshot_config, self.sess)
+        for c in self.graph.collections:
+            self.graph.clear_collection(c)
+
+    def teardown_method(self):
+        self.temp_dir.cleanup()
+        super().teardown_method()
 
     def test_resume(self):
-        with LocalRunner(self.__class__.snapshot_config) as runner:
-            args = runner.restore(self.__class__.temp_dir.name)
-            assert np.isclose(
-                runner.policy.get_param_values(), self.__class__.
-                policy_params).all(), 'Policy parameters should persist'
+        with LocalRunner(self.snapshot_config, self.sess) as runner:
+            args = runner.restore(self.temp_dir.name)
+            assert np.equal(
+                runner.policy.get_param_values(),
+                self.policy_params).all(), 'Policy parameters should persist'
             assert args.n_epochs == 5, (
                 'Snapshot should save training parameters')
             assert args.start_epoch == 5, (
diff --git a/tests/garage/experiment/test_snapshotter_integration.py b/tests/garage/experiment/test_snapshotter_integration.py
--- a/tests/garage/experiment/test_snapshotter_integration.py
+++ b/tests/garage/experiment/test_snapshotter_integration.py
@@ -13,16 +13,20 @@
 
 
 class TestSnapshot(TfGraphTestCase):
-    temp_dir = tempfile.TemporaryDirectory()
-    snapshot_config = SnapshotConfig(
-        snapshot_dir=temp_dir.name, snapshot_mode='all', snapshot_gap=1)
-
-    @classmethod
-    def teardown_class(cls):
-        cls.temp_dir.cleanup()
-
-    def test_before_load(self):
-        fixture_exp(self.__class__.snapshot_config)
+    def setup_method(self):
+        super().setup_method()
+        self.temp_dir = tempfile.TemporaryDirectory()
+        snapshot_config = SnapshotConfig(
+            snapshot_dir=self.temp_dir.name,
+            snapshot_mode='all',
+            snapshot_gap=1)
+        fixture_exp(snapshot_config, self.sess)
+        for c in self.graph.collections:
+            self.graph.clear_collection(c)
+
+    def teardown_method(self):
+        self.temp_dir.cleanup()
+        super().teardown_method()
 
     @pytest.mark.parametrize('load_mode, last_epoch', [*configurations])
     def test_load(self, load_mode, last_epoch):
diff --git a/tests/garage/sampler/test_is_sampler.py b/tests/garage/sampler/test_is_sampler.py
--- a/tests/garage/sampler/test_is_sampler.py
+++ b/tests/garage/sampler/test_is_sampler.py
@@ -44,7 +44,7 @@ def test_is_sampler(self):
                 mocked.assert_not_called()
 
                 assert runner.sampler.obtain_samples(3)
-                mocked.assert_called_once()
+                mocked.assert_called_once_with(3, None, True)
 
             # test importance sampling for first n_is_pretrain iterations
             with unittest.mock.patch.object(ISSampler,
@@ -53,7 +53,7 @@ def test_is_sampler(self):
                 runner.sampler.n_backtrack = 'all'
                 runner.sampler.obtain_samples(4)
 
-                mocked.assert_called_once()
+                mocked.assert_called_once_with(4, None, True)
 
             runner.sampler.obtain_samples(5)
 
diff --git a/tests/garage/tf/algos/test_ppo.py b/tests/garage/tf/algos/test_ppo.py
--- a/tests/garage/tf/algos/test_ppo.py
+++ b/tests/garage/tf/algos/test_ppo.py
@@ -3,7 +3,6 @@
 too low.
 """
 import gym
-import pytest
 import tensorflow as tf
 
 from garage.envs import normalize
@@ -31,7 +30,8 @@ def setup_method(self):
             regressor_args=dict(hidden_sizes=(32, 32)),
         )
 
-    @pytest.mark.large
+    # large marker removed to balance test jobs
+    # @pytest.mark.large
     def test_ppo_pendulum(self):
         """Test PPO with Pendulum environment."""
         with LocalRunner(sess=self.sess) as runner:
@@ -47,7 +47,8 @@ def test_ppo_pendulum(self):
             last_avg_ret = runner.train(n_epochs=10, batch_size=2048)
             assert last_avg_ret > 40
 
-    @pytest.mark.large
+    # large marker removed to balance test jobs
+    # @pytest.mark.large
     def test_ppo_pendulum_recurrent(self):
         """Test PPO with Pendulum environment and recurrent policy."""
         with LocalRunner() as runner:
@@ -64,7 +65,8 @@ def test_ppo_pendulum_recurrent(self):
             last_avg_ret = runner.train(n_epochs=10, batch_size=2048)
             assert last_avg_ret > 40
 
-    @pytest.mark.large
+    # large marker removed to balance test jobs
+    # @pytest.mark.large
     def test_ppo_with_maximum_entropy(self):
         """Test PPO with maxium entropy method."""
         with LocalRunner(sess=self.sess) as runner:
@@ -84,7 +86,8 @@ def test_ppo_with_maximum_entropy(self):
             last_avg_ret = runner.train(n_epochs=10, batch_size=2048)
             assert last_avg_ret > 40
 
-    @pytest.mark.large
+    # large marker removed to balance test jobs
+    # @pytest.mark.large
     def test_ppo_with_neg_log_likeli_entropy_estimation_and_max(self):
         """
         Test PPO with negative log likelihood entropy estimation and max
@@ -108,7 +111,8 @@ def test_ppo_with_neg_log_likeli_entropy_estimation_and_max(self):
             last_avg_ret = runner.train(n_epochs=10, batch_size=2048)
             assert last_avg_ret > 40
 
-    @pytest.mark.large
+    # large marker removed to balance test jobs
+    # @pytest.mark.large
     def test_ppo_with_neg_log_likeli_entropy_estimation_and_regularized(self):
         """
         Test PPO with negative log likelihood entropy estimation and
@@ -132,7 +136,8 @@ def test_ppo_with_neg_log_likeli_entropy_estimation_and_regularized(self):
             last_avg_ret = runner.train(n_epochs=10, batch_size=2048)
             assert last_avg_ret > 40
 
-    @pytest.mark.large
+    # large marker removed to balance test jobs
+    # @pytest.mark.large
     def test_ppo_with_regularized_entropy(self):
         """Test PPO with regularized entropy method."""
         with LocalRunner(sess=self.sess) as runner: