-
Notifications
You must be signed in to change notification settings - Fork 439
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Install the generated python wheels, and test them out across a range of different python versions in github actions
- Loading branch information
Showing
4 changed files
with
166 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import requests | ||
import pkg_resources | ||
import pathlib | ||
|
||
|
||
_VERSIONS_URL = "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json" # noqa | ||
|
||
|
||
def get_github_python_versions(): | ||
versions_json = requests.get(_VERSIONS_URL).json() | ||
raw_versions = [v["version"] for v in versions_json] | ||
versions = [] | ||
for version_str in raw_versions: | ||
if '-' in version_str: | ||
continue | ||
|
||
v = pkg_resources.parse_version(version_str) | ||
if v.major == 3 and v.minor < 5: | ||
# we don't support python 3.0/3.1/3.2 , and don't bother testing 3.3/3.4 | ||
continue | ||
|
||
elif v.major == 2 and v.minor < 7: | ||
# we don't test python support before 2.7 | ||
continue | ||
|
||
versions.append(version_str) | ||
return versions | ||
|
||
|
||
if __name__ == "__main__": | ||
versions = sorted(get_github_python_versions(), key = lambda x: pkg_resources.parse_version(x)) | ||
build_yml = pathlib.Path(__file__).parent.parent / ".github" / "workflows" / "build.yml" | ||
|
||
transformed = [] | ||
for line in open(build_yml): | ||
if line.startswith(" python-version: ["): | ||
print(line) | ||
line = f" python-version: [{', '.join(v for v in versions)}]\n" | ||
print(line) | ||
transformed.append(line) | ||
|
||
with open(build_yml, "w") as o: | ||
o.write("".join(transformed)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import json | ||
import subprocess | ||
import sys | ||
import unittest | ||
import tempfile | ||
import os | ||
from collections import defaultdict, namedtuple | ||
from distutils.spawn import find_executable | ||
|
||
|
||
Frame = namedtuple("Frame", ["file", "name", "line", "col"]) | ||
|
||
# disable gil checks on windows - just rely on active | ||
# (doesn't seem to be working quite right - TODO: investigate) | ||
GIL = ["--gil"] if not sys.platform.startswith("win") else [] | ||
|
||
|
||
class TestPyspy(unittest.TestCase): | ||
""" Basic tests of using py-spy as a commandline application """ | ||
def _sample_process(self, script_name, options=None): | ||
pyspy = find_executable("py-spy") | ||
print("Testing py-spy @", pyspy) | ||
|
||
# for permissions reasons, we really want to run the sampled python process as a | ||
# subprocess of the py-spy (works best on linux etc). So we're running the | ||
# record option, and setting different flags. To get the profile output | ||
# we're using the speedscope format (since we can read that in as json) | ||
with tempfile.NamedTemporaryFile() as profile_file: | ||
cmdline = [ | ||
pyspy, | ||
"record", | ||
"-o", | ||
profile_file.name, | ||
"--format", | ||
"speedscope", | ||
"-d", | ||
"1", | ||
] | ||
cmdline.extend(options or []) | ||
cmdline.extend(["--", sys.executable, script_name]) | ||
|
||
subprocess.check_call(cmdline) | ||
with open(profile_file.name) as f: | ||
profiles = json.load(f) | ||
|
||
frames = profiles["shared"]["frames"] | ||
samples = defaultdict(int) | ||
for p in profiles["profiles"]: | ||
for sample in p["samples"]: | ||
samples[tuple(Frame(**frames[frame]) for frame in sample)] += 1 | ||
return samples | ||
|
||
def test_longsleep(self): | ||
# running with the gil flag should have ~ no samples returned | ||
profile = self._sample_process(_get_script("longsleep.py"), GIL) | ||
assert sum(profile.values()) <= 5 | ||
|
||
# running with the idle flag should have > 95% of samples in the sleep call | ||
profile = self._sample_process(_get_script("longsleep.py"), ["--idle"]) | ||
sample, count = _most_frequent_sample(profile) | ||
assert count >= 95 | ||
assert len(sample) == 2 | ||
assert sample[0].name == "<module>" | ||
assert sample[0].line == 9 | ||
assert sample[1].name == "longsleep" | ||
assert sample[1].line == 5 | ||
|
||
def test_busyloop(self): | ||
# can't be sure what line we're on, but we should have ~ all samples holding the gil | ||
profile = self._sample_process(_get_script("busyloop.py"), GIL) | ||
print(profile) | ||
assert sum(profile.values()) >= 95 | ||
|
||
|
||
|
||
def _get_script(name): | ||
base_dir = os.path.dirname(__file__) | ||
return os.path.join(base_dir, "scripts", name) | ||
|
||
|
||
def _most_frequent_sample(samples): | ||
return max(samples.items(), key=lambda x: x[1]) | ||
|
||
if __name__ == "__main__": | ||
unittest.main() |