pycharting 0.2.6__tar.gz → 0.2.8__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.
- {pycharting-0.2.6 → pycharting-0.2.8}/PKG-INFO +10 -1
- {pycharting-0.2.6 → pycharting-0.2.8}/README.md +9 -0
- {pycharting-0.2.6 → pycharting-0.2.8}/pyproject.toml +4 -8
- pycharting-0.2.8/src/pycharting/__init__.py +49 -0
- {pycharting-0.2.6/src → pycharting-0.2.8/src/pycharting}/api/interface.py +90 -43
- {pycharting-0.2.6/src → pycharting-0.2.8/src/pycharting}/api/routes.py +50 -29
- {pycharting-0.2.6/src → pycharting-0.2.8/src/pycharting}/core/lifecycle.py +53 -17
- {pycharting-0.2.6/src → pycharting-0.2.8/src/pycharting}/core/server.py +76 -19
- {pycharting-0.2.6/src → pycharting-0.2.8/src/pycharting}/web/static/js/chart.js +317 -317
- {pycharting-0.2.6/src → pycharting-0.2.8/src/pycharting}/web/static/js/viewport.js +389 -389
- pycharting-0.2.6/src/pycharting/__init__.py +0 -17
- {pycharting-0.2.6/src → pycharting-0.2.8/src/pycharting}/api/__init__.py +0 -0
- {pycharting-0.2.6/src → pycharting-0.2.8/src/pycharting}/core/__init__.py +0 -0
- {pycharting-0.2.6/src → pycharting-0.2.8/src/pycharting}/web/__init__.py +0 -0
- {pycharting-0.2.6/src → pycharting-0.2.8/src/pycharting}/web/static/demo.html +0 -0
- {pycharting-0.2.6/src → pycharting-0.2.8/src/pycharting}/web/static/js/sync.js +0 -0
- {pycharting-0.2.6/src → pycharting-0.2.8/src/pycharting}/web/static/multi-chart-demo.html +0 -0
- {pycharting-0.2.6/src → pycharting-0.2.8/src/pycharting}/web/static/viewport-demo.html +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pycharting
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: High-performance financial charting library for OHLC data visualization with technical indicators
|
|
5
5
|
Home-page: https://github.com/alihaskar/pycharting
|
|
6
6
|
License: MIT
|
|
@@ -27,6 +27,10 @@ Description-Content-Type: text/markdown
|
|
|
27
27
|
|
|
28
28
|
# PyCharting
|
|
29
29
|
|
|
30
|
+
[](https://pypi.org/project/pycharting/)
|
|
31
|
+
[](https://pypi.org/project/pycharting/)
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
|
|
30
34
|
High‑performance financial charting library for OHLC data visualization with technical indicators.
|
|
31
35
|
|
|
32
36
|
## Overview
|
|
@@ -78,6 +82,11 @@ poetry install
|
|
|
78
82
|
## Quick start
|
|
79
83
|
|
|
80
84
|
The primary API is a single `plot` function that takes OHLC arrays (plus optional overlays and subplots), starts a local server, and opens your default browser on the interactive chart.
|
|
85
|
+
You normally import everything you need like this:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from pycharting import plot, stop_server, get_server_status
|
|
89
|
+
```
|
|
81
90
|
|
|
82
91
|
When you run this script, PyCharting will:
|
|
83
92
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# PyCharting
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/pycharting/)
|
|
4
|
+
[](https://pypi.org/project/pycharting/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
3
7
|
High‑performance financial charting library for OHLC data visualization with technical indicators.
|
|
4
8
|
|
|
5
9
|
## Overview
|
|
@@ -51,6 +55,11 @@ poetry install
|
|
|
51
55
|
## Quick start
|
|
52
56
|
|
|
53
57
|
The primary API is a single `plot` function that takes OHLC arrays (plus optional overlays and subplots), starts a local server, and opens your default browser on the interactive chart.
|
|
58
|
+
You normally import everything you need like this:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from pycharting import plot, stop_server, get_server_status
|
|
62
|
+
```
|
|
54
63
|
|
|
55
64
|
When you run this script, PyCharting will:
|
|
56
65
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pycharting"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.8"
|
|
4
4
|
description = "High-performance financial charting library for OHLC data visualization with technical indicators"
|
|
5
5
|
authors = ["ali askar <26202651+alihaskar@users.noreply.github.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -19,13 +19,9 @@ classifiers = [
|
|
|
19
19
|
"Topic :: Scientific/Engineering :: Visualization",
|
|
20
20
|
]
|
|
21
21
|
|
|
22
|
-
# expose the public package as `pycharting`
|
|
22
|
+
# expose the public package as `pycharting`
|
|
23
23
|
packages = [
|
|
24
|
-
{ include = "pycharting", from = "src" }
|
|
25
|
-
{ include = "api", from = "src" },
|
|
26
|
-
{ include = "core", from = "src" },
|
|
27
|
-
{ include = "data", from = "src" },
|
|
28
|
-
{ include = "web", from = "src" },
|
|
24
|
+
{ include = "pycharting", from = "src" }
|
|
29
25
|
]
|
|
30
26
|
|
|
31
27
|
[tool.poetry.dependencies]
|
|
@@ -43,4 +39,4 @@ httpx = ">=0.27.0,<0.29.0"
|
|
|
43
39
|
|
|
44
40
|
[build-system]
|
|
45
41
|
requires = ["poetry-core>=1.9.0"]
|
|
46
|
-
build-backend = "poetry.core.masonry.api"
|
|
42
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyCharting: Interactive Financial Charting Library.
|
|
3
|
+
|
|
4
|
+
This package provides a high-performance, interactive charting solution for financial data
|
|
5
|
+
(OHLC), designed to handle large datasets efficiently. It uses a local server architecture
|
|
6
|
+
to render charts in the web browser, allowing for smooth zooming, panning, and analysis.
|
|
7
|
+
|
|
8
|
+
Key Features:
|
|
9
|
+
- **High Performance:** Capable of handling millions of data points using efficient data slicing.
|
|
10
|
+
- **Interactive:** Zoom, pan, and inspect data in real-time.
|
|
11
|
+
- **Flexible:** Support for overlays (e.g., Moving Averages) and subplots (e.g., RSI, Volume).
|
|
12
|
+
- **Easy to Use:** Simple Python API similar to matplotlib or plotly.
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
The main entry point is the `plot` function.
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from pycharting import plot, stop_server
|
|
19
|
+
import numpy as np
|
|
20
|
+
|
|
21
|
+
# Prepare your data (numpy arrays or pandas Series)
|
|
22
|
+
index = np.arange(100)
|
|
23
|
+
open_data = np.random.rand(100) + 100
|
|
24
|
+
high_data = open_data + 1
|
|
25
|
+
low_data = open_data - 1
|
|
26
|
+
close_data = open_data + 0.5
|
|
27
|
+
|
|
28
|
+
# Create and open the chart
|
|
29
|
+
plot(index, open_data, high_data, low_data, close_data)
|
|
30
|
+
|
|
31
|
+
# ... keep the script running if needed ...
|
|
32
|
+
# input("Press Enter to stop...")
|
|
33
|
+
# stop_server()
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Exports:
|
|
37
|
+
- `plot`: Main function to create and display charts.
|
|
38
|
+
- `stop_server`: Function to gracefully shut down the local chart server.
|
|
39
|
+
- `get_server_status`: Function to check the status of the background server.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from typing import Any, Dict # re-exported types are only for type checkers
|
|
43
|
+
|
|
44
|
+
from .api.interface import plot, stop_server, get_server_status # type: ignore F401
|
|
45
|
+
|
|
46
|
+
__all__ = ["plot", "stop_server", "get_server_status", "__version__"]
|
|
47
|
+
|
|
48
|
+
# Keep this in sync with pyproject.toml
|
|
49
|
+
__version__ = "0.2.8"
|
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Main User Interface for PyCharting.
|
|
3
|
+
|
|
4
|
+
This module exposes the high-level functions that users interact with to create charts,
|
|
5
|
+
manage the server, and check status. It bridges the gap between the user's data (numpy/pandas)
|
|
6
|
+
and the internal server/data management logic.
|
|
7
|
+
|
|
8
|
+
The primary function is `plot()`, which orchestrates:
|
|
9
|
+
1. Validating and ingesting user data via `DataManager`.
|
|
10
|
+
2. Starting (or reusing) the background `ChartServer`.
|
|
11
|
+
3. Constructing the visualization URL.
|
|
12
|
+
4. Opening the chart in the user's default web browser.
|
|
13
|
+
|
|
14
|
+
It also provides utilities for manual server control (`stop_server`, `get_server_status`)
|
|
15
|
+
and Jupyter notebook integration.
|
|
16
|
+
"""
|
|
2
17
|
|
|
3
18
|
import webbrowser
|
|
4
19
|
import time
|
|
@@ -7,9 +22,9 @@ from typing import Optional, Dict, Any, Union
|
|
|
7
22
|
import numpy as np
|
|
8
23
|
import pandas as pd
|
|
9
24
|
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
from .routes import _data_managers
|
|
25
|
+
from pycharting.data.ingestion import DataManager
|
|
26
|
+
from pycharting.core.lifecycle import ChartServer
|
|
27
|
+
from pycharting.api.routes import _data_managers
|
|
13
28
|
|
|
14
29
|
logger = logging.getLogger(__name__)
|
|
15
30
|
|
|
@@ -31,42 +46,65 @@ def plot(
|
|
|
31
46
|
server_timeout: float = 2.0,
|
|
32
47
|
) -> Dict[str, Any]:
|
|
33
48
|
"""
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
This is the
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
Generate and display an interactive OHLC (Open-High-Low-Close) financial chart.
|
|
50
|
+
|
|
51
|
+
This function is the primary interface for PyCharting. It performs the following steps:
|
|
52
|
+
1. **Data Ingestion:** Converts input lists, pandas Series, or numpy arrays into optimized internal formats.
|
|
53
|
+
2. **Server Management:** Checks if a background chart server is running. If not, it starts one on a separate thread.
|
|
54
|
+
3. **Session Registration:** Stores the provided data under a `session_id`. This allows multiple charts to coexist
|
|
55
|
+
or data to be updated.
|
|
56
|
+
4. **Browser Launch:** Automatically opens the default web browser to the generated chart URL.
|
|
57
|
+
|
|
58
|
+
The chart is rendered using a high-performance web frontend capable of handling millions of data points via
|
|
59
|
+
dynamic data slicing.
|
|
60
|
+
|
|
39
61
|
Args:
|
|
40
|
-
index:
|
|
41
|
-
open: Opening prices
|
|
42
|
-
high:
|
|
43
|
-
low:
|
|
44
|
-
close: Closing prices
|
|
45
|
-
overlays
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
62
|
+
index (Union[np.ndarray, pd.Series, list]): The x-axis data (timestamps or integer indices). Must have the same length as price arrays.
|
|
63
|
+
open (Union[np.ndarray, pd.Series, list]): Opening prices.
|
|
64
|
+
high (Union[np.ndarray, pd.Series, list]): Highest prices during the interval.
|
|
65
|
+
low (Union[np.ndarray, pd.Series, list]): Lowest prices during the interval.
|
|
66
|
+
close (Union[np.ndarray, pd.Series, list]): Closing prices.
|
|
67
|
+
overlays (Optional[Dict[str, Union[np.ndarray, pd.Series, list]]]): A dictionary of additional series to plot *over* the main price chart.
|
|
68
|
+
Keys are labels (e.g., "SMA 50"), values are data arrays. Useful for Moving Averages, Bollinger Bands, etc.
|
|
69
|
+
subplots (Optional[Dict[str, Union[np.ndarray, pd.Series, list]]]): A dictionary of series to plot in separate panels *below* the main chart.
|
|
70
|
+
Keys are labels (e.g., "RSI", "Volume"), values are data arrays.
|
|
71
|
+
session_id (str): A unique identifier for this dataset. Use different IDs to keep multiple charts active simultaneously.
|
|
72
|
+
Defaults to "default".
|
|
73
|
+
port (Optional[int]): Specific port to run the server on. If `None` (default), a free port is automatically found.
|
|
74
|
+
open_browser (bool): If `True` (default), automatically launches the system's default web browser to view the chart.
|
|
75
|
+
server_timeout (float): Maximum time (in seconds) to wait for the server to start before proceeding. Defaults to 2.0.
|
|
76
|
+
|
|
52
77
|
Returns:
|
|
53
|
-
Dict
|
|
54
|
-
|
|
78
|
+
Dict[str, Any]: A dictionary containing execution details:
|
|
79
|
+
- `status`: "success" or "error".
|
|
80
|
+
- `url`: The full URL to view the chart.
|
|
81
|
+
- `server_url`: The base URL of the server.
|
|
82
|
+
- `session_id`: The session ID used.
|
|
83
|
+
- `data_points`: Number of data points loaded.
|
|
84
|
+
- `server_running`: Boolean indicating if the server is active.
|
|
85
|
+
|
|
55
86
|
Example:
|
|
56
87
|
```python
|
|
57
88
|
import numpy as np
|
|
58
89
|
from pycharting import plot
|
|
59
|
-
|
|
60
|
-
#
|
|
61
|
-
n =
|
|
90
|
+
|
|
91
|
+
# 1. Prepare Data
|
|
92
|
+
n = 1000
|
|
62
93
|
index = np.arange(n)
|
|
63
94
|
close = np.cumsum(np.random.randn(n)) + 100
|
|
64
|
-
|
|
65
|
-
high = np.maximum(
|
|
66
|
-
low = np.minimum(
|
|
95
|
+
open_p = close + np.random.randn(n) * 0.5
|
|
96
|
+
high = np.maximum(open_p, close) + np.abs(np.random.randn(n))
|
|
97
|
+
low = np.minimum(open_p, close) - np.abs(np.random.randn(n))
|
|
67
98
|
|
|
68
|
-
#
|
|
69
|
-
|
|
99
|
+
# 2. Add Indicators
|
|
100
|
+
sma = np.convolve(close, np.ones(20)/20, mode='same')
|
|
101
|
+
|
|
102
|
+
# 3. Plot with Overlay
|
|
103
|
+
plot(
|
|
104
|
+
index, open_p, high, low, close,
|
|
105
|
+
overlays={"SMA 20": sma},
|
|
106
|
+
session_id="my-analysis"
|
|
107
|
+
)
|
|
70
108
|
```
|
|
71
109
|
"""
|
|
72
110
|
global _active_server
|
|
@@ -177,16 +215,19 @@ def plot(
|
|
|
177
215
|
|
|
178
216
|
def stop_server():
|
|
179
217
|
"""
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
218
|
+
Manually shut down the active chart server.
|
|
219
|
+
|
|
220
|
+
While the server has an auto-shutdown feature (triggered after a timeout when no clients are connected),
|
|
221
|
+
this function allows for immediate, manual cleanup. This is useful in scripts or notebooks where you want
|
|
222
|
+
to ensure resources are freed immediately after a session.
|
|
223
|
+
|
|
224
|
+
If no server is running, this function does nothing and prints a message.
|
|
225
|
+
|
|
185
226
|
Example:
|
|
186
227
|
```python
|
|
187
228
|
from pycharting import stop_server
|
|
188
|
-
|
|
189
|
-
#
|
|
229
|
+
|
|
230
|
+
# ... after done with analysis ...
|
|
190
231
|
stop_server()
|
|
191
232
|
```
|
|
192
233
|
"""
|
|
@@ -202,17 +243,23 @@ def stop_server():
|
|
|
202
243
|
|
|
203
244
|
def get_server_status() -> Dict[str, Any]:
|
|
204
245
|
"""
|
|
205
|
-
|
|
206
|
-
|
|
246
|
+
Retrieve the current status of the background chart server.
|
|
247
|
+
|
|
248
|
+
This is useful for debugging connection issues or checking if a session is still active.
|
|
249
|
+
|
|
207
250
|
Returns:
|
|
208
|
-
Dict
|
|
209
|
-
|
|
251
|
+
Dict[str, Any]: A dictionary containing:
|
|
252
|
+
- `running`: Boolean indicating if the server process is alive.
|
|
253
|
+
- `server_info`: Dictionary of host, port, and connection details (or None).
|
|
254
|
+
- `active_sessions`: Count of currently loaded datasets/sessions.
|
|
255
|
+
|
|
210
256
|
Example:
|
|
211
257
|
```python
|
|
212
258
|
from pycharting import get_server_status
|
|
213
259
|
|
|
214
260
|
status = get_server_status()
|
|
215
|
-
|
|
261
|
+
if status['running']:
|
|
262
|
+
print(f"Server running at {status['server_info']['url']}")
|
|
216
263
|
```
|
|
217
264
|
"""
|
|
218
265
|
global _active_server
|
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
API Routes Definition for PyCharting.
|
|
3
|
+
|
|
4
|
+
This module defines the REST API endpoints that the frontend JavaScript uses to:
|
|
5
|
+
1. Fetch sliced and diced OHLC data (`/data`).
|
|
6
|
+
2. Manage data sessions (`/sessions`).
|
|
7
|
+
3. Check system status (`/status`).
|
|
8
|
+
4. Initialize demo data (`/data/init`).
|
|
9
|
+
|
|
10
|
+
The data is served from the in-memory `_data_managers` registry, which is populated
|
|
11
|
+
by the main Python process via `src.api.interface.plot()`.
|
|
12
|
+
"""
|
|
2
13
|
|
|
3
14
|
from typing import Optional, Dict, Any
|
|
4
15
|
from fastapi import APIRouter, HTTPException, Query
|
|
@@ -42,18 +53,25 @@ async def get_data(
|
|
|
42
53
|
session_id: str = Query("default", description="Session identifier for data source"),
|
|
43
54
|
):
|
|
44
55
|
"""
|
|
45
|
-
|
|
46
|
-
|
|
56
|
+
Retrieve a specific slice of OHLC data.
|
|
57
|
+
|
|
58
|
+
This endpoint is optimized for high-performance frontend rendering. Instead of sending the full dataset
|
|
59
|
+
at once (which could be millions of points), the frontend requests only the necessary chunk
|
|
60
|
+
based on the current zoom level and viewport.
|
|
61
|
+
|
|
47
62
|
Args:
|
|
48
|
-
start_index:
|
|
49
|
-
end_index:
|
|
50
|
-
|
|
51
|
-
|
|
63
|
+
start_index (int): The zero-based index of the first data point to retrieve.
|
|
64
|
+
end_index (Optional[int]): The zero-based index of the last data point (exclusive).
|
|
65
|
+
If None, retrieves data until the end of the series.
|
|
66
|
+
session_id (str): The ID of the data session to query.
|
|
67
|
+
|
|
52
68
|
Returns:
|
|
53
|
-
DataResponse
|
|
54
|
-
|
|
69
|
+
DataResponse: A JSON object containing parallel arrays for index, open, high, low, close,
|
|
70
|
+
overlays, and subplots for the requested range.
|
|
71
|
+
|
|
55
72
|
Raises:
|
|
56
|
-
HTTPException: If
|
|
73
|
+
HTTPException(404): If the specified session_id does not exist.
|
|
74
|
+
HTTPException(500): If an internal error occurs during data slicing.
|
|
57
75
|
"""
|
|
58
76
|
# Check if session exists
|
|
59
77
|
if session_id not in _data_managers:
|
|
@@ -91,19 +109,19 @@ async def initialize_data(
|
|
|
91
109
|
session_id: str = Query("default", description="Session identifier"),
|
|
92
110
|
):
|
|
93
111
|
"""
|
|
94
|
-
Initialize a data session
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
112
|
+
Initialize a demo data session.
|
|
113
|
+
|
|
114
|
+
This endpoint is primarily used for testing or the standalone demo mode.
|
|
115
|
+
It generates synthetic random walk data and registers it under the given session ID.
|
|
116
|
+
|
|
99
117
|
Args:
|
|
100
|
-
session_id:
|
|
101
|
-
|
|
118
|
+
session_id (str): The ID to assign to the new session.
|
|
119
|
+
|
|
102
120
|
Returns:
|
|
103
|
-
|
|
121
|
+
dict: Status message and session details.
|
|
104
122
|
"""
|
|
105
123
|
import numpy as np
|
|
106
|
-
from
|
|
124
|
+
from pycharting.data.ingestion import DataManager
|
|
107
125
|
|
|
108
126
|
try:
|
|
109
127
|
# Generate demo OHLC data
|
|
@@ -163,10 +181,11 @@ async def initialize_data(
|
|
|
163
181
|
@router.get("/sessions")
|
|
164
182
|
async def list_sessions():
|
|
165
183
|
"""
|
|
166
|
-
List active data sessions.
|
|
167
|
-
|
|
184
|
+
List all currently active data sessions.
|
|
185
|
+
|
|
168
186
|
Returns:
|
|
169
|
-
|
|
187
|
+
dict: A dictionary containing a list of session objects, each with metadata
|
|
188
|
+
like the number of data points and active features (overlays, subplots).
|
|
170
189
|
"""
|
|
171
190
|
sessions = []
|
|
172
191
|
for session_id, dm in _data_managers.items():
|
|
@@ -186,16 +205,18 @@ async def list_sessions():
|
|
|
186
205
|
@router.delete("/sessions/{session_id}")
|
|
187
206
|
async def delete_session(session_id: str):
|
|
188
207
|
"""
|
|
189
|
-
|
|
190
|
-
|
|
208
|
+
Remove a data session from memory.
|
|
209
|
+
|
|
210
|
+
This frees up resources associated with a specific dataset.
|
|
211
|
+
|
|
191
212
|
Args:
|
|
192
|
-
session_id:
|
|
193
|
-
|
|
213
|
+
session_id (str): The ID of the session to remove.
|
|
214
|
+
|
|
194
215
|
Returns:
|
|
195
|
-
|
|
196
|
-
|
|
216
|
+
dict: Confirmation message.
|
|
217
|
+
|
|
197
218
|
Raises:
|
|
198
|
-
HTTPException: If session not found
|
|
219
|
+
HTTPException(404): If the session ID is not found.
|
|
199
220
|
"""
|
|
200
221
|
if session_id not in _data_managers:
|
|
201
222
|
raise HTTPException(
|
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Server Lifecycle Management for PyCharting.
|
|
3
|
+
|
|
4
|
+
This module manages the background execution and lifecycle of the PyCharting server.
|
|
5
|
+
Since the main Python script (e.g., a data science notebook or script) needs to remain responsive,
|
|
6
|
+
the chart server runs in a separate background thread.
|
|
7
|
+
|
|
8
|
+
This module handles:
|
|
9
|
+
- **Thread Management:** Starting and stopping the server in a daemon thread.
|
|
10
|
+
- **Heartbeat Monitoring:** Checking for WebSocket connections from the frontend.
|
|
11
|
+
- **Auto-Shutdown:** Automatically stopping the server when the browser tab is closed (connection lost) to prevent orphaned processes.
|
|
12
|
+
"""
|
|
2
13
|
|
|
3
14
|
import threading
|
|
4
15
|
import time
|
|
@@ -7,17 +18,25 @@ from typing import Optional, Dict, Any
|
|
|
7
18
|
from datetime import datetime
|
|
8
19
|
import uvicorn
|
|
9
20
|
from fastapi import WebSocket, WebSocketDisconnect
|
|
10
|
-
from .server import create_app, find_free_port
|
|
21
|
+
from pycharting.core.server import create_app, find_free_port
|
|
11
22
|
|
|
12
23
|
logger = logging.getLogger(__name__)
|
|
13
24
|
|
|
14
25
|
|
|
15
26
|
class ChartServer:
|
|
16
27
|
"""
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
28
|
+
A controller for managing the PyCharting background server.
|
|
29
|
+
|
|
30
|
+
This class encapsulates the logic for running the FastAPI/Uvicorn server in a separate thread.
|
|
31
|
+
It includes a heartbeat mechanism that monitors a WebSocket connection from the frontend.
|
|
32
|
+
If the frontend disconnects (e.g., user closes the tab), the server can automatically shut down
|
|
33
|
+
after a configurable timeout.
|
|
34
|
+
|
|
35
|
+
Attributes:
|
|
36
|
+
host (str): The hostname to bind to.
|
|
37
|
+
port (int): The port to bind to.
|
|
38
|
+
auto_shutdown_timeout (float): Time in seconds to wait before shutting down after connection loss.
|
|
39
|
+
app (FastAPI): The underlying FastAPI application instance.
|
|
21
40
|
"""
|
|
22
41
|
|
|
23
42
|
def __init__(
|
|
@@ -27,12 +46,13 @@ class ChartServer:
|
|
|
27
46
|
auto_shutdown_timeout: float = 5.0,
|
|
28
47
|
):
|
|
29
48
|
"""
|
|
30
|
-
Initialize ChartServer.
|
|
31
|
-
|
|
49
|
+
Initialize the ChartServer controller.
|
|
50
|
+
|
|
32
51
|
Args:
|
|
33
|
-
host: Host to bind the server to
|
|
34
|
-
port: Port to use
|
|
35
|
-
auto_shutdown_timeout: Seconds to wait before auto-shutdown after
|
|
52
|
+
host (str): Host to bind the server to. Defaults to "127.0.0.1".
|
|
53
|
+
port (Optional[int]): Port to use. If None, an available port is found automatically.
|
|
54
|
+
auto_shutdown_timeout (float): Seconds to wait before auto-shutdown after the last client disconnects.
|
|
55
|
+
Defaults to 5.0 seconds.
|
|
36
56
|
"""
|
|
37
57
|
self.host = host
|
|
38
58
|
self.port = port or find_free_port()
|
|
@@ -122,13 +142,24 @@ class ChartServer:
|
|
|
122
142
|
|
|
123
143
|
def start_server(self) -> Dict[str, Any]:
|
|
124
144
|
"""
|
|
125
|
-
Start the server in a background thread.
|
|
126
|
-
|
|
145
|
+
Start the web server in a background daemon thread.
|
|
146
|
+
|
|
147
|
+
This method:
|
|
148
|
+
1. Checks if the server is already running.
|
|
149
|
+
2. Starts the Uvicorn server in a separate thread.
|
|
150
|
+
3. Starts a monitor thread to check for WebSocket heartbeats.
|
|
151
|
+
4. Waits briefly to ensure the server is up.
|
|
152
|
+
|
|
127
153
|
Returns:
|
|
128
|
-
Dict
|
|
129
|
-
|
|
154
|
+
Dict[str, Any]: A dictionary containing connection details:
|
|
155
|
+
- `host`: The server host.
|
|
156
|
+
- `port`: The server port.
|
|
157
|
+
- `url`: The full HTTP URL to the server.
|
|
158
|
+
- `ws_url`: The WebSocket URL for heartbeats.
|
|
159
|
+
- `running`: Boolean status.
|
|
160
|
+
|
|
130
161
|
Raises:
|
|
131
|
-
RuntimeError: If server is already running
|
|
162
|
+
RuntimeError: If the server is already running.
|
|
132
163
|
"""
|
|
133
164
|
if self._running:
|
|
134
165
|
raise RuntimeError("Server is already running")
|
|
@@ -169,7 +200,12 @@ class ChartServer:
|
|
|
169
200
|
}
|
|
170
201
|
|
|
171
202
|
def stop_server(self):
|
|
172
|
-
"""
|
|
203
|
+
"""
|
|
204
|
+
Gracefully stop the background server and monitor threads.
|
|
205
|
+
|
|
206
|
+
This method signals the server to shut down, closes the Uvicorn loop,
|
|
207
|
+
and joins the background threads. It is safe to call multiple times.
|
|
208
|
+
"""
|
|
173
209
|
if not self._running:
|
|
174
210
|
logger.warning("Server is not running")
|
|
175
211
|
return
|