HBNetwork / python-decouple

Strict separation of config from code.
MIT License
2.82k stars 195 forks source link

Check .env variables first before checking in the os variable #165

Closed hazemAzzam closed 1 year ago

hazemAzzam commented 1 year ago

this way when changing the .env or settings.ini file we get the new values instead of the old ones

almdudler777 commented 1 year ago

Not sure if this change comes without regressions:

When calling a program under linux like this:

DEBUG=True my_program.py

i would expect this DEBUG to take precendence over something in an .env file.

Would this still work with this change?

lucasrcezimbra commented 1 year ago

i would expect this DEBUG to take precendence over something in an .env file.

That's the expected behavior.

I didn't get your proposal, @hazemAzzam. What are you trying to accomplish?

hazemAzzam commented 1 year ago

If you have an .env file that contains, for example, DEBUG=1, and then you change that value to DEBUG=0, when you call config('DEBUG'), it will return 1 instead of 0. So, I thought that DEBUG is saved in the os.environ with a value of 1. Decouple, on the other hand, retrieves the value of DEBUG if it is saved in the os.environ, otherwise, it gets it from the .env file. The solution to this issue is to check the .env file and retrieve the values from it before checking the OS environment.

You can also take a look at this issue here: https://github.com/HBNetwork/python-decouple/issues/162

lucasrcezimbra commented 1 year ago

So, I thought that DEBUG is saved in the os.environ with a value of 1.

decouple doesn't change the environment variables. It just reads from it.

If you have an .env file that contains, for example, DEBUG=1, and then you change that value to DEBUG=0, when you call config('DEBUG'), it will return 1 instead of 0.

It happens because decouple reads the .env file once and keeps the data in memory. As you can see here.

This PR will not solve it. So, I'm closing it.

kevalrajpalknight commented 9 months ago

Hey @lucasrcezimbra, I too came across this issue couple of time. That even I clear my variable from .env or completely removes it. Still it's able to read the variable from os.environ. After going through couple of closed PR. I found that is how it's designed from here.

Just want to confirm from you, can't we change the order of statement to read first from repository then look into the os.environ

example:


+        if option in self.repository:
+           value = self.repository[option]
+       elif option in os.environ:
+           value = os.environ[option]

-        if option in os.environ:
-            value = os.environ[option]
-        elif option in self.repository:
-            value = self.repository[option]

Please let me know about this, Is this really a issue? Cause I can't find section of code where you intentionally update the os.environ.

almdudler777 commented 8 months ago

Hi @kevalrajpalknight,

the problem is not really within decouple.

The order in which environment variables are loaded was defined a long time ago and pretty much comes from the os level (low level - see fork() and exec()).

The way decouple loads the environment is the expected way. ie. anything that is inherited by the parent process takes precedence.

Take my example from above: If you call your binary / program like this: DEBUG=True my_program.py the DEBUG will be available via os.environ and is expected to overwrite any DEBUG inside of an .env file.

So changing decouple's load order will break this expectation.

Whenever you have trouble with variables appearing that you do not have set in an .env file make sure that the parent process (on linux cmdline likely bash) not still has an active copy inside its environment.

Please also note that removing something from .env will only take effect if your program is restarted. Even worse than that, depending on what "parent process" calls your program it might just well be that this program is setup to automatically load .env as-well. if i remember correctly, it is even acceptable that the parent process traverses the directory structures (upwards i think) to load any other .env as well. compare this to how linux bash loads your profile for example.

So in short: please make sure that, your own program is restarted after any changes to .env (especially important for any long running things like let's say a webapp (flask, fastapi etc.), make sure the parent process (bash, systemd, supervisord, docker etc.pp.) is not keeping anything active in their environments that will be passed on to your program. Also investigate that whatever is starting your app is not traversing and loading any unexpected .env files.