Hacky implementation of @staticmethod, @property, mb even @classmethod
This commit is contained in:
parent
824cbc59eb
commit
50e4902170
35
README.md
35
README.md
@ -1291,6 +1291,41 @@ print(doesANestedThing())
|
|||||||
|
|
||||||
_**Note:** The implementation of `with` blocks is incomplete; exceptions raised from within a `with` that are not caught within the block will cause `__exit__` to not be called._
|
_**Note:** The implementation of `with` blocks is incomplete; exceptions raised from within a `with` that are not caught within the block will cause `__exit__` to not be called._
|
||||||
|
|
||||||
|
### Special Decorators
|
||||||
|
|
||||||
|
The compiler implements special handling when the decorators `@staticmethod` and `@property` are used with methods of a class.
|
||||||
|
|
||||||
|
`@staticmethod` will mark the decorated method as a regular function - it will not receive an implicit "self" and it will be attached to the `fields` table of the class and instances of that class, rather than the `methods` table.
|
||||||
|
|
||||||
|
`@property` will mark the decorated method as a property object. When it is retrieved or assigned to from the class or instance through a dot-accessor (eg. `foo.bar`), the wrapped method will be called intead.
|
||||||
|
|
||||||
|
Properties work differently from in Python:
|
||||||
|
|
||||||
|
```py
|
||||||
|
class Foo():
|
||||||
|
def __init__(self):
|
||||||
|
self._bar = 0
|
||||||
|
@property
|
||||||
|
def bar(self, *setter):
|
||||||
|
if setter:
|
||||||
|
print("Setting bar:", setter[0])
|
||||||
|
self._bar = setter[0]
|
||||||
|
else:
|
||||||
|
print("Getting bar.")
|
||||||
|
return self._bar
|
||||||
|
let f = Foo()
|
||||||
|
print(f.bar)
|
||||||
|
f.bar = 42
|
||||||
|
print(f.bar)
|
||||||
|
# → Getting bar.
|
||||||
|
# 0
|
||||||
|
# Setting bar: 42
|
||||||
|
# Getting bar.
|
||||||
|
# 42
|
||||||
|
```
|
||||||
|
|
||||||
|
_**Note:** Special handling when using `del` on a property is not yet implemented._
|
||||||
|
|
||||||
## About the REPL
|
## About the REPL
|
||||||
|
|
||||||
Kuroko's repl provides an interactive environment for executing code and seeing results.
|
Kuroko's repl provides an interactive environment for executing code and seeing results.
|
||||||
|
@ -83,6 +83,8 @@ typedef enum {
|
|||||||
OP_INVOKE_DELETE,
|
OP_INVOKE_DELETE,
|
||||||
OP_IMPORT_FROM,
|
OP_IMPORT_FROM,
|
||||||
|
|
||||||
|
OP_CREATE_PROPERTY,
|
||||||
|
|
||||||
OP_CONSTANT_LONG = 128,
|
OP_CONSTANT_LONG = 128,
|
||||||
OP_DEFINE_GLOBAL_LONG,
|
OP_DEFINE_GLOBAL_LONG,
|
||||||
OP_GET_GLOBAL_LONG,
|
OP_GET_GLOBAL_LONG,
|
||||||
|
@ -92,6 +92,8 @@ typedef enum {
|
|||||||
TYPE_METHOD,
|
TYPE_METHOD,
|
||||||
TYPE_INIT,
|
TYPE_INIT,
|
||||||
TYPE_LAMBDA,
|
TYPE_LAMBDA,
|
||||||
|
TYPE_STATIC,
|
||||||
|
TYPE_PROPERTY,
|
||||||
} FunctionType;
|
} FunctionType;
|
||||||
|
|
||||||
typedef struct Compiler {
|
typedef struct Compiler {
|
||||||
@ -133,6 +135,10 @@ static KrkChunk * currentChunk() {
|
|||||||
#define EMIT_CONSTANT_OP(opc, arg) do { if (arg < 256) { emitBytes(opc, arg); } \
|
#define EMIT_CONSTANT_OP(opc, arg) do { if (arg < 256) { emitBytes(opc, arg); } \
|
||||||
else { emitBytes(opc ## _LONG, arg >> 16); emitBytes(arg >> 8, arg); } } while (0)
|
else { emitBytes(opc ## _LONG, arg >> 16); emitBytes(arg >> 8, arg); } } while (0)
|
||||||
|
|
||||||
|
static int isMethod(int type) {
|
||||||
|
return type == TYPE_METHOD || type == TYPE_INIT || type == TYPE_PROPERTY;
|
||||||
|
}
|
||||||
|
|
||||||
static void initCompiler(Compiler * compiler, FunctionType type) {
|
static void initCompiler(Compiler * compiler, FunctionType type) {
|
||||||
compiler->enclosing = current;
|
compiler->enclosing = current;
|
||||||
current = compiler;
|
current = compiler;
|
||||||
@ -159,7 +165,7 @@ static void initCompiler(Compiler * compiler, FunctionType type) {
|
|||||||
current->function->name = krk_copyString(parser.previous.start, parser.previous.length);
|
current->function->name = krk_copyString(parser.previous.start, parser.previous.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == TYPE_INIT || type == TYPE_METHOD) {
|
if (isMethod(type)) {
|
||||||
Local * local = ¤t->locals[current->localCount++];
|
Local * local = ¤t->locals[current->localCount++];
|
||||||
local->depth = 0;
|
local->depth = 0;
|
||||||
local->isCaptured = 0;
|
local->isCaptured = 0;
|
||||||
@ -876,7 +882,7 @@ static void function(FunctionType type, size_t blockWidth) {
|
|||||||
|
|
||||||
beginScope();
|
beginScope();
|
||||||
|
|
||||||
if (type == TYPE_METHOD || type == TYPE_INIT) current->function->requiredArgs = 1;
|
if (isMethod(type)) current->function->requiredArgs = 1;
|
||||||
|
|
||||||
int hasCollectors = 0;
|
int hasCollectors = 0;
|
||||||
|
|
||||||
@ -885,7 +891,7 @@ static void function(FunctionType type, size_t blockWidth) {
|
|||||||
if (!check(TOKEN_RIGHT_PAREN)) {
|
if (!check(TOKEN_RIGHT_PAREN)) {
|
||||||
do {
|
do {
|
||||||
if (match(TOKEN_SELF)) {
|
if (match(TOKEN_SELF)) {
|
||||||
if (type != TYPE_INIT && type != TYPE_METHOD) {
|
if (!isMethod(type)) {
|
||||||
error("Invalid use of `self` as a function paramenter.");
|
error("Invalid use of `self` as a function paramenter.");
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@ -995,6 +1001,9 @@ static void method(size_t blockWidth) {
|
|||||||
if (!match(TOKEN_EOL) && !match(TOKEN_EOF)) {
|
if (!match(TOKEN_EOL) && !match(TOKEN_EOF)) {
|
||||||
errorAtCurrent("Expected end of line after class attribut declaration");
|
errorAtCurrent("Expected end of line after class attribut declaration");
|
||||||
}
|
}
|
||||||
|
} else if (match(TOKEN_PASS)) {
|
||||||
|
/* bah */
|
||||||
|
consume(TOKEN_EOL, "Expected linefeed after 'pass' in class body.");
|
||||||
} else {
|
} else {
|
||||||
consume(TOKEN_DEF, "expected a definition, got nothing");
|
consume(TOKEN_DEF, "expected a definition, got nothing");
|
||||||
consume(TOKEN_IDENTIFIER, "expected method name");
|
consume(TOKEN_IDENTIFIER, "expected method name");
|
||||||
@ -1134,8 +1143,33 @@ static KrkToken decorator(size_t level, FunctionType type) {
|
|||||||
size_t blockWidth = (parser.previous.type == TOKEN_INDENTATION) ? parser.previous.length : 0;
|
size_t blockWidth = (parser.previous.type == TOKEN_INDENTATION) ? parser.previous.length : 0;
|
||||||
advance(); /* Collect the `@` */
|
advance(); /* Collect the `@` */
|
||||||
|
|
||||||
/* Collect an identifier */
|
KrkToken funcName = {0};
|
||||||
expression();
|
int haveCallable = 0;
|
||||||
|
|
||||||
|
/* hol'up, let's special case some stuff */
|
||||||
|
KrkToken at_staticmethod = syntheticToken("staticmethod");
|
||||||
|
KrkToken at_property = syntheticToken("property");
|
||||||
|
if (identifiersEqual(&at_staticmethod, &parser.current)) {
|
||||||
|
if (level != 0 || type != TYPE_METHOD) {
|
||||||
|
error("Invalid use of @staticmethod, which must be the top decorator of a class method.");
|
||||||
|
return funcName;
|
||||||
|
}
|
||||||
|
advance();
|
||||||
|
type = TYPE_STATIC;
|
||||||
|
emitBytes(OP_DUP, 0); /* SET_PROPERTY will pop class */
|
||||||
|
} else if (identifiersEqual(&at_property, &parser.current)) {
|
||||||
|
if (level != 0 || type != TYPE_METHOD) {
|
||||||
|
error("Invalid use of @property, which must be the top decorator of a class method.");
|
||||||
|
return funcName;
|
||||||
|
}
|
||||||
|
advance();
|
||||||
|
type = TYPE_PROPERTY;
|
||||||
|
emitBytes(OP_DUP, 0);
|
||||||
|
} else {
|
||||||
|
/* Collect an identifier */
|
||||||
|
expression();
|
||||||
|
haveCallable = 1;
|
||||||
|
}
|
||||||
|
|
||||||
consume(TOKEN_EOL, "Expected line feed after decorator.");
|
consume(TOKEN_EOL, "Expected line feed after decorator.");
|
||||||
if (blockWidth) {
|
if (blockWidth) {
|
||||||
@ -1143,7 +1177,6 @@ static KrkToken decorator(size_t level, FunctionType type) {
|
|||||||
if (parser.previous.length != blockWidth) error("Expected next line after decorator to have same indentation.");
|
if (parser.previous.length != blockWidth) error("Expected next line after decorator to have same indentation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
KrkToken funcName = {0};
|
|
||||||
if (check(TOKEN_DEF)) {
|
if (check(TOKEN_DEF)) {
|
||||||
/* We already checked for block level */
|
/* We already checked for block level */
|
||||||
advance();
|
advance();
|
||||||
@ -1160,7 +1193,8 @@ static KrkToken decorator(size_t level, FunctionType type) {
|
|||||||
return funcName;
|
return funcName;
|
||||||
}
|
}
|
||||||
|
|
||||||
emitBytes(OP_CALL, 1);
|
if (haveCallable)
|
||||||
|
emitBytes(OP_CALL, 1);
|
||||||
|
|
||||||
if (level == 0) {
|
if (level == 0) {
|
||||||
if (type == TYPE_FUNCTION) {
|
if (type == TYPE_FUNCTION) {
|
||||||
@ -1168,6 +1202,15 @@ static KrkToken decorator(size_t level, FunctionType type) {
|
|||||||
declareVariable();
|
declareVariable();
|
||||||
size_t ind = (current->scopeDepth > 0) ? 0 : identifierConstant(&funcName);
|
size_t ind = (current->scopeDepth > 0) ? 0 : identifierConstant(&funcName);
|
||||||
defineVariable(ind);
|
defineVariable(ind);
|
||||||
|
} else if (type == TYPE_STATIC) {
|
||||||
|
size_t ind = identifierConstant(&funcName);
|
||||||
|
EMIT_CONSTANT_OP(OP_SET_PROPERTY, ind);
|
||||||
|
emitByte(OP_POP);
|
||||||
|
} else if (type == TYPE_PROPERTY) {
|
||||||
|
emitByte(OP_CREATE_PROPERTY);
|
||||||
|
size_t ind = identifierConstant(&funcName);
|
||||||
|
EMIT_CONSTANT_OP(OP_SET_PROPERTY, ind);
|
||||||
|
emitByte(OP_POP);
|
||||||
} else {
|
} else {
|
||||||
size_t ind = identifierConstant(&funcName);
|
size_t ind = identifierConstant(&funcName);
|
||||||
EMIT_CONSTANT_OP(OP_METHOD, ind);
|
EMIT_CONSTANT_OP(OP_METHOD, ind);
|
||||||
|
@ -122,6 +122,7 @@ size_t krk_disassembleInstruction(FILE * f, KrkFunction * func, size_t offset) {
|
|||||||
SIMPLE(OP_FINALIZE)
|
SIMPLE(OP_FINALIZE)
|
||||||
SIMPLE(OP_IS)
|
SIMPLE(OP_IS)
|
||||||
SIMPLE(OP_POW)
|
SIMPLE(OP_POW)
|
||||||
|
SIMPLE(OP_CREATE_PROPERTY)
|
||||||
OPERANDB(OP_DUP,(void)0)
|
OPERANDB(OP_DUP,(void)0)
|
||||||
OPERANDB(OP_EXPAND_ARGS,EXPAND_ARGS_MORE)
|
OPERANDB(OP_EXPAND_ARGS,EXPAND_ARGS_MORE)
|
||||||
CONSTANT(OP_DEFINE_GLOBAL,(void)0)
|
CONSTANT(OP_DEFINE_GLOBAL,(void)0)
|
||||||
|
@ -87,6 +87,10 @@ static void freeObject(KrkObj * object) {
|
|||||||
FREE(KrkBytes, bytes);
|
FREE(KrkBytes, bytes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OBJ_PROPERTY: {
|
||||||
|
FREE(KrkProperty, object);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,6 +182,11 @@ static void blackenObject(KrkObj * object) {
|
|||||||
markArray(&tuple->values);
|
markArray(&tuple->values);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OBJ_PROPERTY: {
|
||||||
|
KrkProperty * property = (KrkProperty *)object;
|
||||||
|
krk_markValue(property->method);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case OBJ_NATIVE:
|
case OBJ_NATIVE:
|
||||||
case OBJ_STRING:
|
case OBJ_STRING:
|
||||||
case OBJ_BYTES:
|
case OBJ_BYTES:
|
||||||
|
@ -248,6 +248,12 @@ KrkUpvalue * krk_newUpvalue(int slot) {
|
|||||||
return upvalue;
|
return upvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KrkProperty * krk_newProperty(KrkValue method) {
|
||||||
|
KrkProperty * property = ALLOCATE_OBJECT(KrkProperty, OBJ_PROPERTY);
|
||||||
|
property->method = method;
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
|
||||||
KrkClass * krk_newClass(KrkString * name, KrkClass * baseClass) {
|
KrkClass * krk_newClass(KrkString * name, KrkClass * baseClass) {
|
||||||
KrkClass * _class = ALLOCATE_OBJECT(KrkClass, OBJ_CLASS);
|
KrkClass * _class = ALLOCATE_OBJECT(KrkClass, OBJ_CLASS);
|
||||||
_class->name = name;
|
_class->name = name;
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
#define IS_TUPLE(value) isObjType(value, OBJ_TUPLE)
|
#define IS_TUPLE(value) isObjType(value, OBJ_TUPLE)
|
||||||
#define AS_TUPLE(value) ((KrkTuple *)AS_OBJECT(value))
|
#define AS_TUPLE(value) ((KrkTuple *)AS_OBJECT(value))
|
||||||
|
#define IS_PROPERTY(value) isObjType(value, OBJ_PROPERTY)
|
||||||
|
#define AS_PROPERTY(value) ((KrkProperty *)AS_OBJECT(value))
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
OBJ_FUNCTION,
|
OBJ_FUNCTION,
|
||||||
@ -40,6 +42,7 @@ typedef enum {
|
|||||||
OBJ_BOUND_METHOD,
|
OBJ_BOUND_METHOD,
|
||||||
OBJ_TUPLE,
|
OBJ_TUPLE,
|
||||||
OBJ_BYTES,
|
OBJ_BYTES,
|
||||||
|
OBJ_PROPERTY,
|
||||||
} ObjType;
|
} ObjType;
|
||||||
|
|
||||||
struct Obj {
|
struct Obj {
|
||||||
@ -173,6 +176,11 @@ typedef struct {
|
|||||||
KrkValueArray values;
|
KrkValueArray values;
|
||||||
} KrkTuple;
|
} KrkTuple;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
KrkObj obj;
|
||||||
|
KrkValue method;
|
||||||
|
} KrkProperty;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
KrkInstance inst;
|
KrkInstance inst;
|
||||||
KrkValueArray values;
|
KrkValueArray values;
|
||||||
@ -200,6 +208,7 @@ extern KrkClass * krk_newClass(KrkString * name, KrkClass * base);
|
|||||||
extern KrkInstance * krk_newInstance(KrkClass * _class);
|
extern KrkInstance * krk_newInstance(KrkClass * _class);
|
||||||
extern KrkBoundMethod * krk_newBoundMethod(KrkValue receiver, KrkObj * method);
|
extern KrkBoundMethod * krk_newBoundMethod(KrkValue receiver, KrkObj * method);
|
||||||
extern KrkTuple * krk_newTuple(size_t length);
|
extern KrkTuple * krk_newTuple(size_t length);
|
||||||
|
extern KrkProperty * krk_newProperty(KrkValue method);
|
||||||
|
|
||||||
extern void * krk_unicodeString(KrkString * string);
|
extern void * krk_unicodeString(KrkString * string);
|
||||||
extern uint32_t krk_unicodeCodepoint(KrkString * string, size_t index);
|
extern uint32_t krk_unicodeCodepoint(KrkString * string, size_t index);
|
||||||
|
32
src/vm.c
32
src/vm.c
@ -1411,6 +1411,7 @@ KrkValue krk_callSimple(KrkValue value, int argCount, int isMethod) {
|
|||||||
} else if (result == 1) {
|
} else if (result == 1) {
|
||||||
return krk_runNext();
|
return krk_runNext();
|
||||||
}
|
}
|
||||||
|
if (!IS_NONE(vm.currentException)) return NONE_VAL();
|
||||||
return krk_runtimeError(vm.exceptions.typeError, "Invalid internal method call: %d ('%s')", result, krk_typeName(value));
|
return krk_runtimeError(vm.exceptions.typeError, "Invalid internal method call: %d ('%s')", result, krk_typeName(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4133,6 +4134,10 @@ static int valueGetProperty(KrkString * name) {
|
|||||||
if (IS_INSTANCE(krk_peek(0))) {
|
if (IS_INSTANCE(krk_peek(0))) {
|
||||||
KrkInstance * instance = AS_INSTANCE(krk_peek(0));
|
KrkInstance * instance = AS_INSTANCE(krk_peek(0));
|
||||||
if (krk_tableGet(&instance->fields, OBJECT_VAL(name), &value)) {
|
if (krk_tableGet(&instance->fields, OBJECT_VAL(name), &value)) {
|
||||||
|
if (IS_PROPERTY(value)) {
|
||||||
|
krk_push(krk_callSimple(AS_PROPERTY(value)->method, 1, 0));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
krk_pop();
|
krk_pop();
|
||||||
krk_push(value);
|
krk_push(value);
|
||||||
return 1;
|
return 1;
|
||||||
@ -4142,6 +4147,10 @@ static int valueGetProperty(KrkString * name) {
|
|||||||
KrkClass * _class = AS_CLASS(krk_peek(0));
|
KrkClass * _class = AS_CLASS(krk_peek(0));
|
||||||
if (krk_tableGet(&_class->fields, OBJECT_VAL(name), &value) ||
|
if (krk_tableGet(&_class->fields, OBJECT_VAL(name), &value) ||
|
||||||
krk_tableGet(&_class->methods, OBJECT_VAL(name), &value)) {
|
krk_tableGet(&_class->methods, OBJECT_VAL(name), &value)) {
|
||||||
|
if (IS_PROPERTY(value)) {
|
||||||
|
krk_push(krk_callSimple(AS_PROPERTY(value)->method, 1, 0));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
krk_pop();
|
krk_pop();
|
||||||
krk_push(value);
|
krk_push(value);
|
||||||
return 1;
|
return 1;
|
||||||
@ -4584,12 +4593,17 @@ static KrkValue run() {
|
|||||||
case OP_SET_PROPERTY_LONG:
|
case OP_SET_PROPERTY_LONG:
|
||||||
case OP_SET_PROPERTY: {
|
case OP_SET_PROPERTY: {
|
||||||
KrkString * name = READ_STRING(operandWidth);
|
KrkString * name = READ_STRING(operandWidth);
|
||||||
if (IS_INSTANCE(krk_peek(1))) {
|
KrkTable * table = NULL;
|
||||||
KrkInstance * instance = AS_INSTANCE(krk_peek(1));
|
if (IS_INSTANCE(krk_peek(1))) table = &AS_INSTANCE(krk_peek(1))->fields;
|
||||||
krk_tableSet(&instance->fields, OBJECT_VAL(name), krk_peek(0));
|
else if (IS_CLASS(krk_peek(1))) table = &AS_CLASS(krk_peek(1))->fields;
|
||||||
} else if (IS_CLASS(krk_peek(1))) {
|
if (table) {
|
||||||
KrkClass * _class = AS_CLASS(krk_peek(1));
|
KrkValue previous;
|
||||||
krk_tableSet(&_class->fields, OBJECT_VAL(name), krk_peek(0));
|
if (krk_tableGet(table, OBJECT_VAL(name), &previous) && IS_PROPERTY(previous)) {
|
||||||
|
krk_push(krk_callSimple(AS_PROPERTY(previous)->method, 2, 0));
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
krk_tableSet(table, OBJECT_VAL(name), krk_peek(0));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
krk_runtimeError(vm.exceptions.attributeError, "'%s' object has no attribute '%s'", krk_typeName(krk_peek(0)), name->chars);
|
krk_runtimeError(vm.exceptions.attributeError, "'%s' object has no attribute '%s'", krk_typeName(krk_peek(0)), name->chars);
|
||||||
goto _finishException;
|
goto _finishException;
|
||||||
@ -4754,6 +4768,12 @@ static KrkValue run() {
|
|||||||
krk_push(handler);
|
krk_push(handler);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OP_CREATE_PROPERTY: {
|
||||||
|
KrkProperty * newProperty = krk_newProperty(krk_peek(0));
|
||||||
|
krk_pop();
|
||||||
|
krk_push(OBJECT_VAL(newProperty));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (likely(!(vm.flags & KRK_HAS_EXCEPTION))) continue;
|
if (likely(!(vm.flags & KRK_HAS_EXCEPTION))) continue;
|
||||||
_finishException:
|
_finishException:
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
def staticmethod(func):
|
|
||||||
def _wrapper(*args,**kwargs):
|
|
||||||
return func(None,*args,**kwargs)
|
|
||||||
return _wrapper
|
|
||||||
|
|
||||||
class Foo():
|
class Foo():
|
||||||
def amethod():
|
def amethod():
|
||||||
print("If this were Python, I'd be impossible to call.")
|
print("If this were Python, I'd be impossible to call.")
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def astatic():
|
def astatic():
|
||||||
print("Since `None` is always implicit, it's been set by @staticmethod here to None:", self)
|
print("these are special now")
|
||||||
|
|
||||||
# This doesn't work because amethod is unbound and needs an instance.
|
# This doesn't work because amethod is unbound and needs an instance.
|
||||||
try:
|
try:
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
amethod() takes exactly 1 argument (0 given)
|
amethod() takes exactly 1 argument (0 given)
|
||||||
If this were Python, I'd be impossible to call.
|
If this were Python, I'd be impossible to call.
|
||||||
Since `None` is always implicit, it's been set by @staticmethod here to None: None
|
these are special now
|
||||||
|
39
test/testSpecialDecorators.krk
Normal file
39
test/testSpecialDecorators.krk
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
def classmethod(func):
|
||||||
|
def bindClassMethod(callee, *args):
|
||||||
|
if args: raise ValueError("can not reassign class method")
|
||||||
|
let cls = callee if isinstance(callee, type) else callee.__class__
|
||||||
|
def _bound(*args, **kwargs):
|
||||||
|
return func(None, cls, *args, **kwargs)
|
||||||
|
return _bound
|
||||||
|
return bindClassMethod
|
||||||
|
|
||||||
|
class Foo(object):
|
||||||
|
myBar = 42
|
||||||
|
@staticmethod
|
||||||
|
def foo():
|
||||||
|
print("No args!")
|
||||||
|
@property
|
||||||
|
def bar(*setter):
|
||||||
|
if setter:
|
||||||
|
print("Called as a setter:", setter)
|
||||||
|
self.myBar = setter[0]
|
||||||
|
return self.myBar
|
||||||
|
@property
|
||||||
|
@classmethod
|
||||||
|
def fromString(cls, string):
|
||||||
|
print(cls, string)
|
||||||
|
|
||||||
|
class Bar(Foo):
|
||||||
|
|
||||||
|
Foo.foo()
|
||||||
|
print(Foo().bar)
|
||||||
|
let f = Foo()
|
||||||
|
f.myBar = 48
|
||||||
|
print(f.bar)
|
||||||
|
f.bar = 102
|
||||||
|
print(f.bar)
|
||||||
|
|
||||||
|
Foo.fromString("test")
|
||||||
|
f.fromString("test")
|
||||||
|
Bar.fromString("test")
|
8
test/testSpecialDecorators.krk.expect
Normal file
8
test/testSpecialDecorators.krk.expect
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
No args!
|
||||||
|
42
|
||||||
|
48
|
||||||
|
Called as a setter: [102]
|
||||||
|
102
|
||||||
|
<type 'Foo'> test
|
||||||
|
<type 'Foo'> test
|
||||||
|
<type 'Bar'> test
|
Loading…
Reference in New Issue
Block a user