pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/plotly/plotly.py/commit/3678aa925489b9ed429dc28863040dbb391dadb1

nk crossorigen="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/global-9c8f61f9f58ad7b2.css" /> Extract chart studio functionality, optimized imports (#1476) · plotly/plotly.py@3678aa9 · GitHub
Skip to content

Commit 3678aa9

Browse files
authored
Extract chart studio functionality, optimized imports (#1476)
## Overview This PR is an important step towards the [version 4 goal](#1420) of removing all of the chart studio (i.e. cloud-service related) functionality from plotly.py, and putting it in a separate optional package. ## chart studio extraction For the time being, I've done this by creating a new top-level `chart_studio` package next to the top-level `plotly` package. I've moved all of the cloud-related functionality to the `chart_studio` package, following the same structure as in the current plotly package. For example, the `plotly.plotly` module was moved to `chart_studio.plotly`. This PR takes advantage of the `_future_plotly_` system introduced in #1474 to make this refactor backward compatible. - By default all of the old entry points are still usable and they are aliased to the `chart_studio` package. - If the `extract_chart_studio` future flag is set, then deprecation warnings are raised whenever the `chart_studio` modules/functions are used from their legacy locations under the `plotly` package. - If the `remove_deprecations` future flag is set then the chart studio functions are fully removed from the plotly package and are accessible only under `chart_studio`. When `remove_deprecations` is set, `plotly` has no dependency on the `chart_studio` package. ## Usage To remove the chart_studio functionality from the main `plotly` module, use the ```python from _plotly_future_ import remove_deprecations ``` This will further speed up imports, and will allow for testing code to make sure it will be compatible with the package structure of plotly.py version 4. ## Import optimization This PR also makes a relatively minor change to the code generation logic for `graph_objs` and `validator` that yields an import time reduction of ~10-20% . Rather that creating a single file for each datatype and validator class, all of the classes in a `graph_obj` or `validator` module are specified directly in the `__init__.py` file. This reduces the number of files significantly, which seems to yield a modest but consistent speedup while being 100% backward compatible.
1 parent 5f27aec commit 3678aa9

8,169 files changed

Lines changed: 496021 additions & 484974 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

_plotly_future_/__init__.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import warnings
2+
import functools
3+
14
_future_flags = set()
25

36

@@ -6,3 +9,49 @@ def _assert_plotly_not_imported():
69
if 'plotly' in sys.modules:
710
raise ImportError("""\
811
The _plotly_future_ module must be imported before the plotly module""")
12+
13+
14+
warnings.filterwarnings(
15+
'default',
16+
'.*?is deprecated, please use chart_studio*',
17+
DeprecationWarning
18+
)
19+
20+
21+
def _chart_studio_warning(submodule):
22+
if 'extract_chart_studio' in _future_flags:
23+
warnings.warn(
24+
'The plotly.{submodule} module is deprecated, '
25+
'please use chart_studio.{submodule} instead'
26+
.format(submodule=submodule),
27+
DeprecationWarning,
28+
stacklevel=2)
29+
30+
31+
def _chart_studio_deprecation(fn):
32+
33+
fn_name = fn.__name__
34+
fn_module = fn.__module__
35+
plotly_name = '.'.join(
36+
['plotly'] + fn_module.split('.')[1:] + [fn_name])
37+
chart_studio_name = '.'.join(
38+
['chart_studio'] + fn_module.split('.')[1:] + [fn_name])
39+
40+
msg = """\
41+
{plotly_name} is deprecated, please use {chart_studio_name}\
42+
""".format(plotly_name=plotly_name, chart_studio_name=chart_studio_name)
43+
44+
@functools.wraps(fn)
45+
def wrapper(*args, **kwargs):
46+
if 'extract_chart_studio' in _future_flags:
47+
warnings.warn(
48+
msg,
49+
DeprecationWarning,
50+
stacklevel=2)
51+
52+
return fn(*args, **kwargs)
53+
54+
return wrapper
55+
56+
57+
__all__ = ['_future_flags', '_chart_studio_warning']
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from __future__ import absolute_import
2+
from _plotly_future_ import _future_flags, _assert_plotly_not_imported
3+
4+
_assert_plotly_not_imported()
5+
_future_flags.add('extract_chart_studio')
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from __future__ import absolute_import
2+
from _plotly_future_ import _future_flags, _assert_plotly_not_imported
3+
4+
_assert_plotly_not_imported()
5+
_future_flags.add('remove_deprecations')

_plotly_future_/v4.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
from __future__ import absolute_import
2-
from _plotly_future_ import renderer_defaults, template_defaults
2+
from _plotly_future_ import (
3+
renderer_defaults, template_defaults, extract_chart_studio,
4+
remove_deprecations)

_plotly_utils/basevalidators.py

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,18 @@
1+
from __future__ import absolute_import
2+
13
import base64
24
import numbers
35
import textwrap
46
import uuid
57
from importlib import import_module
68
import copy
7-
89
import io
910
from copy import deepcopy
10-
1111
import re
12-
13-
# Optional imports
14-
# ----------------
1512
import sys
1613
from six import string_types
1714

18-
np = None
19-
pd = None
20-
21-
try:
22-
np = import_module('numpy')
23-
24-
try:
25-
pd = import_module('pandas')
26-
except ImportError:
27-
pass
28-
29-
except ImportError:
30-
pass
15+
from _plotly_utils.optional_imports import get_module
3116

3217

3318
# back-port of fullmatch from Py3.4+
@@ -50,6 +35,8 @@ def to_scalar_or_list(v):
5035
# Python native scalar type ('float' in the example above).
5136
# We explicitly check if is has the 'item' method, which conventionally
5237
# converts these types to native scalars.
38+
np = get_module('numpy')
39+
pd = get_module('pandas')
5340
if np and np.isscalar(v) and hasattr(v, 'item'):
5441
return v.item()
5542
if isinstance(v, (list, tuple)):
@@ -86,7 +73,8 @@ def copy_to_readonly_numpy_array(v, kind=None, force_numeric=False):
8673
np.ndarray
8774
Numpy array with the 'WRITEABLE' flag set to False
8875
"""
89-
76+
np = get_module('numpy')
77+
pd = get_module('pandas')
9078
assert np is not None
9179

9280
# ### Process kind ###
@@ -175,7 +163,9 @@ def is_numpy_convertable(v):
175163
def is_homogeneous_array(v):
176164
"""
177165
Return whether a value is considered to be a homogeneous array
178-
"""
166+
"""
167+
np = get_module('numpy')
168+
pd = get_module('pandas')
179169
if ((np and isinstance(v, np.ndarray) or
180170
(pd and isinstance(v, (pd.Series, pd.Index))))):
181171
return True
@@ -616,7 +606,7 @@ def description(self):
616606
as a plotly.grid_objs.Column object""".format(plotly_name=self.plotly_name))
617607

618608
def validate_coerce(self, v):
619-
from plotly.grid_objs import Column
609+
from chart_studio.grid_objs import Column
620610
if v is None:
621611
# Pass None through
622612
pass
@@ -704,7 +694,7 @@ def validate_coerce(self, v):
704694
# Pass None through
705695
pass
706696
elif self.array_ok and is_homogeneous_array(v):
707-
697+
np = get_module('numpy')
708698
try:
709699
v_array = copy_to_readonly_numpy_array(v, force_numeric=True)
710700
except (ValueError, TypeError, OverflowError):
@@ -825,7 +815,7 @@ def validate_coerce(self, v):
825815
# Pass None through
826816
pass
827817
elif self.array_ok and is_homogeneous_array(v):
828-
818+
np = get_module('numpy')
829819
v_array = copy_to_readonly_numpy_array(v,
830820
kind=('i', 'u'),
831821
force_numeric=True)
@@ -964,6 +954,8 @@ def validate_coerce(self, v):
964954
self.raise_invalid_elements(invalid_els)
965955

966956
if is_homogeneous_array(v):
957+
np = get_module('numpy')
958+
967959
# If not strict, let numpy cast elements to strings
968960
v = copy_to_readonly_numpy_array(v, kind='U')
969961

_plotly_utils/exceptions.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
class PlotlyError(Exception):
2+
pass
3+
4+
5+
class PlotlyEmptyDataError(PlotlyError):
6+
pass
7+
8+
9+
class PlotlyGraphObjectError(PlotlyError):
10+
def __init__(self, message='', path=(), notes=()):
11+
"""
12+
General graph object error for validation failures.
13+
14+
:param (str|unicode) message: The error message.
15+
:param (iterable) path: A path pointing to the error.
16+
:param notes: Add additional notes, but keep default exception message.
17+
18+
"""
19+
self.message = message
20+
self.plain_message = message # for backwards compat
21+
self.path = list(path)
22+
self.notes = notes
23+
super(PlotlyGraphObjectError, self).__init__(message)
24+
25+
def __str__(self):
26+
"""This is called by Python to present the error message."""
27+
format_dict = {
28+
'message': self.message,
29+
'path': '[' + ']['.join(repr(k) for k in self.path) + ']',
30+
'notes': '\n'.join(self.notes)
31+
}
32+
return ('{message}\n\nPath To Error: {path}\n\n{notes}'
33+
.format(**format_dict))
34+
35+
36+
class PlotlyDictKeyError(PlotlyGraphObjectError):
37+
def __init__(self, obj, path, notes=()):
38+
"""See PlotlyGraphObjectError.__init__ for param docs."""
39+
format_dict = {'attribute': path[-1], 'object_name': obj._name}
40+
message = ("'{attribute}' is not allowed in '{object_name}'"
41+
.format(**format_dict))
42+
notes = [obj.help(return_help=True)] + list(notes)
43+
super(PlotlyDictKeyError, self).__init__(
44+
message=message, path=path, notes=notes
45+
)
46+
47+
48+
class PlotlyDictValueError(PlotlyGraphObjectError):
49+
def __init__(self, obj, path, notes=()):
50+
"""See PlotlyGraphObjectError.__init__ for param docs."""
51+
format_dict = {'attribute': path[-1], 'object_name': obj._name}
52+
message = ("'{attribute}' has invalid value inside '{object_name}'"
53+
.format(**format_dict))
54+
notes = [obj.help(path[-1], return_help=True)] + list(notes)
55+
super(PlotlyDictValueError, self).__init__(
56+
message=message, notes=notes, path=path
57+
)
58+
59+
60+
class PlotlyListEntryError(PlotlyGraphObjectError):
61+
def __init__(self, obj, path, notes=()):
62+
"""See PlotlyGraphObjectError.__init__ for param docs."""
63+
format_dict = {'index': path[-1], 'object_name': obj._name}
64+
message = ("Invalid entry found in '{object_name}' at index, '{index}'"
65+
.format(**format_dict))
66+
notes = [obj.help(return_help=True)] + list(notes)
67+
super(PlotlyListEntryError, self).__init__(
68+
message=message, path=path, notes=notes
69+
)
70+
71+
72+
class PlotlyDataTypeError(PlotlyGraphObjectError):
73+
def __init__(self, obj, path, notes=()):
74+
"""See PlotlyGraphObjectError.__init__ for param docs."""
75+
format_dict = {'index': path[-1], 'object_name': obj._name}
76+
message = ("Invalid entry found in '{object_name}' at index, '{index}'"
77+
.format(**format_dict))
78+
note = "It's invalid because it doesn't contain a valid 'type' value."
79+
notes = [note] + list(notes)
80+
super(PlotlyDataTypeError, self).__init__(
81+
message=message, path=path, notes=notes
82+
)

_plotly_utils/files.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import os
2+
3+
PLOTLY_DIR = os.environ.get("PLOTLY_DIR",
4+
os.path.join(os.path.expanduser("~"), ".plotly"))
5+
TEST_FILE = os.path.join(PLOTLY_DIR, ".permission_test")
6+
7+
8+
def _permissions():
9+
try:
10+
if not os.path.exists(PLOTLY_DIR):
11+
try:
12+
os.mkdir(PLOTLY_DIR)
13+
except Exception:
14+
# in case of race
15+
if not os.path.isdir(PLOTLY_DIR):
16+
raise
17+
with open(TEST_FILE, 'w') as f:
18+
f.write('testing\n')
19+
try:
20+
os.remove(TEST_FILE)
21+
except Exception:
22+
pass
23+
return True
24+
except Exception: # Do not trap KeyboardInterrupt.
25+
return False
26+
27+
28+
_file_permissions = None
29+
30+
31+
def ensure_writable_plotly_dir():
32+
# Cache permissions status
33+
global _file_permissions
34+
if _file_permissions is None:
35+
_file_permissions = _permissions()
36+
return _file_permissions

_plotly_utils/optional_imports.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""
2+
Stand-alone module to provide information about whether optional deps exist.
3+
4+
"""
5+
from __future__ import absolute_import
6+
7+
from importlib import import_module
8+
import logging
9+
10+
logger = logging.getLogger(__name__)
11+
_not_importable = set()
12+
13+
14+
def get_module(name):
15+
"""
16+
Return module or None. Absolute import is required.
17+
18+
:param (str) name: Dot-separated module path. E.g., 'scipy.stats'.
19+
:raise: (ImportError) Only when exc_msg is defined.
20+
:return: (module|None) If import succeeds, the module will be returned.
21+
22+
"""
23+
if name not in _not_importable:
24+
try:
25+
return import_module(name)
26+
except ImportError:
27+
_not_importable.add(name)
28+
except Exception as e:
29+
_not_importable.add(name)
30+
msg = "Error importing optional module {}".format(name)
31+
logger.exception(msg)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy