Einlesen von Umgebungsvariablen aus einer Umgebungsdatei

Lesezeit: 10 Minuten

Benutzeravatar von Kurt Peek
Kurt Peek

Ich möchte in einer lokalen Umgebung ein Python-Skript ausführen, das normalerweise in einem Docker-Container ausgeführt wird. Die docker-compose.yml spezifiziert ein env_file was (teilweise) wie folgt aussieht:

DB_ADDR=rethinkdb
DB_PORT=28015
DB_NAME=ipercron

Um dies lokal auszuführen, möchte ich, dass diese Zeilen konvertiert werden

os.environ['DB_ADDR'] = 'rethinkdb'
os.environ['DB_PORT'] = '28015'
os.environ['DB_NAME'] = 'ipercron'

Ich könnte meinen Parser schreiben, aber ich habe mich gefragt, ob es vorhandene Module/Tools zum Einlesen von Umgebungsvariablen aus Konfigurationsdateien gibt?

Benutzeravatar von ParisNakitaKejser
ParisNakitaKejser

ich benutze Python Dotenv-Bibliothek. Installieren Sie einfach die Bibliothek pip install python-dotenvein … kreieren .env Datei mit Ihren Umgebungsvariablen und importieren Sie die Umgebungsvariablen wie folgt in Ihren Code:

import os
from dotenv import load_dotenv

load_dotenv()

MY_ENV_VAR = os.getenv('MY_ENV_VAR')

Von dem .env Datei:

MY_ENV_VAR="This is my env var content."

Auf diese Weise mache ich es, wenn ich Code außerhalb meines Docker-Systems testen und ihn darauf vorbereiten muss, ihn wieder in Docker zurückzugeben.

  • benutze ich auch dotenv. Wenn Sie aus einer JS-Backend-Umgebung kommen, gibt es so schöne Ähnlichkeiten, dass die Lernkurve fast flach ist!

    – sk

    10. April 2020 um 11:54 Uhr


  • wenn ich habe MY_ENV_VAR="" in dem .bashrc Datei, für die dies nicht gilt, und gibt eine leere Zeichenfolge zurück; ist das ein normales verhalten?

    – Alper

    18. November 2020 um 21:59 Uhr


  • @alper ja ist es. Es überschreibt nur bereits gesetzte Umgebungsvariablen .bashrc wenn du benutzt load_dotenv(override=True)wie in ihrer beschrieben GitHub-Readme-Datei.

    – Daniel Lavedonio de Lima

    14. März 2021 um 2:46 Uhr

  • Im Allgemeinen sind in .env-Dateien keine Anführungszeichen für die Variablen erforderlich.

    – Alex

    18. Mai 2021 um 2:27 Uhr

  • Ich möchte das gemäß den Dokumenten hinzufügen Lines can start with the export directive, which has no effect on their interpretation. Sehr schön. Bedeutet, dass Sie tun können export MY_VAR="Env var" und kompatibel sein source .env von bash. Vielleicht ist es nützlich, diese Informationen zur Antwort hinzuzufügen. Ich würde eine Bearbeitung vorschlagen, aber die Warteschlange ist voll

    – CervEd

    21. November 2021 um 12:43 Uhr


Benutzeravatar von Gino Mempin
Gino Mempin

Wenn Ihr System/Ihre Umgebung/Ihr Arbeitsablauf die Verwendung von Shell-Skripten unterstützt, können Sie ein Skript erstellen, das diese beiden Vorgänge umschließt:

  1. Beschaffung der .env-Datei und Export als Umgebungsvariablen
    • Verwendung der set -a Option wo “Jede Variable oder Funktion, die erstellt oder geändert wird, erhält das Exportattribut und wird für den Export in die Umgebung nachfolgender Befehle markiert“.
  2. Aufrufen Ihres Python-Skripts / Ihrer Python-App, die plain os.environ.get Code

Beispiel für eine .env-Datei (config.env):

TYPE=prod
PORT=5000

Beispiel-Python-Code (test.py):

import os

print(os.environ.get('TYPE'))
print(os.environ.get('PORT'))

