PEP-0318, @decorator-style. In Guido's words:
"@ seems the syntax that everybody can hate equally" Implementation by Mark Russell, from SF #979728.
This commit is contained in:
parent
fd7dc5169c
commit
c2a5a63654
|
@ -73,6 +73,9 @@
|
|||
\lineiii{Continue}{}{}
|
||||
\hline
|
||||
|
||||
\lineiii{Decorators}{\member{nodes}}{List of function decorator expressions}
|
||||
\hline
|
||||
|
||||
\lineiii{Dict}{\member{items}}{}
|
||||
\hline
|
||||
|
||||
|
@ -101,7 +104,8 @@
|
|||
\lineiii{}{\member{names}}{}
|
||||
\hline
|
||||
|
||||
\lineiii{Function}{\member{name}}{name used in def, a string}
|
||||
\lineiii{Function}{\member{decorators}}{\class{Decorators} or \code{None}}
|
||||
\lineiii{}{\member{name}}{name used in def, a string}
|
||||
\lineiii{}{\member{argnames}}{list of argument names, as strings}
|
||||
\lineiii{}{\member{defaults}}{list of default values}
|
||||
\lineiii{}{\member{flags}}{xxx}
|
||||
|
|
|
@ -109,10 +109,14 @@ def my_import(name):
|
|||
|
||||
\begin{verbatim}
|
||||
class C:
|
||||
@classmethod
|
||||
def f(cls, arg1, arg2, ...): ...
|
||||
f = classmethod(f)
|
||||
\end{verbatim}
|
||||
|
||||
The \code{@classmethod} form is a function decorator -- see the description
|
||||
of function definitions in chapter 7 of the
|
||||
\citetitle[../ref/ref.html]{Python Reference Manual} for details.
|
||||
|
||||
It can be called either on the class (such as \code{C.f()}) or on an
|
||||
instance (such as \code{C().f()}). The instance is ignored except for
|
||||
its class.
|
||||
|
@ -122,6 +126,7 @@ class C:
|
|||
Class methods are different than \Cpp{} or Java static methods.
|
||||
If you want those, see \function{staticmethod()} in this section.
|
||||
\versionadded{2.2}
|
||||
Function decorator syntax added in version 2.4.
|
||||
\end{funcdesc}
|
||||
|
||||
\begin{funcdesc}{cmp}{x, y}
|
||||
|
@ -936,10 +941,14 @@ except NameError:
|
|||
|
||||
\begin{verbatim}
|
||||
class C:
|
||||
@staticmethod
|
||||
def f(arg1, arg2, ...): ...
|
||||
f = staticmethod(f)
|
||||
\end{verbatim}
|
||||
|
||||
The \code{@staticmethod} form is a function decorator -- see the description
|
||||
of function definitions in chapter 7 of the
|
||||
\citetitle[../ref/ref.html]{Python Reference Manual} for details.
|
||||
|
||||
It can be called either on the class (such as \code{C.f()}) or on an
|
||||
instance (such as \code{C().f()}). The instance is ignored except
|
||||
for its class.
|
||||
|
|
|
@ -315,8 +315,12 @@ section~\ref{types}):
|
|||
|
||||
\begin{productionlist}
|
||||
\production{funcdef}
|
||||
{"def" \token{funcname} "(" [\token{parameter_list}] ")"
|
||||
{[\token{decorators}] "def" \token{funcname} "(" [\token{parameter_list}] ")"
|
||||
":" \token{suite}}
|
||||
\production{decorators}
|
||||
{\token{decorator} ([NEWLINE] \token{decorator})* NEWLINE}
|
||||
\production{decorator}
|
||||
{"@" \token{dotted_name} ["(" [\token{argument_list} [","]] ")"]}
|
||||
\production{parameter_list}
|
||||
{(\token{defparameter} ",")*}
|
||||
\productioncont{("*" \token{identifier} [, "**" \token{identifier}]}
|
||||
|
@ -343,6 +347,27 @@ as the global namespace to be used when the function is called.
|
|||
The function definition does not execute the function body; this gets
|
||||
executed only when the function is called.
|
||||
|
||||
A function definition may be wrapped by one or more decorator expressions.
|
||||
Decorator expressions are evaluated when the function is defined, in the scope
|
||||
that contains the function definition. The result must be a callable,
|
||||
which is invoked with the function object as the only argument.
|
||||
The returned value is bound to the function name instead of the function
|
||||
object. If there are multiple decorators, they are applied in reverse
|
||||
order. For example, the following code:
|
||||
|
||||
\begin{verbatim}
|
||||
@f1
|
||||
@f2
|
||||
def func(): pass
|
||||
\end{verbatim}
|
||||
|
||||
is equivalent to:
|
||||
|
||||
\begin{verbatim}
|
||||
def func(): pass
|
||||
func = f2(f1(func))
|
||||
\end{verbatim}
|
||||
|
||||
When one or more top-level parameters have the form \var{parameter}
|
||||
\code{=} \var{expression}, the function is said to have ``default
|
||||
parameter values.'' For a parameter with a
|
||||
|
|
|
@ -28,7 +28,9 @@ single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
|
|||
file_input: (NEWLINE | stmt)* ENDMARKER
|
||||
eval_input: testlist NEWLINE* ENDMARKER
|
||||
|
||||
funcdef: 'def' NAME parameters ':' suite
|
||||
decorator: '@' dotted_name [ '(' [arglist] ')' ]
|
||||
decorators: decorator ([NEWLINE] decorator)* NEWLINE
|
||||
funcdef: [decorators] 'def' NAME parameters ':' suite
|
||||
parameters: '(' [varargslist] ')'
|
||||
varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) | fpdef ['=' test] (',' fpdef ['=' test])* [',']
|
||||
fpdef: NAME | '(' fplist ')'
|
||||
|
|
|
@ -1,72 +1,74 @@
|
|||
#define single_input 256
|
||||
#define file_input 257
|
||||
#define eval_input 258
|
||||
#define funcdef 259
|
||||
#define parameters 260
|
||||
#define varargslist 261
|
||||
#define fpdef 262
|
||||
#define fplist 263
|
||||
#define stmt 264
|
||||
#define simple_stmt 265
|
||||
#define small_stmt 266
|
||||
#define expr_stmt 267
|
||||
#define augassign 268
|
||||
#define print_stmt 269
|
||||
#define del_stmt 270
|
||||
#define pass_stmt 271
|
||||
#define flow_stmt 272
|
||||
#define break_stmt 273
|
||||
#define continue_stmt 274
|
||||
#define return_stmt 275
|
||||
#define yield_stmt 276
|
||||
#define raise_stmt 277
|
||||
#define import_stmt 278
|
||||
#define import_as_name 279
|
||||
#define dotted_as_name 280
|
||||
#define dotted_name 281
|
||||
#define global_stmt 282
|
||||
#define exec_stmt 283
|
||||
#define assert_stmt 284
|
||||
#define compound_stmt 285
|
||||
#define if_stmt 286
|
||||
#define while_stmt 287
|
||||
#define for_stmt 288
|
||||
#define try_stmt 289
|
||||
#define except_clause 290
|
||||
#define suite 291
|
||||
#define test 292
|
||||
#define and_test 293
|
||||
#define not_test 294
|
||||
#define comparison 295
|
||||
#define comp_op 296
|
||||
#define expr 297
|
||||
#define xor_expr 298
|
||||
#define and_expr 299
|
||||
#define shift_expr 300
|
||||
#define arith_expr 301
|
||||
#define term 302
|
||||
#define factor 303
|
||||
#define power 304
|
||||
#define atom 305
|
||||
#define listmaker 306
|
||||
#define testlist_gexp 307
|
||||
#define lambdef 308
|
||||
#define trailer 309
|
||||
#define subscriptlist 310
|
||||
#define subscript 311
|
||||
#define sliceop 312
|
||||
#define exprlist 313
|
||||
#define testlist 314
|
||||
#define testlist_safe 315
|
||||
#define dictmaker 316
|
||||
#define classdef 317
|
||||
#define arglist 318
|
||||
#define argument 319
|
||||
#define list_iter 320
|
||||
#define list_for 321
|
||||
#define list_if 322
|
||||
#define gen_iter 323
|
||||
#define gen_for 324
|
||||
#define gen_if 325
|
||||
#define testlist1 326
|
||||
#define encoding_decl 327
|
||||
#define decorator 259
|
||||
#define decorators 260
|
||||
#define funcdef 261
|
||||
#define parameters 262
|
||||
#define varargslist 263
|
||||
#define fpdef 264
|
||||
#define fplist 265
|
||||
#define stmt 266
|
||||
#define simple_stmt 267
|
||||
#define small_stmt 268
|
||||
#define expr_stmt 269
|
||||
#define augassign 270
|
||||
#define print_stmt 271
|
||||
#define del_stmt 272
|
||||
#define pass_stmt 273
|
||||
#define flow_stmt 274
|
||||
#define break_stmt 275
|
||||
#define continue_stmt 276
|
||||
#define return_stmt 277
|
||||
#define yield_stmt 278
|
||||
#define raise_stmt 279
|
||||
#define import_stmt 280
|
||||
#define import_as_name 281
|
||||
#define dotted_as_name 282
|
||||
#define dotted_name 283
|
||||
#define global_stmt 284
|
||||
#define exec_stmt 285
|
||||
#define assert_stmt 286
|
||||
#define compound_stmt 287
|
||||
#define if_stmt 288
|
||||
#define while_stmt 289
|
||||
#define for_stmt 290
|
||||
#define try_stmt 291
|
||||
#define except_clause 292
|
||||
#define suite 293
|
||||
#define test 294
|
||||
#define and_test 295
|
||||
#define not_test 296
|
||||
#define comparison 297
|
||||
#define comp_op 298
|
||||
#define expr 299
|
||||
#define xor_expr 300
|
||||
#define and_expr 301
|
||||
#define shift_expr 302
|
||||
#define arith_expr 303
|
||||
#define term 304
|
||||
#define factor 305
|
||||
#define power 306
|
||||
#define atom 307
|
||||
#define listmaker 308
|
||||
#define testlist_gexp 309
|
||||
#define lambdef 310
|
||||
#define trailer 311
|
||||
#define subscriptlist 312
|
||||
#define subscript 313
|
||||
#define sliceop 314
|
||||
#define exprlist 315
|
||||
#define testlist 316
|
||||
#define testlist_safe 317
|
||||
#define dictmaker 318
|
||||
#define classdef 319
|
||||
#define arglist 320
|
||||
#define argument 321
|
||||
#define list_iter 322
|
||||
#define list_for 323
|
||||
#define list_if 324
|
||||
#define gen_iter 325
|
||||
#define gen_for 326
|
||||
#define gen_if 327
|
||||
#define testlist1 328
|
||||
#define encoding_decl 329
|
||||
|
|
|
@ -22,7 +22,9 @@ PyAPI_FUNC(void) PyNode_Free(node *n);
|
|||
|
||||
/* Node access functions */
|
||||
#define NCH(n) ((n)->n_nchildren)
|
||||
|
||||
#define CHILD(n, i) (&(n)->n_child[i])
|
||||
#define RCHILD(n, i) (CHILD(n, NCH(n) + i))
|
||||
#define TYPE(n) ((n)->n_type)
|
||||
#define STR(n) ((n)->n_str)
|
||||
|
||||
|
|
|
@ -57,10 +57,11 @@ extern "C" {
|
|||
#define DOUBLESTAREQUAL 47
|
||||
#define DOUBLESLASH 48
|
||||
#define DOUBLESLASHEQUAL 49
|
||||
#define AT 50
|
||||
/* Don't forget to update the table _PyParser_TokenNames in tokenizer.c! */
|
||||
#define OP 50
|
||||
#define ERRORTOKEN 51
|
||||
#define N_TOKENS 52
|
||||
#define OP 51
|
||||
#define ERRORTOKEN 52
|
||||
#define N_TOKENS 53
|
||||
|
||||
/* Special definitions for cooperation with parser */
|
||||
|
||||
|
|
1933
Lib/compiler/ast.py
1933
Lib/compiler/ast.py
File diff suppressed because it is too large
Load Diff
|
@ -366,6 +366,13 @@ class CodeGenerator:
|
|||
self._visitFuncOrLambda(node, isLambda=1)
|
||||
|
||||
def _visitFuncOrLambda(self, node, isLambda=0):
|
||||
if not isLambda and node.decorators:
|
||||
for decorator in reversed(node.decorators.nodes):
|
||||
self.visit(decorator)
|
||||
ndecorators = len(node.decorators.nodes)
|
||||
else:
|
||||
ndecorators = 0
|
||||
|
||||
gen = self.FunctionGen(node, self.scopes, isLambda,
|
||||
self.class_name, self.get_module())
|
||||
walk(node.code, gen)
|
||||
|
@ -383,6 +390,9 @@ class CodeGenerator:
|
|||
self.emit('LOAD_CONST', gen)
|
||||
self.emit('MAKE_FUNCTION', len(node.defaults))
|
||||
|
||||
for i in range(ndecorators):
|
||||
self.emit('CALL_FUNCTION', 1)
|
||||
|
||||
def visitClass(self, node):
|
||||
gen = self.ClassGen(node, self.scopes,
|
||||
self.get_module())
|
||||
|
|
|
@ -224,6 +224,8 @@ class SymbolVisitor:
|
|||
visitExpression = visitModule
|
||||
|
||||
def visitFunction(self, node, parent):
|
||||
if node.decorators:
|
||||
self.visit(node.decorators, parent)
|
||||
parent.add_def(node.name)
|
||||
for n in node.defaults:
|
||||
self.visit(n, parent)
|
||||
|
|
|
@ -185,29 +185,81 @@ class Transformer:
|
|||
### is this sufficient?
|
||||
return Expression(self.com_node(nodelist[0]))
|
||||
|
||||
def decorator_name(self, nodelist):
|
||||
listlen = len(nodelist)
|
||||
assert listlen >= 1 and listlen % 2 == 1
|
||||
|
||||
item = self.atom_name(nodelist)
|
||||
i = 1
|
||||
while i < listlen:
|
||||
assert nodelist[i][0] == token.DOT
|
||||
assert nodelist[i + 1][0] == token.NAME
|
||||
item = Getattr(item, nodelist[i + 1][1])
|
||||
i += 2
|
||||
|
||||
return item
|
||||
|
||||
def decorator(self, nodelist):
|
||||
# '@' dotted_name [ '(' [arglist] ')' ]
|
||||
assert len(nodelist) in (2, 4, 5)
|
||||
assert nodelist[0][0] == token.AT
|
||||
|
||||
assert nodelist[1][0] == symbol.dotted_name
|
||||
funcname = self.decorator_name(nodelist[1][1:])
|
||||
|
||||
if len(nodelist) > 2:
|
||||
assert nodelist[2][0] == token.LPAR
|
||||
expr = self.com_call_function(funcname, nodelist[3])
|
||||
else:
|
||||
expr = funcname
|
||||
|
||||
return expr
|
||||
|
||||
def decorators(self, nodelist):
|
||||
# decorators: decorator ([NEWLINE] decorator)* NEWLINE
|
||||
listlen = len(nodelist)
|
||||
i = 0
|
||||
items = []
|
||||
while i < listlen:
|
||||
assert nodelist[i][0] == symbol.decorator
|
||||
items.append(self.decorator(nodelist[i][1:]))
|
||||
i += 1
|
||||
|
||||
if i < listlen and nodelist[i][0] == token.NEWLINE:
|
||||
i += 1
|
||||
return Decorators(items)
|
||||
|
||||
def funcdef(self, nodelist):
|
||||
# funcdef: 'def' NAME parameters ':' suite
|
||||
# -6 -5 -4 -3 -2 -1
|
||||
# funcdef: [decorators] 'def' NAME parameters ':' suite
|
||||
# parameters: '(' [varargslist] ')'
|
||||
|
||||
lineno = nodelist[1][2]
|
||||
name = nodelist[1][1]
|
||||
args = nodelist[2][2]
|
||||
if len(nodelist) == 6:
|
||||
assert nodelist[0][0] == symbol.decorators
|
||||
decorators = self.decorators(nodelist[0][1:])
|
||||
else:
|
||||
assert len(nodelist) == 5
|
||||
decorators = None
|
||||
|
||||
lineno = nodelist[-4][2]
|
||||
name = nodelist[-4][1]
|
||||
args = nodelist[-3][2]
|
||||
|
||||
if args[0] == symbol.varargslist:
|
||||
names, defaults, flags = self.com_arglist(args[1:])
|
||||
else:
|
||||
names = defaults = ()
|
||||
flags = 0
|
||||
doc = self.get_docstring(nodelist[4])
|
||||
doc = self.get_docstring(nodelist[-1])
|
||||
|
||||
# code for function
|
||||
code = self.com_node(nodelist[4])
|
||||
code = self.com_node(nodelist[-1])
|
||||
|
||||
if doc is not None:
|
||||
assert isinstance(code, Stmt)
|
||||
assert isinstance(code.nodes[0], Discard)
|
||||
del code.nodes[0]
|
||||
n = Function(name, names, defaults, flags, doc, code)
|
||||
n = Function(decorators, name, names, defaults, flags, doc, code)
|
||||
n.lineno = lineno
|
||||
return n
|
||||
|
||||
|
|
|
@ -222,7 +222,7 @@ def _readmodule(module, path, inpackage=None):
|
|||
else:
|
||||
super.append(token)
|
||||
inherit = names
|
||||
cur_class = Class(module, class_name, inherit, file, lineno)
|
||||
cur_class = Class(fullmodule, class_name, inherit, file, lineno)
|
||||
if not stack:
|
||||
dict[class_name] = cur_class
|
||||
stack.append((cur_class, thisindent))
|
||||
|
|
140
Lib/symbol.py
140
Lib/symbol.py
|
@ -13,75 +13,77 @@
|
|||
single_input = 256
|
||||
file_input = 257
|
||||
eval_input = 258
|
||||
funcdef = 259
|
||||
parameters = 260
|
||||
varargslist = 261
|
||||
fpdef = 262
|
||||
fplist = 263
|
||||
stmt = 264
|
||||
simple_stmt = 265
|
||||
small_stmt = 266
|
||||
expr_stmt = 267
|
||||
augassign = 268
|
||||
print_stmt = 269
|
||||
del_stmt = 270
|
||||
pass_stmt = 271
|
||||
flow_stmt = 272
|
||||
break_stmt = 273
|
||||
continue_stmt = 274
|
||||
return_stmt = 275
|
||||
yield_stmt = 276
|
||||
raise_stmt = 277
|
||||
import_stmt = 278
|
||||
import_as_name = 279
|
||||
dotted_as_name = 280
|
||||
dotted_name = 281
|
||||
global_stmt = 282
|
||||
exec_stmt = 283
|
||||
assert_stmt = 284
|
||||
compound_stmt = 285
|
||||
if_stmt = 286
|
||||
while_stmt = 287
|
||||
for_stmt = 288
|
||||
try_stmt = 289
|
||||
except_clause = 290
|
||||
suite = 291
|
||||
test = 292
|
||||
and_test = 293
|
||||
not_test = 294
|
||||
comparison = 295
|
||||
comp_op = 296
|
||||
expr = 297
|
||||
xor_expr = 298
|
||||
and_expr = 299
|
||||
shift_expr = 300
|
||||
arith_expr = 301
|
||||
term = 302
|
||||
factor = 303
|
||||
power = 304
|
||||
atom = 305
|
||||
listmaker = 306
|
||||
testlist_gexp = 307
|
||||
lambdef = 308
|
||||
trailer = 309
|
||||
subscriptlist = 310
|
||||
subscript = 311
|
||||
sliceop = 312
|
||||
exprlist = 313
|
||||
testlist = 314
|
||||
testlist_safe = 315
|
||||
dictmaker = 316
|
||||
classdef = 317
|
||||
arglist = 318
|
||||
argument = 319
|
||||
list_iter = 320
|
||||
list_for = 321
|
||||
list_if = 322
|
||||
gen_iter = 323
|
||||
gen_for = 324
|
||||
gen_if = 325
|
||||
testlist1 = 326
|
||||
encoding_decl = 327
|
||||
decorator = 259
|
||||
decorators = 260
|
||||
funcdef = 261
|
||||
parameters = 262
|
||||
varargslist = 263
|
||||
fpdef = 264
|
||||
fplist = 265
|
||||
stmt = 266
|
||||
simple_stmt = 267
|
||||
small_stmt = 268
|
||||
expr_stmt = 269
|
||||
augassign = 270
|
||||
print_stmt = 271
|
||||
del_stmt = 272
|
||||
pass_stmt = 273
|
||||
flow_stmt = 274
|
||||
break_stmt = 275
|
||||
continue_stmt = 276
|
||||
return_stmt = 277
|
||||
yield_stmt = 278
|
||||
raise_stmt = 279
|
||||
import_stmt = 280
|
||||
import_as_name = 281
|
||||
dotted_as_name = 282
|
||||
dotted_name = 283
|
||||
global_stmt = 284
|
||||
exec_stmt = 285
|
||||
assert_stmt = 286
|
||||
compound_stmt = 287
|
||||
if_stmt = 288
|
||||
while_stmt = 289
|
||||
for_stmt = 290
|
||||
try_stmt = 291
|
||||
except_clause = 292
|
||||
suite = 293
|
||||
test = 294
|
||||
and_test = 295
|
||||
not_test = 296
|
||||
comparison = 297
|
||||
comp_op = 298
|
||||
expr = 299
|
||||
xor_expr = 300
|
||||
and_expr = 301
|
||||
shift_expr = 302
|
||||
arith_expr = 303
|
||||
term = 304
|
||||
factor = 305
|
||||
power = 306
|
||||
atom = 307
|
||||
listmaker = 308
|
||||
testlist_gexp = 309
|
||||
lambdef = 310
|
||||
trailer = 311
|
||||
subscriptlist = 312
|
||||
subscript = 313
|
||||
sliceop = 314
|
||||
exprlist = 315
|
||||
testlist = 316
|
||||
testlist_safe = 317
|
||||
dictmaker = 318
|
||||
classdef = 319
|
||||
arglist = 320
|
||||
argument = 321
|
||||
list_iter = 322
|
||||
list_for = 323
|
||||
list_if = 324
|
||||
gen_iter = 325
|
||||
gen_for = 326
|
||||
gen_if = 327
|
||||
testlist1 = 328
|
||||
encoding_decl = 329
|
||||
#--end constants--
|
||||
|
||||
sym_name = {}
|
||||
|
|
|
@ -645,4 +645,15 @@ test_tokenize
|
|||
174,29-174,30: OP ')'
|
||||
174,30-174,31: NEWLINE '\n'
|
||||
175,0-175,1: NL '\n'
|
||||
176,0-176,0: ENDMARKER ''
|
||||
176,0-176,1: OP '@'
|
||||
176,1-176,13: NAME 'staticmethod'
|
||||
176,13-176,14: NEWLINE '\n'
|
||||
177,0-177,3: NAME 'def'
|
||||
177,4-177,7: NAME 'foo'
|
||||
177,7-177,8: OP '('
|
||||
177,8-177,9: OP ')'
|
||||
177,9-177,10: OP ':'
|
||||
177,11-177,15: NAME 'pass'
|
||||
177,15-177,16: NEWLINE '\n'
|
||||
178,0-178,1: NL '\n'
|
||||
179,0-179,0: ENDMARKER ''
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
"""Test cases for test_pyclbr.py"""
|
||||
|
||||
def f(): pass
|
||||
|
||||
class Other(object):
|
||||
@classmethod
|
||||
def foo(c): pass
|
||||
|
||||
def om(self): pass
|
||||
|
||||
class B (object):
|
||||
def bm(self): pass
|
||||
|
||||
class C (B):
|
||||
foo = Other().foo
|
||||
om = Other.om
|
||||
|
||||
d = 10
|
||||
|
||||
# XXX: This causes test_pyclbr.py to fail, but only because the
|
||||
# introspection-based is_method() code in the test can't
|
||||
# distinguish between this and a geniune method function like m().
|
||||
# The pyclbr.py module gets this right as it parses the text.
|
||||
#
|
||||
#f = f
|
||||
|
||||
def m(self): pass
|
||||
|
||||
@staticmethod
|
||||
def sm(self): pass
|
||||
|
||||
@classmethod
|
||||
def cm(self): pass
|
|
@ -0,0 +1,194 @@
|
|||
import unittest
|
||||
from test import test_support
|
||||
|
||||
def funcattrs(**kwds):
|
||||
def decorate(func):
|
||||
func.__dict__.update(kwds)
|
||||
return func
|
||||
return decorate
|
||||
|
||||
class MiscDecorators (object):
|
||||
@staticmethod
|
||||
def author(name):
|
||||
def decorate(func):
|
||||
func.__dict__['author'] = name
|
||||
return func
|
||||
return decorate
|
||||
|
||||
# -----------------------------------------------
|
||||
|
||||
class DbcheckError (Exception):
|
||||
def __init__(self, exprstr, func, args, kwds):
|
||||
# A real version of this would set attributes here
|
||||
Exception.__init__(self, "dbcheck %r failed (func=%s args=%s kwds=%s)" %
|
||||
(exprstr, func, args, kwds))
|
||||
|
||||
|
||||
def dbcheck(exprstr, globals=None, locals=None):
|
||||
"Decorator to implement debugging assertions"
|
||||
def decorate(func):
|
||||
expr = compile(exprstr, "dbcheck-%s" % func.func_name, "eval")
|
||||
def check(*args, **kwds):
|
||||
if not eval(expr, globals, locals):
|
||||
raise DbcheckError(exprstr, func, args, kwds)
|
||||
return func(*args, **kwds)
|
||||
return check
|
||||
return decorate
|
||||
|
||||
# -----------------------------------------------
|
||||
|
||||
def countcalls(counts):
|
||||
"Decorator to count calls to a function"
|
||||
def decorate(func):
|
||||
name = func.func_name
|
||||
counts[name] = 0
|
||||
def call(*args, **kwds):
|
||||
counts[name] += 1
|
||||
return func(*args, **kwds)
|
||||
# XXX: Would like to say: call.func_name = func.func_name here
|
||||
# to make nested decorators work in any order, but func_name
|
||||
# is a readonly attribute
|
||||
return call
|
||||
return decorate
|
||||
|
||||
# -----------------------------------------------
|
||||
|
||||
def memoize(func):
|
||||
saved = {}
|
||||
def call(*args):
|
||||
try:
|
||||
return saved[args]
|
||||
except KeyError:
|
||||
res = func(*args)
|
||||
saved[args] = res
|
||||
return res
|
||||
except TypeError:
|
||||
# Unhashable argument
|
||||
return func(*args)
|
||||
return call
|
||||
|
||||
# -----------------------------------------------
|
||||
|
||||
class TestDecorators(unittest.TestCase):
|
||||
|
||||
def test_single(self):
|
||||
class C(object):
|
||||
@staticmethod
|
||||
def foo(): return 42
|
||||
self.assertEqual(C.foo(), 42)
|
||||
self.assertEqual(C().foo(), 42)
|
||||
|
||||
def test_dotted(self):
|
||||
decorators = MiscDecorators()
|
||||
@decorators.author('Cleese')
|
||||
def foo(): return 42
|
||||
self.assertEqual(foo(), 42)
|
||||
self.assertEqual(foo.author, 'Cleese')
|
||||
|
||||
def test_argforms(self):
|
||||
# A few tests of argument passing, as we use restricted form
|
||||
# of expressions for decorators.
|
||||
|
||||
def noteargs(*args, **kwds):
|
||||
def decorate(func):
|
||||
setattr(func, 'dbval', (args, kwds))
|
||||
return func
|
||||
return decorate
|
||||
|
||||
args = ( 'Now', 'is', 'the', 'time' )
|
||||
kwds = dict(one=1, two=2)
|
||||
@noteargs(*args, **kwds)
|
||||
def f1(): return 42
|
||||
self.assertEqual(f1(), 42)
|
||||
self.assertEqual(f1.dbval, (args, kwds))
|
||||
|
||||
@noteargs('terry', 'gilliam', eric='idle', john='cleese')
|
||||
def f2(): return 84
|
||||
self.assertEqual(f2(), 84)
|
||||
self.assertEqual(f2.dbval, (('terry', 'gilliam'),
|
||||
dict(eric='idle', john='cleese')))
|
||||
|
||||
@noteargs(1, 2,)
|
||||
def f3(): pass
|
||||
self.assertEqual(f3.dbval, ((1, 2), {}))
|
||||
|
||||
def test_dbcheck(self):
|
||||
@dbcheck('args[1] is not None')
|
||||
def f(a, b):
|
||||
return a + b
|
||||
self.assertEqual(f(1, 2), 3)
|
||||
self.assertRaises(DbcheckError, f, 1, None)
|
||||
|
||||
def test_memoize(self):
|
||||
# XXX: This doesn't work unless memoize is the last decorator -
|
||||
# see the comment in countcalls.
|
||||
counts = {}
|
||||
@countcalls(counts) @memoize
|
||||
def double(x):
|
||||
return x * 2
|
||||
|
||||
self.assertEqual(counts, dict(double=0))
|
||||
|
||||
# Only the first call with a given argument bumps the call count:
|
||||
#
|
||||
self.assertEqual(double(2), 4)
|
||||
self.assertEqual(counts['double'], 1)
|
||||
self.assertEqual(double(2), 4)
|
||||
self.assertEqual(counts['double'], 1)
|
||||
self.assertEqual(double(3), 6)
|
||||
self.assertEqual(counts['double'], 2)
|
||||
|
||||
# Unhashable arguments do not get memoized:
|
||||
#
|
||||
self.assertEqual(double([10]), [10, 10])
|
||||
self.assertEqual(counts['double'], 3)
|
||||
self.assertEqual(double([10]), [10, 10])
|
||||
self.assertEqual(counts['double'], 4)
|
||||
|
||||
def test_errors(self):
|
||||
# Test syntax restrictions - these are all compile-time errors:
|
||||
#
|
||||
for expr in [ "1+2", "x[3]", "(1, 2)" ]:
|
||||
# Sanity check: is expr is a valid expression by itself?
|
||||
compile(expr, "testexpr", "exec")
|
||||
|
||||
codestr = "@%s\ndef f(): pass" % expr
|
||||
self.assertRaises(SyntaxError, compile, codestr, "test", "exec")
|
||||
|
||||
# Test runtime errors
|
||||
|
||||
def unimp(func):
|
||||
raise NotImplementedError
|
||||
context = dict(nullval=None, unimp=unimp)
|
||||
|
||||
for expr, exc in [ ("undef", NameError),
|
||||
("nullval", TypeError),
|
||||
("nullval.attr", AttributeError),
|
||||
("unimp", NotImplementedError)]:
|
||||
codestr = "@%s\ndef f(): pass\nassert f() is None" % expr
|
||||
code = compile(codestr, "test", "exec")
|
||||
self.assertRaises(exc, eval, code, context)
|
||||
|
||||
def test_double(self):
|
||||
class C(object):
|
||||
@funcattrs(abc=1, xyz="haha")
|
||||
@funcattrs(booh=42)
|
||||
def foo(self): return 42
|
||||
self.assertEqual(C().foo(), 42)
|
||||
self.assertEqual(C.foo.abc, 1)
|
||||
self.assertEqual(C.foo.xyz, "haha")
|
||||
self.assertEqual(C.foo.booh, 42)
|
||||
|
||||
def test_order(self):
|
||||
class C(object):
|
||||
@funcattrs(abc=1) @staticmethod
|
||||
def foo(): return 42
|
||||
# This wouldn't work if staticmethod was called first
|
||||
self.assertEqual(C.foo(), 42)
|
||||
self.assertEqual(C().foo(), 42)
|
||||
|
||||
def test_main():
|
||||
test_support.run_unittest(TestDecorators)
|
||||
|
||||
if __name__=="__main__":
|
||||
test_main()
|
|
@ -15,8 +15,8 @@ class RoundtripLegalSyntaxTestCase(unittest.TestCase):
|
|||
t = st1.totuple()
|
||||
try:
|
||||
st2 = parser.sequence2st(t)
|
||||
except parser.ParserError:
|
||||
self.fail("could not roundtrip %r" % s)
|
||||
except parser.ParserError, why:
|
||||
self.fail("could not roundtrip %r: %s" % (s, why))
|
||||
|
||||
self.assertEquals(t, st2.totuple(),
|
||||
"could not re-generate syntax tree")
|
||||
|
@ -119,6 +119,14 @@ class RoundtripLegalSyntaxTestCase(unittest.TestCase):
|
|||
self.check_suite("def f(a, b, foo=bar, *args, **kw): pass")
|
||||
self.check_suite("def f(a, b, foo=bar, **kw): pass")
|
||||
|
||||
self.check_suite("@staticmethod\n"
|
||||
"def f(): pass")
|
||||
self.check_suite("@staticmethod\n"
|
||||
"@funcattrs(x, y)\n"
|
||||
"def f(): pass")
|
||||
self.check_suite("@funcattrs()\n"
|
||||
"def f(): pass")
|
||||
|
||||
def test_import_from_statement(self):
|
||||
self.check_suite("from sys.path import *")
|
||||
self.check_suite("from sys.path import dirname")
|
||||
|
|
|
@ -8,6 +8,9 @@ from types import ClassType, FunctionType, MethodType
|
|||
import pyclbr
|
||||
from unittest import TestCase
|
||||
|
||||
StaticMethodType = type(staticmethod(lambda: None))
|
||||
ClassMethodType = type(classmethod(lambda c: None))
|
||||
|
||||
# This next line triggers an error on old versions of pyclbr.
|
||||
|
||||
from commands import getstatus
|
||||
|
@ -43,11 +46,10 @@ class PyclbrTest(TestCase):
|
|||
print >>sys.stderr, "***",key
|
||||
self.failUnless(obj.has_key(key))
|
||||
|
||||
def assertEquals(self, a, b, ignore=None):
|
||||
def assertEqualsOrIgnored(self, a, b, ignore):
|
||||
''' succeed iff a == b or a in ignore or b in ignore '''
|
||||
if (ignore == None) or (a in ignore) or (b in ignore): return
|
||||
|
||||
unittest.TestCase.assertEquals(self, a, b)
|
||||
if a not in ignore and b not in ignore:
|
||||
self.assertEquals(a, b)
|
||||
|
||||
def checkModule(self, moduleName, module=None, ignore=()):
|
||||
''' succeed iff pyclbr.readmodule_ex(modulename) corresponds
|
||||
|
@ -62,11 +64,22 @@ class PyclbrTest(TestCase):
|
|||
|
||||
dict = pyclbr.readmodule_ex(moduleName)
|
||||
|
||||
def ismethod(obj, name):
|
||||
if not isinstance(obj, MethodType):
|
||||
return False
|
||||
if obj.im_self is not None:
|
||||
return False
|
||||
def ismethod(oclass, obj, name):
|
||||
classdict = oclass.__dict__
|
||||
if isinstance(obj, FunctionType):
|
||||
if not isinstance(classdict[name], StaticMethodType):
|
||||
return False
|
||||
else:
|
||||
if not isinstance(obj, MethodType):
|
||||
return False
|
||||
if obj.im_self is not None:
|
||||
if (not isinstance(classdict[name], ClassMethodType) or
|
||||
obj.im_self is not oclass):
|
||||
return False
|
||||
else:
|
||||
if not isinstance(classdict[name], FunctionType):
|
||||
return False
|
||||
|
||||
objname = obj.__name__
|
||||
if objname.startswith("__") and not objname.endswith("__"):
|
||||
objname = "_%s%s" % (obj.im_class.__name__, objname)
|
||||
|
@ -81,7 +94,7 @@ class PyclbrTest(TestCase):
|
|||
if isinstance(value, pyclbr.Function):
|
||||
self.assertEquals(type(py_item), FunctionType)
|
||||
else:
|
||||
self.assertEquals(type(py_item), ClassType)
|
||||
self.failUnless(isinstance(py_item, (ClassType, type)))
|
||||
real_bases = [base.__name__ for base in py_item.__bases__]
|
||||
pyclbr_bases = [ getattr(base, 'name', base)
|
||||
for base in value.super ]
|
||||
|
@ -94,7 +107,7 @@ class PyclbrTest(TestCase):
|
|||
|
||||
actualMethods = []
|
||||
for m in py_item.__dict__.keys():
|
||||
if ismethod(getattr(py_item, m), m):
|
||||
if ismethod(py_item, getattr(py_item, m), m):
|
||||
actualMethods.append(m)
|
||||
foundMethods = []
|
||||
for m in value.methods.keys():
|
||||
|
@ -107,7 +120,8 @@ class PyclbrTest(TestCase):
|
|||
self.assertListEq(foundMethods, actualMethods, ignore)
|
||||
self.assertEquals(py_item.__module__, value.module)
|
||||
|
||||
self.assertEquals(py_item.__name__, value.name, ignore)
|
||||
self.assertEqualsOrIgnored(py_item.__name__, value.name,
|
||||
ignore)
|
||||
# can't check file or lineno
|
||||
except:
|
||||
print >>sys.stderr, "class=%s" % py_item
|
||||
|
@ -132,6 +146,12 @@ class PyclbrTest(TestCase):
|
|||
self.checkModule('rfc822')
|
||||
self.checkModule('difflib')
|
||||
|
||||
def test_decorators(self):
|
||||
# XXX: See comment in pyclbr_input.py for a test that would fail
|
||||
# if it were not commented out.
|
||||
#
|
||||
self.checkModule('test.pyclbr_input')
|
||||
|
||||
def test_others(self):
|
||||
cm = self.checkModule
|
||||
|
||||
|
|
|
@ -173,3 +173,6 @@ x = -1*1/1 + 1*1 - ---1*1
|
|||
import sys, time
|
||||
x = sys.modules['time'].time()
|
||||
|
||||
@staticmethod
|
||||
def foo(): pass
|
||||
|
||||
|
|
|
@ -60,9 +60,10 @@ RIGHTSHIFTEQUAL = 46
|
|||
DOUBLESTAREQUAL = 47
|
||||
DOUBLESLASH = 48
|
||||
DOUBLESLASHEQUAL = 49
|
||||
OP = 50
|
||||
ERRORTOKEN = 51
|
||||
N_TOKENS = 52
|
||||
AT = 50
|
||||
OP = 51
|
||||
ERRORTOKEN = 52
|
||||
N_TOKENS = 53
|
||||
NT_OFFSET = 256
|
||||
#--end constants--
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"<>", r"!=",
|
|||
r"~")
|
||||
|
||||
Bracket = '[][(){}]'
|
||||
Special = group(r'\r?\n', r'[:;.,`]')
|
||||
Special = group(r'\r?\n', r'[:;.,`@]')
|
||||
Funny = group(Operator, Bracket, Special)
|
||||
|
||||
PlainToken = group(Number, Funny, String, Name)
|
||||
|
|
|
@ -824,6 +824,7 @@ static int validate_terminal(node *terminal, int type, char *string);
|
|||
#define validate_vbar(ch) validate_terminal(ch, VBAR, "|")
|
||||
#define validate_doublestar(ch) validate_terminal(ch, DOUBLESTAR, "**")
|
||||
#define validate_dot(ch) validate_terminal(ch, DOT, ".")
|
||||
#define validate_at(ch) validate_terminal(ch, AT, "@")
|
||||
#define validate_name(ch, str) validate_terminal(ch, NAME, str)
|
||||
|
||||
#define VALIDATER(n) static int validate_##n(node *tree)
|
||||
|
@ -2362,20 +2363,72 @@ validate_testlist_gexp(node *tree)
|
|||
return ok;
|
||||
}
|
||||
|
||||
/* decorator:
|
||||
* '@' dotted_name [ '(' [arglist] ')' ]
|
||||
*/
|
||||
static int
|
||||
validate_decorator(node *tree)
|
||||
{
|
||||
int ok;
|
||||
int nch = NCH(tree);
|
||||
ok = (validate_ntype(tree, decorator) &&
|
||||
(nch == 2 || nch == 4 || nch == 5) &&
|
||||
validate_at(CHILD(tree, 0)) &&
|
||||
validate_dotted_name(CHILD(tree, 1)));
|
||||
|
||||
if (ok && nch != 2) {
|
||||
ok = (validate_lparen(CHILD(tree, 2)) &&
|
||||
validate_rparen(RCHILD(tree, -1)));
|
||||
|
||||
if (ok && nch == 5)
|
||||
ok = validate_arglist(CHILD(tree, 3));
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* decorators:
|
||||
* decorator ([NEWLINE] decorator)* NEWLINE
|
||||
*/
|
||||
static int
|
||||
validate_decorators(node *tree)
|
||||
{
|
||||
int i, nch, ok;
|
||||
nch = NCH(tree);
|
||||
ok = validate_ntype(tree, decorators) && nch >= 2;
|
||||
|
||||
i = 0;
|
||||
while (ok && i < nch - 1) {
|
||||
ok = validate_decorator(CHILD(tree, i));
|
||||
if (TYPE(CHILD(tree, i + 1)) == NEWLINE)
|
||||
++i;
|
||||
++i;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* funcdef:
|
||||
* 'def' NAME parameters ':' suite
|
||||
*
|
||||
* -6 -5 -4 -3 -2 -1
|
||||
* [decorators] 'def' NAME parameters ':' suite
|
||||
*/
|
||||
static int
|
||||
validate_funcdef(node *tree)
|
||||
{
|
||||
return (validate_ntype(tree, funcdef)
|
||||
&& validate_numnodes(tree, 5, "funcdef")
|
||||
&& validate_name(CHILD(tree, 0), "def")
|
||||
&& validate_ntype(CHILD(tree, 1), NAME)
|
||||
&& validate_colon(CHILD(tree, 3))
|
||||
&& validate_parameters(CHILD(tree, 2))
|
||||
&& validate_suite(CHILD(tree, 4)));
|
||||
int nch = NCH(tree);
|
||||
int ok = (validate_ntype(tree, funcdef)
|
||||
&& ((nch == 5) || (nch == 6))
|
||||
&& validate_name(RCHILD(tree, -5), "def")
|
||||
&& validate_ntype(RCHILD(tree, -4), NAME)
|
||||
&& validate_colon(RCHILD(tree, -2))
|
||||
&& validate_parameters(RCHILD(tree, -3))
|
||||
&& validate_suite(RCHILD(tree, -1)));
|
||||
|
||||
if (ok && (nch == 6))
|
||||
ok = validate_decorators(CHILD(tree, 0));
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ char *_PyParser_TokenNames[] = {
|
|||
"DOUBLESTAREQUAL",
|
||||
"DOUBLESLASH",
|
||||
"DOUBLESLASHEQUAL",
|
||||
"AT",
|
||||
/* This table must match the #defines in token.h! */
|
||||
"OP",
|
||||
"<ERRORTOKEN>",
|
||||
|
@ -847,6 +848,7 @@ PyToken_OneChar(int c)
|
|||
case '}': return RBRACE;
|
||||
case '^': return CIRCUMFLEX;
|
||||
case '~': return TILDE;
|
||||
case '@': return AT;
|
||||
default: return OP;
|
||||
}
|
||||
}
|
||||
|
|
125
Python/compile.c
125
Python/compile.c
|
@ -1876,6 +1876,7 @@ com_testlist_gexp(struct compiling *c, node *n)
|
|||
else com_list(c, n, 0);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
com_dictmaker(struct compiling *c, node *n)
|
||||
{
|
||||
|
@ -3963,8 +3964,9 @@ com_argdefs(struct compiling *c, node *n)
|
|||
n = CHILD(n, 1);
|
||||
}
|
||||
else {
|
||||
REQ(n, funcdef); /* funcdef: 'def' NAME parameters ... */
|
||||
n = CHILD(n, 2);
|
||||
REQ(n, funcdef);
|
||||
/* funcdef: [decorators] 'def' NAME parameters ':' suite */
|
||||
n = RCHILD(n, -3);
|
||||
REQ(n, parameters); /* parameters: '(' [varargslist] ')' */
|
||||
n = CHILD(n, 1);
|
||||
}
|
||||
|
@ -4008,16 +4010,90 @@ com_argdefs(struct compiling *c, node *n)
|
|||
return ndefs;
|
||||
}
|
||||
|
||||
static void
|
||||
com_decorator_name(struct compiling *c, node *n)
|
||||
{
|
||||
/* dotted_name: NAME ('.' NAME)* */
|
||||
|
||||
int i, nch;
|
||||
node *varname;
|
||||
|
||||
REQ(n, dotted_name);
|
||||
nch = NCH(n);
|
||||
assert(nch >= 1 && nch % 2 == 1);
|
||||
|
||||
varname = CHILD(n, 0);
|
||||
REQ(varname, NAME);
|
||||
com_addop_varname(c, VAR_LOAD, STR(varname));
|
||||
|
||||
for (i = 1; i < nch; i += 2) {
|
||||
node *attrname;
|
||||
|
||||
REQ(CHILD(n, i), DOT);
|
||||
|
||||
attrname = CHILD(n, i + 1);
|
||||
REQ(attrname, NAME);
|
||||
com_addop_name(c, LOAD_ATTR, STR(attrname));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
com_decorator(struct compiling *c, node *n)
|
||||
{
|
||||
/* decorator: '@' dotted_name [ '(' [arglist] ')' ] */
|
||||
int nch = NCH(n);
|
||||
assert(nch >= 2);
|
||||
REQ(CHILD(n, 0), AT);
|
||||
com_decorator_name(c, CHILD(n, 1));
|
||||
|
||||
if (nch > 2) {
|
||||
assert(nch == 4 || nch == 5);
|
||||
REQ(CHILD(n, 2), LPAR);
|
||||
REQ(CHILD(n, nch - 1), RPAR);
|
||||
com_call_function(c, CHILD(n, 3));
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
com_decorators(struct compiling *c, node *n)
|
||||
{
|
||||
int i, nch, ndecorators;
|
||||
|
||||
/* decorator ([NEWLINE] decorator)* NEWLINE */
|
||||
nch = NCH(n);
|
||||
assert(nch >= 2);
|
||||
REQ(CHILD(n, nch - 1), NEWLINE);
|
||||
|
||||
ndecorators = 0;
|
||||
for (i = NCH(n) - 1; i >= 0; --i) {
|
||||
node *ch = CHILD(n, i);
|
||||
if (TYPE(ch) != NEWLINE) {
|
||||
com_decorator(c, ch);
|
||||
++ndecorators;
|
||||
}
|
||||
}
|
||||
|
||||
return ndecorators;
|
||||
}
|
||||
|
||||
static void
|
||||
com_funcdef(struct compiling *c, node *n)
|
||||
{
|
||||
PyObject *co;
|
||||
int ndefs;
|
||||
REQ(n, funcdef); /* funcdef: 'def' NAME parameters ':' suite */
|
||||
int ndefs, ndecorators;
|
||||
REQ(n, funcdef);
|
||||
/* -6 -5 -4 -3 -2 -1
|
||||
funcdef: [decorators] 'def' NAME parameters ':' suite */
|
||||
|
||||
if (NCH(n) == 6)
|
||||
ndecorators = com_decorators(c, CHILD(n, 0));
|
||||
else
|
||||
ndecorators = 0;
|
||||
|
||||
ndefs = com_argdefs(c, n);
|
||||
if (ndefs < 0)
|
||||
return;
|
||||
symtable_enter_scope(c->c_symtable, STR(CHILD(n, 1)), TYPE(n),
|
||||
symtable_enter_scope(c->c_symtable, STR(RCHILD(n, -4)), TYPE(n),
|
||||
n->n_lineno);
|
||||
co = (PyObject *)icompile(n, c);
|
||||
symtable_exit_scope(c->c_symtable);
|
||||
|
@ -4033,7 +4109,12 @@ com_funcdef(struct compiling *c, node *n)
|
|||
else
|
||||
com_addoparg(c, MAKE_FUNCTION, ndefs);
|
||||
com_pop(c, ndefs);
|
||||
com_addop_varname(c, VAR_STORE, STR(CHILD(n, 1)));
|
||||
while (ndecorators > 0) {
|
||||
com_addoparg(c, CALL_FUNCTION, 1);
|
||||
com_pop(c, 1);
|
||||
ndecorators--;
|
||||
}
|
||||
com_addop_varname(c, VAR_STORE, STR(RCHILD(n, -4)));
|
||||
com_pop(c, 1);
|
||||
Py_DECREF(co);
|
||||
}
|
||||
|
@ -4377,21 +4458,23 @@ compile_funcdef(struct compiling *c, node *n)
|
|||
{
|
||||
PyObject *doc;
|
||||
node *ch;
|
||||
REQ(n, funcdef); /* funcdef: 'def' NAME parameters ':' suite */
|
||||
c->c_name = STR(CHILD(n, 1));
|
||||
doc = get_docstring(c, CHILD(n, 4));
|
||||
REQ(n, funcdef);
|
||||
/* -6 -5 -4 -3 -2 -1
|
||||
funcdef: [decorators] 'def' NAME parameters ':' suite */
|
||||
c->c_name = STR(RCHILD(n, -4));
|
||||
doc = get_docstring(c, RCHILD(n, -1));
|
||||
if (doc != NULL) {
|
||||
(void) com_addconst(c, doc);
|
||||
Py_DECREF(doc);
|
||||
}
|
||||
else
|
||||
(void) com_addconst(c, Py_None); /* No docstring */
|
||||
ch = CHILD(n, 2); /* parameters: '(' [varargslist] ')' */
|
||||
ch = RCHILD(n, -3); /* parameters: '(' [varargslist] ')' */
|
||||
ch = CHILD(ch, 1); /* ')' | varargslist */
|
||||
if (TYPE(ch) == varargslist)
|
||||
com_arglist(c, ch);
|
||||
c->c_infunction = 1;
|
||||
com_node(c, CHILD(n, 4));
|
||||
com_node(c, RCHILD(n, -1));
|
||||
c->c_infunction = 0;
|
||||
com_strip_lnotab(c);
|
||||
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None));
|
||||
|
@ -5587,9 +5670,12 @@ symtable_node(struct symtable *st, node *n)
|
|||
loop:
|
||||
switch (TYPE(n)) {
|
||||
case funcdef: {
|
||||
char *func_name = STR(CHILD(n, 1));
|
||||
char *func_name;
|
||||
if (NCH(n) == 6)
|
||||
symtable_node(st, CHILD(n, 0));
|
||||
func_name = STR(RCHILD(n, -4));
|
||||
symtable_add_def(st, func_name, DEF_LOCAL);
|
||||
symtable_default_args(st, CHILD(n, 2));
|
||||
symtable_default_args(st, RCHILD(n, -3));
|
||||
symtable_enter_scope(st, func_name, TYPE(n), n->n_lineno);
|
||||
symtable_funcdef(st, n);
|
||||
symtable_exit_scope(st);
|
||||
|
@ -5734,6 +5820,17 @@ symtable_node(struct symtable *st, node *n)
|
|||
to be coded with great care, even though they look like
|
||||
rather innocuous. Each case must double-check TYPE(n).
|
||||
*/
|
||||
case decorator:
|
||||
if (TYPE(n) == decorator) {
|
||||
/* decorator: '@' dotted_name [ '(' [arglist] ')' ] */
|
||||
node *name, *varname;
|
||||
name = CHILD(n, 1);
|
||||
REQ(name, dotted_name);
|
||||
varname = CHILD(name, 0);
|
||||
REQ(varname, NAME);
|
||||
symtable_add_use(st, STR(varname));
|
||||
}
|
||||
/* fall through */
|
||||
case argument:
|
||||
if (TYPE(n) == argument && NCH(n) == 3) {
|
||||
n = CHILD(n, 2);
|
||||
|
@ -5787,7 +5884,7 @@ symtable_funcdef(struct symtable *st, node *n)
|
|||
if (NCH(n) == 4)
|
||||
symtable_params(st, CHILD(n, 1));
|
||||
} else
|
||||
symtable_params(st, CHILD(n, 2));
|
||||
symtable_params(st, RCHILD(n, -3));
|
||||
body = CHILD(n, NCH(n) - 1);
|
||||
symtable_node(st, body);
|
||||
}
|
||||
|
|
2406
Python/graminit.c
2406
Python/graminit.c
File diff suppressed because it is too large
Load Diff
|
@ -8,7 +8,8 @@
|
|||
# = ... a default value for the node constructor (optional args)
|
||||
Module: doc*, node
|
||||
Stmt: nodes!
|
||||
Function: name*, argnames*, defaults!, flags*, doc*, code
|
||||
Decorators: nodes!
|
||||
Function: decorators&, name*, argnames*, defaults!, flags*, doc*, code
|
||||
Lambda: argnames*, defaults!, flags*, code
|
||||
Class: name*, bases!, doc*, code
|
||||
Pass:
|
||||
|
|
|
@ -154,19 +154,19 @@ class NodeInfo:
|
|||
else:
|
||||
print >> buf, " return %s" % COMMA.join(clist)
|
||||
else:
|
||||
print >> buf, " nodes = []"
|
||||
template = " nodes.%s(%sself.%s%s)"
|
||||
print >> buf, " nodelist = []"
|
||||
template = " nodelist.%s(%sself.%s%s)"
|
||||
for name in self.argnames:
|
||||
if self.argprops[name] == P_NONE:
|
||||
tmp = (" if self.%s is not None:"
|
||||
" nodes.append(self.%s)")
|
||||
" nodelist.append(self.%s)")
|
||||
print >> buf, tmp % (name, name)
|
||||
elif self.argprops[name] == P_NESTED:
|
||||
print >> buf, template % ("extend", "flatten_nodes(",
|
||||
name, ")")
|
||||
elif self.argprops[name] == P_NODE:
|
||||
print >> buf, template % ("append", "", name, "")
|
||||
print >> buf, " return tuple(nodes)"
|
||||
print >> buf, " return tuple(nodelist)"
|
||||
|
||||
def _gen_repr(self, buf):
|
||||
print >> buf, " def __repr__(self):"
|
||||
|
@ -208,7 +208,7 @@ def parse_spec(file):
|
|||
# some extra code for a Node's __init__ method
|
||||
name = mo.group(1)
|
||||
cur = classes[name]
|
||||
return classes.values()
|
||||
return sorted(classes.values(), key=lambda n: n.name)
|
||||
|
||||
def main():
|
||||
prologue, epilogue = load_boilerplate(sys.argv[-1])
|
||||
|
@ -245,9 +245,9 @@ def flatten(list):
|
|||
def flatten_nodes(list):
|
||||
return [n for n in flatten(list) if isinstance(n, Node)]
|
||||
|
||||
def asList(nodes):
|
||||
def asList(nodearg):
|
||||
l = []
|
||||
for item in nodes:
|
||||
for item in nodearg:
|
||||
if hasattr(item, "asList"):
|
||||
l.append(item.asList())
|
||||
else:
|
||||
|
@ -274,6 +274,21 @@ class Node: # an abstract base class
|
|||
class EmptyNode(Node):
|
||||
pass
|
||||
|
||||
class Expression(Node):
|
||||
# Expression is an artificial node class to support "eval"
|
||||
nodes["expression"] = "Expression"
|
||||
def __init__(self, node):
|
||||
self.node = node
|
||||
|
||||
def getChildren(self):
|
||||
return self.node,
|
||||
|
||||
def getChildNodes(self):
|
||||
return self.node,
|
||||
|
||||
def __repr__(self):
|
||||
return "Expression(%s)" % (repr(self.node))
|
||||
|
||||
### EPILOGUE
|
||||
klasses = globals()
|
||||
for k in nodes.keys():
|
||||
|
|
|
@ -47,6 +47,8 @@ def compile_files(dir):
|
|||
continue
|
||||
# make sure the .pyc file is not over-written
|
||||
os.chmod(source + "c", 444)
|
||||
elif file == 'CVS':
|
||||
pass
|
||||
else:
|
||||
path = os.path.join(dir, file)
|
||||
if os.path.isdir(path):
|
||||
|
|
Loading…
Reference in New Issue