From 378b8910e137de37b61bd44e81636530a3760d94 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 15 Jun 2019 09:30:13 -0400 Subject: [PATCH 01/11] Move chunked_requests submodule to `chart_studio` package --- contributing.md | 1 - .../submodules/chunked_requests/.gitignore | 0 .../submodules/chunked_requests/README.md | 0 .../submodules/chunked_requests/__init__.py | 0 .../submodules/chunked_requests/chunked_requests/__init__.py | 0 .../chunked_requests/chunked_requests/chunked_request.py | 0 .../submodules/chunked_requests/circle.yml | 0 .../submodules/chunked_requests/requirements.txt | 0 .../submodules/chunked_requests/test/__init__.py | 0 .../submodules/chunked_requests/test/package.json | 0 .../submodules/chunked_requests/test/server.js | 0 .../submodules/chunked_requests/test/tests.py | 0 12 files changed, 1 deletion(-) rename packages/python/{plotly => chart-studio}/submodules/chunked_requests/.gitignore (100%) rename packages/python/{plotly => chart-studio}/submodules/chunked_requests/README.md (100%) rename packages/python/{plotly => chart-studio}/submodules/chunked_requests/__init__.py (100%) rename packages/python/{plotly => chart-studio}/submodules/chunked_requests/chunked_requests/__init__.py (100%) rename packages/python/{plotly => chart-studio}/submodules/chunked_requests/chunked_requests/chunked_request.py (100%) rename packages/python/{plotly => chart-studio}/submodules/chunked_requests/circle.yml (100%) rename packages/python/{plotly => chart-studio}/submodules/chunked_requests/requirements.txt (100%) rename packages/python/{plotly => chart-studio}/submodules/chunked_requests/test/__init__.py (100%) rename packages/python/{plotly => chart-studio}/submodules/chunked_requests/test/package.json (100%) rename packages/python/{plotly => chart-studio}/submodules/chunked_requests/test/server.js (100%) rename packages/python/{plotly => chart-studio}/submodules/chunked_requests/test/tests.py (100%) diff --git a/contributing.md b/contributing.md index 5a7c6e6d4e7..63098d1913a 100644 --- a/contributing.md +++ b/contributing.md @@ -40,7 +40,6 @@ That's going to initialize the submodules we use in this project, update them so Here's what you need to know: changes to any files inside the following directories **will get overwritten**. These are synced with the submodules, if you need to change functionality there, you will need to make a pull request in the appropriate sub project repository. - chunked_requests -- graph_reference - mplexporter Additionally, there are some project shortcuts that live in the `makefile` file. You can read all about this in the `make_instructions.txt` file. OR, just run: diff --git a/packages/python/plotly/submodules/chunked_requests/.gitignore b/packages/python/chart-studio/submodules/chunked_requests/.gitignore similarity index 100% rename from packages/python/plotly/submodules/chunked_requests/.gitignore rename to packages/python/chart-studio/submodules/chunked_requests/.gitignore diff --git a/packages/python/plotly/submodules/chunked_requests/README.md b/packages/python/chart-studio/submodules/chunked_requests/README.md similarity index 100% rename from packages/python/plotly/submodules/chunked_requests/README.md rename to packages/python/chart-studio/submodules/chunked_requests/README.md diff --git a/packages/python/plotly/submodules/chunked_requests/__init__.py b/packages/python/chart-studio/submodules/chunked_requests/__init__.py similarity index 100% rename from packages/python/plotly/submodules/chunked_requests/__init__.py rename to packages/python/chart-studio/submodules/chunked_requests/__init__.py diff --git a/packages/python/plotly/submodules/chunked_requests/chunked_requests/__init__.py b/packages/python/chart-studio/submodules/chunked_requests/chunked_requests/__init__.py similarity index 100% rename from packages/python/plotly/submodules/chunked_requests/chunked_requests/__init__.py rename to packages/python/chart-studio/submodules/chunked_requests/chunked_requests/__init__.py diff --git a/packages/python/plotly/submodules/chunked_requests/chunked_requests/chunked_request.py b/packages/python/chart-studio/submodules/chunked_requests/chunked_requests/chunked_request.py similarity index 100% rename from packages/python/plotly/submodules/chunked_requests/chunked_requests/chunked_request.py rename to packages/python/chart-studio/submodules/chunked_requests/chunked_requests/chunked_request.py diff --git a/packages/python/plotly/submodules/chunked_requests/circle.yml b/packages/python/chart-studio/submodules/chunked_requests/circle.yml similarity index 100% rename from packages/python/plotly/submodules/chunked_requests/circle.yml rename to packages/python/chart-studio/submodules/chunked_requests/circle.yml diff --git a/packages/python/plotly/submodules/chunked_requests/requirements.txt b/packages/python/chart-studio/submodules/chunked_requests/requirements.txt similarity index 100% rename from packages/python/plotly/submodules/chunked_requests/requirements.txt rename to packages/python/chart-studio/submodules/chunked_requests/requirements.txt diff --git a/packages/python/plotly/submodules/chunked_requests/test/__init__.py b/packages/python/chart-studio/submodules/chunked_requests/test/__init__.py similarity index 100% rename from packages/python/plotly/submodules/chunked_requests/test/__init__.py rename to packages/python/chart-studio/submodules/chunked_requests/test/__init__.py diff --git a/packages/python/plotly/submodules/chunked_requests/test/package.json b/packages/python/chart-studio/submodules/chunked_requests/test/package.json similarity index 100% rename from packages/python/plotly/submodules/chunked_requests/test/package.json rename to packages/python/chart-studio/submodules/chunked_requests/test/package.json diff --git a/packages/python/plotly/submodules/chunked_requests/test/server.js b/packages/python/chart-studio/submodules/chunked_requests/test/server.js similarity index 100% rename from packages/python/plotly/submodules/chunked_requests/test/server.js rename to packages/python/chart-studio/submodules/chunked_requests/test/server.js diff --git a/packages/python/plotly/submodules/chunked_requests/test/tests.py b/packages/python/chart-studio/submodules/chunked_requests/test/tests.py similarity index 100% rename from packages/python/plotly/submodules/chunked_requests/test/tests.py rename to packages/python/chart-studio/submodules/chunked_requests/test/tests.py From d839703ac7556c2e4b175223a792011c50efd320 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 15 Jun 2019 09:52:51 -0400 Subject: [PATCH 02/11] moved submodules to top-level directory --- .../submodules => submodules}/chunked_requests/.gitignore | 0 .../submodules => submodules}/chunked_requests/README.md | 0 .../chunked_requests/__init__.py | 0 .../chunked_requests/chunked_requests/__init__.py | 0 .../chunked_requests/chunked_requests/chunked_request.py | 0 .../submodules => submodules}/chunked_requests/circle.yml | 0 .../chunked_requests/requirements.txt | 0 .../chunked_requests/test/__init__.py | 0 .../chunked_requests/test/package.json | 0 .../chunked_requests/test/server.js | 0 .../chunked_requests/test/tests.py | 0 .../submodules => submodules}/mplexporter/.gitignore | 0 .../submodules => submodules}/mplexporter/.travis.yml | 0 .../plotly/submodules => submodules}/mplexporter/LICENSE | 0 .../submodules => submodules}/mplexporter/MANIFEST.in | 8 ++++---- .../submodules => submodules}/mplexporter/README.md | 0 .../mplexporter/mplexporter/__init__.py | 0 .../mplexporter/mplexporter/_py3k_compat.py | 0 .../mplexporter/mplexporter/exporter.py | 0 .../mplexporter/mplexporter/renderers/__init__.py | 0 .../mplexporter/mplexporter/renderers/base.py | 0 .../mplexporter/mplexporter/renderers/fake_renderer.py | 0 .../mplexporter/mplexporter/renderers/vega_renderer.py | 0 .../mplexporter/mplexporter/renderers/vincent_renderer.py | 0 .../mplexporter/mplexporter/tests/__init__.py | 0 .../mplexporter/mplexporter/tests/test_basic.py | 0 .../mplexporter/mplexporter/tests/test_utils.py | 0 .../mplexporter/mplexporter/tools.py | 0 .../mplexporter/mplexporter/utils.py | 0 .../plotly/submodules => submodules}/mplexporter/setup.py | 0 30 files changed, 4 insertions(+), 4 deletions(-) rename {packages/python/chart-studio/submodules => submodules}/chunked_requests/.gitignore (100%) rename {packages/python/chart-studio/submodules => submodules}/chunked_requests/README.md (100%) rename {packages/python/chart-studio/submodules => submodules}/chunked_requests/__init__.py (100%) rename {packages/python/chart-studio/submodules => submodules}/chunked_requests/chunked_requests/__init__.py (100%) rename {packages/python/chart-studio/submodules => submodules}/chunked_requests/chunked_requests/chunked_request.py (100%) rename {packages/python/chart-studio/submodules => submodules}/chunked_requests/circle.yml (100%) rename {packages/python/chart-studio/submodules => submodules}/chunked_requests/requirements.txt (100%) rename {packages/python/chart-studio/submodules => submodules}/chunked_requests/test/__init__.py (100%) rename {packages/python/chart-studio/submodules => submodules}/chunked_requests/test/package.json (100%) rename {packages/python/chart-studio/submodules => submodules}/chunked_requests/test/server.js (100%) rename {packages/python/chart-studio/submodules => submodules}/chunked_requests/test/tests.py (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/.gitignore (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/.travis.yml (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/LICENSE (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/MANIFEST.in (94%) rename {packages/python/plotly/submodules => submodules}/mplexporter/README.md (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/mplexporter/__init__.py (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/mplexporter/_py3k_compat.py (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/mplexporter/exporter.py (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/mplexporter/renderers/__init__.py (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/mplexporter/renderers/base.py (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/mplexporter/renderers/fake_renderer.py (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/mplexporter/renderers/vega_renderer.py (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/mplexporter/renderers/vincent_renderer.py (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/mplexporter/tests/__init__.py (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/mplexporter/tests/test_basic.py (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/mplexporter/tests/test_utils.py (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/mplexporter/tools.py (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/mplexporter/utils.py (100%) rename {packages/python/plotly/submodules => submodules}/mplexporter/setup.py (100%) diff --git a/packages/python/chart-studio/submodules/chunked_requests/.gitignore b/submodules/chunked_requests/.gitignore similarity index 100% rename from packages/python/chart-studio/submodules/chunked_requests/.gitignore rename to submodules/chunked_requests/.gitignore diff --git a/packages/python/chart-studio/submodules/chunked_requests/README.md b/submodules/chunked_requests/README.md similarity index 100% rename from packages/python/chart-studio/submodules/chunked_requests/README.md rename to submodules/chunked_requests/README.md diff --git a/packages/python/chart-studio/submodules/chunked_requests/__init__.py b/submodules/chunked_requests/__init__.py similarity index 100% rename from packages/python/chart-studio/submodules/chunked_requests/__init__.py rename to submodules/chunked_requests/__init__.py diff --git a/packages/python/chart-studio/submodules/chunked_requests/chunked_requests/__init__.py b/submodules/chunked_requests/chunked_requests/__init__.py similarity index 100% rename from packages/python/chart-studio/submodules/chunked_requests/chunked_requests/__init__.py rename to submodules/chunked_requests/chunked_requests/__init__.py diff --git a/packages/python/chart-studio/submodules/chunked_requests/chunked_requests/chunked_request.py b/submodules/chunked_requests/chunked_requests/chunked_request.py similarity index 100% rename from packages/python/chart-studio/submodules/chunked_requests/chunked_requests/chunked_request.py rename to submodules/chunked_requests/chunked_requests/chunked_request.py diff --git a/packages/python/chart-studio/submodules/chunked_requests/circle.yml b/submodules/chunked_requests/circle.yml similarity index 100% rename from packages/python/chart-studio/submodules/chunked_requests/circle.yml rename to submodules/chunked_requests/circle.yml diff --git a/packages/python/chart-studio/submodules/chunked_requests/requirements.txt b/submodules/chunked_requests/requirements.txt similarity index 100% rename from packages/python/chart-studio/submodules/chunked_requests/requirements.txt rename to submodules/chunked_requests/requirements.txt diff --git a/packages/python/chart-studio/submodules/chunked_requests/test/__init__.py b/submodules/chunked_requests/test/__init__.py similarity index 100% rename from packages/python/chart-studio/submodules/chunked_requests/test/__init__.py rename to submodules/chunked_requests/test/__init__.py diff --git a/packages/python/chart-studio/submodules/chunked_requests/test/package.json b/submodules/chunked_requests/test/package.json similarity index 100% rename from packages/python/chart-studio/submodules/chunked_requests/test/package.json rename to submodules/chunked_requests/test/package.json diff --git a/packages/python/chart-studio/submodules/chunked_requests/test/server.js b/submodules/chunked_requests/test/server.js similarity index 100% rename from packages/python/chart-studio/submodules/chunked_requests/test/server.js rename to submodules/chunked_requests/test/server.js diff --git a/packages/python/chart-studio/submodules/chunked_requests/test/tests.py b/submodules/chunked_requests/test/tests.py similarity index 100% rename from packages/python/chart-studio/submodules/chunked_requests/test/tests.py rename to submodules/chunked_requests/test/tests.py diff --git a/packages/python/plotly/submodules/mplexporter/.gitignore b/submodules/mplexporter/.gitignore similarity index 100% rename from packages/python/plotly/submodules/mplexporter/.gitignore rename to submodules/mplexporter/.gitignore diff --git a/packages/python/plotly/submodules/mplexporter/.travis.yml b/submodules/mplexporter/.travis.yml similarity index 100% rename from packages/python/plotly/submodules/mplexporter/.travis.yml rename to submodules/mplexporter/.travis.yml diff --git a/packages/python/plotly/submodules/mplexporter/LICENSE b/submodules/mplexporter/LICENSE similarity index 100% rename from packages/python/plotly/submodules/mplexporter/LICENSE rename to submodules/mplexporter/LICENSE diff --git a/packages/python/plotly/submodules/mplexporter/MANIFEST.in b/submodules/mplexporter/MANIFEST.in similarity index 94% rename from packages/python/plotly/submodules/mplexporter/MANIFEST.in rename to submodules/mplexporter/MANIFEST.in index a8f80c2525f..7f16c16db10 100644 --- a/packages/python/plotly/submodules/mplexporter/MANIFEST.in +++ b/submodules/mplexporter/MANIFEST.in @@ -1,4 +1,4 @@ -include *.md -include LICENSE - -recursive-include mplexporter *.py +include *.md +include LICENSE + +recursive-include mplexporter *.py diff --git a/packages/python/plotly/submodules/mplexporter/README.md b/submodules/mplexporter/README.md similarity index 100% rename from packages/python/plotly/submodules/mplexporter/README.md rename to submodules/mplexporter/README.md diff --git a/packages/python/plotly/submodules/mplexporter/mplexporter/__init__.py b/submodules/mplexporter/mplexporter/__init__.py similarity index 100% rename from packages/python/plotly/submodules/mplexporter/mplexporter/__init__.py rename to submodules/mplexporter/mplexporter/__init__.py diff --git a/packages/python/plotly/submodules/mplexporter/mplexporter/_py3k_compat.py b/submodules/mplexporter/mplexporter/_py3k_compat.py similarity index 100% rename from packages/python/plotly/submodules/mplexporter/mplexporter/_py3k_compat.py rename to submodules/mplexporter/mplexporter/_py3k_compat.py diff --git a/packages/python/plotly/submodules/mplexporter/mplexporter/exporter.py b/submodules/mplexporter/mplexporter/exporter.py similarity index 100% rename from packages/python/plotly/submodules/mplexporter/mplexporter/exporter.py rename to submodules/mplexporter/mplexporter/exporter.py diff --git a/packages/python/plotly/submodules/mplexporter/mplexporter/renderers/__init__.py b/submodules/mplexporter/mplexporter/renderers/__init__.py similarity index 100% rename from packages/python/plotly/submodules/mplexporter/mplexporter/renderers/__init__.py rename to submodules/mplexporter/mplexporter/renderers/__init__.py diff --git a/packages/python/plotly/submodules/mplexporter/mplexporter/renderers/base.py b/submodules/mplexporter/mplexporter/renderers/base.py similarity index 100% rename from packages/python/plotly/submodules/mplexporter/mplexporter/renderers/base.py rename to submodules/mplexporter/mplexporter/renderers/base.py diff --git a/packages/python/plotly/submodules/mplexporter/mplexporter/renderers/fake_renderer.py b/submodules/mplexporter/mplexporter/renderers/fake_renderer.py similarity index 100% rename from packages/python/plotly/submodules/mplexporter/mplexporter/renderers/fake_renderer.py rename to submodules/mplexporter/mplexporter/renderers/fake_renderer.py diff --git a/packages/python/plotly/submodules/mplexporter/mplexporter/renderers/vega_renderer.py b/submodules/mplexporter/mplexporter/renderers/vega_renderer.py similarity index 100% rename from packages/python/plotly/submodules/mplexporter/mplexporter/renderers/vega_renderer.py rename to submodules/mplexporter/mplexporter/renderers/vega_renderer.py diff --git a/packages/python/plotly/submodules/mplexporter/mplexporter/renderers/vincent_renderer.py b/submodules/mplexporter/mplexporter/renderers/vincent_renderer.py similarity index 100% rename from packages/python/plotly/submodules/mplexporter/mplexporter/renderers/vincent_renderer.py rename to submodules/mplexporter/mplexporter/renderers/vincent_renderer.py diff --git a/packages/python/plotly/submodules/mplexporter/mplexporter/tests/__init__.py b/submodules/mplexporter/mplexporter/tests/__init__.py similarity index 100% rename from packages/python/plotly/submodules/mplexporter/mplexporter/tests/__init__.py rename to submodules/mplexporter/mplexporter/tests/__init__.py diff --git a/packages/python/plotly/submodules/mplexporter/mplexporter/tests/test_basic.py b/submodules/mplexporter/mplexporter/tests/test_basic.py similarity index 100% rename from packages/python/plotly/submodules/mplexporter/mplexporter/tests/test_basic.py rename to submodules/mplexporter/mplexporter/tests/test_basic.py diff --git a/packages/python/plotly/submodules/mplexporter/mplexporter/tests/test_utils.py b/submodules/mplexporter/mplexporter/tests/test_utils.py similarity index 100% rename from packages/python/plotly/submodules/mplexporter/mplexporter/tests/test_utils.py rename to submodules/mplexporter/mplexporter/tests/test_utils.py diff --git a/packages/python/plotly/submodules/mplexporter/mplexporter/tools.py b/submodules/mplexporter/mplexporter/tools.py similarity index 100% rename from packages/python/plotly/submodules/mplexporter/mplexporter/tools.py rename to submodules/mplexporter/mplexporter/tools.py diff --git a/packages/python/plotly/submodules/mplexporter/mplexporter/utils.py b/submodules/mplexporter/mplexporter/utils.py similarity index 100% rename from packages/python/plotly/submodules/mplexporter/mplexporter/utils.py rename to submodules/mplexporter/mplexporter/utils.py diff --git a/packages/python/plotly/submodules/mplexporter/setup.py b/submodules/mplexporter/setup.py similarity index 100% rename from packages/python/plotly/submodules/mplexporter/setup.py rename to submodules/mplexporter/setup.py From b65c3130957232b10ee4bd46c5c7616e6580da5d Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 15 Jun 2019 10:04:06 -0400 Subject: [PATCH 03/11] Delete submodule directories --- submodules/chunked_requests/.gitignore | 10 - submodules/chunked_requests/README.md | 38 -- submodules/chunked_requests/__init__.py | 0 .../chunked_requests/__init__.py | 1 - .../chunked_requests/chunked_request.py | 329 --------------- submodules/chunked_requests/circle.yml | 20 - submodules/chunked_requests/requirements.txt | 1 - submodules/chunked_requests/test/__init__.py | 0 submodules/chunked_requests/test/package.json | 5 - submodules/chunked_requests/test/server.js | 65 --- submodules/chunked_requests/test/tests.py | 197 --------- submodules/mplexporter/.gitignore | 46 --- submodules/mplexporter/.travis.yml | 32 -- submodules/mplexporter/LICENSE | 27 -- submodules/mplexporter/MANIFEST.in | 4 - submodules/mplexporter/README.md | 4 - .../mplexporter/mplexporter/__init__.py | 2 - .../mplexporter/mplexporter/_py3k_compat.py | 22 - .../mplexporter/mplexporter/exporter.py | 285 ------------- .../mplexporter/renderers/__init__.py | 12 - .../mplexporter/mplexporter/renderers/base.py | 388 ------------------ .../mplexporter/renderers/fake_renderer.py | 68 --- .../mplexporter/renderers/vega_renderer.py | 138 ------- .../mplexporter/renderers/vincent_renderer.py | 52 --- .../mplexporter/mplexporter/tests/__init__.py | 3 - .../mplexporter/tests/test_basic.py | 228 ---------- .../mplexporter/tests/test_utils.py | 34 -- submodules/mplexporter/mplexporter/tools.py | 52 --- submodules/mplexporter/mplexporter/utils.py | 362 ---------------- submodules/mplexporter/setup.py | 30 -- 30 files changed, 2455 deletions(-) delete mode 100644 submodules/chunked_requests/.gitignore delete mode 100644 submodules/chunked_requests/README.md delete mode 100644 submodules/chunked_requests/__init__.py delete mode 100644 submodules/chunked_requests/chunked_requests/__init__.py delete mode 100644 submodules/chunked_requests/chunked_requests/chunked_request.py delete mode 100644 submodules/chunked_requests/circle.yml delete mode 100644 submodules/chunked_requests/requirements.txt delete mode 100644 submodules/chunked_requests/test/__init__.py delete mode 100644 submodules/chunked_requests/test/package.json delete mode 100644 submodules/chunked_requests/test/server.js delete mode 100644 submodules/chunked_requests/test/tests.py delete mode 100644 submodules/mplexporter/.gitignore delete mode 100644 submodules/mplexporter/.travis.yml delete mode 100644 submodules/mplexporter/LICENSE delete mode 100644 submodules/mplexporter/MANIFEST.in delete mode 100644 submodules/mplexporter/README.md delete mode 100644 submodules/mplexporter/mplexporter/__init__.py delete mode 100644 submodules/mplexporter/mplexporter/_py3k_compat.py delete mode 100644 submodules/mplexporter/mplexporter/exporter.py delete mode 100644 submodules/mplexporter/mplexporter/renderers/__init__.py delete mode 100644 submodules/mplexporter/mplexporter/renderers/base.py delete mode 100644 submodules/mplexporter/mplexporter/renderers/fake_renderer.py delete mode 100644 submodules/mplexporter/mplexporter/renderers/vega_renderer.py delete mode 100644 submodules/mplexporter/mplexporter/renderers/vincent_renderer.py delete mode 100644 submodules/mplexporter/mplexporter/tests/__init__.py delete mode 100644 submodules/mplexporter/mplexporter/tests/test_basic.py delete mode 100644 submodules/mplexporter/mplexporter/tests/test_utils.py delete mode 100644 submodules/mplexporter/mplexporter/tools.py delete mode 100644 submodules/mplexporter/mplexporter/utils.py delete mode 100644 submodules/mplexporter/setup.py diff --git a/submodules/chunked_requests/.gitignore b/submodules/chunked_requests/.gitignore deleted file mode 100644 index 8dc73c206fa..00000000000 --- a/submodules/chunked_requests/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ - -*.pyc -.ipynb_checkpoints/ -.ipynb_checkpoints/usage-checkpoint.ipynb - -.ipynb_checkpoints/usage-checkpoint.ipynb - -*node_modules* - -*.pyc diff --git a/submodules/chunked_requests/README.md b/submodules/chunked_requests/README.md deleted file mode 100644 index f6d0a4c73ad..00000000000 --- a/submodules/chunked_requests/README.md +++ /dev/null @@ -1,38 +0,0 @@ -chunked-requests -================ - -A wrapper around Python's httplib for streaming, chunk-encoded HTTP requests. As used in the [Plotly-Python API](https://plot.ly/python/) for streaming, real-time graphing in the browser: [https://plot.ly/python/streaming](https://plot.ly/python/streaming). - -### Quickly -```python -from chunked_requests import Stream - -stream = Stream('127.0.0.1', 8080) - -stream.write('some data') - -# take a break, go on a walk - -stream.write('some more data') # reconnects if disconnected - -response = stream.close() -``` - -See more in this IPython notebook: [http://nbviewer.ipython.org/github/chriddyp/chunked_requests/blob/master/usage.ipynb?create=1](http://nbviewer.ipython.org/github/chriddyp/chunked_requests/blob/master/usage.ipynb?create=1) - -### Docs -- `s = Stream(addr, port=80, headers={})` - - Initializes a connection to `addr:port` with `headers`. - -- `s.write(data, reconnect_on=(200, '', ))` - - Write chunk-encoded data, and reconnect depending on the status code. - -- `response = s.close()` - - Close connection and return a HTTPResponse object - -- `int s.maxtries` - - Max number of times to attempt re-connecting before raising an error diff --git a/submodules/chunked_requests/__init__.py b/submodules/chunked_requests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/submodules/chunked_requests/chunked_requests/__init__.py b/submodules/chunked_requests/chunked_requests/__init__.py deleted file mode 100644 index 044709703b3..00000000000 --- a/submodules/chunked_requests/chunked_requests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . chunked_request import Stream \ No newline at end of file diff --git a/submodules/chunked_requests/chunked_requests/chunked_request.py b/submodules/chunked_requests/chunked_requests/chunked_request.py deleted file mode 100644 index eb70488f59c..00000000000 --- a/submodules/chunked_requests/chunked_requests/chunked_request.py +++ /dev/null @@ -1,329 +0,0 @@ -import time -import six -import os -import ssl - -from six.moves import http_client -from six.moves.urllib.parse import urlparse - - -class Stream: - def __init__(self, server, port=80, headers={}, url='/', ssl_enabled=False, - ssl_verification_enabled=True): - ''' Initialize a stream object and an HTTP or HTTPS connection - with chunked Transfer-Encoding to server:port with optional headers. - ''' - self.maxtries = 5 - self._tries = 0 - self._delay = 1 - self._closed = False - self._server = server - self._port = port - self._headers = headers - self._url = url - self._ssl_enabled = ssl_enabled - self._ssl_verification_enabled = ssl_verification_enabled - self._connect() - - def write(self, data, reconnect_on=('', 200, )): - ''' Send `data` to the server in chunk-encoded form. - Check the connection before writing and reconnect - if disconnected and if the response status code is in `reconnect_on`. - - The response may either be an HTTPResponse object or an empty string. - ''' - - if not self._isconnected(): - - # Attempt to get the response. - response = self._getresponse() - - # Reconnect depending on the status code. - if ((response == '' and '' in reconnect_on) or - (response and isinstance(response, http_client.HTTPResponse) and - response.status in reconnect_on)): - self._reconnect() - - elif response and isinstance(response, http_client.HTTPResponse): - # If an HTTPResponse was recieved then - # make the users aware instead of - # auto-reconnecting in case the - # server is responding with an important - # message that might prevent - # future requests from going through, - # like Invalid Credentials. - # This allows the user to determine when - # to reconnect. - raise Exception("Server responded with " - "status code: {status_code}\n" - "and message: {msg}." - .format(status_code=response.status, - msg=response.read())) - - elif response == '': - raise Exception("Attempted to write but socket " - "was not connected.") - - try: - msg = data - msglen = format(len(msg), 'x') # msg length in hex - # Send the message in chunk-encoded form - self._conn.sock.setblocking(1) - self._conn.send('{msglen}\r\n{msg}\r\n' - .format(msglen=msglen, msg=msg).encode('utf-8')) - self._conn.sock.setblocking(0) - except http_client.socket.error: - self._reconnect() - self.write(data) - - def _get_proxy_config(self): - """ - Determine if self._url should be passed through a proxy. If so, return - the appropriate proxy_server and proxy_port. Assumes https_proxy is used - when ssl_enabled=True. - - """ - - proxy_server = None - proxy_port = None - ssl_enabled = self._ssl_enabled - - if ssl_enabled: - proxy = os.environ.get("https_proxy") - else: - proxy = os.environ.get("http_proxy") - no_proxy = os.environ.get("no_proxy") - no_proxy_url = no_proxy and self._server in no_proxy - - if proxy and not no_proxy_url: - p = urlparse(proxy) - proxy_server = p.hostname - proxy_port = p.port - - return proxy_server, proxy_port - - def _get_ssl_context(self): - """ - Return an unverified context if ssl verification is disabled. - - """ - - context = None - - if not self._ssl_verification_enabled: - context = ssl._create_unverified_context() - - return context - - def _connect(self): - ''' Initialize an HTTP/HTTPS connection with chunked Transfer-Encoding - to server:port with optional headers. - ''' - server = self._server - port = self._port - headers = self._headers - ssl_enabled = self._ssl_enabled - proxy_server, proxy_port = self._get_proxy_config() - - if (proxy_server and proxy_port): - if ssl_enabled: - context = self._get_ssl_context() - self._conn = http_client.HTTPSConnection( - proxy_server, proxy_port, context=context - ) - else: - self._conn = http_client.HTTPConnection( - proxy_server, proxy_port - ) - self._conn.set_tunnel(server, port) - else: - if ssl_enabled: - context = self._get_ssl_context() - self._conn = http_client.HTTPSConnection( - server, port, context=context - ) - else: - self._conn = http_client.HTTPConnection(server, port) - - self._conn.putrequest('POST', self._url) - self._conn.putheader('Transfer-Encoding', 'chunked') - for header in headers: - self._conn.putheader(header, headers[header]) - self._conn.endheaders() - - # Set blocking to False prevents recv - # from blocking while waiting for a response. - self._conn.sock.setblocking(False) - self._bytes = six.b('') - self._reset_retries() - time.sleep(0.5) - - def close(self): - ''' Close the connection to server. - - If available, return a http_client.HTTPResponse object. - - Closing the connection involves sending the - Transfer-Encoding terminating bytes. - ''' - self._reset_retries() - self._closed = True - - # Chunked-encoded posts are terminated with '0\r\n\r\n' - # For some reason, either Python or node.js seems to - # require an extra \r\n. - try: - self._conn.send('\r\n0\r\n\r\n'.encode('utf-8')) - except http_client.socket.error: - # In case the socket has already been closed - return '' - - return self._getresponse() - - def _getresponse(self): - ''' Read from recv and return a HTTPResponse object if possible. - Either - 1 - The client has succesfully closed the connection: Return '' - 2 - The server has already closed the connection: Return the response - if possible. - ''' - # Wait for a response - self._conn.sock.setblocking(True) - # Parse the response - response = self._bytes - while True: - try: - _bytes = self._conn.sock.recv(1) - except http_client.socket.error: - # For error 54: Connection reset by peer - # (and perhaps others) - return six.b('') - if _bytes == six.b(''): - break - else: - response += _bytes - # Set recv to be non-blocking again - self._conn.sock.setblocking(False) - - # Convert the response string to a http_client.HTTPResponse - # object with a bit of a hack - if response != six.b(''): - # Taken from - # http://pythonwise.blogspot.ca/2010/02/parse-http-response.html - try: - response = http_client.HTTPResponse(_FakeSocket(response)) - response.begin() - except: - # Bad headers ... etc. - response = six.b('') - return response - - def _isconnected(self): - ''' Return True if the socket is still connected - to the server, False otherwise. - - This check is done in 3 steps: - 1 - Check if we have closed the connection - 2 - Check if the original socket connection failed - 3 - Check if the server has returned any data. If they have, - assume that the server closed the response after they sent - the data, i.e. that the data was the HTTP response. - ''' - - # 1 - check if we've closed the connection. - if self._closed: - return False - - # 2 - Check if the original socket connection failed - # If this failed, then no socket was initialized - if self._conn.sock is None: - return False - - try: - # 3 - Check if the server has returned any data. - # If they have, then start to store the response - # in _bytes. - self._bytes = six.b('') - self._bytes = self._conn.sock.recv(1) - return False - except http_client.socket.error as e: - # Check why recv failed - # Windows machines are the error codes - # that start with 1 - # (http://msdn.microsoft.com/en-ca/library/windows/desktop/ms740668(v=vs.85).aspx) - if e.errno == 35 or e.errno == 10035: - # This is the "Resource temporarily unavailable" error - # which is thrown cuz there was nothing to receive, i.e. - # the server hasn't returned a response yet. - # This is a non-fatal error and the operation - # should be tried again. - # So, assume that the connection is still open. - return True - elif e.errno == 54 or e.errno == 10054: - # This is the "Connection reset by peer" error - # which is thrown cuz the server reset the - # socket, so the connection is closed. - return False - elif e.errno == 11: - # This is the "Resource temporarily unavailable" error - # which happens because the "operation would have blocked - # but nonblocking operation was requested". - # We require non-blocking reading of this socket because - # we don't want to wait around for a response, we just - # want to see if a response is currently available. So - # let's just assume that we're still connected and - # hopefully recieve some data on the next try. - return True - elif isinstance(e, ssl.SSLError): - if e.errno == 2: - # errno 2 occurs when trying to read or write data, but more - # data needs to be received on the underlying TCP transport - # before the request can be fulfilled. - # - # Python 2.7.9+ and Python 3.3+ give this its own exception, - # SSLWantReadError - return True - raise e - else: - # Unknown scenario - raise e - - def _reconnect(self): - ''' Connect if disconnected. - Retry self.maxtries times with delays - ''' - if not self._isconnected(): - try: - self._connect() - except http_client.socket.error as e: - # Attempt to reconnect if the connection was refused - if e.errno == 61 or e.errno == 10061: - # errno 61 is the "Connection Refused" error - time.sleep(self._delay) - self._delay += self._delay # fibonacii delays - self._tries += 1 - if self._tries < self.maxtries: - self._reconnect() - else: - self._reset_retries() - raise e - else: - # Unknown scenario - raise e - - # Reconnect worked - reset _closed - self._closed = False - - def _reset_retries(self): - ''' Reset the connect counters and delays - ''' - self._tries = 0 - self._delay = 1 - - -class _FakeSocket(six.StringIO): - # Used to construct a http_client.HTTPResponse object - # from a string. - # Thx to: http://pythonwise.blogspot.ca/2010/02/parse-http-response.html - def makefile(self, *args, **kwargs): - return self diff --git a/submodules/chunked_requests/circle.yml b/submodules/chunked_requests/circle.yml deleted file mode 100644 index 72e9dfa0346..00000000000 --- a/submodules/chunked_requests/circle.yml +++ /dev/null @@ -1,20 +0,0 @@ -machine: - node: - version: 0.10.33 - python: - version: 2.7.9 - -dependencies: - pre: - - pip install -r requirements.txt - - cd test - - mkdir node_modules - - npm install http-proxy - -test: - pre: - - node test/server.js: - background: true - - sleep 5 - override: - - nosetests test/tests.py diff --git a/submodules/chunked_requests/requirements.txt b/submodules/chunked_requests/requirements.txt deleted file mode 100644 index ffe2fce4989..00000000000 --- a/submodules/chunked_requests/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -six diff --git a/submodules/chunked_requests/test/__init__.py b/submodules/chunked_requests/test/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/submodules/chunked_requests/test/package.json b/submodules/chunked_requests/test/package.json deleted file mode 100644 index fe9782bf8c2..00000000000 --- a/submodules/chunked_requests/test/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies" : { - "http-proxy": "1.7.3" - } -} diff --git a/submodules/chunked_requests/test/server.js b/submodules/chunked_requests/test/server.js deleted file mode 100644 index 33265a0cea7..00000000000 --- a/submodules/chunked_requests/test/server.js +++ /dev/null @@ -1,65 +0,0 @@ -var http = require('http'), - server = http.createServer(sourcehandler), - fs = require("fs"), - httpProxy = require('http-proxy'); - -server.listen(8080); - -function sourcehandler (req, res) { - - console.log('request! url: ', req.url); - - if (req.method !== "POST") { - console.log(JSON.stringify({msg: "Bad Request Method: " + req.method, - code: 406})); - req.destroy(); - } - - req.once('error', function (e) { - console.log(JSON.stringify(({err:e, msg: "Request Stream Error", code: 500}))); - }); - - req.once('close', function (haderror) { - console.log("REQUEST CLOSING"); - }); - - req.setEncoding('utf8'); - - if(req.url.indexOf('/successful_write') === 0) { - console.log('successful_write URLZ.'); - req.pipe(fs.createWriteStream("request.txt", {flags: 'a'})); - } - - if(req.url==='/5s_timeout') { - // Keep connection open for 5s, close with a 408 - req.pipe(fs.createWriteStream("request.txt", {flags: 'a'})); - setTimeout(function(){ - console.log("\nclosing"); - res.writeHead(408); - res.end("timeout on active data"); - req.destroy(); - }, 5000); - } -} - -// -// Create a proxy server with latency -// -var proxy = httpProxy.createProxyServer(); - -proxy.on('error', function(err, req, res) { - res.end(); -}); - -// -// Create your server that makes an operation that waits a while -// and then proxies the request -// -http.createServer(function (req, res) { - // This simulates an operation that takes 10s to execute - setTimeout(function () { - proxy.web(req, res, { - target: 'http://localhost:8080/successful_write' - }); - }, 10000); -}).listen(9008); diff --git a/submodules/chunked_requests/test/tests.py b/submodules/chunked_requests/test/tests.py deleted file mode 100644 index 41906343b0b..00000000000 --- a/submodules/chunked_requests/test/tests.py +++ /dev/null @@ -1,197 +0,0 @@ -import unittest -import os -import errno -import time -import ssl - -from nose.tools import assert_raises - -# from chunked_requests import -from chunked_requests.chunked_requests import Stream - - -class Test(unittest.TestCase): - def setUp(self): - pass - # stream = Stream('127.0.0.1', port=8080) - - def test_successful_write(self): - ''' Test that data was successfully - written to the server. - ''' - - _remove_file('request.txt') - - stream = Stream('127.0.0.1', - port=8080, - url='/successful_write') - - body = 'request-body'*10 - stream.write(body) - time.sleep(1) - with open('request.txt', 'r') as f: - body_from_file = f.read() - - assert(body_from_file == body) - - _remove_file('request.txt') - - def test_reconnect_on_408_timeout(self): - ''' Test that `reconnect_on` indeed - reconnects on a `408` timeout response - from the server after 5 seconds and - continues to write data. Test that - all of the data was transmitted, even - with a broken connection and re-connect. - ''' - - _remove_file('request.txt') - - stream = Stream('127.0.0.1', - port=8080, - url='/5s_timeout') - - for i in range(8): - stream.write(str(i), - reconnect_on=('', 200, 408)) - time.sleep(1) - - with open('request.txt', 'r') as f: - body_from_file = f.read() - - body_sent = ''.join([str(i) for i in range(8)]) - assert(body_from_file == body_sent) - _remove_file('request.txt') - - def test_failure_on_408_timeout(self): - ''' Test that an error is thrown when - the server returns a 408 timeout and - we choose not to reconnect. - ''' - - with assert_raises(Exception) as cm: - stream = Stream('127.0.0.1', - port=8080, - url='/5s_timeout') - - for i in range(8): - stream.write(str(i), - reconnect_on=('', 200)) - time.sleep(1) - - ex = cm.exception - assert(ex.message == "Server responded " - "with status code: 408\n" - "and message: " - "timeout on active data.") - - def test_huge_request_on_latent_server(self): - _remove_file('request.txt') - - stream = Stream('127.0.0.1', - port=9008) - body = 'x' * (5000 * 1000) - stream.write(body) - - # Proxy servers delays the response for 10 seconds - # Writing 5 mill chars takes a bit, so wait a few - # extra secs before comparing - time.sleep(13) - with open('request.txt', 'r') as f: - body_from_file = f.read() - - assert(body_from_file == body) - - _remove_file('request.txt') - - def test__get_proxy_config(self): - - stream = Stream('127.0.0.1', port=8080) - - # http_proxy is not set - # --> proxy_server and proxy_port should not be set - proxy_server, proxy_port = stream._get_proxy_config() - self.assertIsNone(proxy_server) - self.assertIsNone(proxy_port) - - # set proxy env. variable - os.environ['http_proxy'] = 'http://test:123' - - # http_proxy is set - # --> proxy_server and proxy_port should be set - proxy_server, proxy_port = stream._get_proxy_config() - self.assertEqual(proxy_server, 'test') - self.assertEqual(proxy_port, 123) - - # no_proxy is set but url is not in no_proxy - # --> proxy_server and proxy_port should be set - os.environ['no_proxy'] = 'some_url, other_url' - proxy_server, proxy_port = stream._get_proxy_config() - self.assertEqual(proxy_server, 'test') - self.assertEqual(proxy_port, 123) - - # http_proxy and no_proxy are set and url is in no_proxy - # --> proxy_server and proxy_port should not be set - os.environ['no_proxy'] = 'some_url, {}, other_url'.format( - stream._server - ) - proxy_server, proxy_port = stream._get_proxy_config() - self.assertIsNone(proxy_server) - self.assertIsNone(proxy_port) - - # unset env variables - os.environ.pop('http_proxy') - os.environ.pop('no_proxy') - - def test__get_proxy_config_with_ssl(self): - - stream = Stream('127.0.0.1', port=8080) - - # we actually still have an http connection here, but we're faking https - # to test the expected proxy behaviour - stream._ssl_enabled = True - - # https_proxy is not set - # --> proxy_server and proxy_port should not be set - proxy_server, proxy_port = stream._get_proxy_config() - self.assertIsNone(proxy_server) - self.assertIsNone(proxy_port) - - # set proxy env. variable - os.environ['https_proxy'] = 'https://test:123' - - # https_proxy is set - # --> proxy_server and proxy_port should be set - proxy_server, proxy_port = stream._get_proxy_config() - self.assertEqual(proxy_server, 'test') - self.assertEqual(proxy_port, 123) - - def test__get_ssl_context(self): - - stream = Stream('127.0.0.1', port=8080) - - # we actually still have an http connection here, but we're faking https - # to test the expected proxy behaviour - stream._ssl_enabled = True - - # if ssl verification is enabled (default), context=None expected - context = stream._get_ssl_context() - self.assertIsNone(context) - - # if ssl verification is disabled, SSLContext expected - stream._ssl_verification_enabled = False - context = stream._get_ssl_context() - self.assertIsInstance(context, ssl.SSLContext) - - -def _remove_file(filename): - try: - os.remove(filename) - except OSError as e: - # errno.ENOENT = no such file or directory - if e.errno != errno.ENOENT: - raise e - - -if __name__ == '__main__': - unittest.main() diff --git a/submodules/mplexporter/.gitignore b/submodules/mplexporter/.gitignore deleted file mode 100644 index 8c03c7caa96..00000000000 --- a/submodules/mplexporter/.gitignore +++ /dev/null @@ -1,46 +0,0 @@ -*.py[cod] - -# C extensions -*.so - -# Packages -*.egg -*.egg-info -dist -build -eggs -parts -bin -var -sdist -develop-eggs -.installed.cfg -lib -lib64 -__pycache__ - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -.coverage -.tox -nosetests.xml - -# Translations -*.mo - -# Mr Developer -.mr.developer.cfg -.project -.pydevproject - - -# emacs backup files -*~ - -# ipython backups -.ipynb_checkpoints - -# os x files -.DS_Store \ No newline at end of file diff --git a/submodules/mplexporter/.travis.yml b/submodules/mplexporter/.travis.yml deleted file mode 100644 index 64ccad4740c..00000000000 --- a/submodules/mplexporter/.travis.yml +++ /dev/null @@ -1,32 +0,0 @@ -sudo: false -language: python - -python: - - 2.7 - - 3.4 - - 3.5 - -env: - - DEPS="numpy=1.11 matplotlib=1.5 jinja2=2.8 pandas=0.18 nose" - -install: - - conda create -n testenv --yes python=$TRAVIS_PYTHON_VERSION - - source activate testenv - - conda install --yes $DEPS - - python setup.py install - -before_install: - # setup virtual x - - "export DISPLAY=:99.0" - - "sh -e /etc/init.d/xvfb start" - # then install python version to test - - wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh - - chmod +x miniconda.sh - - bash miniconda.sh -b -p $HOME/miniconda - - export PATH="$HOME/miniconda/bin:$PATH" - # Learned the hard way: miniconda is not always up-to-date with conda. - - conda update --yes conda - -script: - - nosetests mplexporter - diff --git a/submodules/mplexporter/LICENSE b/submodules/mplexporter/LICENSE deleted file mode 100644 index 096369bcdc4..00000000000 --- a/submodules/mplexporter/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2014, mpld3 -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the {organization} nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/submodules/mplexporter/MANIFEST.in b/submodules/mplexporter/MANIFEST.in deleted file mode 100644 index 7f16c16db10..00000000000 --- a/submodules/mplexporter/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -include *.md -include LICENSE - -recursive-include mplexporter *.py diff --git a/submodules/mplexporter/README.md b/submodules/mplexporter/README.md deleted file mode 100644 index 7bd44b9cea6..00000000000 --- a/submodules/mplexporter/README.md +++ /dev/null @@ -1,4 +0,0 @@ -mplexporter -=========== - -A proof of concept general matplotlib exporter diff --git a/submodules/mplexporter/mplexporter/__init__.py b/submodules/mplexporter/mplexporter/__init__.py deleted file mode 100644 index 970731c6d64..00000000000 --- a/submodules/mplexporter/mplexporter/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .renderers import Renderer -from .exporter import Exporter diff --git a/submodules/mplexporter/mplexporter/_py3k_compat.py b/submodules/mplexporter/mplexporter/_py3k_compat.py deleted file mode 100644 index 9ca84550d67..00000000000 --- a/submodules/mplexporter/mplexporter/_py3k_compat.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -Simple fixes for Python 2/3 compatibility -""" -import sys -PY3K = sys.version_info[0] >= 3 - - -if PY3K: - import builtins - import functools - reduce = functools.reduce - zip = builtins.zip - xrange = builtins.range - map = builtins.map -else: - import __builtin__ - import itertools - builtins = __builtin__ - reduce = __builtin__.reduce - zip = itertools.izip - xrange = __builtin__.xrange - map = itertools.imap diff --git a/submodules/mplexporter/mplexporter/exporter.py b/submodules/mplexporter/mplexporter/exporter.py deleted file mode 100644 index 318ee2bf62c..00000000000 --- a/submodules/mplexporter/mplexporter/exporter.py +++ /dev/null @@ -1,285 +0,0 @@ -""" -Matplotlib Exporter -=================== -This submodule contains tools for crawling a matplotlib figure and exporting -relevant pieces to a renderer. -""" -import warnings -import io -from . import utils - -import matplotlib -from matplotlib import transforms, collections -from matplotlib.backends.backend_agg import FigureCanvasAgg - -class Exporter(object): - """Matplotlib Exporter - - Parameters - ---------- - renderer : Renderer object - The renderer object called by the exporter to create a figure - visualization. See mplexporter.Renderer for information on the - methods which should be defined within the renderer. - close_mpl : bool - If True (default), close the matplotlib figure as it is rendered. This - is useful for when the exporter is used within the notebook, or with - an interactive matplotlib backend. - """ - - def __init__(self, renderer, close_mpl=True): - self.close_mpl = close_mpl - self.renderer = renderer - - def run(self, fig): - """ - Run the exporter on the given figure - - Parmeters - --------- - fig : matplotlib.Figure instance - The figure to export - """ - # Calling savefig executes the draw() command, putting elements - # in the correct place. - if fig.canvas is None: - canvas = FigureCanvasAgg(fig) - fig.savefig(io.BytesIO(), format='png', dpi=fig.dpi) - if self.close_mpl: - import matplotlib.pyplot as plt - plt.close(fig) - self.crawl_fig(fig) - - @staticmethod - def process_transform(transform, ax=None, data=None, return_trans=False, - force_trans=None): - """Process the transform and convert data to figure or data coordinates - - Parameters - ---------- - transform : matplotlib Transform object - The transform applied to the data - ax : matplotlib Axes object (optional) - The axes the data is associated with - data : ndarray (optional) - The array of data to be transformed. - return_trans : bool (optional) - If true, return the final transform of the data - force_trans : matplotlib.transform instance (optional) - If supplied, first force the data to this transform - - Returns - ------- - code : string - Code is either "data", "axes", "figure", or "display", indicating - the type of coordinates output. - transform : matplotlib transform - the transform used to map input data to output data. - Returned only if return_trans is True - new_data : ndarray - Data transformed to match the given coordinate code. - Returned only if data is specified - """ - if isinstance(transform, transforms.BlendedGenericTransform): - warnings.warn("Blended transforms not yet supported. " - "Zoom behavior may not work as expected.") - - if force_trans is not None: - if data is not None: - data = (transform - force_trans).transform(data) - transform = force_trans - - code = "display" - if ax is not None: - for (c, trans) in [("data", ax.transData), - ("axes", ax.transAxes), - ("figure", ax.figure.transFigure), - ("display", transforms.IdentityTransform())]: - if transform.contains_branch(trans): - code, transform = (c, transform - trans) - break - - if data is not None: - if return_trans: - return code, transform.transform(data), transform - else: - return code, transform.transform(data) - else: - if return_trans: - return code, transform - else: - return code - - def crawl_fig(self, fig): - """Crawl the figure and process all axes""" - with self.renderer.draw_figure(fig=fig, - props=utils.get_figure_properties(fig)): - for ax in fig.axes: - self.crawl_ax(ax) - - def crawl_ax(self, ax): - """Crawl the axes and process all elements within""" - with self.renderer.draw_axes(ax=ax, - props=utils.get_axes_properties(ax)): - for line in ax.lines: - self.draw_line(ax, line) - for text in ax.texts: - self.draw_text(ax, text) - for (text, ttp) in zip([ax.xaxis.label, ax.yaxis.label, ax.title], - ["xlabel", "ylabel", "title"]): - if(hasattr(text, 'get_text') and text.get_text()): - self.draw_text(ax, text, force_trans=ax.transAxes, - text_type=ttp) - for artist in ax.artists: - # TODO: process other artists - if isinstance(artist, matplotlib.text.Text): - self.draw_text(ax, artist) - for patch in ax.patches: - self.draw_patch(ax, patch) - for collection in ax.collections: - self.draw_collection(ax, collection) - for image in ax.images: - self.draw_image(ax, image) - - legend = ax.get_legend() - if legend is not None: - props = utils.get_legend_properties(ax, legend) - with self.renderer.draw_legend(legend=legend, props=props): - if props['visible']: - self.crawl_legend(ax, legend) - - def crawl_legend(self, ax, legend): - """ - Recursively look through objects in legend children - """ - legendElements = list(utils.iter_all_children(legend._legend_box, - skipContainers=True)) - legendElements.append(legend.legendPatch) - for child in legendElements: - # force a large zorder so it appears on top - child.set_zorder(1E6 + child.get_zorder()) - - # reorder border box to make sure marks are visible - if isinstance(child, matplotlib.patches.FancyBboxPatch): - child.set_zorder(child.get_zorder()-1) - - try: - # What kind of object... - if isinstance(child, matplotlib.patches.Patch): - self.draw_patch(ax, child, force_trans=ax.transAxes) - elif isinstance(child, matplotlib.text.Text): - if child.get_text() != 'None': - self.draw_text(ax, child, force_trans=ax.transAxes) - elif isinstance(child, matplotlib.lines.Line2D): - self.draw_line(ax, child, force_trans=ax.transAxes) - elif isinstance(child, matplotlib.collections.Collection): - self.draw_collection(ax, child, - force_pathtrans=ax.transAxes) - else: - warnings.warn("Legend element %s not impemented" % child) - except NotImplementedError: - warnings.warn("Legend element %s not impemented" % child) - - def draw_line(self, ax, line, force_trans=None): - """Process a matplotlib line and call renderer.draw_line""" - coordinates, data = self.process_transform(line.get_transform(), - ax, line.get_xydata(), - force_trans=force_trans) - linestyle = utils.get_line_style(line) - if (linestyle['dasharray'] is None - and linestyle['drawstyle'] == 'default'): - linestyle = None - markerstyle = utils.get_marker_style(line) - if (markerstyle['marker'] in ['None', 'none', None] - or markerstyle['markerpath'][0].size == 0): - markerstyle = None - label = line.get_label() - if markerstyle or linestyle: - self.renderer.draw_marked_line(data=data, coordinates=coordinates, - linestyle=linestyle, - markerstyle=markerstyle, - label=label, - mplobj=line) - - def draw_text(self, ax, text, force_trans=None, text_type=None): - """Process a matplotlib text object and call renderer.draw_text""" - content = text.get_text() - if content: - transform = text.get_transform() - position = text.get_position() - coords, position = self.process_transform(transform, ax, - position, - force_trans=force_trans) - style = utils.get_text_style(text) - self.renderer.draw_text(text=content, position=position, - coordinates=coords, - text_type=text_type, - style=style, mplobj=text) - - def draw_patch(self, ax, patch, force_trans=None): - """Process a matplotlib patch object and call renderer.draw_path""" - vertices, pathcodes = utils.SVG_path(patch.get_path()) - transform = patch.get_transform() - coordinates, vertices = self.process_transform(transform, - ax, vertices, - force_trans=force_trans) - linestyle = utils.get_path_style(patch, fill=patch.get_fill()) - self.renderer.draw_path(data=vertices, - coordinates=coordinates, - pathcodes=pathcodes, - style=linestyle, - mplobj=patch) - - def draw_collection(self, ax, collection, - force_pathtrans=None, - force_offsettrans=None): - """Process a matplotlib collection and call renderer.draw_collection""" - (transform, transOffset, - offsets, paths) = collection._prepare_points() - - offset_coords, offsets = self.process_transform( - transOffset, ax, offsets, force_trans=force_offsettrans) - path_coords = self.process_transform( - transform, ax, force_trans=force_pathtrans) - - processed_paths = [utils.SVG_path(path) for path in paths] - processed_paths = [(self.process_transform( - transform, ax, path[0], force_trans=force_pathtrans)[1], path[1]) - for path in processed_paths] - - path_transforms = collection.get_transforms() - try: - # matplotlib 1.3: path_transforms are transform objects. - # Convert them to numpy arrays. - path_transforms = [t.get_matrix() for t in path_transforms] - except AttributeError: - # matplotlib 1.4: path transforms are already numpy arrays. - pass - - styles = {'linewidth': collection.get_linewidths(), - 'facecolor': collection.get_facecolors(), - 'edgecolor': collection.get_edgecolors(), - 'alpha': collection._alpha, - 'zorder': collection.get_zorder()} - - offset_dict = {"data": "before", - "screen": "after"} - offset_order = offset_dict[collection.get_offset_position()] - - self.renderer.draw_path_collection(paths=processed_paths, - path_coordinates=path_coords, - path_transforms=path_transforms, - offsets=offsets, - offset_coordinates=offset_coords, - offset_order=offset_order, - styles=styles, - mplobj=collection) - - def draw_image(self, ax, image): - """Process a matplotlib image object and call renderer.draw_image""" - self.renderer.draw_image(imdata=utils.image_to_base64(image), - extent=image.get_extent(), - coordinates="data", - style={"alpha": image.get_alpha(), - "zorder": image.get_zorder()}, - mplobj=image) diff --git a/submodules/mplexporter/mplexporter/renderers/__init__.py b/submodules/mplexporter/mplexporter/renderers/__init__.py deleted file mode 100644 index ba85b1aa00e..00000000000 --- a/submodules/mplexporter/mplexporter/renderers/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -""" -Matplotlib Renderers -==================== -This submodule contains renderer objects which define renderer behavior used -within the Exporter class. The base renderer class is :class:`Renderer`, an -abstract base class -""" - -from .base import Renderer -from .vega_renderer import VegaRenderer, fig_to_vega -from .vincent_renderer import VincentRenderer, fig_to_vincent -from .fake_renderer import FakeRenderer, FullFakeRenderer diff --git a/submodules/mplexporter/mplexporter/renderers/base.py b/submodules/mplexporter/mplexporter/renderers/base.py deleted file mode 100644 index 6bf5acb444f..00000000000 --- a/submodules/mplexporter/mplexporter/renderers/base.py +++ /dev/null @@ -1,388 +0,0 @@ -import warnings -import itertools -from contextlib import contextmanager -from distutils.version import LooseVersion - -import numpy as np -import matplotlib as mpl -from matplotlib import transforms - -from .. import utils -from .. import _py3k_compat as py3k - - -class Renderer(object): - @staticmethod - def ax_zoomable(ax): - return bool(ax and ax.get_navigate()) - - @staticmethod - def ax_has_xgrid(ax): - return bool(ax and ax.xaxis._gridOnMajor and ax.yaxis.get_gridlines()) - - @staticmethod - def ax_has_ygrid(ax): - return bool(ax and ax.yaxis._gridOnMajor and ax.yaxis.get_gridlines()) - - @property - def current_ax_zoomable(self): - return self.ax_zoomable(self._current_ax) - - @property - def current_ax_has_xgrid(self): - return self.ax_has_xgrid(self._current_ax) - - @property - def current_ax_has_ygrid(self): - return self.ax_has_ygrid(self._current_ax) - - @contextmanager - def draw_figure(self, fig, props): - if hasattr(self, "_current_fig") and self._current_fig is not None: - warnings.warn("figure embedded in figure: something is wrong") - self._current_fig = fig - self._fig_props = props - self.open_figure(fig=fig, props=props) - yield - self.close_figure(fig=fig) - self._current_fig = None - self._fig_props = {} - - @contextmanager - def draw_axes(self, ax, props): - if hasattr(self, "_current_ax") and self._current_ax is not None: - warnings.warn("axes embedded in axes: something is wrong") - self._current_ax = ax - self._ax_props = props - self.open_axes(ax=ax, props=props) - yield - self.close_axes(ax=ax) - self._current_ax = None - self._ax_props = {} - - @contextmanager - def draw_legend(self, legend, props): - self._current_legend = legend - self._legend_props = props - self.open_legend(legend=legend, props=props) - yield - self.close_legend(legend=legend) - self._current_legend = None - self._legend_props = {} - - # Following are the functions which should be overloaded in subclasses - - def open_figure(self, fig, props): - """ - Begin commands for a particular figure. - - Parameters - ---------- - fig : matplotlib.Figure - The Figure which will contain the ensuing axes and elements - props : dictionary - The dictionary of figure properties - """ - pass - - def close_figure(self, fig): - """ - Finish commands for a particular figure. - - Parameters - ---------- - fig : matplotlib.Figure - The figure which is finished being drawn. - """ - pass - - def open_axes(self, ax, props): - """ - Begin commands for a particular axes. - - Parameters - ---------- - ax : matplotlib.Axes - The Axes which will contain the ensuing axes and elements - props : dictionary - The dictionary of axes properties - """ - pass - - def close_axes(self, ax): - """ - Finish commands for a particular axes. - - Parameters - ---------- - ax : matplotlib.Axes - The Axes which is finished being drawn. - """ - pass - - def open_legend(self, legend, props): - """ - Beging commands for a particular legend. - - Parameters - ---------- - legend : matplotlib.legend.Legend - The Legend that will contain the ensuing elements - props : dictionary - The dictionary of legend properties - """ - pass - - def close_legend(self, legend): - """ - Finish commands for a particular legend. - - Parameters - ---------- - legend : matplotlib.legend.Legend - The Legend which is finished being drawn - """ - pass - - def draw_marked_line(self, data, coordinates, linestyle, markerstyle, - label, mplobj=None): - """Draw a line that also has markers. - - If this isn't reimplemented by a renderer object, by default, it will - make a call to BOTH draw_line and draw_markers when both markerstyle - and linestyle are not None in the same Line2D object. - - """ - if linestyle is not None: - self.draw_line(data, coordinates, linestyle, label, mplobj) - if markerstyle is not None: - self.draw_markers(data, coordinates, markerstyle, label, mplobj) - - def draw_line(self, data, coordinates, style, label, mplobj=None): - """ - Draw a line. By default, draw the line via the draw_path() command. - Some renderers might wish to override this and provide more - fine-grained behavior. - - In matplotlib, lines are generally created via the plt.plot() command, - though this command also can create marker collections. - - Parameters - ---------- - data : array_like - A shape (N, 2) array of datapoints. - coordinates : string - A string code, which should be either 'data' for data coordinates, - or 'figure' for figure (pixel) coordinates. - style : dictionary - a dictionary specifying the appearance of the line. - mplobj : matplotlib object - the matplotlib plot element which generated this line - """ - pathcodes = ['M'] + (data.shape[0] - 1) * ['L'] - pathstyle = dict(facecolor='none', **style) - pathstyle['edgecolor'] = pathstyle.pop('color') - pathstyle['edgewidth'] = pathstyle.pop('linewidth') - self.draw_path(data=data, coordinates=coordinates, - pathcodes=pathcodes, style=pathstyle, mplobj=mplobj) - - @staticmethod - def _iter_path_collection(paths, path_transforms, offsets, styles): - """Build an iterator over the elements of the path collection""" - N = max(len(paths), len(offsets)) - - # Before mpl 1.4.0, path_transform can be a false-y value, not a valid - # transformation matrix. - if LooseVersion(mpl.__version__) < LooseVersion('1.4.0'): - if path_transforms is None: - path_transforms = [np.eye(3)] - - edgecolor = styles['edgecolor'] - if np.size(edgecolor) == 0: - edgecolor = ['none'] - facecolor = styles['facecolor'] - if np.size(facecolor) == 0: - facecolor = ['none'] - - elements = [paths, path_transforms, offsets, - edgecolor, styles['linewidth'], facecolor] - - it = itertools - return it.islice(py3k.zip(*py3k.map(it.cycle, elements)), N) - - def draw_path_collection(self, paths, path_coordinates, path_transforms, - offsets, offset_coordinates, offset_order, - styles, mplobj=None): - """ - Draw a collection of paths. The paths, offsets, and styles are all - iterables, and the number of paths is max(len(paths), len(offsets)). - - By default, this is implemented via multiple calls to the draw_path() - function. For efficiency, Renderers may choose to customize this - implementation. - - Examples of path collections created by matplotlib are scatter plots, - histograms, contour plots, and many others. - - Parameters - ---------- - paths : list - list of tuples, where each tuple has two elements: - (data, pathcodes). See draw_path() for a description of these. - path_coordinates: string - the coordinates code for the paths, which should be either - 'data' for data coordinates, or 'figure' for figure (pixel) - coordinates. - path_transforms: array_like - an array of shape (*, 3, 3), giving a series of 2D Affine - transforms for the paths. These encode translations, rotations, - and scalings in the standard way. - offsets: array_like - An array of offsets of shape (N, 2) - offset_coordinates : string - the coordinates code for the offsets, which should be either - 'data' for data coordinates, or 'figure' for figure (pixel) - coordinates. - offset_order : string - either "before" or "after". This specifies whether the offset - is applied before the path transform, or after. The matplotlib - backend equivalent is "before"->"data", "after"->"screen". - styles: dictionary - A dictionary in which each value is a list of length N, containing - the style(s) for the paths. - mplobj : matplotlib object - the matplotlib plot element which generated this collection - """ - if offset_order == "before": - raise NotImplementedError("offset before transform") - - for tup in self._iter_path_collection(paths, path_transforms, - offsets, styles): - (path, path_transform, offset, ec, lw, fc) = tup - vertices, pathcodes = path - path_transform = transforms.Affine2D(path_transform) - vertices = path_transform.transform(vertices) - # This is a hack: - if path_coordinates == "figure": - path_coordinates = "points" - style = {"edgecolor": utils.export_color(ec), - "facecolor": utils.export_color(fc), - "edgewidth": lw, - "dasharray": "10,0", - "alpha": styles['alpha'], - "zorder": styles['zorder']} - self.draw_path(data=vertices, coordinates=path_coordinates, - pathcodes=pathcodes, style=style, offset=offset, - offset_coordinates=offset_coordinates, - mplobj=mplobj) - - def draw_markers(self, data, coordinates, style, label, mplobj=None): - """ - Draw a set of markers. By default, this is done by repeatedly - calling draw_path(), but renderers should generally overload - this method to provide a more efficient implementation. - - In matplotlib, markers are created using the plt.plot() command. - - Parameters - ---------- - data : array_like - A shape (N, 2) array of datapoints. - coordinates : string - A string code, which should be either 'data' for data coordinates, - or 'figure' for figure (pixel) coordinates. - style : dictionary - a dictionary specifying the appearance of the markers. - mplobj : matplotlib object - the matplotlib plot element which generated this marker collection - """ - vertices, pathcodes = style['markerpath'] - pathstyle = dict((key, style[key]) for key in ['alpha', 'edgecolor', - 'facecolor', 'zorder', - 'edgewidth']) - pathstyle['dasharray'] = "10,0" - for vertex in data: - self.draw_path(data=vertices, coordinates="points", - pathcodes=pathcodes, style=pathstyle, - offset=vertex, offset_coordinates=coordinates, - mplobj=mplobj) - - def draw_text(self, text, position, coordinates, style, - text_type=None, mplobj=None): - """ - Draw text on the image. - - Parameters - ---------- - text : string - The text to draw - position : tuple - The (x, y) position of the text - coordinates : string - A string code, which should be either 'data' for data coordinates, - or 'figure' for figure (pixel) coordinates. - style : dictionary - a dictionary specifying the appearance of the text. - text_type : string or None - if specified, a type of text such as "xlabel", "ylabel", "title" - mplobj : matplotlib object - the matplotlib plot element which generated this text - """ - raise NotImplementedError() - - def draw_path(self, data, coordinates, pathcodes, style, - offset=None, offset_coordinates="data", mplobj=None): - """ - Draw a path. - - In matplotlib, paths are created by filled regions, histograms, - contour plots, patches, etc. - - Parameters - ---------- - data : array_like - A shape (N, 2) array of datapoints. - coordinates : string - A string code, which should be either 'data' for data coordinates, - 'figure' for figure (pixel) coordinates, or "points" for raw - point coordinates (useful in conjunction with offsets, below). - pathcodes : list - A list of single-character SVG pathcodes associated with the data. - Path codes are one of ['M', 'm', 'L', 'l', 'Q', 'q', 'T', 't', - 'S', 's', 'C', 'c', 'Z', 'z'] - See the SVG specification for details. Note that some path codes - consume more than one datapoint (while 'Z' consumes none), so - in general, the length of the pathcodes list will not be the same - as that of the data array. - style : dictionary - a dictionary specifying the appearance of the line. - offset : list (optional) - the (x, y) offset of the path. If not given, no offset will - be used. - offset_coordinates : string (optional) - A string code, which should be either 'data' for data coordinates, - or 'figure' for figure (pixel) coordinates. - mplobj : matplotlib object - the matplotlib plot element which generated this path - """ - raise NotImplementedError() - - def draw_image(self, imdata, extent, coordinates, style, mplobj=None): - """ - Draw an image. - - Parameters - ---------- - imdata : string - base64 encoded png representation of the image - extent : list - the axes extent of the image: [xmin, xmax, ymin, ymax] - coordinates: string - A string code, which should be either 'data' for data coordinates, - or 'figure' for figure (pixel) coordinates. - style : dictionary - a dictionary specifying the appearance of the image - mplobj : matplotlib object - the matplotlib plot object which generated this image - """ - raise NotImplementedError() diff --git a/submodules/mplexporter/mplexporter/renderers/fake_renderer.py b/submodules/mplexporter/mplexporter/renderers/fake_renderer.py deleted file mode 100644 index 2c4c708c4c2..00000000000 --- a/submodules/mplexporter/mplexporter/renderers/fake_renderer.py +++ /dev/null @@ -1,68 +0,0 @@ -from .base import Renderer - - -class FakeRenderer(Renderer): - """ - Fake Renderer - - This is a fake renderer which simply outputs a text tree representing the - elements found in the plot(s). This is used in the unit tests for the - package. - - Below are the methods your renderer must implement. You are free to do - anything you wish within the renderer (i.e. build an XML or JSON - representation, call an external API, etc.) Here the renderer just - builds a simple string representation for testing purposes. - """ - def __init__(self): - self.output = "" - - def open_figure(self, fig, props): - self.output += "opening figure\n" - - def close_figure(self, fig): - self.output += "closing figure\n" - - def open_axes(self, ax, props): - self.output += " opening axes\n" - - def close_axes(self, ax): - self.output += " closing axes\n" - - def open_legend(self, legend, props): - self.output += " opening legend\n" - - def close_legend(self, legend): - self.output += " closing legend\n" - - def draw_text(self, text, position, coordinates, style, - text_type=None, mplobj=None): - self.output += " draw text '{0}' {1}\n".format(text, text_type) - - def draw_path(self, data, coordinates, pathcodes, style, - offset=None, offset_coordinates="data", mplobj=None): - self.output += " draw path with {0} vertices\n".format(data.shape[0]) - - def draw_image(self, imdata, extent, coordinates, style, mplobj=None): - self.output += " draw image of size {0}\n".format(len(imdata)) - - -class FullFakeRenderer(FakeRenderer): - """ - Renderer with the full complement of methods. - - When the following are left undefined, they will be implemented via - other methods in the class. They can be defined explicitly for - more efficient or specialized use within the renderer implementation. - """ - def draw_line(self, data, coordinates, style, label, mplobj=None): - self.output += " draw line with {0} points\n".format(data.shape[0]) - - def draw_markers(self, data, coordinates, style, label, mplobj=None): - self.output += " draw {0} markers\n".format(data.shape[0]) - - def draw_path_collection(self, paths, path_coordinates, path_transforms, - offsets, offset_coordinates, offset_order, - styles, mplobj=None): - self.output += (" draw path collection " - "with {0} offsets\n".format(offsets.shape[0])) diff --git a/submodules/mplexporter/mplexporter/renderers/vega_renderer.py b/submodules/mplexporter/mplexporter/renderers/vega_renderer.py deleted file mode 100644 index 82a30bd9f22..00000000000 --- a/submodules/mplexporter/mplexporter/renderers/vega_renderer.py +++ /dev/null @@ -1,138 +0,0 @@ -import warnings -import json -import random -from .base import Renderer -from ..exporter import Exporter - - -class VegaRenderer(Renderer): - def open_figure(self, fig, props): - self.props = props - self.figwidth = int(props['figwidth'] * props['dpi']) - self.figheight = int(props['figheight'] * props['dpi']) - self.data = [] - self.scales = [] - self.axes = [] - self.marks = [] - - def open_axes(self, ax, props): - if len(self.axes) > 0: - warnings.warn("multiple axes not yet supported") - self.axes = [dict(type="x", scale="x", ticks=10), - dict(type="y", scale="y", ticks=10)] - self.scales = [dict(name="x", - domain=props['xlim'], - type="linear", - range="width", - ), - dict(name="y", - domain=props['ylim'], - type="linear", - range="height", - ),] - - def draw_line(self, data, coordinates, style, label, mplobj=None): - if coordinates != 'data': - warnings.warn("Only data coordinates supported. Skipping this") - dataname = "table{0:03d}".format(len(self.data) + 1) - - # TODO: respect the other style settings - self.data.append({'name': dataname, - 'values': [dict(x=d[0], y=d[1]) for d in data]}) - self.marks.append({'type': 'line', - 'from': {'data': dataname}, - 'properties': { - "enter": { - "interpolate": {"value": "monotone"}, - "x": {"scale": "x", "field": "data.x"}, - "y": {"scale": "y", "field": "data.y"}, - "stroke": {"value": style['color']}, - "strokeOpacity": {"value": style['alpha']}, - "strokeWidth": {"value": style['linewidth']}, - } - } - }) - - def draw_markers(self, data, coordinates, style, label, mplobj=None): - if coordinates != 'data': - warnings.warn("Only data coordinates supported. Skipping this") - dataname = "table{0:03d}".format(len(self.data) + 1) - - # TODO: respect the other style settings - self.data.append({'name': dataname, - 'values': [dict(x=d[0], y=d[1]) for d in data]}) - self.marks.append({'type': 'symbol', - 'from': {'data': dataname}, - 'properties': { - "enter": { - "interpolate": {"value": "monotone"}, - "x": {"scale": "x", "field": "data.x"}, - "y": {"scale": "y", "field": "data.y"}, - "fill": {"value": style['facecolor']}, - "fillOpacity": {"value": style['alpha']}, - "stroke": {"value": style['edgecolor']}, - "strokeOpacity": {"value": style['alpha']}, - "strokeWidth": {"value": style['edgewidth']}, - } - } - }) - - def draw_text(self, text, position, coordinates, style, - text_type=None, mplobj=None): - if text_type == 'xlabel': - self.axes[0]['title'] = text - elif text_type == 'ylabel': - self.axes[1]['title'] = text - - -class VegaHTML(object): - def __init__(self, renderer): - self.specification = dict(width=renderer.figwidth, - height=renderer.figheight, - data=renderer.data, - scales=renderer.scales, - axes=renderer.axes, - marks=renderer.marks) - - def html(self): - """Build the HTML representation for IPython.""" - id = random.randint(0, 2 ** 16) - html = '
' % id - html += '\n' - return html - - def _repr_html_(self): - return self.html() - - -def fig_to_vega(fig, notebook=False): - """Convert a matplotlib figure to vega dictionary - - if notebook=True, then return an object which will display in a notebook - otherwise, return an HTML string. - """ - renderer = VegaRenderer() - Exporter(renderer).run(fig) - vega_html = VegaHTML(renderer) - if notebook: - return vega_html - else: - return vega_html.html() - - -VEGA_TEMPLATE = """ -( function() { - var _do_plot = function() { - if ( (typeof vg == 'undefined') && (typeof IPython != 'undefined')) { - $([IPython.events]).on("vega_loaded.vincent", _do_plot); - return; - } - vg.parse.spec(%s, function(chart) { - chart({el: "#vis%d"}).update(); - }); - }; - _do_plot(); -})(); -""" diff --git a/submodules/mplexporter/mplexporter/renderers/vincent_renderer.py b/submodules/mplexporter/mplexporter/renderers/vincent_renderer.py deleted file mode 100644 index 73691eab9c3..00000000000 --- a/submodules/mplexporter/mplexporter/renderers/vincent_renderer.py +++ /dev/null @@ -1,52 +0,0 @@ -import warnings -from .base import Renderer -from ..exporter import Exporter - - -class VincentRenderer(Renderer): - def open_figure(self, fig, props): - self.chart = None - self.figwidth = int(props['figwidth'] * props['dpi']) - self.figheight = int(props['figheight'] * props['dpi']) - - def draw_line(self, data, coordinates, style, label, mplobj=None): - import vincent # only import if VincentRenderer is used - if coordinates != 'data': - warnings.warn("Only data coordinates supported. Skipping this") - linedata = {'x': data[:, 0], - 'y': data[:, 1]} - line = vincent.Line(linedata, iter_idx='x', - width=self.figwidth, height=self.figheight) - - # TODO: respect the other style settings - line.scales['color'].range = [style['color']] - - if self.chart is None: - self.chart = line - else: - warnings.warn("Multiple plot elements not yet supported") - - def draw_markers(self, data, coordinates, style, label, mplobj=None): - import vincent # only import if VincentRenderer is used - if coordinates != 'data': - warnings.warn("Only data coordinates supported. Skipping this") - markerdata = {'x': data[:, 0], - 'y': data[:, 1]} - markers = vincent.Scatter(markerdata, iter_idx='x', - width=self.figwidth, height=self.figheight) - - # TODO: respect the other style settings - markers.scales['color'].range = [style['facecolor']] - - if self.chart is None: - self.chart = markers - else: - warnings.warn("Multiple plot elements not yet supported") - - -def fig_to_vincent(fig): - """Convert a matplotlib figure to a vincent object""" - renderer = VincentRenderer() - exporter = Exporter(renderer) - exporter.run(fig) - return renderer.chart diff --git a/submodules/mplexporter/mplexporter/tests/__init__.py b/submodules/mplexporter/mplexporter/tests/__init__.py deleted file mode 100644 index 13028d70afb..00000000000 --- a/submodules/mplexporter/mplexporter/tests/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import matplotlib -matplotlib.use('Agg') -import matplotlib.pyplot as plt diff --git a/submodules/mplexporter/mplexporter/tests/test_basic.py b/submodules/mplexporter/mplexporter/tests/test_basic.py deleted file mode 100644 index 1c4dfb3e92b..00000000000 --- a/submodules/mplexporter/mplexporter/tests/test_basic.py +++ /dev/null @@ -1,228 +0,0 @@ -import matplotlib -import numpy as np -from distutils.version import LooseVersion -from nose.plugins.skip import SkipTest -from numpy.testing import assert_warns - -from ..exporter import Exporter -from ..renderers import FakeRenderer, FullFakeRenderer -from . import plt - - -def fake_renderer_output(fig, Renderer): - renderer = Renderer() - exporter = Exporter(renderer) - exporter.run(fig) - return renderer.output - - -def _assert_output_equal(text1, text2): - for line1, line2 in zip(text1.strip().split(), text2.strip().split()): - assert line1 == line2 - - -def test_lines(): - fig, ax = plt.subplots() - ax.plot(range(20), '-k') - - _assert_output_equal(fake_renderer_output(fig, FakeRenderer), - """ - opening figure - opening axes - draw path with 20 vertices - closing axes - closing figure - """) - - _assert_output_equal(fake_renderer_output(fig, FullFakeRenderer), - """ - opening figure - opening axes - draw line with 20 points - closing axes - closing figure - """) - - -def test_markers(): - fig, ax = plt.subplots() - ax.plot(range(2), 'ok') - - _assert_output_equal(fake_renderer_output(fig, FakeRenderer), - """ - opening figure - opening axes - draw path with 25 vertices - draw path with 25 vertices - closing axes - closing figure - """) - - _assert_output_equal(fake_renderer_output(fig, FullFakeRenderer), - """ - opening figure - opening axes - draw 2 markers - closing axes - closing figure - """) - - -def test_path_collection(): - fig, ax = plt.subplots() - ax.scatter(range(3), range(3)) - - _assert_output_equal(fake_renderer_output(fig, FakeRenderer), - """ - opening figure - opening axes - draw path with 25 vertices - draw path with 25 vertices - draw path with 25 vertices - closing axes - closing figure - """) - - _assert_output_equal(fake_renderer_output(fig, FullFakeRenderer), - """ - opening figure - opening axes - draw path collection with 3 offsets - closing axes - closing figure - """) - - -def test_text(): - fig, ax = plt.subplots() - ax.set_xlabel("my x label") - ax.set_ylabel("my y label") - ax.set_title("my title") - ax.text(0.5, 0.5, "my text") - - _assert_output_equal(fake_renderer_output(fig, FakeRenderer), - """ - opening figure - opening axes - draw text 'my text' None - draw text 'my x label' xlabel - draw text 'my y label' ylabel - draw text 'my title' title - closing axes - closing figure - """) - - -def test_path(): - fig, ax = plt.subplots() - ax.add_patch(plt.Circle((0, 0), 1)) - ax.add_patch(plt.Rectangle((0, 0), 1, 2)) - - _assert_output_equal(fake_renderer_output(fig, FakeRenderer), - """ - opening figure - opening axes - draw path with 25 vertices - draw path with 4 vertices - closing axes - closing figure - """) - -def test_Figure(): - """ if the fig is not associated with a canvas, FakeRenderer shall - not fail. """ - fig = plt.Figure() - ax = fig.add_subplot(111) - ax.add_patch(plt.Circle((0, 0), 1)) - ax.add_patch(plt.Rectangle((0, 0), 1, 2)) - - _assert_output_equal(fake_renderer_output(fig, FakeRenderer), - """ - opening figure - opening axes - draw path with 25 vertices - draw path with 4 vertices - closing axes - closing figure - """) - -def test_multiaxes(): - fig, ax = plt.subplots(2) - ax[0].plot(range(4)) - ax[1].plot(range(10)) - - _assert_output_equal(fake_renderer_output(fig, FakeRenderer), - """ - opening figure - opening axes - draw path with 4 vertices - closing axes - opening axes - draw path with 10 vertices - closing axes - closing figure - """) - - -def test_image(): - # Test fails for matplotlib 1.5+ because the size of the image - # generated by matplotlib has changed. - if LooseVersion(matplotlib.__version__) >= LooseVersion('1.5.0'): - raise SkipTest("Test fails for matplotlib version > 1.5.0"); - np.random.seed(0) # image size depends on the seed - fig, ax = plt.subplots(figsize=(2, 2)) - ax.imshow(np.random.random((10, 10)), - cmap=plt.cm.jet, interpolation='nearest') - _assert_output_equal(fake_renderer_output(fig, FakeRenderer), - """ - opening figure - opening axes - draw image of size 1240 - closing axes - closing figure - """) - - -def test_legend(): - fig, ax = plt.subplots() - ax.plot([1, 2, 3], label='label') - ax.legend().set_visible(False) - _assert_output_equal(fake_renderer_output(fig, FakeRenderer), - """ - opening figure - opening axes - draw path with 3 vertices - opening legend - closing legend - closing axes - closing figure - """) - - -def test_legend_dots(): - fig, ax = plt.subplots() - ax.plot([1, 2, 3], label='label') - ax.plot([2, 2, 2], 'o', label='dots') - ax.legend().set_visible(True) - _assert_output_equal(fake_renderer_output(fig, FullFakeRenderer), - """ - opening figure - opening axes - draw line with 3 points - draw 3 markers - opening legend - draw line with 2 points - draw text 'label' None - draw 2 markers - draw text 'dots' None - draw path with 4 vertices - closing legend - closing axes - closing figure - """) - - -def test_blended(): - fig, ax = plt.subplots() - ax.axvline(0) - #assert_warns(UserWarning, fake_renderer_output, fig, FakeRenderer) diff --git a/submodules/mplexporter/mplexporter/tests/test_utils.py b/submodules/mplexporter/mplexporter/tests/test_utils.py deleted file mode 100644 index eb85fbbf072..00000000000 --- a/submodules/mplexporter/mplexporter/tests/test_utils.py +++ /dev/null @@ -1,34 +0,0 @@ -from numpy.testing import assert_allclose, assert_equal -from . import plt -from .. import utils - - -def test_path_data(): - circle = plt.Circle((0, 0), 1) - vertices, codes = utils.SVG_path(circle.get_path()) - - assert_allclose(vertices.shape, (25, 2)) - assert_equal(codes, ['M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'Z']) - - -def test_linestyle(): - linestyles = {'solid': 'none', '-': 'none', - 'dashed': '6,6', '--': '6,6', - 'dotted': '2,2', ':': '2,2', - 'dashdot': '4,4,2,4', '-.': '4,4,2,4', - '': None, 'None': None} - - for ls, result in linestyles.items(): - line, = plt.plot([1, 2, 3], linestyle=ls) - assert_equal(utils.get_dasharray(line), result) - - -def test_axis_w_fixed_formatter(): - positions, labels = [0, 1, 10], ['A','B','C'] - - plt.xticks(positions, labels) - props = utils.get_axis_properties(plt.gca().xaxis) - - assert_equal(props['tickvalues'], positions) - assert_equal(props['tickformat'], labels) - diff --git a/submodules/mplexporter/mplexporter/tools.py b/submodules/mplexporter/mplexporter/tools.py deleted file mode 100644 index 551e8bead73..00000000000 --- a/submodules/mplexporter/mplexporter/tools.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -Tools for matplotlib plot exporting -""" - - -def ipynb_vega_init(): - """Initialize the IPython notebook display elements - - This function borrows heavily from the excellent vincent package: - http://github.com/wrobstory/vincent - """ - try: - from IPython.core.display import display, HTML - except ImportError: - print('IPython Notebook could not be loaded.') - - require_js = ''' - if (window['d3'] === undefined) {{ - require.config({{ paths: {{d3: "http://d3js.org/d3.v3.min"}} }}); - require(["d3"], function(d3) {{ - window.d3 = d3; - {0} - }}); - }}; - if (window['topojson'] === undefined) {{ - require.config( - {{ paths: {{topojson: "http://d3js.org/topojson.v1.min"}} }} - ); - require(["topojson"], function(topojson) {{ - window.topojson = topojson; - }}); - }}; - ''' - d3_geo_projection_js_url = "http://d3js.org/d3.geo.projection.v0.min.js" - d3_layout_cloud_js_url = ("http://wrobstory.github.io/d3-cloud/" - "d3.layout.cloud.js") - topojson_js_url = "http://d3js.org/topojson.v1.min.js" - vega_js_url = 'http://trifacta.github.com/vega/vega.js' - - dep_libs = '''$.getScript("%s", function() { - $.getScript("%s", function() { - $.getScript("%s", function() { - $.getScript("%s", function() { - $([IPython.events]).trigger("vega_loaded.vincent"); - }) - }) - }) - });''' % (d3_geo_projection_js_url, d3_layout_cloud_js_url, - topojson_js_url, vega_js_url) - load_js = require_js.format(dep_libs) - html = '' - display(HTML(html)) diff --git a/submodules/mplexporter/mplexporter/utils.py b/submodules/mplexporter/mplexporter/utils.py deleted file mode 100644 index 4059a6b6f58..00000000000 --- a/submodules/mplexporter/mplexporter/utils.py +++ /dev/null @@ -1,362 +0,0 @@ -""" -Utility Routines for Working with Matplotlib Objects -==================================================== -""" -import itertools -import io -import base64 - -import numpy as np - -import warnings - -import matplotlib -from matplotlib.colors import colorConverter -from matplotlib.path import Path -from matplotlib.markers import MarkerStyle -from matplotlib.transforms import Affine2D -from matplotlib import ticker - - -def export_color(color): - """Convert matplotlib color code to hex color or RGBA color""" - if color is None or colorConverter.to_rgba(color)[3] == 0: - return 'none' - elif colorConverter.to_rgba(color)[3] == 1: - rgb = colorConverter.to_rgb(color) - return '#{0:02X}{1:02X}{2:02X}'.format(*(int(255 * c) for c in rgb)) - else: - c = colorConverter.to_rgba(color) - return "rgba(" + ", ".join(str(int(np.round(val * 255))) - for val in c[:3])+', '+str(c[3])+")" - - -def _many_to_one(input_dict): - """Convert a many-to-one mapping to a one-to-one mapping""" - return dict((key, val) - for keys, val in input_dict.items() - for key in keys) - -LINESTYLES = _many_to_one({('solid', '-', (None, None)): 'none', - ('dashed', '--'): "6,6", - ('dotted', ':'): "2,2", - ('dashdot', '-.'): "4,4,2,4", - ('', ' ', 'None', 'none'): None}) - - -def get_dasharray(obj): - """Get an SVG dash array for the given matplotlib linestyle - - Parameters - ---------- - obj : matplotlib object - The matplotlib line or path object, which must have a get_linestyle() - method which returns a valid matplotlib line code - - Returns - ------- - dasharray : string - The HTML/SVG dasharray code associated with the object. - """ - if obj.__dict__.get('_dashSeq', None) is not None: - return ','.join(map(str, obj._dashSeq)) - else: - ls = obj.get_linestyle() - dasharray = LINESTYLES.get(ls, 'not found') - if dasharray == 'not found': - warnings.warn("line style '{0}' not understood: " - "defaulting to solid line.".format(ls)) - dasharray = LINESTYLES['solid'] - return dasharray - - -PATH_DICT = {Path.LINETO: 'L', - Path.MOVETO: 'M', - Path.CURVE3: 'S', - Path.CURVE4: 'C', - Path.CLOSEPOLY: 'Z'} - - -def SVG_path(path, transform=None, simplify=False): - """Construct the vertices and SVG codes for the path - - Parameters - ---------- - path : matplotlib.Path object - - transform : matplotlib transform (optional) - if specified, the path will be transformed before computing the output. - - Returns - ------- - vertices : array - The shape (M, 2) array of vertices of the Path. Note that some Path - codes require multiple vertices, so the length of these vertices may - be longer than the list of path codes. - path_codes : list - A length N list of single-character path codes, N <= M. Each code is - a single character, in ['L','M','S','C','Z']. See the standard SVG - path specification for a description of these. - """ - if transform is not None: - path = path.transformed(transform) - - vc_tuples = [(vertices if path_code != Path.CLOSEPOLY else [], - PATH_DICT[path_code]) - for (vertices, path_code) - in path.iter_segments(simplify=simplify)] - - if not vc_tuples: - # empty path is a special case - return np.zeros((0, 2)), [] - else: - vertices, codes = zip(*vc_tuples) - vertices = np.array(list(itertools.chain(*vertices))).reshape(-1, 2) - return vertices, list(codes) - - -def get_path_style(path, fill=True): - """Get the style dictionary for matplotlib path objects""" - style = {} - style['alpha'] = path.get_alpha() - if style['alpha'] is None: - style['alpha'] = 1 - style['edgecolor'] = export_color(path.get_edgecolor()) - if fill: - style['facecolor'] = export_color(path.get_facecolor()) - else: - style['facecolor'] = 'none' - style['edgewidth'] = path.get_linewidth() - style['dasharray'] = get_dasharray(path) - style['zorder'] = path.get_zorder() - return style - - -def get_line_style(line): - """Get the style dictionary for matplotlib line objects""" - style = {} - style['alpha'] = line.get_alpha() - if style['alpha'] is None: - style['alpha'] = 1 - style['color'] = export_color(line.get_color()) - style['linewidth'] = line.get_linewidth() - style['dasharray'] = get_dasharray(line) - style['zorder'] = line.get_zorder() - style['drawstyle'] = line.get_drawstyle() - return style - - -def get_marker_style(line): - """Get the style dictionary for matplotlib marker objects""" - style = {} - style['alpha'] = line.get_alpha() - if style['alpha'] is None: - style['alpha'] = 1 - - style['facecolor'] = export_color(line.get_markerfacecolor()) - style['edgecolor'] = export_color(line.get_markeredgecolor()) - style['edgewidth'] = line.get_markeredgewidth() - - style['marker'] = line.get_marker() - markerstyle = MarkerStyle(line.get_marker()) - markersize = line.get_markersize() - markertransform = (markerstyle.get_transform() - + Affine2D().scale(markersize, -markersize)) - style['markerpath'] = SVG_path(markerstyle.get_path(), - markertransform) - style['markersize'] = markersize - style['zorder'] = line.get_zorder() - return style - - -def get_text_style(text): - """Return the text style dict for a text instance""" - style = {} - style['alpha'] = text.get_alpha() - if style['alpha'] is None: - style['alpha'] = 1 - style['fontsize'] = text.get_size() - style['color'] = export_color(text.get_color()) - style['halign'] = text.get_horizontalalignment() # left, center, right - style['valign'] = text.get_verticalalignment() # baseline, center, top - style['malign'] = text._multialignment # text alignment when '\n' in text - style['rotation'] = text.get_rotation() - style['zorder'] = text.get_zorder() - return style - - -def get_axis_properties(axis): - """Return the property dictionary for a matplotlib.Axis instance""" - props = {} - label1On = axis._major_tick_kw.get('label1On', True) - - if isinstance(axis, matplotlib.axis.XAxis): - if label1On: - props['position'] = "bottom" - else: - props['position'] = "top" - elif isinstance(axis, matplotlib.axis.YAxis): - if label1On: - props['position'] = "left" - else: - props['position'] = "right" - else: - raise ValueError("{0} should be an Axis instance".format(axis)) - - # Use tick values if appropriate - locator = axis.get_major_locator() - props['nticks'] = len(locator()) - if isinstance(locator, ticker.FixedLocator): - props['tickvalues'] = list(locator()) - else: - props['tickvalues'] = None - - # Find tick formats - formatter = axis.get_major_formatter() - if isinstance(formatter, ticker.NullFormatter): - props['tickformat'] = "" - elif isinstance(formatter, ticker.FixedFormatter): - props['tickformat'] = list(formatter.seq) - elif not any(label.get_visible() for label in axis.get_ticklabels()): - props['tickformat'] = "" - else: - props['tickformat'] = None - - # Get axis scale - props['scale'] = axis.get_scale() - - # Get major tick label size (assumes that's all we really care about!) - labels = axis.get_ticklabels() - if labels: - props['fontsize'] = labels[0].get_fontsize() - else: - props['fontsize'] = None - - # Get associated grid - props['grid'] = get_grid_style(axis) - - # get axis visibility - props['visible'] = axis.get_visible() - - return props - - -def get_grid_style(axis): - gridlines = axis.get_gridlines() - if axis._gridOnMajor and len(gridlines) > 0: - color = export_color(gridlines[0].get_color()) - alpha = gridlines[0].get_alpha() - dasharray = get_dasharray(gridlines[0]) - return dict(gridOn=True, - color=color, - dasharray=dasharray, - alpha=alpha) - else: - return {"gridOn": False} - - -def get_figure_properties(fig): - return {'figwidth': fig.get_figwidth(), - 'figheight': fig.get_figheight(), - 'dpi': fig.dpi} - - -def get_axes_properties(ax): - props = {'axesbg': export_color(ax.patch.get_facecolor()), - 'axesbgalpha': ax.patch.get_alpha(), - 'bounds': ax.get_position().bounds, - 'dynamic': ax.get_navigate(), - 'axison': ax.axison, - 'frame_on': ax.get_frame_on(), - 'patch_visible':ax.patch.get_visible(), - 'axes': [get_axis_properties(ax.xaxis), - get_axis_properties(ax.yaxis)]} - - for axname in ['x', 'y']: - axis = getattr(ax, axname + 'axis') - domain = getattr(ax, 'get_{0}lim'.format(axname))() - lim = domain - if isinstance(axis.converter, matplotlib.dates.DateConverter): - scale = 'date' - try: - import pandas as pd - from pandas.tseries.converter import PeriodConverter - except ImportError: - pd = None - - if (pd is not None and isinstance(axis.converter, - PeriodConverter)): - _dates = [pd.Period(ordinal=int(d), freq=axis.freq) - for d in domain] - domain = [(d.year, d.month - 1, d.day, - d.hour, d.minute, d.second, 0) - for d in _dates] - else: - domain = [(d.year, d.month - 1, d.day, - d.hour, d.minute, d.second, - d.microsecond * 1E-3) - for d in matplotlib.dates.num2date(domain)] - else: - scale = axis.get_scale() - - if scale not in ['date', 'linear', 'log']: - raise ValueError("Unknown axis scale: " - "{0}".format(axis.get_scale())) - - props[axname + 'scale'] = scale - props[axname + 'lim'] = lim - props[axname + 'domain'] = domain - - return props - - -def iter_all_children(obj, skipContainers=False): - """ - Returns an iterator over all childen and nested children using - obj's get_children() method - - if skipContainers is true, only childless objects are returned. - """ - if hasattr(obj, 'get_children') and len(obj.get_children()) > 0: - for child in obj.get_children(): - if not skipContainers: - yield child - # could use `yield from` in python 3... - for grandchild in iter_all_children(child, skipContainers): - yield grandchild - else: - yield obj - - -def get_legend_properties(ax, legend): - handles, labels = ax.get_legend_handles_labels() - visible = legend.get_visible() - return {'handles': handles, 'labels': labels, 'visible': visible} - - -def image_to_base64(image): - """ - Convert a matplotlib image to a base64 png representation - - Parameters - ---------- - image : matplotlib image object - The image to be converted. - - Returns - ------- - image_base64 : string - The UTF8-encoded base64 string representation of the png image. - """ - ax = image.axes - binary_buffer = io.BytesIO() - - # image is saved in axes coordinates: we need to temporarily - # set the correct limits to get the correct image - lim = ax.axis() - ax.axis(image.get_extent()) - image.write_png(binary_buffer) - ax.axis(lim) - - binary_buffer.seek(0) - return base64.b64encode(binary_buffer.read()).decode('utf-8') diff --git a/submodules/mplexporter/setup.py b/submodules/mplexporter/setup.py deleted file mode 100644 index aa6591ae307..00000000000 --- a/submodules/mplexporter/setup.py +++ /dev/null @@ -1,30 +0,0 @@ -try: - from setuptools import setup -except ImportError: - from distutils.core import setup - -DESCRIPTION = "General Matplotlib Exporter" -LONG_DESCRIPTION = open('README.md').read() -NAME = "mplexporter" -AUTHOR = "Jake VanderPlas" -AUTHOR_EMAIL = "jakevdp@cs.washington.edu" -MAINTAINER = "Jake VanderPlas" -MAINTAINER_EMAIL = "jakevdp@cs.washington.edu" -DOWNLOAD_URL = 'https://github.com/mpld3/mplexporter' -URL = DOWNLOAD_URL -LICENSE = 'BSD 3-clause' -VERSION = '0.0.1' - -setup(name=NAME, - version=VERSION, - description=DESCRIPTION, - long_description=LONG_DESCRIPTION, - author=AUTHOR, - author_email=AUTHOR_EMAIL, - maintainer=MAINTAINER, - maintainer_email=MAINTAINER_EMAIL, - url=URL, - download_url=DOWNLOAD_URL, - license=LICENSE, - packages=['mplexporter', 'mplexporter.renderers'], - ) From 1a7dc3b20572ea7585283e70e60c0224f90f8821 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 15 Jun 2019 10:05:31 -0400 Subject: [PATCH 04/11] Restore chunked_requests and mplexporter as submodules --- submodules/chunked_requests | 1 + submodules/mplexporter | 1 + 2 files changed, 2 insertions(+) create mode 160000 submodules/chunked_requests create mode 160000 submodules/mplexporter diff --git a/submodules/chunked_requests b/submodules/chunked_requests new file mode 160000 index 00000000000..6d7c5ddc71c --- /dev/null +++ b/submodules/chunked_requests @@ -0,0 +1 @@ +Subproject commit 6d7c5ddc71cebc343c22a16f2151b11c68720a1d diff --git a/submodules/mplexporter b/submodules/mplexporter new file mode 160000 index 00000000000..474a3a49fe3 --- /dev/null +++ b/submodules/mplexporter @@ -0,0 +1 @@ +Subproject commit 474a3a49fe3b3dca859f69a6173263018dd2d34c From ffd0b9b16c76c419cfe53988ac9253c86c11d7e9 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 15 Jun 2019 10:27:54 -0400 Subject: [PATCH 05/11] WIP update_submodules and contributing.md updates --- contributing.md | 12 +++++++++ update_submodules.py | 58 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 update_submodules.py diff --git a/contributing.md b/contributing.md index 63098d1913a..e2f6db08f0f 100644 --- a/contributing.md +++ b/contributing.md @@ -26,12 +26,24 @@ First, you'll need to *get* our project. This is the appropriate *clone* command git clone https://github.com/your_github_username/plotly.py.git ``` +### Create a virtual environment for plotly development +TODO: Use virtualenv or conda, activate it + +### Install requirements +pip install -r requirements.txt +pip install -r optional-requirements.txt + +### Editable install of plotly.py +pip install -e . + + ### Submodules Second, this project uses git submodules! They're both helpful and, at times, difficult to work with. The good news is you probably don't need to think about them! Just run the following shell command to make sure that your local repo is wired properly: **DO THIS (run this command in your new `plotly.py` directory)** + ```bash make setup_subs ``` diff --git a/update_submodules.py b/update_submodules.py new file mode 100644 index 00000000000..b8f19cb38ed --- /dev/null +++ b/update_submodules.py @@ -0,0 +1,58 @@ +import os +import shutil +import subprocess + +here = os.path.dirname(os.path.abspath(__file__)) + +if __name__ == '__main__': + # Init submodules + subprocess.check_output(['git', 'submodule', 'init']) + + # Update submodules + subprocess.check_output(['git', 'submodule', 'update']) + + # Replace mplexporter directory + mpl_dst = os.path.join( + here, + 'packages', + 'python', + 'plotly', + 'plotly', + 'matplotlylib', + 'mplexporter', + ) + + shutil.rmtree(mpl_dst, ignore_errors=True) + + shutil.copytree( + os.path.join( + here, + 'submodules', + 'mplexporter', + 'mplexporter', + ), + mpl_dst, + ) + + # Replace chunked_requests directory + chunked_dst = os.path.join( + here, + 'packages', + 'python', + 'chart-studio', + 'chart_studio', + 'plotly', + 'chunked_requests', + ) + + shutil.rmtree(chunked_dst, ignore_errors=True) + + shutil.copytree( + os.path.join( + here, + 'submodules', + 'mplexporter', + 'mplexporter', + ), + os.path.join(chunked_dst), + ) From baf0494b50faa75d47ef75acfb74fa47bdef9866 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 15 Jun 2019 10:33:57 -0400 Subject: [PATCH 06/11] Fix update_submodules.py --- update_submodules.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/update_submodules.py b/update_submodules.py index b8f19cb38ed..e22dfee6ef8 100644 --- a/update_submodules.py +++ b/update_submodules.py @@ -51,8 +51,8 @@ os.path.join( here, 'submodules', - 'mplexporter', - 'mplexporter', + 'chunked_requests', + 'chunked_requests', ), os.path.join(chunked_dst), ) From a1c45fb78ac516e86eb2e15c611cdd1addbfdf05 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 15 Jun 2019 11:39:31 -0400 Subject: [PATCH 07/11] renamed update_submodules.py to setup_submodules.py --- update_submodules.py => setup_submodules.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename update_submodules.py => setup_submodules.py (100%) diff --git a/update_submodules.py b/setup_submodules.py similarity index 100% rename from update_submodules.py rename to setup_submodules.py From cb55611d6b5849aa29fcb9068e212762a37b3f0f Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 15 Jun 2019 11:39:57 -0400 Subject: [PATCH 08/11] Remove makefile --- makefile | 63 -------------------------------------------------------- 1 file changed, 63 deletions(-) delete mode 100644 makefile diff --git a/makefile b/makefile deleted file mode 100644 index 355c05a1e4f..00000000000 --- a/makefile +++ /dev/null @@ -1,63 +0,0 @@ -all : readme - -readme : - @echo "" - @less make_instructions.txt - -setup_subs : - @echo "Deleting old submodule locations, if they exist" - rm -rf plotly/mplexporter - rm -rf plotly/chunked_requests - rm -rf plotly/plotly/chunked_requests - rm -rf plotly/matplotlylib/mplexporter - @echo "Initializing submodules listed in project" - git submodule init - @echo "Updating submodules to their respective commits" - git submodule update - make sync_subs - -update_default_schema : - @echo "Updating plotly-schema" - python setup.py updateschema - @echo "Auto-generating graph objects based on updated schema." - python setup.py codegen - -install : sync_subs - @echo "" - @echo "Installing Python API with make" - python setup.py install - -sync_subs : sync_mpl sync_chunked - @echo "" - @echo "Submodules synced" - -pull_subs : pull_mpl pull_chunked - @echo "" - @echo "Submodules pulled" - -sync_mpl : submodules/mplexporter - @echo "" - @echo "Syncing mplexporter directories" - rsync -r submodules/mplexporter/mplexporter plotly/matplotlylib/ - -sync_chunked : submodules/chunked_requests - @echo "" - @echo "Syncing chunked_requests directories" - rsync -r submodules/chunked_requests/chunked_requests plotly/plotly/ - -pull_mpl : submodules/mplexporter - @echo "" - @echo "Pulling down updates from mplexporter" - cd submodules/mplexporter; git pull origin master - -pull_chunked : submodules/chunked_requests - @echo "" - @echo "Pulling down updates from chunked_requests" - cd submodules/chunked_requests; git pull origin master - -update_plotlyjs_for_offline : - @echo "Updating plotly.js" - @echo "------------------" - python setup.py updateplotlyjs - @echo "---------------------------------" - @echo "Remember to update the CHANGELOG!" From 65828f4b71aa564a45534fcf5f2fb74996b7fbb1 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 15 Jun 2019 11:40:28 -0400 Subject: [PATCH 09/11] contributing updates --- contributing.md | 268 ++++++++++++++++++++++++++++-------------------- 1 file changed, 154 insertions(+), 114 deletions(-) diff --git a/contributing.md b/contributing.md index e2f6db08f0f..95d77341ead 100644 --- a/contributing.md +++ b/contributing.md @@ -14,7 +14,7 @@ Open an issue! Go to https://github.com/plotly/plotly.py/issues. It's possible t Check out our Support App: https://support.plot.ly/libraries/python or Community Forum: https://community.plot.ly/. -## Setup +## Setup a Development Environment ### Fork, Clone, Setup Your Version of the Plotly Python API @@ -29,37 +29,41 @@ git clone https://github.com/your_github_username/plotly.py.git ### Create a virtual environment for plotly development TODO: Use virtualenv or conda, activate it +http://docs.python-guide.org/en/latest/dev/virtualenvs/ + ### Install requirements -pip install -r requirements.txt -pip install -r optional-requirements.txt + $ pip install -r requirements.txt + $ pip install -r optional-requirements.txt -### Editable install of plotly.py -pip install -e . +### Editable install of plotly packages + $ pip install -e packages/python/plotly/ + $ pip install -e packages/python/chart-studio/ + $ pip install -e packages/python/plotly-geo/ +### ipywidgets development install +Run the following commands in your virtual environment to use the +development version of `FigureWidget`, -### Submodules + $ jupyter nbextension enable --py widgetsnbextension + $ jupyter nbextension install --py --symlink --sys-prefix plotlywidget + $ jupyter nbextension enable --py --sys-prefix plotlywidget + +### Setup Submodules -Second, this project uses git submodules! They're both helpful and, at times, difficult to work with. The good news is you probably don't need to think about them! Just run the following shell command to make sure that your local repo is wired properly: +This project uses git submodules. They're both helpful and, at times, difficult to work with. The good news is you probably don't need to think about them! Just run the following shell command to make sure that your local repo is wired properly: **DO THIS (run this command in your new `plotly.py` directory)** - ```bash -make setup_subs +python update_submodules.py ``` That's going to initialize the submodules we use in this project, update them so that they're synced to the proper commit, and copy files to the appropriate locations in your local repo. Here's what you need to know: changes to any files inside the following directories **will get overwritten**. These are synced with the submodules, if you need to change functionality there, you will need to make a pull request in the appropriate sub project repository. -- chunked_requests -- mplexporter - -Additionally, there are some project shortcuts that live in the `makefile` file. You can read all about this in the `make_instructions.txt` file. OR, just run: - -```bash -make readme -``` - +- `packages/python/chart-studio/chart_studio/plotly/chunked_requests` +- `packages/python/plotly/plotly/matplotlylib/mplexporter` + ### Making a Development Branch Third, *don't* work in the `master` branch. As soon as you get your master branch ready, run: @@ -75,58 +79,6 @@ git checkout -b my-dev-branch Once you've made your changes (and hopefully written some tests...), make that pull request! -## Suggestions - -### Local Python -Setting up Python versions that *don't* require you to use `sudo` is a good idea. In addition, the core Python on your machine may not be the Python that we've developed in! Here are some nice guides for Mac, Windows, and Linux: -- http://docs.python-guide.org/en/latest/starting/install/osx/ -- http://docs.python-guide.org/en/latest/starting/install/win/ -- http://docs.python-guide.org/en/latest/starting/install/linux/ - -### Virtualenv -Virtualenv is a way to create Python environments on your machine that know nothing about one another. This is really helpful for ironing out dependency-problems arising from different versions of packages. Here's a nice guide on how to do this: http://docs.python-guide.org/en/latest/dev/virtualenvs/ - -### Alter Your PYTHONPATH -The PYTHONPATH variable in your shell tells Python where to look for modules. Since you'll be developing, it'll be a pain to need to *install* Python every time you need to test some functionality (or at least ensure you're running code from the right directory...). You can easily make this change from a shell: - -```bash -export PYTHONPATH="/path/to/local/repo:$PYTHONPATH" -``` - -Note, that's non-permanent. When you close the shell, that variable definition disappears. Also, `path/to/local/repo` is *your* specific repository path (e.g., `/Users/andrew/projects/python-api`). - -### Why? - -Now you can run the following code and be guaranteed to have a working development version that you can make changes to on-the-fly, test, and be confident will not break on other's machines! - -```bash -pip install -r requirements.txt -pip install -r optional-requirements.txt -export PYTHONPATH="/path/to/local/repo:$PYTHONPATH" -``` - -## Dependencies - -There's a short list of core dependencies you'll need installed in your Python environment to have any sort of fun with Plotly's Python API (see `requirements.txt`). Additionally, you're likely to have even more fun if you install some other requirements (see `optional-requirements.txt`). - -### Dependencies and Virtualenv - -If you decided to follow the suggestion about the Virtualenv *and* you've run `source bin/activate` within your new virtualenv directory to activate it--you can run the following to install the core dependencies: - -```bash -pip install -r requirements.txt -``` - -To install the optional dependencies: - -```bash -pip install -r optional-requirements.txt -``` - -## ipywidget development install - $ jupyter nbextension enable --py widgetsnbextension - $ jupyter nbextension install --py --symlink --sys-prefix plotlywidget - $ jupyter nbextension enable --py --sys-prefix plotlywidget ## Update to a new version of Plotly.js First update the version of the `plotly.js` dependency in `js/package.json`. @@ -156,19 +108,19 @@ Since our tests cover *all* the functionality, to prevent tons of errors from sh After you've done that, go ahead and follow (y)our Nose! ```bash -nosetests -w plotly/tests +nosetests -w packages/python/plotly/plotly/tests/ ``` Or for more *verbose* output: ```bash -nosetests -w plotly/tests -v +nosetests -w packages/python/plotly/plotly/tests/ -v ``` Either of those will run *every* test we've written for the Python API. You can get more granular by running something like: ```bash -nosetests -w plotly/tests/test_plotly +nosetests -w packages/python/plotly/plotly/tests/test_core/ ``` ... or even more granular by running something like: @@ -182,11 +134,11 @@ nosetests plotly/tests/test_plotly/test_plot.py Running tests with tox is much more powerful, but requires a bit more setup. You'll need to export an environment variable for *each* tox environment you wish to test with. For example, if you want to test with `Python 2.7` and -`Python 3.4`, but only care to check the `core` specs, you would need to ensure that the following variables are exported: +`Python 3.6`, but only care to check the `core` specs, you would need to ensure that the following variables are exported: ``` export PLOTLY_TOX_PYTHON_27= -export PLOTLY_TOX_PYTHON_34= +export PLOTLY_TOX_PYTHON_36= ``` Where the ` Date: Sat, 15 Jun 2019 11:41:35 -0400 Subject: [PATCH 10/11] Add initial conda recipe/CHANGELOG.md files for chart-studio and plotly-geo packages --- packages/python/chart-studio/CHANGELOG.md | 0 packages/python/chart-studio/recipe/meta.yaml | 32 +++++++++++++++++++ packages/python/plotly-geo/CHANGELOG.md | 0 packages/python/plotly-geo/recipe/meta.yaml | 32 +++++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 packages/python/chart-studio/CHANGELOG.md create mode 100644 packages/python/chart-studio/recipe/meta.yaml create mode 100644 packages/python/plotly-geo/CHANGELOG.md create mode 100644 packages/python/plotly-geo/recipe/meta.yaml diff --git a/packages/python/chart-studio/CHANGELOG.md b/packages/python/chart-studio/CHANGELOG.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/python/chart-studio/recipe/meta.yaml b/packages/python/chart-studio/recipe/meta.yaml new file mode 100644 index 00000000000..d5bffcd2de0 --- /dev/null +++ b/packages/python/chart-studio/recipe/meta.yaml @@ -0,0 +1,32 @@ +{% set sdata = load_setup_py_data() %} + +package: + name: chart-studio + version: {{ sdata['version'] }} + +source: + path: .. + +build: + noarch: python + script: "{{ PYTHON }} -m pip install . --no-deps --ignore-installed --no-cache-dir -q" + +requirements: + build: + - python + - pip + run: + - python + {% for dep in sdata.get('install_requires',{}) %} + - {{ dep }} + {% endfor %} + +test: + imports: + - plotly_express + +about: + home: {{ sdata['url'] }} + summary: {{ sdata['description'] }} + license: {{ sdata['license'] }} + license_file: LICENSE.txt diff --git a/packages/python/plotly-geo/CHANGELOG.md b/packages/python/plotly-geo/CHANGELOG.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/python/plotly-geo/recipe/meta.yaml b/packages/python/plotly-geo/recipe/meta.yaml new file mode 100644 index 00000000000..13ead67dd61 --- /dev/null +++ b/packages/python/plotly-geo/recipe/meta.yaml @@ -0,0 +1,32 @@ +{% set sdata = load_setup_py_data() %} + +package: + name: plotly-geo + version: {{ sdata['version'] }} + +source: + path: .. + +build: + noarch: python + script: "{{ PYTHON }} -m pip install . --no-deps --ignore-installed --no-cache-dir -q" + +requirements: + build: + - python + - pip + run: + - python + {% for dep in sdata.get('install_requires',{}) %} + - {{ dep }} + {% endfor %} + +test: + imports: + - plotly_express + +about: + home: {{ sdata['url'] }} + summary: {{ sdata['description'] }} + license: {{ sdata['license'] }} + license_file: LICENSE.txt From 3563894aaf6898c7e727d6eb7fae4920c8fbe48b Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 15 Jun 2019 11:42:57 -0400 Subject: [PATCH 11/11] gitignore updates --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index eb55d2750fd..51dec06a05a 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,4 @@ plotly.egg-info/ plotly/tests/test_orca/images/*/failed plotly/tests/test_orca/images/*/tmp /plotly-package/plotly/tests/test_core/test_offline/plotly.min.js -/plotly-package/plotly/tests/test_core/test_offline/temp-plot.html +temp-plot.html