Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #12140

Weird interaction with nested functions inside a decorator-producing function and closuring of outer data...

Path csiph.com!x330-a1.tempe.blueboxinc.net!usenet.pasdenom.info!aioe.org!feeder.news-service.com!newsfeed.xs4all.nl!newsfeed6.news.xs4all.nl!xs4all!post.news.xs4all.nl!not-for-mail
Return-Path <adam.jorgensen.za@gmail.com>
X-Original-To python-list@python.org
Delivered-To python-list@mail.python.org
X-Spam-Status OK 0.000
X-Spam-Evidence '*H*': 1.00; '*S*': 0.00; 'else:': 0.03; 'args': 0.05; 'parameter': 0.05; 'exception,': 0.07; 'python': 0.08; 'decorator': 0.09; 'finally:': 0.09; 'none:': 0.09; 'referenced': 0.09; 'similarly,': 0.09; 'skip:[ 30': 0.09; 'broken': 0.12; 'def': 0.15; '**kwargs)': 0.16; '**kwargs):': 0.16; '*args,': 0.16; 'above?': 0.16; 'adam': 0.16; 'decorators.': 0.16; 'lambda': 0.16; 'obj,': 0.16; 'subject: \n ': 0.16; 'subject:function': 0.16; 'slightly': 0.19; 'seems': 0.20; "doesn't": 0.22; 'versions': 0.23; 'works.': 0.23; 'index': 0.24; 'code': 0.25; 'parameters': 0.25; "i'm": 0.27; 'work.': 0.27; 'all,': 0.28; 'raise': 0.28; 'effect': 0.28; 'modify': 0.28; 'problem': 0.28; 'fine.': 0.29; 'happening': 0.29; 'message-id:@mail.gmail.com': 0.29; 'fix': 0.29; 'decorators': 0.30; 'thanks': 0.30; 'changing': 0.31; 'actual': 0.32; 'does': 0.32; 'anyone': 0.32; "what's": 0.33; 'there': 0.33; 'to:addr:python-list': 0.33; 'idea': 0.34; 'try:': 0.34; 'weird': 0.34; 'list.': 0.35; 'issue': 0.36; 'variables': 0.37; 'using': 0.37; 'run': 0.37; 'two': 0.37; 'think': 0.38; 'some': 0.38; 'received:google.com': 0.38; 'received:209.85': 0.38; 'skip:o 20': 0.38; 'subject:with': 0.39; 'why': 0.39; 'to:addr:python.org': 0.39; 'skip:d 20': 0.39; "it's": 0.40; 'more': 0.60; 'order': 0.62; 'skip:w 50': 0.73; 'why?': 0.73; 'experiencing': 0.84; 'illustrated': 0.84; 'refuses': 0.84
DKIM-Signature v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=mime-version:from:date:message-id:subject:to:content-type; bh=wJRc3elvQJLSxt+pfG0o7f0RPDRZuHS5eHLNZDBSnIk=; b=eDwLLvkRZ5P4Xnrd6NBEG5f0x1y1nCZx5m4D0suP6JfRmJd2yEAKf5WIPLjUaC5PsN V7vvg/ImmyBPggnbBLDflekhm2k6lggHJJk7ROIf1okIW9N+4DmpGFbbWw6/A2KWzjt5 KR35eiFVyhd0N4Bq2yf1PQyoKz38Yef07ztlU=
MIME-Version 1.0
From Adam Jorgensen <adam.jorgensen.za@gmail.com>
Date Wed, 24 Aug 2011 14:21:18 +0200
Subject Weird interaction with nested functions inside a decorator-producing function and closuring of outer data...
To python-list@python.org
Content-Type text/plain; charset=ISO-8859-1
X-BeenThere python-list@python.org
X-Mailman-Version 2.1.12
Precedence list
List-Id General discussion list for the Python programming language <python-list.python.org>
List-Unsubscribe <http://mail.python.org/mailman/options/python-list>, <mailto:python-list-request@python.org?subject=unsubscribe>
List-Archive <http://mail.python.org/pipermail/python-list>
List-Post <mailto:python-list@python.org>
List-Help <mailto:python-list-request@python.org?subject=help>
List-Subscribe <http://mail.python.org/mailman/listinfo/python-list>, <mailto:python-list-request@python.org?subject=subscribe>
Newsgroups comp.lang.python
Message-ID <mailman.387.1314188521.27778.python-list@python.org> (permalink)
Lines 136
NNTP-Posting-Host 2001:888:2000:d::a6
X-Trace 1314188521 news.xs4all.nl 23937 [2001:888:2000:d::a6]:46705
X-Complaints-To abuse@xs4all.nl
Xref x330-a1.tempe.blueboxinc.net comp.lang.python:12140

Show key headers only | View raw


Hi all, I'm experiencing a weird issue with closuring of parameters
and some nested functions I have inside two functions that
return decorators. I think it's best illustrated with the actual code:

# This decorator doesn't work. For some reason python refuses to
closure the *decode_args parameter into the scope of the nested
decorate and decorate_with_rest_wrapper functions
# Renaming *decode_args has no effect
def rest_wrapper(*decode_args, **deco_kwargs):
    def decorate(func):
        argspec = getfullargspec(func)
        decode_args = [argspec.args.index(decode_arg) for decode_arg
in decode_args]
        def decorate_with_rest_wrapper(func, *args, **kwargs):
            if decode_args:
                args = list(args)
                for index in decode_args:
                    args[index] = json.loads(args[index],
cls=deco_kwargs.get('json_decoder'))
            return Response.ResponseString(json.dumps(func(*args,
**kwargs), cls=deco_kwargs.get('json_encoder')))
        return decorator(decorate_with_rest_wrapper, func)
    return decorate

# If I modify rest_wrapper slightly and use the parameters from
rest_wrapper as default value args for decorate it works. Why?
def rest_wrapper(*decode_args, **deco_kwargs):
    def decorate(func, decode_args=decode_args, deco_kwargs=deco_kwargs):
        argspec = getfullargspec(func)
        decode_args = [argspec.args.index(decode_arg) for decode_arg
in decode_args]
        def decorate_with_rest_wrapper(func, *args, **kwargs):
            if decode_args:
                args = list(args)
                for index in decode_args:
                    args[index] = json.loads(args[index],
cls=deco_kwargs.get('json_decoder'))
            return Response.ResponseString(json.dumps(func(*args,
**kwargs), cls=deco_kwargs.get('json_encoder')))
        return decorator(decorate_with_rest_wrapper, func)
    return decorate


# Similarly, this decorator doesn't work. For some reason python
refuses to closure the sa_session_class_lambda parameter into the
scope of the nested decorate and decorate_with_sqlalchemy functions
# Renaming the sa_session_class_lambda parameter does not work and
neither does changing the order of the args list.
def with_sqlalchemy(sa_session_parameter_name='sa_session',
sa_session_class_lambda=None):
    def decorate(func):
        argspec = getfullargspec(func)
        sa_session_parameter_index =
argspec.args.index(sa_session_parameter_name)
        if sa_session_class_lambda is None:
            if argspec.args[0] == 'self':
                sa_session_class_lambda = lambda obj, *args, **kwargs:
obj.get_sa_session_class()
            else:
                sa_session_class_lambda = lambda *args, **kwargs:
Alchemy.get_sa_session_class()
        def decorate_with_alchemy(func, *args, **kwargs):
            if args[sa_session_parameter_index]:
                raise Exception('%s parameter is not empty!' %
sa_session_parameter_name)
            try:
                sa_session_class = sa_session_class_lambda(*args, **kwargs)
                sa_session = sa_session_class()
                args = list(args)
                args[sa_session_parameter_index] = sa_session
                retval = func(*args, **kwargs)
                sa_session.commit()
                return retval
            except Exception, e:
                sa_session.rollback()
                raise e
            finally:
                sa_session.close()
        return decorator(decorate_with_alchemy, func)
    return decorate

# Again, if I modify decorate and use the parameters from
with_sqlalchemy as default value args in decorate it works fine. What
gives?
def with_sqlalchemy(sa_session_parameter_name='sa_session',
sa_session_class_lambda=None):
    def decorate(func,
sa_session_parameter_name=sa_session_parameter_name,
sa_session_class_lambda=sa_session_class_lambda):
        argspec = getfullargspec(func)
        sa_session_parameter_index =
argspec.args.index(sa_session_parameter_name)
        if sa_session_class_lambda is None:
            if argspec.args[0] == 'self':
                sa_session_class_lambda = lambda obj, *args, **kwargs:
obj.get_sa_session_class()
            else:
                sa_session_class_lambda = lambda *args, **kwargs:
Alchemy.get_sa_session_class()
        def decorate_with_alchemy(func, *args, **kwargs):
            if args[sa_session_parameter_index]:
                raise Exception('%s parameter is not empty!' %
sa_session_parameter_name)
            try:
                sa_session_class = sa_session_class_lambda(*args, **kwargs)
                sa_session = sa_session_class()
                args = list(args)
                args[sa_session_parameter_index] = sa_session
                retval = func(*args, **kwargs)
                sa_session.commit()
                return retval
            except Exception, e:
                sa_session.rollback()
                raise e
            finally:
                sa_session.close()
        return decorator(decorate_with_alchemy, func)
    return decorate


Does anyone have any idea why the problem parameters are not being captured?

When I try to run the code using the broken versions of the two
decorators python fails claiming that the problem variables
are being referenced before they are defined...

What's more interesting is that PyCharm seems to know this is going to
happen as well because the code insight marks
the problem versions as having unused parameters (Specifically, the
*decode_args and sa_session_class_lambda parameters).

Does anyone know why this is happening and if there is a nicer fix
than the one illustrated above?

Thanks
Adam

Back to comp.lang.python | Previous | Next | Find similar | Unroll thread


Thread

Weird interaction with nested functions inside a decorator-producing function and closuring of outer data... Adam Jorgensen <adam.jorgensen.za@gmail.com> - 2011-08-24 14:21 +0200

csiph-web