Skip to content

Commit

Permalink
Added multi-env file capabillities
Browse files Browse the repository at this point in the history
  • Loading branch information
dakrauth committed Mar 5, 2025
1 parent ccac681 commit 05851b2
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 98 deletions.
16 changes: 4 additions & 12 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
name: Test

on:
push:
branches:
- main
pull_request:
branches:
- main

on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down
12 changes: 7 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
*.pyc
.tox/
/.tox
.coverage
__pycache__
castaway.egg-info/
venv/
htmlcov/
temp/
/src/castaway.egg-info
/venv
/htmlcov
/temp
/dist
/.pytest_cache
.python-version
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021 David A Krauth
Copyright (c) 2025 David A Krauth

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
46 changes: 46 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
[build-system]
requires = ["setuptools >= 61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "castaway"
version = "1.1.0"
description = "Simple wrapper for dotenv, with casting"
requires-python = ">=3.10"
readme = { file = "README.rst", content-type = "text/x-rst" }
license = { file = "LICENSE" }
authors = [
{ name = "David Krauth", email = "dakrauth@gmail.com"},
]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Environment :: Console",
"Framework :: Django",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
dependencies = ["python-dotenv[cli]==1.0.1"]

[project.urls]
Homepage = "http://github.com/dakrauth/castaway"

[project.optional-dependencies]
django = [ "dj-email-url==1.0.6", "dj-database-url==2.3.0" ]
test = ["pytest-cov", "tox"]

[tool.setuptools]
package-dir = { "" = "src" }
packages = [ "castaway" ]

[tool.ruff]
line-length = 100
indent-width = 4

[tool.ruff.lint]
ignore = ["E741"]
57 changes: 57 additions & 0 deletions run
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env bash

set -o errexit
set -o pipefail
TIMEFORMAT="Task completed in %3lR"
BIN="./venv/bin"

function help {
echo "Options:"
echo " init: Setup dev environment"
echo " lint: Linter"
echo " check: Formatting checks"
echo " format: Run formatter"
echo " test: Execute tests"
echo " coverage: Run tests with code coverage"
echo " clean: Remove dev, test, and build artifacts"
}

function init {
python3 -m venv --prompt castaway venv
"${BIN}/python" -m pip install -e ".[django,test]"
}

function lint {
"${BIN}/ruff" check castaway.py tests
}

function check {
"${BIN}/ruff" format --check --diff src/castaway tests
}

function format {
"${BIN}/ruff" format src/castaway tests
}

function test {
"${BIN}/pytest" -s -Werror tests "$@"
}

function clean {
rm -rf .ruff_cache .tox htmlcov .coverage dist pytest_cache src/castaway.egg-info
}

function coverage {
"${BIN}/pytest" \
--cov-report html \
--cov-report term \
--cov=castaway
}

if [ -z "$1" ]; then
help
else
what=$1
shift
time ${what:-help} "$@"
fi
9 changes: 0 additions & 9 deletions setup.cfg

This file was deleted.

41 changes: 0 additions & 41 deletions setup.py

This file was deleted.

29 changes: 16 additions & 13 deletions castaway.py → src/castaway/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import os
from pathlib import Path
import dotenv

required = object()


def cast_bool(val):
return (
val.lower() in {"1", "yes", "true", "y", "on"}
if isinstance(val, str)
else bool(val)
)
return val.lower() in {"1", "yes", "true", "y", "on"} if isinstance(val, str) else bool(val)


def cast_list(val):
Expand All @@ -30,8 +27,11 @@ def cast_django_email(val):

class Config:
def __init__(self, filename=".env", **castings):
self.filename = filename
self.found_path = None
if isinstance(filename, (str, Path)):
filename = [filename]

self.filename = [str(f) for f in filename]
self.found_path = []

self.castings = {
bool: cast_bool,
Expand All @@ -40,12 +40,15 @@ def __init__(self, filename=".env", **castings):
"django_email": cast_django_email,
}
self.castings.update(**castings)

if os.path.exists(self.filename):
self.found_path = self.filename
else:
self.found_path = dotenv.find_dotenv(self.filename, usecwd=True)
self.values = dotenv.dotenv_values(self.found_path, verbose=True)
self.values = {}
for path in self.filename:
if os.path.exists(path):
found = path
else:
found = dotenv.find_dotenv(path, usecwd=True)

self.found_path.append(found)
self.values.update(**dotenv.dotenv_values(found, verbose=True))

def add_castings(self, **kwargs):
self.castings.update(kwargs)
Expand Down
1 change: 1 addition & 0 deletions tests/.env2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CASTAWAY_DECIMAL=3.14159
13 changes: 8 additions & 5 deletions tests/test_castenv.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import decimal
import pytest
import castaway

Expand All @@ -13,12 +14,14 @@ def set_cwd():

@pytest.fixture
def cfg(set_cwd):
return castaway.Config()
return castaway.Config(filename=[".env", ".env2"])


def test_default_config(set_cwd):
config = castaway.config
config.add_castings(decimal=decimal.Decimal)
assert config("CASTAWAY_INT", cast=int) == 23
assert config("CASTAWAY_DECIMAL", cast="decimal") == decimal.Decimal("2.3")


def test_bool(cfg):
Expand All @@ -39,10 +42,8 @@ def test_list(cfg):


def test_custom_cast(cfg):
import decimal

cfg.add_castings(decimal=decimal.Decimal)
assert cfg("CASTAWAY_DECIMAL", cast="decimal") == decimal.Decimal("2.3")
assert cfg("CASTAWAY_DECIMAL", cast="decimal") == decimal.Decimal("3.14159")


def test_override():
Expand All @@ -61,7 +62,9 @@ def test_required_failure(cfg):


def test_dj_database(cfg):
assert cfg("CASTAWAY_DJ_DATABASE", cast="django_db") == {
result = cfg("CASTAWAY_DJ_DATABASE", cast="django_db")
result.pop("DISABLE_SERVER_SIDE_CURSORS", None)
assert result == {
"ENGINE": "django.db.backends.mysql",
"OPTIONS": {"init_command": "SET storage_engine=InnoDB", "charset": "utf8mb4"},
"NAME": "dbname",
Expand Down
15 changes: 3 additions & 12 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[tox]
skipsdist = True
envlist =
py{38,39,310,311,312}
py{310,311,312,313}

[testenv]
usedevelop = True
extras = django
commands =
pip install -e .[all]
pip install -e .[django,test]
pytest
deps =
pytest-cov
Expand All @@ -33,22 +33,13 @@ commands =
find {toxinidir} -type d -name "__pycache__" -delete
rm -f {toxworkdir} {toxinidir}/.pytest_cache {toxinidir}/build {toxinidir}/dist

[testenv:pep8]
description = Run PEP8 flake8
skipsdist = true
skip_install = true
basepython = python3.10
deps = flake8
commands = flake8 castaway.py tests

[pytest]
testpaths =
tests

[gh-actions]
python =
3.8: py38
3.9: py39
3.10: py310
3.11: py311
3.12: py312
3.13: py313

0 comments on commit 05851b2

Please sign in to comment.