A decorator syntax not yet mentioned (I think!)

Michael Sparks michaels at rd.bbc.co.uk
Thu Aug 12 13:33:04 CEST 2004


Michael Sparks wrote:

> Regarding J2 on http://www.python.org/moin/PythonDecorators ...
> ...
>>    5 Technical problems with the current grammar parser if a suite
>>      *starts* with an optional part. (Ending with an optional part,
>>      such as "else:" is OK, but starting with one is not.)
> ...
>> Item 6 is always the case for any new feature, so I doubt that's the
>> real problem - the real problem here strikes me as item 5.
>> 
>> I do wonder how difficult it would be to add though...
> 
> It doesn't actually seem that difficult to modify the grammar to
> handle this if the decorator block handles *only* decorators. I've
> just tried modifying the Grammar/Grammar file to see how plausible
> this is, and I can get python to build and parse it. (It bombs out
> because I've not done any backend work, and this is the first time
> I've touched the python compiler source)

Done a bit more work and it certainly *is* caused by the backend
logic not in step with the grammar change I made rather than it not
building/parsing correctly. I've tried changing things to make this
work, but at this exact instant I don't have the time to do this. (I
might take another look this evening, it doesn't look *too* difficult
to do)

I've also changed the grammar rules again to make it a smaller change:

--- Python-2.4a2/Grammar/Grammar        2004-08-02 07:09:53.000000000 +0100
+++ Python-2.4a2-MS/Grammar/Grammar     2004-08-12 12:12:11.567115840 +0100
@@ -28,9 +28,10 @@
 file_input: (NEWLINE | stmt)* ENDMARKER
 eval_input: testlist NEWLINE* ENDMARKER

-decorator: '@' dotted_name [ '(' [arglist] ')' ]
+decorator: dotted_name [ '(' [arglist] ')' ]
 decorators: decorator ([NEWLINE] decorator)* NEWLINE
-funcdef: [decorators] 'def' NAME parameters ':' suite
+funcdef: ['decorate' ':' NEWLINE INDENT decorators DEDENT] 'def' NAME parameters ':' suite
 parameters: '(' [varargslist] ')'
 varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) | fpdef ['=' test] (',' fpdef ['=' test])* [',']
 fpdef: NAME | '(' fplist ')'

This builds, and I've changed some of Python/compile.c to handle this:
--- Python-2.4a2/Python/compile.c       2004-08-04 11:26:08.000000000 +0100
+++ Python-2.4a2-MS/Python/compile.c    2004-08-12 12:19:47.570792744 +0100
@@ -4043,13 +4043,13 @@
 {
        /* decorator: '@' dotted_name [ '(' [arglist] ')' ] */
        int nch = NCH(n);
-       assert(nch >= 2);
-       REQ(CHILD(n, 0), AT);
-       com_decorator_name(c, CHILD(n, 1));
+       assert(nch >= 1);
+       // REQ(CHILD(n, 0), AT);
+       com_decorator_name(c, CHILD(n, 0));

        if (nch > 2) {
                assert(nch == 4 || nch == 5);
-               REQ(CHILD(n, 2), LPAR);
+               REQ(CHILD(n, 1), LPAR);
                REQ(CHILD(n, nch - 1), RPAR);
                com_call_function(c, CHILD(n, 3));
        }
@@ -4083,11 +4083,14 @@
        PyObject *co;
        int ndefs, ndecorators;
        REQ(n, funcdef);
-       /*          -6            -5   -4   -3         -2  -1
+       /*          -6           -5   -4   -3         -2  -1
           funcdef: [decorators] 'def' NAME parameters ':' suite */
+       /*                                        -7         -6        -5   -4   -3         -2  -1
+          funcdef: ['decorate' ':' NEWLINE INDENT decorators DEDENT ] 'def' NAME parameters ':' suite */
+

-       if (NCH(n) == 6)
-               ndecorators = com_decorators(c, CHILD(n, 0));
+       if (NCH(n) == 11)
+               ndecorators = com_decorators(c, CHILD(n, 4));
        else
                ndecorators = 0;

@@ -5823,9 +5826,9 @@
        */
        case decorator:
                if (TYPE(n) == decorator) {
-                       /* decorator: '@' dotted_name [ '(' [arglist] ')' ] */
+                       /* decorator: dotted_name [ '(' [arglist] ')' ] */
                        node *name, *varname;
-                       name = CHILD(n, 1);
+                       name = CHILD(n, 0);
                        REQ(name, dotted_name);
                        varname = CHILD(name, 0);
                        REQ(varname, NAME);

However I'm now getting a new error instead (just before a controlled
core dump):

Fatal Python error: unknown scope for staticmethod in Foo(1) in ./foo.py
symbols: {'hello2': 2, 'hello': 2}
locals: {'hello2': 0, 'hello': 1}
globals: {}

Aborted (core dumped)

Smoke test is this:
---------
class Foo:
   decorate:
      staticmethod
   def hello(who):
      print "woo?", who
   def hello2(who):
      print "woo?", who

Foo.hello("HOO")
---------

I've got to put this aside for the moment, but I'll come back to it
later. (I think this is actually pretty close to working though...)


Michael.
-- 
Michael.Sparks at rd.bbc.co.uk    
British Broadcasting Corporation, Research and Development
Kingswood Warren, Surrey KT20 6NP

This message (and any attachments) may contain personal views
which are not the views of the BBC unless specifically stated.





More information about the Python-list mailing list