diff --git a/python/builtins.py b/python/builtins.py index d27de739..84ab2a23 100644 --- a/python/builtins.py +++ b/python/builtins.py @@ -204,8 +204,24 @@ def help(obj): def complex(real, imag=0): import cmath - return cmath.complex(real, imag) + return cmath.complex(real, imag) # type: ignore +def dir(obj) -> list[str]: + tp_module = type(__import__('math')) + if isinstance(obj, tp_module): + return [k for k, _ in obj.__dict__.items()] + names = set() + if not isinstance(obj, type): + obj_d = obj.__dict__ + if obj_d is not None: + names.update([k for k, _ in obj_d.items()]) + cls = type(obj) + else: + cls = obj + while cls is not None: + names.update([k for k, _ in cls.__dict__.items()]) + cls = cls.__base__ + return sorted(list(names)) class set: def __init__(self, iterable=None): diff --git a/src/common/_generated.c b/src/common/_generated.c index 03215b69..b5177fbc 100644 --- a/src/common/_generated.c +++ b/src/common/_generated.c @@ -2,7 +2,7 @@ #include "pocketpy/common/_generated.h" #include const char kPythonLibs_bisect[] = "\"\"\"Bisection algorithms.\"\"\"\n\ndef insort_right(a, x, lo=0, hi=None):\n \"\"\"Insert item x in list a, and keep it sorted assuming a is sorted.\n\n If x is already in a, insert it to the right of the rightmost x.\n\n Optional args lo (default 0) and hi (default len(a)) bound the\n slice of a to be searched.\n \"\"\"\n\n lo = bisect_right(a, x, lo, hi)\n a.insert(lo, x)\n\ndef bisect_right(a, x, lo=0, hi=None):\n \"\"\"Return the index where to insert item x in list a, assuming a is sorted.\n\n The return value i is such that all e in a[:i] have e <= x, and all e in\n a[i:] have e > x. So if x already appears in the list, a.insert(x) will\n insert just after the rightmost x already there.\n\n Optional args lo (default 0) and hi (default len(a)) bound the\n slice of a to be searched.\n \"\"\"\n\n if lo < 0:\n raise ValueError('lo must be non-negative')\n if hi is None:\n hi = len(a)\n while lo < hi:\n mid = (lo+hi)//2\n if x < a[mid]: hi = mid\n else: lo = mid+1\n return lo\n\ndef insort_left(a, x, lo=0, hi=None):\n \"\"\"Insert item x in list a, and keep it sorted assuming a is sorted.\n\n If x is already in a, insert it to the left of the leftmost x.\n\n Optional args lo (default 0) and hi (default len(a)) bound the\n slice of a to be searched.\n \"\"\"\n\n lo = bisect_left(a, x, lo, hi)\n a.insert(lo, x)\n\n\ndef bisect_left(a, x, lo=0, hi=None):\n \"\"\"Return the index where to insert item x in list a, assuming a is sorted.\n\n The return value i is such that all e in a[:i] have e < x, and all e in\n a[i:] have e >= x. So if x already appears in the list, a.insert(x) will\n insert just before the leftmost x already there.\n\n Optional args lo (default 0) and hi (default len(a)) bound the\n slice of a to be searched.\n \"\"\"\n\n if lo < 0:\n raise ValueError('lo must be non-negative')\n if hi is None:\n hi = len(a)\n while lo < hi:\n mid = (lo+hi)//2\n if a[mid] < x: lo = mid+1\n else: hi = mid\n return lo\n\n# Create aliases\nbisect = bisect_right\ninsort = insort_right\n"; -const char kPythonLibs_builtins[] = "def all(iterable):\n for i in iterable:\n if not i:\n return False\n return True\n\ndef any(iterable):\n for i in iterable:\n if i:\n return True\n return False\n\ndef enumerate(iterable, start=0):\n n = start\n for elem in iterable:\n yield n, elem\n n += 1\n\ndef __minmax_reduce(op, args):\n if len(args) == 2: # min(1, 2)\n return args[0] if op(args[0], args[1]) else args[1]\n if len(args) == 0: # min()\n raise TypeError('expected 1 arguments, got 0')\n if len(args) == 1: # min([1, 2, 3, 4]) -> min(1, 2, 3, 4)\n args = args[0]\n args = iter(args)\n try:\n res = next(args)\n except StopIteration:\n raise ValueError('args is an empty sequence')\n while True:\n try:\n i = next(args)\n except StopIteration:\n break\n if op(i, res):\n res = i\n return res\n\ndef min(*args, key=None):\n key = key or (lambda x: x)\n return __minmax_reduce(lambda x,y: key(x)key(y), args)\n\ndef sum(iterable):\n res = 0\n for i in iterable:\n res += i\n return res\n\ndef map(f, iterable):\n for i in iterable:\n yield f(i)\n\ndef filter(f, iterable):\n for i in iterable:\n if f(i):\n yield i\n\ndef zip(a, b):\n a = iter(a)\n b = iter(b)\n while True:\n try:\n ai = next(a)\n bi = next(b)\n except StopIteration:\n break\n yield ai, bi\n\ndef reversed(iterable):\n a = list(iterable)\n a.reverse()\n return a\n\ndef sorted(iterable, key=None, reverse=False):\n a = list(iterable)\n a.sort(key=key, reverse=reverse)\n return a\n\n##### str #####\ndef __format_string(self: str, *args, **kwargs) -> str:\n def tokenizeString(s: str):\n tokens = []\n L, R = 0,0\n \n mode = None\n curArg = 0\n # lookingForKword = False\n \n while(R list[str]:\n tp_module = type(__import__('math'))\n if isinstance(obj, tp_module):\n return [k for k, _ in obj.__dict__.items()]\n names = set()\n if not isinstance(obj, type):\n obj_d = obj.__dict__\n if obj_d is not None:\n names.update([k for k, _ in obj_d.items()])\n cls = type(obj)\n else:\n cls = obj\n while cls is not None:\n names.update([k for k, _ in cls.__dict__.items()])\n cls = cls.__base__\n return sorted(list(names))\n\nclass set:\n def __init__(self, iterable=None):\n iterable = iterable or []\n self._a = {}\n self.update(iterable)\n\n def add(self, elem):\n self._a[elem] = None\n \n def discard(self, elem):\n self._a.pop(elem, None)\n\n def remove(self, elem):\n del self._a[elem]\n \n def clear(self):\n self._a.clear()\n\n def update(self, other):\n for elem in other:\n self.add(elem)\n\n def __len__(self):\n return len(self._a)\n \n def copy(self):\n return set(self._a.keys())\n \n def __and__(self, other):\n return {elem for elem in self if elem in other}\n\n def __sub__(self, other):\n return {elem for elem in self if elem not in other}\n \n def __or__(self, other):\n ret = self.copy()\n ret.update(other)\n return ret\n\n def __xor__(self, other): \n _0 = self - other\n _1 = other - self\n return _0 | _1\n\n def union(self, other):\n return self | other\n\n def intersection(self, other):\n return self & other\n\n def difference(self, other):\n return self - other\n\n def symmetric_difference(self, other): \n return self ^ other\n \n def __eq__(self, other):\n if not isinstance(other, set):\n return NotImplemented\n return len(self ^ other) == 0\n \n def __ne__(self, other):\n if not isinstance(other, set):\n return NotImplemented\n return len(self ^ other) != 0\n\n def isdisjoint(self, other):\n return len(self & other) == 0\n \n def issubset(self, other):\n return len(self - other) == 0\n \n def issuperset(self, other):\n return len(other - self) == 0\n\n def __contains__(self, elem):\n return elem in self._a\n \n def __repr__(self):\n if len(self) == 0:\n return 'set()'\n return '{'+ ', '.join([repr(i) for i in self._a.keys()]) + '}'\n \n def __iter__(self):\n return iter(self._a.keys())"; const char kPythonLibs_cmath[] = "import math\n\nclass complex:\n def __init__(self, real, imag=0):\n self._real = float(real)\n self._imag = float(imag)\n\n @property\n def real(self):\n return self._real\n \n @property\n def imag(self):\n return self._imag\n\n def conjugate(self):\n return complex(self.real, -self.imag)\n \n def __repr__(self):\n s = ['(', str(self.real)]\n s.append('-' if self.imag < 0 else '+')\n s.append(str(abs(self.imag)))\n s.append('j)')\n return ''.join(s)\n \n def __eq__(self, other):\n if type(other) is complex:\n return self.real == other.real and self.imag == other.imag\n if type(other) in (int, float):\n return self.real == other and self.imag == 0\n return NotImplemented\n \n def __ne__(self, other):\n res = self == other\n if res is NotImplemented:\n return res\n return not res\n \n def __add__(self, other):\n if type(other) is complex:\n return complex(self.real + other.real, self.imag + other.imag)\n if type(other) in (int, float):\n return complex(self.real + other, self.imag)\n return NotImplemented\n \n def __radd__(self, other):\n return self.__add__(other)\n \n def __sub__(self, other):\n if type(other) is complex:\n return complex(self.real - other.real, self.imag - other.imag)\n if type(other) in (int, float):\n return complex(self.real - other, self.imag)\n return NotImplemented\n \n def __rsub__(self, other):\n if type(other) is complex:\n return complex(other.real - self.real, other.imag - self.imag)\n if type(other) in (int, float):\n return complex(other - self.real, -self.imag)\n return NotImplemented\n \n def __mul__(self, other):\n if type(other) is complex:\n return complex(self.real * other.real - self.imag * other.imag,\n self.real * other.imag + self.imag * other.real)\n if type(other) in (int, float):\n return complex(self.real * other, self.imag * other)\n return NotImplemented\n \n def __rmul__(self, other):\n return self.__mul__(other)\n \n def __truediv__(self, other):\n if type(other) is complex:\n denominator = other.real ** 2 + other.imag ** 2\n real_part = (self.real * other.real + self.imag * other.imag) / denominator\n imag_part = (self.imag * other.real - self.real * other.imag) / denominator\n return complex(real_part, imag_part)\n if type(other) in (int, float):\n return complex(self.real / other, self.imag / other)\n return NotImplemented\n \n def __pow__(self, other: int | float):\n if type(other) in (int, float):\n return complex(self.__abs__() ** other * math.cos(other * phase(self)),\n self.__abs__() ** other * math.sin(other * phase(self)))\n return NotImplemented\n \n def __abs__(self) -> float:\n return math.sqrt(self.real ** 2 + self.imag ** 2)\n\n def __neg__(self):\n return complex(-self.real, -self.imag)\n \n def __hash__(self):\n return hash((self.real, self.imag))\n\n\n# Conversions to and from polar coordinates\n\ndef phase(z: complex):\n return math.atan2(z.imag, z.real)\n\ndef polar(z: complex):\n return z.__abs__(), phase(z)\n\ndef rect(r: float, phi: float):\n return r * math.cos(phi) + r * math.sin(phi) * 1j\n\n# Power and logarithmic functions\n\ndef exp(z: complex):\n return math.exp(z.real) * rect(1, z.imag)\n\ndef log(z: complex, base=2.718281828459045):\n return math.log(z.__abs__(), base) + phase(z) * 1j\n\ndef log10(z: complex):\n return log(z, 10)\n\ndef sqrt(z: complex):\n return z ** 0.5\n\n# Trigonometric functions\n\ndef acos(z: complex):\n return -1j * log(z + sqrt(z * z - 1))\n\ndef asin(z: complex):\n return -1j * log(1j * z + sqrt(1 - z * z))\n\ndef atan(z: complex):\n return 1j / 2 * log((1 - 1j * z) / (1 + 1j * z))\n\ndef cos(z: complex):\n return (exp(z) + exp(-z)) / 2\n\ndef sin(z: complex):\n return (exp(z) - exp(-z)) / (2 * 1j)\n\ndef tan(z: complex):\n return sin(z) / cos(z)\n\n# Hyperbolic functions\n\ndef acosh(z: complex):\n return log(z + sqrt(z * z - 1))\n\ndef asinh(z: complex):\n return log(z + sqrt(z * z + 1))\n\ndef atanh(z: complex):\n return 1 / 2 * log((1 + z) / (1 - z))\n\ndef cosh(z: complex):\n return (exp(z) + exp(-z)) / 2\n\ndef sinh(z: complex):\n return (exp(z) - exp(-z)) / 2\n\ndef tanh(z: complex):\n return sinh(z) / cosh(z)\n\n# Classification functions\n\ndef isfinite(z: complex):\n return math.isfinite(z.real) and math.isfinite(z.imag)\n\ndef isinf(z: complex):\n return math.isinf(z.real) or math.isinf(z.imag)\n\ndef isnan(z: complex):\n return math.isnan(z.real) or math.isnan(z.imag)\n\ndef isclose(a: complex, b: complex):\n return math.isclose(a.real, b.real) and math.isclose(a.imag, b.imag)\n\n# Constants\n\npi = math.pi\ne = math.e\ntau = 2 * pi\ninf = math.inf\ninfj = complex(0, inf)\nnan = math.nan\nnanj = complex(0, nan)\n"; const char kPythonLibs_collections[] = "from typing import TypeVar, Iterable\n\ndef Counter[T](iterable: Iterable[T]):\n a: dict[T, int] = {}\n for x in iterable:\n if x in a:\n a[x] += 1\n else:\n a[x] = 1\n return a\n\n\nclass defaultdict(dict):\n def __init__(self, default_factory, *args):\n super().__init__(*args)\n self.default_factory = default_factory\n\n def __missing__(self, key):\n self[key] = self.default_factory()\n return self[key]\n\n def __repr__(self) -> str:\n return f\"defaultdict({self.default_factory}, {super().__repr__()})\"\n\n def copy(self):\n return defaultdict(self.default_factory, self)\n\n\nclass deque[T]:\n _data: list[T]\n _head: int\n _tail: int\n _capacity: int\n\n def __init__(self, iterable: Iterable[T] = None):\n self._data = [None] * 8 # type: ignore\n self._head = 0\n self._tail = 0\n self._capacity = len(self._data)\n\n if iterable is not None:\n self.extend(iterable)\n\n def __resize_2x(self):\n backup = list(self)\n self._capacity *= 2\n self._head = 0\n self._tail = len(backup)\n self._data.clear()\n self._data.extend(backup)\n self._data.extend([None] * (self._capacity - len(backup)))\n\n def append(self, x: T):\n self._data[self._tail] = x\n self._tail = (self._tail + 1) % self._capacity\n if (self._tail + 1) % self._capacity == self._head:\n self.__resize_2x()\n\n def appendleft(self, x: T):\n self._head = (self._head - 1) % self._capacity\n self._data[self._head] = x\n if (self._tail + 1) % self._capacity == self._head:\n self.__resize_2x()\n\n def copy(self):\n return deque(self)\n \n def count(self, x: T) -> int:\n n = 0\n for item in self:\n if item == x:\n n += 1\n return n\n \n def extend(self, iterable: Iterable[T]):\n for x in iterable:\n self.append(x)\n\n def extendleft(self, iterable: Iterable[T]):\n for x in iterable:\n self.appendleft(x)\n \n def pop(self) -> T:\n if self._head == self._tail:\n raise IndexError(\"pop from an empty deque\")\n self._tail = (self._tail - 1) % self._capacity\n return self._data[self._tail]\n \n def popleft(self) -> T:\n if self._head == self._tail:\n raise IndexError(\"pop from an empty deque\")\n x = self._data[self._head]\n self._head = (self._head + 1) % self._capacity\n return x\n \n def clear(self):\n i = self._head\n while i != self._tail:\n self._data[i] = None # type: ignore\n i = (i + 1) % self._capacity\n self._head = 0\n self._tail = 0\n\n def rotate(self, n: int = 1):\n if len(self) == 0:\n return\n if n > 0:\n n = n % len(self)\n for _ in range(n):\n self.appendleft(self.pop())\n elif n < 0:\n n = -n % len(self)\n for _ in range(n):\n self.append(self.popleft())\n\n def __len__(self) -> int:\n return (self._tail - self._head) % self._capacity\n\n def __contains__(self, x: object) -> bool:\n for item in self:\n if item == x:\n return True\n return False\n \n def __iter__(self):\n i = self._head\n while i != self._tail:\n yield self._data[i]\n i = (i + 1) % self._capacity\n\n def __eq__(self, other: object) -> bool:\n if not isinstance(other, deque):\n return NotImplemented\n if len(self) != len(other):\n return False\n for x, y in zip(self, other):\n if x != y:\n return False\n return True\n \n def __ne__(self, other: object) -> bool:\n if not isinstance(other, deque):\n return NotImplemented\n return not self == other\n \n def __repr__(self) -> str:\n return f\"deque({list(self)!r})\"\n\n"; const char kPythonLibs_dataclasses[] = "def _get_annotations(cls: type):\n inherits = []\n while cls is not object:\n inherits.append(cls)\n cls = cls.__base__\n inherits.reverse()\n res = {}\n for cls in inherits:\n res.update(cls.__annotations__)\n return res.keys()\n\ndef _wrapped__init__(self, *args, **kwargs):\n cls = type(self)\n cls_d = cls.__dict__\n fields = _get_annotations(cls)\n i = 0 # index into args\n for field in fields:\n if field in kwargs:\n setattr(self, field, kwargs.pop(field))\n else:\n if i < len(args):\n setattr(self, field, args[i])\n i += 1\n elif field in cls_d: # has default value\n setattr(self, field, cls_d[field])\n else:\n raise TypeError(f\"{cls.__name__} missing required argument {field!r}\")\n if len(args) > i:\n raise TypeError(f\"{cls.__name__} takes {len(fields)} positional arguments but {len(args)} were given\")\n if len(kwargs) > 0:\n raise TypeError(f\"{cls.__name__} got an unexpected keyword argument {next(iter(kwargs))!r}\")\n\ndef _wrapped__repr__(self):\n fields = _get_annotations(type(self))\n obj_d = self.__dict__\n args: list = [f\"{field}={obj_d[field]!r}\" for field in fields]\n return f\"{type(self).__name__}({', '.join(args)})\"\n\ndef _wrapped__eq__(self, other):\n if type(self) is not type(other):\n return False\n fields = _get_annotations(type(self))\n for field in fields:\n if getattr(self, field) != getattr(other, field):\n return False\n return True\n\ndef _wrapped__ne__(self, other):\n return not self.__eq__(other)\n\ndef dataclass(cls: type):\n assert type(cls) is type\n cls_d = cls.__dict__\n if '__init__' not in cls_d:\n cls.__init__ = _wrapped__init__\n if '__repr__' not in cls_d:\n cls.__repr__ = _wrapped__repr__\n if '__eq__' not in cls_d:\n cls.__eq__ = _wrapped__eq__\n if '__ne__' not in cls_d:\n cls.__ne__ = _wrapped__ne__\n fields = _get_annotations(cls)\n has_default = False\n for field in fields:\n if field in cls_d:\n has_default = True\n else:\n if has_default:\n raise TypeError(f\"non-default argument {field!r} follows default argument\")\n return cls\n\ndef asdict(obj) -> dict:\n fields = _get_annotations(type(obj))\n obj_d = obj.__dict__\n return {field: obj_d[field] for field in fields}"; diff --git a/src/public/py_dict.c b/src/public/py_dict.c index c1720dff..9a5361a1 100644 --- a/src/public/py_dict.c +++ b/src/public/py_dict.c @@ -291,11 +291,14 @@ static bool dict__init__(int argc, py_Ref argv) { if(argc > 2) return TypeError("dict.__init__() takes at most 2 arguments (%d given)", argc); if(argc == 1) return true; assert(argc == 2); - PY_CHECK_ARG_TYPE(1, tp_list); + + py_TValue* p; + int length = pk_arrayview(py_arg(1), &p); + if(length == -1) { return TypeError("dict.__init__() expects a list or tuple"); } + Dict* self = py_touserdata(argv); - py_Ref list = py_arg(1); - for(int i = 0; i < py_list_len(list); i++) { - py_Ref tuple = py_list_getitem(list, i); + for(int i = 0; i < length; i++) { + py_Ref tuple = &p[i]; if(!py_istuple(tuple) || py_tuple_len(tuple) != 2) { return TypeError("dict.__init__() argument must be a list of tuple-2"); } diff --git a/src/public/py_mappingproxy.c b/src/public/py_mappingproxy.c index 85d9f2c1..b8da64e0 100644 --- a/src/public/py_mappingproxy.c +++ b/src/public/py_mappingproxy.c @@ -52,9 +52,28 @@ static bool namedict_items(int argc, py_Ref argv) { PY_CHECK_ARGC(1); py_Ref object = py_getslot(argv, 0); NameDict* dict = PyObject__dict(object->_obj); - py_newtuple(py_retval(), dict->length); + py_newlist(py_retval()); + if(object->type == tp_type) { + py_TypeInfo* ti = pk__type_info(py_totype(object)); + for(int j = 0; j < PK_MAGIC_SLOTS_COMMON_LENGTH; j++) { + if(py_isnil(ti->magic_0 + j)) continue; + py_Ref slot = py_list_emplace(py_retval()); + py_newtuple(slot, 2); + py_newstr(py_tuple_getitem(slot, 0), py_name2str(j + PK_MAGIC_SLOTS_UNCOMMON_LENGTH)); + py_assign(py_tuple_getitem(slot, 1), ti->magic_0 + j); + } + if(ti->magic_1) { + for(int j = 0; j < PK_MAGIC_SLOTS_UNCOMMON_LENGTH; j++) { + if(py_isnil(ti->magic_1 + j)) continue; + py_Ref slot = py_list_emplace(py_retval()); + py_newtuple(slot, 2); + py_newstr(py_tuple_getitem(slot, 0), py_name2str(j)); + py_assign(py_tuple_getitem(slot, 1), ti->magic_1 + j); + } + } + } for(int i = 0; i < dict->length; i++) { - py_Ref slot = py_tuple_getitem(py_retval(), i); + py_Ref slot = py_list_emplace(py_retval()); py_newtuple(slot, 2); NameDict_KV* kv = c11__at(NameDict_KV, dict, i); py_newstr(py_tuple_getitem(slot, 0), py_name2str(kv->key));