Skip to content

Commit 8d2bde0

Browse files
committed
Treat a torch that fails with an ImportError as missing, as before
When importing torch raised an ImportError, for example because one of its shared libraries is missing, the bindings used to return ExecuTorchResult outputs, as if torch were not installed. When the torch import moved to the first call that needs it, only a ModuleNotFoundError for torch itself counted as missing, so such a torch made every call raise. That was the only change in output type that had no warning period. Count every ImportError as missing again, so these setups keep working as they did. Any other error from importing torch is still raised, with a note on how to run without it.
1 parent baf7b9a commit 8d2bde0

4 files changed

Lines changed: 18 additions & 21 deletions

File tree

‎docs/source/runtime-python-api-reference.rst‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ For data on a GPU, use DLPack, the standard way to share tensors between Python
4646
Output types
4747
~~~~~~~~~~~~
4848

49-
Importing ``executorch.runtime`` does not import PyTorch, unless the setting below asks for ``torch``. When PyTorch is installed, ``Method.execute`` returns ``torch.Tensor`` outputs, and the first call that returns one imports ``torch``. Without PyTorch, it returns ``ExecuTorchResult`` outputs. If PyTorch is installed but fails to import, every call that returns a tensor raises the import error; set the setting below to ``executorch`` to run without PyTorch.
49+
Importing ``executorch.runtime`` does not import PyTorch, unless the setting below asks for ``torch``. When PyTorch is installed, ``Method.execute`` returns ``torch.Tensor`` outputs, and the first call that returns one imports ``torch``. Without PyTorch, or when importing it raises an ``ImportError``, it returns ``ExecuTorchResult`` outputs. Any other error from importing PyTorch is raised by every call that returns a tensor; set the setting below to ``executorch`` to run without PyTorch.
5050

5151
A future release will make the outputs follow the tensor inputs of each call. A call with any ``torch.Tensor`` input will return ``torch.Tensor`` outputs. A call whose tensor inputs are all NumPy arrays, other buffers, DLPack objects, or ``ExecuTorchResult`` values will return ``ExecuTorchResult`` outputs, even when PyTorch is installed. A call with no tensor inputs will return ``torch.Tensor`` outputs only if ``torch`` is already imported. Each call whose outputs will change warns with a ``FutureWarning``.
5252

‎extension/pybindings/README.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ the same for its outputs.
4444
Unless `EXECUTORCH_PYBINDINGS_TENSOR_OUTPUT` is `torch`, importing
4545
`portable_lib` does not import PyTorch. When PyTorch is installed, portable
4646
bindings return `torch.Tensor` outputs, and the first call that returns one
47-
imports `torch`. When PyTorch is not installed, outputs are `ExecuTorchResult`
48-
values. When it is installed but fails to import, every call that returns a
49-
tensor raises the import error; set the variable to `executorch` to run
50-
without PyTorch. Output conversion uses Python APIs and DLPack; the extension
51-
does not link against PyTorch.
47+
imports `torch`. When PyTorch is not installed, or importing it raises an
48+
`ImportError`, outputs are `ExecuTorchResult` values. Any other error from
49+
importing it is raised by every call that returns a tensor; set the variable
50+
to `executorch` to run without PyTorch. Output conversion uses Python APIs and
51+
DLPack; the extension does not link against PyTorch.
5252
`clone_outputs=True` returns storage that later executions do not overwrite,
5353
on the output's original device. With `clone_outputs=False`, results share
5454
runtime or input storage and later execution may overwrite that storage.

‎extension/pybindings/pybindings.cpp‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -802,10 +802,10 @@ bool keep_torch_outputs() {
802802
try {
803803
py::module_::import("torch");
804804
} catch (py::error_already_set& error) {
805-
// Only a torch that is not there counts as missing. A torch that is
806-
// there but fails to import, even with an ImportError, is reported.
807-
if (!error.matches(PyExc_ModuleNotFoundError) ||
808-
py::str(error.value().attr("name")).cast<std::string>() != "torch") {
805+
// As when torch was imported with ExecuTorch, a torch that fails with
806+
// an ImportError, such as for a missing library, counts as missing.
807+
// Any other error is reported.
808+
if (!error.matches(PyExc_ImportError)) {
809809
try {
810810
error.value().attr("add_note")(
811811
"ExecuTorch imports torch here to return torch.Tensor outputs. "

‎extension/pybindings/test/test_pybindings.py‎

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -406,17 +406,12 @@ def run(index):
406406
from executorch.extension.pybindings import portable_lib
407407
408408
module = portable_lib._load_for_executorch(program_path)
409-
# A torch that is there but broken, even with an ImportError or
410-
# a missing module of its own.
411-
for error_type, missing in (
412-
(RuntimeError, "torch"),
413-
(ImportError, "torch"),
414-
(ModuleNotFoundError, "torch._C"),
415-
):
416-
blocker.error, blocker.missing = error_type, missing
409+
# A torch that fails with an error other than ImportError is
410+
# reported at every call.
411+
for _ in range(2):
417412
try:
418413
module.forward([x, x])
419-
except error_type as error:
414+
except RuntimeError as error:
420415
assert "torch is unavailable" in str(error), error
421416
# add_note is new in Python 3.11.
422417
notes = "".join(getattr(error, "__notes__", []))
@@ -425,11 +420,13 @@ def run(index):
425420
), notes
426421
else:
427422
raise AssertionError("a broken torch must not be hidden")
428-
blocker.missing = "torch"
423+
# An ImportError, even for a part of torch, counts as missing,
424+
# as when torch was imported with ExecuTorch.
425+
blocker.error, blocker.missing = ImportError, "torch._C"
429426
check(lambda: module.forward([x, x]), ["ExecuTorchResult"])
430427
check(lambda: module.run_method("tensor_constant"), ["ExecuTorchResult"])
431428
# A missing torch is not looked for again on every call.
432-
assert blocker.attempts == 4, blocker.attempts
429+
assert blocker.attempts == 3, blocker.attempts
433430
assert "torch" not in sys.modules
434431
""",
435432
),

0 commit comments

Comments
 (0)