From ff2720c35eddb0bd72f047be3eed2cb337361437 Mon Sep 17 00:00:00 2001 From: Diogo de Campos Date: Tue, 2 Jun 2015 16:44:42 -0300 Subject: [PATCH 1/5] Fix CONDA_ENV_PATH See: https://github.com/conda/conda-env/issues/82 --- bin/activate | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/activate b/bin/activate index 6bf6bd8..aab911e 100755 --- a/bin/activate +++ b/bin/activate @@ -67,7 +67,9 @@ if (( $? == 0 )); then export CONDA_DEFAULT_ENV="$@" fi - export CONDA_ENV_PATH=$(get_dirname $_THIS_DIR) + # Get first path and convert it to absolute + ENV_BIN_DIR="$(echo $(echo $PATH | awk -F ':' '{print $1}'))" + export CONDA_ENV_PATH="$(get_dirname "$ENV_BIN_DIR")" if (( $("$_THIS_DIR/conda" ..changeps1) )); then CONDA_OLD_PS1="$PS1" From 812b203c1a77ecb1295b8e71a468cc51365aa4e9 Mon Sep 17 00:00:00 2001 From: Diogo de Campos Date: Tue, 2 Jun 2015 08:49:40 +0000 Subject: [PATCH 2/5] Added conda_env.{specs,utils} to setup.py packages --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index b5d740a..b7fa258 100755 --- a/setup.py +++ b/setup.py @@ -45,6 +45,8 @@ 'conda_env', 'conda_env.cli', 'conda_env.installers', + 'conda_env.specs', + 'conda_env.utils', ], scripts=[ 'bin/conda-env', From 7ebfc6aaf67971cc1881d147fdaf243b50050d8c Mon Sep 17 00:00:00 2001 From: Diogo de Campos Date: Tue, 2 Jun 2015 13:46:40 -0300 Subject: [PATCH 3/5] Add support for environment variables and aliases If an 'environment' or 'aliases' tag is found in environment.yml, 'conda-env create' will create activate/deactivate scripts that configure the environment with those values when activated, and remove them when deactivated. This is done by putting two simple '.sh/.bat' files in $PREFIX/etc/conda/(de)?activate.d These files then evalute the output from a python script that does the actual handling of adding/removing environment variables and aliases. --- bin/activate | 3 +- bin/deactivate | 3 +- conda_env/cli/main_create.py | 46 ++++++++++++- conda_env/env.py | 13 ++-- conda_env/print_env.py | 114 +++++++++++++++++++++++++++++++ tests/test_env.py | 1 - tests/test_main_create.py | 83 +++++++++++++++++++++++ tests/test_print_env.py | 126 +++++++++++++++++++++++++++++++++++ 8 files changed, 378 insertions(+), 11 deletions(-) create mode 100644 conda_env/print_env.py create mode 100644 tests/test_main_create.py create mode 100644 tests/test_print_env.py diff --git a/bin/activate b/bin/activate index aab911e..d988e99 100755 --- a/bin/activate +++ b/bin/activate @@ -29,8 +29,7 @@ get_dirname() { } run_scripts() { - _PREFIX="$(echo $(echo $PATH | awk -F ':' '{print $1}')/..)" - _CONDA_D="${_PREFIX}/etc/conda/$1.d" + _CONDA_D="${CONDA_ENV_PATH}/etc/conda/$1.d" if [[ -d $_CONDA_D ]]; then for f in $(find $_CONDA_D -name "*.sh"); do source $f; done fi diff --git a/bin/deactivate b/bin/deactivate index 8e45608..7fc389a 100755 --- a/bin/deactivate +++ b/bin/deactivate @@ -36,8 +36,7 @@ get_dirname() { } run_scripts() { - _PREFIX="$(echo $(echo $PATH | awk -F ':' '{print $1}')/..)" - _CONDA_D="${_PREFIX}/etc/conda/$1.d" + _CONDA_D="${CONDA_ENV_PATH}/etc/conda/$1.d" if [[ -d $_CONDA_D ]]; then for f in $(find $_CONDA_D -name "*.sh"); do source $f; done fi diff --git a/conda_env/cli/main_create.py b/conda_env/cli/main_create.py index 46d749c..3a7f403 100644 --- a/conda_env/cli/main_create.py +++ b/conda_env/cli/main_create.py @@ -62,7 +62,6 @@ def configure_parser(sub_parsers): def execute(args, parser): - name = None if args.old_name: print("--name is deprecated. Use the following command instead:\n" @@ -103,6 +102,51 @@ def execute(args, parser): ) return -1 + write_activate_deactivate(env, prefix) + touch_nonadmin(prefix) if not args.json: cli_install.print_activate(args.name if args.name else prefix) + + +def write_activate_deactivate(env, prefix): + '''Write activate/deactivate environment variable/aliases scripts''' + if not env.environment and not env.aliases: + return + + # Create directories + conda_dir = os.path.join(prefix, 'etc', 'conda') + activate_dir = os.path.join(conda_dir, 'activate.d') + deactivate_dir = os.path.join(conda_dir, 'deactivate.d') + for directory in [conda_dir, activate_dir, deactivate_dir]: + if not os.path.exists(directory): + os.makedirs(directory) + + # Copy print_env.py + import shutil + shutil.copyfile( + os.path.join(os.path.dirname(__file__), '..', 'print_env.py'), + os.path.join(conda_dir, 'print_env.py'), + ) + + # Create activate and deactivate scripts + if sys.platform == 'win32': + ext = '.bat' + source = 'call' + rm = 'del' + else: + ext = '.sh' + source = 'source' + rm = 'rm' + + with open(os.path.join(activate_dir, '_activate' + ext), 'w') as activate: + activate.write('python "%s" activate "%s" "%s" > _tmp_activate%s\n' % \ + (os.path.join(conda_dir, 'print_env.py'), repr(env.environment), repr(env.aliases), ext)) + activate.write(source + ' _tmp_activate%s\n' % ext) + activate.write(rm + ' _tmp_activate%s\n' % ext) + + with open(os.path.join(deactivate_dir, '_deactivate' + ext), 'w') as deactivate: + deactivate.write('python "%s" deactivate "%s" "%s" > _tmp_deactivate%s\n' % \ + (os.path.join(conda_dir, 'print_env.py'), repr(env.environment), repr(env.aliases), ext)) + deactivate.write(source + ' _tmp_deactivate%s\n' % ext) + deactivate.write(rm + ' _tmp_deactivate%s\n' % ext) diff --git a/conda_env/env.py b/conda_env/env.py index d916fbf..e59bcb9 100644 --- a/conda_env/env.py +++ b/conda_env/env.py @@ -89,14 +89,13 @@ def add(self, package_name): class Environment(object): def __init__(self, name=None, filename=None, channels=None, - dependencies=None): + dependencies=None, environment=None, aliases=None): self.name = name self.filename = filename self.dependencies = Dependencies(dependencies) - - if channels is None: - channels = [] - self.channels = channels + self.channels = channels or [] + self.environment = environment or {} + self.aliases = aliases or {} def to_dict(self): d = yaml.dict([('name', self.name)]) @@ -104,6 +103,10 @@ def to_dict(self): d['channels'] = self.channels if self.dependencies: d['dependencies'] = self.dependencies.raw + if self.environment: + d['environment'] = self.environment + if self.aliases: + d['aliases'] = self.aliases return d def to_yaml(self, stream=None): diff --git a/conda_env/print_env.py b/conda_env/print_env.py new file mode 100644 index 0000000..0a6306b --- /dev/null +++ b/conda_env/print_env.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +''' +Supports: + * bash + * cmd.exe (windows default shell) + * tcc.exe (windows https://jpsoft.com/take-command-windows-scripting.html) +''' +from __future__ import print_function +import os + + +def print_env(action, environment=[], aliases={}): + # Determine shell + shell = os.environ.get('SHELL', os.environ.get('COMSPEC')) + if shell is None: + raise RuntimeError('Could not determine shell from environment variables {SHELL, COMSPEC}') + shell = os.path.basename(shell).lower() + pathsep = os.pathsep + + # Join duplicated environment variables + env = {} + for env_dict in environment: + for k, v in env_dict.items(): + if isinstance(v, list): + env.setdefault(k, []) + env[k] += v + else: + env[k] = v + + # Create environment configuration functions + if shell == 'bash': + def Export(name, value): + if isinstance(value, list): + value = pathsep.join(value) + return 'export %(name)s=%(value)s%(pathsep)s$%(name)s\n' % locals() + else: + return 'export %(name)s=%(value)s\n' % locals() + def Unset(name): + return 'unset %(name)s\n' % locals() + def Alias(name, value): + return 'alias %(name)s="%(value)s"\n' % locals() + def Unalias(name): + return '[ `alias | grep %(name)s= | wc -l` != 0 ] && unalias %(name)s\n' % locals() + + + elif shell == 'cmd.exe': + def Export(name, value): + if isinstance(value, list): + value = pathsep.join(value) + return 'set %(name)s=%(value)s%(pathsep)s%%%(name)s%%\n' % locals() + else: + return 'set %(name)s=%(value)s\n' % locals() + def Unset(name): + return 'set %(name)s=\n' % locals() + def Alias(name, value): + return 'doskey %(name)s=%(value)s\n' % locals() + def Unalias(name): + return 'doskey %(name)s=\n' % locals() + + elif shell == 'tcc.exe': + def Export(name, value): + if isinstance(value, list): + value = pathsep.join(value) + return 'set %(name)s=%(value)s%(pathsep)s%%%(name)s%%\n' % locals() + else: + return 'set %(name)s=%(value)s\n' % locals() + def Unset(name): + return 'set %(name)s=\n' % locals() + def Alias(name, value): + return 'alias %(name)s %(value)s\n' % locals() + def Unalias(name): + return 'unalias %(name)s\n' % locals() + + # Activate/Deactivate + if action == 'activate': + s = '' + for k, v in sorted(env.items()): + s += Export(k, v) + for k, v in sorted(aliases.items()): + s += Alias(k, v) + return s + + elif action == 'deactivate': + s = '' + for k, v in sorted(env.items()): + if k not in os.environ: + continue + + if isinstance(v, list): + current_value = os.environ[k].split(os.pathsep) + current_value = [c for c in current_value if c != ''] + for path in v: + if path in current_value: current_value.remove(path) + if len(current_value) == 0: + s += Unset(k) + else: + s += Export(k, os.pathsep.join(current_value)) + else: + s += Unset(k) + + for alias in sorted(aliases): + s += Unalias(alias) + return s + + +if __name__ == '__main__': + import sys + + action = sys.argv[1] + environment = eval(sys.argv[2]) + aliases = eval(sys.argv[3]) + + s = print_env(action, environment, aliases) + print(s) diff --git a/tests/test_env.py b/tests/test_env.py index 3ad8910..deefd68 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -1,7 +1,6 @@ from collections import OrderedDict import os import random -import textwrap import unittest import yaml diff --git a/tests/test_main_create.py b/tests/test_main_create.py new file mode 100644 index 0000000..948f758 --- /dev/null +++ b/tests/test_main_create.py @@ -0,0 +1,83 @@ +import os +import shutil +import sys +import textwrap +import unittest + +from conda_env import env +from conda_env.cli.main_create import write_activate_deactivate + +from . import utils + + +class activate_deactivate_TestCase(unittest.TestCase): + _ENV = env.Environment(environment=[{'FOO' : 'BAR'}], aliases={'my_ls' : 'ls -la'}) + _CONDA_DIR = utils.support_file('conda') + _PREFIX = os.path.join(_CONDA_DIR, 'envs', 'test_write_activate_deactivate') + _OBTAINED_PRINT_ENV = os.path.join(_PREFIX, 'etc', 'conda', 'print_env.py') + _EXPECTED_PRINT_ENV = os.path.join( + os.path.dirname(__file__), + '..', + 'conda_env', + 'print_env.py' + ) + + def test_write_activate_deactivate_unix(self): + old_platform = sys.platform + sys.platform = 'linux2' + try: + write_activate_deactivate(self._ENV, self._PREFIX) + + with open(os.path.join(self._PREFIX, 'etc', 'conda', 'activate.d', '_activate.sh')) as activate: + self.assertEqual(activate.read(), textwrap.dedent( + ''' + python "%s" activate "[{\'FOO\': \'BAR\'}]" "{\'my_ls\': \'ls -la\'}" > _tmp_activate.sh + source _tmp_activate.sh + rm _tmp_activate.sh + ''' + ).lstrip() % self._OBTAINED_PRINT_ENV) + + with open(os.path.join(self._PREFIX, 'etc', 'conda', 'deactivate.d', '_deactivate.sh')) as deactivate: + self.assertEqual(deactivate.read(), textwrap.dedent( + ''' + python "%s" deactivate "[{\'FOO\': \'BAR\'}]" "{\'my_ls\': \'ls -la\'}" > _tmp_deactivate.sh + source _tmp_deactivate.sh + rm _tmp_deactivate.sh + ''' + ).lstrip() % self._OBTAINED_PRINT_ENV) + + with open(self._OBTAINED_PRINT_ENV) as obtained_print_env: + with open(self._EXPECTED_PRINT_ENV) as expected_print_env: + self.assertEqual(obtained_print_env.read(), expected_print_env.read()) + finally: + sys.platform = old_platform + shutil.rmtree(self._CONDA_DIR) + + + def test_write_activate_deactivate_win(self): + try: + write_activate_deactivate(self._ENV, self._PREFIX) + + with open(os.path.join(self._PREFIX, 'etc', 'conda', 'activate.d', '_activate.bat')) as activate: + self.assertEqual(activate.read(), textwrap.dedent( + ''' + python "%s" activate "[{\'FOO\': \'BAR\'}]" "{\'my_ls\': \'ls -la\'}" > _tmp_activate.bat + call _tmp_activate.bat + del _tmp_activate.bat + ''' + ).lstrip() % self._OBTAINED_PRINT_ENV) + + with open(os.path.join(self._PREFIX, 'etc', 'conda', 'deactivate.d', '_deactivate.bat')) as deactivate: + self.assertEqual(deactivate.read(), textwrap.dedent( + ''' + python "%s" deactivate "[{\'FOO\': \'BAR\'}]" "{\'my_ls\': \'ls -la\'}" > _tmp_deactivate.bat + call _tmp_deactivate.bat + del _tmp_deactivate.bat + ''' + ).lstrip() % self._OBTAINED_PRINT_ENV) + + with open(self._OBTAINED_PRINT_ENV) as obtained_print_env: + with open(self._EXPECTED_PRINT_ENV) as expected_print_env: + self.assertEqual(obtained_print_env.read(), expected_print_env.read()) + finally: + shutil.rmtree(self._CONDA_DIR) diff --git a/tests/test_print_env.py b/tests/test_print_env.py new file mode 100644 index 0000000..78f27f6 --- /dev/null +++ b/tests/test_print_env.py @@ -0,0 +1,126 @@ +from conda_env.print_env import print_env +import os +import textwrap +import unittest + + +class EnvironmentAndAliasesTestCase(unittest.TestCase): + + ENVIRONMENT = [ + {'PATH' : ['mypath1', 'mypath2']}, + {'PATH' : ['mypath3']}, + {'ANY_LIST_REALLY' : ['something1', 'something2']}, + {'SINGLE_VAR' : 'single_value'}, + {'SINGLE_INT' : 200}, + ] + ALIASES = { + 'my_ls' : 'ls -la' + } + + def test_environment_and_aliases_bash(self): + old_environ = os.environ.copy() + old_pathsep = os.pathsep + + try: + os.environ['SHELL'] = '/bin/bash' + os.pathsep = ':' + + activate = print_env('activate', self.ENVIRONMENT, self.ALIASES) + assert activate == textwrap.dedent( + ''' + export ANY_LIST_REALLY=something1:something2:$ANY_LIST_REALLY + export PATH=mypath1:mypath2:mypath3:$PATH + export SINGLE_INT=200 + export SINGLE_VAR=single_value + alias my_ls="ls -la" + ''' + ).lstrip() + + os.environ['PATH'] = '/usr/bin:mypath1:mypath2:mypath3:/usr/local/bin' + os.environ['SINGLE_VAR'] = 'single_value' + os.environ['ANY_LIST_REALLY'] = 'something1:something2:' + deactivate = print_env('deactivate', self.ENVIRONMENT, self.ALIASES) + assert deactivate == textwrap.dedent( + ''' + unset ANY_LIST_REALLY + export PATH=/usr/bin:/usr/local/bin + unset SINGLE_VAR + [ `alias | grep my_ls= | wc -l` != 0 ] && unalias my_ls + ''' + ).lstrip() + finally: + os.environ.clear() + os.environ.update(old_environ) + os.pathsep = old_pathsep + + def test_environment_and_aliases_cmd(self): + old_environ = os.environ.copy() + old_pathsep = os.pathsep + + try: + os.environ['SHELL'] = 'C:\\Windows\\system32\\cmd.exe' + os.pathsep = ';' + + activate = print_env('activate', self.ENVIRONMENT, self.ALIASES) + assert activate == textwrap.dedent( + ''' + set ANY_LIST_REALLY=something1;something2;%ANY_LIST_REALLY% + set PATH=mypath1;mypath2;mypath3;%PATH% + set SINGLE_INT=200 + set SINGLE_VAR=single_value + doskey my_ls=ls -la + ''' + ).lstrip() + + os.environ['PATH'] = 'C:\\bin;mypath1;mypath2;mypath3;C:\\Users\\me\\bin' + os.environ['SINGLE_VAR'] = 'single_value' + os.environ['ANY_LIST_REALLY'] = 'something1;something2;' + deactivate = print_env('deactivate', self.ENVIRONMENT, self.ALIASES) + assert deactivate == textwrap.dedent( + ''' + set ANY_LIST_REALLY= + set PATH=C:\\bin;C:\\Users\\me\\bin + set SINGLE_VAR= + doskey my_ls= + ''' + ).lstrip() + finally: + os.environ.clear() + os.environ.update(old_environ) + os.pathsep = old_pathsep + + def test_environment_and_aliases_tcc(self): + old_environ = os.environ.copy() + old_pathsep = os.pathsep + + try: + os.environ['SHELL'] = 'C:\\Program Files\\tcmd\\TCC.EXE' + os.pathsep = ';' + + activate = print_env('activate', self.ENVIRONMENT, self.ALIASES) + assert activate == textwrap.dedent( + ''' + set ANY_LIST_REALLY=something1;something2;%ANY_LIST_REALLY% + set PATH=mypath1;mypath2;mypath3;%PATH% + set SINGLE_INT=200 + set SINGLE_VAR=single_value + alias my_ls ls -la + ''' + ).lstrip() + + os.environ['PATH'] = 'C:\\bin;mypath1;mypath2;mypath3;C:\\Users\\me\\bin' + os.environ['SINGLE_VAR'] = 'single_value' + os.environ['ANY_LIST_REALLY'] = 'something1;something2;' + deactivate = print_env('deactivate', self.ENVIRONMENT, self.ALIASES) + assert deactivate == textwrap.dedent( + ''' + set ANY_LIST_REALLY= + set PATH=C:\\bin;C:\\Users\\me\\bin + set SINGLE_VAR= + unalias my_ls + ''' + ).lstrip() + finally: + os.environ.clear() + os.environ.update(old_environ) + os.pathsep = old_pathsep From 8fc7fd8e345a01b86c0dd3901e1c2f5b142a0f80 Mon Sep 17 00:00:00 2001 From: Diogo de Campos Date: Wed, 3 Jun 2015 13:42:19 -0300 Subject: [PATCH 4/5] Add support for jinja2 evaluation of environment.yml This is simlar to what 'conda_build' does. --- conda_env/env.py | 30 +++++++++++++++++++++++++++++- conda_env/exceptions.py | 9 +++++++++ conda_env/yaml.py | 1 + tests/support/with-jinja.yml | 9 +++++++++ tests/test_env.py | 5 +++++ tests/utils/__init__.py | 2 +- 6 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 tests/support/with-jinja.yml diff --git a/conda_env/env.py b/conda_env/env.py index e59bcb9..773c771 100644 --- a/conda_env/env.py +++ b/conda_env/env.py @@ -46,9 +46,37 @@ def from_environment(name, prefix): return Environment(name=name, dependencies=dependencies) +# TODO: This is duplicated from conda_build. Could yaml parsing from both libraries +# be merged instead of duplicated? This could include jinja2 and "# [unix]" flags. +def render_jinja(content, **kwargs): + try: + import jinja2 + except ImportError: + return content + + # Add {{ root }} to render dict + if 'filename' in kwargs: + kwargs['root'] = os.path.dirname(os.path.abspath(kwargs['filename'])) + + # Add {{ os }} to render dict + kwargs['os'] = os + + return jinja2.Template(content).render(**kwargs) + + def from_yaml(yamlstr, **kwargs): """Load and return a ``Environment`` from a given ``yaml string``""" - data = yaml.load(yamlstr) + yamlstr = render_jinja(yamlstr, **kwargs) + + try: + data = yaml.load(yamlstr) + except yaml.parser.ParserError: + try: + import jinja2 + except ImportError: + raise exceptions.UnableToParseMissingJinja2() + raise + if kwargs is not None: for key, value in kwargs.items(): data[key] = value diff --git a/conda_env/exceptions.py b/conda_env/exceptions.py index a3db35c..a2f316e 100644 --- a/conda_env/exceptions.py +++ b/conda_env/exceptions.py @@ -49,3 +49,12 @@ class InvalidLoader(Exception): def __init__(self, name): msg = 'Unable to load installer for {}'.format(name) super(InvalidLoader, self).__init__(msg) + + +# TODO: This is copied from conda_build. Could yaml parsing from both libraries +# be merged instead of duplicated? This could include jinja2 and "# [unix]" flags. +class UnableToParseMissingJinja2(CondaEnvRuntimeError): + def __init__(self, *args, **kwargs): + msg = 'It appears you are missing jinja2. Please install that ' + \ + 'package, then attempt to create the environment.' + super(UnableToParseMissingJinja2, self).__init__(msg, *args, **kwargs) diff --git a/conda_env/yaml.py b/conda_env/yaml.py index 74a462f..9eee8a7 100644 --- a/conda_env/yaml.py +++ b/conda_env/yaml.py @@ -23,4 +23,5 @@ def represent_ordereddict(dumper, data): dump = yaml.dump load = yaml.load +parser = yaml.parser dict = OrderedDict diff --git a/tests/support/with-jinja.yml b/tests/support/with-jinja.yml new file mode 100644 index 0000000..e5cab87 --- /dev/null +++ b/tests/support/with-jinja.yml @@ -0,0 +1,9 @@ +name: with_jinja + +dependencies: +{% for i in ['xunit', 'coverage','mock'] %} + - pytest-{{ i }} +{% endfor %} + +environment: + PYTHON_DIR: {{ os.path.join(root, 'python') }} \ No newline at end of file diff --git a/tests/test_env.py b/tests/test_env.py index deefd68..ff62870 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -42,6 +42,11 @@ def test_with_pip(self): self.assert_('foo' in e.dependencies['pip']) self.assert_('baz' in e.dependencies['pip']) + def test_with_jinja(self): + e = env.from_file(utils.support_file('with-jinja.yml')) + self.assertEqual(e.dependencies.raw, ['pytest-xunit', 'pytest-coverage', 'pytest-mock']) + self.assertEqual(e.environment['PYTHON_DIR'], os.path.abspath(utils.support_file('python'))) + class EnvironmentTestCase(unittest.TestCase): def test_has_empty_filename_by_default(self): diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index 108e59e..efc71aa 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -2,4 +2,4 @@ def support_file(filename): - return os.path.join(os.path.dirname(__file__), '../support', filename) + return os.path.join(os.path.dirname(__file__), '..', 'support', filename) From 4d972b4fd18ebc686a1e7649e28ba077e926259c Mon Sep 17 00:00:00 2001 From: Diogo de Campos Date: Wed, 3 Jun 2015 14:21:20 -0300 Subject: [PATCH 5/5] Add documentation for environment/aliases/jinja rendering --- README.rst | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/README.rst b/README.rst index 698d9fe..716058e 100644 --- a/README.rst +++ b/README.rst @@ -71,3 +71,84 @@ in your local package cache. You can explicitly provide an environment spec file using ``-f`` or ``--file`` and the name of the file you would like to use. + + +``environment.yml`` jinja2 rendering +------------------------------------ + +If you have ``jinja2`` available in the environment, ``environment.yml`` files will be +rendered with it before processing. + +.. code-block:: yaml + + name: pytest + dependencies: + {% for i in ['xunit', 'coverage','mock'] %} + - pytest-{{ i }} + {% endfor %} + +In this example, the previous file with ``jinja2`` syntax is equivalent to: + +.. code-block:: yaml + + name: pytest + dependencies: + - pytest-xunit + - pytest-coverage + - pytest-mock + +Available variables +^^^^^^^^^^^^^^^^^^^ + +When using ``jinja2``, on top of the usual template capabilities, you have access to the +following variables: + +- ``root``: The directory containing ``environment.yml`` +- ``os``: Python's ``os`` module. + + +``environment.yml`` examples +---------------------------- + +Name and dependencies +^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: yaml + + name: stats + dependencies: + - numpy + - pandas + +Name and version specific dependencies +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: yaml + + name: stats + dependencies: + - numpy==1.8 + - pandas==0.16.1 + + +Environment/aliases +^^^^^^^^^^^^^^^^^^^ + +.. code-block:: yaml + + name: oracle + dependencies: + - oracle_instantclient + + # List type environment variables will be joined with os.pathsep (':' in unix, ';' in windows). + # These values will be inserted in front of any existing value in the current environment. + # e.g.: + # current PATH: "/usr/local/bin:/usr/bin" + # new PATH: "{{ root }}/bin:/usr/local/bin:/usr/bin" + environment: + - ORACLE_HOME: /usr/local/oracle_instantclient + - PATH: + - {{ root }}/bin + + aliases: + run_db: bash {{ root }}/bin/run_db.sh