pyinvoke / invoke

Pythonic task management & command execution.
http://pyinvoke.org
BSD 2-Clause "Simplified" License
4.31k stars 365 forks source link

AmbiguousEnvVar when setting default runner.env with both lower and uppercase #932

Open u1735067 opened 1 year ago

u1735067 commented 1 year ago

Hi,

I think I discovered an issue with the environment system, you can't set default environment variables with both upper and lowercase variants, like for HTTP_PROXY:

import invoke

ns = invoke.Collection()
ns.configure(dict(
    run=dict(
        echo=True,
        env={
            **{
                env_name: "http://my-proxy/"
                for env_name in ("http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY")
            },
            **{
                env_name: "localhost" for env_name in ("no_proxy", "NO_PROXY")
            }
        }
    )
))

@invoke.task
def test(ctx):
    ctx.run('env && exit 1')

ns.add_task(test, "test")

This is also true with the following:

invoke.yml

run:
  env:
    HTTP_PROXY: 1
    http_proxy: 1

tasks.py

import invoke

@invoke.task
def test(ctx):
    ctx.run('env && exit 1')
inv test
[..]
invoke.exceptions.AmbiguousEnvVar: Found >1 source for RUN_ENV_NO_PROXY

I guess only keys with the invoke prefix or only external (not set in config) environment variables should be analyzed? RUN_ENV_ subkeys should be analyzed without case modification?

u1735067 commented 1 year ago

Workaround:

def patch_invoke_env():
    def _to_env_var(self, key_path):
        if len(key_path) > 2 and key_path[0].lower() == 'run' and key_path[1].lower() == 'env':
            return '_'.join(('_'.join(key_path[:2]).upper(), '_'.join(key_path[2:])))
        return "_".join(key_path).upper()

    import invoke.env
    invoke.env.Environment._to_env_var = _to_env_var

patch_invoke_env()