#include "BaseException.hpp"
#include "MemoryError.hpp"
#include "PyBool.hpp"
#include "PyCode.hpp"
#include "PyFrame.hpp"
#include "PyNone.hpp"
#include "PyString.hpp"
#include "PyTraceback.hpp"
#include "PyTuple.hpp"
#include "PyType.hpp"
#include "SourceManager.hpp"
#include "types/api.hpp"
#include "types/builtin.hpp"
#include "vm/VM.hpp"
namespace py {
template<> BaseException *as(PyObject *obj)
{
ASSERT(types::base_exception());
if (obj->type() == types::base_exception()) { return static_cast(obj); }
return nullptr;
}
template<> const BaseException *as(const PyObject *obj)
{
ASSERT(types::base_exception());
if (obj->type() == types::base_exception()) { return static_cast(obj); }
return nullptr;
}
BaseException::BaseException(PyType *type) : PyBaseObject(type->underlying_type()) {}
BaseException::BaseException(PyType *type, PyTuple *args) : PyBaseObject(type), m_args(args) {}
BaseException::BaseException(PyTuple *args)
: PyBaseObject(types::BuiltinTypes::the().base_exception()), m_args(args)
{}
BaseException::BaseException(const TypePrototype &type, PyTuple *args)
: PyBaseObject(type), m_args(args)
{}
PyResult BaseException::create(PyTuple *args)
{
auto &heap = VirtualMachine::the().heap();
auto *result = heap.allocate(args);
if (!result) { return Err(memory_error(sizeof(BaseException))); }
return Ok(result);
}
PyResult BaseException::__new__(const PyType *type, PyTuple *args, PyDict *kwargs)
{
ASSERT(type == types::base_exception());
ASSERT(!kwargs || kwargs->map().empty());
if (auto result = BaseException::create(args); result.is_ok()) {
return Ok(static_cast(result.unwrap()));
} else {
return Err(result.unwrap_err());
}
}
PyResult BaseException::__init__(PyTuple *args, PyDict *kwargs)
{
// takes no keyword arguments
ASSERT(!kwargs || kwargs->map().empty());
m_args = args;
return Ok(0);
}
PyType *BaseException::static_type() const
{
ASSERT(types::base_exception());
return types::base_exception();
}
PyType *BaseException::class_type()
{
ASSERT(types::base_exception());
return types::base_exception();
}
std::string BaseException::what() const { return BaseException::to_string(); }
void BaseException::visit_graph(Visitor &visitor)
{
PyObject::visit_graph(visitor);
if (m_args) visitor.visit(*m_args);
if (m_dict) visitor.visit(*m_dict);
if (m_traceback) visitor.visit(*m_traceback);
if (m_context) visitor.visit(*m_context);
if (m_cause) visitor.visit(*m_cause);
}
std::string BaseException::to_string() const
{
if (m_args) {
if (m_args->size() == 1) {
auto obj_ = PyObject::from(m_args->elements()[0]);
ASSERT(obj_.is_ok());
return obj_.unwrap()->to_string();
} else {
return m_args->to_string();
}
} else {
return "";
}
}
std::string BaseException::format_traceback() const
{
std::ostringstream out;
out << "Traceback (most recent call last):\n";
auto *tb = m_traceback;
while (tb) {
const auto &filename = tb->m_tb_frame->code()->m_filename;
out << fmt::format(" File \"{}\", line {}, in {}\n",
filename,
tb->m_tb_lineno,
tb->m_tb_frame->code()->name());
const auto source = SourceManager::the().line(filename, tb->m_tb_lineno);
const auto trimmed = SourceManager::strip_leading_whitespace(source);
if (!trimmed.empty()) { out << " " << trimmed << "\n"; }
tb = tb->m_tb_next;
}
out << type()->name() << ": " << what() << "\n";
return out.str();
}
PyResult BaseException::__repr__() const
{
std::string args_part;
if (m_args && m_args->size() == 1) {
auto r = repr_value(m_args->elements()[0]);
if (r.is_err()) { return r; }
args_part = fmt::format("({})", r.unwrap()->to_string());
} else if (m_args) {
auto r = m_args->repr();
if (r.is_err()) { return r; }
args_part = r.unwrap()->to_string();
} else {
args_part = "()";
}
return PyString::create(fmt::format("{}{}", type()->name(), args_part));
}
PyResult BaseException::__str__() const { return PyString::create(to_string()); }
namespace {
std::once_flag base_exception_flag;
std::unique_ptr register_base_exception()
{
return std::move(klass("BaseException")
.property_readonly("args",
[](BaseException *self) -> PyResult {
// args is always a tuple (empty when constructed without args).
if (auto args = self->args()) { return Ok(args); }
return PyTuple::create();
})
.property_readonly("__traceback__",
[](BaseException *self) -> PyResult {
return Ok(self->traceback() ? self->traceback() : py_none());
})
.property(
"__cause__",
[](BaseException *self) -> PyResult {
return Ok(self->cause() ? self->cause() : py_none());
},
[](BaseException *self, PyObject *value) -> PyResult<:monostate> {
// Per the data model, setting __cause__ also suppresses the
// implicit context display (raise ... from ...).
self->set_cause(value == py_none() ? nullptr : value);
self->set_suppress_context(true);
return Ok(std::monostate{});
})
.property(
"__context__",
[](BaseException *self) -> PyResult {
return Ok(self->context() ? self->context() : py_none());
},
[](BaseException *self, PyObject *value) -> PyResult<:monostate> {
self->set_context(value == py_none() ? nullptr : value);
return Ok(std::monostate{});
})
.property(
"__suppress_context__",
[](BaseException *self) -> PyResult {
return Ok(self->suppress_context() ? py_true() : py_false());
},
[](BaseException *self, PyObject *value) -> PyResult<:monostate> {
auto truthy = value->true_();
if (truthy.is_err()) { return Err(truthy.unwrap_err()); }
self->set_suppress_context(truthy.unwrap());
return Ok(std::monostate{});
})
.type);
}
}// namespace
std::function<:unique_ptr>()> BaseException::type_factory()
{
return []() {
static std::unique_ptr type = nullptr;
std::call_once(base_exception_flag, []() { type = register_base_exception(); });
return std::move(type);
};
}
}// namespace py