Repository: nteract/papermill

Issue:
Support empty YAML configuration
Just a suggestion, but it would be nice if empty YAML inputs were allowed. If I run `papermill ... --parameters_file /path/to/empty/file`, it crashes out with the following error:

```python
Traceback (most recent call last):
  File "/usr/local/bin/papermill", line 8, in <module>
    sys.exit(papermill())
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/local/lib/python3.7/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/click/decorators.py", line 21, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/papermill/cli.py", line 228, in papermill
    parameters_final.update(read_yaml_file(files))
TypeError: 'NoneType' object is not iterable
```

The reason for this is that papermill [uses the result of `yaml.load` to update the `parameters_final` dictionary](https://github.com/nteract/papermill/blob/main/papermill/cli.py#L227). When the input to `yaml.load` is empty (either the file is empty or just has some boilerplate comments but no named parameters), then `yaml.load` returns `None`, i.e.

```python
import yaml

# These both evaluate to None
yaml.load('', Loader=yaml.SafeLoader)
yaml.load('# There are no configurable parameters for this notebook', Loader=yaml.SafeLoader)
```

This is a minor gripe and I can work around it, but it would make my orchestration code a lot easier to read if I could just do this and not have to worry about empty files:

```bash
for action in train evaluate do_something_else; do
    papermill "input/${action}.ipynb" "output/${action}.ipynb" --parameters_file "config/${action}.yml"
done
```

The fix here seems easy enough - just change any call to `yaml.load(...)` to `yaml.load(...) or {}`, i.e.

```python
...
parameters_final.update(yaml.load(base64.b64decode(params), Loader=NoDatesSafeLoader) or {})
...
parameters_final.update(read_yaml_file(files) or {})
...
parameters_final.update(yaml.load(params, Loader=NoDatesSafeLoader) or {})
...
```



Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/nteract/papermill/pull/564?src=pr&el=h1) Report
> Merging [#564](https://codecov.io/gh/nteract/papermill/pull/564?src=pr&el=desc) (56d990c) into [main](https://codecov.io/gh/nteract/papermill/commit/a501fe2352fccfd4499e2e44c155a0673b0f01c4?el=desc) (a501fe2) will **not change** coverage.
> The diff coverage is `100.00%`.



```diff
@@           Coverage Diff           @@
##             main     #564   +/-   ##
=======================================
  Coverage   92.35%   92.35%           
=======================================
  Files          16       16           
  Lines        1413     1413           
=======================================
  Hits         1305     1305           
  Misses        108      108           
```


Patch:
diff --git a/papermill/cli.py b/papermill/cli.py
index c2c541e..a9062d2 100755
--- a/papermill/cli.py
+++ b/papermill/cli.py
@@ -223,11 +223,11 @@ def papermill(
     if inject_output_path or inject_paths:
         parameters_final['PAPERMILL_OUTPUT_PATH'] = output_path
     for params in parameters_base64 or []:
-        parameters_final.update(yaml.load(base64.b64decode(params), Loader=NoDatesSafeLoader))
+        parameters_final.update(yaml.load(base64.b64decode(params), Loader=NoDatesSafeLoader) or {})
     for files in parameters_file or []:
-        parameters_final.update(read_yaml_file(files))
+        parameters_final.update(read_yaml_file(files) or {})
     for params in parameters_yaml or []:
-        parameters_final.update(yaml.load(params, Loader=NoDatesSafeLoader))
+        parameters_final.update(yaml.load(params, Loader=NoDatesSafeLoader) or {})
     for name, value in parameters or []:
         parameters_final[name] = _resolve_type(value)
     for name, value in parameters_raw or []:


Test patch:
diff --git a/papermill/tests/test_cli.py b/papermill/tests/test_cli.py
index 8788f16..d59b6a6 100755
--- a/papermill/tests/test_cli.py
+++ b/papermill/tests/test_cli.py
@@ -3,8 +3,10 @@
 """ Test the command line interface """
 
 import os
+from pathlib import Path
 import sys
 import subprocess
+import tempfile
 import uuid
 import nbclient
 
@@ -167,6 +169,31 @@ class TestCLI(unittest.TestCase):
             **self.augment_execute_kwargs(parameters={'a_date': '2019-01-01'})
         )
 
+    @patch(cli.__name__ + '.execute_notebook')
+    def test_parameters_empty(self, execute_patch):
+        # "#empty" ---base64--> "I2VtcHR5"
+        with tempfile.TemporaryDirectory() as tmpdir:
+            empty_yaml = Path(tmpdir) / 'empty.yaml'
+            empty_yaml.write_text('#empty')
+            self.runner.invoke(
+                papermill,
+                self.default_args
+                + [
+                    '--parameters_file',
+                    str(empty_yaml),
+                    '--parameters_yaml',
+                    '#empty',
+                    '--parameters_base64',
+                    'I2VtcHR5',
+                ],
+            )
+            execute_patch.assert_called_with(
+                **self.augment_execute_kwargs(
+                    # should be empty
+                    parameters={}
+                )
+            )
+
     @patch(cli.__name__ + '.execute_notebook')
     def test_parameters_yaml_override(self, execute_patch):
         self.runner.invoke(