Overhaul exceptions with tracebacks; 'except Type...'
This commit is contained in:
parent
b527561b53
commit
9bbb0a1d6e
@ -49,6 +49,7 @@ typedef enum {
|
|||||||
OP_SUBTRACT,
|
OP_SUBTRACT,
|
||||||
OP_SWAP,
|
OP_SWAP,
|
||||||
OP_TRUE,
|
OP_TRUE,
|
||||||
|
OP_FILTER_EXCEPT,
|
||||||
|
|
||||||
OP_CALL = 64,
|
OP_CALL = 64,
|
||||||
OP_CLASS,
|
OP_CLASS,
|
||||||
|
@ -191,7 +191,7 @@ static void and_(int canAssign);
|
|||||||
static KrkToken classDeclaration();
|
static KrkToken classDeclaration();
|
||||||
static void declareVariable();
|
static void declareVariable();
|
||||||
static void namedVariable(KrkToken name, int canAssign);
|
static void namedVariable(KrkToken name, int canAssign);
|
||||||
static void addLocal(KrkToken name);
|
static Local * addLocal(KrkToken name);
|
||||||
static void string(int canAssign);
|
static void string(int canAssign);
|
||||||
static KrkToken decorator(size_t level, FunctionType type);
|
static KrkToken decorator(size_t level, FunctionType type);
|
||||||
static void call(int canAssign);
|
static void call(int canAssign);
|
||||||
@ -1536,7 +1536,8 @@ static void tryStatement() {
|
|||||||
/* Make sure we are in a local scope so this ends up on the stack */
|
/* Make sure we are in a local scope so this ends up on the stack */
|
||||||
beginScope();
|
beginScope();
|
||||||
int tryJump = emitJump(OP_PUSH_TRY);
|
int tryJump = emitJump(OP_PUSH_TRY);
|
||||||
addLocal(syntheticToken("exception"));
|
/* We'll rename this later, but it needs to be on the stack now as it represents the exception handler */
|
||||||
|
Local * exceptionObject = addLocal(syntheticToken(""));
|
||||||
defineVariable(0);
|
defineVariable(0);
|
||||||
|
|
||||||
beginScope();
|
beginScope();
|
||||||
@ -1553,6 +1554,16 @@ static void tryStatement() {
|
|||||||
advance();
|
advance();
|
||||||
}
|
}
|
||||||
if (match(TOKEN_EXCEPT)) {
|
if (match(TOKEN_EXCEPT)) {
|
||||||
|
if (!check(TOKEN_COLON) && !check(TOKEN_AS)) {
|
||||||
|
expression();
|
||||||
|
emitByte(OP_FILTER_EXCEPT);
|
||||||
|
}
|
||||||
|
if (match(TOKEN_AS)) {
|
||||||
|
consume(TOKEN_IDENTIFIER, "Expected identifier after 'as'");
|
||||||
|
exceptionObject->name = parser.previous;
|
||||||
|
} else {
|
||||||
|
exceptionObject->name = syntheticToken("exception");
|
||||||
|
}
|
||||||
consume(TOKEN_COLON, "Expect ':' after except.");
|
consume(TOKEN_COLON, "Expect ':' after except.");
|
||||||
beginScope();
|
beginScope();
|
||||||
block(blockWidth,"except");
|
block(blockWidth,"except");
|
||||||
@ -2428,7 +2439,7 @@ static ssize_t resolveLocal(Compiler * compiler, KrkToken * name) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void addLocal(KrkToken name) {
|
static Local * addLocal(KrkToken name) {
|
||||||
if (current->localCount + 1 > current->localsSpace) {
|
if (current->localCount + 1 > current->localsSpace) {
|
||||||
size_t old = current->localsSpace;
|
size_t old = current->localsSpace;
|
||||||
current->localsSpace = GROW_CAPACITY(old);
|
current->localsSpace = GROW_CAPACITY(old);
|
||||||
@ -2449,6 +2460,7 @@ static void addLocal(KrkToken name) {
|
|||||||
current->function->localNames[current->function->localNameCount].deathday = 0;
|
current->function->localNames[current->function->localNameCount].deathday = 0;
|
||||||
current->function->localNames[current->function->localNameCount].name = krk_copyString(name.start, name.length);
|
current->function->localNames[current->function->localNameCount].name = krk_copyString(name.start, name.length);
|
||||||
current->function->localNameCount++;
|
current->function->localNameCount++;
|
||||||
|
return local;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void declareVariable() {
|
static void declareVariable() {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "value.h"
|
#include "value.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
/* function.__doc__ */
|
/* function.__doc__ */
|
||||||
static KrkValue _closure_get_doc(int argc, KrkValue argv[], int hasKw) {
|
static KrkValue _closure_get_doc(int argc, KrkValue argv[], int hasKw) {
|
||||||
@ -24,13 +25,15 @@ static KrkValue _bound_get_doc(int argc, KrkValue argv[], int hasKw) {
|
|||||||
/* Check for and return the name of a native function as a string object */
|
/* Check for and return the name of a native function as a string object */
|
||||||
static KrkValue nativeFunctionName(KrkValue func) {
|
static KrkValue nativeFunctionName(KrkValue func) {
|
||||||
const char * string = ((KrkNative*)AS_OBJECT(func))->name;
|
const char * string = ((KrkNative*)AS_OBJECT(func))->name;
|
||||||
|
if (!string) return OBJECT_VAL(S("<unnamed>"));
|
||||||
size_t len = strlen(string);
|
size_t len = strlen(string);
|
||||||
return OBJECT_VAL(krk_copyString(string,len));
|
return OBJECT_VAL(krk_copyString(string,len));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* function.__name__ */
|
/* function.__name__ */
|
||||||
static KrkValue _closure_get_name(int argc, KrkValue argv[], int hasKw) {
|
static KrkValue _closure_get_name(int argc, KrkValue argv[], int hasKw) {
|
||||||
if (!IS_CLOSURE(argv[0])) return nativeFunctionName(argv[0]);
|
if (IS_NATIVE(argv[0])) return nativeFunctionName(argv[0]);
|
||||||
|
else if (!IS_CLOSURE(argv[0])) return krk_runtimeError(vm.exceptions->typeError, "'%s' is neither a closure nor a native", krk_typeName(argv[0]));
|
||||||
return AS_CLOSURE(argv[0])->function->name ? OBJECT_VAL(AS_CLOSURE(argv[0])->function->name) : OBJECT_VAL(S(""));
|
return AS_CLOSURE(argv[0])->function->name ? OBJECT_VAL(AS_CLOSURE(argv[0])->function->name) : OBJECT_VAL(S(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,9 +43,22 @@ static KrkValue _bound_get_name(int argc, KrkValue argv[], int hasKw) {
|
|||||||
return _closure_get_name(1, (KrkValue[]){OBJECT_VAL(boundMethod->method)}, hasKw);
|
return _closure_get_name(1, (KrkValue[]){OBJECT_VAL(boundMethod->method)}, hasKw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static KrkValue _closure_ip_to_line(int argc, KrkValue argv[], int hasKw) {
|
||||||
|
if (argc < 2) return krk_runtimeError(vm.exceptions->argumentError, "%s() expects exactly 2 arguments", "ip_to_line");
|
||||||
|
if (!IS_CLOSURE(argv[0])) return NONE_VAL();
|
||||||
|
if (!IS_INTEGER(argv[1])) return TYPE_ERROR(int,argv[1]);
|
||||||
|
return INTEGER_VAL(krk_lineNumber(&AS_CLOSURE(argv[0])->function->chunk, AS_INTEGER(argv[1])));
|
||||||
|
}
|
||||||
|
|
||||||
|
static KrkValue _bound_ip_to_line(int argc, KrkValue argv[], int hasKw) {
|
||||||
|
KrkBoundMethod * boundMethod = AS_BOUND_METHOD(argv[0]);
|
||||||
|
return _closure_ip_to_line(1, (KrkValue[]){OBJECT_VAL(boundMethod->method)}, hasKw);
|
||||||
|
}
|
||||||
|
|
||||||
/* function.__str__ / function.__repr__ */
|
/* function.__str__ / function.__repr__ */
|
||||||
static KrkValue _closure_str(int argc, KrkValue argv[], int hasKw) {
|
static KrkValue _closure_str(int argc, KrkValue argv[], int hasKw) {
|
||||||
KrkValue s = _closure_get_name(argc, argv, hasKw);
|
KrkValue s = _closure_get_name(argc, argv, hasKw);
|
||||||
|
if (!IS_STRING(s)) return NONE_VAL();
|
||||||
krk_push(s);
|
krk_push(s);
|
||||||
|
|
||||||
size_t len = AS_STRING(s)->length + sizeof("<function >");
|
size_t len = AS_STRING(s)->length + sizeof("<function >");
|
||||||
@ -114,6 +130,7 @@ void _createAndBind_functionClass(void) {
|
|||||||
krk_defineNative(&vm.baseClasses->functionClass->methods, ":__name__", _closure_get_name);
|
krk_defineNative(&vm.baseClasses->functionClass->methods, ":__name__", _closure_get_name);
|
||||||
krk_defineNative(&vm.baseClasses->functionClass->methods, ":__file__", _closure_get_file);
|
krk_defineNative(&vm.baseClasses->functionClass->methods, ":__file__", _closure_get_file);
|
||||||
krk_defineNative(&vm.baseClasses->functionClass->methods, ":__args__", _closure_get_argnames);
|
krk_defineNative(&vm.baseClasses->functionClass->methods, ":__args__", _closure_get_argnames);
|
||||||
|
krk_defineNative(&vm.baseClasses->functionClass->methods, "_ip_to_line", _closure_ip_to_line);
|
||||||
krk_finalizeClass(vm.baseClasses->functionClass);
|
krk_finalizeClass(vm.baseClasses->functionClass);
|
||||||
|
|
||||||
ADD_BASE_CLASS(vm.baseClasses->methodClass, "method", vm.baseClasses->objectClass);
|
ADD_BASE_CLASS(vm.baseClasses->methodClass, "method", vm.baseClasses->objectClass);
|
||||||
@ -123,5 +140,6 @@ void _createAndBind_functionClass(void) {
|
|||||||
krk_defineNative(&vm.baseClasses->methodClass->methods, ":__name__", _bound_get_name);
|
krk_defineNative(&vm.baseClasses->methodClass->methods, ":__name__", _bound_get_name);
|
||||||
krk_defineNative(&vm.baseClasses->methodClass->methods, ":__file__", _bound_get_file);
|
krk_defineNative(&vm.baseClasses->methodClass->methods, ":__file__", _bound_get_file);
|
||||||
krk_defineNative(&vm.baseClasses->methodClass->methods, ":__args__", _bound_get_argnames);
|
krk_defineNative(&vm.baseClasses->methodClass->methods, ":__args__", _bound_get_argnames);
|
||||||
|
krk_defineNative(&vm.baseClasses->methodClass->methods, "_ip_to_line", _bound_ip_to_line);
|
||||||
krk_finalizeClass(vm.baseClasses->methodClass);
|
krk_finalizeClass(vm.baseClasses->methodClass);
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ SIMPLE(OP_IS)
|
|||||||
SIMPLE(OP_POW)
|
SIMPLE(OP_POW)
|
||||||
SIMPLE(OP_CREATE_PROPERTY)
|
SIMPLE(OP_CREATE_PROPERTY)
|
||||||
SIMPLE(OP_CLEANUP_WITH)
|
SIMPLE(OP_CLEANUP_WITH)
|
||||||
|
SIMPLE(OP_FILTER_EXCEPT)
|
||||||
CONSTANT(OP_DEFINE_GLOBAL,(void)0)
|
CONSTANT(OP_DEFINE_GLOBAL,(void)0)
|
||||||
CONSTANT(OP_CONSTANT,(void)0)
|
CONSTANT(OP_CONSTANT,(void)0)
|
||||||
CONSTANT(OP_GET_GLOBAL,(void)0)
|
CONSTANT(OP_GET_GLOBAL,(void)0)
|
||||||
|
148
src/vm.c
148
src/vm.c
@ -167,47 +167,66 @@ static void dumpStack(CallFrame * frame) {
|
|||||||
* the source file and print the corresponding line.
|
* the source file and print the corresponding line.
|
||||||
*/
|
*/
|
||||||
void krk_dumpTraceback() {
|
void krk_dumpTraceback() {
|
||||||
if (krk_currentThread.frameCount) {
|
if (!krk_valuesEqual(krk_currentThread.currentException,NONE_VAL())) {
|
||||||
fprintf(stderr, "Traceback (most recent call last):\n");
|
krk_push(krk_currentThread.currentException);
|
||||||
for (size_t i = 0; i <= krk_currentThread.frameCount - 1; i++) {
|
KrkValue tracebackEntries;
|
||||||
CallFrame * frame = &krk_currentThread.frames[i];
|
if (IS_INSTANCE(krk_currentThread.currentException)
|
||||||
KrkFunction * function = frame->closure->function;
|
&& krk_tableGet(&AS_INSTANCE(krk_currentThread.currentException)->fields, OBJECT_VAL(S("traceback")), &tracebackEntries)
|
||||||
size_t instruction = frame->ip - function->chunk.code - 1;
|
&& IS_list(tracebackEntries) && AS_LIST(tracebackEntries)->count > 0) {
|
||||||
int lineNo = (int)krk_lineNumber(&function->chunk, instruction);
|
/* This exception has a traceback we can print. */
|
||||||
fprintf(stderr, " File \"%s\", line %d, in %s\n",
|
fprintf(stderr, "Traceback (most recent call last):\n");
|
||||||
(function->chunk.filename ? function->chunk.filename->chars : "?"),
|
for (size_t i = 0; i < AS_LIST(tracebackEntries)->count; ++i) {
|
||||||
lineNo,
|
|
||||||
(function->name ? function->name->chars : "(unnamed)"));
|
/* Quietly skip invalid entries as we don't want to bother printing explanatory text for them */
|
||||||
if (function->chunk.filename) {
|
if (!IS_TUPLE(AS_LIST(tracebackEntries)->values[i])) continue;
|
||||||
FILE * f = fopen(function->chunk.filename->chars, "r");
|
KrkTuple * entry = AS_TUPLE(AS_LIST(tracebackEntries)->values[i]);
|
||||||
if (f) {
|
if (entry->values.count != 2) continue;
|
||||||
int line = 1;
|
if (!IS_CLOSURE(entry->values.values[0])) continue;
|
||||||
do {
|
if (!IS_INTEGER(entry->values.values[1])) continue;
|
||||||
char c = fgetc(f);
|
|
||||||
if (c < -1) break;
|
/* Get the function and instruction index from this traceback entry */
|
||||||
if (c == '\n') {
|
KrkClosure * closure = AS_CLOSURE(entry->values.values[0]);
|
||||||
line++;
|
KrkFunction * function = closure->function;
|
||||||
continue;
|
size_t instruction = AS_INTEGER(entry->values.values[1]);
|
||||||
}
|
|
||||||
if (line == lineNo) {
|
/* Calculate the line number */
|
||||||
fprintf(stderr," ");
|
int lineNo = (int)krk_lineNumber(&function->chunk, instruction);
|
||||||
while (c == ' ') c = fgetc(f);
|
|
||||||
do {
|
/* Print the simple stuff that we already know */
|
||||||
fputc(c, stderr);
|
fprintf(stderr, " File \"%s\", line %d, in %s\n",
|
||||||
c = fgetc(f);
|
(function->chunk.filename ? function->chunk.filename->chars : "?"),
|
||||||
} while (!feof(f) && c > 0 && c != '\n');
|
lineNo,
|
||||||
fprintf(stderr, "\n");
|
(function->name ? function->name->chars : "(unnamed)"));
|
||||||
break;
|
|
||||||
}
|
/* Try to open the file */
|
||||||
} while (!feof(f));
|
if (function->chunk.filename) {
|
||||||
fclose(f);
|
FILE * f = fopen(function->chunk.filename->chars, "r");
|
||||||
|
if (f) {
|
||||||
|
int line = 1;
|
||||||
|
do {
|
||||||
|
char c = fgetc(f);
|
||||||
|
if (c < -1) break;
|
||||||
|
if (c == '\n') {
|
||||||
|
line++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line == lineNo) {
|
||||||
|
fprintf(stderr," ");
|
||||||
|
while (c == ' ') c = fgetc(f);
|
||||||
|
do {
|
||||||
|
fputc(c, stderr);
|
||||||
|
c = fgetc(f);
|
||||||
|
} while (!feof(f) && c > 0 && c != '\n');
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (!feof(f));
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!krk_valuesEqual(krk_currentThread.currentException,NONE_VAL())) {
|
|
||||||
krk_push(krk_currentThread.currentException);
|
|
||||||
/* Is this a SyntaxError? Handle those specially. */
|
/* Is this a SyntaxError? Handle those specially. */
|
||||||
if (krk_isInstanceOf(krk_currentThread.currentException, vm.exceptions->syntaxError)) {
|
if (krk_isInstanceOf(krk_currentThread.currentException, vm.exceptions->syntaxError)) {
|
||||||
KrkValue result = krk_callSimple(OBJECT_VAL(krk_getType(krk_currentThread.currentException)->_tostr), 1, 0);
|
KrkValue result = krk_callSimple(OBJECT_VAL(krk_getType(krk_currentThread.currentException)->_tostr), 1, 0);
|
||||||
@ -228,6 +247,37 @@ void krk_dumpTraceback() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach a traceback to the current exception object, if it doesn't already have one.
|
||||||
|
*/
|
||||||
|
static void attachTraceback(void) {
|
||||||
|
if (IS_INSTANCE(krk_currentThread.currentException)) {
|
||||||
|
KrkInstance * theException = AS_INSTANCE(krk_currentThread.currentException);
|
||||||
|
|
||||||
|
/* If there already is a traceback, don't add a new one; this exception was re-raised. */
|
||||||
|
KrkValue existing;
|
||||||
|
if (krk_tableGet(&theException->fields, OBJECT_VAL(S("traceback")), &existing)) return;
|
||||||
|
|
||||||
|
KrkValue tracebackList = krk_list_of(0,NULL,0);
|
||||||
|
krk_push(tracebackList);
|
||||||
|
/* Build the traceback object */
|
||||||
|
if (krk_currentThread.frameCount) {
|
||||||
|
for (size_t i = 0; i < krk_currentThread.frameCount; i++) {
|
||||||
|
CallFrame * frame = &krk_currentThread.frames[i];
|
||||||
|
KrkTuple * tbEntry = krk_newTuple(2);
|
||||||
|
krk_push(OBJECT_VAL(tbEntry));
|
||||||
|
tbEntry->values.values[tbEntry->values.count++] = OBJECT_VAL(frame->closure);
|
||||||
|
tbEntry->values.values[tbEntry->values.count++] = INTEGER_VAL(frame->ip - frame->closure->function->chunk.code - 1);
|
||||||
|
krk_tupleUpdateHash(tbEntry);
|
||||||
|
krk_writeValueArray(AS_LIST(tracebackList), OBJECT_VAL(tbEntry));
|
||||||
|
krk_pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
krk_attachNamedValue(&theException->fields, "traceback", tracebackList);
|
||||||
|
krk_pop();
|
||||||
|
} /* else: probably a legacy 'raise str', just don't bother. */
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raise an exception. Creates an exception object of the requested type
|
* Raise an exception. Creates an exception object of the requested type
|
||||||
* and formats a message string to attach to it. Exception classes are
|
* and formats a message string to attach to it. Exception classes are
|
||||||
@ -254,6 +304,7 @@ KrkValue krk_runtimeError(KrkClass * type, const char * fmt, ...) {
|
|||||||
|
|
||||||
/* Set the current exception to be picked up by handleException */
|
/* Set the current exception to be picked up by handleException */
|
||||||
krk_currentThread.currentException = OBJECT_VAL(exceptionObject);
|
krk_currentThread.currentException = OBJECT_VAL(exceptionObject);
|
||||||
|
attachTraceback();
|
||||||
return NONE_VAL();
|
return NONE_VAL();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1831,6 +1882,7 @@ static KrkValue run() {
|
|||||||
case OP_POP: krk_pop(); break;
|
case OP_POP: krk_pop(); break;
|
||||||
case OP_RAISE: {
|
case OP_RAISE: {
|
||||||
krk_currentThread.currentException = krk_pop();
|
krk_currentThread.currentException = krk_pop();
|
||||||
|
attachTraceback();
|
||||||
krk_currentThread.flags |= KRK_HAS_EXCEPTION;
|
krk_currentThread.flags |= KRK_HAS_EXCEPTION;
|
||||||
goto _finishException;
|
goto _finishException;
|
||||||
}
|
}
|
||||||
@ -1945,6 +1997,28 @@ static KrkValue run() {
|
|||||||
krk_push(OBJECT_VAL(newProperty));
|
krk_push(OBJECT_VAL(newProperty));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OP_FILTER_EXCEPT: {
|
||||||
|
int isMatch = 0;
|
||||||
|
if (IS_CLASS(krk_peek(0)) && krk_isInstanceOf(krk_peek(1), AS_CLASS(krk_peek(0)))) {
|
||||||
|
isMatch = 1;
|
||||||
|
} if (IS_TUPLE(krk_peek(0))) {
|
||||||
|
for (size_t i = 0; i < AS_TUPLE(krk_peek(0))->values.count; ++i) {
|
||||||
|
if (IS_CLASS(AS_TUPLE(krk_peek(0))->values.values[i]) && krk_isInstanceOf(krk_peek(1), AS_CLASS(AS_TUPLE(krk_peek(0))->values.values[i]))) {
|
||||||
|
isMatch = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isMatch) {
|
||||||
|
/* Restore and re-raise the exception if it didn't match. */
|
||||||
|
krk_currentThread.currentException = krk_peek(1);
|
||||||
|
krk_currentThread.flags |= KRK_HAS_EXCEPTION;
|
||||||
|
goto _finishException;
|
||||||
|
}
|
||||||
|
/* Else pop the filter value */
|
||||||
|
krk_pop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Two-byte operands
|
* Two-byte operands
|
||||||
|
17
test/testExceptionTracebacks.krk
Normal file
17
test/testExceptionTracebacks.krk
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
def doTheThing(excp):
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
raise excp
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
print("Caught a", repr(exception))
|
||||||
|
for i in exception.traceback:
|
||||||
|
let func, instr = i
|
||||||
|
print(f" File '{func.__file__}', line {func._ip_to_line(instr)}, in {func.__name__}")
|
||||||
|
except NameError:
|
||||||
|
print("That's a name error!")
|
||||||
|
|
||||||
|
|
||||||
|
doTheThing(TypeError("A type error"))
|
||||||
|
doTheThing(ValueError("A value error"))
|
||||||
|
doTheThing(NameError("A name error"))
|
||||||
|
|
7
test/testExceptionTracebacks.krk.expect
Normal file
7
test/testExceptionTracebacks.krk.expect
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Caught a TypeError('A type error')
|
||||||
|
File 'test/testExceptionTracebacks.krk', line 14, in __main__
|
||||||
|
File 'test/testExceptionTracebacks.krk', line 4, in doTheThing
|
||||||
|
Caught a ValueError('A value error')
|
||||||
|
File 'test/testExceptionTracebacks.krk', line 15, in __main__
|
||||||
|
File 'test/testExceptionTracebacks.krk', line 4, in doTheThing
|
||||||
|
That's a name error!
|
14
test/testFilteredExceptions.krk
Normal file
14
test/testFilteredExceptions.krk
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
def doTheThing(excp):
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
raise excp
|
||||||
|
except (TypeError, ValueError) as e:
|
||||||
|
print("Caught a", repr(e))
|
||||||
|
except NameError:
|
||||||
|
print("That's a name error!")
|
||||||
|
|
||||||
|
|
||||||
|
doTheThing(TypeError("A type error"))
|
||||||
|
doTheThing(ValueError("A value error"))
|
||||||
|
doTheThing(NameError("A name error"))
|
||||||
|
|
3
test/testFilteredExceptions.krk.expect
Normal file
3
test/testFilteredExceptions.krk.expect
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Caught a TypeError('A type error')
|
||||||
|
Caught a ValueError('A value error')
|
||||||
|
That's a name error!
|
Loading…
Reference in New Issue
Block a user