Skip to content

Commit 01a2e9a

Browse files
kiukchungfacebook-github-bot
authored andcommitted
Allow empty session name for APIs that take it (#1057)
Summary: Pull Request resolved: #1057 Makes the session name returning and accepting APIs symmetric by allowing empty session names. Today `torchx.runner.Runner()` takes an empty string as `session_name` and returns an `app_handle` with an empty session name (e.g. `local:///app-id`) when the `run()` or `schedule()` methods are called (see the newly added unittest in `torchx/runner/test/api_test.py#test_empty_session_id()`) However runner APIs that accept `app_handle` (e.g. `runner.cancel()`) will error when the session name is empty. This makes the APIs non-symmetric since the `app_handle` returned by `run/schedule` APIs cannot be used. This PR fixes this. Reviewed By: highker, daniel-ohayon Differential Revision: D73799333
1 parent 16cefac commit 01a2e9a

File tree

4 files changed

+58
-5
lines changed

4 files changed

+58
-5
lines changed

.github/workflows/python-unittests.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ jobs:
1212
matrix:
1313
python-version: [3.9, "3.10", 3.11, 3.12]
1414
platform: [linux.24_04.4x]
15-
include:
16-
- python-version: 3.12
17-
platform: macos-13-xlarge
15+
# include:
16+
# - python-version: 3.12
17+
# platform: macos-13-xlarge
1818
fail-fast: false
1919
env:
2020
OS: ${{ matrix.platform }}

torchx/runner/test/api_test.py

+27
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
AppDef,
2626
AppHandle,
2727
AppState,
28+
parse_app_handle,
2829
Resource,
2930
Role,
3031
UnknownAppException,
@@ -132,6 +133,32 @@ def test_session_id(self, record_mock: MagicMock) -> None:
132133
event = record_mock.call_args_list[i].args[0]
133134
self.assertEqual(event.session, CURRENT_SESSION_ID)
134135

136+
def test_empty_session_id(self, _: MagicMock) -> None:
137+
empty_session_name = ""
138+
with Runner(
139+
empty_session_name,
140+
{"local": cast(SchedulerFactory, create_scheduler)},
141+
) as runner:
142+
app = AppDef(
143+
"__unused_for_test__",
144+
roles=[
145+
Role(
146+
name="echo",
147+
image=str(self.tmpdir),
148+
resource=resource.SMALL,
149+
entrypoint="echo",
150+
args=["__unused_for_test__"],
151+
)
152+
],
153+
)
154+
155+
app_handle = runner.run(app, "local", self.cfg)
156+
157+
scheduler, session_name, app_id = parse_app_handle(app_handle)
158+
self.assertEqual(scheduler, "local")
159+
self.assertEqual(empty_session_name, session_name)
160+
self.assertEqual(app_handle, f"local:///{app_id}")
161+
135162
def test_run(self, record_mock: MagicMock) -> None:
136163
test_file = self.tmpdir / "test_file"
137164

torchx/specs/api.py

+19-2
Original file line numberDiff line numberDiff line change
@@ -1115,14 +1115,31 @@ def __init__(self, app_handle: "AppHandle") -> None:
11151115

11161116
def parse_app_handle(app_handle: AppHandle) -> ParsedAppHandle:
11171117
"""
1118-
parses the app handle into ```(scheduler_backend, session_name, and app_id)```
1118+
Parses the app handle into ```(scheduler_backend, session_name, and app_id)```.
1119+
1120+
Example:
1121+
1122+
.. doctest::
1123+
1124+
assert parse_app_handle("k8s://default/foo_bar") == ("k8s", "default", "foo_bar")
1125+
assert parse_app_handle("k8s:///foo_bar") == ("k8s", "", "foo_bar")
1126+
1127+
Args:
1128+
app_handle: a URI of the form ``{scheduler}://{session_name}/{app_id}``,
1129+
where the ``session_name`` is optional. In this case the app handle is
1130+
of the form ``{scheduler}:///{app_id}`` (notice the triple slashes).
1131+
1132+
Returns: A ``Tuple`` of three elements, ``(scheduler, session_name, app_id)``
1133+
parsed from the app_handle URI str. If the session name is not present then
1134+
an empty string is returned in its place in the tuple.
1135+
11191136
"""
11201137

11211138
# parse it manually b/c currently torchx does not
11221139
# define allowed characters nor length for session name and app_id
11231140
import re
11241141

1125-
pattern = r"(?P<scheduler_backend>.+)://(?P<session_name>.+)/(?P<app_id>.+)"
1142+
pattern = r"(?P<scheduler_backend>.+)://(?P<session_name>.*)/(?P<app_id>.+)"
11261143
match = re.match(pattern, app_handle)
11271144
if not match:
11281145
raise MalformedAppHandleException(app_handle)

torchx/specs/test/api_test.py

+9
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,15 @@ def test_parse_malformed_app_handles(self) -> None:
377377
with self.assertRaises(MalformedAppHandleException):
378378
parse_app_handle(handle)
379379

380+
def test_parse_app_handle_empty_session_name(self) -> None:
381+
# missing session name is not OK but an empty one is
382+
app_handle = "local:///my_application_id"
383+
handle = parse_app_handle(app_handle)
384+
385+
self.assertEqual(handle.app_id, "my_application_id")
386+
self.assertEqual("local", handle.scheduler_backend)
387+
self.assertEqual("", handle.session_name)
388+
380389
def test_parse(self) -> None:
381390
(scheduler_backend, session_name, app_id) = parse_app_handle(
382391
"local://my_session/my_app_id_1234"

0 commit comments

Comments
 (0)