Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
b215749
DESIGN: sketch environment backend extraction
HaoZeke Jun 27, 2026
57d5268
TEST: tighten envmgmt matrix_pure isolation checks
HaoZeke Jun 27, 2026
7645ee4
TEST: close matrix_pure source file in isolation test
HaoZeke Jun 27, 2026
f515e4c
DESIGN: keep env extraction design out of public docs
HaoZeke Jun 27, 2026
ad4cd09
REFACTOR: ship virtualenv as the only in-tree env backend
HaoZeke Jun 27, 2026
8186230
TEST: drop conda hooks from test tools (virtualenv-only core)
HaoZeke Jun 27, 2026
78b901d
FEAT: external env plugins (asv_conda, asv_rattler, asv_uv, asv_mamba)
HaoZeke Jun 27, 2026
6c0e605
FIX: register asv_rattler without requiring py-rattler at import
HaoZeke Jun 27, 2026
44ffe9e
FIX: restore plugin_manager bootstrap; conda lock via asv_conda
HaoZeke Jun 27, 2026
4260dff
DOCS: classify env plugins as shell vs API; add asv_pixi stub
HaoZeke Jun 27, 2026
d12c41b
FEAT: drive env plugins via APIs where they exist
HaoZeke Jun 27, 2026
bcf80f7
REFACTOR: env backends are HaoZeke asv_env_* repos, not vendored
HaoZeke Jun 27, 2026
c9cd75d
FIX: point optional conda lock at asv_env_conda naming
HaoZeke Jun 27, 2026
d4d290c
FEAT: single envmgmt.discover path for environment_type
HaoZeke Jun 27, 2026
95b7e1a
FEAT: Stage 1 env discovery with additive in-tree backends
HaoZeke Jul 6, 2026
dd92b8f
REFACTOR: drop in-tree conda/rattler/uv; EP packages are the providers
HaoZeke Jul 7, 2026
f164d2f
FEAT: drop-in env extras and formal matrix layers
HaoZeke Jul 8, 2026
bb83349
MAINT: bump env extras for conda 0.2.3 and pixi 0.4.2
HaoZeke Jul 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions asv/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ def setup_arguments(cls, subparsers):

@classmethod
def run_from_args(cls, args):
from ..plugin_manager import plugin_manager
from asv.envmgmt.discover import ensure_conf_backends

conf = config.Config.load(args.config)
for plugin in conf.plugins:
plugin_manager.import_plugin(plugin)
# Same discovery path as library callers (not Command-only plugins loop).
ensure_conf_backends(conf)
return cls.run_from_conf_args(conf, args)

@classmethod
Expand Down
3 changes: 2 additions & 1 deletion asv/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ def __init__(self):
self.html_dir = "html"
self.show_commit_url = "#"
self.hash_length = 8
self.environment_type = None
# Core ships virtualenv only; other tools need external plugins.
self.environment_type = "virtualenv"
self.install_timeout = 600.0
self.default_benchmark_timeout = 60.0
self.dvcs = None
Expand Down
53 changes: 36 additions & 17 deletions asv/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ def get_environments(conf, env_specifiers, verbose=True):

