| 1 | #include "Python.h"
|
| 2 |
|
| 3 | // Notes on thread-local state that objects use (PyThreadState)
|
| 4 | //
|
| 5 | // Py_EnterRecursiveCall / Py_LeaveRecursiveCall -- so we don't blow the stack
|
| 6 | // Py_ReprEnter / Py_ReprLeave -- for printing [...] in self-referential structures
|
| 7 | //
|
| 8 | // PyErr_Occurred -- check for exceptions, stubbed in pgenmain
|
| 9 | // PyErr_CheckSignals -- for OS signals, stubbed in sigcheck.c, and here
|
| 10 | //
|
| 11 | // Notes on builtins like int(). See Python/bltinmodule.c.
|
| 12 | //
|
| 13 | // #define SETBUILTIN(NAME, OBJECT) \
|
| 14 | // if (PyDict_SetItemString(dict, NAME, (PyObject *)OBJECT) < 0) \
|
| 15 | // return NULL; \
|
| 16 | // ADD_TO_ALL(OBJECT)
|
| 17 | //
|
| 18 | // SETBUILTIN("int", &PyInt_Type);
|
| 19 | //
|
| 20 | // It creates the __builtin__ module, which has a __dict__. And then the types
|
| 21 | // are here. So you can just call a type directly? Sure.
|
| 22 |
|
| 23 | // #include "longintrepr.h" // 32 bytes
|
| 24 |
|
| 25 | #include <stdarg.h> // va_list, etc.
|
| 26 | #include <stdio.h> // vprintf
|
| 27 |
|
| 28 | void Log(const char* fmt, ...) {
|
| 29 | va_list args;
|
| 30 | va_start(args, fmt);
|
| 31 | vprintf(fmt, args);
|
| 32 | va_end(args);
|
| 33 | printf("\n");
|
| 34 | }
|
| 35 |
|
| 36 |
|
| 37 | int Py_VerboseFlag; /* pythonrun.c, does only intobject.c use it? */
|
| 38 |
|
| 39 | PyThreadState *_PyThreadState_Current = NULL;
|
| 40 |
|
| 41 | struct _inittab _PyImport_Inittab[] = {
|
| 42 | /* Sentinel */
|
| 43 | {0, 0}
|
| 44 | };
|
| 45 |
|
| 46 | void
|
| 47 | Py_FatalError(const char *msg)
|
| 48 | {
|
| 49 | fprintf(stderr, "Fatal Python error: %s\n", msg);
|
| 50 | abort();
|
| 51 | }
|
| 52 |
|
| 53 | /* Even smaller than Python/sigcheck.c */
|
| 54 | int
|
| 55 | PyErr_CheckSignals(void)
|
| 56 | {
|
| 57 | return 0;
|
| 58 | }
|
| 59 |
|
| 60 | /* APIs to write to sys.stdout or sys.stderr using a printf-like interface.
|
| 61 | Adapted from code submitted by Just van Rossum.
|
| 62 |
|
| 63 | PySys_WriteStdout(format, ...)
|
| 64 | PySys_WriteStderr(format, ...)
|
| 65 |
|
| 66 | The first function writes to sys.stdout; the second to sys.stderr. When
|
| 67 | there is a problem, they write to the real (C level) stdout or stderr;
|
| 68 | no exceptions are raised.
|
| 69 |
|
| 70 | Both take a printf-style format string as their first argument followed
|
| 71 | by a variable length argument list determined by the format string.
|
| 72 |
|
| 73 | *** WARNING ***
|
| 74 |
|
| 75 | The format should limit the total size of the formatted output string to
|
| 76 | 1000 bytes. In particular, this means that no unrestricted "%s" formats
|
| 77 | should occur; these should be limited using "%.<N>s where <N> is a
|
| 78 | decimal number calculated so that <N> plus the maximum size of other
|
| 79 | formatted text does not exceed 1000 bytes. Also watch out for "%f",
|
| 80 | which can print hundreds of digits for very large numbers.
|
| 81 |
|
| 82 | */
|
| 83 |
|
| 84 | static void
|
| 85 | mywrite(char *name, FILE *fp, const char *format, va_list va)
|
| 86 | {
|
| 87 | #ifdef OBJECTS_ONLY
|
| 88 | assert(0);
|
| 89 | #else
|
| 90 | PyObject *file;
|
| 91 | PyObject *error_type, *error_value, *error_traceback;
|
| 92 |
|
| 93 | PyErr_Fetch(&error_type, &error_value, &error_traceback);
|
| 94 | file = PySys_GetObject(name);
|
| 95 | if (file == NULL || PyFile_AsFile(file) == fp)
|
| 96 | vfprintf(fp, format, va);
|
| 97 | else {
|
| 98 | char buffer[1001];
|
| 99 | const int written = PyOS_vsnprintf(buffer, sizeof(buffer),
|
| 100 | format, va);
|
| 101 | if (PyFile_WriteString(buffer, file) != 0) {
|
| 102 | PyErr_Clear();
|
| 103 | fputs(buffer, fp);
|
| 104 | }
|
| 105 | if (written < 0 || (size_t)written >= sizeof(buffer)) {
|
| 106 | const char *truncated = "... truncated";
|
| 107 | if (PyFile_WriteString(truncated, file) != 0) {
|
| 108 | PyErr_Clear();
|
| 109 | fputs(truncated, fp);
|
| 110 | }
|
| 111 | }
|
| 112 | }
|
| 113 | PyErr_Restore(error_type, error_value, error_traceback);
|
| 114 | #endif
|
| 115 | }
|
| 116 |
|
| 117 | /*
|
| 118 | void
|
| 119 | PySys_WriteStdout(const char *format, ...)
|
| 120 | {
|
| 121 | va_list va;
|
| 122 |
|
| 123 | va_start(va, format);
|
| 124 | mywrite("stdout", stdout, format, va);
|
| 125 | va_end(va);
|
| 126 | }
|
| 127 | */
|
| 128 |
|
| 129 | void
|
| 130 | PySys_WriteStderr(const char *format, ...)
|
| 131 | {
|
| 132 | va_list va;
|
| 133 |
|
| 134 | va_start(va, format);
|
| 135 | mywrite("stderr", stderr, format, va);
|
| 136 | va_end(va);
|
| 137 | }
|
| 138 |
|
| 139 |
|
| 140 | #define FILE_BEGIN_ALLOW_THREADS(fobj)
|
| 141 | #define FILE_END_ALLOW_THREADS(fobj)
|
| 142 |
|
| 143 | static PyObject *
|
| 144 | err_closed(void)
|
| 145 | {
|
| 146 | PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
|
| 147 | return NULL;
|
| 148 | }
|
| 149 |
|
| 150 | /* Interfaces to write objects/strings to file-like objects */
|
| 151 |
|
| 152 | int
|
| 153 | PyFile_WriteObject(PyObject *v, PyObject *f, int flags)
|
| 154 | {
|
| 155 | #ifdef OBJECTS_ONLY
|
| 156 | assert(0);
|
| 157 | #else
|
| 158 | PyObject *writer, *value, *args, *result;
|
| 159 | if (f == NULL) {
|
| 160 | PyErr_SetString(PyExc_TypeError, "writeobject with NULL file");
|
| 161 | return -1;
|
| 162 | }
|
| 163 | else if (PyFile_Check(f)) {
|
| 164 | PyFileObject *fobj = (PyFileObject *) f;
|
| 165 | #ifdef Py_USING_UNICODE
|
| 166 | PyObject *enc = fobj->f_encoding;
|
| 167 | int result;
|
| 168 | #endif
|
| 169 | if (fobj->f_fp == NULL) {
|
| 170 | err_closed();
|
| 171 | return -1;
|
| 172 | }
|
| 173 | #ifdef Py_USING_UNICODE
|
| 174 | if ((flags & Py_PRINT_RAW) &&
|
| 175 | PyUnicode_Check(v) && enc != Py_None) {
|
| 176 | char *cenc = PyString_AS_STRING(enc);
|
| 177 | char *errors = fobj->f_errors == Py_None ?
|
| 178 | "strict" : PyString_AS_STRING(fobj->f_errors);
|
| 179 | value = PyUnicode_AsEncodedString(v, cenc, errors);
|
| 180 | if (value == NULL)
|
| 181 | return -1;
|
| 182 | } else {
|
| 183 | value = v;
|
| 184 | Py_INCREF(value);
|
| 185 | }
|
| 186 | result = file_PyObject_Print(value, fobj, flags);
|
| 187 | Py_DECREF(value);
|
| 188 | return result;
|
| 189 | #else
|
| 190 | return file_PyObject_Print(v, fobj, flags);
|
| 191 | #endif
|
| 192 | }
|
| 193 | writer = PyObject_GetAttrString(f, "write");
|
| 194 | if (writer == NULL)
|
| 195 | return -1;
|
| 196 | if (flags & Py_PRINT_RAW) {
|
| 197 | if (PyUnicode_Check(v)) {
|
| 198 | value = v;
|
| 199 | Py_INCREF(value);
|
| 200 | } else
|
| 201 | value = PyObject_Str(v);
|
| 202 | }
|
| 203 | else
|
| 204 | value = PyObject_Repr(v);
|
| 205 | if (value == NULL) {
|
| 206 | Py_DECREF(writer);
|
| 207 | return -1;
|
| 208 | }
|
| 209 | args = PyTuple_Pack(1, value);
|
| 210 | if (args == NULL) {
|
| 211 | Py_DECREF(value);
|
| 212 | Py_DECREF(writer);
|
| 213 | return -1;
|
| 214 | }
|
| 215 | result = PyEval_CallObject(writer, args);
|
| 216 | Py_DECREF(args);
|
| 217 | Py_DECREF(value);
|
| 218 | Py_DECREF(writer);
|
| 219 | if (result == NULL)
|
| 220 | return -1;
|
| 221 | Py_DECREF(result);
|
| 222 | #endif
|
| 223 | return 0;
|
| 224 | }
|
| 225 |
|
| 226 | int
|
| 227 | PyFile_WriteString(const char *s, PyObject *f)
|
| 228 | {
|
| 229 | #ifdef OBJECTS_ONLY
|
| 230 | assert(0);
|
| 231 | #else
|
| 232 | if (f == NULL) {
|
| 233 | /* Should be caused by a pre-existing error */
|
| 234 | if (!PyErr_Occurred())
|
| 235 | PyErr_SetString(PyExc_SystemError,
|
| 236 | "null file for PyFile_WriteString");
|
| 237 | return -1;
|
| 238 | }
|
| 239 | else if (PyFile_Check(f)) {
|
| 240 | PyFileObject *fobj = (PyFileObject *) f;
|
| 241 | FILE *fp = PyFile_AsFile(f);
|
| 242 | if (fp == NULL) {
|
| 243 | err_closed();
|
| 244 | return -1;
|
| 245 | }
|
| 246 | FILE_BEGIN_ALLOW_THREADS(fobj)
|
| 247 | fputs(s, fp);
|
| 248 | FILE_END_ALLOW_THREADS(fobj)
|
| 249 | return 0;
|
| 250 | }
|
| 251 | else if (!PyErr_Occurred()) {
|
| 252 | PyObject *v = PyString_FromString(s);
|
| 253 | int err;
|
| 254 | if (v == NULL)
|
| 255 | return -1;
|
| 256 | err = PyFile_WriteObject(v, f, Py_PRINT_RAW);
|
| 257 | Py_DECREF(v);
|
| 258 | return err;
|
| 259 | }
|
| 260 | else
|
| 261 | return -1;
|
| 262 | #endif
|
| 263 | }
|
| 264 |
|
| 265 |
|
| 266 | /* Stub of function in Python/_warnings.c */
|
| 267 | int
|
| 268 | PyErr_WarnEx(PyObject *category, const char *text, Py_ssize_t stack_level)
|
| 269 | {
|
| 270 | return 0;
|
| 271 | }
|
| 272 |
|
| 273 | /* from Python/ceval.c */
|
| 274 |
|
| 275 | #define Py_DEFAULT_RECURSION_LIMIT 1000
|
| 276 | /* a lot of code uses this too */
|
| 277 | int _Py_CheckRecursionLimit = Py_DEFAULT_RECURSION_LIMIT;
|
| 278 |
|
| 279 | int
|
| 280 | Py_GetRecursionLimit(void)
|
| 281 | {
|
| 282 | return Py_DEFAULT_RECURSION_LIMIT;
|
| 283 | }
|
| 284 |
|
| 285 | void
|
| 286 | Py_SetRecursionLimit(int new_limit)
|
| 287 | {
|
| 288 | }
|
| 289 |
|
| 290 | int
|
| 291 | _Py_CheckRecursiveCall(const char *where)
|
| 292 | {
|
| 293 | return 0;
|
| 294 | }
|
| 295 |
|
| 296 | /* Extract a slice index from a PyInt or PyLong or an object with the
|
| 297 | nb_index slot defined, and store in *pi.
|
| 298 | Silently reduce values larger than PY_SSIZE_T_MAX to PY_SSIZE_T_MAX,
|
| 299 | and silently boost values less than -PY_SSIZE_T_MAX-1 to -PY_SSIZE_T_MAX-1.
|
| 300 | Return 0 on error, 1 on success.
|
| 301 | */
|
| 302 | /* Note: If v is NULL, return success without storing into *pi. This
|
| 303 | is because_PyEval_SliceIndex() is called by apply_slice(), which can be
|
| 304 | called by the SLICE opcode with v and/or w equal to NULL.
|
| 305 | */
|
| 306 | int
|
| 307 | _PyEval_SliceIndex(PyObject *v, Py_ssize_t *pi)
|
| 308 | {
|
| 309 | if (v != NULL) {
|
| 310 | Py_ssize_t x;
|
| 311 | if (PyInt_Check(v)) {
|
| 312 | /* XXX(nnorwitz): I think PyInt_AS_LONG is correct,
|
| 313 | however, it looks like it should be AsSsize_t.
|
| 314 | There should be a comment here explaining why.
|
| 315 | */
|
| 316 | x = PyInt_AS_LONG(v);
|
| 317 | }
|
| 318 | else if (PyIndex_Check(v)) {
|
| 319 | x = PyNumber_AsSsize_t(v, NULL);
|
| 320 | if (x == -1 && PyErr_Occurred())
|
| 321 | return 0;
|
| 322 | }
|
| 323 | else {
|
| 324 | PyErr_SetString(PyExc_TypeError,
|
| 325 | "slice indices must be integers or "
|
| 326 | "None or have an __index__ method");
|
| 327 | return 0;
|
| 328 | }
|
| 329 | *pi = x;
|
| 330 | }
|
| 331 | return 1;
|
| 332 | }
|
| 333 |
|
| 334 | /* External interface to call any callable object.
|
| 335 | The arg must be a tuple or NULL. The kw must be a dict or NULL. */
|
| 336 |
|
| 337 | PyObject *
|
| 338 | PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw)
|
| 339 | {
|
| 340 | PyObject *result;
|
| 341 |
|
| 342 | if (arg == NULL) {
|
| 343 | arg = PyTuple_New(0);
|
| 344 | if (arg == NULL)
|
| 345 | return NULL;
|
| 346 | }
|
| 347 | else if (!PyTuple_Check(arg)) {
|
| 348 | PyErr_SetString(PyExc_TypeError,
|
| 349 | "argument list must be a tuple");
|
| 350 | return NULL;
|
| 351 | }
|
| 352 | else
|
| 353 | Py_INCREF(arg);
|
| 354 |
|
| 355 | if (kw != NULL && !PyDict_Check(kw)) {
|
| 356 | PyErr_SetString(PyExc_TypeError,
|
| 357 | "keyword list must be a dictionary");
|
| 358 | Py_DECREF(arg);
|
| 359 | return NULL;
|
| 360 | }
|
| 361 |
|
| 362 | result = PyObject_Call(func, arg, kw);
|
| 363 | Py_DECREF(arg);
|
| 364 | return result;
|
| 365 | }
|
| 366 |
|
| 367 | /* NOTE: Can't use a switch here because it's not an integer constant!
|
| 368 | *
|
| 369 | * Instead of "with tagswitch", do "with typeswitch"?
|
| 370 | * */
|
| 371 |
|
| 372 | void Type(PyObject* obj) {
|
| 373 | struct _typeobject* type = obj->ob_type;
|
| 374 |
|
| 375 | if (type == &PyBool_Type) {
|
| 376 | Log("bool");
|
| 377 | } else if (type == &PyLong_Type) {
|
| 378 | Log("long");
|
| 379 | } else if (type == &PyFloat_Type) {
|
| 380 | Log("float");
|
| 381 | } else if (type == &PyString_Type) {
|
| 382 | Log("string");
|
| 383 | } else if (type == &PyTuple_Type) {
|
| 384 | Log("tuple");
|
| 385 | } else if (type == &PyList_Type) {
|
| 386 | Log("list");
|
| 387 | } else if (type == &PyDict_Type) {
|
| 388 | Log("dict");
|
| 389 | } else {
|
| 390 | Log("unknown type");
|
| 391 | }
|
| 392 | }
|
| 393 |
|
| 394 | void PrintList(reprfunc list_repr, PyObject* list) {
|
| 395 | PyObject* r = list_repr(list);
|
| 396 | assert(r != NULL);
|
| 397 | PyStringObject* rstr = (PyStringObject*) r;
|
| 398 |
|
| 399 | fprintf(stderr, "list = %.*s\n", (int)rstr->ob_size, rstr->ob_sval);
|
| 400 | }
|
| 401 |
|
| 402 | int main(int argc, char **argv) {
|
| 403 | PyObject* bool1 = PyBool_FromLong(1);
|
| 404 |
|
| 405 | reprfunc bool_repr = PyBool_Type.tp_repr;
|
| 406 | PyObject* b = bool_repr(bool1);
|
| 407 |
|
| 408 | PyStringObject* br = (PyStringObject*) b;
|
| 409 |
|
| 410 | fprintf(stderr, "true = %.*s\n", (int)br->ob_size, br->ob_sval);
|
| 411 |
|
| 412 |
|
| 413 | PyObject* long1 = PyLong_FromLong(42);
|
| 414 | PyObject* long2 = PyLong_FromLong(1);
|
| 415 |
|
| 416 | binaryfunc long_add = PyLong_Type.tp_as_number->nb_add;
|
| 417 | PyObject* long_sum = long_add(long1, long2);
|
| 418 |
|
| 419 | reprfunc long_repr = PyLong_Type.tp_repr;
|
| 420 | PyObject* r = long_repr(long_sum);
|
| 421 |
|
| 422 | PyStringObject* rstr = (PyStringObject*) r;
|
| 423 |
|
| 424 | fprintf(stderr, "42 + 1 = %.*s\n", (int)rstr->ob_size, rstr->ob_sval);
|
| 425 |
|
| 426 |
|
| 427 | PyObject* float1 = PyFloat_FromDouble(42.1);
|
| 428 | PyObject* float2 = PyFloat_FromDouble(1.2);
|
| 429 |
|
| 430 | binaryfunc float_add = PyFloat_Type.tp_as_number->nb_add;
|
| 431 | PyObject* float_sum = float_add(float1, float2);
|
| 432 |
|
| 433 | reprfunc float_repr = PyFloat_Type.tp_repr;
|
| 434 | PyObject* r3 = float_repr(float_sum);
|
| 435 |
|
| 436 | PyStringObject* rstr3 = (PyStringObject*) r3;
|
| 437 |
|
| 438 | fprintf(stderr, "42.1 + 1.2 = %.*s\n", (int)rstr3->ob_size, rstr3->ob_sval);
|
| 439 |
|
| 440 |
|
| 441 | PyObject* str1 = PyString_FromString("foo ");
|
| 442 | PyObject* str2 = PyString_FromString("bar");
|
| 443 |
|
| 444 | binaryfunc str_add = PyString_Type.tp_as_sequence->sq_concat;
|
| 445 | PyObject* concat = str_add(str1, str2);
|
| 446 |
|
| 447 | reprfunc str_repr = PyString_Type.tp_repr;
|
| 448 | PyObject* r2 = str_repr(concat);
|
| 449 |
|
| 450 | PyStringObject* rstr2 = (PyStringObject*) r2;
|
| 451 |
|
| 452 | fprintf(stderr, "foo + bar = %.*s\n", (int)rstr2->ob_size, rstr2->ob_sval);
|
| 453 |
|
| 454 | // List
|
| 455 |
|
| 456 | PyObject* list = PyList_New(3);
|
| 457 | PyList_SetItem(list, 0, long_sum);
|
| 458 | PyList_SetItem(list, 1, float_sum);
|
| 459 | /* Test the printing of cyclic data structures */
|
| 460 | PyList_SetItem(list, 2, list);
|
| 461 |
|
| 462 | reprfunc list_repr = PyList_Type.tp_repr;
|
| 463 | PyObject* r5 = list_repr(list);
|
| 464 | assert(r5 != NULL);
|
| 465 | PyStringObject* rstr5 = (PyStringObject*) r5;
|
| 466 |
|
| 467 | fprintf(stderr, "list = %.*s\n", (int)rstr5->ob_size, rstr5->ob_sval);
|
| 468 |
|
| 469 | Log("");
|
| 470 |
|
| 471 | // Sort and reverse list
|
| 472 | PyObject* list2 = PyList_New(3);
|
| 473 | PyList_SetItem(list2, 0, long1);
|
| 474 | PyList_SetItem(list2, 1, long2);
|
| 475 | PyList_SetItem(list2, 2, long_sum);
|
| 476 | PrintList(list_repr, list2);
|
| 477 |
|
| 478 | // These are internal API calls. listsort() and listreverse() are in the
|
| 479 | // method table, and do less work.
|
| 480 | PyList_Sort(list2);
|
| 481 | PrintList(list_repr, list2);
|
| 482 |
|
| 483 | PyList_Reverse(list2);
|
| 484 | PrintList(list_repr, list2);
|
| 485 |
|
| 486 | /* Works if you make it non-static
|
| 487 | listreverse(list2);
|
| 488 | PrintList(list_repr, list2);
|
| 489 | */
|
| 490 |
|
| 491 | Log("");
|
| 492 |
|
| 493 | // Tuple
|
| 494 |
|
| 495 | PyObject* tuple = PyTuple_New(3);
|
| 496 | PyTuple_SetItem(tuple, 0, long_sum);
|
| 497 | PyTuple_SetItem(tuple, 1, float_sum);
|
| 498 | PyTuple_SetItem(tuple, 2, concat);
|
| 499 |
|
| 500 | reprfunc tuple_repr = PyTuple_Type.tp_repr;
|
| 501 | PyObject* r4 = tuple_repr(tuple);
|
| 502 | assert(r4 != NULL);
|
| 503 |
|
| 504 | PyStringObject* rstr4 = (PyStringObject*) r4;
|
| 505 |
|
| 506 | fprintf(stderr, "tuple = %.*s\n", (int)rstr4->ob_size, rstr4->ob_sval);
|
| 507 |
|
| 508 | // Dict
|
| 509 |
|
| 510 | PyObject* dict = PyDict_New();
|
| 511 | PyDict_SetItem(dict, concat, long_sum);
|
| 512 |
|
| 513 | reprfunc dict_repr = PyDict_Type.tp_repr;
|
| 514 | PyObject* d = dict_repr(dict);
|
| 515 | assert(d != NULL);
|
| 516 |
|
| 517 | PyStringObject* dr = (PyStringObject*) d;
|
| 518 |
|
| 519 | fprintf(stderr, "dict = %.*s\n", (int)dr->ob_size, dr->ob_sval);
|
| 520 |
|
| 521 | // suceess
|
| 522 | PyObject* val = PyDict_GetItem(dict, concat);
|
| 523 | Log("val = %p", val);
|
| 524 |
|
| 525 | // fail
|
| 526 | PyObject* val2 = PyDict_GetItem(dict, long1);
|
| 527 | Log("val2 = %p", val2);
|
| 528 | // TODO: How to get exception?
|
| 529 |
|
| 530 | Type(long_sum);
|
| 531 | Type(float_sum);
|
| 532 | Type(list);
|
| 533 | Type(dict);
|
| 534 |
|
| 535 | Log("sizeof(PyBoolObject) = %zu", sizeof(PyBoolObject)); // 24
|
| 536 | // 32 bytes, needs header
|
| 537 | //Log("sizeof(PyLongObject) = %zu", sizeof(PyLongObject));
|
| 538 | Log("sizeof(PyFloatObject) = %zu", sizeof(PyFloatObject)); // 24
|
| 539 | Log("sizeof(PyStringObject) = %zu", sizeof(PyStringObject)); // 40
|
| 540 | Log("sizeof(PyTupleObject) = %zu", sizeof(PyTupleObject)); // 32
|
| 541 | Log("sizeof(PyListObject) = %zu", sizeof(PyListObject)); // 40
|
| 542 | Log("sizeof(PyDictObject) = %zu", sizeof(PyDictObject)); // 248
|
| 543 |
|
| 544 |
|
| 545 | // TODO:
|
| 546 | // - Iterate over dicts and lists
|
| 547 | // - Call methods -- how?
|
| 548 | // - .keys() and .value() -- see addmethods()
|
| 549 | // - respect calling convention flags for L.sort() and L.reverse()
|
| 550 | // - Test exceptions like IndexError, KeyError, and more
|
| 551 | // - Other types: slices? What about range and enumeration?
|
| 552 | // - repr(None)? How? Py_None
|
| 553 | // object.c has PyNone_Type, but it's not in a header!
|
| 554 | // - builtins like int() str()
|
| 555 | return 0;
|
| 556 | }
|