django-environ icon indicating copy to clipboard operation
django-environ copied to clipboard

Lazily load default value of environment variable

Open konoufo opened this issue 6 years ago • 12 comments

Would you accept a pull request to enable lazy evaluation of default value ? I think there are several use cases where you want to generate the default on-demand. For example, creating a new temporary directory and use it as a default value when a env var doesn't exist in environment.

konoufo avatar May 31 '18 16:05 konoufo

Please describe how you suggest it will work! Allowing the value to be just a plain function?

I use a pattern like this when I want to have that feature and it's not very verbose and easy to follow:


env = environ.Env(
    SOMETHING=(str),
)

SOMETHING = env("SOMETHING",default=None)
if not SOMETHING:
    ... calculate and assign value of something

I think it's maybe not a good idea to allow lazy evaluation of values after settings.py has been loaded, it's good to have the settings at least somewhat validated when the file is loaded..

thomasf avatar Jun 27 '18 07:06 thomasf

Yeah I think being able to pass a plain function that's evaluated when env("SOMETHING", fun) is called. This wouldn't make a difference timing-wise compared to what we're doing right now.

konoufo avatar Jun 30 '18 00:06 konoufo

So something like this then.. I wonder if it clashes with other features or not, I cant think of anything from the top of my head..


def create_something():
    return "SOMETHING"

env = environ.Env(
    SOMETHING=(str, create_something),
)

SOMETHING = env('SOMETHING')

OTHER_THING=env('OTHER_THING', default_value=create_something, cast_to=str)

Note that the amount of code isn't shorter than the code I posted earlier, the only benifit I can see is that the type str can be validated by django-environ after being returned from the function.. I still think that the settings file becomes harder to read and I wonder if it's worth it.

thomasf avatar Jul 01 '18 11:07 thomasf

Yeah this is how I'd use it. I could submit a PR if you're okay with this.


De : Thomas Frössman [email protected] Envoyé : dimanche 1 juillet 2018 11:02:16 À : joke2k/django-environ Cc : konoufo; Author Objet : Re: [joke2k/django-environ] Lazily load default value of environment variable (#185)

So something like this then..

def create_something(): return "SOMETHING"

env = environ.Env( SOMETHING=(str, create_something), )

SOMETHING = env('SOMETHING')

OTHER_THING=env('OTHER_THING', default_value=create_something, cast_to=str)

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/joke2k/django-environ/issues/185#issuecomment-401599514, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AJMLjLkjRj9f6f-n7pqylFxKFr7sd_mfks5uCKw4gaJpZM4UVWCm.

konoufo avatar Jul 01 '18 17:07 konoufo

I am not a maintainer. I just wanted to understand what you were requesting (for the benefit of everyone) , it looks clear to me now..

thomasf avatar Jul 01 '18 17:07 thomasf

Oh okay! Sorry. Alright, waiting for the maintainers then...


De : Thomas Frössman [email protected] Envoyé : dimanche 1 juillet 2018 17:45:57 À : joke2k/django-environ Cc : konoufo; Author Objet : Re: [joke2k/django-environ] Lazily load default value of environment variable (#185)

I am not a maintainer. I just wanted to understand what you were requesting, it looks clear to me now..

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/joke2k/django-environ/issues/185#issuecomment-401621835, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AJMLjHAUoyFzbQDZ-tawBnfDcQZvTpBnks5uCQrVgaJpZM4UVWCm.

konoufo avatar Jul 01 '18 18:07 konoufo

I haven't tested this, but would creating your own lazy class work?

def create_something():
    return "SOMETHING"

class LazyString:
    def __init__(self, value, fn):
        self.value = value
        self.fn = fn

    def __str__(self):
        if self.value is not None:
            return self.value
        return self.fn()


OTHER_THING=LazyString(env('OTHERTHING', default=None), create_something)


# someotherfile.py
from django.conf import settings

other_thing = str(settings.OTHER_THING)

Jafnee avatar Jul 02 '18 03:07 Jafnee

Well, I'd have to edit every code that uses OTHER_THING to replace settings.OTHER_THING with settings.OTHER_THING.value. I'm aware of workarounds to get the functionality I want. But for the sake of separation of concerns, I don't want to call create_something inside the settings file. And for consistency, I want the actual value of a setting to be always directly accessible thru settings.FOO. Moreover, I think to have the default argument to also accept a function just make sense and this pattern is ubiquitous in Python libraries.


De : Jafnee [email protected] Envoyé : lundi 2 juillet 2018 03:29:24 À : joke2k/django-environ Cc : konoufo; Author Objet : Re: [joke2k/django-environ] Lazily load default value of environment variable (#185)

I haven't tested this, but would creating your own lazy class work?

def create_something(): return "SOMETHING"

class LazyString: def init(self, value, fn): self.value = value self.fn = fn

def __str__(self):
    if self.value:
        return self.value
    return self.fn()

OTHER_THING=LazyString(env('OTHERTHING', default=None), create_something)

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/joke2k/django-environ/issues/185#issuecomment-401662154, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AJMLjIn_CBbk2BYfjx6illwqOByYc_1eks5uCZOUgaJpZM4UVWCm.

konoufo avatar Jul 02 '18 04:07 konoufo

Shouldn't need to replace everything to settings.OTHER_THING.value, the example above is meant to evaluated lazily whenever the setting is used. The value will either be the string set via env var or the return value of the function provided. The function is also called each time the setting is used, e.g if it returned the timestamp now, it will always return the latest value.

Jafnee avatar Jul 02 '18 07:07 Jafnee

Remember that if you are using it for creating a new temporary directory and use it as a default value and don't return a static value the value will probably be different for each django process in a uwsgi server. If possible you should normally always prefer a deterministic way to create the settings even if it's lazily set.

thomasf avatar Jul 02 '18 07:07 thomasf

Hi, I think this feature could be useful. As @konoufo said, callable defaults are ubiquitous in Python, and we could implement it as easy. Here https://github.com/joke2k/django-environ/blob/develop/environ/environ.py#L279 we could handle this, without side effects.

joke2k avatar Jul 02 '18 09:07 joke2k

@Jafnee that is if you assume that the setting will be used in some way as a string or coerced to a string. @thomasf I realize that and the temporary directory setting is just a specific use case; but yeah I'm aware that it has to be deterministic if I want a single unique directory for every process. @joke2k Right, I could submit a PR.

konoufo avatar Jul 02 '18 18:07 konoufo