Repository: IAMconsortium/pyam
Pull request #258 (2019-08-09 18:39:15)

add a fixture `logger.ignoreWarnings()`, use in `aggregate_region()`
# Please confirm that this PR has done the following:

- [x] Documentation Added
- [x] Tests added

# Description of PR

The function `filter()` prints a warning to the log if a returned `IamDataFrame` is empty. This makes sense in most instance (and should remain the default behaviour), but it produces verbose and potentially misleading output in some cases and larger scripts.

One such case (fixed in this PR) is the `aggregate_region()` function when the new `region` does not have any data yet - which is not a problem and should not raise a warning.

This PR adds a fixture `logger.adjust_log_level()` (see [logging.captureWarnings](https://docs.python.org/2/library/logging.html#logging.captureWarnings) for comparison) as short-hand to change the logging level around a block of code.

# Expected use

```
with logger.adjust_log_level():
    df.filter(model='foo')
```

coveralls: 
[![Coverage Status](https://coveralls.io/builds/25090886/badge)](https://coveralls.io/builds/25090886)

Coverage decreased (-0.2%) to 87.926% when pulling **10d302dfabdd4aed78927825d0b053ab310ae027 on danielhuppmann:fix/ignoreWarnings** into **3a83f912ff2b5e59b6a39eddefdbb7b2fb42a86e on IAMconsortium:master**.

danielhuppmann: @gidden, building the docs fails, all other tests pass - can you please review, and maybe take a look at why the docs fails? thanks!

Patch:
diff --git a/.travis.yml b/.travis.yml
index cb8d22e..3249ad3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -37,7 +37,7 @@ script:
   - make test
   # only test docs once to make sure everything works on most recent python
   - cd doc
-  - if [[ "${PYENV}" == "py37" && "${TRAVIS_OS_NAME}" != 'windows' ]]; then make html; fi
+  - if [[ "${PYENV}" == "py37" && "${TRAVIS_OS_NAME}" != 'windows' ]]; then conda install --yes kealib==1.4.7; make html; fi
   - cd ..
 
 after_success:
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 3cf842e..bbd0b84 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,6 +1,7 @@
 
 # Next Release
 
+- [#261](https://github.com/IAMconsortium/pyam/pull/261) Add a check that `keep` in `filter()` is a boolean
 - [#243](https://github.com/IAMconsortium/pyam/pull/243) Update `pyam.iiasa.Connection` to support all public and private database connections. DEPRECATED: the argument 'iamc15' has been deprecated in favor of names as queryable directly from the REST API.
 - [#241](https://github.com/IAMconsortium/pyam/pull/241) Add `set_meta_from_data` feature
 - [#236](https://github.com/IAMconsortium/pyam/pull/236) Add `swap_time_for_year` method and confirm datetime column is compatible with pyam features
diff --git a/ci/environment-conda-forge.txt b/ci/environment-conda-forge.txt
index cec0e53..097c3cb 100644
--- a/ci/environment-conda-forge.txt
+++ b/ci/environment-conda-forge.txt
@@ -5,5 +5,6 @@ seaborn==0.9.0
 gdal
 fiona
 "geopandas<0.5.0"
+"poppler<0.66"
 cython
 cartopy
diff --git a/pyam/core.py b/pyam/core.py
index e945530..b1c3575 100644
--- a/pyam/core.py
+++ b/pyam/core.py
@@ -3,7 +3,6 @@ import importlib
 import itertools
 import os
 import sys
-import warnings
 
 import numpy as np
 import pandas as pd
@@ -16,7 +15,7 @@ except ImportError:
 
 from pyam import plotting
 
-from pyam.logger import logger
+from pyam.logger import logger, adjust_log_level
 from pyam.run_control import run_control
 from pyam.utils import (
     write_sheet,
@@ -842,7 +841,8 @@ class IamDataFrame(object):
 
         # add components at the `region` level, defaults to all variables one
         # level below `variable` that are only present in `region`
-        region_df = self.filter(region=region)
+        with adjust_log_level():
+            region_df = self.filter(region=region)
         components = components or (
             set(region_df._variable_components(variable)).difference(
                 subregion_df._variable_components(variable)))
@@ -974,6 +974,10 @@ class IamDataFrame(object):
                ('month', 'hour', 'time')
              - 'regexp=True' disables pseudo-regexp syntax in `pattern_match()`
         """
+        if not isinstance(keep, bool):
+            msg = '`filter(keep={}, ...)` is not valid, must be boolean'
+            raise ValueError(msg.format(keep))
+
         _keep = self._apply_filters(**kwargs)
         _keep = _keep if keep else ~_keep
         ret = copy.deepcopy(self) if not inplace else self
diff --git a/pyam/logger.py b/pyam/logger.py
index 5000821..080e5a2 100644
--- a/pyam/logger.py
+++ b/pyam/logger.py
@@ -1,3 +1,4 @@
+from contextlib import contextmanager
 import logging
 
 # globally accessible logger
@@ -12,3 +13,12 @@ def logger():
         _LOGGER = logging.getLogger()
         _LOGGER.setLevel('INFO')
     return _LOGGER
+
+
+@contextmanager
+def adjust_log_level(level='ERROR'):
+    """Context manager to change log level"""
+    old_level = _LOGGER.getEffectiveLevel()
+    _LOGGER.setLevel(level)
+    yield
+    _LOGGER.setLevel(old_level)


Test patch:
diff --git a/tests/test_core.py b/tests/test_core.py
index 3bc0596..e4df621 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -201,8 +201,15 @@ def test_variable_depth_raises(test_df):
     pytest.raises(ValueError, test_df.filter, level='1/')
 
 
-def test_filter_error(test_df):
-    pytest.raises(ValueError, test_df.filter, foo='foo')
+def test_filter_error_illegal_column(test_df):
+    # filtering by column `foo` is not valid
+    pytest.raises(ValueError, test_df.filter, foo='test')
+
+
+def test_filter_error_keep(test_df):
+    # string or non-starred dict was mis-interpreted as `keep` kwarg, see #253
+    pytest.raises(ValueError, test_df.filter, model='foo', keep=1)
+    pytest.raises(ValueError, test_df.filter, dict(model='foo'))
 
 
 def test_filter_year(test_df):
diff --git a/tests/test_logger.py b/tests/test_logger.py
new file mode 100644
index 0000000..c5e90e0
--- /dev/null
+++ b/tests/test_logger.py
@@ -0,0 +1,9 @@
+from pyam import logger
+from pyam.logger import adjust_log_level
+
+
+def test_context_adjust_log_level():
+    assert logger().getEffectiveLevel() == 20
+    with adjust_log_level():
+        assert logger().getEffectiveLevel() == 40
+    assert logger().getEffectiveLevel() == 20