pycharting 0.2.7__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.7 → pycharting-0.2.8}/PKG-INFO +1 -1
- {pycharting-0.2.7 → pycharting-0.2.8}/pyproject.toml +4 -8
- pycharting-0.2.8/src/pycharting/__init__.py +49 -0
- {pycharting-0.2.7/src → pycharting-0.2.8/src/pycharting}/api/interface.py +90 -43
- {pycharting-0.2.7/src → pycharting-0.2.8/src/pycharting}/api/routes.py +50 -29
- {pycharting-0.2.7/src → pycharting-0.2.8/src/pycharting}/core/lifecycle.py +53 -17
- {pycharting-0.2.7/src → pycharting-0.2.8/src/pycharting}/core/server.py +76 -19
- pycharting-0.2.7/src/pycharting/__init__.py +0 -17
- {pycharting-0.2.7 → pycharting-0.2.8}/README.md +0 -0
- {pycharting-0.2.7/src → pycharting-0.2.8/src/pycharting}/api/__init__.py +0 -0
- {pycharting-0.2.7/src → pycharting-0.2.8/src/pycharting}/core/__init__.py +0 -0
- {pycharting-0.2.7/src → pycharting-0.2.8/src/pycharting}/web/__init__.py +0 -0
- {pycharting-0.2.7/src → pycharting-0.2.8/src/pycharting}/web/static/demo.html +0 -0
- {pycharting-0.2.7/src → pycharting-0.2.8/src/pycharting}/web/static/js/chart.js +0 -0
- {pycharting-0.2.7/src → pycharting-0.2.8/src/pycharting}/web/static/js/sync.js +0 -0
- {pycharting-0.2.7/src → pycharting-0.2.8/src/pycharting}/web/static/js/viewport.js +0 -0
- {pycharting-0.2.7/src → pycharting-0.2.8/src/pycharting}/web/static/multi-chart-demo.html +0 -0
- {pycharting-0.2.7/src → pycharting-0.2.8/src/pycharting}/web/static/viewport-demo.html +0 -0
|
@@ -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 data.ingestion import DataManager
|
|
11
|
-
from core.lifecycle import ChartServer
|
|
12
|
-
from api.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 data.ingestion import DataManager
|
|
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
|
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Core Server Module for PyCharting.
|
|
3
|
+
|
|
4
|
+
This module implements the FastAPI-based web server that powers the interactive charts.
|
|
5
|
+
It handles serving static assets (HTML, JS, CSS) and providing API endpoints for data retrieval.
|
|
6
|
+
The server is designed to run locally and provide a seamless bridge between the Python runtime
|
|
7
|
+
and the browser-based visualization.
|
|
8
|
+
|
|
9
|
+
Key Responsibilities:
|
|
10
|
+
- **Port Management:** Automatically finding available ports for the server.
|
|
11
|
+
- **Application Factory:** Creating and configuring the FastAPI application instance.
|
|
12
|
+
- **Static File Serving:** Serving the frontend assets required for the chart UI.
|
|
13
|
+
- **API Routing:** Connecting API routes to the application.
|
|
14
|
+
- **Server Execution:** Launching the Uvicorn server.
|
|
15
|
+
"""
|
|
2
16
|
|
|
3
17
|
import socket
|
|
4
18
|
import logging
|
|
@@ -20,17 +34,27 @@ logger = logging.getLogger(__name__)
|
|
|
20
34
|
|
|
21
35
|
def find_free_port(start_port: int = 8000, end_port: int = 9000) -> int:
|
|
22
36
|
"""
|
|
23
|
-
Find
|
|
24
|
-
|
|
37
|
+
Find an available TCP port in the specified range.
|
|
38
|
+
|
|
39
|
+
This utility function iterates through a range of ports to find one that is currently
|
|
40
|
+
not in use. This is crucial for ensuring the chart server can start without conflicts,
|
|
41
|
+
even if the default port is occupied or multiple instances are running.
|
|
42
|
+
|
|
25
43
|
Args:
|
|
26
|
-
start_port:
|
|
27
|
-
end_port:
|
|
28
|
-
|
|
44
|
+
start_port (int): The starting port number to search from (inclusive). Defaults to 8000.
|
|
45
|
+
end_port (int): The ending port number to search to (exclusive). Defaults to 9000.
|
|
46
|
+
|
|
29
47
|
Returns:
|
|
30
|
-
|
|
31
|
-
|
|
48
|
+
int: An available port number found within the range.
|
|
49
|
+
|
|
32
50
|
Raises:
|
|
33
|
-
RuntimeError: If no free port
|
|
51
|
+
RuntimeError: If no free port can be found in the specified range.
|
|
52
|
+
|
|
53
|
+
Example:
|
|
54
|
+
```python
|
|
55
|
+
port = find_free_port(8000, 8010)
|
|
56
|
+
print(f"Found free port: {port}")
|
|
57
|
+
```
|
|
34
58
|
"""
|
|
35
59
|
for port in range(start_port, end_port):
|
|
36
60
|
try:
|
|
@@ -46,10 +70,21 @@ def find_free_port(start_port: int = 8000, end_port: int = 9000) -> int:
|
|
|
46
70
|
|
|
47
71
|
def create_app() -> FastAPI:
|
|
48
72
|
"""
|
|
49
|
-
Create and configure the FastAPI application.
|
|
50
|
-
|
|
73
|
+
Create and configure the FastAPI application instance.
|
|
74
|
+
|
|
75
|
+
This factory function initializes the FastAPI app with necessary configurations:
|
|
76
|
+
- Sets up metadata (title, description, version).
|
|
77
|
+
- Configures CORS (Cross-Origin Resource Sharing) to allow local browser access.
|
|
78
|
+
- Mounts the static files directory to serve the frontend application.
|
|
79
|
+
- Configures the root endpoint to serve the main HTML entry point.
|
|
80
|
+
- Includes the API router for data endpoints.
|
|
81
|
+
- Sets up global exception handlers and health check endpoints.
|
|
82
|
+
|
|
83
|
+
The application is stateless regarding data; data is managed via the `_data_managers`
|
|
84
|
+
registry in `src.api.routes` which is accessed by the API routes included here.
|
|
85
|
+
|
|
51
86
|
Returns:
|
|
52
|
-
|
|
87
|
+
FastAPI: The fully configured FastAPI application ready to be run by Uvicorn.
|
|
53
88
|
"""
|
|
54
89
|
app = FastAPI(
|
|
55
90
|
title="PyCharting",
|
|
@@ -136,7 +171,7 @@ def create_app() -> FastAPI:
|
|
|
136
171
|
"""
|
|
137
172
|
|
|
138
173
|
# Include API routes
|
|
139
|
-
from api.routes import router as api_router
|
|
174
|
+
from pycharting.api.routes import router as api_router
|
|
140
175
|
app.include_router(api_router)
|
|
141
176
|
|
|
142
177
|
# Health check endpoint
|
|
@@ -175,13 +210,35 @@ def run_server(
|
|
|
175
210
|
reload: bool = False,
|
|
176
211
|
) -> None:
|
|
177
212
|
"""
|
|
178
|
-
|
|
179
|
-
|
|
213
|
+
Launch the PyCharting web server.
|
|
214
|
+
|
|
215
|
+
This function is the main entry point for running the server directly (e.g., for development).
|
|
216
|
+
It handles port selection, application creation, and starting the Uvicorn server process.
|
|
217
|
+
|
|
218
|
+
In the library usage context, this is typically managed by `src.core.lifecycle.ChartServer`,
|
|
219
|
+
which runs this logic in a background thread. However, this function can be used to run
|
|
220
|
+
the server in the main thread (blocking) or for testing purposes.
|
|
221
|
+
|
|
180
222
|
Args:
|
|
181
|
-
host:
|
|
182
|
-
port:
|
|
183
|
-
|
|
184
|
-
|
|
223
|
+
host (str): The hostname or IP address to bind the server to. Defaults to "127.0.0.1".
|
|
224
|
+
port (Optional[int]): The specific port to use. If `None`, a free port will be found automatically
|
|
225
|
+
unless `auto_port` is False.
|
|
226
|
+
auto_port (bool): If True (default), automatically finds an alternative free port if the specified
|
|
227
|
+
(or default) port is unavailable.
|
|
228
|
+
reload (bool): If True, enables Uvicorn's auto-reload feature. Useful for development.
|
|
229
|
+
Defaults to False.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
None: This function blocks until the server stops.
|
|
233
|
+
|
|
234
|
+
Example:
|
|
235
|
+
```python
|
|
236
|
+
# Run server on localhost, finding a free port automatically
|
|
237
|
+
run_server()
|
|
238
|
+
|
|
239
|
+
# Run on a specific port
|
|
240
|
+
run_server(port=8080)
|
|
241
|
+
```
|
|
185
242
|
"""
|
|
186
243
|
# Determine port
|
|
187
244
|
if port is None:
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"""Public package for the PyCharting library.
|
|
2
|
-
|
|
3
|
-
This module re-exports the main Python API surface so that users can do:
|
|
4
|
-
|
|
5
|
-
from pycharting import plot, stop_server, get_server_status
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from typing import Any, Dict # re-exported types are only for type checkers
|
|
9
|
-
|
|
10
|
-
from api.interface import plot, stop_server, get_server_status # type: ignore F401
|
|
11
|
-
|
|
12
|
-
__all__ = ["plot", "stop_server", "get_server_status", "__version__"]
|
|
13
|
-
|
|
14
|
-
# Keep this in sync with pyproject.toml
|
|
15
|
-
__version__ = "0.2.3"
|
|
16
|
-
|
|
17
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|