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 +252 -0
- esje-0.1.0/README.md +227 -0
- esje-0.1.0/esje/__init__.py +128 -0
- esje-0.1.0/esje/config.py +30 -0
- esje-0.1.0/esje/connection.py +149 -0
- esje-0.1.0/esje/display.py +76 -0
- esje-0.1.0/esje/drivers/__init__.py +6 -0
- esje-0.1.0/esje/drivers/base.py +34 -0
- esje-0.1.0/esje/drivers/mysql.py +117 -0
- esje-0.1.0/esje/errors.py +32 -0
- esje-0.1.0/esje/extension.py +21 -0
- esje-0.1.0/esje/live.py +372 -0
- esje-0.1.0/esje/magics.py +198 -0
- esje-0.1.0/esje/prompts.py +108 -0
- esje-0.1.0/esje.egg-info/PKG-INFO +252 -0
- esje-0.1.0/esje.egg-info/SOURCES.txt +25 -0
- esje-0.1.0/esje.egg-info/dependency_links.txt +1 -0
- esje-0.1.0/esje.egg-info/requires.txt +13 -0
- esje-0.1.0/esje.egg-info/top_level.txt +1 -0
- esje-0.1.0/pyproject.toml +43 -0
- esje-0.1.0/setup.cfg +4 -0
- esje-0.1.0/tests/test_config.py +38 -0
- esje-0.1.0/tests/test_connection.py +101 -0
- esje-0.1.0/tests/test_display.py +38 -0
- esje-0.1.0/tests/test_errors.py +22 -0
- esje-0.1.0/tests/test_magics.py +117 -0
- esje-0.1.0/tests/test_prompts.py +51 -0
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
|
+
[](https://pypi.org/project/esje/)
|
|
29
|
+
[](https://pypi.org/project/esje/)
|
|
30
|
+
[](https://opensource.org/licenses/MIT)
|
|
31
|
+
[](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
|
+
[](https://pypi.org/project/esje/)
|
|
4
|
+
[](https://pypi.org/project/esje/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](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()
|