-1

I am looking to port a bash script that makes heavy use of environment variables to a python script. For a quick sanity check, I would like to follow a similar approach and define all environment variables (or a selection of them) as python global variables. Is there a better way of doing this besides defining each variable one by one using VAR = os.getenv('VAR').

Update: I think this will do it. Contents of def_vars.py

#!/usr/bin/env python3

import os

def define_env_vars(env_vars=None):
    """ Define all (select few) environment variables as python global variables 

    Args:
        env_vars: list of selected environement variables to import, or None,
        in which case all environment variables are imported
    Returns:
        None
    """

    if env_vars == None:
        env_vars = os.environ
    else:
        env_vars = { k: os.environ[k] for k in env_vars }

    for k,v in env_vars.items():
        globals()[k] = v 

# Test
import_env_vars = ["PWD"]
define_env_vars(import_env_vars)
print(PWD)

But using this from another python module doesn't seem to work ??

Update 2 It does work now but only if the variables are prepended with the package path. I want to avoid this too. Contents of test.py

#!/usr/bin/env python3

import def_vars
# from def_vars import *   #This doesn't work

env_vars = ["PWD"]
def_vars.define_env_vars(env_vars)
print(def_vars.PWD)

Update 3 This does what I needed it, and puts the variables on the caller module's globals()

#!/usr/bin/env python3

import os
import inspect

def define_env_vars(env_vars=None):
    """ Define all (select few) environment variables as python global variables
        of the caller module 

    Args:
        env_vars: list of selected environement variables to import, or None,
        in which case all environment variables are imported
    Returns:
        None
    """

    if env_vars == None:
        env_vars = os.environ
    else:
        env_vars = { k: os.environ[k] for k in env_vars }

    for k,v in env_vars.items():
        inspect.stack()[1][0].f_globals[k] = v 
danny
  • 1,101
  • 1
  • 12
  • 34
  • 3
    that certainly works, whats the problem and what do you imagine a more elegant script looks like ... you could always do `globals().update(os.environ)` but in general explicit is better than implicit (and it would break any type hinting) ... there may be more elegant ways of doing it but its hard to tell with just this... – Joran Beasley Jan 22 '22 at 20:17
  • 2
    if this was for a job and i saw this in a merge request we would have to have a talk about discoverability ... also that whole function can be reduced to `globals().update(os.environ)` but again anyone who has to work with this code in the future might wish great bodily harm upon you – Joran Beasley Jan 22 '22 at 20:43
  • 2
    The behaviour you desire is basically PHP's infamous *register_globals*, which was removed because it's a security problem. Maybe that's not quite such a concern for programs that aren't internet facing, but you're still running the risk of having global names replaces by something else, which would at least make debugging hard and the script prone to breaking randomly in different environments. – deceze Jan 22 '22 at 20:44
  • Ok I will not use to import all environment variables, but only those that the bash script assumes to exist. Saves me lots of typing... – danny Jan 22 '22 at 20:51
  • 2
    You can alias `os.environ` or `os.getenv`, or even a custom class, and then type things like `e.FOOBAR`. Hardly *a lot* of typing that globals will save you… – deceze Jan 22 '22 at 20:54
  • It is not the typing I am concerned about but readability of the script. I would rather the environment variables are readily available to the python script as are for the bash script. I think I am quite satisfied with the solutiion. Btw my code above doesn't work as intended for selected env vars ... – danny Jan 22 '22 at 20:57
  • 2
    You really **shouldn't** be dynamically defining variables — see [How do I create variable variables?](https://stackoverflow.com/questions/1373164/how-do-i-create-variable-variables) and [Why you don't want to dynamically create variables](https://stupidpythonideas.blogspot.com/2013/05/why-you-dont-want-to-dynamically-create.html). – martineau Jan 22 '22 at 21:02
  • This solution doesn't seem to work when called from another python file. What am I missing ? – danny Jan 22 '22 at 21:10
  • 1
    As for "readability of the script": it's usually a lot more readable if it's clear which variables are being defined internally, and which come from an external source. – deceze Jan 22 '22 at 21:44
  • This is a pointless discussion. I am working with a complex code that uses a lot of shell scripts and globals, so bad that I am working on replacing it with python. First step is proof of concept doing things exactly (as close as possible) the way things are done in the shell scripts. After that proof of concept, things will be done the pythonic way, probably using yaml file to pass around these variables etc... For the first proof of concept, however, a concise function call to import environment variables does help in readability so please stop lecturing ... – danny Jan 22 '22 at 22:20

1 Answers1

1

Use operator.itemgetter to fetch the values from os.environ.

from operator import itemgetter
import os

import_vars = ['FOO', 'BAR', 'BAZ']
pairs = zip(import_vars, itemgetter(*import_vars)(os.environ))
globals().update(pairs)

It won't work if import_vars is empty, but then, if it were empty, you wouldn't be worried about bulk creation of global variables.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • Thank you! I had to do `list(zip(..` to get it working in python3 but this one seems to be short enough to do from every file I need to do this. If you have a solution for putting this in a separate function in a different file, please let me know... See the second update I made to my OP. – danny Jan 22 '22 at 21:41