diff --git a/README.md b/README.md index 73b786b..4b280a9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Mypy Extensions =============== -The "mypy_extensions" module defines experimental extensions to the -standard "typing" module that are supported by the mypy typechecker. - +The `mypy_extensions` module defines extensions to the Python standard +library `typing` module that are supported by the mypy type checker and +the mypyc compiler. diff --git a/mypy_extensions.py b/mypy_extensions.py index cf90b66..6600b21 100644 --- a/mypy_extensions.py +++ b/mypy_extensions.py @@ -162,3 +162,52 @@ def __getitem__(self, args): FlexibleAlias = _FlexibleAliasCls() + + +class _NativeIntMeta(type): + def __instancecheck__(cls, inst): + return isinstance(inst, int) + + +_sentinel = object() + + +class i64(metaclass=_NativeIntMeta): + def __new__(cls, x=0, base=_sentinel): + if base is not _sentinel: + return int(x, base) + return int(x) + + +class i32(metaclass=_NativeIntMeta): + def __new__(cls, x=0, base=_sentinel): + if base is not _sentinel: + return int(x, base) + return int(x) + + +class i16(metaclass=_NativeIntMeta): + def __new__(cls, x=0, base=_sentinel): + if base is not _sentinel: + return int(x, base) + return int(x) + + +class u8(metaclass=_NativeIntMeta): + def __new__(cls, x=0, base=_sentinel): + if base is not _sentinel: + return int(x, base) + return int(x) + + +for _int_type in i64, i32, i16, u8: + _int_type.__doc__ = \ + """A native fixed-width integer type when used with mypyc. + + In code not compiled with mypyc, behaves like the 'int' type in these + runtime contexts: + + * {name}(x[, base=n]) converts a number or string to 'int' + * isinstance(x, {name}) is the same as isinstance(x, int) + """.format(name=_int_type.__name__) +del _int_type diff --git a/tests/testextensions.py b/tests/testextensions.py index 861962d..991c4e5 100644 --- a/tests/testextensions.py +++ b/tests/testextensions.py @@ -2,7 +2,7 @@ import pickle import typing from unittest import TestCase, main, skipUnless -from mypy_extensions import TypedDict +from mypy_extensions import TypedDict, i64, i32, i16, u8 class BaseTestCase(TestCase): @@ -140,5 +140,46 @@ def test_total(self): self.assertEqual(Options.__total__, False) # noqa +native_int_types = [i64, i32, i16, u8] + + +class MypycNativeIntTests(TestCase): + def test_construction(self): + for native_int in native_int_types: + self.assert_same(native_int(), 0) + + self.assert_same(native_int(0), 0) + self.assert_same(native_int(1), 1) + self.assert_same(native_int(-3), -3) + self.assert_same(native_int(2**64), 2**64) + self.assert_same(native_int(-2**64), -2**64) + + self.assert_same(native_int(1.234), 1) + self.assert_same(native_int(2.634), 2) + self.assert_same(native_int(-1.234), -1) + self.assert_same(native_int(-2.634), -2) + + self.assert_same(native_int("0"), 0) + self.assert_same(native_int("123"), 123) + self.assert_same(native_int("abc", 16), 2748) + self.assert_same(native_int("-101", base=2), -5) + + def test_isinstance(self): + for native_int in native_int_types: + assert isinstance(0, native_int) + assert isinstance(1234, native_int) + assert isinstance(True, native_int) + assert not isinstance(1.0, native_int) + + def test_docstring(self): + for native_int in native_int_types: + # Just check that a docstring exists + assert native_int.__doc__ + + def assert_same(self, x, y): + assert type(x) is type(y) + assert x == y + + if __name__ == '__main__': main()