Beispiel-Bash-Skript (run.sh):

#!/usr/bin/env bash

set -a
source config.env
set +a

python3 test.py

Beispiellauf:

$ tree
.
├── config.env
├── run.sh
└── test.py

$ echo $TYPE

$ echo $PORT

$ python3 test.py
None
None

$ ./run.sh 
prod
5000

Wenn Sie das Python-Skript direkt ausführen (python3 test.py) ohne source-ing die .env-Datei, alle environ.get Anrufe zurück None.

Wenn Sie es jedoch in ein Shell-Skript einschließen, das zuerst die .env-Datei in Umgebungsvariablen lädt und anschließend das Python-Skript ausführt, sollte das Python-Skript jetzt in der Lage sein, die Umgebungsvariablen korrekt zu lesen. Es stellt auch sicher, dass die exportierten env-Variablen nur als Teil der Ausführung Ihrer Python-Anwendung/Ihres Python-Skripts existieren.

Im Vergleich zu der anderen beliebten Antwort benötigt dies keine externen Python-Bibliotheken.

  • Dies ist eine echte Linux-Lösung für dieses Problem, denke ich.

    – Ehsan88

    31. März 2021 um 12:17 Uhr

  • Das war genau das, was ich brauchte. Die set -a trick ist wirklich elegant und wird sich in einer Reihe anderer Szenarien als nützlich erweisen. Vielen Dank!

    – András Aszodi

    26. Mai 2021 um 14:53 Uhr

  • Beachten Sie, dass export MY_VAR="Hello!" ist kompatibel mit dotenv ebenso gut wie bash source. nett

    – CervEd

    21. November 2021 um 12:45 Uhr


  • …Sonderzeichen wie ! in der exportierten env ist der var-Wert kompatibel mit source solange es richtig zitiert wird. Siehe stackoverflow.com/q/55703950/2745495

    – Gino Mempin

    21. Februar 2022 um 10:44 Uhr


Benutzeravatar von radtek
radtek

Das könnte auch für dich funktionieren:

env_vars = [] # or dict {}
with open(env_file) as f:
    for line in f:
        if line.startswith('#') or not line.strip():
            continue
        # if 'export' not in line:
        #     continue
        # Remove leading `export `, if you have those
        # then, split name / value pair
        # key, value = line.replace('export ', '', 1).strip().split('=', 1)
        key, value = line.strip().split('=', 1)
        # os.environ[key] = value  # Load to local environ
        # env_vars[key] = value # Save to a dict, initialized env_vars = {}
        env_vars.append({'name': key, 'value': value}) # Save to a list

print(env_vars)

In den Kommentaren finden Sie einige verschiedene Möglichkeiten, die env-Variablen zu speichern, und auch einige Parsing-Optionen, dh um die führenden zu entfernen export Stichwort. Eine andere Möglichkeit wäre die Verwendung von python-dotenv Bücherei. Beifall.

UPDATE: Ich habe meine eigene envvar_utils.py eingerichtet, um die Konvertierung von Zeichenfolgen usw.

"""Utility functions for dealing with env variables and reading variables from env file"""
import os
import logging
import json

BOOLEAN_TYPE = 'boolean'
INT_TYPE = 'int'
FLOAT_TYPE = 'float'
STRING_TYPE = 'str'
LIST_TYPE = 'list'
DICT_TYPE = 'dict'


def get_envvars(env_file=".env", set_environ=True, ignore_not_found_error=False, exclude_override=()):
    """
    Set env vars from a file
    :param env_file:
    :param set_environ:
    :param ignore_not_found_error: ignore not found error
    :param exclude_override: if parameter found in this list, don't overwrite environment
    :return: list of tuples, env vars
    """
    env_vars = []
    try:

        with open(env_file) as f:
            for line in f:
                line = line.replace('\n', '')

                if not line or line.startswith('#'):
                    continue

                # Remove leading `export `
                if line.lower().startswith('export '):
                    key, value = line.replace('export ', '', 1).strip().split('=', 1)
                else:
                    try:
                        key, value = line.strip().split('=', 1)
                    except ValueError:
                        logging.error(f"envar_utils.get_envvars error parsing line: '{line}'")
                        raise

                if set_environ and key not in exclude_override:
                    os.environ[key] = value

                if key in exclude_override:
                    env_vars.append({'name': key, 'value': os.getenv(key)})
                else:
                    env_vars.append({'name': key, 'value': value})
    except FileNotFoundError:
        if not ignore_not_found_error:
            raise

    return env_vars


