plotly-cloud 0.2.0__py3-none-any.whl → 0.2.1__py3-none-any.whl

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.
plotly_cloud/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Plotly Cloud Extension package."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.2.1"
plotly_cloud/_deploy.py CHANGED
@@ -135,9 +135,7 @@ async def create_deployment_zip(project_path: str, output_path: str) -> int:
135
135
  dirs[:] = [
136
136
  d
137
137
  for d in dirs
138
- if not should_exclude_path(
139
- str(os.path.relpath(os.path.join(root, d), project_path)), exclude_patterns
140
- )
138
+ if not should_exclude_path(str(os.path.relpath(os.path.join(root, d), project_path)), exclude_patterns)
141
139
  ]
142
140
 
143
141
  for file in files:
@@ -1,36 +1,11 @@
1
- import asyncio
2
-
3
1
  import dash
4
2
  import flask
5
- from packaging.version import parse as _parse_version
6
3
 
7
4
  from plotly_cloud._devtool_publish_rpc import PlotlyCloudPublishRPC
8
-
9
- dash_version = _parse_version(dash.__version__)
10
-
11
-
12
- def _run_sync(coro):
13
- """Run an async coroutine synchronously, handling event loop correctly for Python 3.10+."""
14
- try:
15
- # Check if there's already a running loop
16
- loop = asyncio.get_running_loop()
17
- except RuntimeError:
18
- # No running loop, try to get or create one
19
- loop = asyncio.get_event_loop()
20
- if loop is None or loop.is_closed():
21
- loop = asyncio.new_event_loop()
22
- asyncio.set_event_loop(loop)
23
-
24
- return loop.run_until_complete(coro)
5
+ from plotly_cloud._run_sync import run_sync
25
6
 
26
7
 
27
8
  def install_hook():
28
- import nest_asyncio
29
-
30
- # Make asyncio stuff runs smoothly.
31
- # Prevent no loop and existing loop errors.
32
- nest_asyncio.apply()
33
-
34
9
  rpc = PlotlyCloudPublishRPC()
35
10
 
36
11
  try:
@@ -59,7 +34,7 @@ def install_hook():
59
34
  @dash.hooks.route("_plotly_cloud_publish", methods=["POST"])
60
35
  def plotly_cloud_publish_rpc():
61
36
  data = flask.request.get_json()
62
- data = _run_sync(rpc.handle_operation(data))
37
+ data = run_sync(rpc.handle_operation(data))
63
38
  return flask.jsonify(data)
64
39
 
65
40
  @dash.hooks.setup()
@@ -4,6 +4,7 @@ import asyncio
4
4
  import importlib
5
5
  import os
6
6
  import tempfile
7
+ import traceback
7
8
  from typing import Any
8
9
 
9
10
  import dash
@@ -167,6 +168,7 @@ class PlotlyCloudPublishRPC:
167
168
  method = getattr(self, operation_name)
168
169
  return await method(data)
169
170
  except Exception as e:
171
+ traceback.print_exc()
170
172
  return {"error": str(e)}
171
173
 
172
174
  async def initialize(self, data: Any) -> RPCResponse:
@@ -0,0 +1,34 @@
1
+ # Background event loop for async operations
2
+ import asyncio
3
+ import threading
4
+
5
+ _loop: asyncio.AbstractEventLoop = None # type: ignore
6
+ _loop_thread: threading.Thread = None # type: ignore
7
+
8
+
9
+ def _get_event_loop():
10
+ """Get or create a background event loop running in a separate thread."""
11
+ global _loop, _loop_thread
12
+
13
+ if _loop is None or _loop.is_closed():
14
+ _loop = asyncio.new_event_loop()
15
+
16
+ def run_loop():
17
+ asyncio.set_event_loop(_loop)
18
+ _loop.run_forever()
19
+
20
+ _loop_thread = threading.Thread(target=run_loop, daemon=True)
21
+ _loop_thread.start()
22
+
23
+ return _loop
24
+
25
+
26
+ def run_sync(coro):
27
+ """Run an async coroutine synchronously using a background event loop.
28
+
29
+ This avoids issues with nested event loops and ensures proper task context
30
+ for Python 3.14+ where anyio requires asyncio.current_task() to return a valid task.
31
+ """
32
+ loop = _get_event_loop()
33
+ future = asyncio.run_coroutine_threadsafe(coro, loop)
34
+ return future.result()
plotly_cloud/cli.py CHANGED
@@ -7,7 +7,6 @@ import textwrap
7
7
  from collections.abc import Sequence
8
8
  from typing import Union
9
9
 
10
- import nest_asyncio
11
10
  from rich.console import Console
12
11
  from rich.panel import Panel
13
12
 
@@ -216,8 +215,6 @@ def print_command_help(command_class: BaseCommand, group: str, command: str) ->
216
215
 
217
216
  def main() -> None:
218
217
  """Main CLI entry point."""
219
- # Allow to run multiple calls to asyncio run.
220
- nest_asyncio.apply()
221
218
 
222
219
  # Parse group and command
223
220
  group, command, args_index = parse_group_and_command()
@@ -1,13 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plotly-cloud
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Python extension for Plotly Cloud with CLI and Dash dev tools integration
5
5
  Project-URL: Repository, https://github.com/plotly/plotly-cloud-extension
6
6
  License-File: LICENSE
7
7
  Requires-Python: >=3.9
8
- Requires-Dist: dash>=2.0.0
8
+ Requires-Dist: dash>=3.2.0
9
9
  Requires-Dist: httpx<1.0.0,>=0.24.0
