Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 57 additions & 17 deletions dotenv.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,39 @@
# -*- coding: utf-8 -*-

import os
import sys
import warnings
from collections import OrderedDict
from typing import Dict, Generator, Optional, Tuple

import click


def load_dotenv(dotenv_path):
def load_dotenv(dotenv_path: Optional[str] = None,
encoding: str = 'utf-8',
verbose: bool = False) -> Optional[bool]:
"""
Read a .env file and load into os.environ.

Args:
dotenv_path: Path to the .env file. If not provided, searches for .env in current directory.
encoding: File encoding to use (default: utf-8)
verbose: Enable verbose output

Returns:
True if successful, None if file doesn't exist
"""
if dotenv_path is None:
dotenv_path = os.path.join(os.getcwd(), '.env')

if not os.path.exists(dotenv_path):
warnings.warn("can't read %s - it doesn't exist." % dotenv_path)
if verbose:
warnings.warn("can't read %s - it doesn't exist." % dotenv_path)
return None
for k, v in parse_dotenv(dotenv_path):
for k, v in parse_dotenv(dotenv_path, encoding=encoding):
os.environ.setdefault(k, v)
if verbose:
click.echo("Successfully loaded %s" % dotenv_path)
return True


Expand Down Expand Up @@ -76,8 +94,18 @@ def unset_key(dotenv_path, key_to_unset):
return success, key_to_unset


def parse_dotenv(dotenv_path):
with open(dotenv_path) as f:
def parse_dotenv(dotenv_path: str, encoding: str = 'utf-8') -> Generator[Tuple[str, str], None, None]:
"""
Parse a .env file and yield key-value pairs.

Args:
dotenv_path: Path to the .env file
encoding: File encoding to use (default: utf-8)

Yields:
Tuples of (key, value)
"""
with open(dotenv_path, encoding=encoding) as f:
for line in f:
line = line.strip()
if not line or line.startswith('#') or '=' not in line:
Expand All @@ -87,6 +115,28 @@ def parse_dotenv(dotenv_path):
yield k, v


def load_dotenv_as_dict(dotenv_path: Optional[str] = None,
encoding: str = 'utf-8') -> Dict[str, str]:
"""
Load a .env file and return as a dictionary.

Args:
dotenv_path: Path to the .env file. If not provided, searches for .env in current directory.
encoding: File encoding to use (default: utf-8)

Returns:
Dictionary of environment variables
"""
if dotenv_path is None:
dotenv_path = os.path.join(os.getcwd(), '.env')

result = {}
if os.path.exists(dotenv_path):
for k, v in parse_dotenv(dotenv_path, encoding=encoding):
result[k] = v
return result


def flatten_and_write(dotenv_path, dotenv_as_dict):
with open(dotenv_path, "w") as f:
for k, v in dotenv_as_dict.items():
Expand All @@ -104,16 +154,6 @@ def cli(ctx, file):
ctx.obj = {}
ctx.obj['FILE'] = file

# Need to investigate if this can actually work or if the scope of the new environ variables
# Expires when python exits

# elif action == "load":
# success = load_dotenv(file)
# if success != None:
# click.echo("loaded %s into environment" % file)
# else:
# exit(1)


@cli.command()
@click.pass_context
Expand Down Expand Up @@ -143,7 +183,7 @@ def set(ctx, key, value):
@click.pass_context
@click.argument('key', required=True)
def get(ctx, key):
'''Retrive the value for the given key.'''
'''Retrive the value for the key.'''
file = ctx.obj['FILE']
stored_value = get_key(file, key)
if stored_value:
Expand All @@ -166,4 +206,4 @@ def unset(ctx, key):


if __name__ == "__main__":
cli()
cli()