This matches the behaviour of run-tests.py, which sets cwd to the directory
containing the test script, which helps to isolate the filesystem.
It means that the SSL tests no longer need to know the name of their
containing directory to find the certificate files, and helps to run these
tests on bare-metal.
Signed-off-by: Damien George <damien@micropython.org>
The existing thread_sleep1.py test only tests execution, not accuracy, of
time.sleep. Also the existing test only tests sleep(0) on targets like rp2
that can only create a single thread.
The new test in this commit checks for timing accuracy on the main thread
and one other thread when they run at the same time.
Signed-off-by: Damien George <damien@micropython.org>
The aim of this commit is to make it so that the existing thread tests can
be used to test the _thread module on the rp2 port. The rp2 port only
allows up to one thread to be created at a time, and does not have the GIL
enabled.
The following changes have been made:
- run-tests.py skips mutation tests on rp2, because there's no GIL.
- run-tests.py skips other tests on rp2 that require more than one thread.
- The tests stop trying to start a new thread after there is an OSError,
which indicates that the system cannot create more threads.
- Some of these tests also now run the test function on the main thread,
not just the spawned threads.
- In some tests the output printing is adjusted so it's the same regardless
of how many threads were spawned.
- Some time.sleep(1) are replaced with time.sleep(0) to make the tests run
a little faster (finish sooner when the work is done).
For the most part the tests are unchanged for existing platforms like esp32
and unix.
Signed-off-by: Damien George <damien@micropython.org>
The poll_obj_t instances have their pollfd field point into this
allocation. So if re-allocating results in a move, we need to update the
existing poll_obj_t's.
Update the test to cover this case.
Fixes issue #12887.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This adds asyncio ssl support with SSLContext and the corresponding
tests in `tests/net_inet` and `tests/multi_net`.
Note that not doing the handshake on connect will delegate the handshake to
the following `mbedtls_ssl_read/write` calls. However if the handshake
fails when a client certificate is required and not presented by the peer,
it needs to be notified of this handshake error (otherwise it will hang
until timeout if any). Finally at MicroPython side raise the proper
mbedtls error code and message.
Signed-off-by: Carlos Gil <carlosgilglez@gmail.com>
Changes are:
- use ssl.SSLContext.wrap_socket instead of ssl.wrap_socket
- disable check_hostname and call load_default_certs() where appropriate,
to get CPython to run the tests correctly
- pass socket.AF_INET to getaddrinfo and socket.socket(), to force IPv4
- change tests to use github.com instead of google.com, because certificate
validation was failing with google.com
Signed-off-by: Damien George <damien@micropython.org>
And only enable this method when the relevant feature is available in
mbedtls. Otherwise, if mbedtls doesn't support getting the peer
certificate, this method always returns None and it's confusing why it does
that. It's better to remove the method altogether, so the error trying to
use it is more obvious.
Signed-off-by: Damien George <damien@micropython.org>
This commit adds:
1) Methods to SSLContext class that match CPython signature:
- `SSLContext.load_cert_chain(certfile, keyfile)`
- `SSLContext.load_verify_locations(cafile=, cadata=)`
- `SSLContext.get_ciphers()` --> ["CIPHERSUITE"]
- `SSLContext.set_ciphers(["CIPHERSUITE"])`
2) `sslsocket.cipher()` to get current ciphersuite and protocol
version.
3) `ssl.MBEDTLS_VERSION` string constant.
4) Certificate verification errors info instead of
`MBEDTLS_ERR_X509_CERT_VERIFY_FAILED`.
5) Tests in `net_inet` and `multi_net` to test these new methods.
`SSLContext.load_cert_chain` method allows loading key and cert from disk
passing a filepath in `certfile` or `keyfile` options.
`SSLContext.load_verify_locations`'s `cafile` option enables the same
functionality for ca files.
Signed-off-by: Carlos Gil <carlosgilglez@gmail.com>
Otherwise passing in a non-integer can lead to an invalid memory access.
Thanks to Junwha Hong and Wonil Jang @S2Lab, UNIST for finding the issue.
Fixes issue #13007.
Signed-off-by: Damien George <damien@micropython.org>
Set the position of new line tokens as the end of the preceding line
instead of the beginning of the next line. This is done by first moving
the pointer to the end of the current line to skip any whitespace, record
the position for the token, then finaly skip any other line and whitespace.
The previous behavior was to skip every new line and whitespace, including
the indent of the next line, before recording the token position.
(Note that both lex->emit_dent and lex->nested_bracket_level equal 0 if
had_physical_newline == true, which allows simplifying the if-logic for
MP_TOKEN_NEWLINE.)
And update the cmd_parsetree.py test expected output, because the position
of the new-line token has changed.
Fixes issue #12792.
Signed-off-by: Mathieu Serandour <mathieu.serandour@numworks.fr>
This updates a small number of files that change with ruff-format's (vs
black's) rules.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This test depends on the order in which qstrs are stored in ROM, which
affects the order in which `dir()` will probe the object to see what it
supports. Because of the lazy-loading in asyncio/__init__.py, if it
tries to do e.g. `wait_for_ms` before `funcs` then it will import funcs,
making `funcs` later succeed. But in the other way around, `funcs` will
initially not be found.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
These tests are designed to measure changes in performance relating to:
- string interning / searching for existing strings
- map lookup
- string operations
- string hashing
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
The unwritten API contract expected of a VFS.getcwd() by mp_vfs_getcwd()
is that its return value should be either "" or "/" when the CWD is at
the root of the VFS and otherwise start with a slash and not end with a
slash. This was not correctly implemented in VfsPosix for instances with
a non-empty root - the required leading slash, if any, was cut off
because the root length includes a trailing slash. This would result in
missing slashes in the middle of the return value of os.getcwd() or in
uninitialized garbage from beyond a string's null terminator when the
CWD was at the VFS root.
Signed-off-by: Christian Walther <cwalther@gmx.ch>
The unwritten API contract expected of a VFS by mp_vfs_lookup_path() is
that paths passed in are relative to the root of the VFS if they start
with '/' and relative to the current directory of the VFS otherwise.
This was not correctly implemented in VfsPosix for instances with a
non-empty root - all paths were interpreted relative to the root. Fix
that. Since VfsPosix tracks its CWD using the "external" CWD of the Unix
process, the correct handling for relative paths is to pass them through
unmodified.
Also, when concatenating absolute paths, fix an off-by-one resulting in
a harmless double slash (the root path already has a trailing slash).
Signed-off-by: Christian Walther <cwalther@gmx.ch>
These tests test an unrealistic situation and only pass by accident due
to a bug. The upcoming fix for the bug would make them fail.
The unrealistic situation is that VfsPosix methods are called with
relative paths while the current working directory is somewhere outside
of the root of the VFS. In the intended use of VFS objects via
os.mount() (as opposed to calling methods directly as the tests do),
this never happens, as mp_vfs_lookup_path() directs incoming calls to
the VFS that contains the CWD.
Make the testing situation realistic by changing the working directory
to the root of the VFS before calling methods on it, as the subsequent
relative path accesses expect.
Thanks to the preceding commit, the tests still pass, but still for the
wrong reason. The following commit "Fix relative paths on non-root VFS"
will make them pass for the correct reason.
Signed-off-by: Christian Walther <cwalther@gmx.ch>
A VfsPosix created with a relative root path would get confused when
chdir() was called on it and become unable to properly resolve absolute
paths, because changing directories effectively shifted its root. The
simplest fix for that would be to say "don't do that", but since the
unit tests themselves do it, fix it by making a relative path absolute
before storing it.
Signed-off-by: Christian Walther <cwalther@gmx.ch>
This wasn't correctly accounting for the bits-per-pixel and was returning a
bufinfo struct with the incorrect length. Instead, just forward directly
to the underlying buffer object.
Fixes issue #12563.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This ensures that the buffer is large enough for the specified width,
height, bits-per-pixel, and stride.
Also makes the legacy FrameBuffer1 constructor re-use the FrameBuffer
make_new to save some code size.
Fixes issue #12562.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This commit adds tests for bound method comparison and hashing to support
the changes in the previous commit.
Signed-off-by: Ned Konz <ned@productcreationstudio.com>
If a non-string buffer was passed to execfile, then it would be passed
as a non-null-terminated char* to mp_lexer_new_from_file.
This changes mp_lexer_new_from_file to take a qstr instead (as in almost
all cases a qstr will be created from this input anyway to set the
`__file__` attribute on the module).
This now makes execfile require a string (not generic buffer) argument,
which is probably a good fix to make anyway.
Fixes issue #12522.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
"Raise SomeException() from None" is a common Python idiom to suppress
chained exceptions and thus shouldn't trigger a warning on a version of
Python that doesn't support them in the first place.
The following code:
server = await asyncio.start_server(...)
async with server:
... code that raises ...
would lose the original exception because the server's task would not have
had a chance to be scheduled yet, and so awaiting the task in wait_closed
would raise the cancellation instead of the original exception.
Additionally, ensures that explicitly cancelling the parent task delivers
the cancellation correctly (previously was masked by the server loop), now
this only happens if the server was closed, not when the task was
cancelled.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
1. Remove the skip for detecting support for polling user-defined objects
as this is always possible now on all ports.
2. Don't print when the scheduled task runs as the ordering of this
relative to the other prints is dependent on other factors (e.g. if
using the native emitter).
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Update expired certificate, increase time validity period to five years and
fix command arguments typos in commentaries.
Signed-off-by: Carlos Gil <carlosgilglez@gmail.com>
The PYBD_SF2 is right on the limit of being able to run this test and so
it succeeds the first two cases and fails the next two with MemoryError.
This causes it to SKIP, but that only works if it's the first thing
printed. So reverse the order of the tests so it fails on the biggest
one first.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Increase asyncio tests timeouts to account for different WiFi modules and
CPU clocks on different boards.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
In CPython, `_thread.start_new_thread()` returns an ID that is the same ID
that is returned by `_thread.get_ident()`. The current MicroPython
implementation of `_thread.start_new_thread()` always returns `None`.
This modifies the required functions to return a value. The native thread
id is returned since this can be used for interop with other functions, for
example, `pthread_kill()` on *nix. `_thread.get_ident()` is also modified
to return the native thread id so that the values match and avoids the need
for a separate `native_id` attribute.
Fixes issue #12153.
Signed-off-by: David Lechner <david@pybricks.com>
Add the buffer attribute to sys.stdin, sys.stdout and sys.stderr. This
provides raw access to underlying stdio streams for the unix port (and
others that use VfsPosix).
Signed-off-by: stephanelsmith <stephane.smith@titansensor.com>
Because mpy_ld.py doesn't know the target object representation, it emits
instances of `MP_OBJ_NEW_QSTR(MP_QSTR_Foo)` as const string objects, rather
than qstrs. However this doesn't work for map keys (e.g. for a locals dict)
because the map has all_keys_are_qstrs flag is set (and also auto-complete
requires the map keys to be qstrs).
Instead, emit them as regular qstrs, and make a functioning MP_OBJ_NEW_QSTR
function available (via `native_to_obj`, also used for e.g. making
integers).
Remove the code from mpy_ld.py to emit qstrs as constant strings, but leave
behind the scaffold to emit constant objects in case we want to do use this
in the future.
Strictly this should be a .mpy sub-version bump, even though the function
table isn't changing, it does lead to a change in behavior for a new .mpy
running against old MicroPython. `mp_native_to_obj` will incorrectly return
the qstr value directly as an `mp_obj_t`, leading to unexpected results.
But given that it's broken at the moment, it seems unlikely that anyone is
relying on this, so it's not work the other downsides of a sub-version bump
(i.e. breaking pure-Python modules that use @native). The opposite case of
running an old .mpy on new MicroPython is unchanged, and remains broken in
exactly the same way.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This adds support for the x format code in struct.pack and struct.unpack.
The primary use case for this is ignoring bytes while unpacking. When
interfacing with existing systems, it may often happen that you either have
fields in a struct that aren't properly specified or you simply don't care
about them. Being able to easily skip them is useful.
Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
Eliminate `TypeError` when format string contains no named conversions.
This matches CPython behavior.
Signed-off-by: mcskatkat <mc_skatkat@hotmail.com>
The "true" command by default is unavailable on windows so use
an equivalent which works on both unix and windows.
Signed-off-by: stijn <stijn@ignitron.net>
The only reason that const had to be disabled was to make the test output
match CPython when const was involved. Instead, this commit fixes the test
to handle the lines where const is used.
Also:
- remove the special handling for MICROPY_PERSISTENT_CODE_SAVE in
unix/mpconfigport.h, and make this automatic.
- move the check for MICROPY_PERSISTENT_CODE_SAVE to where it's used (like
we do for other similar checks) and add a comment explaining it.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This test was failing on CPython 3.11 as it now emits `0` as the line
number for the "call" event corresponding to import, where as in 3.6 it had
`1` as the line number.
We maintain the old behavior, but in order to make this test pass on both
CPython versions, the trace handler now converts the `0` to a `1`.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
An SSL stream can only handle CLOSE and POLL ioctls. Other ones do not
make sense, or at least it doesn't make sense to pass the ioctl request
directly down to the underlying stream.
In particular MP_STREAM_GET_FILENO should not be passed to the underlying
stream because the SSL stream is not directly related to a file descriptor,
and the SSL stream must handle the polling itself.
Signed-off-by: Damien George <damien@micropython.org>
Some targets (eg PYBV10) have the socket module but are unable to create
UDP sockets without a registered NIC. So skip UDP tests on these targets.
Signed-off-by: Damien George <damien@micropython.org>
Previously this was explicitly enabled on esp32/stm32/renesas/mimxrt/samd,
but didn't get a default feature level because it wasn't in py/mpconfig.h.
With this commit it's now enabled at the "extra features" level, which adds
rp2, unix-standard, windows, esp8266, webassembly, and some nrf boards.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This provides similar functionality to the former zlib.DecompIO and
especially CPython's gzip.GzipFile for both compression and decompression.
This class can be used directly, and also can be used from Python to
implement (via io.BytesIO) zlib.decompress and zlib.compress, as well as
gzip.GzipFile.
Enable/disable this on all ports/boards that zlib was previously configured
for.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This will be replaced with a new deflate module providing the same
functionality, with an optional frozen Python wrapper providing a
replacement zlib module.
binascii.crc32 is temporarily disabled.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Prior to this fix, async for assumed the iterator expression was a simple
identifier, and used that identifier as a local to store the intermediate
iterator object. This is incorrect behaviour.
This commit fixes the issue by keeping the iterator object on the stack as
an anonymous local variable.
Fixes issue #11511.
Signed-off-by: Damien George <damien@micropython.org>
This commit adds the SSLContext class to the ssl module, and retains the
existing ssl.wrap_socket() function to maintain backwards compatibility.
CPython deprecated the ssl.wrap_socket() function since CPython 3.7 and
instead one should use ssl.SSLContext().wrap_socket(). This commit makes
that possible.
For the axtls implementation:
- ssl.SSLContext is added, although it doesn't hold much state because
axtls requires calling ssl_ctx_new() for each new socket
- ssl.SSLContext.wrap_socket() is added
- ssl.PROTOCOL_TLS_CLIENT and ssl.PROTOCOL_TLS_SERVER are added
For the mbedtls implementation:
- ssl.SSLContext is added, and holds most of the mbedtls state
- ssl.verify_mode is added (getter and setter)
- ssl.SSLContext.wrap_socket() is added
- ssl.PROTOCOL_TLS_CLIENT and ssl.PROTOCOL_TLS_SERVER are added
The signatures match CPython:
- SSLContext(protocol)
- SSLContext.wrap_socket(sock, *, server_side=False,
do_handshake_on_connect=True, server_hostname=None)
The existing ssl.wrap_socket() functions retain their existing signature.
Signed-off-by: Damien George <damien@micropython.org>
Tests framebuf1 and framebuf2 do not take the need for byte-aligned
strides into consideration when calculating buffer lengths.
Accordingly, the buffers allocated are slightly too small. Fixed
buffer length calculations.
Signed-off-by: Duncan Lowther <Duncan.Lowther@glasgow.ac.uk>
Structure descriptor in test extmod/uctypes_array_assign_le
is 6 bytes long, due to member "arr3" having length 4
(2 * UINT16) and offset 2, but only 5 bytes are allocated.
Increased buffer length to 6 bytes.
Signed-off-by: Duncan Lowther <Duncan.Lowther@glasgow.ac.uk>
This allows existing code that does `import uasyncio` or
`import uasyncio as asyncio` to continue working.
It uses the same lazy-loading as asyncio to prevent loading of unused
features.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Instead of having a special set of arguments to test for each math-module
function, just test all functions with all sets of arguments. This gives
improved test cases to prevent regressions.
Signed-off-by: Damien George <damien@micropython.org>
PEP-498 allows for conversion specifiers like !r and !s to convert the
expression declared in braces to be passed through repr() and str()
respectively.
This updates the logic that detects the end of the expression to also stop
when it sees "![rs]" that is either at the end of the f-string or before
the ":" indicating the start of the format specifier. The "![rs]" is now
retained in the format string, whereas previously it stayed on the end
of the expression leading to a syntax error.
Previously: `f"{x!y:z}"` --> `"{:z}".format(x!y)`
Now: `f"{x!y:z}"` --> `"{!y:z}".format(x)`
Note that "!a" is not supported by `str.format` as MicroPython has no
`ascii()`, but now this will raise the correct error.
Updated cpydiff and added tests.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This is a MicroPython-specific module that existed to support the old
version of uasyncio. It's undocumented and not enabled on all ports and
takes up code size unnecessarily.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Make tests run in an isolated environment (i.e. `import io` would
otherwise get the `tests/io` directory).
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Make tests run in an isolated environment (i.e. `import io` would
otherwise get the `tests/io` directory).
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Make tests run in an isolated environment (i.e. `import io` would
otherwise get the `tests/io` directory).
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Prior to this commit, importing a module that exists but has a syntax error
or some other problem that happens at import time would result in a
potentially-incomplete module object getting added to sys.modules.
Subsequent imports would use that object, resulting in confusing error
messages that hide the root cause of the problem.
This commit fixes that issue by removing the failed module from sys.modules
using the new NLR callback mechanism.
Note that it is still important to add the module to sys.modules while the
import is happening so that we can support circular imports just like
CPython does.
Fixes issue #967.
Signed-off-by: David Grayson <davidegrayson@gmail.com>
When foo.bar is imported, bar is added as an attribute to foo. Previously
this happened on every import, but should only happen on first import.
This verifies the behavior for relative imports and overriding.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This verifies the behavior:
- Exact matches of built-ins bypass filesystem.
- u-prefix modules can be overridden from the filesystem.
- Builtin import can be forced using either u-prefix or sys.path=[].
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This demonstrates how to add a sub-package in a user c module, as well
as how to define the necessary qstrs and enable the feature in the build.
This is used by the unix coverage build to test this feature.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This generalises and simplifies the code and follows CPython behaviour.
See similar change for floats in a07fc5b640.
Signed-off-by: Damien George <damien@micropython.org>
This is possible now that MP_UNARY_OP_INT_MAYBE exists.
As a consequence mp_obj_get_int now also supports user types, which was
previously possible with MP_UNARY_OP_INT but no tests existed for it.
Signed-off-by: Damien George <damien@micropython.org>
To be consistent with MP_UNARY_OP_INT_FLOAT and MP_UNARY_OP_INT_COMPLEX,
and allow int() to first check if a type supports __int__ before trying
other things (as per CPython).
Signed-off-by: Damien George <damien@micropython.org>
MicroPython does not support these special methods, and they may get in the
way of other tests (eg indexing with __int__).
Signed-off-by: Damien George <damien@micropython.org>
The code that handles inplace-operator to normal-binary-operator fallback
is moved in this commit from py/objtype.c to py/runtime.c, making it apply
to all types, not just user classes.
Signed-off-by: Damien George <damien@micropython.org>
So that user types can implement reverse operators and have them work with
str on the left-hand-side, eg `"a" + UserType()`.
Signed-off-by: Damien George <damien@micropython.org>
This adds a unary_op implementation for the dict_view type that makes
the implementation of `hash()` for these types compatible with CPython.
Signed-off-by: David Lechner <david@pybricks.com>
As per https://bugs.python.org/issue408326, the slice object should not be
hashable. Since MicroPython has an implicit fallback when the unary_op
slot is empty, we need to fill this slot.
Signed-off-by: David Lechner <david@pybricks.com>
Previously when using --via-mpy, the file was compiled to tests/<tmp>.mpy
and then run using `micropython -m <tmp>` in the current cwd
(usually tests/). This meant that an import in the test would be resolved
relative to tests/.
This is different to regular (non-via-mpy) tests, where we run (for
example) `micropython basics/test.py` which means that an import would be
resolved relative to basics/.
Now --via-mpy matches the .py behavior. This is important because:
a) It makes it so import tests do the right thing.
b) There are directory names in tests/ that match built-in module names.
Furthermore, it always ensures the cwd (for both micropython and cpython)
is the test directory (e.g. basics/) rather than being left unset. This
also makes it clearer inside the test that e.g. file access is relative to
the Python file.
Updated tests with file paths to match.
This work was funded through GitHub Sponsors.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
When a tuple is the condition of an if statement, it's only possible to
optimise that tuple away when it is a constant tuple (ie all its elements
are constants), because if it's not constant then the elements must be
evaluated in case they have side effects (even though the resulting tuple
will always be "true").
The code before this change handled the empty tuple OK (because it doesn't
need to be evaluated), but it discarded non-empty tuples without evaluating
them, which is incorrect behaviour (as show by the updated test).
This optimisation is anyway rarely applied because it's not common Python
coding practice to write things like `if (): ...` and `if (1, 2): ...`, so
removing this optimisation completely won't affect much code, if any.
Furthermore, when MICROPY_COMP_CONST_TUPLE is enabled, constant tuples are
already optimised by the parser, so expression with constant tuples like
`if (): ...` and `if (1, 2): ...` will continue to be optimised properly
(and so when this option is enabled the code that's deleted in this commit
is actually unreachable when the if condition is a constant tuple).
Signed-off-by: Damien George <damien@micropython.org>