diff --git a/stdlib/internal/python.codon b/stdlib/internal/python.codon index 72e3c4a8..c3496532 100644 --- a/stdlib/internal/python.codon +++ b/stdlib/internal/python.codon @@ -109,6 +109,7 @@ PyErr_SetString = Function[[cobj, cobj], NoneType](cobj()) Py_None = cobj() Py_True = cobj() Py_False = cobj() +Py_Ellipsis = cobj() Py_NotImplemented = cobj() Py_LT = 0 Py_LE = 1 @@ -117,6 +118,18 @@ Py_NE = 3 Py_GT = 4 Py_GE = 5 +# types +PyLong_Type = cobj() +PyFloat_Type = cobj() +PyBool_Type = cobj() +PyUnicode_Type = cobj() +PyComplex_Type = cobj() +PyList_Type = cobj() +PyDict_Type = cobj() +PySet_Type = cobj() +PyTuple_Type = cobj() +PySlice_Type = cobj() + # exceptions PyExc_BaseException = cobj() PyExc_Exception = cobj() @@ -269,7 +282,18 @@ def init_handles_dlopen(py_handle: cobj): global Py_None global Py_True global Py_False + global Py_Ellipsis global Py_NotImplemented + global PyLong_Type + global PyFloat_Type + global PyBool_Type + global PyUnicode_Type + global PyComplex_Type + global PyList_Type + global PyDict_Type + global PySet_Type + global PyTuple_Type + global PySlice_Type global PyExc_BaseException global PyExc_Exception global PyExc_NameError @@ -384,7 +408,18 @@ def init_handles_dlopen(py_handle: cobj): Py_None = dlsym(py_handle, "_Py_NoneStruct") Py_True = dlsym(py_handle, "_Py_TrueStruct") Py_False = dlsym(py_handle, "_Py_FalseStruct") + Py_Ellipsis = dlsym(py_handle, "_Py_EllipsisObject") Py_NotImplemented = dlsym(py_handle, "_Py_NotImplementedStruct") + PyLong_Type = dlsym(py_handle, "PyLong_Type") + PyFloat_Type = dlsym(py_handle, "PyFloat_Type") + PyBool_Type = dlsym(py_handle, "PyBool_Type") + PyUnicode_Type = dlsym(py_handle, "PyUnicode_Type") + PyComplex_Type = dlsym(py_handle, "PyComplex_Type") + PyList_Type = dlsym(py_handle, "PyList_Type") + PyDict_Type = dlsym(py_handle, "PyDict_Type") + PySet_Type = dlsym(py_handle, "PySet_Type") + PyTuple_Type = dlsym(py_handle, "PyTuple_Type") + PySlice_Type = dlsym(py_handle, "PySlice_Type") PyExc_BaseException = Ptr[cobj](dlsym(py_handle, "PyExc_BaseException"))[0] PyExc_Exception = Ptr[cobj](dlsym(py_handle, "PyExc_Exception"))[0] PyExc_NameError = Ptr[cobj](dlsym(py_handle, "PyExc_NameError"))[0] @@ -500,7 +535,18 @@ def init_handles_static(): from C import _Py_NoneStruct: cobj from C import _Py_TrueStruct: cobj from C import _Py_FalseStruct: cobj + from C import _Py_EllipsisObject: cobj from C import _Py_NotImplementedStruct: cobj + from C import PyLong_Type: cobj as _PyLong_Type + from C import PyFloat_Type: cobj as _PyFloat_Type + from C import PyBool_Type: cobj as _PyBool_Type + from C import PyUnicode_Type: cobj as _PyUnicode_Type + from C import PyComplex_Type: cobj as _PyComplex_Type + from C import PyList_Type: cobj as _PyList_Type + from C import PyDict_Type: cobj as _PyDict_Type + from C import PySet_Type: cobj as _PySet_Type + from C import PyTuple_Type: cobj as _PyTuple_Type + from C import PySlice_Type: cobj as _PySlice_Type from C import PyExc_BaseException: cobj as _PyExc_BaseException from C import PyExc_Exception: cobj as _PyExc_Exception from C import PyExc_NameError: cobj as _PyExc_NameError @@ -615,7 +661,18 @@ def init_handles_static(): global Py_None global Py_True global Py_False + global Py_Ellipsis global Py_NotImplemented + global PyLong_Type + global PyFloat_Type + global PyBool_Type + global PyUnicode_Type + global PyComplex_Type + global PyList_Type + global PyDict_Type + global PySet_Type + global PyTuple_Type + global PySlice_Type global PyExc_BaseException global PyExc_Exception global PyExc_NameError @@ -730,7 +787,18 @@ def init_handles_static(): Py_None = __ptr__(_Py_NoneStruct).as_byte() Py_True = __ptr__(_Py_TrueStruct).as_byte() Py_False = __ptr__(_Py_FalseStruct).as_byte() + Py_Ellipsis = __ptr__(_Py_EllipsisObject).as_byte() Py_NotImplemented = __ptr__(_Py_NotImplementedStruct).as_byte() + PyLong_Type = __ptr__(_PyLong_Type).as_byte() + PyFloat_Type = __ptr__(_PyFloat_Type).as_byte() + PyBool_Type = __ptr__(_PyBool_Type).as_byte() + PyUnicode_Type = __ptr__(_PyUnicode_Type).as_byte() + PyComplex_Type = __ptr__(_PyComplex_Type).as_byte() + PyList_Type = __ptr__(_PyList_Type).as_byte() + PyDict_Type = __ptr__(_PyDict_Type).as_byte() + PySet_Type = __ptr__(_PySet_Type).as_byte() + PyTuple_Type = __ptr__(_PyTuple_Type).as_byte() + PySlice_Type = __ptr__(_PySlice_Type).as_byte() PyExc_BaseException = _PyExc_BaseException PyExc_Exception = _PyExc_Exception PyExc_NameError = _PyExc_NameError @@ -1148,6 +1216,14 @@ def _get_identifier(typ: str) -> pyobj: def _isinstance(what: pyobj, typ: pyobj) -> bool: return bool(pyobj.exc_wrap(PyObject_IsInstance(what.p, typ.p))) +@tuple +class _PyObject_Struct: + refcnt: int + pytype: cobj + +def _is_type(o: cobj, t: cobj): + return Ptr[_PyObject_Struct](o)[0].pytype == t + # Type conversions @@ -1160,6 +1236,9 @@ class NoneType: def __from_py__(i: cobj) -> None: return + def __try_from_py__(i: cobj) -> Optional[NoneType]: + return + @extend class int: def __to_py__(self) -> cobj: @@ -1168,6 +1247,11 @@ class int: def __from_py__(i: cobj) -> int: return pyobj.exc_wrap(PyLong_AsLong(i)) + def __try_from_py__(i: cobj) -> Optional[int]: + if _is_type(i, PyLong_Type): + return PyLong_AsLong(i) + return None + @extend class float: def __to_py__(self) -> cobj: @@ -1176,6 +1260,11 @@ class float: def __from_py__(d: cobj) -> float: return pyobj.exc_wrap(PyFloat_AsDouble(d)) + def __try_from_py__(d: cobj) -> Optional[float]: + if _is_type(d, PyFloat_Type): + return PyFloat_AsDouble(d) + return None + @extend class bool: def __to_py__(self) -> cobj: @@ -1184,6 +1273,11 @@ class bool: def __from_py__(b: cobj) -> bool: return pyobj.exc_wrap(PyObject_IsTrue(b)) != 0 + def __try_from_py__(b: cobj) -> Optional[bool]: + if _is_type(b, PyBool_Type): + return PyObject_IsTrue(b) != 0 + return None + @extend class byte: def __to_py__(self) -> cobj: @@ -1192,6 +1286,12 @@ class byte: def __from_py__(c: cobj) -> byte: return str.__from_py__(c).ptr[0] + def __try_from_py__(c: cobj) -> Optional[byte]: + x = str.__try_from_py__(c) + if x is None or len(x) != 1: + return None + return x.ptr[0] + @extend class str: def __to_py__(self) -> cobj: @@ -1200,6 +1300,11 @@ class str: def __from_py__(s: cobj) -> str: return pyobj.exc_wrap(pyobj.to_str(s, "strict")) + def __try_from_py__(s: cobj) -> Optional[str]: + if _is_type(s, PyUnicode_Type): + return pyobj.to_str(s, "strict") + return None + @extend class complex: def __to_py__(self) -> cobj: @@ -1210,6 +1315,13 @@ class complex: imag = pyobj.exc_wrap(PyComplex_ImagAsDouble(c)) return complex(real, imag) + def __try_from_py__(c: cobj) -> Optional[complex]: + if _is_type(c, PyComplex_Type): + real = PyComplex_RealAsDouble(c) + imag = PyComplex_ImagAsDouble(c) + return complex(real, imag) + return None + @extend class List: def __to_py__(self) -> cobj: @@ -1231,6 +1343,19 @@ class List: t.append(T.__from_py__(elem)) return t + def __try_from_py__(v: cobj) -> Optional[List[T]]: + if _is_type(v, PyList_Type): + n = PyObject_Length(v) + t = List[T](n) + for i in range(n): + elem = PyList_GetItem(v, i) + x = T.__try_from_py__(elem) + if x is None: + return None + t.append(x) + return t + return None + @extend class Dict: def __to_py__(self) -> cobj: @@ -1253,6 +1378,21 @@ class Dict: b[k] = v return b + def __try_from_py__(d: cobj) -> Optional[Dict[K, V]]: + if _is_type(d, PyDict_Type): + b = dict[K, V]() + pos = 0 + k_ptr = cobj() + v_ptr = cobj() + while PyDict_Next(d, __ptr__(pos), __ptr__(k_ptr), __ptr__(v_ptr)): + k = K.__try_from_py__(k_ptr) + v = V.__try_from_py__(v_ptr) + if k is None or v is None: + return None + b[k] = v + return b + return None + @extend class Set: def __to_py__(self) -> cobj: @@ -1276,6 +1416,23 @@ class Set: pyobj.decref(s_iter) return b + def __try_from_py__(s: cobj) -> Optional[Set[K]]: + if _is_type(s, PySet_Type): + b = set[K]() + s_iter = PyObject_GetIter(s) + while True: + k_ptr = PyIter_Next(s_iter) + if not k_ptr: + break + k = K.__try_from_py__(k_ptr) + if k is None: + return None + pyobj.decref(k_ptr) + b.add(k) + pyobj.decref(s_iter) + return b + return None + @extend class DynamicTuple: def __to_py__(self) -> cobj: @@ -1294,6 +1451,18 @@ class DynamicTuple: p[i] = T.__from_py__(PyTuple_GetItem(t, i)) return DynamicTuple(p, n) + def __try_from_py__(t: cobj) -> Optional[DynamicTuple[T]]: + if _is_type(t, PyTuple_Type): + n = PyTuple_Size(t) + p = Ptr[T](n) + for i in range(n): + x = T.__try_from_py__(PyTuple_GetItem(t, i)) + if x is None: + return None + p[i] = x + return DynamicTuple(p, n) + return None + @extend class Slice: def __to_py__(self) -> cobj: @@ -1312,6 +1481,15 @@ class Slice: pyobj.exc_wrap(PySlice_Unpack(s, __ptr__(start), __ptr__(stop), __ptr__(step))) return Slice(Optional(start), Optional(stop), Optional(step)) + def __try_from_py__(s: cobj) -> Optional[Slice]: + if _is_type(s, PySlice_Type): + start = 0 + stop = 0 + step = 0 + PySlice_Unpack(s, __ptr__(start), __ptr__(stop), __ptr__(step)) + return Slice(Optional(start), Optional(stop), Optional(step)) + return None + @extend class Optional: def __to_py__(self) -> cobj: @@ -1326,6 +1504,12 @@ class Optional: else: return Optional[T](T.__from_py__(o)) + def __try_from_py__(o: cobj) -> Optional[Optional[T]]: + if o == Py_None: + return Optional[Optional[T]]() + else: + return Optional[Optional[T]](T.__try_from_py__(o)) + __pyenv__: Optional[pyobj] = None def _____(): __pyenv__ # make it global!