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.
Files changed (40) hide show
  1. nodpy/__init__.py +451 -0
  2. nodpy/_version.py +4 -0
  3. nodpy/ast_tools.py +176 -0
  4. nodpy/datastore.py +21 -0
  5. nodpy/embed_kernel.py +305 -0
  6. nodpy/file_helpers.py +246 -0
  7. nodpy/hatch_build.py +52 -0
  8. nodpy/ip_plugin.py +72 -0
  9. nodpy/labextension/package.json +232 -0
  10. nodpy/labextension/schemas/nodjs/package.json.orig +227 -0
  11. nodpy/labextension/schemas/nodjs/plugin.json +46 -0
  12. nodpy/labextension/static/298.1fa66e2cd2088e804334.js +1 -0
  13. nodpy/labextension/static/728.12765e602400f37a118b.js +1 -0
  14. nodpy/labextension/static/781.8294abe512b48b00402a.js +1 -0
  15. nodpy/labextension/static/835.4539cede11218d7fe226.js +1 -0
  16. nodpy/labextension/static/remoteEntry.f340994a4ea855dbfc8a.js +1 -0
  17. nodpy/labextension/static/style.js +4 -0
  18. nodpy/labextension/static/third-party-licenses.json +64 -0
  19. nodpy/nod_cli.py +184 -0
  20. nodpy/provisioner.py +461 -0
  21. nodpy/serverExtension.py +202 -0
  22. nodpy/tests/__init__.py +1 -0
  23. nodpy/tracing.py +28 -0
  24. nodpy-0.1.3.data/data/etc/jupyter/jupyter_server_config.d/nodpy.json +7 -0
  25. nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/install.json +5 -0
  26. nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/package.json +232 -0
  27. nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/schemas/nodjs/package.json.orig +227 -0
  28. nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/schemas/nodjs/plugin.json +46 -0
  29. nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/static/298.1fa66e2cd2088e804334.js +1 -0
  30. nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/static/728.12765e602400f37a118b.js +1 -0
  31. nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/static/781.8294abe512b48b00402a.js +1 -0
  32. nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/static/835.4539cede11218d7fe226.js +1 -0
  33. nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/static/remoteEntry.f340994a4ea855dbfc8a.js +1 -0
  34. nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/static/style.js +4 -0
  35. nodpy-0.1.3.data/data/share/jupyter/labextensions/nodjs/static/third-party-licenses.json +64 -0
  36. nodpy-0.1.3.dist-info/METADATA +98 -0
  37. nodpy-0.1.3.dist-info/RECORD +40 -0
  38. nodpy-0.1.3.dist-info/WHEEL +4 -0
  39. nodpy-0.1.3.dist-info/entry_points.txt +5 -0
  40. 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
@@ -0,0 +1,4 @@
1
+ # This file is auto-generated by Hatchling. As such, do not:
2
+ # - modify
3
+ # - track in version control e.g. be sure to add to .gitignore
4
+ __version__ = VERSION = '0.1.3'
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 = {}