mirror of https://github.com/pyodide/pyodide.git
Fix accessing Python reserved word attributes on js objects (#3926)
Removes some of the compatibility break introduced in #3617. Update faq too.
This commit is contained in:
parent
e3789b8dfa
commit
54540364d4
|
@ -53,6 +53,11 @@ myst:
|
|||
`{env: {HOME: whatever_directory}}`.
|
||||
{pr}`3870`
|
||||
|
||||
- {{ Fix }} `getattr(jsproxy, 'python_reserved_word')` works as expected again
|
||||
(as well as `hasattr` and `setattr`). This fixes a regression introduced in
|
||||
{pr}`3617`.
|
||||
{pr}`3926`
|
||||
|
||||
### Packages
|
||||
|
||||
- OpenBLAS has been added and scipy now uses OpenBLAS rather than CLAPACK
|
||||
|
|
|
@ -441,30 +441,70 @@ lambda = (x) => {return x + 1};
|
|||
pyodide.runPython(`from js import lambda; print(lambda(1))`);
|
||||
```
|
||||
|
||||
For JS objects with attributes that are Python reserved keywords, {py:func}`getattr` and {py:func}`setattr` can be used to access the attribute by name:
|
||||
If you try to access a Python reserved word followed by one or more underscores
|
||||
on a `JsProxy`, Pyodide will remove a single underscore:
|
||||
|
||||
```pyodide
|
||||
pyodide.runPython(`
|
||||
from js import Array
|
||||
print(Array.from_([1,2,3]))
|
||||
`);
|
||||
```
|
||||
|
||||
If you meant to access the keyword with an underscore at the end, you'll have to
|
||||
add an extra one:
|
||||
|
||||
```pyodide
|
||||
globalThis.lambda = 7;
|
||||
globalThis.lambda_ = 8;
|
||||
pyodide.runPython(`
|
||||
from js import lambda_, lambda__
|
||||
print(lambda_, lambda__) # 7, 8
|
||||
`);
|
||||
```
|
||||
|
||||
Another example:
|
||||
|
||||
```pyodide
|
||||
people = {global: "lots and lots"};
|
||||
pyodide.runPython(`
|
||||
from js import people
|
||||
# the dir contains global_ but not global:
|
||||
assert "global_" in dir(people)
|
||||
assert "global" not in dir(people)
|
||||
people.global_ = 'even more'
|
||||
print(people.global_)
|
||||
`);
|
||||
```
|
||||
|
||||
You can also use `getattr`, `setattr`, and `delattr` to access the attribute:
|
||||
|
||||
```pyodide
|
||||
pyodide.runPython(`
|
||||
from js import Array
|
||||
fromFunc = getattr(Array, 'from')
|
||||
print(fromFunc([1,2,3]))
|
||||
`);
|
||||
`);
|
||||
|
||||
people = {global: "lots and lots"};
|
||||
pyodide.runPython(`
|
||||
from js import people
|
||||
setattr(people, 'global', 'even more')
|
||||
print(getattr(people, 'global'))
|
||||
`);
|
||||
`);
|
||||
```
|
||||
|
||||
For objects whose names are keywords, one can similarly use {py:func}`getattr` on the `js` module itself:
|
||||
For JavaScript globals whose names are keywords, one can similarly use
|
||||
{py:func}`getattr` on the `js` module itself:
|
||||
|
||||
```pyodide
|
||||
lambda = (x) => {return x + 1};
|
||||
globalThis.lambda = 7;
|
||||
globalThis.lambda_ = 8;
|
||||
pyodide.runPython(`
|
||||
import js
|
||||
js_lambda = getattr(js, 'lambda')
|
||||
print(js_lambda(1))
|
||||
`);
|
||||
js_lambda_ = getattr(js, 'lambda_')
|
||||
js_lambda__ = getattr(js, 'lambda__')
|
||||
print(js_lambda, js_lambda_, js_lambda__) # 7, 7, 8
|
||||
`);
|
||||
```
|
||||
|
|
|
@ -947,7 +947,7 @@ EM_JS(bool, isReservedWord, (int word), {
|
|||
* action: a javascript string, one of get, set, or delete. For error reporting.
|
||||
* word: a javascript string, the property being accessed
|
||||
*/
|
||||
EM_JS(int, normalizeReservedWords, (int action, int word), {
|
||||
EM_JS(int, normalizeReservedWords, (int word), {
|
||||
// clang-format off
|
||||
// 1. if word is not a reserved word followed by 0 or more underscores, return
|
||||
// it unchanged.
|
||||
|
@ -960,19 +960,14 @@ EM_JS(int, normalizeReservedWords, (int action, int word), {
|
|||
if (noTrailing_ !== word) {
|
||||
return word.slice(0, -1);
|
||||
}
|
||||
// 3. If the word is exactly a reserved word, this is an error.
|
||||
let action_ptr = stringToNewUTF8(action);
|
||||
let word_ptr = stringToNewUTF8(word);
|
||||
_setReservedError(action_ptr, word_ptr);
|
||||
_free(action_ptr);
|
||||
_free(word_ptr);
|
||||
throw new Module._PropagatePythonError();
|
||||
// 3. If the word is exactly a reserved word, return it unchanged
|
||||
return word;
|
||||
// clang-format on
|
||||
});
|
||||
|
||||
EM_JS_REF(JsRef, JsObject_GetString, (JsRef idobj, const char* ptrkey), {
|
||||
let jsobj = Hiwire.get_value(idobj);
|
||||
let jskey = normalizeReservedWords("get", UTF8ToString(ptrkey));
|
||||
let jskey = normalizeReservedWords(UTF8ToString(ptrkey));
|
||||
if (jskey in jsobj) {
|
||||
return Hiwire.new_value(jsobj[jskey]);
|
||||
}
|
||||
|
@ -985,7 +980,7 @@ JsObject_SetString,
|
|||
(JsRef idobj, const char* ptrkey, JsRef idval),
|
||||
{
|
||||
let jsobj = Hiwire.get_value(idobj);
|
||||
let jskey = normalizeReservedWords("set", UTF8ToString(ptrkey));
|
||||
let jskey = normalizeReservedWords(UTF8ToString(ptrkey));
|
||||
let jsval = Hiwire.get_value(idval);
|
||||
jsobj[jskey] = jsval;
|
||||
});
|
||||
|
@ -993,7 +988,7 @@ JsObject_SetString,
|
|||
|
||||
EM_JS_NUM(errcode, JsObject_DeleteString, (JsRef idobj, const char* ptrkey), {
|
||||
let jsobj = Hiwire.get_value(idobj);
|
||||
let jskey = normalizeReservedWords("delete", UTF8ToString(ptrkey));
|
||||
let jskey = normalizeReservedWords(UTF8ToString(ptrkey));
|
||||
delete jsobj[jskey];
|
||||
});
|
||||
|
||||
|
|
|
@ -2409,12 +2409,15 @@ def test_python_reserved_keywords(selenium):
|
|||
assert o.async___ == 3
|
||||
assert getattr(o, "async_") == 1 # noqa: B009
|
||||
assert getattr(o, "async__") == 2 # noqa: B009
|
||||
with pytest.raises(AttributeError, match="async"):
|
||||
getattr(o, "async")
|
||||
with pytest.raises(AttributeError, match="reserved.*set.*'async_'"):
|
||||
setattr(o, "async", 2)
|
||||
with pytest.raises(AttributeError, match="reserved.*delete.*'async_'"):
|
||||
delattr(o, "async")
|
||||
assert getattr(o, "async") == 1
|
||||
|
||||
assert hasattr(o, "async_")
|
||||
assert hasattr(o, "async")
|
||||
setattr(o, "async", 2)
|
||||
assert o.async_ == 2
|
||||
delattr(o, "async")
|
||||
assert not hasattr(o, "async_")
|
||||
assert not hasattr(o, "async")
|
||||
|
||||
|
||||
@run_in_pyodide
|
||||
|
|
Loading…
Reference in New Issue