Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strange micropip failure when loading wheel from custom URL #1653

Open
deeplook opened this issue Jun 23, 2021 · 23 comments
Open

Strange micropip failure when loading wheel from custom URL #1653

deeplook opened this issue Jun 23, 2021 · 23 comments
Labels
bug Something isn't working core / type conversions

Comments

@deeplook
Copy link

I'm trying to install a soon-to-be-open-sourced package with zero dependencies in JupyterLite like this, but am getting a weird TypeError: exceptions must derive from BaseException. Any idea what I can check further?

import micropip
url = ("https://example.com/foobar-py2.py3-none-any.whl")
await micropip.install(url)
Traceback (most recent call last):
  File "<console>", line 6, in <module>
  File "/lib/python3.8/asyncio/futures.py", line 260, in __await__
    yield self  # This tells Task to wait for completion.
  File "/lib/python3.8/asyncio/tasks.py", line 349, in __wakeup
    future.result()
  File "/lib/python3.8/asyncio/futures.py", line 178, in result
    raise self._exception
  File "/lib/python3.8/asyncio/tasks.py", line 282, in __step
    result = coro.throw(exc)
  File "/lib/python3.8/site-packages/micropip.py", line 178, in install
    await gather(*wheel_promises)
  File "/lib/python3.8/asyncio/futures.py", line 260, in __await__
    yield self  # This tells Task to wait for completion.
  File "/lib/python3.8/asyncio/tasks.py", line 349, in __wakeup
    future.result()
  File "/lib/python3.8/asyncio/futures.py", line 178, in result
    raise self._exception
  File "/lib/python3.8/asyncio/tasks.py", line 282, in __step
    result = coro.throw(exc)
  File "/lib/python3.8/site-packages/micropip.py", line 123, in _install_wheel
    wheel = await _get_url(url)
  File "/lib/python3.8/site-packages/micropip.py", line 33, in _get_url
    resp = await fetch(url)
  File "/lib/python3.8/asyncio/futures.py", line 260, in __await__
    yield self  # This tells Task to wait for completion.
  File "/lib/python3.8/asyncio/tasks.py", line 349, in __wakeup
    future.result()
  File "/lib/python3.8/asyncio/futures.py", line 178, in result
    raise self._exception
TypeError: exceptions must derive from BaseException
@hoodmane
Copy link
Member

Well it looks like fetch(url) threw a weird object that Pyodide was unable to recognize as an exception. First try await fetch("https://example.com/foobar-py2.py3-none-any.whl") in Javascript and in Pyodide and double check that those succeed.

@deeplook
Copy link
Author

Ok, thanks! So, await micropip.fetch(url) returns a shorter traceback with the same exception:

Traceback (most recent call last):
  File "<console>", line 6, in <module>
  File "/lib/python3.8/asyncio/futures.py", line 260, in __await__
    yield self  # This tells Task to wait for completion.
  File "/lib/python3.8/asyncio/tasks.py", line 349, in __wakeup
    future.result()
  File "/lib/python3.8/asyncio/futures.py", line 178, in result
    raise self._exception
TypeError: exceptions must derive from BaseException

While in a Javascript kernel fetch("<SAME_URL>") returns [object Object].

@hoodmane
Copy link
Member

Try monkey patching fetch before loading Pyodide:

window.orig_fetch = fetch;
window.fetch = async function(...args){
	console.log("fetch called with args:", fetch);
	let resp;
	try {
		resp = await orig_fetch(...args);
	} catch(e) {
		console.log("fetch threw:", e);
		throw e;
	}
	console.log("fetch returned:", resp);
	return resp;
}

What does it say?

@deeplook
Copy link
Author

[object Object]

fetch called with args: async function(...args){
	console.log("fetch called with args:", fetch);
	let resp;
	try {
		resp = await orig_fetch(...args);
	} catch(e) {
		console.log("fetch threw:", e);
		throw e;
	}
	console.log("fetch returned:", resp);
	return resp;
}
fetch threw: TypeError: NetworkError when attempting to fetch resource.

@hoodmane
Copy link
Member

So it did await fetch(fetch)??

@hoodmane
Copy link
Member

And it seems to have thrown a perfectly reasonable error object in response, so I don't know why Pyodide isn't converting it to a valid Python exception...

@hoodmane
Copy link
Member

Very mysterious.

@hoodmane
Copy link
Member

Could you provide a reproduction? This behavior seems to suggest a serious bug -- logic errors aside I would hope that it could fail a bit more gracefully....

@deeplook
Copy link
Author

You can try with the URL behind this short link: http://s.here.com/TeGPQR which is publically reachable.

@hoodmane
Copy link
Member

So I just try to load that wheel on https://pyodide-cdn2.iodide.io/v0.17.0/full/console.html?

@hoodmane
Copy link
Member