if not env_specifiers:
all_environments = ()
env_specifiers = [conf.environment_type]
env_specifiers = [conf.environment_type or "virtualenv"]
if not conf.environment_type and verbose:
log.warning(
"No `environment_type` specified in asv.conf.json. "
Expand Down Expand Up @@ -365,7 +365,7 @@ def get_environments(conf, env_specifiers, verbose=True):

try:
if env_type:
cls = get_environment_class_by_name(env_type)
cls = get_environment_class_by_name(env_type, conf=conf)
else:
cls = get_environment_class(conf, python)

Expand Down Expand Up @@ -411,8 +411,8 @@ def get_environment_class(conf, python):
Python version specifier. Acceptable values depend on the
Environment plugins installed but generally are:

- 'X.Y': A Python version, in which case conda or virtualenv
will be used to create a new environment.
- 'X.Y': A Python version; the virtualenv backend creates a new
environment (unless a different plugin is configured).

- 'python' or '/usr/bin/python': Search for the given
executable on the search PATH, and use that. It is assumed
Expand All @@ -422,32 +422,40 @@ def get_environment_class(conf, python):
if python == 'same':
return ExistingEnvironment

# Ensure conf plugins + configured type are imported (library-safe).
from asv.envmgmt.discover import ensure_conf_backends

ensure_conf_backends(conf)

classes = list(util.iter_subclasses(Environment))

if conf.environment_type:
cls = get_environment_class_by_name(conf.environment_type)
# Core default is virtualenv; other backends must be supplied as plugins.
env_type = conf.environment_type or "virtualenv"
cls = get_environment_class_by_name(env_type, conf=conf)
if cls in classes:
classes.remove(cls)
classes.insert(0, cls)
else:
raise RuntimeError("Environment type must be specified")
classes.insert(0, cls)

for cls in classes:
if cls.matches_python_fallback or cls.matches(python):
return cls
raise EnvironmentUnavailable(f"No way to create environment for python='{python}'")


def get_environment_class_by_name(environment_type):
def get_environment_class_by_name(environment_type, conf=None, plugins=None):
"""
Find the environment class with the given name.

Uses the single discovery path in :mod:`asv.envmgmt.discover` so that an
installed optional backend (entry point group ``asv.environment_backends``
and/or conf ``plugins``; optional legacy ``asv_env_<type>`` if enabled) is imported when
*environment_type* is requested — including from library code without
going through ``Command``.
"""
for cls in util.iter_subclasses(Environment):
if cls.tool_name == environment_type:
return cls
tool_names = [cls.tool_name for cls in util.iter_subclasses(Environment)]
raise EnvironmentUnavailable(
f"Unknown environment type '{environment_type}'. "
f"Allowed values based on existing plugins are {tool_names}. "
from asv.envmgmt.discover import ensure_environment_backend

return ensure_environment_backend(
environment_type, conf=conf, plugins=plugins
)


Expand All @@ -472,6 +480,14 @@ class Environment:
tool_name = None
matches_python_fallback = True

# Matrix / project layer capabilities (see asv.envmgmt.matrix_layers).
# Optional backends should override these to match real solver behaviour.
matrix_install_mode = "create" # create | post | joint
supports_joint_pypi_conda_solve = False
supports_joint_pypi_solve = False
project_install_prefers_no_deps = False
requires_host_tool = None

def __init__(self, conf, python, requirements, tagged_env_vars):
"""
Get an environment for a given requirement matrix and
Expand Down Expand Up @@ -1037,6 +1053,9 @@ def save_info_file(self, path):

class ExistingEnvironment(Environment):
tool_name = "existing"
matrix_install_mode = "create"
supports_joint_pypi_solve = False
project_install_prefers_no_deps = False

def __init__(self, conf, executable, requirements, tagged_env_vars):
if executable == 'same':
Expand Down
46 changes: 46 additions & 0 deletions asv/envmgmt/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""Environment management: discovery (production) and lifecycle spike.

Discovery (:mod:`asv.envmgmt.discover`) is the host-side resolver for
``environment_type``. Matrix layers (:mod:`asv.envmgmt.matrix_layers`)
document how conf ``matrix`` constraints relate to create vs project
install. Protocol/facade modules are a longer-term lifecycle spike and
are not required for discovery.
"""

from .discover import (
ENTRY_POINT_GROUP,
ensure_conf_backends,
ensure_environment_backend,
resolve_environment_class,
)
from .identity import env_spec_fingerprint, requirements_fingerprint
from .matrix_layers import (
KNOWN_BACKEND_CAPABILITIES,
LAYER_HOST,
LAYER_MATRIX,
LAYER_PROJECT,
LAYER_RUNTIME,
backend_capabilities,
matrix_means_something_checklist,
recommend_install_command,
)
from .protocol import EnvironmentBackend

__all__ = [
"EnvironmentBackend",
"ENTRY_POINT_GROUP",
"LAYER_HOST",
"LAYER_MATRIX",
"LAYER_PROJECT",
"LAYER_RUNTIME",
"KNOWN_BACKEND_CAPABILITIES",
"backend_capabilities",
"env_spec_fingerprint",
"requirements_fingerprint",
"ensure_conf_backends",
"ensure_environment_backend",
"resolve_environment_class",
"matrix_means_something_checklist",
"recommend_install_command",
]
Loading
Loading