Skip to content
Merged
Show file tree
Hide file tree
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 9.4.0 (unreleased)

Bug fixes:

- Fix compatibility with marshmallow>=3.13.0
so that no DeprecationWarnings are raised ([#224](https://github.com/sloria/environs/issues/224)).

## 9.3.2 (2021-03-28)

Bug fixes:
Expand Down
1 change: 1 addition & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jobs:
parameters:
toxenvs:
- lint
- py36-marshmallowlowest
- py36-marshmallow3
- py37-marshmallow3
- py38-marshmallow3
Expand Down
40 changes: 35 additions & 5 deletions environs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class ParserConflictError(ValueError):
"""Raised when adding a custom parser that conflicts with a built-in parser method."""


_SUPPORTS_LOAD_DEFAULT = ma.__version_info__ >= (3, 13)


def _field2method(
field_or_factory: FieldOrFactory, method_name: str, *, preprocess: typing.Optional[typing.Callable] = None
) -> ParserMethod:
Expand All @@ -63,16 +66,43 @@ def method(
name: str,
default: typing.Any = ma.missing,
subcast: typing.Optional[Subcast] = None,
*,
# Subset of relevant marshmallow.Field kwargs
load_default: typing.Any = ma.missing,
missing: typing.Any = ma.missing,
validate: typing.Optional[
typing.Union[
typing.Callable[[typing.Any], typing.Any],
typing.Iterable[typing.Callable[[typing.Any], typing.Any]],
]
] = None,
required: bool = False,
allow_none: typing.Optional[bool] = None,
error_messages: typing.Optional[typing.Dict[str, str]] = None,
metadata: typing.Optional[typing.Mapping[str, typing.Any]] = None,
**kwargs,
) -> typing.Optional[_T]:
if self._sealed:
raise EnvSealedError("Env has already been sealed. New values cannot be parsed.")
missing = kwargs.pop("missing", None) or default
field_kwargs = dict(
validate=validate,
required=required,
allow_none=allow_none,
error_messages=error_messages,
metadata=metadata,
)
if _SUPPORTS_LOAD_DEFAULT:
field_kwargs["load_default"] = load_default or default
else:
field_kwargs["missing"] = missing or default
if isinstance(field_or_factory, type) and issubclass(field_or_factory, ma.fields.Field):
field = field_or_factory(missing=missing, **kwargs)
# TODO: Remove `type: ignore` after https://github.com/python/mypy/issues/9676 is fixed
field = field_or_factory(**field_kwargs) # type: ignore
else:
field = field_or_factory(subcast=subcast, missing=missing, **kwargs)
parsed_key, value, proxied_key = self._get_from_environ(name, field.missing)
field = field_or_factory(subcast=subcast, **field_kwargs)
parsed_key, value, proxied_key = self._get_from_environ(
name, field.load_default if _SUPPORTS_LOAD_DEFAULT else field.missing
)
self._fields[parsed_key] = field
source_key = proxied_key or parsed_key
if value is ma.missing:
Expand Down Expand Up @@ -110,7 +140,7 @@ def method(
if self._sealed:
raise EnvSealedError("Env has already been sealed. New values cannot be parsed.")
parsed_key, raw_value, proxied_key = self._get_from_environ(name, default)
self._fields[parsed_key] = ma.fields.Field(**kwargs)
self._fields[parsed_key] = ma.fields.Field()
source_key = proxied_key or parsed_key
if raw_value is ma.missing:
if self.eager:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import re
from setuptools import setup

INSTALL_REQUIRES = ["marshmallow>=2.7.0", "python-dotenv"]
INSTALL_REQUIRES = ["marshmallow>=3.0.0", "python-dotenv"]
DJANGO_REQUIRES = ["dj-database-url", "dj-email-url", "django-cache-url"]
EXTRAS_REQUIRE = {
"django": DJANGO_REQUIRES,
Expand Down
6 changes: 5 additions & 1 deletion tests/test_environs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import dj_email_url
import django_cache_url
import pytest
import marshmallow as ma
from marshmallow import fields, validate

import environs
Expand Down Expand Up @@ -155,7 +156,10 @@ def test_missing_raises_error(self, env):
assert exc.value.args[0] == 'Environment variable "FOO" not set'

def test_default_set(self, env):
assert env.str("FOO", missing="foo") == "foo"
if ma.__version_info__ >= (3, 13):
assert env.str("FOO", load_default="foo") == "foo"
else:
assert env.str("FOO", missing="foo") == "foo"
# Passed positionally
assert env.str("FOO", "foo") == "foo"

Expand Down
5 changes: 3 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
[tox]
envlist=
lint
py{36,37,38,39}-marshmallow3
py{36,37,38,39}-marshmallow{3,lowest}
py39-marshmallowdev

[testenv]
extras = tests
deps =
marshmallow3: marshmallow>=3.0.0a1,<4.0.0
marshmallowlowest: marshmallow==3.0.0
marshmallow3: marshmallow>=3.0.0,<4.0.0
marshmallowdev: https://github.com/marshmallow-code/marshmallow/archive/dev.tar.gz
commands = pytest {posargs}

Expand Down