Okay it reproduces thanks. I'll investigate and get back to you.

@deeplook
Copy link
Author

Thanks!

@hoodmane hoodmane added bug Something isn't working micropip labels Jun 24, 2021
@hoodmane hoodmane changed the title Getting TypeError for zero dependency package Strange micropip failure when loading custom wheel URL Jun 24, 2021
@hoodmane hoodmane changed the title Strange micropip failure when loading custom wheel URL Strange micropip failure when loading wheel from custom URL Jun 24, 2021
@hoodmane
Copy link
Member

hoodmane commented Jun 26, 2021

So the problem is that fetch is throwing a network error which for some reason the reflection code in jsproxy.c fails to detect as an error object, but only in firefox. In chrome it works fine. The reason the NetworkError is being thrown in the first place is probably because of a problem with coors. Anyways this bug is also only present in v0.17.0, on main Firefox handles this correctly. I tweaked the logic in hiwire_is_error recently due to a different bug and that tweak seems to have fixed this.

Closed as fixed on main.

@deeplook
Copy link
Author

Thanks for your efforts! If I understand correctly you didn't observe any issue on Chrome. I gave it a try, too, using Version 90.0.4430.212 (Official Build) (x86_64) on macOS 10.14.6 and I get his even more impressive traceback below. Can you shed some more light on this, please?

Traceback (most recent call last):
  File "<console>", line 5, in <module>
  File "/lib/python3.8/asyncio/futures.py", line 260, in __await__
    yield self  # This tells Task to wait for completion.
  File "/lib/python3.8/asyncio/tasks.py", line 349, in __wakeup
    future.result()
  File "/lib/python3.8/asyncio/futures.py", line 178, in result
    raise self._exception
  File "/lib/python3.8/asyncio/tasks.py", line 282, in __step
    result = coro.throw(exc)
  File "/lib/python3.8/site-packages/micropip.py", line 178, in install
    await gather(*wheel_promises)
  File "/lib/python3.8/asyncio/futures.py", line 260, in __await__
    yield self  # This tells Task to wait for completion.
  File "/lib/python3.8/asyncio/tasks.py", line 349, in __wakeup
    future.result()
  File "/lib/python3.8/asyncio/futures.py", line 178, in result
    raise self._exception
  File "/lib/python3.8/asyncio/tasks.py", line 282, in __step
    result = coro.throw(exc)
  File "/lib/python3.8/site-packages/micropip.py", line 123, in _install_wheel
    wheel = await _get_url(url)
  File "/lib/python3.8/site-packages/micropip.py", line 33, in _get_url
    resp = await fetch(url)
  File "/lib/python3.8/asyncio/futures.py", line 260, in __await__
    yield self  # This tells Task to wait for completion.
  File "/lib/python3.8/asyncio/tasks.py", line 349, in __wakeup
    future.result()
  File "/lib/python3.8/asyncio/futures.py", line 178, in result
    raise self._exception
