Repository: sloria/environs

Issue:
Error message within prefixed() context
The error message does not prepend the prefix to error message output, when an error occurs in an env.prefixed() context. The code below illustrates the issue. The environment variable is named TEST_VAR, but the error message is showing VAR.

```python

######@shim-dev:~/workspace/shim (environ-adoption) 
$ export TEST_VAR=-1
######@shim-dev:~/workspace/shim (environ-adoption) 
$ ipython
Python 3.6.5 (default, Aug 15 2019, 19:51:45) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.7.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from environs import Env                                                                                                                                                                                                                                                     

In [2]: env = Env()                                                                                                                                                                                                                                                                  

In [3]: with env.prefixed("TEST_"): 
   ...:     invalid_test = env.int("VAR", validate=lambda n: n > 0) 
   ...:                                                                                                                                                                                                                                                                              
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
~/.pyenv/versions/3.6.5/lib/python3.6/site-packages/environs.py in method(self, name, default, subcast, **kwargs)
     55         try:
---> 56             value = field.deserialize(value)
     57         except ma.ValidationError as error:

~/.pyenv/versions/3.6.5/lib/python3.6/site-packages/marshmallow/fields.py in deserialize(self, value, attr, data)
    265         output = self._deserialize(value, attr, data)
--> 266         self._validate(output)
    267         return output

~/.pyenv/versions/3.6.5/lib/python3.6/site-packages/marshmallow/fields.py in _validate(self, value)
    205         if errors:
--> 206             raise ValidationError(errors, **kwargs)
    207 

ValidationError: ['Invalid value.']

The above exception was the direct cause of the following exception:

EnvError                                  Traceback (most recent call last)
<ipython-input-3-be1d17efe506> in <module>
      1 with env.prefixed("TEST_"):
----> 2     invalid_test = env.int("VAR", validate=lambda n: n > 0)
      3 

~/.pyenv/versions/3.6.5/lib/python3.6/site-packages/environs.py in method(self, name, default, subcast, **kwargs)
     56             value = field.deserialize(value)
     57         except ma.ValidationError as error:
---> 58             raise EnvError('Environment variable "{}" invalid: {}'.format(name, error.args[0])) from error
     59         else:
     60             self._values[parsed_key] = value

EnvError: Environment variable "VAR" invalid: ['Invalid value.']

In [4]:  

```

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc28b52..871fd8f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,13 @@ def https_url(value):
     return "https://" + value
 ```
 
+Bug fixes:
+
+- Fix error message for prefixed variables ([#102](https://github.com/sloria/environs/issues/102)).
+  Thanks [AGeekInside](https://github.com/AGeekInside) for reporting.
+
+Other changes:
+
 - _Backwards-incompatible_: Rename `Env.__parser_map__` to `Env.__custom_parsers__`.
 
 ## 5.2.1 (2019-08-08)
diff --git a/environs.py b/environs.py
index da97c0c..cb895f6 100644
--- a/environs.py
+++ b/environs.py
@@ -52,8 +52,9 @@ def _field2method(
             field = typing.cast(FieldFactory, field_or_factory)(subcast=subcast, missing=missing, **kwargs)
         parsed_key, raw_value, proxied_key = self._get_from_environ(name, ma.missing)
         self._fields[parsed_key] = field
+        source_key = proxied_key or parsed_key
         if raw_value is ma.missing and field.missing is ma.missing:
-            raise EnvError('Environment variable "{}" not set'.format(proxied_key or parsed_key))
+            raise EnvError('Environment variable "{}" not set'.format(source_key))
         if raw_value or raw_value == "":
             value = raw_value
         else:
@@ -63,7 +64,9 @@ def _field2method(
         try:
             value = field.deserialize(value)
         except ma.ValidationError as error:
-            raise EnvError('Environment variable "{}" invalid: {}'.format(name, error.args[0])) from error
+            raise EnvError(
+                'Environment variable "{}" invalid: {}'.format(source_key, error.args[0])
+            ) from error
         else:
             self._values[parsed_key] = value
             return value


Test patch:
diff --git a/tests/test_environs.py b/tests/test_environs.py
index b57b179..bc5d284 100644
--- a/tests/test_environs.py
+++ b/tests/test_environs.py
@@ -435,6 +435,11 @@ class TestPrefix:
             env("NOT_FOUND", "mydefault") == "mydefault"
         assert env.dump() == {"APP_STR": "foo", "APP_INT": 42, "APP_NOT_FOUND": "mydefault"}
 
+    def test_error_message_for_prefixed_var(self, env):
+        with env.prefixed("APP_"):
+            with pytest.raises(environs.EnvError, match='Environment variable "APP_INT" invalid'):
+                env.int("INT", validate=lambda val: val < 42)
+
 
 class TestNestedPrefix:
     @pytest.fixture(autouse=True)