Skip to content

Commit

Permalink
Re-export validate module from marshmallow (#391)
Browse files Browse the repository at this point in the history
* Re-export validate module from marshmallow

* Update docs and changelog
  • Loading branch information
sloria authored Jan 10, 2025
1 parent 8b1228d commit f6a0115
Show file tree
Hide file tree
Showing 9 changed files with 46 additions and 44 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Features:
- Add `prefix` parameter to `Env` constructor ([#384](https://github.com/sloria/environs/issues/384)).
Thanks [arthurc0102](https://github.com/sloria/environs/issues/384)
for the suggestion.
- Re-export `validate` module from marshmallow ([#385](https://github.com/sloria/environs/issues/385)).

Bug fixes:

Expand Down
31 changes: 16 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,34 +219,35 @@ year = env.int("YEAR") # =>2020
# export NODE_ENV='invalid'
# export EMAIL='^_^'

from environs import env, ValidationError
from marshmallow.validate import OneOf, Length, Email
from environs import env, validate, ValidationError


# simple validator
def validator(n):
if n <= 0:
raise ValidationError("Invalid value.")


env.int("TTL", validate=validator)
# => Environment variable "TTL" invalid: ['Invalid value.']


# using marshmallow validators
# built-in validators (provided by marshmallow)
env.str(
"NODE_ENV",
validate=OneOf(
validate=validate.OneOf(
["production", "development"], error="NODE_ENV must be one of: {choices}"
),
)
# => Environment variable "NODE_ENV" invalid: ['NODE_ENV must be one of: production, development']

# multiple validators
env.str("EMAIL", validate=[Length(min=4), Email()])
env.str("EMAIL", validate=[validate.Length(min=4), validate.Email()])
# => Environment variable "EMAIL" invalid: ['Shorter than minimum length 4.', 'Not a valid email address.']


# custom validator
def validator(n):
if n <= 0:
raise ValidationError("Invalid value.")


env.int("TTL", validate=validator)
# => Environment variable "TTL" invalid: ['Invalid value.']
```

`environs.validate` is equivalent to [`marshmallow.validate`](https://marshmallow.readthedocs.io/en/stable/marshmallow.validate.html), so you can use any of the validators provided by that module.

## Deferred validation

By default, a validation error is raised immediately upon calling a parser method for an invalid environment variable.
Expand Down
10 changes: 4 additions & 6 deletions examples/deferred_validation_example.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import os
from pprint import pprint

from marshmallow.validate import Email, Length, OneOf, Range

from environs import Env, EnvValidationError
from environs import Env, EnvValidationError, validate

os.environ["TTL"] = "-2"
os.environ["NODE_ENV"] = "invalid"
os.environ["EMAIL"] = "^_^"


env = Env(eager=False)
TTL = env.int("TTL", validate=Range(min=0, max=100))
TTL = env.int("TTL", validate=validate.Range(min=0, max=100))
NODE_ENV = env.str(
"NODE_ENV",
validate=OneOf(
validate=validate.OneOf(
["production", "development"], error="NODE_ENV must be one of: {choices}"
),
)
EMAIL = env.str("EMAIL", validate=[Length(min=4), Email()])
EMAIL = env.str("EMAIL", validate=[validate.Length(min=4), validate.Email()])

# This will raise an error with the combined validation messages
try:
Expand Down
3 changes: 1 addition & 2 deletions examples/django_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@
import os
from pprint import pprint

import environs
from environs import env

env = environs.Env()
env.read_env()

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
Expand Down
3 changes: 1 addition & 2 deletions examples/marshmallow_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import marshmallow as ma

from environs import Env
from environs import env

os.environ["STATIC_PATH"] = "app/static"

Expand All @@ -16,7 +16,6 @@ def _serialize(self, value, *args, **kwargs):
return str(value)


env = Env()
env.parser_from_field("path", PathField)

static_path = env.path("STATIC_PATH")
Expand Down
3 changes: 1 addition & 2 deletions examples/plugin_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from furl import furl as Furl
from yarl import URL

from environs import Env
from environs import env

##### This is the beginning of the plugin code #####

Expand All @@ -28,7 +28,6 @@ def setup(env):

# Our application activates the plugin using the setup function

env = Env()
setup(env)

# We now have the 'furl' and 'yurl' methods available
Expand Down
32 changes: 17 additions & 15 deletions examples/validation_example.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
import os

from marshmallow.validate import Email, Length, OneOf

from environs import Env, EnvError
from environs import EnvError, ValidationError, env, validate

os.environ["TTL"] = "-2"
os.environ["NODE_ENV"] = "invalid"
os.environ["EMAIL"] = "^_^"


env = Env()

# simple validator
try:
env.int("TTL", validate=lambda n: n > 0)
except EnvError as err:
print(err)

# marshmallow validator
# built-in validator
try:
env.str(
"NODE_ENV",
validate=OneOf(
validate=validate.OneOf(
["production", "development"], error="NODE_ENV must be one of: {choices}"
),
)
Expand All @@ -31,6 +20,19 @@

# multiple validators
try:
env.str("EMAIL", validate=[Length(min=4), Email()])
env.str("EMAIL", validate=[validate.Length(min=4), validate.Email()])
except EnvError as err:
print(err)


# custom validator
def validate_ttl(value):
if value <= 0:
raise ValidationError("TTL must be greater than 0")
return value


try:
env.int("TTL", validate=validate_ttl)
except EnvError as err:
print(err)
4 changes: 3 additions & 1 deletion src/environs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@
_EXPANDED_VAR_PATTERN = re.compile(r"(?<!\\)\$\{([A-Za-z0-9_]+)(:-[^\}:]*)?\}")


# Reexport marshmallow's ValidationError. Custom validators should raise this for invalid input.
# Reexport marshmallow's validate module and ValidationError.
validate = ma.validate
# Custom validators should raise this for invalid input.
ValidationError = ma.ValidationError


Expand Down
3 changes: 2 additions & 1 deletion tests/test_environs.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
import django_cache_url
import marshmallow as ma
import pytest
from marshmallow import fields, validate
from marshmallow import fields
from packaging.version import Version

import environs
from environs import validate

HERE = pathlib.Path(__file__).parent
MARSHMALLOW_VERSION = Version(importlib.metadata.version("marshmallow"))
Expand Down

0 comments on commit f6a0115

Please sign in to comment.