JsException: TypeError: Failed to fetch
PythonError: Traceback (most recent call last):
  File "/lib/python3.8/asyncio/futures.py", line 178, in result
    raise self._exception
  File "/lib/python3.8/asyncio/tasks.py", line 282, in __step
    result = coro.throw(exc)
  File "/lib/python3.8/site-packages/pyolite/interpreter.py", line 25, in run
    result = await self.run_complete
  File "/lib/python3.8/asyncio/futures.py", line 260, in __await__
    yield self  # This tells Task to wait for completion.
  File "/lib/python3.8/asyncio/tasks.py", line 349, in __wakeup
    future.result()
  File "/lib/python3.8/asyncio/futures.py", line 178, in result
    raise self._exception
  File "/lib/python3.8/asyncio/tasks.py", line 282, in __step
    result = coro.throw(exc)
  File "/lib/python3.8/site-packages/pyodide/console.py", line 253, in load_packages_and_run
    raise e
  File "/lib/python3.8/site-packages/pyodide/console.py", line 247, in load_packages_and_run
    result = await eval_code_async(
  File "/lib/python3.8/site-packages/pyodide/_base.py", line 419, in eval_code_async
    return await CodeRunner(
  File "/lib/python3.8/site-packages/pyodide/_base.py", line 278, in run_async
    res = await res
  File "<console>", line 5, in <module>
  File "/lib/python3.8/asyncio/futures.py", line 260, in __await__
    yield self  # This tells Task to wait for completion.
  File "/lib/python3.8/asyncio/tasks.py", line 349, in __wakeup
    future.result()
  File "/lib/python3.8/asyncio/futures.py", line 178, in result
    raise self._exception
  File "/lib/python3.8/asyncio/tasks.py", line 282, in __step
    result = coro.throw(exc)
  File "/lib/python3.8/site-packages/micropip.py", line 178, in install
    await gather(*wheel_promises)
  File "/lib/python3.8/asyncio/futures.py", line 260, in __await__
    yield self  # This tells Task to wait for completion.
  File "/lib/python3.8/asyncio/tasks.py", line 349, in __wakeup
    future.result()
  File "/lib/python3.8/asyncio/futures.py", line 178, in result
    raise self._exception
  File "/lib/python3.8/asyncio/tasks.py", line 282, in __step
    result = coro.throw(exc)
  File "/lib/python3.8/site-packages/micropip.py", line 123, in _install_wheel
    wheel = await _get_url(url)
  File "/lib/python3.8/site-packages/micropip.py", line 33, in _get_url
    resp = await fetch(url)
  File "/lib/python3.8/asyncio/futures.py", line 260, in __await__
    yield self  # This tells Task to wait for completion.
  File "/lib/python3.8/asyncio/tasks.py", line 349, in __wakeup
    future.result()
  File "/lib/python3.8/asyncio/futures.py", line 178, in result
    raise self._exception
JsException: TypeError: Failed to fetch

    at new_error (https://cdn.jsdelivr.net/pyodide/v0.17.0/full/pyodide.asm.js:9:175413)
    at wrap_exception (https://cdn.jsdelivr.net/pyodide/v0.17.0/full/pyodide.asm.wasm:wasm-function[297]:0x1a6c94)
    at FutureDoneCallback_call (https://cdn.jsdelivr.net/pyodide/v0.17.0/full/pyodide.asm.wasm:wasm-function[379]:0x1aaab3)
    at byn$fpcast-emu$FutureDoneCallback_call (https://cdn.jsdelivr.net/pyodide/v0.17.0/full/pyodide.asm.wasm:wasm-function[14420]:0x7a257c)
    at _PyObject_MakeTpCall (https://cdn.jsdelivr.net/pyodide/v0.17.0/full/pyodide.asm.wasm:wasm-function[760]:0x1cabed)
    at __static_1198 (https://cdn.jsdelivr.net/pyodide/v0.17.0/full/pyodide.asm.wasm:wasm-function[2906]:0x2c54f1)
    at byn$fpcast-emu$__static_1198 (https://cdn.jsdelivr.net/pyodide/v0.17.0/full/pyodide.asm.wasm:wasm-function[22640]:0x7bf866)
    at __static_678 (https://cdn.jsdelivr.net/pyodide/v0.17.0/full/pyodide.asm.wasm:wasm-function[1621]:0x21220e)
    at byn$fpcast-emu$__static_678 (https://cdn.jsdelivr.net/pyodide/v0.17.0/full/pyodide.asm.wasm:wasm-function[22120]:0x7bdce6)
    at PyVectorcall_Call (https://cdn.jsdelivr.net/pyodide/v0.17.0/full/pyodide.asm.wasm:wasm-function[763]:0x1cb006)

@hoodmane
Copy link
Member

Yeah on tip of tree I think there is no problem, on v0.17.0 there is one. JsException: TypeError: Failed to fetch is the correct response (unlike TypeError: exceptions must derive from BaseException). I believe that the fetch request is failing due to COORS. When I tested it on my computer in Javascript fetch(yoururl) raises TypeError: Failed to fetch in Chrome and TypeError: NetworkError when attempting to fetch resource. in Firefox.

Does fetch(yoururl) in Javascript succeed for you?

@deeplook
Copy link
Author

In a JS kernel on Chrome I get for both, fetch(myurl) as well as fetch("https://spatial.utk.edu/download/pyshp-2.1.3-py3-none-any.whl") the result [object Object].

@hoodmane
Copy link
Member

Hmmmmm. Okay, I think I was never reproducing this then.

@hoodmane hoodmane reopened this Jun 29, 2021
@hoodmane
Copy link
Member

So can you reproduce it in https://pyodide-cdn2.iodide.io/v0.17.0/full/console.html and or https://pyodide-cdn2.iodide.io/dev/full/console.html? There I get COORS errors for your URLs I think. To allow the request to succeed do you need special HTTP headers?

@deeplook
Copy link
Author

Both give me JsException: TypeError: Failed to fetch for both URLs.

@deeplook
Copy link
Author

No special headers needed.

@hoodmane
Copy link
Member

So if you use fetch directly in the browser console it works?

@hoodmane
Copy link
Member

For f = await fetch("https://spatial.utk.edu/download/pyshp-2.1.3-py3-none-any.whl") Firefox says:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://spatial.utk.edu/download/pyshp-2.1.3-py3-none-any.whl. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

Uncaught (in promise) TypeError: NetworkError when attempting to fetch resource.

@deeplook
Copy link
Author

My Firefox Developer Edition doesn't...

Screenshot 2021-06-29 at 19 10 55

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working core / type conversions
Projects
None yet
Development

No branches or pull requests

2 participants