Repository: audreyr/cookiecutter (python)
Issue: Expand Environment Variables in Cookiecutter Configuration File
I set my cookiecutterrc file via an environment variable, like this:

    export COOKIECUTTER_CONFIG="$XDG_CONFIG_HOME/cookiecutter/cookiecutterrc"

In my cookiecutterrc, I'd like to use those same environment variables to set paths, however they don't currently expand:

    default_context:
        full_name: "Nathan Farrar"
        email: "nathan.farrar@gmail.com"
        github_username: "nfarrar"
    cookiecutters_dir: "$XDG_CACHE_HOME/cookiecutter/template"
    replay_dir: "$XDG_CACHE_HOME/cookiecutter/replay"
    abbreviations:
        pp: https://github.com/audreyr/cookiecutter-pypackage.git
        gh: https://github.com/{0}.git
        bb: https://bitbucket.org/{0}

For example:

    $ cookiecutter pp
    $ ls ~/
    ...
    drwxr-xr-x   3 nfarrar staff  102 Feb 28 07:37 '$XDG_CACHE_HOME'
    ...
Pull request description: Expand config values
Resolve #640 

Patch:
diff --git a/cookiecutter/config.py b/cookiecutter/config.py
index 1b85ac4..fb69b47 100755
--- a/cookiecutter/config.py
+++ b/cookiecutter/config.py
@@ -31,6 +31,13 @@ DEFAULT_CONFIG = {
 }
 
 
+def _expand_path(path):
+    """Expand both environment variables and user home in the given path."""
+    path = os.path.expandvars(path)
+    path = os.path.expanduser(path)
+    return path
+
+
 def get_config(config_path):
     """
     Retrieve the config from the specified path, returning it as a config dict.
@@ -52,6 +59,12 @@ def get_config(config_path):
     config_dict = copy.copy(DEFAULT_CONFIG)
     config_dict.update(yaml_dict)
 
+    raw_replay_dir = config_dict['replay_dir']
+    config_dict['replay_dir'] = _expand_path(raw_replay_dir)
+
+    raw_cookies_dir = config_dict['cookiecutters_dir']
+    config_dict['cookiecutters_dir'] = _expand_path(raw_cookies_dir)
+
     return config_dict
 
 


Test patch:
diff --git a/tests/test-config/config-expand-user.yaml b/tests/test-config/config-expand-user.yaml
new file mode 100644
index 0000000..2b6b14e
--- /dev/null
+++ b/tests/test-config/config-expand-user.yaml
@@ -0,0 +1,2 @@
+cookiecutters_dir: "~/templates"
+replay_dir: "~/replay-files"
diff --git a/tests/test-config/config-expand-vars.yaml b/tests/test-config/config-expand-vars.yaml
new file mode 100644
index 0000000..ba56b0d
--- /dev/null
+++ b/tests/test-config/config-expand-vars.yaml
@@ -0,0 +1,2 @@
+cookiecutters_dir: "$COOKIES/templates"
+replay_dir: "$COOKIES/replay-files"
diff --git a/tests/test_get_user_config.py b/tests/test_get_user_config.py
index b923954..e60b09f 100644
--- a/tests/test_get_user_config.py
+++ b/tests/test_get_user_config.py
@@ -141,3 +141,25 @@ def test_force_default_config(mocker):
 
     assert user_config == config.DEFAULT_CONFIG
     assert not spy_get_config.called
+
+
+def test_expand_user_for_directories_in_config(monkeypatch):
+    def _expanduser(path):
+        return path.replace('~', 'Users/bob')
+    monkeypatch.setattr('os.path.expanduser', _expanduser)
+
+    config_file = 'tests/test-config/config-expand-user.yaml'
+
+    user_config = config.get_user_config(config_file)
+    assert user_config['replay_dir'] == 'Users/bob/replay-files'
+    assert user_config['cookiecutters_dir'] == 'Users/bob/templates'
+
+
+def test_expand_vars_for_directories_in_config(monkeypatch):
+    monkeypatch.setenv('COOKIES', 'Users/bob/cookies')
+
+    config_file = 'tests/test-config/config-expand-vars.yaml'
+
+    user_config = config.get_user_config(config_file)
+    assert user_config['replay_dir'] == 'Users/bob/cookies/replay-files'
+    assert user_config['cookiecutters_dir'] == 'Users/bob/cookies/templates'