Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions micropython/bundles/bundle-typing/manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# type: ignore[all]

metadata(
version="1.26.1",
description="Limited runtime typing support for MicroPython.",
)

options.defaults(opt_level=3, extensions=False)

# Primary typing related modules
require("__future__", opt_level=options.opt_level)
require("typing", opt_level=options.opt_level)

# # Optional typing modules
if options.extensions:
require("collections", opt_level=options.opt_level)
require("collections-abc", opt_level=options.opt_level)
require("abc", opt_level=options.opt_level)
6 changes: 4 additions & 2 deletions python-stdlib/__future__/manifest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
metadata(version="0.1.0")
metadata(version="0.1.1")

module("__future__.py")
options.defaults(opt_level=3)

module("__future__.py", opt=options.opt_level)
12 changes: 10 additions & 2 deletions python-stdlib/abc/abc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# type: ignore
class ABC:
pass


def abstractmethod(f):
return f
def abstractmethod(arg):
return arg

try:
# add functionality if typing module is available
from typing import __getattr__ as __getattr__

except: # naked except saves 4 bytes
pass
6 changes: 4 additions & 2 deletions python-stdlib/abc/manifest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
metadata(version="0.1.0")
metadata(version="0.2.0")

module("abc.py")
options.defaults(opt_level=3)

module("abc.py", opt=options.opt_level)
7 changes: 7 additions & 0 deletions python-stdlib/collections-abc/collections/abc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# collections.abc
# minimal support for runtime typing
# type: ignore
try:
from typing import __Ignore as ABC, __getattr__ as __getattr__
except:
pass
5 changes: 5 additions & 0 deletions python-stdlib/collections-abc/manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
metadata(version="1.26.1")

# require("collections")
require("typing")
package("collections")
8 changes: 8 additions & 0 deletions python-stdlib/collections/collections/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
from .defaultdict import defaultdict
except ImportError:
pass
# optional collections.abc typing dummy module
try:
# cannot use relative import here
import collections.abc as abc
import sys
sys.modules['collections.abc'] = abc
except ImportError:
pass


class MutableMapping:
Expand Down
8 changes: 8 additions & 0 deletions python-stdlib/typing/manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# type: ignore

metadata(version="1.26.1", description="Typing module for MicroPython.")

# default to opt_level 3 for minimal firmware size
options.defaults(opt_level=3)

module("typing.py", opt=options.opt_level)
111 changes: 111 additions & 0 deletions python-stdlib/typing/typing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""
This module provides runtime support for type hints.
based on :
- https://github.com/micropython/micropython-lib/pull/584
- https://github.com/Josverl/micropython-stubs/tree/main/mip
- https://github.com/Josverl/rt_typing

