尽管代码中没有任何内容,但Decorator仍在运行

时间:2017-05-17 14:52:48

标签: python

我有一个奇怪的问题。我有一个装饰器,即使在最初运行程序时没有被调用,它似乎也在运行。

在这个程序中没有任何运行。为了确保这一点,我做了一个grep来删除任何以空格开头的行并得到:

[keith@kb-linux ]$ grep -v '^\s' remedy.py
"""
Module to handle remedy calls
"""
import random
import json
from itertools import count
from pprint import pprint
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
from textproc import remuser, rempass, remurl, templateid
def counter():
def logout(key):
def remedy_api_call(func, c=None):
def remedy_auth():
@remedy_api_call
def get_entries(key, table):
@remedy_api_call
def get_ticket(key, table, t):
@remedy_api_call
def wtf_is_this_table(key, table):
@remedy_api_call
def new_ticket(key, person, message, bigmessage):
@remedy_api_call
def new_incident(key, message):
@remedy_api_call
def update_change(key, changeno, message):
def update_change_undec(key, changeno, message):
@remedy_api_call
def new_change(key, message="", **kwargs):
def parse_tickets(vals):
def get_change_templates():
def get_incident_templates():

正如你所看到的,一些导入,然后基本上是函数定义和我的装饰。

有一个非标准模块导入。我删除了这个,问题仍然存在(基本上他们只是凭证而已。

这是我的装饰师和相关功能:

def counter():
    """
    wraps an itertools count instance
    """
    return lambda c=count(): next(c)

def logout(key):
    """
    End api session and release key
    """
    requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
    r = requests.post(url="https://" +remurl + "/api/jwt/logout", 
                      headers={ "Authorization" : "AR-JWT " + key},
                      verify=False)
    return r.text 

def remedy_api_call(func, c=None):
    """
    Decorator for handling auth for remedy API calls.
    """
    try:
        key = remedy_auth()
    except:
        key = "<br>"
    if "<br>" not in str(key):
        def wrapper(*args, **kwargs):
            """
            wrapped function runs here.
            In any wrapped function add "key" before your args
            """
            r = func(key, *args, **kwargs)
            logout(key)
            return r
    else:
        print "problem getting token, retrying"
        if c is None:
            c = counter()
        if int(c()) < 9:
            remedy_api_call(func, c)
        else:
            print "tried too many times... Fail."
    return wrapper

def remedy_auth():
    """
    Get the key, get the secret
    """
    try:
        requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
        r = requests.post(url="https://" +remurl + "/api/jwt/login", 
                          data={"username" : remuser,
                                "password" : rempass},
                          verify=False)
        return r.text
    except:
        return "<br>"

如果我运行此代码,这就是我得到的......请记住,就像我说的那样,实际上在代码中调用了NOTHING。它是一个空白的函数集合,其中一些是装饰的,但我得到了:

>python remedy.py
problem getting token, retrying
problem getting token, retrying
problem getting token, retrying
problem getting token, retrying
problem getting token, retrying
problem getting token, retrying
problem getting token, retrying
problem getting token, retrying
problem getting token, retrying
problem getting token, retrying
tried too many times... Fail.
Traceback (most recent call last):
  File "remedy.py", line 75, in <module>
    @remedy_api_call
  File "remedy.py", line 53, in remedy_api_call
    remedy_api_call(func, c)
  File "remedy.py", line 53, in remedy_api_call
    remedy_api_call(func, c)
  File "remedy.py", line 53, in remedy_api_call
    remedy_api_call(func, c)
  File "remedy.py", line 53, in remedy_api_call
    remedy_api_call(func, c)
  File "remedy.py", line 53, in remedy_api_call
    remedy_api_call(func, c)
  File "remedy.py", line 53, in remedy_api_call
    remedy_api_call(func, c)
  File "remedy.py", line 53, in remedy_api_call
    remedy_api_call(func, c)
  File "remedy.py", line 53, in remedy_api_call
    remedy_api_call(func, c)
  File "remedy.py", line 53, in remedy_api_call
    remedy_api_call(func, c)
  File "remedy.py", line 56, in remedy_api_call
    return wrapper
UnboundLocalError: local variable 'wrapper' referenced before assignment

这个特定的运行是在自定义模块导入(from textproc...行)哈希的情况下完成的。

这是正常行为吗?我以前从未见过它。我期待一个文件只包含运行完全干净的函数

1 个答案:

答案 0 :(得分:2)

装饰器在函数定义之后执行,而不是在调用函数时执行。考虑一个更简单的例子:

def foo(f):
    print("Decorator foo is executing")
    return f

@foo
def bar():
    pass

结果:

Decorator foo is executing

即使永远不会执行bar,也会发生打印。这是因为装饰符号仅仅是用于赋值的语法糖。上面的代码相当于:

def foo(f):
    print("Decorator foo is executing")
    return f

def bar():
    pass
bar = foo(bar)

“好的,那么如何让代码仅在调用函数时执行?”你假设问。您可以通过在装饰器的定义中创建一个可调用对象并返回它来实现此目的。例如:

def foo(f):
    def this_gets_called_when_the_decorated_function_executes():
        print("Decorated function is executing")
        #conventionally, you call the original function in here, but it isn't strictly required
        f()
    return this_gets_called_when_the_decorated_function_executes

@foo
def bar():
    pass

print("About to call bar.")
bar()
print("bar called.")

结果:

About to call bar.
Decorated function is executing
bar called.