Skip to content

Commit

Permalink
Add log_level handler (#92)
Browse files Browse the repository at this point in the history
  • Loading branch information
sloria committed Jul 13, 2019
1 parent 6771e55 commit 680b825
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 3 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,10 @@

### 5.1.0 (unreleased)

Features:

- Add `env.log_level` ([#7](https://github.com/sloria/environs/issues/7)).

Other changes:

- Improve typings.
Expand Down
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -54,6 +54,7 @@ export TTL=42
export ENABLE_LOGIN=true
export GITHUB_REPOS=webargs,konch,ped
export COORDINATES=23.3,50.0
export LOG_LEVEL=DEBUG
```

Parse them with environs...
Expand All @@ -71,6 +72,7 @@ secret = env("SECRET") # => raises error if not set
max_connections = env.int("MAX_CONNECTIONS") # => 100
ship_date = env.date("SHIP_DATE") # => datetime.date(1984, 6, 25)
ttl = env.timedelta("TTL") # => datetime.timedelta(0, 42)
log_level = env.log_level("LOG_LEVEL") # => logging.DEBUG

# providing a default value
enable_login = env.bool("ENABLE_LOGIN", False) # => True
Expand Down Expand Up @@ -98,6 +100,7 @@ The following are all type-casting methods of `Env`:
- `env.timedelta` (assumes value is an integer in seconds)
- `env.url`
- `env.uuid`
- `env.log_level`
- `env.path` (casts to a [`pathlib.Path`](https://docs.python.org/3/library/pathlib.html))

## Reading `.env` files
Expand Down
13 changes: 13 additions & 0 deletions environs.py
Expand Up @@ -2,6 +2,7 @@
import inspect
import functools
import json as pyjson
import logging
import os
import re
import typing
Expand Down Expand Up @@ -156,6 +157,17 @@ def _deserialize(self, value, *args, **kwargs) -> Path:
return Path(ret)


class LogLevelField(ma.fields.Int):
def _format_num(self, value):
try:
return super()._format_num(value)
except (TypeError, ValueError) as error:
if hasattr(logging, value):
return getattr(logging, value)
else:
raise ma.ValidationError("Not a valid log level.") from error


class Env:
"""An environment variable reader."""

Expand All @@ -173,6 +185,7 @@ class Env:
datetime=_field2method(ma.fields.DateTime, "datetime"),
date=_field2method(ma.fields.Date, "date"),
path=_field2method(PathField, "path"),
log_level=_field2method(LogLevelField, "log_level"),
timedelta=_field2method(ma.fields.TimeDelta, "timedelta"),
uuid=_field2method(ma.fields.UUID, "uuid"),
url=_field2method(URLField, "url"),
Expand Down
19 changes: 16 additions & 3 deletions tests/test_environs.py
@@ -1,5 +1,4 @@
from __future__ import unicode_literals

import logging
import uuid
import datetime as dt
import urllib.parse
Expand Down Expand Up @@ -106,7 +105,7 @@ def test_dict_with_default_from_string(self, set_env, env):
def test_dict_with_default_from_dict(self, set_env, env):
assert env.dict("DICT", {"key1": "1"}) == {"key1": "1"}

def test_decimat_cast(self, set_env, env):
def test_decimal_cast(self, set_env, env):
set_env({"DECIMAL": "12.34"})
assert env.decimal("DECIMAL") == Decimal("12.34")

Expand Down Expand Up @@ -157,6 +156,17 @@ def test_path_cast(self, set_env, env):
res = env.path("PTH")
assert isinstance(res, pathlib.Path)

def test_log_level_cast(self, set_env, env):
set_env({"LOG_LEVEL": "WARNING", "LOG_LEVEL_INT": str(logging.WARNING)})
assert env.log_level("LOG_LEVEL_INT") == logging.WARNING
assert env.log_level("LOG_LEVEL") == logging.WARNING

def test_invalid_log_level(self, set_env, env):
set_env({"LOG_LEVEL": "INVALID"})
with pytest.raises(environs.EnvError) as excinfo:
env.log_level("LOG_LEVEL")
assert "Not a valid log level" in excinfo.value.args[0]

@pytest.mark.parametrize("url", ["foo", "42", "foo@bar"])
def test_invalid_url(self, url, set_env, env):
set_env({"URL": url})
Expand Down Expand Up @@ -320,6 +330,7 @@ def test_dump(self, set_env, env):
"DTIME": dtime.isoformat(),
"URLPARSE": "http://stevenloria.com/projects/?foo=42",
"PTH": "/home/sloria",
"LOG_LEVEL": "WARNING",
}
)

Expand All @@ -328,6 +339,7 @@ def test_dump(self, set_env, env):
env.datetime("DTIME")
env.url("URLPARSE")
env.path("PTH")
env.log_level("LOG_LEVEL")

result = env.dump()
assert result["STR"] == "foo"
Expand All @@ -338,6 +350,7 @@ def test_dump(self, set_env, env):
assert result["URLPARSE"] == "http://stevenloria.com/projects/?foo=42"
assert isinstance(result["PTH"], str)
assert result["PTH"] == str(pathlib.Path("/home/sloria"))
assert result["LOG_LEVEL"] == logging.WARNING

def test_env_with_custom_parser(self, set_env, env):
@env.parser_for("url")
Expand Down

0 comments on commit 680b825

Please sign in to comment.