From a73fc6a78871988cd842e52111c00879cf90882b Mon Sep 17 00:00:00 2001 From: Matthew Gidden Date: Fri, 10 May 2019 14:39:34 +0200 Subject: [PATCH] release candidate 0.2.0 (#233) * deprecate filters argument * remove unused version info in setup.py * update gallery for filter deprecation * bump references down one toc level * update release notes * starting to work on a release procedure * better message in makefile * use VENV_DIR everywhere * more notes * finish release procedure * additional note * get pyam version in docs from introspection * try new list * new list again * final release notes --- Makefile | 10 +++-- RELEASE_NOTES.md | 13 ++++++ RELEASE_PROCEDURE.md | 42 +++++++++++++++++++ doc/source/_examples/plot_bar.py | 10 ++--- .../_examples/plot_bar_with_net_lines.py | 16 +++---- doc/source/_examples/plot_pie.py | 12 +++--- doc/source/_examples/plot_ranges.py | 4 +- doc/source/_examples/plot_regions.py | 4 +- doc/source/_examples/plot_stack.py | 10 ++--- doc/source/_examples/plot_timeseries.py | 4 +- doc/source/conf.py | 5 ++- doc/source/install.rst | 2 +- doc/source/tutorials/pyam_first_steps.ipynb | 7 ++-- pyam/core.py | 12 ++---- setup.py | 4 -- 15 files changed, 101 insertions(+), 54 deletions(-) create mode 100644 RELEASE_PROCEDURE.md diff --git a/Makefile b/Makefile index e5b15c99a..cf0d22b29 100644 --- a/Makefile +++ b/Makefile @@ -48,10 +48,11 @@ publish-on-testpypi: $(VENV_DIR) ## publish release on test PyPI -rm -rf build dist @status=$$(git status --porcelain); \ if test "x$${status}" = x; then \ - ./venv/bin/python setup.py bdist_wheel --universal; \ - ./venv/bin/twine upload -r testpypi dist/*; \ + $(VENV_DIR)/bin/python setup.py bdist_wheel --universal; \ + $(VENV_DIR)/bin/twine upload -r testpypi dist/*; \ else \ echo Working directory is dirty >&2; \ + echo run git status --porcelain to find dirty files >&2; \ fi; .PHONY: publish-on-pypi @@ -59,10 +60,11 @@ publish-on-pypi: $(VENV_DIR) ## publish release on PyPI -rm -rf build dist @status=$$(git status --porcelain); \ if test "x$${status}" = x; then \ - ./venv/bin/python setup.py bdist_wheel --universal; \ - ./venv/bin/twine upload dist/*; \ + $(VENV_DIR)/bin/python setup.py bdist_wheel --universal; \ + $(VENV_DIR)/bin/twine upload dist/*; \ else \ echo Working directory is dirty >&2; \ + echo run git status --porcelain to find dirty files >&2; \ fi; .PHONY: regenerate-test-figures diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a35cc1796..6766a1683 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,6 +1,19 @@ # Next Release +# Release v0.2.0 + +## Highlights + +- the `filters` argument in `IamDataFrame.filter()` has been deprecated +- `pd.date_time` now has **experimental** supported for time-related columns +- plots now support the official IPCC scenario color palatte +- native support for putting legends outside of plot axes +- dataframes can now be initialized with default values, making reading raw + datasets easier + +## Individual Updates + - [#228](https://github.com/IAMconsortium/pyam/pull/228) Update development environment creation instructions and make pandas requirement more specific - [#219](https://github.com/IAMconsortium/pyam/pull/219) Add ability to query metadata from iiasa data sources - [#214](https://github.com/IAMconsortium/pyam/pull/214) Tidy up requirements specifications a little diff --git a/RELEASE_PROCEDURE.md b/RELEASE_PROCEDURE.md new file mode 100644 index 000000000..38bd04e66 --- /dev/null +++ b/RELEASE_PROCEDURE.md @@ -0,0 +1,42 @@ + +# Steps required for a release + +1. Make a release candidate branch and pull request it into master with the following updates: + 1. Deprecate any stated portion of the API (you can find them by searching the code base for "deprecate") + 1. Update `RELEASE_NOTES.md` (see the examples of other releases) + - create new section under "# Next Release" with "# Release v" + - For this new release, add a "Highlights section" with some of the most important updates + - Make a new heading "## Individual Updates" before the PR listing + 1. Confirm that the PR passes all tests and checks + 1. Tag the release number: `git tag v`, e.g., `git tag v1.2.0` + - **THIS IS NOT THE TAGGED COMMIT WE WILL DISTRIBUTE, IT IS ONLY FOR TESTING** + - **DO NOT PUSH THIS TAG TO UPSTREAM** + 1. Run `make publish-on-testpypi` + - this should "just work" if it does not, fix any issues, retag (`git tag + -d` then `git tag`), and try again + - note, you need an account on https://test.pypi.org + 1. Once successful, delete the tag, and merge the candidate PR into master on Github +1. Switch to now updated master branch: `git checkout master` and `git pull upstream master` +1. Tag the release number: `git tag v`, e.g., `git tag v1.2.0` + - `versioneer` automatically updates the version number based on the tag + - this is now the official tagged commit +1. Push the tag upstream: `git push upstream --tags` +1. Run `make publish-on-pypi` + - note, you need an account on https://pypi.org + - this will make wheels that all us to be installed via `pip install` +1. Update on conda-forge: + - Issue a PR on https://github.com/conda-forge/pyam-feedstock following the + instructions there for how to edit the `recipe/meta.yaml` file + - confirm that any new depedencies are added there + - Note that you can get the correct SHA256 hash from + https://pypi.org/project/pyam-iamc/#files once that step has been + successful +1. Make a new release on Github + - Make sure that you choose the same tag name as was used earlier + - Copy the markdown from `RELEASE_NOTES.md` into the release description box +1. Announce it on our mailing list: https://groups.google.com/forum/#!forum/pyam + - Again, just copy the now rendered HTML from the Github release directly in + the email +1. Confirm that the doc page is updated to the latest release: https://pyam-iamc.readthedocs.io/ + +And that's it! Whew... diff --git a/doc/source/_examples/plot_bar.py b/doc/source/_examples/plot_bar.py index eff8eff24..67aea799a 100644 --- a/doc/source/_examples/plot_bar.py +++ b/doc/source/_examples/plot_bar.py @@ -18,9 +18,9 @@ ############################### # We generated a simple stacked bar chart as below -data = df.filter({'variable': 'Emissions|CO2|*', - 'level': 0, - 'region': 'World'}) +data = df.filter(variable='Emissions|CO2|*', + level=0, + region='World') fig, ax = plt.subplots(figsize=(10, 10)) data.bar_plot(ax=ax, stacked=True) @@ -40,8 +40,8 @@ # IamDataFrame can be used. data = (df - .filter({'variable': 'Emissions|CO2'}) - .filter({'region': 'World'}, keep=False) + .filter(variable='Emissions|CO2') + .filter(region='World', keep=False) ) fig, ax = plt.subplots(figsize=(10, 10)) data.bar_plot(ax=ax, bars='region', stacked=True, cmap='tab20') diff --git a/doc/source/_examples/plot_bar_with_net_lines.py b/doc/source/_examples/plot_bar_with_net_lines.py index 2a1bf6969..3632995d7 100644 --- a/doc/source/_examples/plot_bar_with_net_lines.py +++ b/doc/source/_examples/plot_bar_with_net_lines.py @@ -20,10 +20,10 @@ ############################### # We generated a simple stacked bar chart as below -data = df.filter({'variable': 'Emissions|CO2|*', - 'level': 0, - 'region': 'World', - 'year': [2040, 2050, 2060]}) +data = df.filter(variable='Emissions|CO2|*', + level=0, + region='World', + year=[2040, 2050, 2060]) fig, ax = plt.subplots(figsize=(6, 6)) data.bar_plot(ax=ax, stacked=True) @@ -34,10 +34,10 @@ # Sometimes stacked bar charts have negative entries - in that case it helps to # add a line showing the net value. -data = df.filter({'variable': 'Emissions|CO2|*', - 'level': 0, - 'region': 'World', - 'year': [2040, 2050, 2060]}) +data = df.filter(variable='Emissions|CO2|*', + level=0, + region='World', + year=[2040, 2050, 2060]) fig, ax = plt.subplots(figsize=(6, 6)) data.bar_plot(ax=ax, stacked=True) diff --git a/doc/source/_examples/plot_pie.py b/doc/source/_examples/plot_pie.py index 094c4a926..1e5f6f751 100644 --- a/doc/source/_examples/plot_pie.py +++ b/doc/source/_examples/plot_pie.py @@ -17,10 +17,10 @@ ############################### # We generated a simple stacked bar chart as below -data = df.filter({'variable': 'Emissions|CO2|*', - 'level': 0, - 'year': 2050, - 'region': 'World'}) +data = df.filter(variable='Emissions|CO2|*', + level=0, + year=2050, + region='World') fig, ax = plt.subplots(figsize=(10, 10)) data.pie_plot(ax=ax) @@ -40,8 +40,8 @@ # IamDataFrame can be used. data = (df - .filter({'variable': 'Emissions|CO2', 'year': 2050}) - .filter({'region': 'World'}, keep=False) + .filter(variable='Emissions|CO2', year=2050) + .filter(region='World', keep=False) ) data.pie_plot(category='region', cmap='tab20') plt.show() diff --git a/doc/source/_examples/plot_ranges.py b/doc/source/_examples/plot_ranges.py index 83bb85cc0..12e1e38b8 100644 --- a/doc/source/_examples/plot_ranges.py +++ b/doc/source/_examples/plot_ranges.py @@ -16,8 +16,8 @@ df = pyam.IamDataFrame(fname, encoding='ISO-8859-1') df = (df - .filter({'variable': 'Emissions|CO2'}) - .filter({'region': 'World'}, keep=False) + .filter(variable='Emissions|CO2') + .filter(region='World', keep=False) ) print(df.head()) diff --git a/doc/source/_examples/plot_regions.py b/doc/source/_examples/plot_regions.py index 30234a79e..81f7ed265 100644 --- a/doc/source/_examples/plot_regions.py +++ b/doc/source/_examples/plot_regions.py @@ -14,8 +14,8 @@ df = pyam.IamDataFrame(fname, encoding='ISO-8859-1') df = (df - .filter({'variable': 'Emissions|CO2', 'year': 2050}) - .filter({'region': 'World'}, keep=False) + .filter(variable='Emissions|CO2', year=2050) + .filter(region='World', keep=False) .map_regions('iso', region_col='R5_region') ) diff --git a/doc/source/_examples/plot_stack.py b/doc/source/_examples/plot_stack.py index 9f50dfc48..71abc8d95 100644 --- a/doc/source/_examples/plot_stack.py +++ b/doc/source/_examples/plot_stack.py @@ -18,9 +18,9 @@ ############################### # We generated a simple stacked stack chart as below -data = df.filter({'variable': 'Emissions|CO2|*', - 'level': 0, - 'region': 'World'}) +data = df.filter(variable='Emissions|CO2|*', + level=0, + region='World') data.interpolate(2015) # some values are missing fig, ax = plt.subplots(figsize=(10, 10)) @@ -33,8 +33,8 @@ # IamDataFrame can be used. data = (df - .filter({'variable': 'Emissions|CO2'}) - .filter({'region': 'World'}, keep=False) + .filter(variable='Emissions|CO2') + .filter(region='World', keep=False) ) fig, ax = plt.subplots(figsize=(10, 10)) data.stack_plot(ax=ax, stack='region', cmap='tab20', total=True) diff --git a/doc/source/_examples/plot_timeseries.py b/doc/source/_examples/plot_timeseries.py index 4e90ee881..9fee5ea78 100644 --- a/doc/source/_examples/plot_timeseries.py +++ b/doc/source/_examples/plot_timeseries.py @@ -12,8 +12,8 @@ df = pyam.IamDataFrame(fname, encoding='ISO-8859-1') df = (df - .filter({'variable': 'Emissions|CO2'}) - .filter({'region': 'World'}, keep=False) + .filter(variable='Emissions|CO2') + .filter(region='World', keep=False) ) print(df.head()) diff --git a/doc/source/conf.py b/doc/source/conf.py index 342cf1fce..be4aa634a 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -77,9 +77,10 @@ # built documents. # # The short X.Y version. -version = '0.1.2' +import pyam +version = pyam.__version__ # The full version, including alpha/beta/rc tags. -release = '0.1.2' +release = pyam.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/source/install.rst b/doc/source/install.rst index 8ddb1a5f9..b7924d292 100644 --- a/doc/source/install.rst +++ b/doc/source/install.rst @@ -65,7 +65,7 @@ The depedencies for building this documentation are: :literal: References -********** +~~~~~~~~~~ .. bibliography:: refs.bib :style: plain diff --git a/doc/source/tutorials/pyam_first_steps.ipynb b/doc/source/tutorials/pyam_first_steps.ipynb index b4d149201..188bf07f7 100644 --- a/doc/source/tutorials/pyam_first_steps.ipynb +++ b/doc/source/tutorials/pyam_first_steps.ipynb @@ -407,8 +407,8 @@ "3. Check for every scenario from the `AMPERE` project\n", " whether the value for `'Primary Energy|Coal'` exceeds 400 EJ/y in mid-century.\n", "\n", - "The `validate()` function takes a `filters` dictionary to perform the checks on a selection of models/scenarios\n", - "similar to the functions introduced above. \n", + "The `validate()` function performs the checks on models and scenarios.\n", + "\n", "The ``criteria`` argument can specify a valid range by an upper and lower bound (``up``, ``lo``) for a variable and a subset of years to which the validation is applied - all scenarios with a value in at least one year outside that range are considered to *not satisfy* the validation.\n", "\n", "By setting the argument ``exclude=True``, all scenarios failing the validation will be categorized as ``exclude`` in the metadata. This allows to remove these scenarios from subsequent analysis or figures." @@ -438,8 +438,7 @@ "metadata": {}, "outputs": [], "source": [ - "pyam.validate(df, \n", - " filters={'region': 'World', 'scenario': 'AMPERE*'}, \n", + "pyam.validate(df.filter(region='World', scenario='AMPERE*'), \n", " criteria={'Primary Energy|Coal': {'up': 400, 'year': 2050}}\n", ")" ] diff --git a/pyam/core.py b/pyam/core.py index 36ba1684a..14c13c720 100644 --- a/pyam/core.py +++ b/pyam/core.py @@ -588,7 +588,7 @@ def rename(self, mapping=None, inplace=False, append=False, # if append is True, downselect and append renamed data if append: - df = self.filter(filters) + df = self.filter(**filters) # note that `append(other, inplace=True)` returns None return self.append(df.rename(mapping), inplace=inplace) @@ -894,7 +894,7 @@ def _exclude_on_fail(self, df): logger().info('{} non-valid scenario{} will be excluded' .format(len(idx), '' if len(idx) == 1 else 's')) - def filter(self, filters=None, keep=True, inplace=False, **kwargs): + def filter(self, keep=True, inplace=False, **kwargs): """Return a filtered IamDataFrame (i.e., a subset of current data) Parameters @@ -903,7 +903,7 @@ def filter(self, filters=None, keep=True, inplace=False, **kwargs): keep all scenarios satisfying the filters (if True) or the inverse inplace: bool, default False if True, do operation inplace and return None - filters by kwargs or dict (deprecated): + filters by kwargs: The following columns are available for filtering: - metadata columns: filter by category assignment - 'model', 'scenario', 'region', 'variable', 'unit': @@ -917,12 +917,6 @@ def filter(self, filters=None, keep=True, inplace=False, **kwargs): ('month', 'hour', 'time') - 'regexp=True' disables pseudo-regexp syntax in `pattern_match()` """ - if filters is not None: - msg = '`filters` keyword argument in `filter()` is deprecated ' + \ - 'and will be removed in the next release' - warnings.warn(msg) - kwargs.update(filters) - _keep = self._apply_filters(**kwargs) _keep = _keep if keep else ~_keep ret = copy.deepcopy(self) if not inplace else self diff --git a/setup.py b/setup.py index 52f5782c6..24cd32c82 100755 --- a/setup.py +++ b/setup.py @@ -18,10 +18,6 @@ \/_/ \/_____/ \/_/\/_/ \/_/ \/_/ """ -INFO = { - 'version': '0.1.2', -} - REQUIREMENTS = [ 'argparse', 'numpy',