From b845710e8dd24eb0fd3144c7ca9df62ab0c38aed Mon Sep 17 00:00:00 2001 From: sophia Date: Mon, 23 Dec 2024 07:24:39 -0800 Subject: [PATCH 1/5] Add namespaace and environment info to plugin context --- .../plugins/plugin_context.py | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/conda-store-server/conda_store_server/plugins/plugin_context.py b/conda-store-server/conda_store_server/plugins/plugin_context.py index 4a80e443a..e51c12839 100644 --- a/conda-store-server/conda_store_server/plugins/plugin_context.py +++ b/conda-store-server/conda_store_server/plugins/plugin_context.py @@ -9,11 +9,26 @@ class PluginContext: - """The plugin context provides some useful attributes to a hook. + """The plugin context provides some useful attributes and functions to a hook. This includes - * the variables: conda_store, log, stdout, stderr - * the functions: run_command, run + * the variables: conda_store, log, stdout, stderr, namespace, environment + * the functions: run_command + + Attributes + ---------- + conda_store : conda_store_server.conda_store + conda_store instance + log : logging.logger + logger + stdout : io.StringIO + stream to write command output to + stderr : io.StringIO + stream to write command error output to + namespace : str + namespace context the plugin is running in + environment : str + environment context the plugin is running in """ def __init__( @@ -22,6 +37,8 @@ def __init__( stdout=None, stderr=None, log_level=logging.INFO, + namespace=None, + environment=None, ): if stdout is not None and stderr is None: stderr = stdout @@ -36,6 +53,8 @@ def __init__( self.log.addHandler(logging.StreamHandler(stream=self.stdout)) self.log.setLevel(log_level) self.conda_store = conda_store + self.namespace = namespace + self.environment = environment def run_command(self, command: str, redirect_stderr: bool = True, **kwargs): """Runs command and immediately writes to logs""" From 596b7a1582bb4c64eafcf2b353c4479cccb0a5ac Mon Sep 17 00:00:00 2001 From: sophia Date: Mon, 23 Dec 2024 07:39:07 -0800 Subject: [PATCH 2/5] Add namespace and env ino to plugin context --- conda-store-server/conda_store_server/_internal/worker/build.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conda-store-server/conda_store_server/_internal/worker/build.py b/conda-store-server/conda_store_server/_internal/worker/build.py index 04b25d3a8..fe9808718 100644 --- a/conda-store-server/conda_store_server/_internal/worker/build.py +++ b/conda-store-server/conda_store_server/_internal/worker/build.py @@ -239,6 +239,8 @@ def build_conda_environment(db: Session, conda_store, build): build=build, prefix=f"plugin-{lock_backend}: ", ), + namespace=build.environment.namespace.name, + environment=build.environment.name, ), ) From e3c3dc06f15a10160a0059be93231260695c5fe5 Mon Sep 17 00:00:00 2001 From: sophia Date: Mon, 23 Dec 2024 07:41:49 -0800 Subject: [PATCH 3/5] Ensure conda_lock plugin users namespace+environment to get appropriate settings --- .../plugins/lock/conda_lock/conda_lock.py | 11 +++++--- .../plugins/plugin_context.py | 16 +++++------ .../_internal/plugins/lock/test_conda_lock.py | 27 +++++++++++++++++++ .../tests/plugins/test_plugin_context.py | 8 +++--- 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/conda-store-server/conda_store_server/_internal/plugins/lock/conda_lock/conda_lock.py b/conda-store-server/conda_store_server/_internal/plugins/lock/conda_lock/conda_lock.py index a9d222121..2bb5ae72d 100644 --- a/conda-store-server/conda_store_server/_internal/plugins/lock/conda_lock/conda_lock.py +++ b/conda-store-server/conda_store_server/_internal/plugins/lock/conda_lock/conda_lock.py @@ -17,9 +17,10 @@ class CondaLock(lock.LockPlugin): - def _conda_command(self, conda_store) -> str: - settings = conda_store.get_settings() - return settings.conda_command + def _conda_command(self, conda_store, namespace=None, environment=None) -> str: + return conda_store.get_setting( + "conda_command", namespace=namespace, environment_name=environment + ) def _conda_flags(self, conda_store) -> str: return conda_store.config.conda_flags @@ -32,7 +33,9 @@ def lock_environment( platforms: typing.List[str] = [conda_utils.conda_platform()], ) -> str: context.log.info("lock_environment entrypoint for conda-lock") - conda_command = self._conda_command(context.conda_store) + conda_command = self._conda_command( + context.conda_store, namespace=context.namespace, environment=context.environment + ) conda_flags = self._conda_flags(context.conda_store) environment_filename = pathlib.Path.cwd() / "environment.yaml" diff --git a/conda-store-server/conda_store_server/plugins/plugin_context.py b/conda-store-server/conda_store_server/plugins/plugin_context.py index e51c12839..822844dec 100644 --- a/conda-store-server/conda_store_server/plugins/plugin_context.py +++ b/conda-store-server/conda_store_server/plugins/plugin_context.py @@ -19,8 +19,8 @@ class PluginContext: ---------- conda_store : conda_store_server.conda_store conda_store instance - log : logging.logger - logger + log_level : int + logging level stdout : io.StringIO stream to write command output to stderr : io.StringIO @@ -33,12 +33,12 @@ class PluginContext: def __init__( self, - conda_store=None, - stdout=None, - stderr=None, - log_level=logging.INFO, - namespace=None, - environment=None, + conda_store, + stdout: io.StringIO | None = None, + stderr: io.StringIO | None = None, + log_level: int = logging.INFO, + namespace: str | None = None, + environment: str | None = None, ): if stdout is not None and stderr is None: stderr = stdout diff --git a/conda-store-server/tests/_internal/plugins/lock/test_conda_lock.py b/conda-store-server/tests/_internal/plugins/lock/test_conda_lock.py index f269acaa9..62000ee3d 100644 --- a/conda-store-server/tests/_internal/plugins/lock/test_conda_lock.py +++ b/conda-store-server/tests/_internal/plugins/lock/test_conda_lock.py @@ -74,6 +74,33 @@ def test_solve_lockfile_simple(conda_store, simple_specification): assert "zlib" in [pkg["name"] for pkg in lock_result["package"]] +@mock.patch("conda_store_server._internal.plugins.lock.conda_lock.conda_lock.run_lock") +def test_solve_right_conda_command(mock_run_lock, conda_store, simple_specification): + # Update conda_command settings + conda_store.set_settings( + data={"conda_command": "conda"} + ) + + # Dump dummy data to the expected lockfile output location + def run_lock_side_effect(lockfile_path, **kwargs): + with open(lockfile_path, "w") as f: + yaml.dump({"foo": "bar"}, f) + + mock_run_lock.side_effect = run_lock_side_effect + + locker = conda_lock.CondaLock() + locker.lock_environment( + context=plugin_context.PluginContext(conda_store, namespace="test", environment="one"), + spec=simple_specification, + platforms=[conda_utils.conda_platform()], + ) + + # Check that the call to `conda_lock` is correctly formed + mock_run_lock.assert_called_once() + call_args = mock_run_lock.call_args_list[0][1] + assert call_args["conda_exe"] == "conda" + + @pytest.mark.parametrize( "specification", [ diff --git a/conda-store-server/tests/plugins/test_plugin_context.py b/conda-store-server/tests/plugins/test_plugin_context.py index e3496221c..053976ea6 100644 --- a/conda-store-server/tests/plugins/test_plugin_context.py +++ b/conda-store-server/tests/plugins/test_plugin_context.py @@ -16,7 +16,7 @@ def test_run_command_no_logs(): out = io.StringIO() err = io.StringIO() - context = PluginContext(stdout=out, stderr=err, log_level=logging.ERROR) + context = PluginContext(conda_store=None, stdout=out, stderr=err, log_level=logging.ERROR) context.run_command(["echo", "testing"]) assert err.getvalue() == "" @@ -26,7 +26,7 @@ def test_run_command_no_logs(): def test_run_command_log_info(): out = io.StringIO() err = io.StringIO() - context = PluginContext(stdout=out, stderr=err, log_level=logging.INFO) + context = PluginContext(conda_store=None, stdout=out, stderr=err, log_level=logging.INFO) context.run_command(["echo", "testing"]) assert err.getvalue() == "" @@ -39,7 +39,7 @@ def test_run_command_log_info(): def test_run_command_errors(): - context = PluginContext(log_level=logging.ERROR) + context = PluginContext(conda_store=None, log_level=logging.ERROR) with pytest.raises(subprocess.CalledProcessError): context.run_command(["conda-store-server", "-thiswillreturnanonzeroexitcode"]) @@ -52,7 +52,7 @@ def test_run_command_kwargs(): """Ensure that kwargs get passed to subprocess""" out = io.StringIO() err = io.StringIO() - context = PluginContext(stdout=out, stderr=err, log_level=logging.ERROR) + context = PluginContext(conda_store=None, stdout=out, stderr=err, log_level=logging.ERROR) # set the cwd to this directory and check that this file exists dir_path = os.path.dirname(os.path.realpath(__file__)) From f6f5ab3f45c96932ef1d6793b512d4ad58fd3ec2 Mon Sep 17 00:00:00 2001 From: sophia Date: Tue, 28 Jan 2025 10:23:47 -0800 Subject: [PATCH 4/5] Extract mock lock function from tests --- .../_internal/plugins/lock/test_conda_lock.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/conda-store-server/tests/_internal/plugins/lock/test_conda_lock.py b/conda-store-server/tests/_internal/plugins/lock/test_conda_lock.py index 62000ee3d..e89feb48b 100644 --- a/conda-store-server/tests/_internal/plugins/lock/test_conda_lock.py +++ b/conda-store-server/tests/_internal/plugins/lock/test_conda_lock.py @@ -13,6 +13,15 @@ from conda_store_server.plugins import plugin_context +def run_lock_side_effect(lockfile_path, **kwargs): + """Mock method to simulate the output of running a + lock with conda-lock. + Dumps dummy data to the expected lockfile output location. + """ + with open(lockfile_path, "w") as f: + yaml.dump({"foo": "bar"}, f) + + @pytest.mark.parametrize( "specification", [ @@ -30,11 +39,6 @@ def test_solve_lockfile( ): """Test that the call to conda_lock.run_lock is formed correctly.""" - # Dump dummy data to the expected lockfile output location - def run_lock_side_effect(lockfile_path, **kwargs): - with open(lockfile_path, "w") as f: - yaml.dump({"foo": "bar"}, f) - mock_run_lock.side_effect = run_lock_side_effect platforms = [conda_utils.conda_platform()] @@ -81,11 +85,6 @@ def test_solve_right_conda_command(mock_run_lock, conda_store, simple_specificat data={"conda_command": "conda"} ) - # Dump dummy data to the expected lockfile output location - def run_lock_side_effect(lockfile_path, **kwargs): - with open(lockfile_path, "w") as f: - yaml.dump({"foo": "bar"}, f) - mock_run_lock.side_effect = run_lock_side_effect locker = conda_lock.CondaLock() From 4781eb8934be67edbb616c46330467fb8d9acec2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 18:24:58 +0000 Subject: [PATCH 5/5] [pre-commit.ci] Apply automatic pre-commit fixes --- .../_internal/plugins/lock/conda_lock/conda_lock.py | 4 +++- .../tests/_internal/plugins/lock/test_conda_lock.py | 11 +++++------ .../tests/plugins/test_plugin_context.py | 12 +++++++++--- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/conda-store-server/conda_store_server/_internal/plugins/lock/conda_lock/conda_lock.py b/conda-store-server/conda_store_server/_internal/plugins/lock/conda_lock/conda_lock.py index 2bb5ae72d..a4fb35c0a 100644 --- a/conda-store-server/conda_store_server/_internal/plugins/lock/conda_lock/conda_lock.py +++ b/conda-store-server/conda_store_server/_internal/plugins/lock/conda_lock/conda_lock.py @@ -34,7 +34,9 @@ def lock_environment( ) -> str: context.log.info("lock_environment entrypoint for conda-lock") conda_command = self._conda_command( - context.conda_store, namespace=context.namespace, environment=context.environment + context.conda_store, + namespace=context.namespace, + environment=context.environment, ) conda_flags = self._conda_flags(context.conda_store) diff --git a/conda-store-server/tests/_internal/plugins/lock/test_conda_lock.py b/conda-store-server/tests/_internal/plugins/lock/test_conda_lock.py index e89feb48b..6e00e2d6f 100644 --- a/conda-store-server/tests/_internal/plugins/lock/test_conda_lock.py +++ b/conda-store-server/tests/_internal/plugins/lock/test_conda_lock.py @@ -38,7 +38,6 @@ def test_solve_lockfile( request, ): """Test that the call to conda_lock.run_lock is formed correctly.""" - mock_run_lock.side_effect = run_lock_side_effect platforms = [conda_utils.conda_platform()] @@ -81,15 +80,15 @@ def test_solve_lockfile_simple(conda_store, simple_specification): @mock.patch("conda_store_server._internal.plugins.lock.conda_lock.conda_lock.run_lock") def test_solve_right_conda_command(mock_run_lock, conda_store, simple_specification): # Update conda_command settings - conda_store.set_settings( - data={"conda_command": "conda"} - ) - + conda_store.set_settings(data={"conda_command": "conda"}) + mock_run_lock.side_effect = run_lock_side_effect locker = conda_lock.CondaLock() locker.lock_environment( - context=plugin_context.PluginContext(conda_store, namespace="test", environment="one"), + context=plugin_context.PluginContext( + conda_store, namespace="test", environment="one" + ), spec=simple_specification, platforms=[conda_utils.conda_platform()], ) diff --git a/conda-store-server/tests/plugins/test_plugin_context.py b/conda-store-server/tests/plugins/test_plugin_context.py index 053976ea6..c4eec3d27 100644 --- a/conda-store-server/tests/plugins/test_plugin_context.py +++ b/conda-store-server/tests/plugins/test_plugin_context.py @@ -16,7 +16,9 @@ def test_run_command_no_logs(): out = io.StringIO() err = io.StringIO() - context = PluginContext(conda_store=None, stdout=out, stderr=err, log_level=logging.ERROR) + context = PluginContext( + conda_store=None, stdout=out, stderr=err, log_level=logging.ERROR + ) context.run_command(["echo", "testing"]) assert err.getvalue() == "" @@ -26,7 +28,9 @@ def test_run_command_no_logs(): def test_run_command_log_info(): out = io.StringIO() err = io.StringIO() - context = PluginContext(conda_store=None, stdout=out, stderr=err, log_level=logging.INFO) + context = PluginContext( + conda_store=None, stdout=out, stderr=err, log_level=logging.INFO + ) context.run_command(["echo", "testing"]) assert err.getvalue() == "" @@ -52,7 +56,9 @@ def test_run_command_kwargs(): """Ensure that kwargs get passed to subprocess""" out = io.StringIO() err = io.StringIO() - context = PluginContext(conda_store=None, stdout=out, stderr=err, log_level=logging.ERROR) + context = PluginContext( + conda_store=None, stdout=out, stderr=err, log_level=logging.ERROR + ) # set the cwd to this directory and check that this file exists dir_path = os.path.dirname(os.path.realpath(__file__))