Repository: IAMconsortium/pyam

Issue:
Regression: `IamDataFrame.load_meta` with csv filename string broken
PR #491 removed the wrapping of the `path` variable into `pathlib.Path`:

https://github.com/IAMconsortium/pyam/commit/3d31fdd36f9b1ca6088eadbc01d23857b9fab4ae#diff-473e3e462e12aec320a41b983e95a130a194c4700b63a0a5f97f153b899129e7L1737-R1745

Which has a call like `idf.load_meta("test.csv")` try to read the csv file with `pd.ExcelReader` in `read_pandas` at

https://github.com/IAMconsortium/pyam/blob/3d31fdd36f9b1ca6088eadbc01d23857b9fab4ae/pyam/utils.py#L116-L120

I'd suggest to put a
```
path = Path(path)
```
wrapper before line 116.


Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/IAMconsortium/pyam/pull/496?src=pr&el=h1) Report
> Merging [#496](https://codecov.io/gh/IAMconsortium/pyam/pull/496?src=pr&el=desc) (10a699d) into [master](https://codecov.io/gh/IAMconsortium/pyam/commit/76c33a51c57a62745af3cee30ea54ff89ea2e3d7?el=desc) (76c33a5) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/IAMconsortium/pyam/pull/496/graphs/tree.svg?width=650&height=150&src=pr&token=ThxWlNkonJ)](https://codecov.io/gh/IAMconsortium/pyam/pull/496?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #496      +/-   ##
==========================================
+ Coverage   92.96%   92.97%   +0.01%     
==========================================
  Files          41       41              
  Lines        4747     4755       +8     
==========================================
+ Hits         4413     4421       +8     
  Misses        334      334              
```


| [Impacted Files](https://codecov.io/gh/IAMconsortium/pyam/pull/496?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pyam/core.py](https://codecov.io/gh/IAMconsortium/pyam/pull/496/diff?src=pr&el=tree#diff-cHlhbS9jb3JlLnB5) | `92.32% <100.00%> (+<0.01%)` | :arrow_up: |
| [tests/test\_io.py](https://codecov.io/gh/IAMconsortium/pyam/pull/496/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9pby5weQ==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/IAMconsortium/pyam/pull/496?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/IAMconsortium/pyam/pull/496?src=pr&el=footer). Last update [76c33a5...ed87e16](https://codecov.io/gh/IAMconsortium/pyam/pull/496?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

danielhuppmann: pinging @coroa for a review

Patch:
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index b5f9472..b57d58a 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -8,6 +8,7 @@ starting with `data` will be parsed for timeseries data.
 
 ## Individual updates
 
+- [#496](https://github.com/IAMconsortium/pyam/pull/496) Enable loading meta from csv file
 - [#494](https://github.com/IAMconsortium/pyam/pull/494) Small performance improvements
 - [#491](https://github.com/IAMconsortium/pyam/pull/491) Allow loading meta from file without exclude columns or with empty rows
 - [#488](https://github.com/IAMconsortium/pyam/pull/488) Read all sheets starting with `data` when reading from xlsx
diff --git a/pyam/core.py b/pyam/core.py
index 8e66660..af448f2 100755
--- a/pyam/core.py
+++ b/pyam/core.py
@@ -1731,17 +1731,18 @@ class IamDataFrame(object):
 
         Parameters
         ----------
-        path : str or :class:`pathlib.Path`
-            A valid path
+        path : str, :class:`pathlib.Path` or :class:`pandas.ExcelFile`
+            A valid path or instance of an xlsx or csv file
         sheet_name : str, optional
-            Name of the sheet to be parsed
+            Name of the sheet to be parsed (if xlsx)
         ignore_conflict : bool, optional
             If `True`, values in `path` take precedence over existing `meta`.
             If `False`, raise an error in case of conflicts.
         kwargs
-            passed to :func:`pandas.read_excel`
+            Passed to :func:`pandas.read_excel` or :func:`pandas.read_csv`
         """
         # load from file
+        path = path if isinstance(path, pd.ExcelFile) else Path(path)
         df = read_pandas(path, sheet_name=sheet_name, **kwargs)
 
         # cast model-scenario column headers to lower-case (if necessary)


Test patch:
diff --git a/tests/test_io.py b/tests/test_io.py
index eb532ff..8b916e7 100644
--- a/tests/test_io.py
+++ b/tests/test_io.py
@@ -115,12 +115,10 @@ def test_init_df_with_na_unit(test_pd_df, tmpdir):
     ('foo', dict(sheet_name='foo'), False),
     ('foo', dict(sheet_name='foo'), True),
 ])
-def test_load_meta(test_pd_df, sheet_name, init_args, rename, tmpdir):
+def test_load_meta_xlsx(test_pd_df, sheet_name, init_args, rename, tmpdir):
     """Test loading meta from an Excel file"""
-    meta = META_DF.copy()
     # downselect meta
-    if rename:
-        meta = meta.iloc[0:1]
+    meta = META_DF.iloc[0:1] if rename else META_DF
 
     # initialize a new IamDataFrame directly from data and meta
     exp = IamDataFrame(test_pd_df, meta=meta)
@@ -136,6 +134,25 @@ def test_load_meta(test_pd_df, sheet_name, init_args, rename, tmpdir):
     assert_iamframe_equal(obs, exp)
 
 
+@pytest.mark.parametrize("rename", [True, False])
+def test_load_meta_csv(test_pd_df, rename, tmpdir):
+    """Test loading meta from an csv file"""
+    meta = META_DF.iloc[0:1] if rename else META_DF
+
+    # initialize a new IamDataFrame directly from data and meta
+    exp = IamDataFrame(test_pd_df, meta=meta)
+
+    # write meta to file (without an exclude col)
+    file = tmpdir / 'testing_io_meta.csv'
+    meta.reset_index().to_csv(file, index=False)
+
+    # initialize a new IamDataFrame and load meta from file
+    obs = IamDataFrame(test_pd_df)
+    obs.load_meta(file)
+
+    assert_iamframe_equal(obs, exp)
+
+
 def test_load_meta_wrong_index(test_df_year, tmpdir):
     """Loading meta without (at least) index cols as headers raises an error"""