Skip to content

Commit

Permalink
chore: initial support for dot env loading
Browse files Browse the repository at this point in the history
  • Loading branch information
joamag committed Apr 22, 2024
1 parent 74b71c0 commit 369e6a0
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 0 deletions.
49 changes: 49 additions & 0 deletions src/appier/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ def load(names=(FILE_NAME,), path=None, encoding="utf-8", ctx=None):
for path in paths:
for name in names:
load_file(name=name, path=path, encoding=encoding, ctx=ctx)
load_dot_env(ctx=ctx)
load_env(ctx=ctx)


Expand Down Expand Up @@ -218,6 +219,54 @@ def load_file(name=FILE_NAME, path=None, encoding="utf-8", ctx=None):
configs[key] = value


def load_dot_env(name=".env", encoding="utf-8", ctx=None):
configs = ctx["configs"] if ctx else CONFIGS
config_f = ctx["config_f"] if ctx else CONFIG_F

file_path = os.path.abspath(name)
file_path = os.path.normpath(file_path)

exists = os.path.exists(file_path)
if not exists:
return

exists = file_path in config_f
if exists:
config_f.remove(file_path)
config_f.append(file_path)

file = open(file_path, "rb")
try:
data = file.read()
finally:
file.close()
if not data:
return

data = data.decode(encoding)
data = data.strip()
lines = data.splitlines()
lines = [line.strip() for line in lines]

for line in lines:
line = line.strip()
if not line:
continue
if line.startswith("#"):
continue
key, value = line.split("=", 1)
key = key.strip()
value = value.strip()
if (
value.startswith('"')
and value.endswith('"')
or value.startswith("'")
and value.endswith("'")
):
value = value[1:-1].replace('\\"', '"')
configs[key] = value


def load_env(ctx=None):
configs = ctx["configs"] if ctx else CONFIGS

Expand Down
36 changes: 36 additions & 0 deletions src/appier/test/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@

import appier

try:
import unittest.mock as mock
except ImportError:
mock = None


class ConfigTest(unittest.TestCase):
def test_basic(self):
Expand Down Expand Up @@ -79,3 +84,34 @@ def test_none(self):
result = appier.conf("HEIGHT", cast=int)

self.assertEqual(result, None)

def test_load_dot_env(self):
if mock == None:
self.skipTest("Skipping test: mock unavailable")

mock_data = mock.mock_open(
read_data=b"#This is a comment\nAGE=10\nNAME=colony\n"
)

with mock.patch("os.path.exists", return_value=True), mock.patch(
"builtins.open", mock_data, create=True
) as mock_open:
ctx = dict(configs={}, config_f=[])

appier.config.load_dot_env(".env", "utf-8", ctx)

result = appier.conf("AGE", cast=int)
self.assertEqual(type(result), int)
self.assertEqual(result, 10)

result = appier.conf("AGE", cast=str)

self.assertEqual(result, "10")
self.assertEqual(type(result), str)

result = appier.conf("HEIGHT", cast=int)
self.assertEqual(result, None)

self.assertEqual(len(ctx["configs"]), 2)

self.assertEqual(mock_open.return_value.close.call_count, 1)

0 comments on commit 369e6a0

Please sign in to comment.