Thanks to visit codestin.com
Credit goes to github.com

Skip to content

webassembly/objpyproxy: Avoid throwing on symbols or iterator. #17604

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

Merged
merged 1 commit into from
Jul 4, 2025

Conversation

WebReflection
Copy link
Contributor

Not only our latest stack uses internally symbols to brand check its own proxies, there are various 3rd party libraries that might perform similar checks and you also have:

if (prop === Symbol.iterator) ...

in your get trap but a check before such as:

if (Symbol.iterator in ref) ...

would fail with an error that states that a string was expected but a symbol was received.

This MR would like to fix the situation for at least known symbols you also handle internally.

Summary

Testing

Trade-offs and Alternatives

@WebReflection WebReflection changed the title [webassembly] Avoid throwing on symbols + allow iterator check webassembly: Avoid throwing on symbols + allow iterator check Jul 2, 2025
@WebReflection WebReflection changed the title webassembly: Avoid throwing on symbols + allow iterator check ports/webassembly/objpyproxy.js: Avoid throwing on symbols + allow iterator check Jul 2, 2025
@WebReflection WebReflection changed the title ports/webassembly/objpyproxy.js: Avoid throwing on symbols + allow iterator check ports/webassembly/objpyproxy.js: Avoid throwing on symbols or iterator Jul 2, 2025
@WebReflection WebReflection force-pushed the patch-1 branch 7 times, most recently from 952ac8b to ebd9662 Compare July 2, 2025 18:29
Copy link

codecov bot commented Jul 2, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.56%. Comparing base (16f9d7f) to head (e33a0f4).
Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #17604   +/-   ##
=======================================
  Coverage   98.56%   98.56%           
=======================================
  Files         169      169           
  Lines       21946    21946           
=======================================
  Hits        21632    21632           
  Misses        314      314           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@WebReflection WebReflection changed the title ports/webassembly/objpyproxy.js: Avoid throwing on symbols or iterator webassembly/objpyproxy: Avoid throwing on symbols or iterator. Jul 2, 2025
@WebReflection
Copy link
Contributor Author

for quick check about current state VS state after this MR (published on my npm account with same version + this MR in) and for history sake, here a test that shows the issue:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>MicroPython VS JS Symbols</title>
  <script type="module">
    import { loadMicroPython } from 'https://cdn.jsdelivr.net/npm/@micropython/micropython-webassembly-pyscript/micropython.mjs';
    // change this with webreflection instead to see it working -- ^^^^^^^^^^^
    import codedent from 'https://esm.run/codedent';
    const mpy = await loadMicroPython();
    // sync or async it doesn't matter
    await mpy.runPythonAsync(codedent(document.querySelector('script[type=micropython]').textContent));
  </script>
  <script>
    globalThis.hasSymbol = (symbol, ref) => symbol in ref;
    globalThis.getSymbol = (symbol, ref) => ref[symbol];

    // some 3rd party JS library might use symbols to brand-check
    // so it's not about symbols traveling from MicroPython
    // it's about MicroPython proxies traps not understanding symbols
    globalThis.hasIterator = ref => Symbol.iterator in ref;
  </script>
  <script type="micropython">
    import js

    # change this to Symbol.toStringTag to see it throwing all over
    symbol = js.Symbol.iterator

    # this passes only with Symbol.iterator
    print('get', js.getSymbol(symbol, []))

    # these both fail miserably
    print('has', js.hasSymbol(symbol, []))
    print('direct', js.hasIterator([]))
  </script>
</head>
</html>

@dpgeorge
Copy link
Member

dpgeorge commented Jul 3, 2025

Thanks for the patch.

Was this a regression that worked in a prior version?

@ntoll
Copy link
Contributor

ntoll commented Jul 3, 2025

Yes, it's a regression. If you compare MicroPython bundled in the previous version of PyScript 2025.5.1 with current PyScript (2025.7.1) there's a difference in behaviour.

@WebReflection
Copy link
Contributor Author

@dpgeorge

Was this a regression that worked in a prior version?

I don't think so ... I've never seen symbol in this code to my memory and I don't think that part has been changed/touched

@ntoll

Yes, it's a regression. If you compare MicroPython bundled in the previous version of PyScript 2025.5.1 with current PyScript (2025.7.1) there's a difference in behaviour.

that's my miscommunication ... the regression is introduced in our latest PyScript because we use better primitives to handle worker/main use cases and those better primitives revealed this issue with MicroPython which has been broken to date but now it starts hurting when paired with more modern libraries and code.

As mentioned in Discord, there's much more to do to have MicroPython fully handling symbols but for the time being, at least having it not throwing and return false for has traps unless it's Symbol.iterator which again, it doesn't handle well but it's needed to make list and tuples iterables, would be ideal and I'd be more than happy to provide a way wider MR that touches tons of things around the Proxy dance done in MicroPython, as an attempt to contribute the JS/Python interoperability story and never have surprises, if that's welcomed and desired in here too.

@ntoll
Copy link
Contributor

ntoll commented Jul 3, 2025

@WebReflection ah, my bad. I misconstrued the context.

The MicroPython/JS proxy dance could be something to discuss in our next call (21st July)..? I think if we (PyScript - i.e. Andrea) can contribute back to upstream MicroPython we're telling and embodying a good FLOSS open source story.

@WebReflection
Copy link
Contributor Author

WebReflection commented Jul 3, 2025

@dpgeorge last time this was touched is in here: #15293 I should've reviewed that (or provided hints around this topic) ... symbols are very special (and weird) beast in JS and particularly from workers these cannot ever travel but even on main we have issues in checking if special, libraries defined, symbols are present in objects to simply brand check these or guard against undesired access (private symbols in JS are also a thing, these are all typeof "symbol").

This MR prevents those checks from failing but it also grant that the only one understood and implemented in the get trap is reflected ... and about that, it should be reflected in get only if the underlying python object is iterable, which luckily enough for Python is probably dictionaries too, but surely not callbacks ... so there's much more to consider to have 100% symbol support but at least please let's not throw errors for traps that by specifications expect either strings or symbols (which is all traps that accept a key among the target): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/has#parameters


edid I did review that and forgot checking the has trap 🤦🤦🤦

#15293 (comment)

Copy link
Member

@dpgeorge dpgeorge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fix looks good to me, and the new test covers the new code and passes.

JavaScript code uses "Symbol in object" to brand check its own proxies, and
such checks should also work on the Python side.

Signed-off-by: Andrea Giammarchi <[email protected]>
@dpgeorge dpgeorge merged commit e33a0f4 into micropython:master Jul 4, 2025
31 of 32 checks passed
@ntoll
Copy link
Contributor

ntoll commented Jul 4, 2025

Bravo and thank you..! Has this made it into a release..?

@dpgeorge
Copy link
Member

dpgeorge commented Jul 4, 2025

@ntoll
Copy link
Contributor

ntoll commented Jul 4, 2025

Awesome work. As always. 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants