Skip to content

Commit 048339d

Browse files
committed
Merge branch 'master' into refactor/spawn/setup-environment
2 parents cd9bf08 + c926e10 commit 048339d

File tree

1,036 files changed

+19902
-9650
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,036 files changed

+19902
-9650
lines changed

.actions/assistant.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import os
55
import re
66
import shutil
7-
from distutils.version import LooseVersion, StrictVersion
7+
from distutils.version import LooseVersion
88
from importlib.util import module_from_spec, spec_from_file_location
99
from itertools import chain
1010
from pathlib import Path
@@ -16,6 +16,7 @@
1616

1717
import fire
1818
import pkg_resources
19+
from packaging.version import parse as version_parse
1920

2021
REQUIREMENT_FILES = {
2122
"pytorch": (
@@ -30,15 +31,20 @@
3031
PACKAGE_MAPPING = {"app": "lightning-app", "pytorch": "pytorch-lightning"}
3132

3233

33-
def pypi_versions(package_name: str) -> List[str]:
34-
"""Return a list of released versions of a provided pypi name."""
34+
def pypi_versions(package_name: str, drop_pre: bool = True) -> List[str]:
35+
"""Return a list of released versions of a provided pypi name.
36+
37+
>>> _ = pypi_versions("lightning_app", drop_pre=False)
38+
"""
3539
# https://stackoverflow.com/a/27239645/4521646
3640
url = f"https://pypi.org/pypi/{package_name}/json"
3741
data = json.load(urlopen(Request(url)))
3842
versions = list(data["releases"].keys())
3943
# todo: drop this line after cleaning Pypi history from invalid versions
40-
versions = list(filter(lambda v: v.count(".") == 2 and "rc" not in v, versions))
41-
versions.sort(key=StrictVersion)
44+
versions = list(filter(lambda v: v.count(".") == 2, versions))
45+
if drop_pre:
46+
versions = list(filter(lambda v: all(c not in v for c in ["rc", "dev"]), versions))
47+
versions.sort(key=version_parse)
4248
return versions
4349

4450

@@ -122,15 +128,16 @@ def download_package(package: str, folder: str = ".", version: Optional[str] = N
122128
url = f"https://pypi.org/pypi/{PACKAGE_MAPPING[package]}/json"
123129
data = json.load(urlopen(Request(url)))
124130
if not version:
125-
versions = list(data["releases"].keys())
126-
version = sorted(versions, key=LooseVersion)[-1]
131+
pypi_vers = pypi_versions(PACKAGE_MAPPING[package], drop_pre=False)
132+
version = pypi_vers[-1]
127133
releases = list(filter(lambda r: r["packagetype"] == "sdist", data["releases"][version]))
128134
assert releases, f"Missing 'sdist' for this package/version aka {package}/{version}"
129135
release = releases[0]
130136
pkg_url = release["url"]
131137
pkg_file = os.path.basename(pkg_url)
132138
pkg_path = os.path.join(folder, pkg_file)
133139
os.makedirs(folder, exist_ok=True)
140+
print(f"downloading: {pkg_url}")
134141
request.urlretrieve(pkg_url, pkg_path)
135142

136143
@staticmethod

.actions/requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fire
2+
packaging
3+
requests

.actions/setup_tools.py

Lines changed: 92 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@
2121
import tempfile
2222
import urllib.request
2323
from datetime import datetime
24+
from distutils.version import LooseVersion
2425
from importlib.util import module_from_spec, spec_from_file_location
2526
from itertools import chain, groupby
2627
from types import ModuleType
2728
from typing import List
2829

30+
from pkg_resources import parse_requirements
31+
2932
_PROJECT_ROOT = os.path.dirname(os.path.dirname(__file__))
3033
_PACKAGE_MAPPING = {"pytorch": "pytorch_lightning", "app": "lightning_app"}
3134

@@ -42,45 +45,92 @@ def _load_py_module(name: str, location: str) -> ModuleType:
4245
return py
4346

4447

48+
def _augment_requirement(ln: str, comment_char: str = "#", unfreeze: str = "all") -> str:
49+
"""Adjust the upper version contrains.
50+
51+
Args:
52+
ln: raw line from requirement
53+
comment_char: charter marking comment
54+
unfreeze: Enum or "all"|"major"|""
55+
56+
Returns:
57+
adjusted requirement
58+
59+
>>> _augment_requirement("arrow>=1.2.0, <=1.2.2 # anything", unfreeze="")
60+
'arrow>=1.2.0, <=1.2.2'
61+
>>> _augment_requirement("arrow>=1.2.0, <=1.2.2 # strict", unfreeze="")
62+
'arrow>=1.2.0, <=1.2.2 # strict'
63+
>>> _augment_requirement("arrow>=1.2.0, <=1.2.2 # my name", unfreeze="all")
64+
'arrow>=1.2.0'
65+
>>> _augment_requirement("arrow>=1.2.0, <=1.2.2 # strict", unfreeze="all")
66+
'arrow>=1.2.0, <=1.2.2 # strict'
67+
>>> _augment_requirement("arrow", unfreeze="all")
68+
'arrow'
69+
>>> _augment_requirement("arrow>=1.2.0, <=1.2.2 # cool", unfreeze="major")
70+
'arrow>=1.2.0, <2.0 # strict'
71+
>>> _augment_requirement("arrow>=1.2.0, <=1.2.2 # strict", unfreeze="major")
72+
'arrow>=1.2.0, <=1.2.2 # strict'
73+
>>> _augment_requirement("arrow>=1.2.0", unfreeze="major")
74+
'arrow>=1.2.0, <2.0 # strict'
75+
>>> _augment_requirement("arrow", unfreeze="major")
76+
'arrow'
77+
"""
78+
# filer all comments
79+
if comment_char in ln:
80+
comment = ln[ln.index(comment_char) :]
81+
ln = ln[: ln.index(comment_char)]
82+
is_strict = "strict" in comment
83+
else:
84+
is_strict = False
85+
req = ln.strip()
86+
# skip directly installed dependencies
87+
if not req or req.startswith("http") or "@http" in req:
88+
return ""
89+
# extract the major version from all listed versions
90+
if unfreeze == "major":
91+
req_ = list(parse_requirements([req]))[0]
92+
vers = [LooseVersion(v) for s, v in req_.specs if s not in ("==", "~=")]
93+
ver_major = sorted(vers)[-1].version[0] if vers else None
94+
else:
95+
ver_major = None
96+
97+
# remove version restrictions unless they are strict
98+
if unfreeze and "<" in req and not is_strict:
99+
req = re.sub(r",? *<=? *[\d\.\*]+", "", req).strip()
100+
if ver_major is not None and not is_strict:
101+
# add , only if there are already some versions
102+
req += f"{',' if any(c in req for c in '<=>') else ''} <{int(ver_major) + 1}.0"
103+
104+
# adding strict back to the comment
105+
if is_strict or ver_major is not None:
106+
req += " # strict"
107+
108+
return req
109+
110+
45111
def load_requirements(
46-
path_dir: str, file_name: str = "base.txt", comment_char: str = "#", unfreeze: bool = True
112+
path_dir: str, file_name: str = "base.txt", comment_char: str = "#", unfreeze: str = "all"
47113
) -> List[str]:
48114
"""Loading requirements from a file.
49115
50116
>>> path_req = os.path.join(_PROJECT_ROOT, "requirements")
51-
>>> load_requirements(path_req) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
52-
['numpy...', 'torch...', ...]
117+
>>> load_requirements(path_req, unfreeze="major") # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
118+
['pytorch_lightning...', 'lightning_app...']
53119
"""
54120
with open(os.path.join(path_dir, file_name)) as file:
55121
lines = [ln.strip() for ln in file.readlines()]
56122
reqs = []
57123
for ln in lines:
58-
# filer all comments
59-
comment = ""
60-
if comment_char in ln:
61-
comment = ln[ln.index(comment_char) :]
62-
ln = ln[: ln.index(comment_char)]
63-
req = ln.strip()
64-
# skip directly installed dependencies
65-
if not req or req.startswith("http") or "@http" in req:
66-
continue
67-
# remove version restrictions unless they are strict
68-
if unfreeze and "<" in req and "strict" not in comment:
69-
req = re.sub(r",? *<=? *[\d\.\*]+", "", req).strip()
70-
71-
# adding strict back to the comment
72-
if "strict" in comment:
73-
req += " # strict"
74-
75-
reqs.append(req)
76-
return reqs
124+
reqs.append(_augment_requirement(ln, comment_char=comment_char, unfreeze=unfreeze))
125+
# filter empty lines
126+
return [str(req) for req in reqs if req]
77127

78128

79129
def load_readme_description(path_dir: str, homepage: str, version: str) -> str:
80130
"""Load readme as decribtion.
81131
82132
>>> load_readme_description(_PROJECT_ROOT, "", "") # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
83-
'<div align="center">...'
133+
'...'
84134
"""
85135
path_readme = os.path.join(path_dir, "README.md")
86136
text = open(path_readme, encoding="utf-8").read()
@@ -127,7 +177,18 @@ def replace_block_with_imports(lines: List[str], import_path: str, kword: str =
127177
>>> lines = replace_block_with_imports(lines, import_path, "def")
128178
"""
129179
body, tracking, skip_offset = [], False, 0
130-
for ln in lines:
180+
for i, ln in enumerate(lines):
181+
# support for defining a class with this condition
182+
conditional_class_definitions = ("if TYPE_CHECKING", "if typing.TYPE_CHECKING", "if torch.", "if _TORCH_")
183+
if (
184+
any(ln.startswith(pattern) for pattern in conditional_class_definitions)
185+
# avoid bug in CI for the <1.7 meta code
186+
and "pytorch_lightning.utilities.meta" not in import_path
187+
):
188+
# dedent the next line
189+
lines[i + 1] = lines[i + 1].lstrip()
190+
continue
191+
131192
offset = len(ln) - len(ln.lstrip())
132193
# in case of mating the class args are multi-line
133194
if tracking and ln and offset <= skip_offset and not any(ln.lstrip().startswith(c) for c in ")]"):
@@ -439,12 +500,14 @@ def _download_frontend(root: str = _PROJECT_ROOT):
439500
print("The Lightning UI downloading has failed!")
440501

441502

442-
def _adjust_require_versions(source_dir: str = "src", req_dir: str = "requirements") -> None:
443-
"""Parse the base requirements and append as version adjustments if needed `pkg>=X1.Y1.Z1,==X2.Y2.*`."""
503+
def _relax_require_versions(source_dir: str = "src", req_dir: str = "requirements") -> None:
504+
"""Parse the base requirements and append as version adjustments if needed `pkg>=X1.Y1.Z1,==X2.Y2.*`.
505+
506+
>>> _relax_require_versions("../src", "../requirements")
507+
"""
444508
reqs = load_requirements(req_dir, file_name="base.txt")
445-
for i, req in enumerate(reqs):
446-
pkg_name = req[: min(req.index(c) for c in ">=" if c in req)]
447-
ver_ = parse_version_from_file(os.path.join(source_dir, pkg_name))
509+
for i, req in enumerate(parse_requirements(reqs)):
510+
ver_ = parse_version_from_file(os.path.join(source_dir, req.name))
448511
if not ver_:
449512
continue
450513
ver2 = ".".join(ver_.split(".")[:2] + ["*"])

.azure/app-cloud-e2e.yml

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,38 @@ trigger:
1212
- "master"
1313
- "release/*"
1414
- "refs/tags/*"
15+
paths:
16+
include:
17+
- ".azure/app-cloud-e2e.yml"
18+
- "requirements/app/**"
19+
- "src/lightning_app/**"
20+
- "examples/app_*"
1521

1622
pr:
17-
- "master"
18-
- "release/*"
23+
branches:
24+
include:
25+
- "master"
26+
- "release/*"
27+
paths:
28+
include:
29+
- ".azure/app-cloud-e2e.yml"
30+
- "requirements/app/**"
31+
- "src/lightning_app/**"
32+
- "examples/app_*"
1933

34+
# variables are automatically exported as environment variables so this will override pip's default cache dir
2035
variables:
21-
# variables are automatically exported as environment variables so this will override pip's default cache dir
2236
- name: pip_cache_dir
2337
value: $(Pipeline.Workspace)/.pip
38+
- name: local_id
39+
value: $(Build.BuildId)
2440

2541
jobs:
2642
- job: App_cloud_e2e_testing
2743
pool: azure-cpus
2844
container:
2945
image: mcr.microsoft.com/playwright/python:v1.25.2-focal
3046
options: "--shm-size=2g"
31-
timeoutInMinutes: "30"
32-
cancelTimeoutInMinutes: "2"
3347
strategy:
3448
matrix:
3549
'App: v0_app':
@@ -54,20 +68,24 @@ jobs:
5468
name: "payload"
5569
'App: commands_and_api':
5670
name: "commands_and_api"
71+
timeoutInMinutes: "30"
72+
cancelTimeoutInMinutes: "2"
73+
# values: https://docs.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml#workspace
5774
workspace:
5875
clean: all
5976
steps:
77+
78+
- script: echo '##vso[task.setvariable variable=local_id]$(System.PullRequest.PullRequestNumber)'
79+
displayName: "Set id for this PR"
80+
condition: eq(variables['Build.Reason'], 'PullRequest')
81+
6082
- bash: |
6183
whoami
84+
printf "local id: $(local_id)\n"
6285
python --version
6386
pip --version
6487
displayName: 'Info'
6588
66-
# TODO: parse the PR number
67-
- bash: |
68-
ID=$(date +%s)
69-
echo "##vso[task.setvariable variable=local_id]$ID"
70-
7189
- task: Cache@2
7290
inputs:
7391
key: 'pip | "$(name)" | requirements/app/base.txt'
@@ -90,12 +108,15 @@ jobs:
90108
displayName: 'Install lightning'
91109

92110
- bash: |
111+
rm -rf examples/app_template_jupyterlab || true
93112
git clone https://github.com/Lightning-AI/LAI-lightning-template-jupyterlab-App examples/app_template_jupyterlab
94113
cp examples/app_template_jupyterlab/tests/test_template_jupyterlab.py tests/tests_app_examples/test_template_jupyterlab.py
95114
condition: eq(variables['name'], 'template_jupyterlab')
96115
displayName: 'Clone Template Jupyter Lab Repo'
97116
98-
- bash: git clone https://github.com/Lightning-AI/lightning-template-react examples/app_template_react_ui
117+
- bash: |
118+
rm -rf examples/app_template_react_ui || true
119+
git clone https://github.com/Lightning-AI/lightning-template-react examples/app_template_react_ui
99120
condition: eq(variables['name'], 'template_react_ui')
100121
displayName: 'Clone Template React UI Repo'
101122
@@ -119,14 +140,17 @@ jobs:
119140
LIGHTNING_API_KEY: $(LIGHTNING_API_KEY_PROD)
120141
LIGHTNING_USERNAME: $(LIGHTNING_USERNAME)
121142
LIGHTNING_CLOUD_URL: $(LIGHTNING_CLOUD_URL_PROD)
143+
LIGHTNING_DEBUG: '1'
122144
displayName: 'Run the tests'
123145
124146
- publish: '$(Build.ArtifactStagingDirectory)/videos'
147+
condition: failed()
125148
displayName: 'Publish videos'
126149
artifact: $(name)
127150

128151
- bash: |
129152
time python -c "from lightning.app import testing; testing.delete_cloud_lightning_apps()"
153+
condition: always()
130154
env:
131155
# LAI_USER: $(LAI_USER)
132156
# LAI_PASS: $(LAI_PASS)

0 commit comments

Comments
 (0)