Repository: IAMconsortium/pyam
Pull request #147 (2018-12-05 13:28:42)

Fix wrong links and RST
It seems like that the code in this file was marked with markdown style instead of RST. Furthermore, the links to `pyam.IamDataFrame.filter` and `pyam.filter_by_meta` where not correctly specified.

# Please confirm that this PR has done the following:

- [ ] Tests Added
- [ ] Documentation Added
- [ ] Description in RELEASE_NOTES.md Added

## Adding to RELEASE_NOTES.md (remove section after adding to RELEASE_NOTES.md)

Please add a single line in the release notes similar to the following:

```
- (#XX)[http://link-to-pr.com] Added feature which does something
```

# Description of PR

Inline with https://github.com/openjournals/joss-reviews/issues/1095 this PR fixes some wrong formatting in data.rst. I don't think it is worth adding something to RELEASE_NOTES.md, but if you like, tell me and I will do it.


gidden: @danielhuppmann would you mind taking a look?
gidden: Thanks also for issuing the PR @Chilipp !

Patch:
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 64b2e83..a79e7b1 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,6 +1,7 @@
 
 # Next Release
 
+- [#151](https://github.com/IAMconsortium/pyam/pull/151) Fix bug where excel files were not being written on Windows and MacOSX (#149)
 - [#145](https://github.com/IAMconsortium/pyam/pull/145) Support full semantic and VCS-style versioning with `versioneer`
 
 # Release v0.1.2
diff --git a/doc/source/data.rst b/doc/source/data.rst
index fe2f606..44faf9c 100644
--- a/doc/source/data.rst
+++ b/doc/source/data.rst
@@ -16,23 +16,23 @@ MESSAGE V.4   AMPERE3-Base   World       Primary Energy  EJ/y      454.5     479
 ============  =============  ==========  ==============  ========  ========  ========  ========
 
 
-`pyam.IamDataFrame`
-^^^^^^^^^^^^^^^^^^^
+``pyam.IamDataFrame``
+^^^^^^^^^^^^^^^^^^^^^
 
-A `pyam.IamDataFrame` is a wrapper for two `pandas.DataFrame` instances:
+A ``pyam.IamDataFrame`` is a wrapper for two ``pandas.DataFrame`` instances:
 
  - `data`: The data table is a dataframe containing the timeseries data in
-   "long format". It has the columns `pyam.LONG_IDX = ['model', 'scenario',
-   'region', 'unit', 'year', 'value']`.
+   "long format". It has the columns ``pyam.LONG_IDX = ['model', 'scenario',
+   'region', 'unit', 'year', 'value']``.
 
  - `meta`: The meta table is a dataframe containing categorisation and
-   descriptive indicators. It has the index `pyam.META_IDX = ['model',
-   'scenario']`.
+   descriptive indicators. It has the index ``pyam.META_IDX = ['model',
+   'scenario']``.
 
 The standard output format is the IAMC-style "wide format", see the example
-above. This format can be accessed using `pd.IamDataFrame.timeseries()`,
-which returns a `pandas.DataFrame` with the index `pyam.IAMC_IDX = ['model',
-'scenario', 'region', 'variable', 'unit']` and the years as columns.
+above. This format can be accessed using :meth:`pyam.IamDataFrame.timeseries`,
+which returns a ``pandas.DataFrame`` with the index ``pyam.IAMC_IDX = ['model',
+'scenario', 'region', 'variable', 'unit']`` and the years as columns.
 
 Filtering
 ^^^^^^^^^
@@ -40,18 +40,14 @@ Filtering
 The `pyam` package provides two methods for filtering timeseries data:
 
 An existing IamDataFrame can be filtered using
-`pyam.IamDataFrame.filter(col=...)`_, where `col` can be any column of the
-`data` table (i.e., `['model', 'scenario', 'region', 'unit', 'year']) or any
-column of the `meta` table. The returned object is a new `pyam.IamDataFrame`
+:meth:`pyam.IamDataFrame.filter(col=...) <pyam.IamDataFrame.filter>`, where `col` can be any column of the
+`data` table (i.e., ``['model', 'scenario', 'region', 'unit', 'year']``) or any
+column of the `meta` table. The returned object is a new ``pyam.IamDataFrame``
 instance.
 
-A `pandas.DataFrame` with columns or index `['model', 'scenario']` can be
-filtered by any `meta` columns from a `pyam.IamDataFrame` using
-`pyam.filter_by_meta(data, df, col=..., join_meta=False)`_. The returned
-object is a `pandas.DataFrame` downselected to those models-and-scenarios where
+A ``pandas.DataFrame`` with columns or index ``['model', 'scenario']`` can be
+filtered by any `meta` columns from a ``pyam.IamDataFrame`` using
+:func:`pyam.filter_by_meta(data, df, col=..., join_meta=False) <pyam.filter_by_meta>`. The returned
+object is a ``pandas.DataFrame`` downselected to those models-and-scenarios where
 the `meta` column satisfies the criteria given by `col=...` .
 Optionally, the `meta` columns are joined to the returned dataframe.
-
- .. _`pyam.IamDataFrame.filter(col=...)` : IamDataFrame.html#pyam.IamDataFrame.filter
-
- .. _`pyam.filter_by_meta(data, df, col=..., join_meta=False)` : pyam_functions.html
diff --git a/pyam/core.py b/pyam/core.py
index 98274da..8e0e8ba 100644
--- a/pyam/core.py
+++ b/pyam/core.py
@@ -799,10 +799,13 @@ class IamDataFrame(object):
         if (path is None and writer is None) or \
            (path is not None and writer is not None):
             raise ValueError('Only one of path and writer must have a value')
+        close = writer is None
         if writer is None:
             writer = pd.ExcelWriter(path)
         self._to_file_format().to_excel(writer, sheet_name=sheet_name,
                                         index=index, **kwargs)
+        if close:
+            writer.close()
 
     def export_metadata(self, path):
         """Export metadata to Excel


Test patch:
diff --git a/tests/test_core.py b/tests/test_core.py
index b4821b4..e68f54b 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -49,6 +49,20 @@ def test_init_df_from_timeseries(test_df):
     pd.testing.assert_frame_equal(df.timeseries(), test_df.timeseries())
 
 
+def test_to_excel(test_df):
+    fname = 'foo_testing.xlsx'
+    test_df.to_excel(fname)
+    assert os.path.exists(fname)
+    os.remove(fname)
+
+
+def test_to_csv(test_df):
+    fname = 'foo_testing.csv'
+    test_df.to_csv(fname)
+    assert os.path.exists(fname)
+    os.remove(fname)
+
+
 def test_get_item(test_df):
     assert test_df['model'].unique() == ['a_model']
 
diff --git a/tests/test_tutorials.py b/tests/test_tutorials.py
index 74f0bb8..13b7b4b 100644
--- a/tests/test_tutorials.py
+++ b/tests/test_tutorials.py
@@ -63,6 +63,8 @@ def test_pyam_first_steps(capsys):
     fname = os.path.join(tut_path, 'pyam_first_steps.ipynb')
     nb, errors = _notebook_run(fname, capsys=capsys)
     assert errors == []
+    assert os.path.exists(os.path.join(tut_path, 'tutorial_export.xlsx'))
+    assert os.path.exists(os.path.join(tut_path, 'tutorial_metadata.xlsx'))
 
 
 @pytest.mark.skipif(not jupyter_installed, reason=jupyter_reason)