Path: blob/main/singlestoredb/magics/run_shared.py
469 views
import os1import tempfile2from pathlib import Path3from typing import Any4from warnings import warn56from IPython.core.interactiveshell import InteractiveShell7from IPython.core.magic import line_magic8from IPython.core.magic import Magics9from IPython.core.magic import magics_class10from IPython.core.magic import needs_local_scope11from IPython.core.magic import no_var_expand12from IPython.utils.contexts import preserve_keys13from IPython.utils.syspathcontext import prepended_to_syspath14from jinja2 import Template151617@magics_class18class RunSharedMagic(Magics):19def __init__(self, shell: InteractiveShell):20Magics.__init__(self, shell=shell)2122@no_var_expand23@needs_local_scope24@line_magic('run_shared')25def run_shared(self, line: str, local_ns: Any = None) -> Any:26"""27Downloads a shared file using the %sql magic and then runs it using %run.2829Examples::3031# Line usage3233%run_shared shared_file.ipynb3435%run_shared {{ sample_notebook_name }}3637"""3839template = Template(line.strip())40shared_file = template.render(local_ns)41if not shared_file:42raise ValueError('No shared file specified.')43if (shared_file.startswith("'") and shared_file.endswith("'")) or \44(shared_file.startswith('"') and shared_file.endswith('"')):45shared_file = shared_file[1:-1]46if not shared_file:47raise ValueError('No personal file specified.')4849with tempfile.TemporaryDirectory() as temp_dir:50temp_file_path = os.path.join(temp_dir, shared_file)51sql_command = f"DOWNLOAD SHARED FILE '{shared_file}' TO '{temp_file_path}'"5253# Execute the SQL command54self.shell.run_line_magic('sql', sql_command)55# Run the downloaded file56with preserve_keys(self.shell.user_ns, '__file__'):57self.shell.user_ns['__file__'] = temp_file_path58self.safe_execfile_ipy(temp_file_path, raise_exceptions=True)5960def safe_execfile_ipy(61self,62fname: str,63shell_futures: bool = False,64raise_exceptions: bool = False,65) -> None:66"""Like safe_execfile, but for .ipy or .ipynb files with IPython syntax.6768Parameters69----------70fname : str71The name of the file to execute. The filename must have a72.ipy or .ipynb extension.73shell_futures : bool (False)74If True, the code will share future statements with the interactive75shell. It will both be affected by previous __future__ imports, and76any __future__ imports in the code will affect the shell. If False,77__future__ imports are not shared in either direction.78raise_exceptions : bool (False)79If True raise exceptions everywhere. Meant for testing.80"""81fpath = Path(fname).expanduser().resolve()8283# Make sure we can open the file84try:85with fpath.open('rb'):86pass87except Exception:88warn('Could not open file <%s> for safe execution.' % fpath)89return9091# Find things also in current directory. This is needed to mimic the92# behavior of running a script from the system command line, where93# Python inserts the script's directory into sys.path94dname = str(fpath.parent)9596def get_cells() -> Any:97"""generator for sequence of code blocks to run"""98if fpath.suffix == '.ipynb':99from nbformat import read100nb = read(fpath, as_version=4)101if not nb.cells:102return103for cell in nb.cells:104if cell.cell_type == 'code':105if not cell.source.strip():106continue107if getattr(cell, 'metadata', {}).get('language', '') == 'sql':108output_redirect = getattr(109cell, 'metadata', {},110).get('output_variable', '') or ''111if output_redirect:112output_redirect = f' {output_redirect} <<'113yield f'%%sql{output_redirect}\n{cell.source}'114else:115yield cell.source116else:117yield fpath.read_text(encoding='utf-8')118119with prepended_to_syspath(dname):120try:121for cell in get_cells():122result = self.shell.run_cell(123cell, silent=True, shell_futures=shell_futures,124)125if raise_exceptions:126result.raise_error()127elif not result.success:128break129except Exception:130if raise_exceptions:131raise132self.shell.showtraceback()133warn('Unknown failure executing file: <%s>' % fpath)134135136