From f81a90c5b06ce2033ac8abc1900872ec67d3f41a Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Thu, 12 Jun 2025 12:06:12 +1000 Subject: [PATCH 1/2] dry --- pystackql/magic_ext/base.py | 98 +++++++++++++++++++- pystackql/magic_ext/local.py | 166 ++++++++++++++++++++++++---------- pystackql/magic_ext/server.py | 161 +++++++++++++++++++++++---------- 3 files changed, 332 insertions(+), 93 deletions(-) diff --git a/pystackql/magic_ext/base.py b/pystackql/magic_ext/base.py index 7a647f9..c6260e2 100644 --- a/pystackql/magic_ext/base.py +++ b/pystackql/magic_ext/base.py @@ -1,3 +1,57 @@ +# # pystackql/magic_ext/base.py + +# """ +# Base Jupyter magic extension for PyStackQL. + +# This module provides the base class for PyStackQL Jupyter magic extensions. +# """ + +# from __future__ import print_function +# from IPython.core.magic import Magics +# from string import Template + +# class BaseStackqlMagic(Magics): +# """Base Jupyter magic extension enabling running StackQL queries. + +# This extension allows users to conveniently run StackQL queries against cloud +# or SaaS resources directly from Jupyter notebooks, and visualize the results in a tabular +# format using Pandas DataFrames. +# """ +# def __init__(self, shell, server_mode): +# """Initialize the BaseStackqlMagic class. + +# :param shell: The IPython shell instance. +# :param server_mode: Whether to use server mode. +# """ +# from ..core import StackQL +# super(BaseStackqlMagic, self).__init__(shell) +# self.stackql_instance = StackQL(server_mode=server_mode, output='pandas') + +# def get_rendered_query(self, data): +# """Substitute placeholders in a query template with variables from the current namespace. + +# :param data: SQL query template containing placeholders. +# :type data: str +# :return: A SQL query with placeholders substituted. +# :rtype: str +# """ +# t = Template(data) +# return t.substitute(self.shell.user_ns) + +# def run_query(self, query): +# """Execute a StackQL query + +# :param query: StackQL query to be executed. +# :type query: str +# :return: Query results, returned as a Pandas DataFrame. +# :rtype: pandas.DataFrame +# """ +# # Check if the query starts with "registry pull" (case insensitive) +# if query.strip().lower().startswith("registry pull"): +# return self.stackql_instance.executeStmt(query) + +# return self.stackql_instance.execute(query) + # pystackql/magic_ext/base.py """ @@ -50,4 +104,46 @@ def run_query(self, query): if query.strip().lower().startswith("registry pull"): return self.stackql_instance.executeStmt(query) - return self.stackql_instance.execute(query) \ No newline at end of file + return self.stackql_instance.execute(query) + + def _display_with_csv_download(self, df): + """Display DataFrame with CSV download link. + + :param df: The DataFrame to display and make downloadable. + """ + import IPython.display + + try: + # Generate CSV data + import io + import base64 + csv_buffer = io.StringIO() + df.to_csv(csv_buffer, index=False) + csv_data = csv_buffer.getvalue() + + # Encode to base64 for data URI + csv_base64 = base64.b64encode(csv_data.encode()).decode() + + # Create download link + download_link = f'data:text/csv;base64,{csv_base64}' + + # Display the DataFrame first + IPython.display.display(df) + + # Create and display the download button + download_html = f''' +
+ + 📥 Download CSV + +
+ ''' + IPython.display.display(IPython.display.HTML(download_html)) + + except Exception as e: + # If CSV generation fails, just display the DataFrame normally + IPython.display.display(df) + print(f"Error generating CSV download: {e}") \ No newline at end of file diff --git a/pystackql/magic_ext/local.py b/pystackql/magic_ext/local.py index ba98d99..a031d78 100644 --- a/pystackql/magic_ext/local.py +++ b/pystackql/magic_ext/local.py @@ -1,3 +1,122 @@ +# # pystackql/magic_ext/local.py + +# """ +# Local Jupyter magic extension for PyStackQL. + +# This module provides a Jupyter magic command for running StackQL queries +# using a local StackQL binary. +# """ + +# from IPython.core.magic import (magics_class, line_cell_magic) +# from IPython.display import display, HTML +# from .base import BaseStackqlMagic +# import argparse +# import base64 +# import io + +# @magics_class +# class StackqlMagic(BaseStackqlMagic): +# """Jupyter magic command for running StackQL queries in local mode.""" + +# def __init__(self, shell): +# """Initialize the StackqlMagic class. + +# :param shell: The IPython shell instance. +# """ +# super().__init__(shell, server_mode=False) + +# @line_cell_magic +# def stackql(self, line, cell=None): +# """A Jupyter magic command to run StackQL queries. + +# Can be used as both line and cell magic: +# - As a line magic: `%stackql QUERY` +# - As a cell magic: `%%stackql [OPTIONS]` followed by the QUERY in the next line. + +# :param line: The arguments and/or StackQL query when used as line magic. +# :param cell: The StackQL query when used as cell magic. +# :return: StackQL query results as a named Pandas DataFrame (`stackql_df`). +# """ +# is_cell_magic = cell is not None + +# if is_cell_magic: +# parser = argparse.ArgumentParser() +# parser.add_argument("--no-display", action="store_true", help="Suppress result display.") +# parser.add_argument("--csv-download", action="store_true", help="Add CSV download link to output.") +# args = parser.parse_args(line.split()) +# query_to_run = self.get_rendered_query(cell) +# else: +# args = None +# query_to_run = self.get_rendered_query(line) + +# results = self.run_query(query_to_run) +# self.shell.user_ns['stackql_df'] = results + +# if is_cell_magic and args and args.no_display: +# return None +# elif is_cell_magic and args and args.csv_download and not args.no_display: +# self._display_with_csv_download(results) +# return results +# elif is_cell_magic and args and not args.no_display: +# return results +# elif not is_cell_magic: +# return results + +# def _display_with_csv_download(self, df): +# """Display DataFrame with CSV download link. + +# :param df: The DataFrame to display and make downloadable. +# """ +# import IPython.display + +# try: +# # Generate CSV data +# import io +# import base64 +# csv_buffer = io.StringIO() +# df.to_csv(csv_buffer, index=False) +# csv_data = csv_buffer.getvalue() + +# # Encode to base64 for data URI +# csv_base64 = base64.b64encode(csv_data.encode()).decode() + +# # Create download link +# download_link = f'data:text/csv;base64,{csv_base64}' + +# # Display the DataFrame first +# IPython.display.display(df) + +# # Create and display the download button +# download_html = f''' +#
+# +# 📥 Download CSV +# +#
+# ''' +# IPython.display.display(IPython.display.HTML(download_html)) + +# except Exception as e: +# # If CSV generation fails, just display the DataFrame normally +# IPython.display.display(df) +# print(f"Error generating CSV download: {e}") + +# def load_ipython_extension(ipython): +# """Load the non-server magic in IPython. + +# This is called when running %load_ext pystackql.magic in a notebook. +# It registers the %stackql and %%stackql magic commands. + +# :param ipython: The IPython shell instance +# """ +# # Create an instance of the magic class and register it +# magic_instance = StackqlMagic(ipython) +# ipython.register_magics(magic_instance) + + # pystackql/magic_ext/local.py """ @@ -8,11 +127,8 @@ """ from IPython.core.magic import (magics_class, line_cell_magic) -from IPython.display import display, HTML from .base import BaseStackqlMagic import argparse -import base64 -import io @magics_class class StackqlMagic(BaseStackqlMagic): @@ -62,48 +178,6 @@ def stackql(self, line, cell=None): elif not is_cell_magic: return results - def _display_with_csv_download(self, df): - """Display DataFrame with CSV download link. - - :param df: The DataFrame to display and make downloadable. - """ - import IPython.display - - try: - # Generate CSV data - import io - import base64 - csv_buffer = io.StringIO() - df.to_csv(csv_buffer, index=False) - csv_data = csv_buffer.getvalue() - - # Encode to base64 for data URI - csv_base64 = base64.b64encode(csv_data.encode()).decode() - - # Create download link - download_link = f'data:text/csv;base64,{csv_base64}' - - # Display the DataFrame first - IPython.display.display(df) - - # Create and display the download button - download_html = f''' -
- - 📥 Download CSV - -
- ''' - IPython.display.display(IPython.display.HTML(download_html)) - - except Exception as e: - # If CSV generation fails, just display the DataFrame normally - IPython.display.display(df) - print(f"Error generating CSV download: {e}") - def load_ipython_extension(ipython): """Load the non-server magic in IPython. @@ -114,4 +188,4 @@ def load_ipython_extension(ipython): """ # Create an instance of the magic class and register it magic_instance = StackqlMagic(ipython) - ipython.register_magics(magic_instance) + ipython.register_magics(magic_instance) \ No newline at end of file diff --git a/pystackql/magic_ext/server.py b/pystackql/magic_ext/server.py index 074603f..a9db537 100644 --- a/pystackql/magic_ext/server.py +++ b/pystackql/magic_ext/server.py @@ -1,3 +1,117 @@ +# # pystackql/magic_ext/server.py + +# """ +# Server Jupyter magic extension for PyStackQL. + +# This module provides a Jupyter magic command for running StackQL queries +# using a StackQL server connection. +# """ + +# from IPython.core.magic import (magics_class, line_cell_magic) +# from IPython.display import display, HTML +# from .base import BaseStackqlMagic +# import argparse +# import base64 +# import io + +# @magics_class +# class StackqlServerMagic(BaseStackqlMagic): +# """Jupyter magic command for running StackQL queries in server mode.""" + +# def __init__(self, shell): +# """Initialize the StackqlServerMagic class. + +# :param shell: The IPython shell instance. +# """ +# super().__init__(shell, server_mode=True) + +# @line_cell_magic +# def stackql(self, line, cell=None): +# """A Jupyter magic command to run StackQL queries. + +# Can be used as both line and cell magic: +# - As a line magic: `%stackql QUERY` +# - As a cell magic: `%%stackql [OPTIONS]` followed by the QUERY in the next line. + +# :param line: The arguments and/or StackQL query when used as line magic. +# :param cell: The StackQL query when used as cell magic. +# :return: StackQL query results as a named Pandas DataFrame (`stackql_df`). +# """ +# is_cell_magic = cell is not None + +# if is_cell_magic: +# parser = argparse.ArgumentParser() +# parser.add_argument("--no-display", action="store_true", help="Suppress result display.") +# parser.add_argument("--csv-download", action="store_true", help="Add CSV download link to output.") +# args = parser.parse_args(line.split()) +# query_to_run = self.get_rendered_query(cell) +# else: +# args = None +# query_to_run = self.get_rendered_query(line) + +# results = self.run_query(query_to_run) +# self.shell.user_ns['stackql_df'] = results + +# if is_cell_magic and args and args.no_display: +# return None +# elif is_cell_magic and args and args.csv_download and not args.no_display: +# self._display_with_csv_download(results) +# return results +# elif is_cell_magic and args and not args.no_display: +# return results +# elif not is_cell_magic: +# return results +# else: +# return results + +# def _display_with_csv_download(self, df): +# """Display DataFrame with CSV download link. + +# :param df: The DataFrame to display and make downloadable. +# """ +# import IPython.display + +# try: +# # Generate CSV data +# import io +# import base64 +# csv_buffer = io.StringIO() +# df.to_csv(csv_buffer, index=False) +# csv_data = csv_buffer.getvalue() + +# # Encode to base64 for data URI +# csv_base64 = base64.b64encode(csv_data.encode()).decode() + +# # Create download link +# download_link = f'data:text/csv;base64,{csv_base64}' + +# # Display the DataFrame first +# IPython.display.display(df) + +# # Create and display the download button +# download_html = f''' +#
+# +# 📥 Download CSV +# +#
+# ''' +# IPython.display.display(IPython.display.HTML(download_html)) + +# except Exception as e: +# # If CSV generation fails, just display the DataFrame normally +# IPython.display.display(df) +# print(f"Error generating CSV download: {e}") + +# def load_ipython_extension(ipython): +# """Load the server magic in IPython.""" +# # Create an instance of the magic class and register it +# magic_instance = StackqlServerMagic(ipython) +# ipython.register_magics(magic_instance) + # pystackql/magic_ext/server.py """ @@ -8,11 +122,8 @@ """ from IPython.core.magic import (magics_class, line_cell_magic) -from IPython.display import display, HTML from .base import BaseStackqlMagic import argparse -import base64 -import io @magics_class class StackqlServerMagic(BaseStackqlMagic): @@ -64,50 +175,8 @@ def stackql(self, line, cell=None): else: return results - def _display_with_csv_download(self, df): - """Display DataFrame with CSV download link. - - :param df: The DataFrame to display and make downloadable. - """ - import IPython.display - - try: - # Generate CSV data - import io - import base64 - csv_buffer = io.StringIO() - df.to_csv(csv_buffer, index=False) - csv_data = csv_buffer.getvalue() - - # Encode to base64 for data URI - csv_base64 = base64.b64encode(csv_data.encode()).decode() - - # Create download link - download_link = f'data:text/csv;base64,{csv_base64}' - - # Display the DataFrame first - IPython.display.display(df) - - # Create and display the download button - download_html = f''' -
- - 📥 Download CSV - -
- ''' - IPython.display.display(IPython.display.HTML(download_html)) - - except Exception as e: - # If CSV generation fails, just display the DataFrame normally - IPython.display.display(df) - print(f"Error generating CSV download: {e}") - def load_ipython_extension(ipython): """Load the server magic in IPython.""" # Create an instance of the magic class and register it magic_instance = StackqlServerMagic(ipython) - ipython.register_magics(magic_instance) + ipython.register_magics(magic_instance) \ No newline at end of file From 731ace409a76b381c9ea459caed9e9c12901ee3e Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Thu, 12 Jun 2025 12:22:10 +1000 Subject: [PATCH 2/2] added notebook --- .devcontainer/devcontainer.json | 25 ++++++ notebooks/demo.ipynb | 150 ++++++++++++++++++++++++++++++++ pystackql/magic_ext/base.py | 54 ------------ pystackql/magic_ext/local.py | 119 ------------------------- pystackql/magic_ext/server.py | 114 ------------------------ 5 files changed, 175 insertions(+), 287 deletions(-) create mode 100644 .devcontainer/devcontainer.json create mode 100644 notebooks/demo.ipynb diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..8786236 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,25 @@ +{ + "image": "mcr.microsoft.com/devcontainers/universal:2", + "containerEnv": { + }, + "hostRequirements": { + "cpus": 2 + }, + "waitFor": "onCreateCommand", + "updateContentCommand": "pip install -e ", + "postCreateCommand": "", + "postStartCommand": "git reset --hard && git clean -fd", + "customizations": { + "codespaces": { + "openFiles": [ + "notebooks/demo.ipynb" + ] + }, + "vscode": { + "extensions": [ + "ms-toolsai.jupyter", + "ms-python.python" + ] + } + } +} \ No newline at end of file diff --git a/notebooks/demo.ipynb b/notebooks/demo.ipynb new file mode 100644 index 0000000..c980af3 --- /dev/null +++ b/notebooks/demo.ipynb @@ -0,0 +1,150 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# PyStackQL Development Demo\n", + "\n", + "This notebook demonstrates how to use the development version of PyStackQL directly from the source code. Any changes you make to the PyStackQL code will be immediately reflected here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# First, let's check what version of PyStackQL we're using\n", + "import pystackql\n", + "print(f\"PyStackQL Version: {pystackql.__version__}\")\n", + "\n", + "# Check the location of the package to confirm we're using the development version\n", + "print(f\"Package Location: {pystackql.__file__}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load the magic extension\n", + "%load_ext pystackql.magic" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Basic Query Test\n", + "\n", + "Let's run a simple query to test that everything is working:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%stackql SELECT 42 as answer" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## CSV Download Test\n", + "\n", + "Let's test the CSV download functionality:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%stackql --csv-download\n", + "SELECT \n", + " 'Python' as language,\n", + " 'Development' as mode,\n", + " 'PyStackQL' as package" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test Cloud Provider Functionality\n", + "\n", + "If you have credentials configured, you can test actual cloud provider queries:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Uncomment and run the appropriate provider query based on your available credentials\n", + "\n", + "# AWS Example\n", + "# %stackql DESCRIBE aws.ec2.instances\n", + "\n", + "# GitHub Example\n", + "# %stackql registry pull github\n", + "# %stackql SELECT login FROM github.users.followers WHERE username = 'stackql'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Development Tips\n", + "\n", + "1. After modifying PyStackQL code, you don't need to reinstall the package - changes are reflected immediately\n", + "2. You can run tests from the terminal with `pytest tests/`\n", + "3. If you modify deep core functionality, you may need to restart the kernel\n", + "4. To debug issues, you can use Python's built-in debugging tools:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Example debugging a PyStackQL function\n", + "from pystackql.core import StackQL\n", + "\n", + "# Get instance properties\n", + "stackql = StackQL()\n", + "props = stackql.properties()\n", + "print(props)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python (PyStackQL Dev)", + "language": "python", + "name": "pystackql-dev" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.4" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/pystackql/magic_ext/base.py b/pystackql/magic_ext/base.py index c6260e2..0febcc3 100644 --- a/pystackql/magic_ext/base.py +++ b/pystackql/magic_ext/base.py @@ -1,57 +1,3 @@ -# # pystackql/magic_ext/base.py - -# """ -# Base Jupyter magic extension for PyStackQL. - -# This module provides the base class for PyStackQL Jupyter magic extensions. -# """ - -# from __future__ import print_function -# from IPython.core.magic import Magics -# from string import Template - -# class BaseStackqlMagic(Magics): -# """Base Jupyter magic extension enabling running StackQL queries. - -# This extension allows users to conveniently run StackQL queries against cloud -# or SaaS resources directly from Jupyter notebooks, and visualize the results in a tabular -# format using Pandas DataFrames. -# """ -# def __init__(self, shell, server_mode): -# """Initialize the BaseStackqlMagic class. - -# :param shell: The IPython shell instance. -# :param server_mode: Whether to use server mode. -# """ -# from ..core import StackQL -# super(BaseStackqlMagic, self).__init__(shell) -# self.stackql_instance = StackQL(server_mode=server_mode, output='pandas') - -# def get_rendered_query(self, data): -# """Substitute placeholders in a query template with variables from the current namespace. - -# :param data: SQL query template containing placeholders. -# :type data: str -# :return: A SQL query with placeholders substituted. -# :rtype: str -# """ -# t = Template(data) -# return t.substitute(self.shell.user_ns) - -# def run_query(self, query): -# """Execute a StackQL query - -# :param query: StackQL query to be executed. -# :type query: str -# :return: Query results, returned as a Pandas DataFrame. -# :rtype: pandas.DataFrame -# """ -# # Check if the query starts with "registry pull" (case insensitive) -# if query.strip().lower().startswith("registry pull"): -# return self.stackql_instance.executeStmt(query) - -# return self.stackql_instance.execute(query) - # pystackql/magic_ext/base.py """ diff --git a/pystackql/magic_ext/local.py b/pystackql/magic_ext/local.py index a031d78..e88479b 100644 --- a/pystackql/magic_ext/local.py +++ b/pystackql/magic_ext/local.py @@ -1,122 +1,3 @@ -# # pystackql/magic_ext/local.py - -# """ -# Local Jupyter magic extension for PyStackQL. - -# This module provides a Jupyter magic command for running StackQL queries -# using a local StackQL binary. -# """ - -# from IPython.core.magic import (magics_class, line_cell_magic) -# from IPython.display import display, HTML -# from .base import BaseStackqlMagic -# import argparse -# import base64 -# import io - -# @magics_class -# class StackqlMagic(BaseStackqlMagic): -# """Jupyter magic command for running StackQL queries in local mode.""" - -# def __init__(self, shell): -# """Initialize the StackqlMagic class. - -# :param shell: The IPython shell instance. -# """ -# super().__init__(shell, server_mode=False) - -# @line_cell_magic -# def stackql(self, line, cell=None): -# """A Jupyter magic command to run StackQL queries. - -# Can be used as both line and cell magic: -# - As a line magic: `%stackql QUERY` -# - As a cell magic: `%%stackql [OPTIONS]` followed by the QUERY in the next line. - -# :param line: The arguments and/or StackQL query when used as line magic. -# :param cell: The StackQL query when used as cell magic. -# :return: StackQL query results as a named Pandas DataFrame (`stackql_df`). -# """ -# is_cell_magic = cell is not None - -# if is_cell_magic: -# parser = argparse.ArgumentParser() -# parser.add_argument("--no-display", action="store_true", help="Suppress result display.") -# parser.add_argument("--csv-download", action="store_true", help="Add CSV download link to output.") -# args = parser.parse_args(line.split()) -# query_to_run = self.get_rendered_query(cell) -# else: -# args = None -# query_to_run = self.get_rendered_query(line) - -# results = self.run_query(query_to_run) -# self.shell.user_ns['stackql_df'] = results - -# if is_cell_magic and args and args.no_display: -# return None -# elif is_cell_magic and args and args.csv_download and not args.no_display: -# self._display_with_csv_download(results) -# return results -# elif is_cell_magic and args and not args.no_display: -# return results -# elif not is_cell_magic: -# return results - -# def _display_with_csv_download(self, df): -# """Display DataFrame with CSV download link. - -# :param df: The DataFrame to display and make downloadable. -# """ -# import IPython.display - -# try: -# # Generate CSV data -# import io -# import base64 -# csv_buffer = io.StringIO() -# df.to_csv(csv_buffer, index=False) -# csv_data = csv_buffer.getvalue() - -# # Encode to base64 for data URI -# csv_base64 = base64.b64encode(csv_data.encode()).decode() - -# # Create download link -# download_link = f'data:text/csv;base64,{csv_base64}' - -# # Display the DataFrame first -# IPython.display.display(df) - -# # Create and display the download button -# download_html = f''' -#
-# -# 📥 Download CSV -# -#
-# ''' -# IPython.display.display(IPython.display.HTML(download_html)) - -# except Exception as e: -# # If CSV generation fails, just display the DataFrame normally -# IPython.display.display(df) -# print(f"Error generating CSV download: {e}") - -# def load_ipython_extension(ipython): -# """Load the non-server magic in IPython. - -# This is called when running %load_ext pystackql.magic in a notebook. -# It registers the %stackql and %%stackql magic commands. - -# :param ipython: The IPython shell instance -# """ -# # Create an instance of the magic class and register it -# magic_instance = StackqlMagic(ipython) -# ipython.register_magics(magic_instance) - - # pystackql/magic_ext/local.py """ diff --git a/pystackql/magic_ext/server.py b/pystackql/magic_ext/server.py index a9db537..70d561c 100644 --- a/pystackql/magic_ext/server.py +++ b/pystackql/magic_ext/server.py @@ -1,117 +1,3 @@ -# # pystackql/magic_ext/server.py - -# """ -# Server Jupyter magic extension for PyStackQL. - -# This module provides a Jupyter magic command for running StackQL queries -# using a StackQL server connection. -# """ - -# from IPython.core.magic import (magics_class, line_cell_magic) -# from IPython.display import display, HTML -# from .base import BaseStackqlMagic -# import argparse -# import base64 -# import io - -# @magics_class -# class StackqlServerMagic(BaseStackqlMagic): -# """Jupyter magic command for running StackQL queries in server mode.""" - -# def __init__(self, shell): -# """Initialize the StackqlServerMagic class. - -# :param shell: The IPython shell instance. -# """ -# super().__init__(shell, server_mode=True) - -# @line_cell_magic -# def stackql(self, line, cell=None): -# """A Jupyter magic command to run StackQL queries. - -# Can be used as both line and cell magic: -# - As a line magic: `%stackql QUERY` -# - As a cell magic: `%%stackql [OPTIONS]` followed by the QUERY in the next line. - -# :param line: The arguments and/or StackQL query when used as line magic. -# :param cell: The StackQL query when used as cell magic. -# :return: StackQL query results as a named Pandas DataFrame (`stackql_df`). -# """ -# is_cell_magic = cell is not None - -# if is_cell_magic: -# parser = argparse.ArgumentParser() -# parser.add_argument("--no-display", action="store_true", help="Suppress result display.") -# parser.add_argument("--csv-download", action="store_true", help="Add CSV download link to output.") -# args = parser.parse_args(line.split()) -# query_to_run = self.get_rendered_query(cell) -# else: -# args = None -# query_to_run = self.get_rendered_query(line) - -# results = self.run_query(query_to_run) -# self.shell.user_ns['stackql_df'] = results - -# if is_cell_magic and args and args.no_display: -# return None -# elif is_cell_magic and args and args.csv_download and not args.no_display: -# self._display_with_csv_download(results) -# return results -# elif is_cell_magic and args and not args.no_display: -# return results -# elif not is_cell_magic: -# return results -# else: -# return results - -# def _display_with_csv_download(self, df): -# """Display DataFrame with CSV download link. - -# :param df: The DataFrame to display and make downloadable. -# """ -# import IPython.display - -# try: -# # Generate CSV data -# import io -# import base64 -# csv_buffer = io.StringIO() -# df.to_csv(csv_buffer, index=False) -# csv_data = csv_buffer.getvalue() - -# # Encode to base64 for data URI -# csv_base64 = base64.b64encode(csv_data.encode()).decode() - -# # Create download link -# download_link = f'data:text/csv;base64,{csv_base64}' - -# # Display the DataFrame first -# IPython.display.display(df) - -# # Create and display the download button -# download_html = f''' -#
-# -# 📥 Download CSV -# -#
-# ''' -# IPython.display.display(IPython.display.HTML(download_html)) - -# except Exception as e: -# # If CSV generation fails, just display the DataFrame normally -# IPython.display.display(df) -# print(f"Error generating CSV download: {e}") - -# def load_ipython_extension(ipython): -# """Load the server magic in IPython.""" -# # Create an instance of the magic class and register it -# magic_instance = StackqlServerMagic(ipython) -# ipython.register_magics(magic_instance) - # pystackql/magic_ext/server.py """