Programmatic .condarc File API#
This guide explains how to programmatically read and write conda configuration files (.condarc)
using the ConfigurationFile class. This is useful for tools that need to modify conda settings
without shelling out to conda config commands.
Overview#
The conda.cli.condarc.ConfigurationFile class provides a high-level interface for reading,
modifying, and writing conda configuration files (.condarc). It handles:
Configuration file validation
Type checking and conversion
Parameter existence checking
Atomic file operations via context manager
Support for sequence, map, and primitive parameters
Basic Usage#
Reading Configuration#
from conda.cli.condarc import ConfigurationFile
# Read user's .condarc file
config = ConfigurationFile.from_user_condarc()
# Access the configuration content
print(config.content)
# {'channels': ['defaults', 'conda-forge'], 'auto_update_conda': False}
# Get a specific key
key, value = config.get_key("channels")
print(f"{key}: {value}")
# channels: ['defaults', 'conda-forge']
Writing Configuration#
from conda.cli.condarc import ConfigurationFile
# Create a configuration file instance
config = ConfigurationFile.from_user_condarc()
# Set a primitive parameter
config.set_key("auto_update_conda", False)
# Add to a sequence parameter
config.add("channels", "conda-forge", prepend=True)
# Set a map parameter
config.set_key("proxy_servers.http", "http://proxy.example.com")
# Write changes to file
config.write()
Using Context Manager#
For atomic operations, use the context manager pattern:
from conda.cli.condarc import ConfigurationFile
# Changes are automatically written on successful exit
with ConfigurationFile.from_user_condarc() as config:
config.set_key("channels", ["conda-forge", "defaults"])
config.set_key("auto_update_conda", False)
# File is written here automatically
If an exception occurs within the context, changes are not written:
try:
with ConfigurationFile.from_user_condarc() as config:
config.set_key("channels", ["conda-forge"])
raise ValueError("Something went wrong")
except ValueError:
pass
# File was NOT modified because of the exception
Factory Methods#
The ConfigurationFile class provides several factory methods for common configuration file locations:
User Configuration#
from conda.cli.condarc import ConfigurationFile
# User's .condarc file (typically ~/.condarc)
config = ConfigurationFile.from_user_condarc()
System Configuration#
from conda.cli.condarc import ConfigurationFile
# System-wide .condarc file
config = ConfigurationFile.from_system_condarc()
Environment Configuration#
from conda.cli.condarc import ConfigurationFile
# Environment-specific .condarc file at {prefix}/.condarc
config = ConfigurationFile.from_env_condarc(prefix="/path/to/env")
# Or use CONDA_PREFIX environment variable
config = ConfigurationFile.from_env_condarc()
Custom Path#
from pathlib import Path
from conda.cli.condarc import ConfigurationFile
# Custom configuration file path
config = ConfigurationFile(path=Path("/custom/path/.condarc"))
Parameter Types#
Conda configuration parameters come in three types, and each supports different operations:
Primitive Parameters#
Single scalar values (strings, numbers, booleans):
config.set_key("auto_update_conda", False)
config.set_key("channel_priority", "strict")
config.set_key("rollback_enabled", True)
Sequence Parameters#
Lists of values:
# Add to end of list
config.add("channels", "conda-forge", prepend=False)
# Add to beginning of list
config.add("channels", "defaults", prepend=True)
# Remove from list
config.remove_item("channels", "conda-forge")
Map Parameters#
Dictionaries with nested values:
# Set a map entry
config.set_key("proxy_servers.http", "http://proxy.example.com")
config.set_key("proxy_servers.https", "https://proxy.example.com")
# Add to a nested sequence within a map
config.add("conda_build.config_file", "/path/to/config.yaml")
Key Validation#
The ConfigurationFile class validates keys against the current conda context:
from conda.cli.condarc import ConfigurationFile
config = ConfigurationFile.from_user_condarc()
# Check if a key exists
if config.key_exists("channels"):
print("channels is a valid parameter")
# Attempting to set an invalid key raises an error
try:
config.set_key("invalid_key", "value")
except Exception as e:
print(f"Error: {e}")
# Error: CondaKeyError: 'invalid_key': unknown parameter
Handling Missing Keys#
When getting a key that doesn’t exist, the method returns a sentinel value:
from conda.cli.condarc import ConfigurationFile, MISSING
config = ConfigurationFile(content={})
key, value = config.get_key("undefined_key")
if value is MISSING:
print(f"Key '{key}' not found in config")
Working with Plugin Configuration#
Plugin parameters use a plugins. prefix:
# Set a plugin-specific parameter
config.set_key("plugins.custom_solver.enabled", True)
# Add to a plugin sequence parameter
config.add("plugins.custom_reporters.backends", "custom_backend")
Advanced Usage#
Custom Context#
You can provide a custom context instance for testing or specialized configurations:
from conda.base.context import Context
from conda.cli.condarc import ConfigurationFile
# Create a custom context
custom_context = Context()
# Use it with ConfigurationFile
config = ConfigurationFile(path="/path/to/config", context=custom_context)
Warning Handlers#
Customize how warnings are reported:
warnings = []
def collect_warnings(msg):
warnings.append(msg)
config = ConfigurationFile(path="/path/to/config", warning_handler=collect_warnings)
config.add("channels", "defaults", prepend=False)
# If "defaults" already exists, a warning is collected
print(warnings)
Manual Read/Write Control#
For more control over when files are read or written:
from conda.cli.condarc import ConfigurationFile
config = ConfigurationFile(path="/path/to/.condarc")
# Explicitly read
config.read()
# Make changes
config.content["channels"] = ["conda-forge"]
# Explicitly write
config.write()
# Or write to a different location
config.write(path="/different/path/.condarc")
Working with Content Directly#
The content property provides direct access to the underlying configuration dictionary:
config = ConfigurationFile.from_user_condarc()
# Access content
current_channels = config.content.get("channels", [])
# Modify content directly (use with caution)
config.content["channels"] = ["conda-forge", "defaults"]
config.write()
Note: Direct content manipulation bypasses validation. Use the provided methods
(set_key, add, remove_item, etc.) for safer operations.
Complete Example#
Here’s a complete example that demonstrates multiple operations:
from conda.cli.condarc import ConfigurationFile
# Use context manager for atomic operations
with ConfigurationFile.from_user_condarc() as config:
# Set primitive parameters
config.set_key("auto_update_conda", False)
config.set_key("channel_priority", "strict")
# Configure channels
config.set_key("channels", []) # Clear existing
config.add("channels", "conda-forge", prepend=False)
config.add("channels", "defaults", prepend=False)
# Set proxy servers
config.set_key("proxy_servers.http", "http://proxy.example.com:8080")
config.set_key("proxy_servers.https", "https://proxy.example.com:8080")
# Configure conda-build
config.add("conda_build.config_file", "/path/to/conda_build_config.yaml")
# Print current configuration
print("Current configuration:")
for key, value in config.content.items():
print(f" {key}: {value}")
# File is automatically written when exiting the context manager
print("Configuration saved!")
Migration Guide#
If you were previously using the private functions from conda.cli.main_config, here’s how to migrate:
Before (deprecated)#
from conda.cli.main_config import (
_read_rc,
_write_rc,
_set_key,
_get_key,
_remove_key,
)
# Old way
rc_config = _read_rc("/path/to/.condarc")
_set_key("auto_update_conda", False, rc_config)
_write_rc("/path/to/.condarc", rc_config)
After (recommended)#
from conda.cli.condarc import ConfigurationFile
# New way
config = ConfigurationFile(path="/path/to/.condarc")
config.set_key("auto_update_conda", False)
config.write()
# Or even simpler with context manager
with ConfigurationFile(path="/path/to/.condarc") as config:
config.set_key("auto_update_conda", False)
Error Handling#
The ConfigurationFile class raises specific exceptions for different error conditions:
from conda.cli.condarc import ConfigurationFile
from conda.exceptions import CondaKeyError, CondaValueError, CouldntParseError
config = ConfigurationFile.from_user_condarc()
try:
# Unknown parameter
config.set_key("unknown_param", "value")
except CondaKeyError as e:
print(f"Invalid key: {e}")
try:
# Invalid operation for parameter type
config.set_key("channels", "not-a-list")
except CondaKeyError as e:
print(f"Invalid operation: {e}")
try:
# Adding to a non-sequence parameter
config.add("auto_update_conda", "value")
except CondaValueError as e:
print(f"Type error: {e}")
Thread Safety#
The ConfigurationFile class is not thread-safe. If you need to access configuration from multiple
threads, consider using locks or creating separate instances for each thread.
Best Practices#
Use context managers: For atomic operations, always use the context manager pattern to ensure changes are only written on success.
Validate keys: Use
key_exists()to check parameter validity before attempting operations.Use factory methods: Prefer
from_user_condarc(),from_system_condarc(), andfrom_env_condarc()over manual path construction.Handle missing keys: Always check for
MISSINGwhen usingget_key()to handle undefined parameters gracefully.Avoid direct content manipulation: Use the provided methods (
set_key,add, etc.) instead of modifyingcontentdirectly to ensure proper validation.Custom warning handlers: For library code, provide custom warning handlers to integrate with your logging system.
See Also#
conda config and context - Understanding the conda context object
Configuration - User-facing configuration documentation
cli - CLI API documentation