def create_envvar_file(env_file_path, envvars):
    """
    Writes envvar file using env var dict
    :param env_file_path: str, path to file to write to
    :param envvars: dict, env vars
    :return:
    """
    with open(env_file_path, "w+") as f:
        for key, value in envvars.items():
            f.write("{}={}\n".format(key, value))
    return True


def convert_env_var_flag_to(env_var_name, required_type, default_value):
    """
    Convert env variable string flag values to required_type
    :param env_var_name: str, environment variable name
    :param required_type: str, required type to cast the env var to
    :param default_value: boolean, default value to use if the environment variable is not available
    :return: environment variable value in required type
    """
    env_var_orginal_value = os.getenv(env_var_name, default_value)
    env_var_value = ""
    try:
        if required_type == INT_TYPE:
            env_var_value = int(env_var_orginal_value)
        elif required_type == FLOAT_TYPE:
            env_var_value = float(env_var_orginal_value)
        elif required_type == BOOLEAN_TYPE:
            env_var_value = bool(int(env_var_orginal_value))
        elif required_type == STRING_TYPE:
            env_var_value = str(env_var_orginal_value)
        elif required_type == LIST_TYPE:
            env_var_value = env_var_orginal_value.split(',') if len(env_var_orginal_value) > 0 else default_value
        elif required_type == DICT_TYPE:
            try:
                env_var_value = json.loads(env_var_orginal_value) if env_var_orginal_value else default_value
            except Exception as e:
                logging.error(f"convert_env_var_flag_to: failed loading {env_var_orginal_value} error {e}")
                env_var_value = default_value
        else:
            logging.error("Unrecognized type {} for env var {}".format(required_type, env_var_name))

    except ValueError:
        env_var_value = default_value
        logging.warning("{} is {}".format(env_var_name, env_var_orginal_value))

    return env_var_value

  • Woud speichern sie nicht in a dict sein besserer Ansatz als in eine Liste? // +1, um den Zeilenvorsprung loszuwerden export Stichwort

    – Alper

    18. November 2020 um 22:21 Uhr


  • Das ist wirklich eine ziemlich schmutzige Methode und erfordert viel Reinigung, um tatsächlich zu funktionieren … Zeichenfolgen aus dem dotenv werden “wie sie sind” gelesen, dh sie behalten ihr ” “. Auch wenn das dotenv Leerzeichen um das = hat, sind diese Teil des Schlüssels

    – KönigOtto

    28. Dezember 2021 um 13:02 Uhr

  • Bei mir funktioniert es jetzt seit Jahren gut. env-Variablen sind Zeichenfolgen, also müssten Sie sie wie bei jeder env-Variablen nach Bedarf konvertieren.

    – radtek

    4. Januar 2022 um 20:54 Uhr

Benutzeravatar von Moinuddin Quadri
Moinuddin Quadri

Sie können verwenden ConfigParser. Musterbeispiel finden Sie hier.

Aber diese Bibliothek erwartet Ihre key=value Daten unter einigen vorhanden sein [heading]. Zum Beispiel wie:

[mysqld]
user = mysql  # Key with values
pid-file = /var/run/mysqld/mysqld.pid
skip-external-locking
old_passwords = 1
skip-bdb      # Key without value
skip-innodb

Benutzeravatar von Martin Nowosad
Martin Nowosad

Dewald Abrie hat eine gute Lösung gepostet.

Hier ist eine kleine Modifikation, die Bruchkanten ignoriert (\n)

def get_env_data_as_dict(path: str) -> dict:
    with open(path, 'r') as f:
       return dict(tuple(line.replace('\n', '').split('=')) for line
                in f.readlines() if not line.startswith('#'))

