Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
Adam-D-Lewis authored Jul 23, 2024
2 parents c8cdb5f + 94f7a26 commit d0c6085
Show file tree
Hide file tree
Showing 14 changed files with 536 additions and 305 deletions.
34 changes: 34 additions & 0 deletions .dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
version: 2

updates:
# auto update github actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
labels:
- "area: javascript"
- "area: dependencies 📦"
# ensure we have a nicely formatter commit message
prefix: "MAINT - "

- package-ecosystem: "npm"
directory: "/docusaurus-docs"
schedule:
interval: "monthly"
labels:
- "area: javascript"
- "area: dependencies 📦"
# ensure we have a nicely formatter commit message
prefix: "MAINT - "

- package-ecosystem: "docker"
directories:
- "/conda-store"
- "/conda-store-server"
schedule:
interval: "monthly"
labels:
- "area: dependencies 📦"
# ensure we have a nicely formatter commit message
prefix: "MAINT - "
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ repos:

- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: "v0.4.7"
rev: "v0.5.0"
hooks:
- id: ruff
exclude: "examples|tests/assets"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
import subprocess
import tempfile
import time
import typing
import uuid

Expand All @@ -27,8 +28,14 @@ def wrapper(*args, stdout=None, stderr=None, **kwargs):
# enter temporary directory
stack.enter_context(utils.chdir(tmpdir))

start_time = time.monotonic()

# run function and store result
action_context.result = f(action_context, *args, **kwargs)
action_context.log.info(
f"Action {f.__name__} completed in {time.monotonic() - start_time:.3f} s."
)

return action_context

