Repository: scikit-hep/pyhf

Issue:
Catch unspecified/unknown kwargs
# Description

Came up in #620. The "bug" there is that the line had a typo:

```
p = w.model(measurement_name=None, patches=patches, modifiers_settings=msettings)
                                                            ^
```

instead of

```
p = w.model(measurement_name=None, patches=patches, modifier_settings=msettings)
```

We should probably catch unknown kwargs (e.g. pop them all off and make sure there's no unconsumed kwargs).

## Is your feature request related to a problem? Please describe.

Typos happen and we should probably help identify the typos.

### Describe the solution you'd like

Check if all kwargs are used up and raise an error if not.

### Describe alternatives you've considered

None.

# Relevant Issues and Pull Requests

- #620


Discussion:

Resolving patch:
diff --git a/src/pyhf/pdf.py b/src/pyhf/pdf.py
--- a/src/pyhf/pdf.py
+++ b/src/pyhf/pdf.py
@@ -222,12 +222,22 @@ def __init__(self, spec, **config_kwargs):
             spec, self.channel_nbins
         )
 
-        poiname = config_kwargs.get('poiname', 'mu')
+        # measurement_name is passed in via Workspace::model and this is a bug. We'll remove it here for now
+        # but needs to be fixed upstream. #836 is filed to keep track.
+        config_kwargs.pop('measurement_name', None)
+
+        poiname = config_kwargs.pop('poiname', 'mu')
+
         default_modifier_settings = {'normsys': {'interpcode': 'code1'}}
-        self.modifier_settings = (
-            config_kwargs.get('modifier_settings') or default_modifier_settings
+        self.modifier_settings = config_kwargs.pop(
+            'modifier_settings', default_modifier_settings
         )
 
+        if config_kwargs:
+            raise KeyError(
+                f"""Unexpected keyword argument(s): '{"', '".join(config_kwargs.keys())}'"""
+            )
+
         self.par_map = {}
         self.par_order = []
         self.poi_name = None
diff --git a/src/pyhf/workspace.py b/src/pyhf/workspace.py
--- a/src/pyhf/workspace.py
+++ b/src/pyhf/workspace.py
@@ -325,7 +325,7 @@ def get_measurement(self, **config_kwargs):
 
     def _get_measurement(self, **config_kwargs):
         """See `Workspace::get_measurement`."""
-        poi_name = config_kwargs.get('poi_name')
+        poi_name = config_kwargs.pop('poi_name', None)
         if poi_name:
             return {
                 'name': 'NormalMeasurement',
@@ -333,7 +333,7 @@ def _get_measurement(self, **config_kwargs):
             }
 
         if self.measurement_names:
-            measurement_name = config_kwargs.get('measurement_name')
+            measurement_name = config_kwargs.pop('measurement_name', None)
             if measurement_name:
                 if measurement_name not in self.measurement_names:
                     log.debug(
@@ -350,7 +350,7 @@ def _get_measurement(self, **config_kwargs):
                     self.measurement_names.index(measurement_name)
                 ]
 
-            measurement_index = config_kwargs.get('measurement_index')
+            measurement_index = config_kwargs.pop('measurement_index', None)
             if measurement_index:
                 return self['measurements'][measurement_index]
 
@@ -380,7 +380,7 @@ def model(self, **config_kwargs):
             'model being created for measurement {0:s}'.format(measurement['name'])
         )
 
-        patches = config_kwargs.get('patches', [])
+        patches = config_kwargs.pop('patches', [])
 
         modelspec = {
             'channels': self['channels'],


Test patch:
diff --git a/tests/test_pdf.py b/tests/test_pdf.py
--- a/tests/test_pdf.py
+++ b/tests/test_pdf.py
@@ -650,3 +650,46 @@ def test_sample_wrong_bins():
     }
     with pytest.raises(pyhf.exceptions.InvalidModel):
         pyhf.Model(spec)
+
+
+@pytest.mark.parametrize(
+    'measurements, msettings',
+    [
+        (
+            None,
+            {'normsys': {'interpcode': 'code4'}, 'histosys': {'interpcode': 'code4p'},},
+        )
+    ],
+)
+def test_unexpected_keyword_argument(measurements, msettings):
+    spec = {
+        "channels": [
+            {
+                "name": "singlechannel",
+                "samples": [
+                    {
+                        "name": "signal",
+                        "data": [5.0, 10.0],
+                        "modifiers": [
+                            {"name": "mu", "type": "normfactor", "data": None}
+                        ],
+                    },
+                    {
+                        "name": "background",
+                        "data": [50.0, 60.0],
+                        "modifiers": [
+                            {
+                                "name": "uncorr_bkguncrt",
+                                "type": "shapesys",
+                                "data": [5.0, 12.0],
+                            }
+                        ],
+                    },
+                ],
+            }
+        ]
+    }
+    with pytest.raises(KeyError):
+        pyhf.pdf._ModelConfig(
+            spec, measurement_name=measurements, modifiers_settings=msettings
+        )