print(get_env_data_as_dict('../db.env'))

  • Ich denke, das ist eine großartige Antwort, sauber, praktisch und ohne externe Abhängigkeiten. Ich habe die komplexen Anforderungen nicht überprüft, aber es ist sehr praktisch, wenn man ein lokales Repo hat und den gesamten Code, aber nicht den variablen Inhalt (z. B. ein Token) bereitstellen muss. Befolgen Sie einfach diese allgemeinen Schritte: 1) erstellte eine .env-Datei, 2) lege sie in deinen virtuellen Umgebungsordner, 3) füge den virtualenv-Ordner in .gitignore ein, 4) lese die Variable mit der bereitgestellten Funktion in einem beliebigen Skript und wird nicht im Repo öffentlich sein, sondern nur in deinem lokalen Maschine.

    – IF.Francisco.ME

    29. Mai 2021 um 0:24 Uhr

  • Das funktioniert bei mir nicht, ich bekomme diesen Fehler: ValueError: dictionary update sequence element #2 has length 1; 2 is required

    – Chud37

    19. September 2022 um 20:33 Uhr

  • Schöner Code, aber es funktioniert nicht mit Kommentaren am Ende einer Zeile (nach Daten), oder? (Dies gilt wahrscheinlich auch für andere Antworten.)

    – jakob.j

    19. Dezember 2022 um 21:15 Uhr


  • @jakob.j nein, aber Sie sollten die Kommentare trotzdem über dem Code hinzufügen. Wenn Sie möchten, dass Kommentare verschwinden, können Sie dies mit Regex handhaben

    – Martin Nowosad

    21. Dezember 2022 um 21:01 Uhr


Benutzeravatar von Arash Hatami
Arash Hatami

Wie wäre es damit für eine kompaktere Lösung:

import os

with open('.docker-compose-env', 'r') as fh:
    vars_dict = dict(
        tuple(line.replace('\n', '').split('='))
        for line in fh.readlines() if not line.startswith('#')
    )

print(vars_dict)
os.environ.update(vars_dict)

  • Ich denke, das ist eine großartige Antwort, sauber, praktisch und ohne externe Abhängigkeiten. Ich habe die komplexen Anforderungen nicht überprüft, aber es ist sehr praktisch, wenn man ein lokales Repo hat und den gesamten Code, aber nicht den variablen Inhalt (z. B. ein Token) bereitstellen muss. Befolgen Sie einfach diese allgemeinen Schritte: 1) erstellte eine .env-Datei, 2) lege sie in deinen virtuellen Umgebungsordner, 3) füge den virtualenv-Ordner in .gitignore ein, 4) lese die Variable mit der bereitgestellten Funktion in einem beliebigen Skript und wird nicht im Repo öffentlich sein, sondern nur in deinem lokalen Maschine.

    – IF.Francisco.ME

    29. Mai 2021 um 0:24 Uhr

  • Das funktioniert bei mir nicht, ich bekomme diesen Fehler: ValueError: dictionary update sequence element #2 has length 1; 2 is required

    – Chud37

    19. September 2022 um 20:33 Uhr

  • Schöner Code, aber es funktioniert nicht mit Kommentaren am Ende einer Zeile (nach Daten), oder? (Dies gilt wahrscheinlich auch für andere Antworten.)

    – jakob.j

    19. Dezember 2022 um 21:15 Uhr


  • @jakob.j nein, aber Sie sollten die Kommentare trotzdem über dem Code hinzufügen. Wenn Sie möchten, dass Kommentare verschwinden, können Sie dies mit Regex handhaben

    – Martin Nowosad

    21. Dezember 2022 um 21:01 Uhr


Verwenden Sie nur Python Std

import re

envre = re.compile(r'''^([^=]+)\s+?=\s+?(?:[\s"']*)(.+?)(?:[\s"']*)$''')
result = {}
with open('/etc/os-release') as ins:
    for line in ins:
        match = envre.match(line)
        if match is not None:
            result[match.group(1)] = match.group(2)

1439740cookie-checkEinlesen von Umgebungsvariablen aus einer Umgebungsdatei

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy