nodpy 0.1.3__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.
- nodpy/__init__.py +451 -0
- nodpy/_version.py +4 -0
- nodpy/ast_tools.py +176 -0
- nodpy/datastore.py +21 -0
- nodpy/embed_kernel.py +305 -0
- nodpy/file_helpers.py +246 -0
- nodpy/hatch_build.py +52 -0
- nodpy/ip_plugin.py +72 -0
- nodpy/labextension/package.json +232 -0
- nodpy/labextension/schemas/nodjs/package.json.orig +227 -0
- nodpy/labextension/schemas/nodjs/plugin.json +46 -0
- nodpy/labextension/static/298.1fa66e2cd2088e804334.js +1 -0
- nodpy/labextension/static/728.12765e602400f37a118b.js +1 -0
- nodpy/labextension/static/781.8294abe512b48b00402a.js +1 -0
- nodpy/labextension/static/835.4539cede11218d7fe226.js +1 -0
- nodpy/labextension/static/remoteEntry.f340994a4ea855dbfc8a.js +1 -0
- nodpy/labextension/static/style.js +4 -0
- nodpy/labextension/static/third-party-licenses.json +64 -0
- nodpy/nod_cli.py +184 -0
- nodpy/provisioner.py +461 -0
- nodpy/serverExtension.py +202 -0
- nodpy/tests/__init__.py +1 -0
- nodpy/tracing.py +28 -0
- nodpy-0.1.3.data/data/etc/jupyter/jupyter_server_config.d/nodpy.json +7 -0
- nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/install.json +5 -0
- nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/package.json +232 -0
- nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/schemas/nodjs/package.json.orig +227 -0
- nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/schemas/nodjs/plugin.json +46 -0
- nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/static/298.1fa66e2cd2088e804334.js +1 -0
- nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/static/728.12765e602400f37a118b.js +1 -0
- nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/static/781.8294abe512b48b00402a.js +1 -0
- nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/static/835.4539cede11218d7fe226.js +1 -0
- nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/static/remoteEntry.f340994a4ea855dbfc8a.js +1 -0
- nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/static/style.js +4 -0
- nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/static/third-party-licenses.json +64 -0
- nodpy-0.1.3.dist-info/METADATA +98 -0
- nodpy-0.1.3.dist-info/RECORD +40 -0
- nodpy-0.1.3.dist-info/WHEEL +4 -0
- nodpy-0.1.3.dist-info/entry_points.txt +5 -0
- nodpy-0.1.3.dist-info/licenses/LICENSE +29 -0
nodpy/__init__.py
ADDED
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
try:
|
|
2
|
+
from ._version import __version__
|
|
3
|
+
except ImportError:
|
|
4
|
+
# Fallback when using the package in dev mode without installing
|
|
5
|
+
# in editable mode with pip. It is highly recommended to install
|
|
6
|
+
# the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
|
|
7
|
+
import warnings
|
|
8
|
+
|
|
9
|
+
warnings.warn("Importing 'nodpy' outside a proper installation.")
|
|
10
|
+
__version__ = "dev"
|
|
11
|
+
import atexit
|
|
12
|
+
import base64
|
|
13
|
+
import inspect
|
|
14
|
+
from pprint import pprint
|
|
15
|
+
import shutil
|
|
16
|
+
import types
|
|
17
|
+
|
|
18
|
+
import jupytext # type: ignore
|
|
19
|
+
from nbformat import NotebookNode
|
|
20
|
+
import nbformat
|
|
21
|
+
import orjson
|
|
22
|
+
import shlex
|
|
23
|
+
import sys
|
|
24
|
+
import copy
|
|
25
|
+
import signal
|
|
26
|
+
import re
|
|
27
|
+
import tempfile
|
|
28
|
+
import typing
|
|
29
|
+
import ipykernel
|
|
30
|
+
|
|
31
|
+
# from jupyter_client import KernelProvisionerBase
|
|
32
|
+
import os
|
|
33
|
+
import logging
|
|
34
|
+
import json
|
|
35
|
+
import os
|
|
36
|
+
import subprocess
|
|
37
|
+
from traitlets.config import Config
|
|
38
|
+
import uuid
|
|
39
|
+
from pathlib import Path
|
|
40
|
+
import libcst as cst
|
|
41
|
+
from libcst.display import dump
|
|
42
|
+
from IPython.core.error import UsageError
|
|
43
|
+
from nodpy.ip_plugin import returnTransformer
|
|
44
|
+
from .serverExtension import Nod
|
|
45
|
+
from libcst.metadata import CodePosition, CodeRange
|
|
46
|
+
from libcst.metadata import PositionProvider, ParentNodeProvider
|
|
47
|
+
from IPython.core.interactiveshell import InteractiveShell
|
|
48
|
+
from dataclasses import dataclass
|
|
49
|
+
from typing import List, Literal, TYPE_CHECKING, IO, Any, cast
|
|
50
|
+
from IPython.core.getipython import get_ipython
|
|
51
|
+
from ipykernel.connect import get_connection_info
|
|
52
|
+
from .embed_kernel import embed_kernel
|
|
53
|
+
from .ast_tools import FunctionFinder, NodFinder
|
|
54
|
+
from .file_helpers import PathManager, ProgramInfo, makeProgramInfo
|
|
55
|
+
from .datastore import LogStore
|
|
56
|
+
from inspect import FrameInfo, Traceback
|
|
57
|
+
from types import FrameType, TracebackType
|
|
58
|
+
from jupytext.formats import long_form_one_format # type: ignore
|
|
59
|
+
from IPython.terminal.interactiveshell import TerminalInteractiveShell
|
|
60
|
+
from .provisioner import NodProvisioner
|
|
61
|
+
from enum import Enum, IntEnum
|
|
62
|
+
from .ip_plugin import nodReturn
|
|
63
|
+
from IPython.core.interactiveshell import ExecutionResult
|
|
64
|
+
import traceback
|
|
65
|
+
|
|
66
|
+
if TYPE_CHECKING:
|
|
67
|
+
# False at run time, only for type checker
|
|
68
|
+
from _typeshed import SupportsWrite
|
|
69
|
+
|
|
70
|
+
_log = logging.getLogger(__name__)
|
|
71
|
+
logging.basicConfig()
|
|
72
|
+
_log.setLevel(logging.INFO)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
DRY_RUN = False
|
|
76
|
+
|
|
77
|
+
DEBUG: bool = True
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass
|
|
81
|
+
class FrameIdentifiers:
|
|
82
|
+
function: str
|
|
83
|
+
lineno: int
|
|
84
|
+
filename: str
|
|
85
|
+
|
|
86
|
+
def get_id(self) -> str:
|
|
87
|
+
return self.function + str(self.lineno) + self.filename
|
|
88
|
+
|
|
89
|
+
def __init__(self, frame_info: FrameInfo | Traceback):
|
|
90
|
+
self.function = frame_info.function
|
|
91
|
+
self.lineno = frame_info.lineno
|
|
92
|
+
self.filename = frame_info.filename
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def compare_identifiers(
|
|
96
|
+
frame_id: FrameInfo | Traceback | FrameIdentifiers,
|
|
97
|
+
frame_info: FrameInfo | Traceback | FrameIdentifiers,
|
|
98
|
+
) -> bool:
|
|
99
|
+
return (
|
|
100
|
+
frame_info.function == frame_id.function
|
|
101
|
+
and frame_info.lineno == frame_id.lineno
|
|
102
|
+
and frame_info.filename == frame_id.filename
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# def loadState(frame_identifier:FrameIdentifiers, state:dict[dict]):
|
|
107
|
+
# frame_id = frame_identifier.get_id()
|
|
108
|
+
# shell: InteractiveShell = get_ipython()
|
|
109
|
+
# if frame_id in state.keys():
|
|
110
|
+
# shell.push(state[frame_id])
|
|
111
|
+
|
|
112
|
+
# def saveState(frame_identifier:FrameIdentifiers, state:dict[dict]):
|
|
113
|
+
# frame_id = frame_identifier.get_id()
|
|
114
|
+
# shell: InteractiveShell = get_ipython()
|
|
115
|
+
# if frame_id in state.keys():
|
|
116
|
+
# shell.
|
|
117
|
+
|
|
118
|
+
# def clearState():
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# def resetState():
|
|
122
|
+
# """Clear all internal namespaces, and attempt to release references to
|
|
123
|
+
# user objects.
|
|
124
|
+
|
|
125
|
+
# If new_session is True, a new history session will be opened.
|
|
126
|
+
# """
|
|
127
|
+
# shell = get_ipython()
|
|
128
|
+
# shell.run_line_magic("reset", "-f -s")
|
|
129
|
+
# if shell.user_ns.get("__STARTINGVARIABLES", False):
|
|
130
|
+
# shell.push(shell.user_ns["__STARTINGVARIABLES"])
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# def log(*args, **kwargs):
|
|
134
|
+
# logStore = LogStore()
|
|
135
|
+
# logStore.logs.append()
|
|
136
|
+
|
|
137
|
+
# logStore.logs.append(dict(val))
|
|
138
|
+
# vars = locals() + globals()
|
|
139
|
+
|
|
140
|
+
# [k for k, v in locals.items() if v in args][0]
|
|
141
|
+
|
|
142
|
+
# for key, val in kwargs.items():
|
|
143
|
+
# logStore.logs.append(dict(key=val))
|
|
144
|
+
# print(key, val)
|
|
145
|
+
# def nodConfig():
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def nodPrint(
|
|
149
|
+
*values: object,
|
|
150
|
+
sep: str | None = " ",
|
|
151
|
+
end: str | None = "\n",
|
|
152
|
+
file: SupportsWrite[str] | None = None,
|
|
153
|
+
flush: Literal[False] = False,
|
|
154
|
+
):
|
|
155
|
+
"""Inside of an IPython Instance, prints the values to a stream, or to sys.stdout by default.
|
|
156
|
+
|
|
157
|
+
sep
|
|
158
|
+
string inserted between values, default a space.
|
|
159
|
+
end
|
|
160
|
+
string appended after the last value, default a newline.
|
|
161
|
+
file
|
|
162
|
+
a file-like object (stream); defaults to the current sys.stdout.
|
|
163
|
+
flush
|
|
164
|
+
whether to forcibly flush the stream.
|
|
165
|
+
"""
|
|
166
|
+
# Prevent Nested Nod Instances
|
|
167
|
+
try:
|
|
168
|
+
name = get_ipython().__class__.__name__
|
|
169
|
+
if name != "NoneType":
|
|
170
|
+
print(
|
|
171
|
+
*values,
|
|
172
|
+
sep=sep,
|
|
173
|
+
end=end,
|
|
174
|
+
file=file,
|
|
175
|
+
flush=flush,
|
|
176
|
+
)
|
|
177
|
+
except NameError:
|
|
178
|
+
pass
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# class Signals(IntEnum):
|
|
182
|
+
# SIGINT: int
|
|
183
|
+
# SIGKILL: int
|
|
184
|
+
# SIGTERM: int
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# _SIGNUM = typing.Union[int, Signals]
|
|
188
|
+
_fmt: typing.Literal["light", "percent"] = "light"
|
|
189
|
+
_modules: list[str] = [os.getcwd() + "/*"]
|
|
190
|
+
_how_restart: typing.Union[typing.Literal["continue"], int] = "continue"
|
|
191
|
+
_dangerously_bypass_readonly: bool = False
|
|
192
|
+
|
|
193
|
+
class NodException(Exception):
|
|
194
|
+
"""Exception raised for custom error in the application."""
|
|
195
|
+
|
|
196
|
+
def __init__(self, message):
|
|
197
|
+
super().__init__(message)
|
|
198
|
+
self.message = message
|
|
199
|
+
|
|
200
|
+
def __str__(self):
|
|
201
|
+
return f"{self.message})"
|
|
202
|
+
|
|
203
|
+
def nodConfig(
|
|
204
|
+
fmt: typing.Literal["light", "percent"] = "light",
|
|
205
|
+
modules: list[str] = [],
|
|
206
|
+
how_restart: typing.Union[typing.Literal["continue"], int] = "continue",
|
|
207
|
+
dangerously_bypass_readonly: bool = False,
|
|
208
|
+
):
|
|
209
|
+
"""Configure Nod Settings
|
|
210
|
+
modules:
|
|
211
|
+
list of modules (as strings) to include in the trace. __main__ included by default, also accepts *, ?, and [] as wildcards
|
|
212
|
+
|
|
213
|
+
fmt:
|
|
214
|
+
notebook conversion format.
|
|
215
|
+
|
|
216
|
+
how_restart
|
|
217
|
+
how the python program should be restarted from the notebook.
|
|
218
|
+
"continue" returns to let the program finish
|
|
219
|
+
SIGINT, SIGKILL, or SIGTERM will send that signal to the program instead
|
|
220
|
+
|
|
221
|
+
dangerously_bypass_readonly
|
|
222
|
+
Once the code in associated with one stack frame in a Nod Session is edited, the others become read-only by default to prevent reaching a confusing state. Set to true to remove this safeguard, if you know what you're doing.
|
|
223
|
+
"""
|
|
224
|
+
global _fmt
|
|
225
|
+
_fmt = fmt
|
|
226
|
+
global _modules
|
|
227
|
+
_modules = modules
|
|
228
|
+
global _how_restart
|
|
229
|
+
_how_restart = how_restart
|
|
230
|
+
global _dangerously_bypass_readonly
|
|
231
|
+
_dangerously_bypass_readonly = dangerously_bypass_readonly
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def notebook(
|
|
235
|
+
modules: list[str] = [],
|
|
236
|
+
# indent: int = 1,
|
|
237
|
+
# on_condition: bool = True,
|
|
238
|
+
# deep_copy: bool = False,
|
|
239
|
+
):
|
|
240
|
+
"""Invoke a Jupyter Notebook at this location in the source code, with code in the same indent block being editable.
|
|
241
|
+
modules:
|
|
242
|
+
list of modules (as strings) to include in the trace. __main__ included by default.
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
"""
|
|
246
|
+
# on_condition
|
|
247
|
+
# if true, invoke the notebook, else no-op.
|
|
248
|
+
|
|
249
|
+
# deep_copy
|
|
250
|
+
# if true, deep copy the variables to put into the notebook. By default, in-place modifications to existing variables are persisted through kernel restarts. Enabling can lead to performance issues with large variables in memory. Some variables cannot be deep-copied, and will throw a warning on execution.
|
|
251
|
+
|
|
252
|
+
# indent
|
|
253
|
+
# number of indent blocks to make editable
|
|
254
|
+
# if not on_condition:
|
|
255
|
+
# return
|
|
256
|
+
|
|
257
|
+
# Prevent Nested Nod Instances
|
|
258
|
+
try:
|
|
259
|
+
name = get_ipython().__class__.__name__
|
|
260
|
+
if name != "NoneType":
|
|
261
|
+
return
|
|
262
|
+
except NameError:
|
|
263
|
+
pass
|
|
264
|
+
|
|
265
|
+
runtime_dir = os.environ.get("NOD_RUNTIME_DIR", "")
|
|
266
|
+
_log.info(f"NOD_RUNTIME_DIR: {runtime_dir}")
|
|
267
|
+
stack = inspect.stack()
|
|
268
|
+
|
|
269
|
+
def find_notebook_func(frame: inspect.FrameInfo):
|
|
270
|
+
if frame.code_context is None:
|
|
271
|
+
# raise RuntimeError
|
|
272
|
+
return False
|
|
273
|
+
|
|
274
|
+
for line in frame.code_context:
|
|
275
|
+
if line.find("notebook(") > 0: # TODO replace with regex
|
|
276
|
+
return True
|
|
277
|
+
return False
|
|
278
|
+
|
|
279
|
+
notebook_call = next((frame for frame in stack if find_notebook_func(frame)), None)
|
|
280
|
+
|
|
281
|
+
if notebook_call is None:
|
|
282
|
+
raise NodException("Cannot find notebook() function call in callstack")
|
|
283
|
+
|
|
284
|
+
notebook_call_index = stack.index(notebook_call)
|
|
285
|
+
if notebook_call_index + 1 > len(stack):
|
|
286
|
+
raise IndexError
|
|
287
|
+
notebook_parent_frame = stack[notebook_call_index + 1]
|
|
288
|
+
frozenPattern = re.compile("<frozen .*>")
|
|
289
|
+
relevant_stack_frames = [
|
|
290
|
+
frame
|
|
291
|
+
for frame in stack[notebook_call_index:]
|
|
292
|
+
if frozenPattern.match(frame.filename) is None
|
|
293
|
+
]
|
|
294
|
+
# _log.info(stack[notebook_call_index:])
|
|
295
|
+
# _log.info(relevant_stack_frames)
|
|
296
|
+
|
|
297
|
+
## FILE ORGANIZATION
|
|
298
|
+
pm = PathManager()
|
|
299
|
+
|
|
300
|
+
module_sources: dict[str, cst.Module] = {}
|
|
301
|
+
for stackFrame in relevant_stack_frames:
|
|
302
|
+
if module_sources.get(stackFrame.filename) is None:
|
|
303
|
+
try:
|
|
304
|
+
if os.path.isfile(stackFrame.filename):
|
|
305
|
+
program_text = open(stackFrame.filename).read()
|
|
306
|
+
module_sources.update(
|
|
307
|
+
{stackFrame.filename: cst.parse_module(program_text)}
|
|
308
|
+
)
|
|
309
|
+
else:
|
|
310
|
+
_log.info(stackFrame)
|
|
311
|
+
except:
|
|
312
|
+
_log.info(f"Couldn't find source for {stackFrame.filename}")
|
|
313
|
+
pass
|
|
314
|
+
|
|
315
|
+
stack_info = [
|
|
316
|
+
makeProgramInfo(
|
|
317
|
+
stackFrame,
|
|
318
|
+
index,
|
|
319
|
+
module_sources.get(stackFrame.filename, None),
|
|
320
|
+
pm,
|
|
321
|
+
_fmt,
|
|
322
|
+
)
|
|
323
|
+
for index, stackFrame in enumerate(relevant_stack_frames)
|
|
324
|
+
]
|
|
325
|
+
if modules == []:
|
|
326
|
+
module_filters = _modules
|
|
327
|
+
else:
|
|
328
|
+
module_filters = modules
|
|
329
|
+
|
|
330
|
+
jsonInfo = orjson.dumps(
|
|
331
|
+
{
|
|
332
|
+
"stack_info": stack_info,
|
|
333
|
+
"module_filters": module_filters,
|
|
334
|
+
"fmt": _fmt,
|
|
335
|
+
"how_restart": _how_restart,
|
|
336
|
+
"dangerously_bypass_readonly": _dangerously_bypass_readonly,
|
|
337
|
+
}
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
c = Config()
|
|
341
|
+
# so they get added to user namespace
|
|
342
|
+
# c.InteractiveShellApp.exec_lines = [
|
|
343
|
+
# "get_ipython().push(__STARTINGVARIABLES)",
|
|
344
|
+
# ]
|
|
345
|
+
# c.InteractiveShellApp.hide_initial_ns = False
|
|
346
|
+
# c.HistoryManager.hist_file = ":memory:"
|
|
347
|
+
|
|
348
|
+
startingVariables = {}
|
|
349
|
+
startingVariables.update(notebook_call.frame.f_globals)
|
|
350
|
+
startingVariables.update(notebook_call.frame.f_locals)
|
|
351
|
+
startingVariables.update({"nodReturn": nodReturn})
|
|
352
|
+
|
|
353
|
+
app = embed_kernel(
|
|
354
|
+
local_ns=startingVariables,
|
|
355
|
+
config=c,
|
|
356
|
+
no_stdout=False,
|
|
357
|
+
no_stderr=False,
|
|
358
|
+
quiet=False, # (not DEBUG)
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
shell = cast(TerminalInteractiveShell, app.shell)
|
|
362
|
+
shell.ast_transformers.append(returnTransformer())
|
|
363
|
+
old_traceback = shell.showtraceback
|
|
364
|
+
|
|
365
|
+
# To handle returns gracefully, we need to intercept the tracebacks so users don't see the error
|
|
366
|
+
def showtraceback(
|
|
367
|
+
self,
|
|
368
|
+
exc_tuple: tuple[type[BaseException], BaseException, Any] | None = None,
|
|
369
|
+
filename: str | None = None,
|
|
370
|
+
tb_offset: int | None = None,
|
|
371
|
+
exception_only: bool = False,
|
|
372
|
+
running_compiled_code: bool = False,
|
|
373
|
+
) -> None:
|
|
374
|
+
"""Display the exception that just occurred.
|
|
375
|
+
|
|
376
|
+
If nothing is known about the exception, this is the method which
|
|
377
|
+
should be used throughout the code for presenting user tracebacks,
|
|
378
|
+
rather than directly invoking the InteractiveTB object.
|
|
379
|
+
|
|
380
|
+
A specific showsyntaxerror() also exists, but this method can take
|
|
381
|
+
care of calling it if needed, so unless you are explicitly catching a
|
|
382
|
+
SyntaxError exception, don't try to analyze the stack manually and
|
|
383
|
+
simply call this method."""
|
|
384
|
+
|
|
385
|
+
try:
|
|
386
|
+
etype, value, tb = self._get_exc_info(exc_tuple)
|
|
387
|
+
if str(type(value).__name__) == "NodStopExecution":
|
|
388
|
+
return
|
|
389
|
+
except:
|
|
390
|
+
pass
|
|
391
|
+
|
|
392
|
+
old_traceback(
|
|
393
|
+
exc_tuple, filename, tb_offset, exception_only, running_compiled_code
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
shell.showtraceback = types.MethodType(showtraceback, shell)
|
|
397
|
+
|
|
398
|
+
app.kernel.relevant_stack_frames = relevant_stack_frames
|
|
399
|
+
|
|
400
|
+
## COPY CONNECTION FILE, ADD KERNEL NAME
|
|
401
|
+
connection_file = app.abs_connection_file
|
|
402
|
+
_log.info("Connection File Path: " + str(app.abs_connection_file))
|
|
403
|
+
|
|
404
|
+
info = None
|
|
405
|
+
with open(connection_file) as f:
|
|
406
|
+
info_str = f.read()
|
|
407
|
+
info = orjson.loads(info_str)
|
|
408
|
+
info["kernel_name"] = "nod"
|
|
409
|
+
info["display_name"] = "nod"
|
|
410
|
+
# info["language"] = "python"
|
|
411
|
+
# info["metadata"] = {
|
|
412
|
+
# "kernel_provisioner": {"provisioner_name": "nod-provisioner"}
|
|
413
|
+
# }
|
|
414
|
+
# info["metadata"] = {"kernel_provisioner": {"config": {}}}
|
|
415
|
+
_log.info("Connection File: " + str(info))
|
|
416
|
+
|
|
417
|
+
with open(connection_file, "w") as f:
|
|
418
|
+
f.write(orjson.dumps(info).decode("utf-8"))
|
|
419
|
+
|
|
420
|
+
nod_connection_file = os.path.join(pm.connection_dir, Path(connection_file).name)
|
|
421
|
+
shutil.copy(connection_file, nod_connection_file)
|
|
422
|
+
|
|
423
|
+
with open(os.path.join(pm.connection_dir, "nodInfo.json"), "x") as f:
|
|
424
|
+
f.write(jsonInfo.decode("utf-8"))
|
|
425
|
+
|
|
426
|
+
if not DRY_RUN:
|
|
427
|
+
# atexit.register(close_notebook)
|
|
428
|
+
app.start()
|
|
429
|
+
app.reset_io()
|
|
430
|
+
# if _how_restart == 'continue':
|
|
431
|
+
# newStackFrame = relevant_stack_frames[0]
|
|
432
|
+
# if app.shell is not None:
|
|
433
|
+
# reset(app.shell, True, True)
|
|
434
|
+
# app.shell.user_ns.update(newStackFrame.frame.f_locals)
|
|
435
|
+
# app.shell.user_global_ns.update(newStackFrame.frame.f_globals)
|
|
436
|
+
# app.shell.user_ns_hidden.update(newStackFrame.frame.f_builtins)
|
|
437
|
+
# TODO nonlocal promote
|
|
438
|
+
# switch back to first frame
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def _jupyter_labextension_paths():
|
|
442
|
+
return [{"src": "labextension", "dest": "nodjs"}]
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _jupyter_server_extension_points():
|
|
446
|
+
return [
|
|
447
|
+
{
|
|
448
|
+
"module": "nodpy",
|
|
449
|
+
"app": Nod,
|
|
450
|
+
}
|
|
451
|
+
]
|
nodpy/_version.py
ADDED
nodpy/ast_tools.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
from inspect import FrameInfo
|
|
3
|
+
import logging
|
|
4
|
+
from typing import List, Optional, Sequence, Union
|
|
5
|
+
import libcst as cst
|
|
6
|
+
from libcst.display import dump
|
|
7
|
+
import libcst.matchers as m
|
|
8
|
+
from libcst.metadata import PositionProvider, ParentNodeProvider
|
|
9
|
+
from libcst.metadata import CodePosition, CodeRange
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# def getCSTInfo(sourceProgram: str, frameInfo: FrameInfo):
|
|
13
|
+
# wrapper = cst.MetadataWrapper(cst.parse_module(sourceProgram))
|
|
14
|
+
|
|
15
|
+
# finder = NodFinder(frameInfo.lineno)
|
|
16
|
+
# metaAST = wrapper.visit(finder)
|
|
17
|
+
# func_body: CodeRange = finder.body_indent
|
|
18
|
+
# func_pos: CodeRange = finder.target_pos
|
|
19
|
+
# newWrapper = cst.MetadataWrapper(metaAST)
|
|
20
|
+
# filteredAST = newWrapper.visit(NodRemove(frameInfo.lineno))
|
|
21
|
+
# indent = func_body.start.column
|
|
22
|
+
|
|
23
|
+
# return filteredAST, func_body, func_pos, indent
|
|
24
|
+
_log = logging.getLogger(__name__)
|
|
25
|
+
_log.setLevel(logging.INFO)
|
|
26
|
+
|
|
27
|
+
# class findIndent(cst.CSTVisitor):
|
|
28
|
+
# METADATA_DEPENDENCIES = (PositionProvider,)
|
|
29
|
+
|
|
30
|
+
# def __init__(self):
|
|
31
|
+
# super(__class__, self).__init__()
|
|
32
|
+
# self.block: cst.IndentedBlock = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# def visit_IndentedBlock(self, node) -> Optional[bool]:
|
|
36
|
+
# self.block = node
|
|
37
|
+
# return False
|
|
38
|
+
class FunctionFinder(m.MatcherDecoratableTransformer):
|
|
39
|
+
METADATA_DEPENDENCIES = (
|
|
40
|
+
ParentNodeProvider,
|
|
41
|
+
PositionProvider,
|
|
42
|
+
)
|
|
43
|
+
parent_node: cst.CSTNode
|
|
44
|
+
parent_pos: CodeRange
|
|
45
|
+
body_indent: CodeRange
|
|
46
|
+
target_function: str
|
|
47
|
+
|
|
48
|
+
def __init__(self, target_function: str):
|
|
49
|
+
super(__class__, self).__init__() # type: ignore
|
|
50
|
+
self.target_function: str = target_function
|
|
51
|
+
|
|
52
|
+
@m.visit(m.FunctionDef())
|
|
53
|
+
def visit_function(self, node: cst.FunctionDef):
|
|
54
|
+
if m.matches(node, m.FunctionDef(m.Name(self.target_function))):
|
|
55
|
+
self.parent_node = node
|
|
56
|
+
parent_pos = self.get_metadata(PositionProvider, node)
|
|
57
|
+
if isinstance(parent_pos, CodeRange):
|
|
58
|
+
self.parent_pos = parent_pos
|
|
59
|
+
body_indent = self.get_metadata(PositionProvider, node.body)
|
|
60
|
+
if isinstance(body_indent, CodeRange):
|
|
61
|
+
self.body_indent = body_indent
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class NodFinder(m.MatcherDecoratableTransformer):
|
|
65
|
+
METADATA_DEPENDENCIES = (
|
|
66
|
+
ParentNodeProvider,
|
|
67
|
+
PositionProvider,
|
|
68
|
+
)
|
|
69
|
+
parent_node: cst.CSTNode
|
|
70
|
+
parent_pos: CodeRange
|
|
71
|
+
body_indent: CodeRange
|
|
72
|
+
|
|
73
|
+
def __init__(self, lineno: int):
|
|
74
|
+
super(__class__, self).__init__() # type: ignore
|
|
75
|
+
self.lineno = lineno
|
|
76
|
+
self.indent_stack: List[cst.IndentedBlock] = []
|
|
77
|
+
|
|
78
|
+
@m.visit(m.Call())
|
|
79
|
+
def visit_notebook_call(self, node: cst.Call):
|
|
80
|
+
if m.matches(node, m.Call(func=m.Name("notebook"))):
|
|
81
|
+
pos = self.get_metadata(PositionProvider, node)
|
|
82
|
+
if isinstance(pos, CodeRange):
|
|
83
|
+
if pos.start.line == self.lineno and len(self.indent_stack) > 0:
|
|
84
|
+
indent_block = self.indent_stack[-1]
|
|
85
|
+
parent_node = self.get_metadata(ParentNodeProvider, indent_block)
|
|
86
|
+
if isinstance(parent_node, cst.CSTNode):
|
|
87
|
+
self.parent_node = parent_node
|
|
88
|
+
parent_pos = self.get_metadata(PositionProvider, parent_node)
|
|
89
|
+
if isinstance(parent_pos, CodeRange):
|
|
90
|
+
self.parent_pos = parent_pos
|
|
91
|
+
body_indent = self.get_metadata(PositionProvider, indent_block)
|
|
92
|
+
if isinstance(body_indent, CodeRange):
|
|
93
|
+
self.body_indent = body_indent
|
|
94
|
+
|
|
95
|
+
def visit_IndentedBlock(self, node):
|
|
96
|
+
self.indent_stack.append(cst.ensure_type(node, cst.IndentedBlock))
|
|
97
|
+
|
|
98
|
+
def leave_IndentedBlock(self, original_node, updated_node):
|
|
99
|
+
self.indent_stack.pop()
|
|
100
|
+
return original_node
|
|
101
|
+
|
|
102
|
+
# @m.leave(m.SimpleStatementLine(body=[m.Expr(value=m.Call(func=m.Name("nod")))]))
|
|
103
|
+
# def rem_nod(
|
|
104
|
+
# self, original_node, updated_node
|
|
105
|
+
# ) -> Union[cst.SimpleStatementLine, cst.RemovalSentinel]:
|
|
106
|
+
# pos: cst.metadata.CodePosition = self.get_metadata(
|
|
107
|
+
# PositionProvider, original_node
|
|
108
|
+
# )
|
|
109
|
+
# if pos.start.line == self.lineno:
|
|
110
|
+
# # newnode = updated_node.with_changes(body=[cst.Newline()])
|
|
111
|
+
# return cst.SimpleStatementLine(body=[cst.Pass()])
|
|
112
|
+
|
|
113
|
+
# @m.visit(
|
|
114
|
+
# m.IndentedBlock(
|
|
115
|
+
# body=[
|
|
116
|
+
# m.ZeroOrMore(m.DoNotCare()),
|
|
117
|
+
# m.SimpleStatementLine(
|
|
118
|
+
# body=[m.Expr(value=m.Call(func=m.Name(value="notebook")))]
|
|
119
|
+
# ),
|
|
120
|
+
# m.ZeroOrMore(m.DoNotCare()),
|
|
121
|
+
# ]
|
|
122
|
+
# )
|
|
123
|
+
# )
|
|
124
|
+
# def find_notebook_indent(self, node):
|
|
125
|
+
# _log.debug("Found notebook()")
|
|
126
|
+
# _log.debug(cst.dump(node))
|
|
127
|
+
# notebook_pos: cst.metadata.CodePosition = self.get_metadata(
|
|
128
|
+
# PositionProvider, node
|
|
129
|
+
# )
|
|
130
|
+
|
|
131
|
+
# if pos.start.line == self.lineno:
|
|
132
|
+
# if len(self.call_stack) > 0:
|
|
133
|
+
# parent_node: cst.FunctionDef = self.call_stack[-1]
|
|
134
|
+
# self.parent_node = parent_node
|
|
135
|
+
# self.parent_pos = self.get_metadata(PositionProvider, parent_node)
|
|
136
|
+
|
|
137
|
+
# indentVisitor = findIndent()
|
|
138
|
+
# parent_node.visit(indentVisitor)
|
|
139
|
+
# self.body_indent = self.get_metadata(PositionProvider, indentVisitor.block)
|
|
140
|
+
|
|
141
|
+
# def leave_Call(
|
|
142
|
+
# self, original_node: cst.Call, updated_node: cst.Call
|
|
143
|
+
# ) -> Union[cst.Call, cst.RemovalSentinel]:
|
|
144
|
+
# if m.matches(original_node.func, m.Name("nod")):
|
|
145
|
+
# pos: cst.metadata.CodePosition = self.get_metadata(
|
|
146
|
+
# PositionProvider, original_node
|
|
147
|
+
# )
|
|
148
|
+
# if pos.start.line == self.lineno:
|
|
149
|
+
# print("removing")
|
|
150
|
+
# parent = self.get_metadata(ParentNodeProvider, original_node)
|
|
151
|
+
# return cst.RemoveFromParent()
|
|
152
|
+
# return original_node
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# class NodRemove(m.MatcherDecoratableTransformer):
|
|
156
|
+
# METADATA_DEPENDENCIES = (PositionProvider,)
|
|
157
|
+
|
|
158
|
+
# def __init__(self, lineno):
|
|
159
|
+
# super(__class__, self).__init__()
|
|
160
|
+
# self.lineno = lineno
|
|
161
|
+
|
|
162
|
+
# @m.leave(
|
|
163
|
+
# m.SimpleStatementLine(body=[m.Expr(value=m.Call(func=m.Name("notebook")))])
|
|
164
|
+
# )
|
|
165
|
+
# # @m.leave(m.Expr(value=m.Call(func=m.Name("nod"))))
|
|
166
|
+
# def rem_nod(
|
|
167
|
+
# self, original_node, updated_node: cst.SimpleStatementLine
|
|
168
|
+
# ) -> Union[cst.SimpleStatementLine, cst.RemovalSentinel]:
|
|
169
|
+
# print("FOUND")
|
|
170
|
+
# print(original_node)
|
|
171
|
+
# pos: cst.metadata.CodePosition = self.get_metadata(
|
|
172
|
+
# PositionProvider, original_node
|
|
173
|
+
# )
|
|
174
|
+
# if pos.start.line == self.lineno:
|
|
175
|
+
# # newnode = updated_node.with_changes(body=[cst.Newline()])
|
|
176
|
+
# return cst.SimpleStatementLine(body=[cst.Pass()])
|
nodpy/datastore.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class LogStore:
|
|
2
|
+
instance = None
|
|
3
|
+
|
|
4
|
+
def __new__(cls):
|
|
5
|
+
if cls.instance is None:
|
|
6
|
+
cls.instance = super().__new__(cls)
|
|
7
|
+
return cls.instance
|
|
8
|
+
|
|
9
|
+
logs: list[str] = []
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# class StartingVariables:
|
|
13
|
+
# instance = None
|
|
14
|
+
|
|
15
|
+
# def __new__(cls):
|
|
16
|
+
# if cls.instance is None:
|
|
17
|
+
# cls.instance = super().__new__(cls)
|
|
18
|
+
# return cls.instance
|
|
19
|
+
|
|
20
|
+
# app = None
|
|
21
|
+
# variables = {}
|