void raise3(Box* arg0, Box* arg1, Box* arg2) { // TODO switch this to PyErr_Normalize if (arg2 == None) arg2 = getTraceback(); if (isSubclass(arg0->cls, type_cls)) { BoxedClass* c = static_cast<BoxedClass*>(arg0); if (isSubclass(c, BaseException)) { Box* exc_obj; if (isSubclass(arg1->cls, BaseException)) { exc_obj = arg1; c = exc_obj->cls; } else if (arg1 != None) { exc_obj = runtimeCall(c, ArgPassSpec(1), arg1, NULL, NULL, NULL, NULL); } else { exc_obj = runtimeCall(c, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL); } raiseRaw(ExcInfo(c, exc_obj, arg2)); } } if (isSubclass(arg0->cls, BaseException)) { if (arg1 != None) raiseExcHelper(TypeError, "instance exception may not have a separate value"); raiseRaw(ExcInfo(arg0->cls, arg0, arg2)); } raiseExcHelper(TypeError, "exceptions must be old-style classes or derived from BaseException, not %s", getTypeName(arg0)); }
void raise3(Box* arg0, Box* arg1, Box* arg2) { RELEASE_ASSERT(arg2 == None, "unsupported"); if (isSubclass(arg0->cls, type_cls)) { BoxedClass* c = static_cast<BoxedClass*>(arg0); if (isSubclass(c, Exception)) { Box* exc_obj; if (arg1 != None) exc_obj = exceptionNew2(c, arg1); else exc_obj = exceptionNew1(c); raiseExc(exc_obj); } else { raiseExcHelper(TypeError, "exceptions must be old-style classes or derived from BaseException, not %s", getTypeName(arg0)->c_str()); } } if (arg1 != None) raiseExcHelper(TypeError, "instance exception may not have a separate value"); // TODO: should only allow throwing of old-style classes or things derived // from BaseException: raiseExc(arg0); }
void raise3(Box* arg0, Box* arg1, Box* arg2) { // TODO switch this to PyErr_Normalize if (isSubclass(arg0->cls, type_cls)) { BoxedClass* c = static_cast<BoxedClass*>(arg0); if (isSubclass(c, BaseException)) { Box* exc_obj; if (arg1 != None) exc_obj = exceptionNew2(c, arg1); else exc_obj = exceptionNew1(c); raiseRaw(ExcInfo(c, exc_obj, arg2)); } } if (isSubclass(arg0->cls, BaseException)) { if (arg1 != None) raiseExcHelper(TypeError, "instance exception may not have a separate value"); raiseRaw(ExcInfo(arg0->cls, arg0, arg2)); } raiseExcHelper(TypeError, "exceptions must be old-style classes or derived from BaseException, not %s", getTypeName(arg0)->c_str()); }
extern "C" Box* listPop(BoxedList* self, Box* idx) { LOCK_REGION(self->lock.asWrite()); if (idx == None) { if (self->size == 0) { raiseExcHelper(IndexError, "pop from empty list"); } self->size--; Box* rtn = self->elts->elts[self->size]; return rtn; } if (idx->cls != int_cls) { raiseExcHelper(TypeError, "an integer is required"); } int64_t n = static_cast<BoxedInt*>(idx)->n; if (n < 0) n = self->size + n; if (n < 0 || n >= self->size) { if (self->size == 0) fprintf(stderr, "IndexError: pop from empty list\n"); else fprintf(stderr, "IndexError: pop index out of range\n"); raiseExcHelper(IndexError, ""); } Box* rtn = self->elts->elts[n]; memmove(self->elts->elts + n, self->elts->elts + n + 1, (self->size - n - 1) * sizeof(Box*)); self->size--; return rtn; }
Box* fileRead(BoxedFile* self, Box* _size) { assert(self->cls == file_cls); if (_size->cls != int_cls) { fprintf(stderr, "TypeError: an integer is required\n"); raiseExcHelper(TypeError, ""); } int64_t size = static_cast<BoxedInt*>(_size)->n; if (self->closed) { fprintf(stderr, "IOError: file not open for reading\n"); raiseExcHelper(IOError, ""); } std::ostringstream os(""); if (size < 0) size = 1L << 60; i64 read = 0; while (read < size) { const int BUF_SIZE = 1024; char buf[BUF_SIZE]; size_t more_read = fread(buf, 1, std::min((i64)BUF_SIZE, size - read), self->f); if (more_read == 0) { ASSERT(!ferror(self->f), "%d", ferror(self->f)); break; } read += more_read; // this is probably inefficient: os << std::string(buf, more_read); } return boxString(os.str()); }
Box* setRemove(BoxedSet* self, Box* key) { RELEASE_ASSERT(isSubclass(self->cls, set_cls), ""); if (PySet_Check(key)) { try { bool existed = _setRemove(self, key); if (existed) return incref(None); } catch (ExcInfo e) { if (!e.matches(TypeError)) throw e; e.clear(); BoxedSet* tmpKey = makeNewSet(frozenset_cls, key); AUTO_DECREF(tmpKey); bool existed = _setRemove(self, tmpKey); if (existed) return incref(None); } raiseExcHelper(KeyError, key); } bool existed = _setRemove(self, key); if (existed) return incref(None); raiseExcHelper(KeyError, key); }
Box* complexNew(Box* _cls, Box* real, Box* imag) { RELEASE_ASSERT(_cls == complex_cls, ""); double real_f; if (real->cls == int_cls) { real_f = static_cast<BoxedInt*>(real)->n; } else if (real->cls == float_cls) { real_f = static_cast<BoxedFloat*>(real)->d; } else { // TODO: implement taking a string argument raiseExcHelper(TypeError, "complex() argument must be a string or number"); } double imag_f; if (imag->cls == int_cls) { imag_f = static_cast<BoxedInt*>(imag)->n; } else if (imag->cls == float_cls) { imag_f = static_cast<BoxedFloat*>(imag)->d; } else if (imag->cls == str_cls) { raiseExcHelper(TypeError, "complex() second arg can't be a string"); } else { raiseExcHelper(TypeError, "complex() argument must be a string or number"); } return new BoxedComplex(real_f, imag_f); }
static Box* _intNew(Box* val, Box* base) { if (isSubclass(val->cls, int_cls)) { RELEASE_ASSERT(!base, ""); BoxedInt* n = static_cast<BoxedInt*>(val); if (val->cls == int_cls) return n; return new BoxedInt(n->n); } else if (isSubclass(val->cls, str_cls)) { int base_n; if (!base) base_n = 10; else { RELEASE_ASSERT(base->cls == int_cls, ""); base_n = static_cast<BoxedInt*>(base)->n; } BoxedString* s = static_cast<BoxedString*>(val); RELEASE_ASSERT(s->size() == strlen(s->data()), ""); Box* r = PyInt_FromString(s->data(), NULL, base_n); if (!r) throwCAPIException(); return r; } else if (isSubclass(val->cls, unicode_cls)) { int base_n; if (!base) base_n = 10; else { RELEASE_ASSERT(base->cls == int_cls, ""); base_n = static_cast<BoxedInt*>(base)->n; } Box* r = PyInt_FromUnicode(PyUnicode_AS_UNICODE(val), PyUnicode_GET_SIZE(val), base_n); if (!r) throwCAPIException(); return r; } else if (val->cls == float_cls) { RELEASE_ASSERT(!base, ""); double d = static_cast<BoxedFloat*>(val)->d; return new BoxedInt(d); } else { RELEASE_ASSERT(!base, ""); static const std::string int_str("__int__"); Box* r = callattr(val, &int_str, CallattrFlags({.cls_only = true, .null_on_nonexistent = true }), ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL); if (!r) { fprintf(stderr, "TypeError: int() argument must be a string or a number, not '%s'\n", getTypeName(val)); raiseExcHelper(TypeError, ""); } if (!isSubclass(r->cls, int_cls) && !isSubclass(r->cls, long_cls)) { raiseExcHelper(TypeError, "__int__ returned non-int (type %s)", r->cls->tp_name); } return r; }
static uint64_t asUnsignedLong(BoxedLong* self) { assert(self->cls == long_cls); if (mpz_sgn(self->n) == -1) raiseExcHelper(OverflowError, "can't convert negative value to unsigned long"); if (!mpz_fits_ulong_p(self->n)) raiseExcHelper(OverflowError, "long int too large to convert"); return mpz_get_ui(self->n); }
// takes the three arguments of a `raise' and produces the ExcInfo to throw ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) { assert(type && value && tb); // use None for default behavior, not nullptr // TODO switch this to PyErr_Normalize if (tb == None) { tb = NULL; } else if (tb != NULL && !PyTraceBack_Check(tb)) { raiseExcHelper(TypeError, "raise: arg 3 must be a traceback or None"); } /* Next, repeatedly, replace a tuple exception with its first item */ while (PyTuple_Check(type) && PyTuple_Size(type) > 0) { PyObject* tmp = type; type = PyTuple_GET_ITEM(type, 0); Py_INCREF(type); Py_DECREF(tmp); } if (PyExceptionClass_Check(type)) { PyErr_NormalizeException(&type, &value, &tb); if (!PyExceptionInstance_Check(value)) { raiseExcHelper(TypeError, "calling %s() should have returned an instance of " "BaseException, not '%s'", ((PyTypeObject*)type)->tp_name, Py_TYPE(value)->tp_name); } } else if (PyExceptionInstance_Check(type)) { /* Raising an instance. The value should be a dummy. */ if (value != Py_None) { raiseExcHelper(TypeError, "instance exception may not have a separate value"); } else { /* Normalize to raise <class>, <instance> */ Py_DECREF(value); value = type; type = PyExceptionInstance_Class(type); Py_INCREF(type); } } else { /* Not something you can raise. You get an exception anyway, just not what you specified :-) */ raiseExcHelper(TypeError, "exceptions must be old-style classes or " "derived from BaseException, not %s", type->cls->tp_name); } assert(PyExceptionClass_Check(type)); if (tb == NULL) { tb = None; } return ExcInfo(type, value, tb); }
// called from both generatorHasNext and generatorSend/generatorNext (but only if generatorHasNext hasn't been called) static void generatorSendInternal(BoxedGenerator* self, Box* v) { STAT_TIMER(t0, "us_timer_generator_switching", 0); if (self->running) raiseExcHelper(ValueError, "generator already executing"); // check if the generator already exited if (self->entryExited) { freeGeneratorStack(self); raiseExcHelper(StopIteration, (const char*)nullptr); } self->returnValue = v; self->running = true; #if STAT_TIMERS if (!self->prev_stack) self->prev_stack = StatTimer::createStack(self->my_timer); else self->prev_stack = StatTimer::swapStack(self->prev_stack); #endif swapContext(&self->returnContext, self->context, (intptr_t)self); #if STAT_TIMERS self->prev_stack = StatTimer::swapStack(self->prev_stack); if (self->entryExited) { assert(self->prev_stack == &self->my_timer); assert(self->my_timer.isPaused()); } #endif self->running = false; // propagate exception to the caller if (self->exception.type) { assert(self->entryExited); freeGeneratorStack(self); // don't raise StopIteration exceptions because those are handled specially. if (!self->exception.matches(StopIteration)) throw self->exception; return; } if (self->entryExited) { freeGeneratorStack(self); // Reset the current exception. // We could directly create the StopIteration exception but we delay creating it because often the caller is not // interested in the exception (=generatorHasnext). If we really need it we will create it inside generatorSend. self->exception = ExcInfo(NULL, NULL, NULL); return; } }
extern "C" Box* tupleNew(Box* _cls, BoxedTuple* args, BoxedDict* kwargs) { if (!PyType_Check(_cls)) raiseExcHelper(TypeError, "tuple.__new__(X): X is not a type object (%s)", getTypeName(_cls)); BoxedClass* cls = static_cast<BoxedClass*>(_cls); if (!isSubclass(cls, tuple_cls)) raiseExcHelper(TypeError, "tuple.__new__(%s): %s is not a subtype of tuple", getNameOfClass(cls), getNameOfClass(cls)); int args_sz = args->size(); int kwargs_sz = kwargs ? kwargs->d.size() : 0; if (args_sz + kwargs_sz > 1) raiseExcHelper(TypeError, "tuple() takes at most 1 argument (%d given)", args_sz + kwargs_sz); if (args_sz || kwargs_sz) { Box* elements; // if initializing from iterable argument, check common case positional args first if (args_sz) { elements = args->elts[0]; } else { assert(kwargs_sz); auto const seq = *(kwargs->d.begin()); auto const kw = static_cast<BoxedString*>(seq.first.value); if (kw->s() == "sequence") elements = seq.second; else raiseExcHelper(TypeError, "'%s' is an invalid keyword argument for this function", kw->data()); } if (cls == tuple_cls) { // Call PySequence_Tuple since it has some perf special-cases // that can make it quite a bit faster than the generic pyElements iteration: Box* r = PySequence_Tuple(elements); if (!r) throwCAPIException(); return r; } std::vector<Box*, StlCompatAllocator<Box*>> elts; for (auto e : elements->pyElements()) elts.push_back(e); return BoxedTuple::create(elts.size(), &elts[0], cls); } else { if (cls == tuple_cls) return EmptyTuple; return BoxedTuple::create(0, cls); } }
BoxedLong* _longNew(Box* val, Box* _base) { BoxedLong* rtn = new BoxedLong(); if (_base) { if (!isSubclass(_base->cls, int_cls)) raiseExcHelper(TypeError, "an integer is required"); int base = static_cast<BoxedInt*>(_base)->n; if (!isSubclass(val->cls, str_cls)) raiseExcHelper(TypeError, "long() can't convert non-string with explicit base"); BoxedString* s = static_cast<BoxedString*>(val); rtn = (BoxedLong*)PyLong_FromString(s->data(), NULL, base); checkAndThrowCAPIException(); } else { if (isSubclass(val->cls, long_cls)) { BoxedLong* l = static_cast<BoxedLong*>(val); if (val->cls == long_cls) return l; BoxedLong* rtn = new BoxedLong(); mpz_init_set(rtn->n, l->n); return rtn; } else if (isSubclass(val->cls, int_cls)) { mpz_init_set_si(rtn->n, static_cast<BoxedInt*>(val)->n); } else if (val->cls == str_cls) { llvm::StringRef s = static_cast<BoxedString*>(val)->s(); assert(s.data()[s.size()] == '\0'); int r = mpz_init_set_str(rtn->n, s.data(), 10); RELEASE_ASSERT(r == 0, ""); } else if (val->cls == float_cls) { mpz_init_set_si(rtn->n, static_cast<BoxedFloat*>(val)->d); } else { static BoxedString* long_str = static_cast<BoxedString*>(PyString_InternFromString("__long__")); Box* r = callattr(val, long_str, CallattrFlags({.cls_only = true, .null_on_nonexistent = true }), ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL); if (!r) { fprintf(stderr, "TypeError: long() argument must be a string or a number, not '%s'\n", getTypeName(val)); raiseExcHelper(TypeError, ""); } if (isSubclass(r->cls, int_cls)) { mpz_init_set_si(rtn->n, static_cast<BoxedInt*>(r)->n); } else if (!isSubclass(r->cls, long_cls)) { raiseExcHelper(TypeError, "__long__ returned non-long (type %s)", r->cls->tp_name); } else { return static_cast<BoxedLong*>(r); } } }
Box* sysGetFrame(Box* val) { int depth = 0; if (val) { if (!PyInt_Check(val)) { raiseExcHelper(TypeError, "TypeError: an integer is required"); } depth = static_cast<BoxedInt*>(val)->n; } Box* frame = getFrame(depth); if (!frame) { raiseExcHelper(ValueError, "call stack is not deep enough"); } return incref(frame); }
Box* sysGetFrame(Box* val) { int depth = 0; if (val) { if (!isSubclass(val->cls, int_cls)) { raiseExcHelper(TypeError, "TypeError: an integer is required"); } depth = static_cast<BoxedInt*>(val)->n; } Box* frame = getFrame(depth); if (!frame) { raiseExcHelper(ValueError, "call stack is not deep enough"); } return frame; }
static Box* importSub(const std::string* name, Box* parent_module) { BoxedList* path_list; if (parent_module == NULL) { path_list = getSysPath(); if (path_list == NULL || path_list->cls != list_cls) { raiseExcHelper(RuntimeError, "sys.path must be a list of directory names"); } } else { path_list = static_cast<BoxedList*>(parent_module->getattr("__path__", NULL)); if (path_list == NULL || path_list->cls != list_cls) { raiseExcHelper(ImportError, "No module named %s", name->c_str()); } } llvm::SmallString<128> joined_path; for (int i = 0; i < path_list->size; i++) { Box* _p = path_list->elts->elts[i]; if (_p->cls != str_cls) continue; BoxedString* p = static_cast<BoxedString*>(_p); joined_path.clear(); llvm::sys::path::append(joined_path, p->s, *name + ".py"); std::string fn(joined_path.str()); if (VERBOSITY() >= 2) printf("Searching for %s at %s...\n", name->c_str(), fn.c_str()); bool exists; llvm::error_code code = llvm::sys::fs::exists(joined_path.str(), exists); assert(LLVM_SYS_FS_EXISTS_CODE_OKAY(code)); if (!exists) continue; if (VERBOSITY() >= 1) printf("Importing %s from %s\n", name->c_str(), fn.c_str()); BoxedModule* module = createAndRunModule(*name, fn); return module; } if (*name == "test") { return importTestExtension(); } raiseExcHelper(ImportError, "No module named %s", name->c_str()); }
extern "C" Box* intTrunc(BoxedInt* self) { if (!isSubclass(self->cls, int_cls)) raiseExcHelper(TypeError, "descriptor '__trunc__' requires a 'int' object but received a '%s'", getTypeName(self)); return self; }
extern "C" Box* intNonzero(BoxedInt* v) { if (!isSubclass(v->cls, int_cls)) raiseExcHelper(TypeError, "descriptor '__nonzero__' requires a 'int' object but received a '%s'", getTypeName(v)); return boxBool(v->n != 0); }
extern "C" Box* intInvert(BoxedInt* v) { if (!isSubclass(v->cls, int_cls)) raiseExcHelper(TypeError, "descriptor '__invert__' requires a 'int' object but received a '%s'", getTypeName(v)); return boxInt(~v->n); }
Box* dictClear(BoxedDict* self) { if (!isSubclass(self->cls, dict_cls)) raiseExcHelper(TypeError, "descriptor 'clear' requires a 'dict' object but received a '%s'", getTypeName(self)); self->d.clear(); return None; }
extern "C" Box* listInsert(BoxedList* self, Box* idx, Box* v) { if (idx->cls != int_cls) { raiseExcHelper(TypeError, "an integer is required"); } LOCK_REGION(self->lock.asWrite()); int64_t n = static_cast<BoxedInt*>(idx)->n; if (n < 0) n = self->size + n; if (n >= self->size) { listAppendInternal(self, v); } else { if (n < 0) n = 0; assert(0 <= n && n < self->size); self->ensure(1); memmove(self->elts->elts + n + 1, self->elts->elts + n, (self->size - n) * sizeof(Box*)); self->size++; self->elts->elts[n] = v; } return None; }
Box* timeSleep(Box* arg) { double secs; if (arg->cls == int_cls) secs = static_cast<BoxedInt*>(arg)->n; else if (arg->cls == float_cls) secs = static_cast<BoxedFloat*>(arg)->d; else { raiseExcHelper(TypeError, "a float is required"); } double fullsecs; double nanosecs = modf(secs, &fullsecs); struct timespec req; req.tv_sec = (int)(fullsecs + 0.01); req.tv_nsec = (int)(nanosecs * 1000000000); { threading::GLAllowThreadsReadRegion _allow_threads; int code = nanosleep(&req, NULL); if (code) err(1, NULL); } return None; }
Box* setGe(BoxedSet* self, BoxedSet* rhs) { RELEASE_ASSERT(PyAnySet_Check(self), ""); if (!PyAnySet_Check(rhs)) raiseExcHelper(TypeError, "can only compare to a set"); return setIssuperset(self, rhs); }
template <ExceptionStyle S> Box* generatorClose(Box* s) noexcept(S == CAPI) { assert(s->cls == generator_cls); BoxedGenerator* self = static_cast<BoxedGenerator*>(s); // check if the generator already exited if (self->entryExited) return incref(Py_None); if (S == CAPI) { Box* rtn = generatorThrow<S>(self, GeneratorExit, nullptr, nullptr); if (rtn) { PyErr_SetString(RuntimeError, "generator ignored GeneratorExit"); return NULL; } if (PyErr_ExceptionMatches(PyExc_StopIteration) || PyErr_ExceptionMatches(PyExc_GeneratorExit)) { PyErr_Clear(); return incref(Py_None); } return NULL; } else { try { autoDecref(generatorThrow<S>(self, GeneratorExit, nullptr, nullptr)); raiseExcHelper(RuntimeError, "generator ignored GeneratorExit"); } catch (ExcInfo e) { if (e.matches(StopIteration) || e.matches(GeneratorExit)) { e.clear(); return incref(Py_None); } throw e; } } assert(0); // unreachable }
Box* generatorThrow(Box* s, BoxedClass* exc_cls, Box* exc_val = nullptr, Box** args = nullptr) noexcept(S == CAPI) { assert(s->cls == generator_cls); BoxedGenerator* self = static_cast<BoxedGenerator*>(s); if (self->iterated_from__hasnext__ && !self->entryExited) Py_FatalError(".throw called on generator last advanced with __hasnext__"); Box* exc_tb = args ? args[0] : nullptr; if (exc_tb && exc_tb != Py_None && !PyTraceBack_Check(exc_tb)) { if (S == CAPI) { PyErr_SetString(TypeError, "throw() third argument must be a traceback object"); return NULL; } raiseExcHelper(TypeError, "throw() third argument must be a traceback object"); } if (!exc_val) exc_val = Py_None; if (!exc_tb) exc_tb = Py_None; ExcInfo exc_info = excInfoForRaise(incref(exc_cls), incref(exc_val), incref(exc_tb)); if (self->entryExited) { if (S == CAPI) { setCAPIException(exc_info); return NULL; } throw exc_info; } self->exception = exc_info; return generatorSend<S>(self, Py_None); }
static Box* createAndRunModule(BoxedString* name, const std::string& fn, const std::string& module_path) { BoxedModule* module = createModule(name, fn.c_str()); Box* b_path = boxString(module_path); BoxedList* path_list = new BoxedList(); listAppendInternal(path_list, b_path); static BoxedString* path_str = internStringImmortal("__path__"); module->setattr(path_str, path_list, NULL); AST_Module* ast = caching_parse_file(fn.c_str(), /* future_flags = */ 0); assert(ast); try { compileAndRunModule(ast, module); } catch (ExcInfo e) { removeModule(name); throw e; } Box* r = getSysModulesDict()->getOrNull(name); if (!r) raiseExcHelper(ImportError, "Loaded module %.200s not found in sys.modules", name->c_str()); return r; }
Box* generatorSend(Box* s, Box* v) { assert(s->cls == generator_cls); BoxedGenerator* self = static_cast<BoxedGenerator*>(s); if (self->iterated_from__hasnext__) Py_FatalError(".throw called on generator last advanced with __hasnext__"); generatorSendInternal(self, v); // throw StopIteration if the generator exited if (self->entryExited) { // But we can't just create a new exc because the generator may have exited because of an explicit // 'raise StopIterationSubClass, "test"' statement and we can't replace it with the generic StopIteration // exception. // That's why we set inside 'generatorSendInternal()' 'self->exception' to the raised StopIteration exception or // create a new one if the generator exited implicit. // CPython raises the custom exception just once, on the next generator 'next' it will we a normal StopIteration // exc. assert(self->exception.type == NULL || self->exception.matches(StopIteration)); ExcInfo old_exc = self->exception; // Clear the exception for GC purposes: self->exception = ExcInfo(nullptr, nullptr, nullptr); if (old_exc.type == NULL) raiseExcHelper(StopIteration, (const char*)nullptr); throw old_exc; } return self->returnValue; }
Box* dictLen(BoxedDict* self) { if (!isSubclass(self->cls, dict_cls)) raiseExcHelper(TypeError, "descriptor '__len__' requires a 'dict' object but received a '%s'", getTypeName(self)); return boxInt(self->d.size()); }
Box* mathSqrt(Box* b) { double d = _extractFloat(b); if (d < 0) { raiseExcHelper(ValueError, "math domain error"); } return boxFloat(sqrt(d)); }
Box* tupleMul(BoxedTuple* self, Box* rhs) { Py_ssize_t n; if (PyIndex_Check(rhs)) { n = PyNumber_AsSsize_t(rhs, PyExc_OverflowError); if (n == -1 && PyErr_Occurred()) throwCAPIException(); } else { raiseExcHelper(TypeError, "can't multiply sequence by non-int of type '%s'", getTypeName(rhs)); } int s = self->size(); if (n < 0) n = 0; if ((s == 0 || n == 1) && PyTuple_CheckExact(self)) { return self; } else { BoxedTuple* rtn = BoxedTuple::create(n * s); int rtn_i = 0; for (int i = 0; i < n; ++i) { memmove(&rtn->elts[rtn_i], &self->elts[0], sizeof(Box*) * s); rtn_i += s; } return rtn; } }