lets-plot 4.8.0rc1__cp313-cp313-macosx_12_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of lets-plot might be problematic. Click here for more details.
- lets_plot/__init__.py +283 -0
- lets_plot/_global_settings.py +192 -0
- lets_plot/_kbridge.py +149 -0
- lets_plot/_type_utils.py +133 -0
- lets_plot/_version.py +6 -0
- lets_plot/bistro/__init__.py +16 -0
- lets_plot/bistro/_plot2d_common.py +106 -0
- lets_plot/bistro/corr.py +448 -0
- lets_plot/bistro/im.py +196 -0
- lets_plot/bistro/joint.py +192 -0
- lets_plot/bistro/qq.py +207 -0
- lets_plot/bistro/residual.py +341 -0
- lets_plot/bistro/waterfall.py +332 -0
- lets_plot/export/__init__.py +6 -0
- lets_plot/export/ggsave_.py +170 -0
- lets_plot/frontend_context/__init__.py +8 -0
- lets_plot/frontend_context/_configuration.py +151 -0
- lets_plot/frontend_context/_frontend_ctx.py +16 -0
- lets_plot/frontend_context/_html_contexts.py +117 -0
- lets_plot/frontend_context/_intellij_python_json_ctx.py +38 -0
- lets_plot/frontend_context/_json_contexts.py +39 -0
- lets_plot/frontend_context/_jupyter_notebook_ctx.py +119 -0
- lets_plot/frontend_context/_mime_types.py +7 -0
- lets_plot/frontend_context/_static_html_page_ctx.py +27 -0
- lets_plot/frontend_context/_static_svg_ctx.py +26 -0
- lets_plot/frontend_context/_webbr_html_page_ctx.py +29 -0
- lets_plot/frontend_context/sandbox.py +5 -0
- lets_plot/geo_data/__init__.py +19 -0
- lets_plot/geo_data/core.py +335 -0
- lets_plot/geo_data/geocoder.py +988 -0
- lets_plot/geo_data/geocodes.py +512 -0
- lets_plot/geo_data/gis/__init__.py +0 -0
- lets_plot/geo_data/gis/fluent_dict.py +201 -0
- lets_plot/geo_data/gis/geocoding_service.py +42 -0
- lets_plot/geo_data/gis/geometry.py +91 -0
- lets_plot/geo_data/gis/json_request.py +232 -0
- lets_plot/geo_data/gis/json_response.py +308 -0
- lets_plot/geo_data/gis/request.py +492 -0
- lets_plot/geo_data/gis/response.py +247 -0
- lets_plot/geo_data/livemap_helper.py +65 -0
- lets_plot/geo_data/to_geo_data_frame.py +141 -0
- lets_plot/geo_data/type_assertion.py +34 -0
- lets_plot/geo_data_internals/__init__.py +4 -0
- lets_plot/geo_data_internals/constants.py +13 -0
- lets_plot/geo_data_internals/utils.py +33 -0
- lets_plot/mapping.py +115 -0
- lets_plot/package_data/lets-plot.min.js +3 -0
- lets_plot/plot/__init__.py +64 -0
- lets_plot/plot/_global_theme.py +14 -0
- lets_plot/plot/annotation.py +290 -0
- lets_plot/plot/coord.py +242 -0
- lets_plot/plot/core.py +1025 -0
- lets_plot/plot/expand_limits_.py +78 -0
- lets_plot/plot/facet.py +210 -0
- lets_plot/plot/font_features.py +71 -0
- lets_plot/plot/geom.py +9144 -0
- lets_plot/plot/geom_extras.py +53 -0
- lets_plot/plot/geom_function_.py +219 -0
- lets_plot/plot/geom_imshow_.py +393 -0
- lets_plot/plot/geom_livemap_.py +343 -0
- lets_plot/plot/ggbunch_.py +96 -0
- lets_plot/plot/gggrid_.py +139 -0
- lets_plot/plot/ggtb_.py +81 -0
- lets_plot/plot/guide.py +231 -0
- lets_plot/plot/label.py +187 -0
- lets_plot/plot/marginal_layer.py +181 -0
- lets_plot/plot/plot.py +245 -0
- lets_plot/plot/pos.py +344 -0
- lets_plot/plot/sampling.py +338 -0
- lets_plot/plot/sandbox_.py +26 -0
- lets_plot/plot/scale.py +3580 -0
- lets_plot/plot/scale_colormap_mpl.py +300 -0
- lets_plot/plot/scale_convenience.py +155 -0
- lets_plot/plot/scale_identity_.py +662 -0
- lets_plot/plot/scale_position.py +1342 -0
- lets_plot/plot/series_meta.py +209 -0
- lets_plot/plot/stat.py +585 -0
- lets_plot/plot/subplots.py +331 -0
- lets_plot/plot/subplots_util.py +24 -0
- lets_plot/plot/theme_.py +795 -0
- lets_plot/plot/theme_set.py +418 -0
- lets_plot/plot/tooltip.py +486 -0
- lets_plot/plot/util.py +267 -0
- lets_plot/settings_utils.py +244 -0
- lets_plot/tilesets.py +429 -0
- lets_plot-4.8.0rc1.dist-info/METADATA +220 -0
- lets_plot-4.8.0rc1.dist-info/RECORD +95 -0
- lets_plot-4.8.0rc1.dist-info/WHEEL +5 -0
- lets_plot-4.8.0rc1.dist-info/licenses/LICENSE +21 -0
- lets_plot-4.8.0rc1.dist-info/licenses/licenses/LICENSE.FreeType +166 -0
- lets_plot-4.8.0rc1.dist-info/licenses/licenses/LICENSE.ImageMagick +106 -0
- lets_plot-4.8.0rc1.dist-info/licenses/licenses/LICENSE.expat +21 -0
- lets_plot-4.8.0rc1.dist-info/licenses/licenses/LICENSE.fontconfig +200 -0
- lets_plot-4.8.0rc1.dist-info/top_level.txt +2 -0
- lets_plot_kotlin_bridge.cpython-313-darwin.so +0 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Copyright (c) 2020. JetBrains s.r.o.
|
|
2
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
from os.path import join
|
|
6
|
+
from typing import Union, Optional
|
|
7
|
+
|
|
8
|
+
from ..plot.core import PlotSpec
|
|
9
|
+
from ..plot.core import _to_svg, _to_html, _export_as_raster
|
|
10
|
+
from ..plot.plot import GGBunch
|
|
11
|
+
from ..plot.subplots import SupPlotsSpec
|
|
12
|
+
|
|
13
|
+
__all__ = ['ggsave']
|
|
14
|
+
|
|
15
|
+
_DEF_EXPORT_DIR = "lets-plot-images"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def ggsave(plot: Union[PlotSpec, SupPlotsSpec, GGBunch], filename: str, *, path: str = None, iframe: bool = True,
|
|
19
|
+
scale: float = None, w: Optional[float] = None, h: Optional[float] = None, unit: Optional[str] = None,
|
|
20
|
+
dpi: Optional[int] = None) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Export plot to a file.
|
|
23
|
+
Supported formats: PNG, SVG, PDF, HTML.
|
|
24
|
+
|
|
25
|
+
The exported file is created in the directory ${user.dir}/lets-plot-images
|
|
26
|
+
if not specified otherwise (see the ``path`` parameter).
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
plot : ``PlotSpec``
|
|
31
|
+
Plot specification to export.
|
|
32
|
+
filename : str
|
|
33
|
+
Name of the file. It must end with a file extension corresponding
|
|
34
|
+
to one of the supported formats: SVG, HTML (or HTM), PNG, PDF (requires the pillow library).
|
|
35
|
+
path : str
|
|
36
|
+
Path to a directory to save image files in.
|
|
37
|
+
By default, it is ${user.dir}/lets-plot-images.
|
|
38
|
+
iframe : bool, default=True
|
|
39
|
+
Whether to wrap the HTML page into an iFrame.
|
|
40
|
+
Only applicable when exporting to HTML.
|
|
41
|
+
Some browsers may not display some UTF-8 characters correctly when setting iframe=True
|
|
42
|
+
scale : float, default=2.0
|
|
43
|
+
Scaling factor for raster output.
|
|
44
|
+
Only applicable when exporting to PNG or PDF.
|
|
45
|
+
w : float, default=None
|
|
46
|
+
Width of the output image in units.
|
|
47
|
+
Only applicable when exporting to SVG, PNG, or PDF.
|
|
48
|
+
h : float, default=None
|
|
49
|
+
Height of the output image in units.
|
|
50
|
+
Only applicable when exporting to SVG, PNG, or PDF.
|
|
51
|
+
unit : {'in', 'cm', 'mm', 'px'}, default='in'
|
|
52
|
+
Unit of the output image. One of: 'in', 'cm', 'mm' or 'px'.
|
|
53
|
+
Only applicable when exporting to SVG, PNG, or PDF.
|
|
54
|
+
dpi : int, default=300
|
|
55
|
+
Resolution in dots per inch.
|
|
56
|
+
Only applicable when exporting to PNG or PDF.
|
|
57
|
+
The default value depends on the unit:
|
|
58
|
+
|
|
59
|
+
- for 'px' it is 96 (output image will have the same pixel size as ``w``, and ``h`` values)
|
|
60
|
+
- for physical units ('in', 'cm', 'mm') it is 300.
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
str
|
|
65
|
+
Absolute pathname of the created file.
|
|
66
|
+
|
|
67
|
+
Notes
|
|
68
|
+
-----
|
|
69
|
+
Large plot dimensions without units require explicit unit specification.
|
|
70
|
+
When ``w`` or ``h`` value exceeds 20 without specifying units (e.g., ``ggsave(p, 300, 400)``),
|
|
71
|
+
we ask to specify units explicitly:
|
|
72
|
+
``ggsave(p, 300, 400, unit='px')`` or ``ggsave(p, 3, 4, unit='in')``.
|
|
73
|
+
|
|
74
|
+
----
|
|
75
|
+
|
|
76
|
+
The output format is inferred from the filename extension.
|
|
77
|
+
|
|
78
|
+
For PNG, and PDF formats:
|
|
79
|
+
|
|
80
|
+
- If ``w``, ``h``, ``unit``, and ``dpi`` are all specified:
|
|
81
|
+
|
|
82
|
+
- The plot's pixel size (default or set by `ggsize() <https://lets-plot.org/python/pages/api/lets_plot.ggsize.html>`__) is ignored.
|
|
83
|
+
- The output size is calculated using the specified ``w``, ``h``, ``unit`` and ``dpi``.
|
|
84
|
+
|
|
85
|
+
- The plot is resized to fit the specified ``w`` x ``h`` area, which may affect the layout, tick labels, and other elements.
|
|
86
|
+
|
|
87
|
+
- If only ``dpi`` is specified:
|
|
88
|
+
|
|
89
|
+
- The plot's pixel size (default or set by `ggsize() <https://lets-plot.org/python/pages/api/lets_plot.ggsize.html>`__) is converted to inches using the standard display PPI of 96.
|
|
90
|
+
- The output size is then calculated based on the specified DPI.
|
|
91
|
+
|
|
92
|
+
- The plot maintains its aspect ratio, preserving layout, tick labels, and other visual elements.
|
|
93
|
+
- Useful for printing - the plot will appear nearly the same size as on screen.
|
|
94
|
+
|
|
95
|
+
- If ``w``, ``h`` are not specified:
|
|
96
|
+
|
|
97
|
+
- The ``scale`` parameter is used to determine the output size.
|
|
98
|
+
|
|
99
|
+
- The plot maintains its aspect ratio, preserving layout, tick labels, and other visual elements.
|
|
100
|
+
- Useful for generating high-resolution images suitable for publication.
|
|
101
|
+
|
|
102
|
+
For SVG format:
|
|
103
|
+
|
|
104
|
+
- If ``w``, ``h``, and ``unit`` are specified:
|
|
105
|
+
|
|
106
|
+
- The plot's pixel size (default or set by `ggsize() <https://lets-plot.org/python/pages/api/lets_plot.ggsize.html>`__) is ignored.
|
|
107
|
+
- The output size is calculated using the specified ``w``, ``h``, and ``unit``.
|
|
108
|
+
|
|
109
|
+
----
|
|
110
|
+
|
|
111
|
+
Plots with ``geom_livemap()`` can be saved to HTML only.
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
Examples
|
|
115
|
+
--------
|
|
116
|
+
.. jupyter-execute::
|
|
117
|
+
:linenos:
|
|
118
|
+
:emphasize-lines: 6
|
|
119
|
+
|
|
120
|
+
from IPython.display import Image
|
|
121
|
+
from lets_plot import *
|
|
122
|
+
LetsPlot.setup_html()
|
|
123
|
+
filename = 'plot.png'
|
|
124
|
+
plot = ggplot() + geom_point(x=0, y=0) + ggtitle(filename)
|
|
125
|
+
fullpath = ggsave(plot, filename, w=4, h=3)
|
|
126
|
+
Image(filename=fullpath, width=600, height=450)
|
|
127
|
+
|
|
128
|
+
|
|
|
129
|
+
|
|
130
|
+
.. jupyter-execute::
|
|
131
|
+
:linenos:
|
|
132
|
+
:emphasize-lines: 6
|
|
133
|
+
|
|
134
|
+
from IPython.display import HTML
|
|
135
|
+
from lets_plot import *
|
|
136
|
+
LetsPlot.setup_html()
|
|
137
|
+
filename = 'plot.html'
|
|
138
|
+
plot = ggplot() + geom_point(x=0, y=0) + ggtitle(filename)
|
|
139
|
+
fullpath = ggsave(plot, filename, iframe=False)
|
|
140
|
+
HTML(filename=fullpath)
|
|
141
|
+
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
if not (isinstance(plot, PlotSpec) or isinstance(plot, SupPlotsSpec) or isinstance(plot, GGBunch)):
|
|
145
|
+
raise ValueError("PlotSpec, SupPlotsSpec or GGBunch expected but was: {}".format(type(plot)))
|
|
146
|
+
|
|
147
|
+
filename = filename.strip()
|
|
148
|
+
name, ext = os.path.splitext(filename)
|
|
149
|
+
|
|
150
|
+
if not name:
|
|
151
|
+
raise ValueError("Malformed filename: '{}'.".format(filename))
|
|
152
|
+
if not ext:
|
|
153
|
+
raise ValueError("Missing file extension: '{}'.".format(filename))
|
|
154
|
+
|
|
155
|
+
if not path:
|
|
156
|
+
path = join(os.getcwd(), _DEF_EXPORT_DIR)
|
|
157
|
+
|
|
158
|
+
pathname = join(path, filename)
|
|
159
|
+
|
|
160
|
+
ext = ext[1:].lower()
|
|
161
|
+
if ext == 'svg':
|
|
162
|
+
return _to_svg(plot, pathname, w=w, h=h, unit=unit)
|
|
163
|
+
elif ext in ['html', 'htm']:
|
|
164
|
+
return _to_html(plot, pathname, iframe=iframe)
|
|
165
|
+
elif ext in ['png', 'pdf']:
|
|
166
|
+
return _export_as_raster(plot, pathname, scale, export_format=ext, w=w, h=h, unit=unit, dpi=dpi)
|
|
167
|
+
else:
|
|
168
|
+
raise ValueError(
|
|
169
|
+
"Unsupported file extension: '{}'\nPlease use one of: 'png', 'svg', 'pdf', 'html', 'htm'".format(ext)
|
|
170
|
+
)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2019. JetBrains s.r.o.
|
|
3
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
4
|
+
#
|
|
5
|
+
from typing import Dict, Any
|
|
6
|
+
|
|
7
|
+
from ._frontend_ctx import FrontendContext
|
|
8
|
+
from ._html_contexts import _create_html_frontend_context, _use_isolated_frame, _create_wb_html_frontend_context
|
|
9
|
+
from ._json_contexts import _create_json_frontend_context, _is_Intellij_Python_Lets_Plot_Plugin
|
|
10
|
+
from ._mime_types import TEXT_HTML, LETS_PLOT_JSON
|
|
11
|
+
from ._static_svg_ctx import StaticSvgImageContext
|
|
12
|
+
from ._webbr_html_page_ctx import WebBrHtmlPageContext
|
|
13
|
+
from .._version import __version__
|
|
14
|
+
from ..plot.core import PlotSpec
|
|
15
|
+
from ..plot.plot import GGBunch
|
|
16
|
+
from ..plot.subplots import SupPlotsSpec
|
|
17
|
+
|
|
18
|
+
__all__ = []
|
|
19
|
+
|
|
20
|
+
_frontend_contexts: Dict[str, FrontendContext] = {}
|
|
21
|
+
|
|
22
|
+
_default_mimetype = TEXT_HTML
|
|
23
|
+
if _is_Intellij_Python_Lets_Plot_Plugin():
|
|
24
|
+
_default_mimetype = LETS_PLOT_JSON
|
|
25
|
+
_frontend_contexts[LETS_PLOT_JSON] = _create_json_frontend_context()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _setup_html_context(*,
|
|
29
|
+
isolated_frame: bool = None,
|
|
30
|
+
offline: bool,
|
|
31
|
+
no_js: bool,
|
|
32
|
+
show_status: bool) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Configures Lets-Plot HTML output.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
isolated_frame : bool
|
|
39
|
+
True - generate HTLM which can be used in `iframe` or in a standalone HTML document
|
|
40
|
+
False - pre-load Lets-Plot JS library. Notebook cell output will only consist of HTML for the plot rendering.
|
|
41
|
+
Default: None - auto-detect.
|
|
42
|
+
offline : bool
|
|
43
|
+
True - full Lets-Plot JS bundle will be added to the notebook. Use this option if you would like
|
|
44
|
+
to work with notebook without the Internet connection.
|
|
45
|
+
False - load Lets-Plot JS library from CDN.
|
|
46
|
+
no_js : bool
|
|
47
|
+
True - do not generate HTML+JS as an output - just static SVG image.
|
|
48
|
+
show_status : bool
|
|
49
|
+
Whether to show status of loading of the Lets-Plot JS library.
|
|
50
|
+
Only applicable when the Lets-Plot JS library is preloaded.
|
|
51
|
+
|
|
52
|
+
"""
|
|
53
|
+
global _default_mimetype
|
|
54
|
+
if _default_mimetype == LETS_PLOT_JSON:
|
|
55
|
+
# Plots will be rendered by Lets-Plot IntelliJ plugin.
|
|
56
|
+
# No other contexts are needed.
|
|
57
|
+
if show_status:
|
|
58
|
+
print(
|
|
59
|
+
'Lets-Plot v{}: output mimetype {} configured by default. No need for HTML output.'.format(__version__,
|
|
60
|
+
LETS_PLOT_JSON))
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
if no_js:
|
|
64
|
+
ctx = StaticSvgImageContext()
|
|
65
|
+
else:
|
|
66
|
+
ctx = _create_html_frontend_context(isolated_frame, offline=offline)
|
|
67
|
+
|
|
68
|
+
ctx.configure(verbose=show_status)
|
|
69
|
+
_frontend_contexts[TEXT_HTML] = ctx
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _setup_wb_html_context(*,
|
|
73
|
+
exec: str,
|
|
74
|
+
new: bool) -> None:
|
|
75
|
+
"""
|
|
76
|
+
Configures Lets-Plot HTML output for showing in a browser.
|
|
77
|
+
|
|
78
|
+
Parameters
|
|
79
|
+
----------
|
|
80
|
+
exec : str, optional
|
|
81
|
+
Command to execute to open the plot in a web browser.
|
|
82
|
+
If not specified, the default browser will be used.
|
|
83
|
+
new : bool, default=False
|
|
84
|
+
If True, the URL is opened in a new window of the web browser.
|
|
85
|
+
If False, the URL is opened in the already opened web browser window.
|
|
86
|
+
"""
|
|
87
|
+
ctx = _create_wb_html_frontend_context(exec, new)
|
|
88
|
+
_frontend_contexts[TEXT_HTML] = ctx
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _display_plot(spec: Any):
|
|
92
|
+
"""
|
|
93
|
+
Draw plot or `bunch` of plots in the current frontend context
|
|
94
|
+
:param spec: PlotSpec or GGBunch object
|
|
95
|
+
"""
|
|
96
|
+
if not (isinstance(spec, PlotSpec) or isinstance(spec, SupPlotsSpec) or isinstance(spec, GGBunch)):
|
|
97
|
+
raise ValueError("PlotSpec or SupPlotsSpec expected but was: {}".format(type(spec)))
|
|
98
|
+
|
|
99
|
+
if _default_mimetype == TEXT_HTML:
|
|
100
|
+
if TEXT_HTML not in _frontend_contexts:
|
|
101
|
+
raise RuntimeError(
|
|
102
|
+
"HTML frontend not configured. Before displaying plots, please call either:\n"
|
|
103
|
+
"- LetsPlot.setup_html() for displaying HTML output inplace\n"
|
|
104
|
+
"- LetsPlot.setup_show_ext() for displaying HTML output in an external web browser\n"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
if isinstance(_frontend_contexts[TEXT_HTML], WebBrHtmlPageContext):
|
|
108
|
+
_frontend_contexts[TEXT_HTML].show(spec.as_dict())
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
plot_html = _as_html(spec.as_dict())
|
|
112
|
+
try:
|
|
113
|
+
from IPython.display import display_html
|
|
114
|
+
display_html(plot_html, raw=True)
|
|
115
|
+
return
|
|
116
|
+
except ImportError:
|
|
117
|
+
pass
|
|
118
|
+
|
|
119
|
+
print(spec.as_dict())
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
if _default_mimetype == LETS_PLOT_JSON:
|
|
123
|
+
_frontend_contexts[LETS_PLOT_JSON].show(spec.as_dict())
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
# fallback to plain text.
|
|
127
|
+
print(spec.as_dict())
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _as_html(plot_spec: Dict) -> str:
|
|
131
|
+
"""
|
|
132
|
+
Creates plot HTML using 'html' frontend context.
|
|
133
|
+
|
|
134
|
+
:param plot_spec: dict
|
|
135
|
+
"""
|
|
136
|
+
if TEXT_HTML not in _frontend_contexts:
|
|
137
|
+
if _use_isolated_frame():
|
|
138
|
+
# 'Isolated' HTML context can be setup lazily.
|
|
139
|
+
_setup_html_context(isolated_frame=True,
|
|
140
|
+
offline=False,
|
|
141
|
+
no_js=False,
|
|
142
|
+
show_status=False)
|
|
143
|
+
else:
|
|
144
|
+
return """\
|
|
145
|
+
<div style="color:darkred;">
|
|
146
|
+
Lets-plot `html` is not configured.<br>
|
|
147
|
+
Try to use `LetsPlot.setup_html()` before first occurrence of plot.
|
|
148
|
+
</div>
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
return _frontend_contexts[TEXT_HTML].as_str(plot_spec)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2019. JetBrains s.r.o.
|
|
3
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
4
|
+
#
|
|
5
|
+
from typing import Dict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FrontendContext:
|
|
9
|
+
def configure(self, verbose: bool):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
def as_str(self, plot_spec: Dict) -> str:
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
def show(self, plot_spec: Dict) -> str:
|
|
16
|
+
pass
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Copyright (c) 2020. JetBrains s.r.o.
|
|
2
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from ._frontend_ctx import FrontendContext
|
|
7
|
+
from ._jupyter_notebook_ctx import JupyterNotebookContext
|
|
8
|
+
from ._static_html_page_ctx import StaticHtmlPageContext
|
|
9
|
+
from ._webbr_html_page_ctx import WebBrHtmlPageContext
|
|
10
|
+
from .._global_settings import has_global_value, get_global_bool, HTML_ISOLATED_FRAME
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _create_html_frontend_context(isolated_frame: bool = None, offline: bool = None) -> FrontendContext:
|
|
14
|
+
"""
|
|
15
|
+
Configures Lets-Plot HTML output.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
isolated_frame : bool, optional, default None - auto-detect
|
|
20
|
+
If True, generate HTLM which can be used in `iframe` or in a standalone HTML document
|
|
21
|
+
If False, pre-load Lets-Plot JS library. Notebook cell output will only consist of HTML for the plot rendering.
|
|
22
|
+
|
|
23
|
+
offline : bool, optional, default None - evaluated to 'connected' mode in production environment.
|
|
24
|
+
If True, full Lets-Plot JS bundle will be added to the notebook. Use this option if you would like
|
|
25
|
+
to work with notebook without the Internet connection.
|
|
26
|
+
If False, load Lets-Plot JS library from CDN.
|
|
27
|
+
"""
|
|
28
|
+
if isolated_frame is None:
|
|
29
|
+
isolated_frame = _use_isolated_frame()
|
|
30
|
+
|
|
31
|
+
if isolated_frame:
|
|
32
|
+
return StaticHtmlPageContext(offline)
|
|
33
|
+
else:
|
|
34
|
+
return JupyterNotebookContext(offline)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _create_wb_html_frontend_context(exec: str, new: bool) -> FrontendContext:
|
|
38
|
+
"""
|
|
39
|
+
Configures Lets-Plot HTML output for showing in web browser.
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
exec : str, optional
|
|
44
|
+
The name of the web browser to use.
|
|
45
|
+
If not specified, the default browser will be used.
|
|
46
|
+
new : bool, default=False
|
|
47
|
+
If True, the URL is opened in a new window of the web browser.
|
|
48
|
+
If False, the URL is opened in the already opened web browser window.
|
|
49
|
+
"""
|
|
50
|
+
return WebBrHtmlPageContext(exec, new)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _use_isolated_frame() -> bool:
|
|
54
|
+
# check environment
|
|
55
|
+
if has_global_value(HTML_ISOLATED_FRAME):
|
|
56
|
+
return get_global_bool(HTML_ISOLATED_FRAME)
|
|
57
|
+
|
|
58
|
+
return _detect_isolated_frame()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _detect_isolated_frame() -> bool:
|
|
62
|
+
if not _is_IPython_display():
|
|
63
|
+
return True # isolated HTML page to show somehow
|
|
64
|
+
|
|
65
|
+
# Most online notebook platforms are showing cell output in iframe and require
|
|
66
|
+
# a self-contained HTML which includes both:
|
|
67
|
+
# - the script loading JS library and
|
|
68
|
+
# - the script that uses this JS lib to create plot.
|
|
69
|
+
|
|
70
|
+
# Try to detect the platform.
|
|
71
|
+
try:
|
|
72
|
+
import google.colab
|
|
73
|
+
return True # Colab -> iframe
|
|
74
|
+
except ImportError:
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
if os.path.exists("/kaggle/input"):
|
|
78
|
+
return False # Kaggle -> no iframe
|
|
79
|
+
|
|
80
|
+
if "AZURE_NOTEBOOKS_HOST" in os.environ:
|
|
81
|
+
return True # Azure Notebook -> iframe
|
|
82
|
+
|
|
83
|
+
if "DEEPNOTE_PROJECT_ID" in os.environ:
|
|
84
|
+
return True # Deepnote Notebook -> iframe
|
|
85
|
+
|
|
86
|
+
if "databricks" in str(os.environ):
|
|
87
|
+
# Databricks notebook -> iframe
|
|
88
|
+
# As proposed: https://github.com/JetBrains/lets-plot/issues/602
|
|
89
|
+
return True
|
|
90
|
+
|
|
91
|
+
if "NEXTJOURNAL" in str(os.environ):
|
|
92
|
+
return True # NextJournal notebook -> iframe
|
|
93
|
+
|
|
94
|
+
if os.getenv("PLOTLY_RENDERER") == "colab":
|
|
95
|
+
# good enouth - something colab-like
|
|
96
|
+
return True # Colab -> iframe
|
|
97
|
+
|
|
98
|
+
# ToDo: other platforms: vscode, nteract, cocalc
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
shell = get_ipython().__class__.__name__
|
|
102
|
+
if shell == 'ZMQInteractiveShell':
|
|
103
|
+
return False # Jupyter notebook or qtconsole -> load JS librarty once per notebook
|
|
104
|
+
elif shell == 'TerminalInteractiveShell':
|
|
105
|
+
return True # Terminal running IPython -> an isolated HTML page to show somehow
|
|
106
|
+
else:
|
|
107
|
+
return True # Other type (?)
|
|
108
|
+
except NameError:
|
|
109
|
+
return True # some other env (even standard Python interpreter) -> an isolated HTML page to show somehow
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _is_IPython_display() -> bool:
|
|
113
|
+
try:
|
|
114
|
+
from IPython.display import display_html
|
|
115
|
+
return True
|
|
116
|
+
except ImportError:
|
|
117
|
+
return False
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2019. JetBrains s.r.o.
|
|
3
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
from typing import Dict, Tuple
|
|
7
|
+
|
|
8
|
+
from ._frontend_ctx import FrontendContext
|
|
9
|
+
from ._mime_types import LETS_PLOT_JSON
|
|
10
|
+
from .._type_utils import standardize_dict
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class IntellijPythonJsonContext(FrontendContext):
|
|
14
|
+
|
|
15
|
+
def configure(self, verbose: bool):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
def show(self, plot_spec: Dict) -> str:
|
|
19
|
+
plot_spec_std = standardize_dict(plot_spec)
|
|
20
|
+
data_object = DisplayDataObject(plot_spec_std)
|
|
21
|
+
|
|
22
|
+
# See intellij.python.helpers module in IDEA
|
|
23
|
+
from datalore.display import display
|
|
24
|
+
display(data_object)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class DisplayDataObject():
|
|
28
|
+
|
|
29
|
+
def __init__(self, plot_spec: Dict) -> None:
|
|
30
|
+
super().__init__()
|
|
31
|
+
self.data_object = (LETS_PLOT_JSON, plot_spec)
|
|
32
|
+
|
|
33
|
+
def _repr_display_(self) -> Tuple[str, Dict]:
|
|
34
|
+
"""
|
|
35
|
+
Special method discovered and invoked by datalore.display.display()
|
|
36
|
+
See: IDEA/community/python/helpers/pycharm_display/datalore/display/display_.py
|
|
37
|
+
"""
|
|
38
|
+
return self.data_object
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Copyright (c) 2020. JetBrains s.r.o.
|
|
2
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from ._frontend_ctx import FrontendContext
|
|
7
|
+
from ._intellij_python_json_ctx import IntellijPythonJsonContext
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _create_json_frontend_context() -> FrontendContext:
|
|
11
|
+
"""
|
|
12
|
+
Configures Lets-Plot JSON output.
|
|
13
|
+
|
|
14
|
+
Such context requires a Lets-Plot JSON interpreter plugged in to the frontend env (like PyCharm)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
if _is_Intellij_Python_Lets_Plot_Plugin():
|
|
18
|
+
return IntellijPythonJsonContext()
|
|
19
|
+
|
|
20
|
+
# ToDo: GenericJsonFrontendContext
|
|
21
|
+
raise RuntimeError("Couldn't detect Intellij Python environment")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _is_Intellij_Python_Lets_Plot_Plugin() -> bool:
|
|
25
|
+
try:
|
|
26
|
+
# An empty marker module defined by Intellij Lets-Plot plugin
|
|
27
|
+
# import lets_plot_intellij_python_plugin <---- the old way.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# The check above is not working with PyCharm remote interpreter: https://github.com/JetBrains/lets-plot/issues/348
|
|
31
|
+
|
|
32
|
+
# 1) The "datalore.display" is present in both PyCharm and Datalore env.
|
|
33
|
+
# See `intellij.python.helpers` module in IDEA.
|
|
34
|
+
from datalore.display import display
|
|
35
|
+
|
|
36
|
+
# 2) The "DATALORE_HOME" is only present in Datalore env but not in PyCharm env.
|
|
37
|
+
return "DATALORE_HOME" not in os.environ
|
|
38
|
+
except ImportError:
|
|
39
|
+
return False
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2019. JetBrains s.r.o.
|
|
3
|
+
# Use of this source code is governed by the MIT license that can be found in the LICENSE file.
|
|
4
|
+
#
|
|
5
|
+
import os
|
|
6
|
+
import pkgutil
|
|
7
|
+
import random
|
|
8
|
+
import string
|
|
9
|
+
from typing import Dict
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from IPython.display import display_html
|
|
13
|
+
except ImportError:
|
|
14
|
+
display_html = None
|
|
15
|
+
|
|
16
|
+
from ._frontend_ctx import FrontendContext
|
|
17
|
+
from .. import _kbridge as kbr
|
|
18
|
+
from .._global_settings import get_js_cdn_url
|
|
19
|
+
from .._global_settings import JS_BASE_URL, JS_NAME
|
|
20
|
+
from .._version import __version__
|
|
21
|
+
|
|
22
|
+
# Data-attributes used to store extra information about the meaning of 'script' elements
|
|
23
|
+
_ATT_SCRIPT_KIND = 'data-lets-plot-script'
|
|
24
|
+
_SCRIPT_KIND_LIB_LOADING = 'library'
|
|
25
|
+
_SCRIPT_KIND_PLOT = 'plot'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class JupyterNotebookContext(FrontendContext):
|
|
29
|
+
|
|
30
|
+
def __init__(self, offline: bool) -> None:
|
|
31
|
+
super().__init__()
|
|
32
|
+
self.connected = not offline
|
|
33
|
+
|
|
34
|
+
def configure(self, verbose: bool):
|
|
35
|
+
if self.connected:
|
|
36
|
+
# noinspection PyTypeChecker
|
|
37
|
+
display_html(self._configure_connected_script(verbose), raw=True)
|
|
38
|
+
else:
|
|
39
|
+
# noinspection PyTypeChecker
|
|
40
|
+
display_html(self._configure_embedded_script(verbose), raw=True)
|
|
41
|
+
|
|
42
|
+
def as_str(self, plot_spec: Dict) -> str:
|
|
43
|
+
return kbr._generate_dynamic_display_html(plot_spec)
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def _configure_connected_script(verbose: bool) -> str:
|
|
47
|
+
url = get_js_cdn_url()
|
|
48
|
+
output_id = JupyterNotebookContext._rand_string()
|
|
49
|
+
success_message = """
|
|
50
|
+
var div = document.createElement("div");
|
|
51
|
+
div.style.color = 'darkblue';
|
|
52
|
+
div.textContent = 'Lets-Plot JS successfully loaded.';
|
|
53
|
+
document.getElementById("{id}").appendChild(div);
|
|
54
|
+
""".format(id=output_id) if verbose else ""
|
|
55
|
+
|
|
56
|
+
return """
|
|
57
|
+
<div id="{id}"></div>
|
|
58
|
+
<script type="text/javascript" {data_attr}="{script_kind}">
|
|
59
|
+
if(!window.letsPlotCallQueue) {{
|
|
60
|
+
window.letsPlotCallQueue = [];
|
|
61
|
+
}};
|
|
62
|
+
window.letsPlotCall = function(f) {{
|
|
63
|
+
window.letsPlotCallQueue.push(f);
|
|
64
|
+
}};
|
|
65
|
+
(function() {{
|
|
66
|
+
var script = document.createElement("script");
|
|
67
|
+
script.type = "text/javascript";
|
|
68
|
+
script.src = "{url}";
|
|
69
|
+
script.onload = function() {{
|
|
70
|
+
window.letsPlotCall = function(f) {{f();}};
|
|
71
|
+
window.letsPlotCallQueue.forEach(function(f) {{f();}});
|
|
72
|
+
window.letsPlotCallQueue = [];
|
|
73
|
+
{success_message}
|
|
74
|
+
}};
|
|
75
|
+
script.onerror = function(event) {{
|
|
76
|
+
window.letsPlotCall = function(f) {{}}; // noop
|
|
77
|
+
window.letsPlotCallQueue = [];
|
|
78
|
+
var div = document.createElement("div");
|
|
79
|
+
div.style.color = 'darkred';
|
|
80
|
+
div.textContent = 'Error loading Lets-Plot JS';
|
|
81
|
+
document.getElementById("{id}").appendChild(div);
|
|
82
|
+
}};
|
|
83
|
+
var e = document.getElementById("{id}");
|
|
84
|
+
e.appendChild(script);
|
|
85
|
+
}})()
|
|
86
|
+
</script>
|
|
87
|
+
""".format(
|
|
88
|
+
data_attr=_ATT_SCRIPT_KIND,
|
|
89
|
+
script_kind=_SCRIPT_KIND_LIB_LOADING,
|
|
90
|
+
id=output_id,
|
|
91
|
+
url=url,
|
|
92
|
+
success_message=success_message)
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def _configure_embedded_script(verbose: bool) -> str:
|
|
96
|
+
js_name = "lets-plot.min.js"
|
|
97
|
+
path = os.path.join("package_data", js_name)
|
|
98
|
+
js_code = pkgutil.get_data("lets_plot", path).decode("utf-8")
|
|
99
|
+
success_message = '<div style="color:darkblue;">Lets-Plot JS is embedded.</div>' if verbose else ""
|
|
100
|
+
|
|
101
|
+
return """
|
|
102
|
+
<script type="text/javascript" {data_attr}="{script_kind}">
|
|
103
|
+
window.letsPlotCall = function(f) {{f();}};
|
|
104
|
+
console.log('Embedding: {js_name}');
|
|
105
|
+
{js_code}
|
|
106
|
+
</script>
|
|
107
|
+
{success_message}
|
|
108
|
+
""".format(
|
|
109
|
+
data_attr=_ATT_SCRIPT_KIND,
|
|
110
|
+
script_kind=_SCRIPT_KIND_LIB_LOADING,
|
|
111
|
+
js_code=js_code,
|
|
112
|
+
js_name=js_name,
|
|
113
|
+
success_message=success_message)
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def _rand_string(size=6) -> str:
|
|
117
|
+
alphabet = string.ascii_letters + string.digits
|
|
118
|
+
# noinspection PyShadowingBuiltins
|
|
119
|
+
return ''.join([random.choice(alphabet) for _ in range(size)])
|