From ecb635a171ba494f64713804979804aa9c1c0ea0 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Thu, 7 May 2020 08:10:42 -0400 Subject: [PATCH 1/4] Fix FigureWidget attribute error on wildcard import with ipywidgets not installed (#2445) * Fix `from plotly.graph_objs import *` when ipywidgets is not installed Introduces a dummy `plotly.missing_ipywidgets.FigureWidget` class that is imported as plotly.graph_obj(ect)?s when ipywidgets is not installed with a supported version. This class is a BaseFigure subclass that raises an ImportError in the constructor. * Add changelog entry --- CHANGELOG.md | 7 ++++ packages/python/plotly/codegen/__init__.py | 27 +++++++------ .../plotly/plotly/graph_objects/__init__.py | 22 ++++++----- .../plotly/plotly/graph_objs/__init__.py | 22 ++++++----- .../plotly/plotly/missing_ipywidgets.py | 15 ++++++++ .../test_missing_ipywigets.py | 38 +++++++++++++++++++ .../test_validate_no_frames.py | 8 +++- 7 files changed, 109 insertions(+), 30 deletions(-) create mode 100644 packages/python/plotly/plotly/missing_ipywidgets.py create mode 100644 packages/python/plotly/plotly/tests/test_core/test_figure_widget_backend/test_missing_ipywigets.py diff --git a/CHANGELOG.md b/CHANGELOG.md index cd428c03688..9b423962209 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [4.7.1] - ??? + +### Fixed + + - Fix `AttributeError: module 'plotly.graph_objs' has no attribute 'FigureWidget'` exception on `from plotly.graph_objs import *` when `ipywidgets` is not installed. Error also occurred when importing `plotly.figure_factor`. It is now possible to import `plotly.graph_objs.FigureWidget` when `ipywidgets` is not installed, and an informative `ImportError` exception will be raised in the `FigureWidget` constructor ([#2443](https://github.com/plotly/plotly.py/issues/2443), [#1111](https://github.com/plotly/plotly.py/issues/1111)). + + ## [4.7.0] - 2020-05-06 ### Updated diff --git a/packages/python/plotly/codegen/__init__.py b/packages/python/plotly/codegen/__init__.py index 82f3b94abd9..3f61adbe5dd 100644 --- a/packages/python/plotly/codegen/__init__.py +++ b/packages/python/plotly/codegen/__init__.py @@ -269,14 +269,14 @@ def perform_codegen(): optional_figure_widget_import = f""" if sys.version_info < (3, 7): try: - import ipywidgets - from distutils.version import LooseVersion - if LooseVersion(ipywidgets.__version__) >= LooseVersion('7.0.0'): + import ipywidgets as _ipywidgets + from distutils.version import LooseVersion as _LooseVersion + if _LooseVersion(_ipywidgets.__version__) >= _LooseVersion("7.0.0"): from ..graph_objs._figurewidget import FigureWidget - del LooseVersion - del ipywidgets - except ImportError: - pass + else: + raise ImportError() + except Exception: + from ..missing_ipywidgets import FigureWidget else: __all__.append("FigureWidget") orig_getattr = __getattr__ @@ -285,12 +285,17 @@ def __getattr__(import_name): try: import ipywidgets from distutils.version import LooseVersion - if LooseVersion(ipywidgets.__version__) >= LooseVersion('7.0.0'): + + if LooseVersion(ipywidgets.__version__) >= LooseVersion("7.0.0"): from ..graph_objs._figurewidget import FigureWidget + return FigureWidget - except ImportError: - pass - + else: + raise ImportError() + except Exception: + from ..missing_ipywidgets import FigureWidget + return FigureWidget + return orig_getattr(import_name) """ # ### __all__ ### diff --git a/packages/python/plotly/plotly/graph_objects/__init__.py b/packages/python/plotly/plotly/graph_objects/__init__.py index d63d8ff757b..cb52fcecac9 100644 --- a/packages/python/plotly/plotly/graph_objects/__init__.py +++ b/packages/python/plotly/plotly/graph_objects/__init__.py @@ -261,15 +261,15 @@ if sys.version_info < (3, 7): try: - import ipywidgets - from distutils.version import LooseVersion + import ipywidgets as _ipywidgets + from distutils.version import LooseVersion as _LooseVersion - if LooseVersion(ipywidgets.__version__) >= LooseVersion("7.0.0"): + if _LooseVersion(_ipywidgets.__version__) >= _LooseVersion("7.0.0"): from ..graph_objs._figurewidget import FigureWidget - del LooseVersion - del ipywidgets - except ImportError: - pass + else: + raise ImportError() + except Exception: + from ..missing_ipywidgets import FigureWidget else: __all__.append("FigureWidget") orig_getattr = __getattr__ @@ -284,7 +284,11 @@ def __getattr__(import_name): from ..graph_objs._figurewidget import FigureWidget return FigureWidget - except ImportError: - pass + else: + raise ImportError() + except Exception: + from ..missing_ipywidgets import FigureWidget + + return FigureWidget return orig_getattr(import_name) diff --git a/packages/python/plotly/plotly/graph_objs/__init__.py b/packages/python/plotly/plotly/graph_objs/__init__.py index f39d43dd1e8..e2861522c42 100644 --- a/packages/python/plotly/plotly/graph_objs/__init__.py +++ b/packages/python/plotly/plotly/graph_objs/__init__.py @@ -261,15 +261,15 @@ if sys.version_info < (3, 7): try: - import ipywidgets - from distutils.version import LooseVersion + import ipywidgets as _ipywidgets + from distutils.version import LooseVersion as _LooseVersion - if LooseVersion(ipywidgets.__version__) >= LooseVersion("7.0.0"): + if _LooseVersion(_ipywidgets.__version__) >= _LooseVersion("7.0.0"): from ..graph_objs._figurewidget import FigureWidget - del LooseVersion - del ipywidgets - except ImportError: - pass + else: + raise ImportError() + except Exception: + from ..missing_ipywidgets import FigureWidget else: __all__.append("FigureWidget") orig_getattr = __getattr__ @@ -284,7 +284,11 @@ def __getattr__(import_name): from ..graph_objs._figurewidget import FigureWidget return FigureWidget - except ImportError: - pass + else: + raise ImportError() + except Exception: + from ..missing_ipywidgets import FigureWidget + + return FigureWidget return orig_getattr(import_name) diff --git a/packages/python/plotly/plotly/missing_ipywidgets.py b/packages/python/plotly/plotly/missing_ipywidgets.py new file mode 100644 index 00000000000..9f5d5726087 --- /dev/null +++ b/packages/python/plotly/plotly/missing_ipywidgets.py @@ -0,0 +1,15 @@ +from .basedatatypes import BaseFigure + + +class FigureWidget(BaseFigure): + """ + FigureWidget stand-in for use when ipywidgets is not installed. The only purpose + of this class is to provide something to import as + `plotly.graph_objs.FigureWidget` when ipywidgets is not installed. This class + simply raises an informative error message when the constructor is called + """ + + def __init__(self, *args, **kwargs): + raise ImportError( + "Please install ipywidgets>=7.0.0 to use the FigureWidget class" + ) diff --git a/packages/python/plotly/plotly/tests/test_core/test_figure_widget_backend/test_missing_ipywigets.py b/packages/python/plotly/plotly/tests/test_core/test_figure_widget_backend/test_missing_ipywigets.py new file mode 100644 index 00000000000..8e03e2c59a0 --- /dev/null +++ b/packages/python/plotly/plotly/tests/test_core/test_figure_widget_backend/test_missing_ipywigets.py @@ -0,0 +1,38 @@ +import pytest + +# Use wildcard import to make sure FigureWidget is always included +from plotly.graph_objects import * +from plotly.missing_ipywidgets import FigureWidget as FigureWidgetMissingIPywidgets + +try: + import ipywidgets as _ipywidgets + from distutils.version import LooseVersion as _LooseVersion + + if _LooseVersion(_ipywidgets.__version__) >= _LooseVersion("7.0.0"): + missing_ipywidgets = False + else: + raise ImportError() +except Exception: + missing_ipywidgets = True + + +if missing_ipywidgets: + + def test_import_figurewidget_without_ipywidgets(): + assert FigureWidget is FigureWidgetMissingIPywidgets + + with pytest.raises(ImportError): + # ipywidgets import error raised on construction, not import + FigureWidget() + + +else: + + def test_import_figurewidget_with_ipywidgets(): + from plotly.graph_objs._figurewidget import ( + FigureWidget as FigureWidgetWithIPywidgets, + ) + + assert FigureWidget is FigureWidgetWithIPywidgets + fig = FigureWidget() + assert isinstance(fig, FigureWidgetWithIPywidgets) diff --git a/packages/python/plotly/plotly/tests/test_core/test_figure_widget_backend/test_validate_no_frames.py b/packages/python/plotly/plotly/tests/test_core/test_figure_widget_backend/test_validate_no_frames.py index 81f07496954..ca5a2b5da40 100644 --- a/packages/python/plotly/plotly/tests/test_core/test_figure_widget_backend/test_validate_no_frames.py +++ b/packages/python/plotly/plotly/tests/test_core/test_figure_widget_backend/test_validate_no_frames.py @@ -2,9 +2,15 @@ import plotly.graph_objs as go import pytest +try: + go.FigureWidget() + figure_widget_available = True +except ImportError: + figure_widget_available = False + class TestNoFrames(TestCase): - if "FigureWidget" in go.__dict__.keys(): + if figure_widget_available: def test_no_frames_in_constructor_kwarg(self): with pytest.raises(ValueError): From ca743027d5478517111130f0f31f5dcd50ca50cb Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Thu, 7 May 2020 16:04:24 -0400 Subject: [PATCH 2/4] Fix `TypeError: unhashable type: 'Template'` during `Figure` construction when `plotly.io.templates.default` is set to a `Template` object rather than a string. --- CHANGELOG.md | 1 + packages/python/plotly/plotly/basedatatypes.py | 10 ++++++++-- .../tests/test_core/test_graph_objs/test_template.py | 6 ++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b423962209..ec0906d5d84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Fix `AttributeError: module 'plotly.graph_objs' has no attribute 'FigureWidget'` exception on `from plotly.graph_objs import *` when `ipywidgets` is not installed. Error also occurred when importing `plotly.figure_factor`. It is now possible to import `plotly.graph_objs.FigureWidget` when `ipywidgets` is not installed, and an informative `ImportError` exception will be raised in the `FigureWidget` constructor ([#2443](https://github.com/plotly/plotly.py/issues/2443), [#1111](https://github.com/plotly/plotly.py/issues/1111)). + - Fix `TypeError: unhashable type: 'Template'` during `Figure` construction when `plotly.io.templates.default` is set to a `Template` object rather than a string. ## [4.7.0] - 2020-05-06 diff --git a/packages/python/plotly/plotly/basedatatypes.py b/packages/python/plotly/plotly/basedatatypes.py index 87da535702a..65f138f96c2 100644 --- a/packages/python/plotly/plotly/basedatatypes.py +++ b/packages/python/plotly/plotly/basedatatypes.py @@ -1951,8 +1951,14 @@ def _initialize_layout_template(self): if self._allow_disable_validation: self._layout_obj._validate = False try: - template_dict = pio.templates[pio.templates.default] - self._layout_obj.template = template_dict + if isinstance(pio.templates.default, BasePlotlyType): + # Template object. Don't want to actually import `Template` + # here for performance so we check against `BasePlotlyType` + template_object = pio.templates.default + else: + # Name of registered template object + template_object = pio.templates[pio.templates.default] + self._layout_obj.template = template_object finally: self._layout_obj._validate = self._validate diff --git a/packages/python/plotly/plotly/tests/test_core/test_graph_objs/test_template.py b/packages/python/plotly/plotly/tests/test_core/test_graph_objs/test_template.py index d6ff6dc8995..a3061dfe5aa 100644 --- a/packages/python/plotly/plotly/tests/test_core/test_graph_objs/test_template.py +++ b/packages/python/plotly/plotly/tests/test_core/test_graph_objs/test_template.py @@ -175,6 +175,12 @@ def test_template_in(self): def test_template_iter(self): self.assertIn("test_template", set(pio.templates)) + def test_template_default_as_object(self): + template = go.layout.Template({"layout": {"font": {"family": "Rockwell"}}}) + pio.templates.default = template + fig = go.Figure() + self.assertEqual(fig.layout.template, template) + class TestToTemplated(TestCaseNoTemplate): def test_move_layout_nested_properties(self): From e026ef52b132d9a177c1a65ae7f91ccad79d587d Mon Sep 17 00:00:00 2001 From: Nicolas Kruchten Date: Fri, 8 May 2020 08:41:00 -0400 Subject: [PATCH 3/4] bump versions for 4.7.1 --- CHANGELOG.md | 4 ++-- README.md | 10 +++++----- doc/python/getting-started.md | 8 ++++---- .../javascript/jupyterlab-plotly/package-lock.json | 2 +- packages/javascript/jupyterlab-plotly/package.json | 2 +- packages/javascript/plotlywidget/package-lock.json | 2 +- packages/javascript/plotlywidget/package.json | 2 +- packages/python/plotly/plotly/_widget_version.py | 2 +- packages/python/plotly/plotlywidget/static/index.js | 2 +- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec0906d5d84..599942e04c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [4.7.1] - ??? +## [4.7.1] - 2020-05-08 ### Fixed - - Fix `AttributeError: module 'plotly.graph_objs' has no attribute 'FigureWidget'` exception on `from plotly.graph_objs import *` when `ipywidgets` is not installed. Error also occurred when importing `plotly.figure_factor`. It is now possible to import `plotly.graph_objs.FigureWidget` when `ipywidgets` is not installed, and an informative `ImportError` exception will be raised in the `FigureWidget` constructor ([#2443](https://github.com/plotly/plotly.py/issues/2443), [#1111](https://github.com/plotly/plotly.py/issues/1111)). + - Fix `AttributeError: module 'plotly.graph_objs' has no attribute 'FigureWidget'` exception on `from plotly.graph_objs import *` when `ipywidgets` is not installed. Error also occurred when importing `plotly.figure_factor`. It is now possible to import `plotly.graph_objs.FigureWidget` when `ipywidgets` is not installed, and an informative `ImportError` exception will be raised in the `FigureWidget` constructor ([#2443](https://github.com/plotly/plotly.py/issues/2443), [#1111](https://github.com/plotly/plotly.py/issues/1111)). - Fix `TypeError: unhashable type: 'Template'` during `Figure` construction when `plotly.io.templates.default` is set to a `Template` object rather than a string. diff --git a/README.md b/README.md index baa39353e6f..309021a5575 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ ## Quickstart -`pip install plotly==4.7.0` +`pip install plotly==4.7.1` Inside [Jupyter notebook](https://jupyter.org/install) (installable with `pip install "notebook>=5.3" "ipywidgets>=7.2"`): @@ -82,13 +82,13 @@ Built on top of [plotly.js](https://github.com/plotly/plotly.js), `plotly.py` is plotly.py may be installed using pip... ``` -pip install plotly==4.7.0 +pip install plotly==4.7.1 ``` or conda. ``` -conda install -c plotly plotly=4.7.0 +conda install -c plotly plotly=4.7.1 ``` ### Jupyter Notebook Support @@ -125,10 +125,10 @@ Then run the following commands to install the required JupyterLab extensions (n ``` # Basic JupyterLab renderer support -jupyter labextension install jupyterlab-plotly@4.7.0 +jupyter labextension install jupyterlab-plotly@4.7.1 # OPTIONAL: Jupyter widgets extension for FigureWidget support -jupyter labextension install @jupyter-widgets/jupyterlab-manager plotlywidget@4.7.0 +jupyter labextension install @jupyter-widgets/jupyterlab-manager plotlywidget@4.7.1 ``` Please check out our [Troubleshooting guide](https://plotly.com/python/troubleshooting/) if you run into any problems with JupyterLab. diff --git a/doc/python/getting-started.md b/doc/python/getting-started.md index de9d3493805..9eb0e2256d9 100644 --- a/doc/python/getting-started.md +++ b/doc/python/getting-started.md @@ -49,13 +49,13 @@ Thanks to deep integration with the [orca](https://github.com/plotly/orca) image plotly.py may be installed using pip... ``` -$ pip install plotly==4.7.0 +$ pip install plotly==4.7.1 ``` or conda. ``` -$ conda install -c plotly plotly=4.7.0 +$ conda install -c plotly plotly=4.7.1 ``` This package contains everything you need to write figures to standalone HTML files. @@ -135,10 +135,10 @@ Then run the following commands to install the required JupyterLab extensions (n ``` # JupyterLab renderer support -jupyter labextension install jupyterlab-plotly@4.7.0 +jupyter labextension install jupyterlab-plotly@4.7.1 # OPTIONAL: Jupyter widgets extension -jupyter labextension install @jupyter-widgets/jupyterlab-manager plotlywidget@4.7.0 +jupyter labextension install @jupyter-widgets/jupyterlab-manager plotlywidget@4.7.1 ``` These packages contain everything you need to run JupyterLab... diff --git a/packages/javascript/jupyterlab-plotly/package-lock.json b/packages/javascript/jupyterlab-plotly/package-lock.json index 4be1a6ad364..58dc70b06f9 100644 --- a/packages/javascript/jupyterlab-plotly/package-lock.json +++ b/packages/javascript/jupyterlab-plotly/package-lock.json @@ -1,6 +1,6 @@ { "name": "jupyterlab-plotly", - "version": "4.7.0", + "version": "4.7.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/javascript/jupyterlab-plotly/package.json b/packages/javascript/jupyterlab-plotly/package.json index ef6bae373b7..2ae021f2db6 100644 --- a/packages/javascript/jupyterlab-plotly/package.json +++ b/packages/javascript/jupyterlab-plotly/package.json @@ -1,6 +1,6 @@ { "name": "jupyterlab-plotly", - "version": "4.7.0", + "version": "4.7.1", "description": "The plotly JupyterLab extension", "author": "The plotly.py team", "license": "MIT", diff --git a/packages/javascript/plotlywidget/package-lock.json b/packages/javascript/plotlywidget/package-lock.json index 8a2337fa377..f857017d648 100644 --- a/packages/javascript/plotlywidget/package-lock.json +++ b/packages/javascript/plotlywidget/package-lock.json @@ -1,6 +1,6 @@ { "name": "plotlywidget", - "version": "4.7.0", + "version": "4.7.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/javascript/plotlywidget/package.json b/packages/javascript/plotlywidget/package.json index 0f2959dca23..562183c2dd9 100644 --- a/packages/javascript/plotlywidget/package.json +++ b/packages/javascript/plotlywidget/package.json @@ -1,6 +1,6 @@ { "name": "plotlywidget", - "version": "4.7.0", + "version": "4.7.1", "description": "The plotly JupyterLab extension", "author": "The plotly.py team", "license": "MIT", diff --git a/packages/python/plotly/plotly/_widget_version.py b/packages/python/plotly/plotly/_widget_version.py index 7c78bc1fb16..f851f7c6266 100644 --- a/packages/python/plotly/plotly/_widget_version.py +++ b/packages/python/plotly/plotly/_widget_version.py @@ -2,4 +2,4 @@ # for automated dev builds # # It is edited by hand prior to official releases -__frontend_version__ = "4.7.0" +__frontend_version__ = "4.7.1" diff --git a/packages/python/plotly/plotlywidget/static/index.js b/packages/python/plotly/plotlywidget/static/index.js index 91d7e46335e..224056b4ca7 100644 --- a/packages/python/plotly/plotlywidget/static/index.js +++ b/packages/python/plotly/plotlywidget/static/index.js @@ -94,7 +94,7 @@ module.exports = g; /* 1 */ /***/ (function(module, exports) { -module.exports = {"name":"plotlywidget","version":"4.7.0","description":"The plotly JupyterLab extension","author":"The plotly.py team","license":"MIT","main":"src/index.js","repository":{"type":"git","url":"https://github.com/plotly/plotly.py"},"keywords":["jupyter","widgets","ipython","ipywidgets","plotly"],"files":["src/**/*.js","dist/*.js","style/*.*"],"scripts":{"build":"webpack","clean":"rimraf dist/ && rimraf ../../python/plotly/plotlywidget/static'","test":"echo \"Error: no test specified\" && exit 1"},"devDependencies":{"webpack":"^3.10.0","rimraf":"^2.6.1","ify-loader":"^1.1.0","typescript":"~3.1.1"},"dependencies":{"plotly.js":"^1.54.1","@jupyter-widgets/base":"^2.0.0 || ^3.0.0","lodash":"^4.17.4"},"jupyterlab":{"extension":"src/jupyterlab-plugin.js"}} +module.exports = {"name":"plotlywidget","version":"4.7.1","description":"The plotly JupyterLab extension","author":"The plotly.py team","license":"MIT","main":"src/index.js","repository":{"type":"git","url":"https://github.com/plotly/plotly.py"},"keywords":["jupyter","widgets","ipython","ipywidgets","plotly"],"files":["src/**/*.js","dist/*.js","style/*.*"],"scripts":{"build":"webpack","clean":"rimraf dist/ && rimraf ../../python/plotly/plotlywidget/static'","test":"echo \"Error: no test specified\" && exit 1"},"devDependencies":{"webpack":"^3.10.0","rimraf":"^2.6.1","ify-loader":"^1.1.0","typescript":"~3.1.1"},"dependencies":{"plotly.js":"^1.54.1","@jupyter-widgets/base":"^2.0.0 || ^3.0.0","lodash":"^4.17.4"},"jupyterlab":{"extension":"src/jupyterlab-plugin.js"}} /***/ }), /* 2 */ From f6a29af7ab4562d1352572d22465a6dfc4ad32d8 Mon Sep 17 00:00:00 2001 From: William C Grisaitis Date: Sat, 9 May 2020 21:00:22 -0400 Subject: [PATCH 4/4] Fix markdown syntax, typo in docs --- doc/python/creating-and-updating-figures.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/doc/python/creating-and-updating-figures.md b/doc/python/creating-and-updating-figures.md index 005dd6704a7..e210b481bf0 100644 --- a/doc/python/creating-and-updating-figures.md +++ b/doc/python/creating-and-updating-figures.md @@ -613,20 +613,22 @@ There are also `for_each_xaxis()` and `for_each_yaxis()` methods that are analog ### Other Update Methods Figures created with the plotly.py graphing library also support: - - the `update_layout_images()` method in order to [update background layout images](/python/images/), - - `update_annotations()` in order to [update annotations](/python/text-and-annotations/#multiple-annotations), - - and `update-shapes()` in order to [update shapes](/python/shapes/). + + - the `update_layout_images()` method in order to [update background layout images](/python/images/), + - `update_annotations()` in order to [update annotations](/python/text-and-annotations/#multiple-annotations), + - and `update_shapes()` in order to [update shapes](/python/shapes/). #### Chaining Figure Operations All of the figure update operations described above are methods that return a reference to the figure being modified. This makes it possible the chain multiple figure modification operations together into a single expression. Here is an example of a chained expression that creates: - - a faceted scatter plot with OLS trend lines using Plotly Express, - - sets the title font size using `update_layout()`, - - disables vertical grid lines using `update_xaxes()`, - - updates the width and dash pattern of the trend lines using `update_traces()`, - - and then displays the figure using `show()`. + + - a faceted scatter plot with OLS trend lines using Plotly Express, + - sets the title font size using `update_layout()`, + - disables vertical grid lines using `update_xaxes()`, + - updates the width and dash pattern of the trend lines using `update_traces()`, + - and then displays the figure using `show()`. ```python import plotly.express as px