Configuration
This project uses YAML configuration files in the config/ directory to manage
settings across different environments. This approach keeps configuration separate
from code, making it easy to adjust behavior without modifying Python source files.
File Layout
config/
├── config.yml # Main project settings (committed to version control)
├── secrets_template.yml # Template for credentials (committed)
└── secrets.yml # Your actual credentials (git-ignored, create from template)
Main Configuration — config.yml
The configuration file has two parts:
- Shared settings — always loaded regardless of environment (e.g.
project). - Environment-specific sections — under the
environmentskey. Add, rename, or remove environments by editing this block — no Python changes required.
project:
name: "arcgis-enterprise-clone-content"
title: "ArcGIS Enterprise Clone Content"
description: "Clone content between ArcGIS portals."
environments:
default:
logging:
level: DEBUG
data:
input: "data/raw/input_data.csv"
output: "data/processed/processed.gdb/output_data"
migration:
source_env: "source" # key in secrets.yml for the source portal
destination_env: "destination" # key in secrets.yml for the destination portal
source:
logging:
level: DEBUG
destination:
logging:
level: INFO
When the configuration is loaded, the active environment's section is deep-merged
onto the shared settings, so you only need to specify what differs per environment.
The default block provides base values that all named environments inherit.
Custom portal environment names
The available environments are introspected from the keys under
environments in config.yml. If your organisation names its ArcGIS portals
differently (e.g. prod_portal and dr_portal), add matching blocks here and
update migration.source_env and migration.destination_env accordingly.
Secrets — secrets.yml
Credentials and API keys live in a separate file that is never committed to version control.
- Copy
secrets_template.ymltosecrets.yml. - Fill in your actual values.
source:
profile: "" # ArcGIS named profile (takes precedence over url/username/password if set)
url: "https://source-arcgis-enterprise.example.com/arcgis"
username: "your_source_username"
password: "your_source_password"
destination:
profile: ""
url: "https://destination-arcgis-enterprise.example.com/arcgis"
username: "your_destination_username"
password: "your_destination_password"
Profile vs. URL authentication
If profile is set to a non-empty string, url, username, and password are
ignored. Set up a named profile via ArcGIS Pro or the ArcGIS API for Python.
Warning
secrets.yml is listed in .gitignore. Never commit real credentials.
Switching Environments
The active environment defaults to source. There are two ways to switch:
Option 1 — Environment Variable
Set the PROJECT_ENV variable before running any script or notebook:
=== "PowerShell"
```powershell
$env:PROJECT_ENV = "destination"
python -m scripts.make_data
```
=== "Bash / macOS"
```bash
export PROJECT_ENV=destination
python -m scripts.make_data
```
Option 2 — Edit the Constant
Open src/arcgis_cloning/config.py and change the default value:
ENVIRONMENT: str = os.environ.get("PROJECT_ENV", "source") # change to "destination" or a custom portal name
Using Configuration in Python
In Scripts
from arcgis_cloning.config import config, secrets, ENVIRONMENT
# dot-notation access
log_level = config.logging.level
input_path = config.data.input
# dict-style access
output_path = config["data"]["output"]
# secrets
gis_url = secrets.esri.gis_url
# check current environment
print(f"Running in {ENVIRONMENT} mode")
Legacy-style imports also work for backward compatibility:
In Notebooks
import yaml
from pathlib import Path
config_path = Path.cwd().parent / "config" / "config.yml"
with open(config_path, encoding="utf-8") as f:
cfg = yaml.safe_load(f)
# access the source environment settings
env = cfg["environments"]["source"]
print(env["logging"]["level"])
Or import the config module directly:
import sys, pathlib
sys.path.insert(0, str(pathlib.Path.cwd().parent / "src"))
from arcgis_cloning.config import config
print(config.logging.level)
API Reference
arcgis_cloning.config
Configuration loader for the project.
Reads settings from YAML configuration files in the config/ directory using
a singleton pattern so the files are parsed once and reused across modules.
The YAML config supports environment-specific sections defined under the
environments key in config.yml. A special default sub-section
provides fallback values for any key that is not overridden in a named
environment. The merge order is:
- Top-level keys in
config.yml environments.default(if present) — overrides top-level defaultsenvironments.<active-env>— overrides both of the above
Add, rename, or remove environments by editing that YAML block — no Python
changes required. Change the :pydata:ENVIRONMENT constant below — or set
the PROJECT_ENV environment variable — to select the active environment.
Usage::
from arcgis_cloning.config import config, secrets, ENVIRONMENT, load_config, load_secrets
# dot-notation access (singleton loaded for the default 'source' portal)
log_level = config.logging.level
# dict-style access
input_path = config["data"]["input"]
# Per-portal clone pattern — load source and destination explicitly:
source_cfg = load_config(environment="source")
source_sec = load_secrets()
dest_cfg = load_config(environment="destination")
dest_sec = load_secrets()
# Access portal credentials — profile takes precedence over url/username/password
# Profile (ArcGIS named credential store):
if source_sec.source.profile:
source_gis = GIS(profile=source_sec.source.profile)
else:
source_gis = GIS(source_sec.source.url, source_sec.source.username, source_sec.source.password)
if dest_sec.destination.profile:
dest_gis = GIS(profile=dest_sec.destination.profile)
else:
dest_gis = GIS(dest_sec.destination.url, dest_sec.destination.username, dest_sec.destination.password)
# check current environment
print(f"Running in {ENVIRONMENT} mode")
ENVIRONMENT = os.environ.get('PROJECT_ENV', 'source')
module-attribute
ConfigNode
Immutable, attribute-accessible wrapper around nested dictionaries.
Supports both dot-notation (cfg.logging.level) and dict-style
(cfg["logging"]["level"]) access for convenience.
Source code in src/arcgis_cloning/config.py
get(key, default=None)
to_dict()
Recursively convert back to a plain dictionary.
Source code in src/arcgis_cloning/config.py
get_available_environments(config_path=None)
Return the named environment keys defined in config.yml.
The reserved default key is excluded from the result because it is a
fallback section, not a selectable environment.
Parameters
config_path : Path or str, optional
Explicit path to a YAML file. Defaults to config/config.yml.
Returns
list[str]
Sorted list of environment keys found under the environments
section, excluding default (e.g. ["dev", "prod", "test"]).
Source code in src/arcgis_cloning/config.py
load_config(config_path=None, environment=None)
Load the main project configuration for a given environment.
Configuration is built up in three layers:
- Top-level keys (e.g.
project) — always included. environments.default— deep-merged on top of the top-level keys, providing shared fallback values for all environments.environments.<env>— deep-merged last, overriding both of the above with environment-specific values.
Any key present in default but absent from the active environment
section is inherited from default. Keys present in the active
environment always win.
Available environments are introspected from the environments key in
config.yml (excluding the reserved default key) — add or remove
named sections there to define your own.
Parameters
config_path : Path or str, optional
Explicit path to a YAML file. Defaults to config/config.yml
relative to the project root.
environment : str, optional
One of the keys under environments in config.yml.
Defaults to the module-level :pydata:ENVIRONMENT constant.
Returns
ConfigNode A recursively accessible configuration object.
Raises
ValueError
If the requested environment is not defined in config.yml.
Source code in src/arcgis_cloning/config.py
load_secrets(secrets_path=None)
Load project secrets.
Parameters
secrets_path : Path or str, optional
Explicit path to a YAML file. Defaults to config/secrets.yml
relative to the project root.
Returns
ConfigNode A recursively accessible secrets object.
Raises
FileNotFoundError
If the secrets file does not exist. Copy
config/secrets_template.yml to config/secrets.yml and
fill in your values.