return wrapper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ def action_solve_lockfile(
with environment_filename.open("w") as f:
json.dump(specification.dict(), f)

context.log.info(
"Note that the output of `conda config --show` displayed below only reflects "
"settings in the conda configuration file, which might be overridden by "
"variables required to be set by conda-store via the environment. Overridden "
f"settings: CONDA_FLAGS={conda_flags}"
)

# The info command can be used with either mamba or conda
logged_command(context, [conda_command, "info"])
# The config command is not supported by mamba
Expand Down
2 changes: 1 addition & 1 deletion conda-store-server/conda_store_server/_internal/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def build_cleanup(
build_active_tasks = collections.defaultdict(list)
for worker_name, tasks in active_tasks.items():
for task in tasks:
match = re.fullmatch("build-(\d+)-(.*)", str(task["id"]))
match = re.fullmatch(r"build-(\d+)-(.*)", str(task["id"]))
if match:
build_id, name = match.groups()
build_active_tasks[build_id].append(task["name"])
Expand Down
4 changes: 3 additions & 1 deletion conda-store-server/conda_store_server/_internal/orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,9 @@ class KeyValueStore(Base):
value = Column(JSON)


def new_session_factory(url="sqlite:///:memory:", reset=False, **kwargs):
def new_session_factory(
url="sqlite:///:memory:", reset=False, **kwargs
) -> sessionmaker:
engine = create_engine(
url,
# See the comment on the CustomJSONEncoder class on why this is needed
Expand Down
4 changes: 2 additions & 2 deletions conda-store-server/conda_store_server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import pydantic

from celery import Celery, group
from sqlalchemy.orm import Session
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy.pool import QueuePool
from traitlets import (
Bool,
Expand Down Expand Up @@ -376,7 +376,7 @@ def _docker_base_image(build: orm.Build):
)

@property
def session_factory(self):
def session_factory(self) -> sessionmaker:
if hasattr(self, "_session_factory"):
return self._session_factory

Expand Down
106 changes: 68 additions & 38 deletions conda-store-server/tests/test_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,32 @@
import datetime
import pathlib
import re
import subprocess
import sys
import tempfile

from unittest import mock

import pytest
import yaml
import yarl

from conda.base.context import context as conda_base_context
from constructor import construct
from fastapi.responses import RedirectResponse
from traitlets import TraitError

from conda_store_server import BuildKey, api
from conda_store_server._internal import action, conda_utils, orm, schema, server, utils
from conda_store_server._internal.action import generate_lockfile
from conda_store_server._internal.action import (
generate_constructor_installer,
generate_lockfile,
)
from conda_store_server.server.auth import DummyAuthentication


def test_action_decorator():
"""Test that the action decorator captures stdout/stderr and logs correctly."""

@action.action
def test_function(context):
print("stdout")
Expand Down Expand Up @@ -48,10 +55,13 @@ def test_function(context):
return pathlib.Path.cwd()

context = test_function()
assert (
context.stdout.getvalue()
== "stdout\nstderr\nsubprocess\nsubprocess_stdout\nsubprocess_stderr\nlog\n"

stdout = context.stdout.getvalue()
assert stdout.startswith(
"stdout\nstderr\nsubprocess\nsubprocess_stdout\nsubprocess_stderr\nlog\n"
)
assert re.search(r"Action test_function completed in \d+\.\d+ s.\n$", stdout)

assert context.stderr.getvalue() == "subprocess_stderr_no_redirect\n"
# test that action direction is not the same as outside function
assert context.result != pathlib.Path.cwd()
Expand Down Expand Up @@ -170,50 +180,70 @@ def test_solve_lockfile_multiple_platforms(conda_store, specification, request):
def test_generate_constructor_installer(
conda_store, specification_name, request, tmp_path
):
"""Test that generate_construction_installer correctly produces the files needed by `constructor`."""
specification = request.getfixturevalue(specification_name)
installer_dir = tmp_path / "installer_dir"
is_lockfile = specification_name in [
"simple_lockfile_specification",
"simple_lockfile_specification_with_pip",
]

# Creates the installer
context = action.action_generate_constructor_installer(
conda_command=conda_store.conda_command,
specification=specification,
installer_dir=installer_dir,
version="1",
is_lockfile=is_lockfile,
)
# action_generate_constructor_installer uses a temporary directory context manager
# to create and store the installer, but it usually gets deleted when the function
# exits. Here, we manually create that temporary directory, run the action,
# persisting the directory (so that we can verify the contents). Only then do we
# manually clean up afterward.
class PersistentTemporaryDirectory(tempfile.TemporaryDirectory):
def __exit__(self, exc, value, tb):
pass

# Checks that the installer was created
installer = context.result
assert installer.exists()
temp_directory = None

def tmp_dir_side_effect(*args, **kwargs):
nonlocal temp_directory
temp_directory = PersistentTemporaryDirectory(*args, **kwargs)
return temp_directory

with mock.patch.object(
generate_constructor_installer, "tempfile", wraps=tempfile
) as mock_tempfile:
mock_tempfile.TemporaryDirectory.side_effect = tmp_dir_side_effect

# Create the installer, but don't actually run `constructor` - it uses conda to solve the
# environment, which we don't need to do for the purposes of this test.
with mock.patch(
"conda_store_server._internal.action.generate_constructor_installer.logged_command"
) as mock_command:
generate_constructor_installer.action_generate_constructor_installer(
conda_command=conda_store.conda_command,
specification=specification,
installer_dir=installer_dir,
version="1",
is_lockfile=is_lockfile,
)

tmp_dir = tmp_path / "tmp"
mock_command.assert_called()

# Runs the installer
out_dir = pathlib.Path(tmp_dir) / "out"
if sys.platform == "win32":
subprocess.check_output([installer, "/S", f"/D={out_dir}"])
else:
subprocess.check_output([installer, "-b", "-p", str(out_dir)])
# First call to `constructor` is used to check that it is installed
mock_command.call_args_list[0].args[1] == ["constructor", "--help"]

# Checks the output directory
assert out_dir.exists()
lib_dir = out_dir / "lib"
if specification_name in ["simple_specification", "simple_lockfile_specification"]:
if sys.platform == "win32":
assert any(str(x).endswith("zlib.dll") for x in out_dir.iterdir())
elif sys.platform == "darwin":
assert any(str(x).endswith("libz.dylib") for x in lib_dir.iterdir())
else:
assert any(str(x).endswith("libz.so") for x in lib_dir.iterdir())
else:
# Uses rglob to not depend on the version of the python
# directory, which is where site-packages is located
flask = pathlib.Path("site-packages") / "flask"
assert any(str(x).endswith(str(flask)) for x in out_dir.rglob("*"))
# Second call is used to build the installer
call_args = mock_command.call_args_list[1].args[1]
cache_dir = pathlib.Path(call_args[3])
platform = call_args[5]
tmp_dir = pathlib.Path(call_args[6])
assert call_args[0:3] == ["constructor", "-v", "--cache-dir"]
assert str(cache_dir).endswith("pkgs")
assert call_args[4:6] == ["--platform", conda_base_context.subdir]
assert str(tmp_dir).endswith("build")

# Use some of the constructor internals to verify the action's artifacts are valid
# constructor input
info = construct.parse(str(tmp_dir / "construct.yaml"), platform)
construct.verify(info)

assert temp_directory is not None
temp_directory.cleanup()


def test_fetch_and_extract_conda_packages(tmp_path, simple_conda_lock):
Expand Down
2 changes: 0 additions & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3.8"

services:
conda-store-worker:
build:
Expand Down
Loading

0 comments on commit d0c6085

Please sign in to comment.