"""

# -------------------------------------
# code reduction by Ignoring type hints
# -------------------------------------
class __Ignore:
"""A class to ignore type hints in code."""

def __call__(*keys, **values):
# May need some guardrails here
pass

def __getitem__(self, key):
# May need some guardrails here
return __ignore

def __getattr__(self, key):
return self.__dict__.get(key, __ignore)


__ignore = __Ignore()
# -----------------
# typing essentials
# -----------------
TYPE_CHECKING = False
def reveal_type(value):
return value

override = final = reveal_type # saves bytes, and is semantically similar

# def overload(arg): # ( 27 bytes)
# # ignore functions signatures with @overload decorator
# return None
overload = __ignore # saves bytes, and is semantically similar

def NewType(_, value): # (21 bytes)
# https://docs.python.org/3/library/typing.html#newtype
# MicroPython: just use the original type.
return value

def TypeVar(key, *types, bound = None, covariant=False, contravariant=False, infer_variance=False):
return key
# ---------------
# useful methods
# ---------------
# is semantically similar
TypeVarTuple = final

# https://docs.python.org/3/library/typing.html#typing.cast
# def cast(type, arg): # ( 23 bytes)
# return arg
cast = NewType # saves bytes, and is semantically similar

# https://docs.python.org/3/library/typing.html#typing.no_type_check
# def no_type_check(arg): # ( 26 bytes)
# # decorator to disable type checking on a function or method
# return arg
no_type_check = final # saves bytes, and is semantically similar

# -----------------
# less used methods
# -----------------

# def reveal_type(x): # ( 38 bytes)
# # # https://docs.python.org/3/library/typing.html#typing.reveal_type
# return x
# or for smaller size:
reveal_type = final # saves bytes, and is semantically similar

# The get_origin behaviour is # already implemented by the __getattr__
# method of __Ignore, which passess the current test suite.

# def get_origin(type): # ( 23 bytes)
# # https://docs.python.org/3/library/typing.html#typing.get_origin
# # Return None for all unsupported objects.
# return None


def get_args(_): # ( 22 bytes)
# https://docs.python.org/3/library/typing.html#typing.get_args
# Python 3.8+ only
return ()

# https://typing.python.org/en/latest/spec/typeddict.html
# make TypedDict dict-like at runtime.
TypedDict = dict

class IO:
pass
class TextIO:
pass
class BinaryIO:
pass

AnyStr=str

# ref: https://github.com/micropython/micropython-lib/pull/584#issuecomment-2317690854
def __getattr__(key):
return __ignore

# snarky way to alias typing_extensions to typing ( saving 59 bytes)
import sys
sys.modules["typing_extensions"] = sys.modules["typing"]
del sys
10 changes: 10 additions & 0 deletions python-stdlib/typing_extensions/manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
metadata(version="1.26.1")


# default to opt_level 3 for minimal firmware size
options.defaults(opt_level=3)

module("typing_extensions.py", opt=options.opt_level)

require("typing")
package("typing_extensions")
8 changes: 8 additions & 0 deletions python-stdlib/typing_extensions/typing_extensions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# typing_extensions.py
# type: ignore
import typing

# snarky way to alias typing_extensions to typing as import * wont work
import sys
sys.modules["typing_extensions"] = sys.modules["typing"]
del sys
67 changes: 58 additions & 9 deletions python-stdlib/unittest/unittest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,14 +228,21 @@ def skipUnless(cond, msg):
return skip(msg)


class _ExpectedFailure(Exception):
pass


class _UnexpectedSuccess(Exception):
pass


def expectedFailure(test):
def test_exp_fail(*args, **kwargs):
try:
test(*args, **kwargs)
except:
pass
else:
assert False, "unexpected success"
except Exception:
raise _ExpectedFailure
raise _UnexpectedSuccess

return test_exp_fail

Expand Down Expand Up @@ -270,12 +277,29 @@ def run(self, suite: TestSuite):
res.printErrors()
print("----------------------------------------------------------------------")
print("Ran %d tests\n" % res.testsRun)
if res.failuresNum > 0 or res.errorsNum > 0:
print("FAILED (failures=%d, errors=%d)" % (res.failuresNum, res.errorsNum))
extras = []
if res.skippedNum > 0:
extras.append("skipped=%d" % res.skippedNum)
if res.expectedFailuresNum > 0:
extras.append("expected failures=%d" % res.expectedFailuresNum)
if res.unexpectedSuccessesNum > 0:
extras.append("unexpected successes=%d" % res.unexpectedSuccessesNum)
if (res.failuresNum + res.errorsNum + res.unexpectedSuccessesNum) > 0:
parts = [
"failures=%d" % res.failuresNum,
"errors=%d" % res.errorsNum,
]
if res.unexpectedSuccessesNum > 0:
parts.append("unexpected successes=%d" % res.unexpectedSuccessesNum)
if res.expectedFailuresNum > 0:
parts.append("expected failures=%d" % res.expectedFailuresNum)
if res.skippedNum > 0:
parts.append("skipped=%d" % res.skippedNum)
print("FAILED (%s)" % ", ".join(parts))
else:
msg = "OK"
if res.skippedNum > 0:
msg += " (skipped=%d)" % res.skippedNum
if extras:
msg += " (%s)" % ", ".join(extras)
print(msg)

return res
Expand All @@ -289,14 +313,22 @@ def __init__(self):
self.errorsNum = 0
self.failuresNum = 0
self.skippedNum = 0
self.expectedFailuresNum = 0
self.unexpectedSuccessesNum = 0
self.testsRun = 0
self.errors = []
self.failures = []
self.skipped = []
self.expectedFailures = []
self.unexpectedSuccesses = []
self._newFailures = 0

def wasSuccessful(self):
return self.errorsNum == 0 and self.failuresNum == 0
return (
self.errorsNum == 0
and self.failuresNum == 0
and self.unexpectedSuccessesNum == 0
)

def printErrors(self):
if self.errors or self.failures:
Expand Down Expand Up @@ -325,10 +357,14 @@ def __add__(self, other):
self.errorsNum += other.errorsNum
self.failuresNum += other.failuresNum
self.skippedNum += other.skippedNum
self.expectedFailuresNum += other.expectedFailuresNum
self.unexpectedSuccessesNum += other.unexpectedSuccessesNum
self.testsRun += other.testsRun
self.errors.extend(other.errors)
self.failures.extend(other.failures)
self.skipped.extend(other.skipped)
self.expectedFailures.extend(other.expectedFailures)
self.unexpectedSuccesses.extend(other.unexpectedSuccesses)
return self


Expand All @@ -353,6 +389,19 @@ def _handle_test_exception(
test_result.skipped.append((current_test, reason))
print(" skipped:", reason)
return
elif isinstance(exc, _ExpectedFailure):
test_result.expectedFailuresNum += 1
test_result.expectedFailures.append((current_test, ""))
if verbose:
print(" expected failure")
return
elif isinstance(exc, _UnexpectedSuccess):
test_result.unexpectedSuccessesNum += 1
test_result.unexpectedSuccesses.append((current_test, ""))
if verbose:
print(" unexpected success")
test_result._newFailures += 1
return
elif isinstance(exc, AssertionError):
test_result.failuresNum += 1
test_result.failures.append((current_test, ex_str))
Expand Down
Loading