Skip to content

Commit 78fa3af

Browse files
authored
Merge branch 'master' into master
2 parents 77043b0 + d961d9a commit 78fa3af

File tree

97 files changed

+2027
-560
lines changed

Some content is hidden

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

97 files changed

+2027
-560
lines changed

.actions/assistant.py

Lines changed: 97 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
from itertools import chain
2424
from os.path import dirname, isfile
2525
from pathlib import Path
26-
from typing import Dict, List, Optional, Sequence, Tuple
26+
from typing import Any, Dict, Iterable, Iterator, List, Optional, Sequence, Tuple, Union
2727

28-
from pkg_resources import parse_requirements
28+
from pkg_resources import parse_requirements, Requirement, yield_lines
2929

3030
REQUIREMENT_FILES = {
3131
"pytorch": (
@@ -49,86 +49,106 @@
4949
_PROJECT_ROOT = os.path.dirname(os.path.dirname(__file__))
5050

5151

52-
def _augment_requirement(ln: str, comment_char: str = "#", unfreeze: str = "all") -> str:
53-
"""Adjust the upper version contrains.
54-
55-
Args:
56-
ln: raw line from requirement
57-
comment_char: charter marking comment
58-
unfreeze: Enum or "all"|"major"|""
59-
60-
Returns:
61-
adjusted requirement
62-
63-
>>> _augment_requirement("arrow<=1.2.2,>=1.2.0 # anything", unfreeze="none")
64-
'arrow<=1.2.2,>=1.2.0'
65-
>>> _augment_requirement("arrow<=1.2.2,>=1.2.0 # strict", unfreeze="none")
66-
'arrow<=1.2.2,>=1.2.0 # strict'
67-
>>> _augment_requirement("arrow<=1.2.2,>=1.2.0 # my name", unfreeze="all")
68-
'arrow>=1.2.0'
69-
>>> _augment_requirement("arrow>=1.2.0, <=1.2.2 # strict", unfreeze="all")
70-
'arrow>=1.2.0, <=1.2.2 # strict'
71-
>>> _augment_requirement("arrow", unfreeze="all")
72-
'arrow'
73-
>>> _augment_requirement("arrow>=1.2.0, <=1.2.2 # cool", unfreeze="major")
74-
'arrow>=1.2.0, <2.0 # strict'
75-
>>> _augment_requirement("arrow>=1.2.0, <=1.2.2 # strict", unfreeze="major")
76-
'arrow>=1.2.0, <=1.2.2 # strict'
77-
>>> _augment_requirement("arrow>=1.2.0", unfreeze="major")
78-
'arrow>=1.2.0, <2.0 # strict'
79-
>>> _augment_requirement("arrow", unfreeze="major")
80-
'arrow'
52+
class _RequirementWithComment(Requirement):
53+
strict_string = "# strict"
54+
55+
def __init__(self, *args: Any, comment: str = "", pip_argument: Optional[str] = None, **kwargs: Any) -> None:
56+
super().__init__(*args, **kwargs)
57+
self.comment = comment
58+
assert pip_argument is None or pip_argument # sanity check that it's not an empty str
59+
self.pip_argument = pip_argument
60+
self.strict = self.strict_string in comment.lower()
61+
62+
def adjust(self, unfreeze: str) -> str:
63+
"""Remove version restrictions unless they are strict.
64+
65+
>>> _RequirementWithComment("arrow<=1.2.2,>=1.2.0", comment="# anything").adjust("none")
66+
'arrow<=1.2.2,>=1.2.0'
67+
>>> _RequirementWithComment("arrow<=1.2.2,>=1.2.0", comment="# strict").adjust("none")
68+
'arrow<=1.2.2,>=1.2.0 # strict'
69+
>>> _RequirementWithComment("arrow<=1.2.2,>=1.2.0", comment="# my name").adjust("all")
70+
'arrow>=1.2.0'
71+
>>> _RequirementWithComment("arrow>=1.2.0, <=1.2.2", comment="# strict").adjust("all")
72+
'arrow<=1.2.2,>=1.2.0 # strict'
73+
>>> _RequirementWithComment("arrow").adjust("all")
74+
'arrow'
75+
>>> _RequirementWithComment("arrow>=1.2.0, <=1.2.2", comment="# cool").adjust("major")
76+
'arrow<2.0,>=1.2.0'
77+
>>> _RequirementWithComment("arrow>=1.2.0, <=1.2.2", comment="# strict").adjust("major")
78+
'arrow<=1.2.2,>=1.2.0 # strict'
79+
>>> _RequirementWithComment("arrow>=1.2.0").adjust("major")
80+
'arrow>=1.2.0'
81+
>>> _RequirementWithComment("arrow").adjust("major")
82+
'arrow'
83+
"""
84+
out = str(self)
85+
if self.strict:
86+
return f"{out} {self.strict_string}"
87+
if unfreeze == "major":
88+
for operator, version in self.specs:
89+
if operator in ("<", "<="):
90+
major = LooseVersion(version).version[0]
91+
# replace upper bound with major version increased by one
92+
return out.replace(f"{operator}{version}", f"<{major + 1}.0")
93+
elif unfreeze == "all":
94+
for operator, version in self.specs:
95+
if operator in ("<", "<="):
96+
# drop upper bound
97+
return out.replace(f"{operator}{version},", "")
98+
elif unfreeze != "none":
99+
raise ValueError(f"Unexpected unfreeze: {unfreeze!r} value.")
100+
return out
101+
102+
103+
def _parse_requirements(strs: Union[str, Iterable[str]]) -> Iterator[_RequirementWithComment]:
104+
"""Adapted from `pkg_resources.parse_requirements` to include comments.
105+
106+
>>> txt = ['# ignored', '', 'this # is an', '--piparg', 'example', 'foo # strict', 'thing', '-r different/file.txt']
107+
>>> [r.adjust('none') for r in _parse_requirements(txt)]
108+
['this', 'example', 'foo # strict', 'thing']
109+
>>> txt = '\\n'.join(txt)
110+
>>> [r.adjust('none') for r in _parse_requirements(txt)]
111+
['this', 'example', 'foo # strict', 'thing']
81112
"""
82-
assert unfreeze in {"none", "major", "all"}
83-
# filer all comments
84-
if comment_char in ln:
85-
comment = ln[ln.index(comment_char) :]
86-
ln = ln[: ln.index(comment_char)]
87-
is_strict = "strict" in comment
88-
else:
89-
is_strict = False
90-
req = ln.strip()
91-
# skip directly installed dependencies
92-
if not req or any(c in req for c in ["http:", "https:", "@"]):
93-
return ""
94-
# extract the major version from all listed versions
95-
if unfreeze == "major":
96-
req_ = list(parse_requirements([req]))[0]
97-
vers = [LooseVersion(v) for s, v in req_.specs if s not in ("==", "~=")]
98-
ver_major = sorted(vers)[-1].version[0] if vers else None
99-
else:
100-
ver_major = None
101-
102-
# remove version restrictions unless they are strict
103-
if unfreeze != "none" and "<" in req and not is_strict:
104-
req = re.sub(r",? *<=? *[\d\.\*]+,? *", "", req).strip()
105-
if ver_major is not None and not is_strict:
106-
# add , only if there are already some versions
107-
req += f"{',' if any(c in req for c in '<=>') else ''} <{int(ver_major) + 1}.0"
108-
109-
# adding strict back to the comment
110-
if is_strict or ver_major is not None:
111-
req += " # strict"
112-
113-
return req
114-
115-
116-
def load_requirements(
117-
path_dir: str, file_name: str = "base.txt", comment_char: str = "#", unfreeze: str = "all"
118-
) -> List[str]:
113+
lines = yield_lines(strs)
114+
pip_argument = None
115+
for line in lines:
116+
# Drop comments -- a hash without a space may be in a URL.
117+
if " #" in line:
118+
comment_pos = line.find(" #")
119+
line, comment = line[:comment_pos], line[comment_pos:]
120+
else:
121+
comment = ""
122+
# If there is a line continuation, drop it, and append the next line.
123+
if line.endswith("\\"):
124+
line = line[:-2].strip()
125+
try:
126+
line += next(lines)
127+
except StopIteration:
128+
return
129+
# If there's a pip argument, save it
130+
if line.startswith("--"):
131+
pip_argument = line
132+
continue
133+
if line.startswith("-r "):
134+
# linked requirement files are unsupported
135+
continue
136+
yield _RequirementWithComment(line, comment=comment, pip_argument=pip_argument)
137+
pip_argument = None
138+
139+
140+
def load_requirements(path_dir: str, file_name: str = "base.txt", unfreeze: str = "all") -> List[str]:
119141
"""Loading requirements from a file.
120142
121143
>>> path_req = os.path.join(_PROJECT_ROOT, "requirements")
122144
>>> load_requirements(path_req, "docs.txt", unfreeze="major") # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
123-
['sphinx>=4.0, <6.0 # strict', ...]
145+
['sphinx<6.0,>=4.0', ...]
124146
"""
125147
assert unfreeze in {"none", "major", "all"}
126-
with open(os.path.join(path_dir, file_name)) as file:
127-
lines = [ln.strip() for ln in file.readlines()]
128-
reqs = [_augment_requirement(ln, comment_char=comment_char, unfreeze=unfreeze) for ln in lines]
129-
# filter empty lines and containing @ which means redirect to some git/http
130-
reqs = [str(req) for req in reqs if req and not any(c in req for c in ["@", "http:", "https:"])]
131-
return reqs
148+
path = Path(path_dir) / file_name
149+
assert path.exists(), (path_dir, file_name, path)
150+
text = path.read_text()
151+
return [req.adjust(unfreeze) for req in _parse_requirements(text)]
132152

133153

134154
def load_readme_description(path_dir: str, homepage: str, version: str) -> str:
@@ -213,14 +233,13 @@ def _load_aggregate_requirements(req_dir: str = "requirements", freeze_requireme
213233
>>> _load_aggregate_requirements(os.path.join(_PROJECT_ROOT, "requirements"))
214234
"""
215235
requires = [
216-
# TODO: consider passing unfreeze as string instead
217-
load_requirements(d, file_name="base.txt", unfreeze="none" if freeze_requirements else "major")
236+
load_requirements(d, unfreeze="none" if freeze_requirements else "major")
218237
for d in glob.glob(os.path.join(req_dir, "*"))
219238
# skip empty folder as git artefacts, and resolving Will's special issue
220239
if os.path.isdir(d) and len(glob.glob(os.path.join(d, "*"))) > 0 and "__pycache__" not in d
221240
]
222241
if not requires:
223-
return None
242+
return
224243
# TODO: add some smarter version aggregation per each package
225244
requires = sorted(set(chain(*requires)))
226245
with open(os.path.join(req_dir, "base.txt"), "w") as fp:

.azure/app-cloud-e2e.yml

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pr:
3131
- ".azure/app-cloud-e2e.yml"
3232
- "src/lightning_app/**"
3333
- "requirements/app/**"
34-
- "tests/integrations_app_examples/**"
34+
- "tests/integrations_app/**"
3535
- "setup.py"
3636
exclude:
3737
- "requirements/*/docs.txt"
@@ -48,7 +48,7 @@ variables:
4848
value: ./videos
4949

5050
jobs:
51-
- job: App_e2e_cloud
51+
- job: test_e2e
5252
pool: "azure-cpus"
5353
container:
5454
image: mcr.microsoft.com/playwright/python:v1.28.0-focal
@@ -58,41 +58,57 @@ jobs:
5858
'App: v0_app':
5959
name: "v0_app"
6060
dir: "public"
61+
queue_type: "redis"
6162
'App: boring_app':
6263
name: "boring_app"
6364
dir: "public"
65+
queue_type: "redis"
66+
'App: boring_app / HTTP':
67+
name: "boring_app"
68+
dir: "public"
69+
queue_type: "http"
6470
'App: template_streamlit_ui':
6571
name: "template_streamlit_ui"
6672
dir: "public"
73+
queue_type: "redis"
6774
'App: template_react_ui':
6875
name: "template_react_ui"
6976
dir: "public"
77+
queue_type: "redis"
7078
# 'App: template_jupyterlab': # TODO: clarify where these files lives
7179
# name: "template_jupyterlab"
7280
'App: installation_commands_app':
7381
name: "installation_commands_app"
7482
dir: "public"
83+
queue_type: "redis"
7584
'App: drive':
7685
name: "drive"
7786
dir: "public"
87+
queue_type: "redis"
7888
'App: payload':
7989
name: "payload"
8090
dir: "public"
91+
queue_type: "redis"
8192
'App: commands_and_api':
8293
name: "commands_and_api"
8394
dir: "public"
95+
queue_type: "redis"
8496
'App: quick_start':
8597
name: "quick_start"
8698
dir: "public"
99+
queue_type: "redis"
87100
'App: idle_timeout':
88101
name: "idle_timeout"
89102
dir: "local"
103+
queue_type: "redis"
90104
'App: collect_failures':
91105
name: "collect_failures"
92106
dir: "local"
107+
queue_type: "redis"
93108
'App: custom_work_dependencies':
94109
name: "custom_work_dependencies"
95110
dir: "local"
111+
queue_type: "redis"
96112
timeoutInMinutes: "15"
97113
cancelTimeoutInMinutes: "1"
98114
# values: https://docs.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml#workspace
@@ -109,6 +125,7 @@ jobs:
109125
HAR_LOCATION: './artifacts/hars'
110126
SLOW_MO: '50'
111127
LIGHTNING_DEBUG: '1'
128+
LIGHTNING_CLOUD_QUEUE_TYPE: $(queue_type)
112129
steps:
113130

114131
- script: echo '##vso[task.setvariable variable=local_id]$(System.PullRequest.PullRequestNumber)'
@@ -117,7 +134,7 @@ jobs:
117134

118135
- bash: |
119136
whoami
120-
mkdir -p $(video_artifact_dir)
137+
mkdir -p $(VIDEO_LOCATION)
121138
printf "local id: $(local_id)\n"
122139
python --version
123140
pip --version
@@ -161,17 +178,16 @@ jobs:
161178
python .actions/assistant.py copy_replace_imports --source_dir="./tests" --source_import="lightning_app" --target_import="lightning.app"
162179
displayName: 'Adjust examples'
163180
164-
- bash: |
165-
pip --version
166-
pip list
181+
- bash: pip --version && pip list
167182
displayName: 'List pip dependency'
168183

169184
- bash: |
170-
ls -l examples/${TEST_APP_NAME}
171-
python -m pytest ${TEST_FILE}::test_${TEST_APP_NAME}_example_cloud \
185+
ls -l examples/$(TEST_APP_NAME)
186+
echo ${TEST_FILE}
187+
python -m pytest ${TEST_FILE}::test_$(TEST_APP_NAME)_example_cloud \
172188
--timeout=540 --capture=no -v --color=yes
173189
env:
174-
TEST_FILE: tests/integrations_app_examples/$(TEST_APP_FOLDER)/test_$(TEST_APP_NAME).py
190+
TEST_FILE: tests/integrations_app/$(TEST_APP_FOLDER)/test_$(TEST_APP_NAME).py
175191
#LAI_USER: $(LAI_USER) # for STAGING
176192
#LAI_PASS: $(LAI_PASS) # for STAGING
177193
LIGHTNING_USER_ID: $(LIGHTNING_USER_ID_PROD)
@@ -191,12 +207,12 @@ jobs:
191207
- bash: |
192208
time python -c "from lightning.app import testing; testing.delete_cloud_lightning_apps()"
193209
condition: always()
210+
timeoutInMinutes: "3"
194211
env:
195212
#LAI_USER: $(LAI_USER) # for STAGING
196213
#LAI_PASS: $(LAI_PASS) # for STAGING
197214
LIGHTNING_USER_ID: $(LIGHTNING_USER_ID_PROD)
198215
LIGHTNING_API_KEY: $(LIGHTNING_API_KEY_PROD)
199216
LIGHTNING_USERNAME: $(LIGHTNING_USERNAME_PROD)
200217
LIGHTNING_CLOUD_URL: $(LIGHTNING_CLOUD_URL_PROD)
201-
timeoutInMinutes: "3"
202218
displayName: 'Clean Previous Apps'

0 commit comments

Comments
 (0)