esje 0.1.0__tar.gz

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.
esje-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,252 @@
1
+ Metadata-Version: 2.4
2
+ Name: esje
3
+ Version: 0.1.0
4
+ Summary: SQL Magic for Jupyter Notebooks with simple, opinionated credentials management
5
+ Author: EasySQL Team
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Framework :: IPython
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: Science/Research
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: ipython>=7.0.0
15
+ Requires-Dist: pandas>=1.0.0
16
+ Requires-Dist: sqlalchemy>=1.4.0
17
+ Requires-Dist: pymysql>=1.0.0
18
+ Requires-Dist: python-dotenv>=0.19.0
19
+ Provides-Extra: pyarrow
20
+ Requires-Dist: pyarrow>=10.0.0; extra == "pyarrow"
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
23
+ Requires-Dist: pytest-cov>=3.0.0; extra == "dev"
24
+ Requires-Dist: pyarrow>=10.0.0; extra == "dev"
25
+
26
+ # `esje` — Credential-Safe SQL Magic & Live Dashboards for Jupyter
27
+
28
+ [![PyPI Version](https://img.shields.io/pypi/v/esje.svg)](https://pypi.org/project/esje/)
29
+ [![Python Versions](https://img.shields.io/pypi/pyversions/esje.svg)](https://pypi.org/project/esje/)
30
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
31
+ [![Framework: IPython](https://img.shields.io/badge/Framework-IPython-blue.svg)](https://ipython.org)
32
+
33
+ `esje` brings powerful, credential-safe `%sql` and `%%sql` magics to Jupyter Notebooks and JupyterLab. Designed for data analysts and engineers, it eliminates hardcoded secrets in `.ipynb` files, seamlessly executes SQL alongside Python visualization code, and provides non-blocking auto-refreshing `--live` dashboards with Play/Pause/Stop controls.
34
+
35
+ ---
36
+
37
+ ## ✨ Features
38
+
39
+ - 🔒 **Zero Hardcoded Secrets**: Interactive `getpass` prompts and automatic `.env` / environment variable fallbacks prevent password leaks in notebook cells, git commits, or exports.
40
+ - ⚡ **PyArrow High-Performance Backend**: Optional PyArrow data type integration for memory-efficient and fast query execution on large datasets.
41
+ - 📊 **SQL + Python Inline Execution**: Write SQL queries and Python plotting code (`matplotlib`, `seaborn`, `plotly`) in the exact same `%%sql` cell.
42
+ - ⏱️ **Non-Blocking `--live` Dashboards**: Run queries on an auto-refresh timer without blocking the Jupyter kernel execution thread. Includes interactive Play/Pause/Stop widget controls.
43
+ - 🔌 **Named Connection Registry**: Connect to multiple databases and switch between them effortlessly using `-c <conn_name>` or `esje.use()`.
44
+ - 🛡️ **Clean Exception Handling**: Friendly, concise error messages by default without distracting multi-page Python tracebacks.
45
+
46
+ ---
47
+
48
+ ## 📦 Installation
49
+
50
+ Install `esje` via `pip`:
51
+
52
+ ```bash
53
+ pip install esje
54
+ ```
55
+
56
+ For high-performance PyArrow data type acceleration, install with the optional `pyarrow` extra:
57
+
58
+ ```bash
59
+ pip install "esje[pyarrow]"
60
+ ```
61
+
62
+ ---
63
+
64
+ ## 🚀 Quickstart
65
+
66
+ ### 1. Load the Extension
67
+
68
+ In your Jupyter Notebook, load `esje`:
69
+
70
+ ```python
71
+ %load_ext esje
72
+ ```
73
+
74
+ ### 2. Connect to MySQL
75
+
76
+ Connect interactively (you will be prompted securely for any missing credentials):
77
+
78
+ ```python
79
+ import esje
80
+
81
+ # Prompts for host, user, password, database if not found in .env or environment
82
+ conn = esje.connect_mysql()
83
+ ```
84
+
85
+ Or connect with a named connection:
86
+
87
+ ```python
88
+ esje.connect_mysql(name="analytics", database="sales_db")
89
+ ```
90
+
91
+ ---
92
+
93
+ ## 💡 Usage Examples
94
+
95
+ ### Line Magic (`%sql`)
96
+
97
+ Run a quick one-liner SQL query:
98
+
99
+ ```python
100
+ %sql SELECT * FROM users LIMIT 5
101
+ ```
102
+
103
+ Assign the query result directly to a Python variable:
104
+
105
+ ```python
106
+ df = %sql SELECT country, SUM(revenue) FROM sales GROUP BY country
107
+ ```
108
+
109
+ ### Cell Magic (`%%sql`)
110
+
111
+ Execute multi-line SQL queries and capture results into a DataFrame with `-o <var_name>`:
112
+
113
+ ```python
114
+ %%sql -o sales_summary
115
+ SELECT
116
+ category,
117
+ COUNT(*) AS total_orders,
118
+ SUM(revenue) AS total_revenue
119
+ FROM sales_data
120
+ WHERE created_at >= '2026-01-01'
121
+ GROUP BY category
122
+ ORDER BY total_revenue DESC;
123
+ ```
124
+
125
+ ### SQL + Python Code Execution in a Single Cell
126
+
127
+ Combine SQL data extraction with immediate visualization. The result DataFrame is automatically made available to your Python snippet as `df`:
128
+
129
+ ```python
130
+ %%sql
131
+ SELECT category, SUM(revenue) AS total_revenue
132
+ FROM sales_data
133
+ GROUP BY category;
134
+
135
+ import matplotlib.pyplot as plt
136
+
137
+ df.plot(
138
+ x='category',
139
+ y='total_revenue',
140
+ kind='bar',
141
+ title='Total Revenue by Category',
142
+ color='skyblue',
143
+ figsize=(8, 4)
144
+ )
145
+ plt.ylabel('Revenue ($)')
146
+ plt.tight_layout()
147
+ plt.show()
148
+ ```
149
+
150
+ ---
151
+
152
+ ## 🔄 Non-Blocking Live Dashboards (`--live`)
153
+
154
+ Create real-time, auto-refreshing dashboard widgets right inside your notebook! Passing `--live <interval_seconds>` launches a background thread that periodically re-executes the query and updates the visualization **without blocking your Jupyter kernel**.
155
+
156
+ ```python
157
+ %%sql --live 2
158
+ SELECT category, SUM(revenue) AS total_revenue
159
+ FROM sales_data
160
+ GROUP BY category;
161
+
162
+ import matplotlib.pyplot as plt
163
+
164
+ df.plot(
165
+ x='category',
166
+ y='total_revenue',
167
+ kind='bar',
168
+ title='Real-Time Revenue Dashboard',
169
+ color='teal',
170
+ figsize=(8, 4)
171
+ )
172
+ plt.ylabel('Revenue ($)')
173
+ plt.tight_layout()
174
+ plt.show()
175
+ ```
176
+
177
+ ### Dashboard Widget Controls
178
+
179
+ Each live widget provides interactive buttons:
180
+ - ▶️ **Play**: Resume live auto-refresh.
181
+ - ⏸️ **Pause**: Freeze updates while keeping the widget visible.
182
+ - ⏹️ **Stop**: Terminate the background updater thread.
183
+
184
+ ### Programmatic Control API
185
+
186
+ You can also control active live widgets directly from Python cells:
187
+
188
+ ```python
189
+ esje.pause_live() # Pause all active live widgets
190
+ esje.resume_live() # Resume all live widgets
191
+ esje.stop_live() # Stop a specific live widget by ID
192
+ esje.stop_all_live() # Stop all running background widgets
193
+ ```
194
+
195
+ ---
196
+
197
+ ## 🔑 Credential Resolution Order
198
+
199
+ When calling `esje.connect_mysql()`, credentials are automatically resolved in the following priority order:
200
+
201
+ 1. **Explicit Parameters**: Arguments passed directly to `esje.connect_mysql(host=..., user=..., password=...)`.
202
+ 2. **Environment File (`.env`)**: Variables defined in a local `.env` file (`ESJE_MYSQL_HOST`, `ESJE_MYSQL_USER`, `ESJE_MYSQL_PASSWORD`, `ESJE_MYSQL_DATABASE`, `ESJE_MYSQL_PORT`).
203
+ 3. **OS Environment Variables**: System environment variables set in shell context.
204
+ 4. **Interactive `getpass` Prompts**: Secure interactive prompts for missing credentials without echoing inputs.
205
+
206
+ ---
207
+
208
+ ## ⚙️ Configuration Options
209
+
210
+ Tune `esje` settings globally via `esje.config`:
211
+
212
+ ```python
213
+ import esje
214
+
215
+ # Limit max table rows displayed in HTML output (default: 100)
216
+ esje.config.max_display_rows = 50
217
+
218
+ # Enable verbose Python tracebacks for debugging (default: False)
219
+ esje.config.verbose_errors = True
220
+
221
+ # Enable PyArrow backend for faster queries (default: True if pyarrow is installed)
222
+ esje.config.use_pyarrow = True
223
+
224
+ # Auto-commit DML statements (default: True)
225
+ esje.config.auto_commit = True
226
+ ```
227
+
228
+ ---
229
+
230
+ ## 🔌 Connection Management
231
+
232
+ List, switch, and close active database connections:
233
+
234
+ ```python
235
+ # List all active connections in a pandas DataFrame
236
+ esje.connections()
237
+
238
+ # Switch the default active connection for %sql magics
239
+ esje.use("analytics")
240
+
241
+ # Close a specific connection
242
+ esje.close("analytics")
243
+
244
+ # Close all connections and stop all live widgets
245
+ esje.close_all()
246
+ ```
247
+
248
+ ---
249
+
250
+ ## 📄 License
251
+
252
+ Distributed under the [MIT License](https://opensource.org/licenses/MIT).
esje-0.1.0/README.md ADDED
@@ -0,0 +1,227 @@
1
+ # `esje` — Credential-Safe SQL Magic & Live Dashboards for Jupyter
2
+
3
+ [![PyPI Version](https://img.shields.io/pypi/v/esje.svg)](https://pypi.org/project/esje/)
4
+ [![Python Versions](https://img.shields.io/pypi/pyversions/esje.svg)](https://pypi.org/project/esje/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![Framework: IPython](https://img.shields.io/badge/Framework-IPython-blue.svg)](https://ipython.org)
7
+
8
+ `esje` brings powerful, credential-safe `%sql` and `%%sql` magics to Jupyter Notebooks and JupyterLab. Designed for data analysts and engineers, it eliminates hardcoded secrets in `.ipynb` files, seamlessly executes SQL alongside Python visualization code, and provides non-blocking auto-refreshing `--live` dashboards with Play/Pause/Stop controls.
9
+
10
+ ---
11
+
12
+ ## ✨ Features
13
+
14
+ - 🔒 **Zero Hardcoded Secrets**: Interactive `getpass` prompts and automatic `.env` / environment variable fallbacks prevent password leaks in notebook cells, git commits, or exports.
15
+ - ⚡ **PyArrow High-Performance Backend**: Optional PyArrow data type integration for memory-efficient and fast query execution on large datasets.
16
+ - 📊 **SQL + Python Inline Execution**: Write SQL queries and Python plotting code (`matplotlib`, `seaborn`, `plotly`) in the exact same `%%sql` cell.
17
+ - ⏱️ **Non-Blocking `--live` Dashboards**: Run queries on an auto-refresh timer without blocking the Jupyter kernel execution thread. Includes interactive Play/Pause/Stop widget controls.
18
+ - 🔌 **Named Connection Registry**: Connect to multiple databases and switch between them effortlessly using `-c <conn_name>` or `esje.use()`.
19
+ - 🛡️ **Clean Exception Handling**: Friendly, concise error messages by default without distracting multi-page Python tracebacks.
20
+
21
+ ---
22
+
23
+ ## 📦 Installation
24
+
25
+ Install `esje` via `pip`:
26
+
27
+ ```bash
28
+ pip install esje
29
+ ```
30
+
31
+ For high-performance PyArrow data type acceleration, install with the optional `pyarrow` extra:
32
+
33
+ ```bash
34
+ pip install "esje[pyarrow]"
35
+ ```
36
+
37
+ ---
38
+
39
+ ## 🚀 Quickstart
40
+
41
+ ### 1. Load the Extension
42
+
43
+ In your Jupyter Notebook, load `esje`:
44
+
45
+ ```python
46
+ %load_ext esje
47
+ ```
48
+
49
+ ### 2. Connect to MySQL
50
+
51
+ Connect interactively (you will be prompted securely for any missing credentials):
52
+
53
+ ```python
54
+ import esje
55
+
56
+ # Prompts for host, user, password, database if not found in .env or environment
57
+ conn = esje.connect_mysql()
58
+ ```
59
+
60
+ Or connect with a named connection:
61
+
62
+ ```python
63
+ esje.connect_mysql(name="analytics", database="sales_db")
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 💡 Usage Examples
69
+
70
+ ### Line Magic (`%sql`)
71
+
72
+ Run a quick one-liner SQL query:
73
+
74
+ ```python
75
+ %sql SELECT * FROM users LIMIT 5
76
+ ```
77
+
78
+ Assign the query result directly to a Python variable:
79
+
80
+ ```python
81
+ df = %sql SELECT country, SUM(revenue) FROM sales GROUP BY country
82
+ ```
83
+
84
+ ### Cell Magic (`%%sql`)
85
+
86
+ Execute multi-line SQL queries and capture results into a DataFrame with `-o <var_name>`:
87
+
88
+ ```python
89
+ %%sql -o sales_summary
90
+ SELECT
91
+ category,
92
+ COUNT(*) AS total_orders,
93
+ SUM(revenue) AS total_revenue
94
+ FROM sales_data
95
+ WHERE created_at >= '2026-01-01'
96
+ GROUP BY category
97
+ ORDER BY total_revenue DESC;
98
+ ```
99
+
100
+ ### SQL + Python Code Execution in a Single Cell
101
+
102
+ Combine SQL data extraction with immediate visualization. The result DataFrame is automatically made available to your Python snippet as `df`:
103
+
104
+ ```python
105
+ %%sql
106
+ SELECT category, SUM(revenue) AS total_revenue
107
+ FROM sales_data
108
+ GROUP BY category;
109
+
110
+ import matplotlib.pyplot as plt
111
+
112
+ df.plot(
113
+ x='category',
114
+ y='total_revenue',
115
+ kind='bar',
116
+ title='Total Revenue by Category',
117
+ color='skyblue',
118
+ figsize=(8, 4)
119
+ )
120
+ plt.ylabel('Revenue ($)')
121
+ plt.tight_layout()
122
+ plt.show()
123
+ ```
124
+
125
+ ---
126
+
127
+ ## 🔄 Non-Blocking Live Dashboards (`--live`)
128
+
129
+ Create real-time, auto-refreshing dashboard widgets right inside your notebook! Passing `--live <interval_seconds>` launches a background thread that periodically re-executes the query and updates the visualization **without blocking your Jupyter kernel**.
130
+
131
+ ```python
132
+ %%sql --live 2
133
+ SELECT category, SUM(revenue) AS total_revenue
134
+ FROM sales_data
135
+ GROUP BY category;
136
+
137
+ import matplotlib.pyplot as plt
138
+
139
+ df.plot(
140
+ x='category',
141
+ y='total_revenue',
142
+ kind='bar',
143
+ title='Real-Time Revenue Dashboard',
144
+ color='teal',
145
+ figsize=(8, 4)
146
+ )
147
+ plt.ylabel('Revenue ($)')
148
+ plt.tight_layout()
149
+ plt.show()
150
+ ```
151
+
152
+ ### Dashboard Widget Controls
153
+
154
+ Each live widget provides interactive buttons:
155
+ - ▶️ **Play**: Resume live auto-refresh.
156
+ - ⏸️ **Pause**: Freeze updates while keeping the widget visible.
157
+ - ⏹️ **Stop**: Terminate the background updater thread.
158
+
159
+ ### Programmatic Control API
160
+
161
+ You can also control active live widgets directly from Python cells:
162
+
163
+ ```python
164
+ esje.pause_live() # Pause all active live widgets
165
+ esje.resume_live() # Resume all live widgets
166
+ esje.stop_live() # Stop a specific live widget by ID
167
+ esje.stop_all_live() # Stop all running background widgets
168
+ ```
169
+
170
+ ---
171
+
172
+ ## 🔑 Credential Resolution Order
173
+
174
+ When calling `esje.connect_mysql()`, credentials are automatically resolved in the following priority order:
175
+
176
+ 1. **Explicit Parameters**: Arguments passed directly to `esje.connect_mysql(host=..., user=..., password=...)`.
177
+ 2. **Environment File (`.env`)**: Variables defined in a local `.env` file (`ESJE_MYSQL_HOST`, `ESJE_MYSQL_USER`, `ESJE_MYSQL_PASSWORD`, `ESJE_MYSQL_DATABASE`, `ESJE_MYSQL_PORT`).
178
+ 3. **OS Environment Variables**: System environment variables set in shell context.
179
+ 4. **Interactive `getpass` Prompts**: Secure interactive prompts for missing credentials without echoing inputs.
180
+
181
+ ---
182
+
183
+ ## ⚙️ Configuration Options
184
+
185
+ Tune `esje` settings globally via `esje.config`:
186
+
187
+ ```python
188
+ import esje
189
+
190
+ # Limit max table rows displayed in HTML output (default: 100)
191
+ esje.config.max_display_rows = 50
192
+
193
+ # Enable verbose Python tracebacks for debugging (default: False)
194
+ esje.config.verbose_errors = True
195
+
196
+ # Enable PyArrow backend for faster queries (default: True if pyarrow is installed)
197
+ esje.config.use_pyarrow = True
198
+
199
+ # Auto-commit DML statements (default: True)
200
+ esje.config.auto_commit = True
201
+ ```
202
+
203
+ ---
204
+
205
+ ## 🔌 Connection Management
206
+
207
+ List, switch, and close active database connections:
208
+
209
+ ```python
210
+ # List all active connections in a pandas DataFrame
211
+ esje.connections()
212
+
213
+ # Switch the default active connection for %sql magics
214
+ esje.use("analytics")
215
+
216
+ # Close a specific connection
217
+ esje.close("analytics")
218
+
219
+ # Close all connections and stop all live widgets
220
+ esje.close_all()
221
+ ```
222
+
223
+ ---
224
+
225
+ ## 📄 License
226
+
227
+ Distributed under the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,128 @@
1
+ """esje: SQL Magic for Jupyter Notebooks."""
2
+
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ import pandas as pd
6
+
7
+ from esje.config import config
8
+ from esje.connection import Connection, manager
9
+ from esje.drivers.mysql import MySQLDriver
10
+ from esje.errors import ConnectionError, EsjeError, QueryError
11
+ from esje.extension import load_ipython_extension, unload_ipython_extension
12
+ from esje.prompts import resolve_mysql_credentials
13
+
14
+ from esje.live import live_manager
15
+
16
+ __version__ = "0.1.0"
17
+
18
+
19
+ def pause_live(widget_id: Optional[str] = None) -> None:
20
+ """Pause live auto-refresh dashboard widget(s)."""
21
+ live_manager.pause(widget_id)
22
+
23
+
24
+ def resume_live(widget_id: Optional[str] = None) -> None:
25
+ """Resume live auto-refresh dashboard widget(s)."""
26
+ live_manager.resume(widget_id)
27
+
28
+
29
+ def stop_live(widget_id: Optional[str] = None) -> None:
30
+ """Stop live auto-refresh dashboard widget(s)."""
31
+ live_manager.stop(widget_id)
32
+
33
+
34
+ def stop_all_live() -> None:
35
+ """Stop all active live dashboard widgets."""
36
+ live_manager.stop()
37
+
38
+
39
+ def connect_mysql(
40
+ name: str = "default",
41
+ host: Optional[str] = None,
42
+ port: Optional[int] = None,
43
+ user: Optional[str] = None,
44
+ password: Optional[str] = None,
45
+ database: Optional[str] = None,
46
+ interactive_prompt: Optional[bool] = None,
47
+ custom_engine: Any = None,
48
+ ) -> Connection:
49
+ """Connect to a MySQL database and store in connection registry."""
50
+ creds = resolve_mysql_credentials(
51
+ host=host,
52
+ port=port,
53
+ user=user,
54
+ password=password,
55
+ database=database,
56
+ interactive_prompt=interactive_prompt,
57
+ )
58
+
59
+ driver = MySQLDriver(
60
+ host=creds["host"],
61
+ port=creds["port"],
62
+ user=creds["user"],
63
+ password=creds["password"],
64
+ database=creds["database"],
65
+ custom_engine=custom_engine,
66
+ )
67
+ driver.connect()
68
+
69
+ conn = Connection(name=name, driver=driver)
70
+ manager.add(conn)
71
+ print(f"Connected to MySQL on {creds['host']}:{creds['port']} as connection '{name}'.")
72
+ return conn
73
+
74
+
75
+ def connect(dialect: str = "mysql", **kwargs: Any) -> Connection:
76
+ """Generic connection helper dispatching to dialect-specific connector."""
77
+ if dialect.lower() == "mysql":
78
+ return connect_mysql(**kwargs)
79
+ else:
80
+ raise ConnectionError(
81
+ f"Unsupported dialect '{dialect}' in v1. Currently supported: 'mysql'.",
82
+ hint="PostgreSQL support planned for v2.",
83
+ )
84
+
85
+
86
+ def use(name: str) -> str:
87
+ """Set the active connection name for %sql commands."""
88
+ return manager.use(name)
89
+
90
+
91
+ def connections() -> pd.DataFrame:
92
+ """Return DataFrame listing all open connections."""
93
+ conns = manager.list_connections()
94
+ if not conns:
95
+ return pd.DataFrame(columns=["name", "active", "dialect", "host", "port", "user", "database"])
96
+ return pd.DataFrame(conns)
97
+
98
+
99
+ def close(name: str) -> str:
100
+ """Close connection by name."""
101
+ return manager.close(name)
102
+
103
+
104
+ def close_all() -> None:
105
+ """Close all open connections."""
106
+ live_manager.stop()
107
+ manager.close_all()
108
+
109
+
110
+ __all__ = [
111
+ "connect_mysql",
112
+ "connect",
113
+ "use",
114
+ "connections",
115
+ "close",
116
+ "close_all",
117
+ "pause_live",
118
+ "resume_live",
119
+ "stop_live",
120
+ "stop_all_live",
121
+ "config",
122
+ "load_ipython_extension",
123
+ "unload_ipython_extension",
124
+ "EsjeError",
125
+ "ConnectionError",
126
+ "QueryError",
127
+ ]
128
+
@@ -0,0 +1,30 @@
1
+ """Configuration options for esje."""
2
+
3
+
4
+ class Config:
5
+ """Global configuration settings for esje."""
6
+
7
+ def __init__(self) -> None:
8
+ self.verbose_errors: bool = False
9
+ self.max_display_rows: int = 20
10
+ self.auto_commit: bool = True
11
+ self.use_pyarrow: bool = False
12
+
13
+ def reset(self) -> None:
14
+ """Reset configuration to default values."""
15
+ self.verbose_errors = False
16
+ self.max_display_rows = 20
17
+ self.auto_commit = True
18
+ self.use_pyarrow = False
19
+
20
+ def __repr__(self) -> str:
21
+ return (
22
+ f"Config(verbose_errors={self.verbose_errors}, "
23
+ f"max_display_rows={self.max_display_rows}, "
24
+ f"auto_commit={self.auto_commit}, "
25
+ f"use_pyarrow={self.use_pyarrow})"
26
+ )
27
+
28
+
29
+
30
+ config = Config()