jupyter-duckdb 1.2.101__py3-none-any.whl → 1.2.103__py3-none-any.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.
- duckdb_kernel/db/Connection.py +3 -0
- duckdb_kernel/db/implementation/duckdb/Connection.py +3 -0
- duckdb_kernel/db/implementation/postgres/Connection.py +4 -0
- duckdb_kernel/db/implementation/sqlite/Connection.py +3 -0
- duckdb_kernel/kernel.py +229 -190
- duckdb_kernel/magics/MagicCommandCallback.py +7 -14
- duckdb_kernel/magics/MagicCommandHandler.py +9 -9
- duckdb_kernel/magics/MagicState.py +11 -0
- duckdb_kernel/magics/__init__.py +1 -0
- duckdb_kernel/visualization/Plotly.py +8 -18
- duckdb_kernel/visualization/RATreeDrawer.py +34 -2
- duckdb_kernel/visualization/lib/__init__.py +53 -0
- duckdb_kernel/visualization/lib/plotly-3.0.1.min.js +3879 -0
- duckdb_kernel/visualization/lib/ra.css +3 -0
- duckdb_kernel/visualization/lib/ra.js +55 -0
- {jupyter_duckdb-1.2.101.dist-info → jupyter_duckdb-1.2.103.dist-info}/METADATA +13 -12
- {jupyter_duckdb-1.2.101.dist-info → jupyter_duckdb-1.2.103.dist-info}/RECORD +19 -15
- {jupyter_duckdb-1.2.101.dist-info → jupyter_duckdb-1.2.103.dist-info}/WHEEL +1 -1
- duckdb_kernel/magics/StringWrapper.py +0 -3
- {jupyter_duckdb-1.2.101.dist-info → jupyter_duckdb-1.2.103.dist-info}/top_level.txt +0 -0
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
from typing import Optional, List
|
|
2
2
|
|
|
3
|
-
from . import MagicCommand
|
|
4
|
-
from .StringWrapper import StringWrapper
|
|
3
|
+
from . import MagicCommand, MagicState
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
class MagicCommandCallback:
|
|
8
|
-
def __init__(self, mc: MagicCommand, silent: bool,
|
|
7
|
+
def __init__(self, mc: MagicCommand, silent: bool, state: MagicState, *args, **kwargs):
|
|
9
8
|
self._mc: MagicCommand = mc
|
|
10
9
|
self._silent: bool = silent
|
|
11
|
-
self.
|
|
10
|
+
self._state: MagicState = state
|
|
12
11
|
self._args = args
|
|
13
12
|
self._kwargs = kwargs
|
|
14
13
|
|
|
@@ -18,14 +17,8 @@ class MagicCommandCallback:
|
|
|
18
17
|
|
|
19
18
|
def __call__(self, columns: Optional[List[str]] = None, rows: Optional[List[List]] = None):
|
|
20
19
|
if self._mc.requires_code:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return result
|
|
26
|
-
|
|
27
|
-
if self._mc.requires_query_result:
|
|
28
|
-
return self._mc(self._silent, columns, rows, *self._args, **self._kwargs)
|
|
29
|
-
|
|
20
|
+
self._mc(self._silent, self._state, *self._args, **self._kwargs)
|
|
21
|
+
elif self._mc.requires_query_result:
|
|
22
|
+
self._mc(self._silent, self._state, columns, rows, *self._args, **self._kwargs)
|
|
30
23
|
else:
|
|
31
|
-
|
|
24
|
+
self._mc(self._silent, self._state, *self._args, **self._kwargs)
|
|
@@ -2,7 +2,8 @@ import re
|
|
|
2
2
|
from typing import Dict, Tuple, List
|
|
3
3
|
|
|
4
4
|
from . import MagicCommand, MagicCommandException, MagicCommandCallback
|
|
5
|
-
from .
|
|
5
|
+
from .MagicState import MagicState
|
|
6
|
+
from ..db import Connection
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class MagicCommandHandler:
|
|
@@ -18,8 +19,8 @@ class MagicCommandHandler:
|
|
|
18
19
|
def __getitem__(self, key: str) -> MagicCommand:
|
|
19
20
|
return self._magics[key.lower()]
|
|
20
21
|
|
|
21
|
-
def __call__(self, silent: bool,
|
|
22
|
-
|
|
22
|
+
def __call__(self, silent: bool, state: MagicState) \
|
|
23
|
+
-> Tuple[List[MagicCommandCallback], List[MagicCommandCallback]]:
|
|
23
24
|
enabled_callbacks: List[MagicCommandCallback] = []
|
|
24
25
|
|
|
25
26
|
# enable commands with default==True
|
|
@@ -27,21 +28,21 @@ class MagicCommandHandler:
|
|
|
27
28
|
if magic.is_default:
|
|
28
29
|
flags = {name: False for name, _ in magic.flags}
|
|
29
30
|
optionals = {name: default for name, default, _ in magic.optionals}
|
|
30
|
-
callback = MagicCommandCallback(magic, silent,
|
|
31
|
+
callback = MagicCommandCallback(magic, silent, state, **flags, **optionals)
|
|
31
32
|
|
|
32
33
|
enabled_callbacks.append(callback)
|
|
33
34
|
|
|
34
35
|
# search for magic commands in code
|
|
35
36
|
while True:
|
|
36
37
|
# ensure code starts with '%' or '%%' but not with '%%%'
|
|
37
|
-
match = re.match(r'^%{1,2}([^% ]+?)([ \t]*$| .+?$)', code, re.MULTILINE | re.IGNORECASE)
|
|
38
|
+
match = re.match(r'^%{1,2}([^% ]+?)([ \t]*$| .+?$)', state.code, re.MULTILINE | re.IGNORECASE)
|
|
38
39
|
|
|
39
40
|
if match is None:
|
|
40
41
|
break
|
|
41
42
|
|
|
42
43
|
# remove magic command from code
|
|
43
44
|
start, end = match.span()
|
|
44
|
-
code = code[:start] + code[end + 1:]
|
|
45
|
+
state.code = state.code[:start] + state.code[end + 1:]
|
|
45
46
|
|
|
46
47
|
# extract command
|
|
47
48
|
command = match.group(1).lower()
|
|
@@ -99,7 +100,7 @@ class MagicCommandHandler:
|
|
|
99
100
|
optionals[name.lower()] = value
|
|
100
101
|
|
|
101
102
|
# add to callbacks
|
|
102
|
-
callback = MagicCommandCallback(magic, silent,
|
|
103
|
+
callback = MagicCommandCallback(magic, silent, state, *args, **flags, **optionals)
|
|
103
104
|
enabled_callbacks.append(callback)
|
|
104
105
|
|
|
105
106
|
# disable overwritten callbacks
|
|
@@ -129,5 +130,4 @@ class MagicCommandHandler:
|
|
|
129
130
|
post_query_callbacks.append(callback)
|
|
130
131
|
|
|
131
132
|
# return callbacks
|
|
132
|
-
|
|
133
|
-
return code, pre_query_callbacks, post_query_callbacks
|
|
133
|
+
return pre_query_callbacks, post_query_callbacks
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from typing import Union, Dict, Optional
|
|
2
|
+
|
|
3
|
+
from ..db import Connection
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MagicState:
|
|
7
|
+
def __init__(self, db: Connection, code: str, max_rows: Optional[int]):
|
|
8
|
+
self.db: Connection = db
|
|
9
|
+
self.code: Union[str, Dict] = code
|
|
10
|
+
self.max_rows: Optional[int] = max_rows
|
|
11
|
+
self.column_name_mapping: Dict[str, str] = {}
|
duckdb_kernel/magics/__init__.py
CHANGED
|
@@ -1,22 +1,9 @@
|
|
|
1
|
-
from decimal import Decimal
|
|
2
|
-
|
|
3
|
-
PLOTLY_VERSION = '3.0.1'
|
|
4
|
-
|
|
5
1
|
import json
|
|
6
|
-
from
|
|
2
|
+
from decimal import Decimal
|
|
7
3
|
from typing import Dict, List, Optional
|
|
4
|
+
from uuid import uuid4
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def __init() -> str:
|
|
13
|
-
global __PLOTLY_INITIALIZED
|
|
14
|
-
|
|
15
|
-
if not __PLOTLY_INITIALIZED:
|
|
16
|
-
__PLOTLY_INITIALIZED = True
|
|
17
|
-
return f'<script src="https://cdn.plot.ly/plotly-{PLOTLY_VERSION}.min.js"></script>'
|
|
18
|
-
else:
|
|
19
|
-
return ''
|
|
6
|
+
from .lib import init_plotly
|
|
20
7
|
|
|
21
8
|
|
|
22
9
|
def __div_id() -> str:
|
|
@@ -60,7 +47,7 @@ def __fix_decimal(x: List):
|
|
|
60
47
|
|
|
61
48
|
|
|
62
49
|
def draw_chart(title: Optional[str], traces: List[Dict] | Dict) -> str:
|
|
63
|
-
init =
|
|
50
|
+
init = init_plotly()
|
|
64
51
|
div_id = __div_id()
|
|
65
52
|
layout = __layout(title)
|
|
66
53
|
config = __config()
|
|
@@ -69,7 +56,10 @@ def draw_chart(title: Optional[str], traces: List[Dict] | Dict) -> str:
|
|
|
69
56
|
traces = json.dumps(traces)
|
|
70
57
|
|
|
71
58
|
return f'''
|
|
72
|
-
|
|
59
|
+
<script type="text/javascript">
|
|
60
|
+
{init}
|
|
61
|
+
</script>
|
|
62
|
+
|
|
73
63
|
<div id="{div_id}"></div>
|
|
74
64
|
<script type="text/javascript">
|
|
75
65
|
Plotly.newPlot('{div_id}', {traces}, {json.dumps(layout)}, {json.dumps(config)});
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
from typing import Dict
|
|
1
|
+
from typing import Dict, Optional
|
|
2
2
|
|
|
3
3
|
from graphviz import Digraph
|
|
4
|
+
from uuid import uuid4
|
|
4
5
|
|
|
5
6
|
from duckdb_kernel.db import Table
|
|
6
7
|
from duckdb_kernel.parser.elements import RAElement
|
|
7
8
|
from duckdb_kernel.util.formatting import row_count
|
|
8
9
|
from .Drawer import Drawer
|
|
9
10
|
from ..db import Connection
|
|
11
|
+
from .lib import *
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
class RATreeDrawer(Drawer):
|
|
@@ -15,6 +17,9 @@ class RATreeDrawer(Drawer):
|
|
|
15
17
|
self.root_node: RAElement = root_node
|
|
16
18
|
self.tables: Dict[str, Table] = tables
|
|
17
19
|
|
|
20
|
+
self.nodes: Dict[str, RAElement] = {}
|
|
21
|
+
self.root_node_id: Optional[str] = None
|
|
22
|
+
|
|
18
23
|
def to_graph(self) -> Digraph:
|
|
19
24
|
# create graph
|
|
20
25
|
ps = Digraph('Schema',
|
|
@@ -31,7 +36,11 @@ class RATreeDrawer(Drawer):
|
|
|
31
36
|
|
|
32
37
|
def __add_node(self, ps: Digraph, node: RAElement) -> str:
|
|
33
38
|
# use id of node object as identifier
|
|
34
|
-
node_id = f'node_{
|
|
39
|
+
node_id = f'node_{str(uuid4()).replace("-", "_")}'
|
|
40
|
+
|
|
41
|
+
self.nodes[node_id] = node
|
|
42
|
+
if node == self.root_node:
|
|
43
|
+
self.root_node_id = node_id
|
|
35
44
|
|
|
36
45
|
# generate child nodes
|
|
37
46
|
child_ids = [self.__add_node(ps, child) for child in node.children]
|
|
@@ -69,3 +78,26 @@ class RATreeDrawer(Drawer):
|
|
|
69
78
|
|
|
70
79
|
# return node identifier to generate edges
|
|
71
80
|
return node_id
|
|
81
|
+
|
|
82
|
+
def to_interactive_svg(self) -> str:
|
|
83
|
+
div_id = f'div-{str(uuid4())}'
|
|
84
|
+
|
|
85
|
+
css = init_css()
|
|
86
|
+
ra = init_ra()
|
|
87
|
+
svg = self.to_svg(True)
|
|
88
|
+
|
|
89
|
+
return f'''
|
|
90
|
+
<style type="text/css">
|
|
91
|
+
{css}
|
|
92
|
+
</style>
|
|
93
|
+
|
|
94
|
+
<div id="{div_id}">
|
|
95
|
+
{svg}
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<script type="text/javascript">
|
|
99
|
+
{ra}
|
|
100
|
+
|
|
101
|
+
animate_ra('{div_id}', '{self.root_node_id}')
|
|
102
|
+
</script>
|
|
103
|
+
'''
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
__CSS_INITIALIZED = False
|
|
4
|
+
__RA_INITIALIZED = False
|
|
5
|
+
__PLOTLY_INITIALIZED = False
|
|
6
|
+
|
|
7
|
+
__location = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def init_css() -> str:
|
|
11
|
+
global __CSS_INITIALIZED
|
|
12
|
+
|
|
13
|
+
if not __CSS_INITIALIZED:
|
|
14
|
+
with open(os.path.join(__location, 'ra.css')) as ra_file:
|
|
15
|
+
css = ra_file.read()
|
|
16
|
+
else:
|
|
17
|
+
css = ''
|
|
18
|
+
|
|
19
|
+
__CSS_INITIALIZED = True
|
|
20
|
+
return css
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def init_ra() -> str:
|
|
24
|
+
global __RA_INITIALIZED
|
|
25
|
+
|
|
26
|
+
if not __RA_INITIALIZED:
|
|
27
|
+
with open(os.path.join(__location, 'ra.js')) as ra_file:
|
|
28
|
+
ra = ra_file.read()
|
|
29
|
+
else:
|
|
30
|
+
ra = ''
|
|
31
|
+
|
|
32
|
+
__RA_INITIALIZED = True
|
|
33
|
+
return ra
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def init_plotly() -> str:
|
|
37
|
+
global __PLOTLY_INITIALIZED
|
|
38
|
+
|
|
39
|
+
if not __PLOTLY_INITIALIZED:
|
|
40
|
+
with open(os.path.join(__location, 'plotly-3.0.1.min.js')) as plotly_file:
|
|
41
|
+
plotly = plotly_file.read()
|
|
42
|
+
else:
|
|
43
|
+
plotly = ''
|
|
44
|
+
|
|
45
|
+
__PLOTLY_INITIALIZED = True
|
|
46
|
+
return plotly
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
__all__ = [
|
|
50
|
+
'init_css',
|
|
51
|
+
'init_ra',
|
|
52
|
+
'init_plotly',
|
|
53
|
+
]
|