Skip to content

Commit 309f650

Browse files
committed
Remove py-spy code
1 parent ea1e082 commit 309f650

File tree

3 files changed

+6
-148
lines changed

3 files changed

+6
-148
lines changed

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ doc =
115115
quartodoc==0.7.2
116116
griffe==0.33.0
117117
profile =
118-
py-spy
118+
pyinstrument
119119

120120
[options.packages.find]
121121
include = shiny, shiny.*

shiny/_main.py

Lines changed: 2 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import sys
1212
import types
1313
from pathlib import Path
14-
from typing import Any, Iterable, NoReturn, Optional
14+
from typing import Any, Optional
1515

1616
import click
1717
import uvicorn
@@ -21,8 +21,7 @@
2121

2222
from . import _autoreload, _hostenv, _static, _utils
2323
from ._docstring import no_example
24-
from ._profiler import check_dependencies as check_profiler_dependencies
25-
from ._profiler import profiler
24+
from ._profiler import check_profiler_dependencies, profiler
2625
from ._typing_extensions import NotRequired, TypedDict
2726
from .express import is_express_app
2827
from .express._utils import escape_to_var_name
@@ -697,144 +696,3 @@ def _verify_rsconnect_version() -> None:
697696
)
698697
except PackageNotFoundError:
699698
pass
700-
701-
702-
def find_pyspy_path() -> str | None:
703-
import sysconfig
704-
705-
schemes = [
706-
sysconfig.get_default_scheme(),
707-
sysconfig.get_preferred_scheme("prefix"),
708-
sysconfig.get_preferred_scheme("home"),
709-
sysconfig.get_preferred_scheme("user"),
710-
]
711-
712-
for scheme in schemes:
713-
path = sysconfig.get_path("scripts", scheme=scheme)
714-
pyspy_path = os.path.join(path, "py-spy" + (".exe" if os.name == "nt" else ""))
715-
if os.path.exists(pyspy_path):
716-
return pyspy_path
717-
718-
return None
719-
720-
721-
def get_current_argv() -> Iterable[str]:
722-
r"""
723-
## Windows, `shiny run`
724-
725-
argv: C:\Users\jcheng\Development\posit-dev\py-shiny\.venv311\Scripts\shiny run app.py
726-
orig_argv: C:\Users\jcheng\AppData\Local\Programs\Python\Python311\python.exe C:\Users\jcheng\Development\posit-dev\py-shiny\.venv311\Scripts\shiny.exe run app.py
727-
728-
The argv is usable, the orig_argv is not because the Python path points to the
729-
physical python.exe instead of the python.exe inside of the venv.
730-
731-
## Windows, `python -m shiny run`
732-
733-
argv: C:\Users\jcheng\Development\posit-dev\py-shiny\.venv311\Lib\site-packages\shiny\__main__.py run app.py
734-
orig_argv: C:\Users\jcheng\AppData\Local\Programs\Python\Python311\python.exe -m shiny run app.py
735-
736-
The argv is not usable because argv[0] is not executable. The orig_argv is not
737-
usable because it's the physical python.exe instead of the python.exe inside
738-
of the venv.
739-
740-
## Mac, `shiny run`
741-
742-
argv: /Users/jcheng/Development/posit-dev/py-shiny/.venv/bin/shiny run app.py
743-
orig_argv: /Users/jcheng/Development/posit-dev/py-shiny/.venv/bin/python /Users/jcheng/Development/posit-dev/py-shiny/.venv/bin/shiny run app.py
744-
745-
Both are usable, nice.
746-
747-
## Mac, `python -m shiny run`
748-
749-
argv: /Users/jcheng/Development/posit-dev/py-shiny/.venv/lib/python3.10/site-packages/shiny/__main__.py run app.py
750-
orig_argv: python -m shiny run app.py
751-
752-
The argv is not usable because argv[0] is not executable. The orig_argv is
753-
usable.
754-
"""
755-
756-
# print("argv: " + " ".join(sys.argv))
757-
# print("orig_argv: " + " ".join(sys.orig_argv))
758-
759-
args = sys.argv.copy()
760-
761-
if Path(args[0]).suffix == ".py":
762-
args = [*sys.orig_argv]
763-
764-
if os.name == "nt" and sys.prefix != sys.base_prefix:
765-
# In a virtualenv. Make sure we use the correct Python, the one from inside
766-
# the venv.
767-
args[0] = sys.executable
768-
769-
return args
770-
771-
772-
def run_under_pyspy() -> NoReturn:
773-
import base64
774-
import subprocess
775-
import time
776-
import webbrowser
777-
from urllib.parse import quote_plus
778-
779-
pyspy_path = find_pyspy_path()
780-
if pyspy_path is None:
781-
print(
782-
"Error: Profiler is not installed. You can install it with "
783-
"'pip install shiny[profile]'.",
784-
file=sys.stderr,
785-
)
786-
sys.exit(1)
787-
788-
print(" ".join(get_current_argv()))
789-
# Strip out the --profile argument and launch again under py-spy
790-
new_argv = [x for x in get_current_argv() if x != "--profile"]
791-
792-
# For some reason, on Windows, I see python.exe and shiny.exe as the first two
793-
# arguments
794-
# if os.name == "nt" and Path(new_argv[1]).suffix == ".exe":
795-
# new_argv.pop(0)
796-
797-
# Create a filename based on "profile.json" but with a unique name based on the
798-
# current date/time
799-
epoch_time = int(time.time())
800-
output_filename = f"profile-{epoch_time}.json"
801-
output_filename_abs = os.path.join(os.getcwd(), output_filename)
802-
803-
try:
804-
# TODO: Print out command line that will be run, in case user wants to change it
805-
subprocess_args = [
806-
pyspy_path,
807-
"record",
808-
"--format=speedscope",
809-
f"--output={output_filename}",
810-
"--idle",
811-
"--subprocesses",
812-
"--",
813-
*new_argv,
814-
]
815-
816-
print(" ".join(subprocess_args))
817-
818-
# Run a new process under py-spy
819-
proc = subprocess.run(
820-
subprocess_args,
821-
check=False,
822-
shell=False,
823-
stdin=sys.stdin,
824-
stdout=sys.stdout,
825-
stderr=sys.stderr,
826-
)
827-
sys.exit(proc.returncode)
828-
finally:
829-
if os.path.exists(output_filename_abs):
830-
with open(output_filename_abs, "rb") as profile_file:
831-
b64_str = base64.b64encode(profile_file.read()).decode("utf-8")
832-
data_uri = f"data:application/json;base64,{b64_str}"
833-
full_url = (
834-
"https://speedscope.app/#profileURL="
835-
+ quote_plus(data_uri)
836-
# + "&title="
837-
# + quote_plus(output_filename)
838-
)
839-
print(full_url)
840-
webbrowser.open(full_url)

shiny/_profiler.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from __future__ import annotations
22

33
import contextlib
4+
import sys
45
from typing import Any, Generator
56

67

7-
def check_dependencies() -> None:
8+
def check_profiler_dependencies() -> None:
89
try:
9-
import pyinstrument
10+
import pyinstrument # pyright: ignore[reportUnusedImport]
1011
except ImportError:
1112
print(
1213
"Error: Profiler is not installed. You can install it with "
@@ -20,7 +21,6 @@ def check_dependencies() -> None:
2021
def profiler() -> Generator[None, Any, None]:
2122
import base64
2223
import os
23-
import sys
2424
import time
2525
import webbrowser
2626
from urllib.parse import quote_plus

0 commit comments

Comments
 (0)