10
- Requires-Dist: nest-asyncio>=1.6.0
11
10
  Requires-Dist: rich>=10.0.0
12
11
  Requires-Dist: tomli-w>=1.2.0
13
12
  Requires-Dist: tomli>=2.2.1
@@ -1,21 +1,22 @@
1
- plotly_cloud/__init__.py,sha256=oVX8cZCjc1qrroC9dAdwHPNfeQ2HKb4K4r9_zfs5dWE,61
1
+ plotly_cloud/__init__.py,sha256=PbEQJEsJ2iftoa4BI-eA8mOAWoEVTrHNLVx7RL9hEoo,61
2
2
  plotly_cloud/_api_types.py,sha256=CdP0w2YGjBUourO_vsoPcpHVXxemW6V3ezHewKXuIyc,1529
3
3
  plotly_cloud/_changes.py,sha256=YQJGChUzSGCgQOVnsA98vqWLARaY893UqVePYsCAhh8,2084
4
4
  plotly_cloud/_cloud_env.py,sha256=FDjIBUsJssKMW14HOrITg-5tdgkCI1FnmEy8U6zulcQ,2953
5
5
  plotly_cloud/_commands.py,sha256=8rPwUoGZ9BpwVNt2xT-NtQ7kEwZBO92sqQg_aB0Ii0Q,39206
6
6
  plotly_cloud/_definitions.py,sha256=GKLat5c9fkM-qJQ6_jPu5zjC6g3fkDANpTVKzMO50z0,2456
7
- plotly_cloud/_deploy.py,sha256=q76NaEI9iN137fhYzWsGuEk6VPjoGrZmsTnR-HQLqKU,17620
8
- plotly_cloud/_devtool_hooks.py,sha256=IPpX1tcMwHpLtBKhTkaXz48FvfFjv0-cgoZsZP2Aqcg,1913
9
- plotly_cloud/_devtool_publish_rpc.py,sha256=JY8FlP0yue6Yjsil6EDHpGSEdXfYnjEmLnK75CGKopM,11390
7
+ plotly_cloud/_deploy.py,sha256=RQIqa2hOYz4tfxeBELS5nx5y1_N9XBLhQOzLHtI2UYg,17582
8
+ plotly_cloud/_devtool_hooks.py,sha256=gO4BdRU1gRa56bntkt8JwIOsH-QC0EQKMrA0GmvHNrI,1189
9
+ plotly_cloud/_devtool_publish_rpc.py,sha256=K1NM__QUf9V3iNHk88Gxi6DsCriPnvnT456VdDo87eY,11441
10
10
  plotly_cloud/_oauth.py,sha256=BwkeBOVHqoUfguiP4WBpT_xXB-oFJoFL-8VGqp6cuoQ,12403
11
11
  plotly_cloud/_parser.py,sha256=EoyehZZRy_A8PHO2yTMUkJcXTgztgh-7gAQF2uaP640,5795
12
- plotly_cloud/cli.py,sha256=keez1KpXxZEDLWYo58VnvzPBG6mctMf1aNqDWGqfvq4,10474
12
+ plotly_cloud/_run_sync.py,sha256=Km782HXOwp-o6murS_Ng_vC7wG_7AR9bwVVfF0aY4Zk,1012
13
+ plotly_cloud/cli.py,sha256=OEtpZ7oG5FFjTSNlJpy1iWRL1eRqVWbEDlREYS7N290,10379
13
14
  plotly_cloud/cloud-env.toml,sha256=VhoG3DY6yBEwQG17_FqlJP8BxDqaffHbYlIFrxV6k9o,254
14
15
  plotly_cloud/cloud_devtools.css,sha256=3y2Q9NzM1arrkniLGZHfkiqtttuD5X7LkmCuxrkMRtI,3730
15
16
  plotly_cloud/cloud_devtools.js,sha256=rGhhRjM8BHWrrIspGlHgvjRz_OvfEQXueiwzrb_fVIQ,44265
16
17
  plotly_cloud/exceptions.py,sha256=QtNXLtryTbLqw3fUvho5GjsvR2W6BZ6rcMmvXJ0nVd0,4045
17
- plotly_cloud-0.2.0.dist-info/METADATA,sha256=zlHwCUtx33W4rORzOrr4b2diItblXVTZuXqoWvfjvyg,8137
18
- plotly_cloud-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
19
- plotly_cloud-0.2.0.dist-info/entry_points.txt,sha256=iZVffCSWjpG8Ht2HZAJDwZBmG-kVlIbH_V6SF8cwLNY,115
20
- plotly_cloud-0.2.0.dist-info/licenses/LICENSE,sha256=ElWBU-rl1xjn3Pvcguv5MNomrIMao1mPtuT_j8Q6WaI,1068
21
- plotly_cloud-0.2.0.dist-info/RECORD,,
18
+ plotly_cloud-0.2.1.dist-info/METADATA,sha256=1xlLW8zWcopmkPN1F0bUrOIRQBVwVsGuLjEqDhEXxlg,8102
19
+ plotly_cloud-0.2.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
20
+ plotly_cloud-0.2.1.dist-info/entry_points.txt,sha256=iZVffCSWjpG8Ht2HZAJDwZBmG-kVlIbH_V6SF8cwLNY,115
21
+ plotly_cloud-0.2.1.dist-info/licenses/LICENSE,sha256=ElWBU-rl1xjn3Pvcguv5MNomrIMao1mPtuT_j8Q6WaI,1068
22
+ plotly_cloud-0.2.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.28.0
2
+ Generator: hatchling 1.29.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any