brython-dev / brython

Brython (Browser Python) is an implementation of Python 3 running in the browser
BSD 3-Clause "New" or "Revised" License
6.4k stars 512 forks source link

JavaScript library-related issue "AttributeError: 'Javascript Promise' object has no attribute..." #2399

Closed tjnaughton closed 8 months ago

tjnaughton commented 8 months ago

Hi,

There seems to be a regression using some JavaScript libraries, or at least the specific JavaScript library GSAP I've tried. For example, with a html file containing something like this

<head>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>

<script src="brython.js"></script>
<script src="brython_stdlib.js"></script>
</head>

<body onload="brython({debug: 1, cache: false})">

<script type='text/python' src='test.py'></script>

<div id='div_id'>
  <img id='image' src='image.svg'>
</div>
</body>
</html>

and a test.py with something like this

from browser import document, window

gsap = window.gsap

tl = gsap.timeline({'defaults': {'duration': 1, 'ease': 'sine.inOut'}})

tl.to(document['image'], {'x': 100})

the image is animated.

The code above works with release Brython-3.12.1 (#45b714b 2023-11-27) and earlier, but raises the exception below with the development version from earlier today #be12eec, but also with the development version from a month ago #2bf694b (2024-02-10).

brython.js:10958 Traceback (most recent call last):
  File "test.py", line 26, in <module>
    tl.to(document['image'], {'x': 100})
    ^^^^^
AttributeError: 'Javascript Promise' object has no attribute 'to'

Thank you!

moepnse commented 8 months ago

Hey @tjnaughton!

I think it might be related to commit #25e0317, since timeline objects support the Thenable interface [1]. [[2]](https://gsap.com/docs/v3/GSAP/Timeline/then())

I'll take a look at it.

As I said, timeline objects have support for the Thenable interface, so you could change your code like this:

<html>
  <head>
      <meta charset="utf-8">
      <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
      <script type="text/javascript"
          src="https://cdn.jsdelivr.net/npm/brython@3.12/brython.min.js">
      </script>
      <script type="text/javascript"
          src="https://cdn.jsdelivr.net/npm/brython@3.12/brython_stdlib.js">
      </script>
  </head>
  <body onload="brython({debug: 1, cache: false})">
    <script type='text/python'>
from browser import document, window, aio

async def main():
    gsap = window.gsap
    tl = await gsap.timeline({'defaults': {'duration': 1, 'ease': 'sine.inOut'}})
    tl.to(document['image'], {'x': 100})

aio.run(main())
    </script>
    <div id='div_id'>
      <img id='image' src='https://upload.wikimedia.org/wikipedia/commons/1/14/Animated_PNG_example_bouncing_beach_ball.png'>
    </div>
  </body>
</html>
moepnse commented 8 months ago

Thanks for reporting this!

Looks like I introduced the bug some time ago while fixing another bug. Sorry about that! 🙈

The latest development snapshot should fix the problem, and both ways should work now, as you can see in the following example:

<html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
    <script src="https://raw.githack.com/brython-dev/brython/master/www/src/brython.js"
      crossorigin="anonymous">
    </script>
    <script src="https://raw.githack.com/brython-dev/brython/master/www/src/brython_stdlib.js"
      crossorigin="anonymous">
    </script>
  </head>
  <body onload="brython({debug: 1, cache: false})">
    <script type='text/python'>
from browser import document, window, aio

async def main():
    tl = await gsap.timeline({'defaults': {'duration': 1, 'ease': 'sine.inOut'}})
    tl.to(document['image2'], {'x': 100})

gsap = window.gsap
aio.run(main())

tl = gsap.timeline({'defaults': {'duration': 1, 'ease': 'sine.inOut'}})
tl.to(document['image1'], {'x': 250})
    </script>
    <div id='div_id'>
      <img id='image1' src='https://upload.wikimedia.org/wikipedia/commons/1/14/Animated_PNG_example_bouncing_beach_ball.png'>
      <img id='image2' src='https://upload.wikimedia.org/wikipedia/commons/1/14/Animated_PNG_example_bouncing_beach_ball.png'>
    </div>
  </body>
</html>

I will write a test for this to make sure it does not happen again.

Good night!

tjnaughton commented 8 months ago

That's great, thanks so much for such a quick reaction. Brython makes it so convenient to use existing JavaScript libraries, and using syntax that's basically identical to that in the documentation of those libraries. Amazing!