Imported from conda-incubator/conda-exec (
AGENTS.md). Install upstream withnpx skills add conda-incubator/conda-exec. Copyright stays with the author.
AGENTS.md -- conda-exec coding guidelines
Project structure
-
The package provides the
conda execsubcommand via a conda plugin and a standalonececommand via a console script entry point. Both dispatch to the same CLI handler. -
All modules live at the package root.
cli.pycontains parser configuration and dispatch.execute.pyhandles the run command.list.pyandclean.pyhandle--listand--cleanflags.format.pyprovides display formatting utilities. -
Other core modules handle distinct concerns:
cache.py(ephemeral environment lifecycle and cache key hashing),binaries.py(executable discovery),run.py(subprocess execution),paths.py(filesystem layout),exceptions.py(all exceptions). -
Tests mirror the source structure. Tests for
conda_exec/cache.pylive intests/test_cache.py, tests forconda_exec/cli.pylive intests/test_cli.py.
Imports
-
Use relative imports for all intra-package references (
from .cache import CacheManager,from .exceptions import SolveError). Absoluteconda_exec.*imports should only appear in tests and entry points. -
Inline (lazy) imports are reserved for performance-critical paths or optional dependencies. Acceptable cases:
plugin.pyhooks (loaded on everycondainvocation),cli.pycommand dispatch (only the chosen handler is loaded). Everywhere else, imports belong at the top of the module.
Dependencies
-
Minimize the dependency graph. Prefer stdlib or already-required packages over adding new ones.
-
Use conda's own APIs where available:
conda.common.path.BIN_DIRECTORYfor platform-correct bin directories,conda.core.prefix_data.PrefixDatafor environment metadata,conda.models.match_spec.MatchSpecfor spec parsing,conda.activatefor activation env vars. -
Pin minimum versions in
pyproject.tomldependencies (e.g.,"platformdirs >=4.0"), not exact versions.
Code structure
-
Avoid private module-level helper functions (
_foo()). If a helper is called once, inline it at the call site. If it is genuinely reused or tested independently, make it a public function with a clear name and docstring. The underscore-prefix convention creates untestable, hard-to-patch indirection without real encapsulation. -
Do not use section header comments (e.g.,
# --------------- section name ---------------). Well-named functions and clear module structure make them unnecessary. If a file needs section dividers, it should be split into separate modules instead.
Typing and linting
-
All code must be typed using modern annotations (
str | NonenotOptional[str],list[str]notList[str]). -
Use
tyfor type checking andrufffor linting and formatting. Both are configured inpyproject.toml. -
Use
from __future__ import annotationsin all modules.
Testing
-
Tests are plain
pytestfunctions. Do not group tests in classes; use module-level functions with descriptive names. -
Never use
unittest.mock,MagicMock,patch,Mock, or any othermocklibrary. Usepytestnative fixtures (tmp_path,monkeypatch,capsys,tmp_path_factory) and real fakes. Build small local classes ormonkeypatch.setattrwith recording closures when a test needs to observe calls. -
Use
pytest.mark.parametrizeextensively. When multiple test cases exercise the same logic with different inputs, consolidate them into a single parameterized test withids=[...]. Stack multiple@pytest.mark.parametrizedecorators to cross-product independent axes. -
Put shared setup in fixtures, not in repeated inline code. Shared fixtures belong in
conftest.pyat the appropriate level. -
After adding or modifying tests or production code, always run the full test suite (
pixi run -e test pytest) and bothpixi run ruff checkandpixi run ruff format --checkto verify the changes pass. Fix any lint or formatting issues before considering work done. -
Coverage is measured with
pytest-cov. Runpixi run -e test test-covto generate a coverage report.
Conda integration -- batteries included
-
Always reuse conda's built-in APIs before writing custom code. conda is a large project with many utilities. Before implementing any functionality, check whether conda already provides it:
conda.common.path.BIN_DIRECTORYfor platform-correct bin dirsconda.common.compat.on_winfor platform detectionconda.core.prefix_data.PrefixDatafor environment metadata, file listings (PrefixRecord.files), timestamps (.created,.last_modified), and size (.size())conda.models.match_spec.MatchSpecfor spec parsingconda.models.channel.Channelfor channel objectsconda.activate.PosixActivator/CmdExeActivatorfor activation env vars as dicts (.build_activate())conda.core.envs_manager.unregister_envfor env cleanupconda.gateways.disk.delete.rm_rffor safe recursive deletionconda.exceptions.CondaErroras the base for all plugin errorsconda.reporters.confirm_ynfor yes/no confirmation prompts (respectscontext.always_yesandcontext.dry_runglobally)conda.base.context.contextfor global settings likedry_run,always_yes,json
Do not reimplement platform detection, path construction, confirmation prompts, or config parsing when conda already handles it.
-
The plugin registers via
pluggyhooks (conda_subcommands) and the[project.entry-points.conda]entry point. -
Solver invocation follows the pattern from
conda_global/envs.py:context.plugin_manager.get_cached_solver_backend(),MatchSpec,Channel,solve_for_transaction().
Security
-
Never pass unsanitized user input to shell commands. Use
subprocess.runwith list arguments, never shell=True. -
Validate all file sizes before deserialization. Cap collection sizes to prevent memory exhaustion.
-
Use atomic file writes (tempfile + rename) for any state files.
-
Limit candidate output counts to prevent terminal flooding.
Performance
-
Plugin load must stay under 1ms. All heavy imports are deferred to inside hook functions.
-
Cache lookups (checking if an environment exists) must be sub-millisecond. Only stat the directory, don't load PrefixData for existence checks.
-
Solver invocation is the expensive path. Cache aggressively via hash-keyed environment directories.
Documentation
-
Docs use Sphinx with
conda-sphinx-theme,myst-parser, andsphinx-design. -
Follow the Diataxis framework: tutorials, how-to guides, reference, and explanation sections.
Releases
- Do NOT under any circumstance create tags, push tags, create GitHub releases, or publish packages without explicit user approval. "Prepare a release" means assembling changelogs, verifying infrastructure, and reporting what steps the user needs to take. It does not mean executing those steps.
Lockfile maintenance
- After any change to
pyproject.tomlthat affects pixi metadata (dependencies, features, tasks, or workspace settings), runpixi lockand commit the updatedpixi.lockalongside thepyproject.tomlchange.