动态创建方法和装饰器,出现错误'functools.partial'对象没有属性'__module__' [英] dynamic create method and decorator, got error 'functools.partial' object has no attribute '__module__'

本文介绍了动态创建方法和装饰器,出现错误'functools.partial'对象没有属性'__module__'的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前使用EndpointsModel为AppEngine上的所有模型创建RESTful API。由于它是RESTful,这些api有很多我想避免的重复代码。例如:

<

 class Reducer(EndpointsModel):
name = ndb.StringProperty(indexed = False)

@ endpoints.api(
name = bigdata,
version =v1,
description =BigData API,
allowed_client_ids = ALLOWED_CLIENT_IDS,

class BigDataApi(remote .Service):
@ Reducer.method(
path =reducer,
http_method =POST,
name =reducer.insert,
user_required = True,

def ReducerInsert(self,obj):
传递

##和GET,POST,PUT,DELETE
## REPEATED对于每个模型

我想让它们变得通用。所以我尝试动态添加方法的类。
我到目前为止试过的东西:

  from functools import partial,wrap 

def GenericInsert(self,obj,cls):
obj.owner = endpoints.get_current_user()
obj.put()
return obj

#忽略GenericDelete,GenericGet ,GenericUpdate ...

导入类型$ b $ from functools导入部分

def register_rest_api(api_server,endpoint_cls):
name = endpoint_cls .__ name__

#create list method
query_method = types.MethodType(
endpoint_cls.query_method(
query_fields =('limit','pageToken'),
path = %ss%name,
http_method =GET,
name =%s.list%name,
user_required = True
)(partial(GenericList,cls = $ endpoint

setattr(api_server,%sList,query_method)

#create insert method
#...

register_rest_api(BigDataApi,Reducer)

但是我得到'functools.partial'对象没有属性'__module__'异常。
我认为这是因为 endpoints.method 的装饰器和部分。

  Traceback(最近一次调用的最后一个):
文件/ Applications / GoogleAppEngineLauncher .app / Contents / Resources / GoogleAppEngine-default.bundle / Contents / Resources / google_appengine / google / appengine / runtime / wsgi.py,第239行,在Handle
handler = _config_handle.add_wsgi_middleware(self._LoadHandler() )
在_LoadHandler
中的文件/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/runtime/wsgi.py,第298行处理程序,路径,err = LoadObject(self._handler)
文件/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/runtime/wsgi.py ,第84行,在LoadObject
obj = __import __(path [0])
文件/Users/Sylvia/gcdc2013/apis.py,第795行,位于< module>
register_rest_api(BigDataApi,Reducer)
文件/Users/Sylvia/gcdc2013/apis.py,第788行,在register_rest_api
)(partial(GenericList,cls = endpoint_cls)))
File // System / Library / Frameworks / Python.framework / Versions / 2.7 / lib / python2.7 / functools.py,第33行,在update_wrapper
setattr(wrapper,attr,getattr(wrapped,attr))
AttributeError:'functools。部分'对象没有属性'__module__'

相关文章:



更新




  1. 完成调用栈

  2. 缩短问题时间


解决方案

我也偶然发现了这个,我真的很惊讶,对我来说问题是部分对象缺少某些属性,特别是 __ module __ __ name__



默认情况下,包装使用 functools.WRAPPER_ASSIGNMENTS 在python 2.7.6中更新属性,默认为('__ module__','__name__','__doc __'),无论如何,处理这种情况的几种方法...



更新 only present 属性...

 导入functools 
导入itertools

def wraps_safel y(obj,attr_names = functools.WRAPPER_ASSIGNMENTS):
返回包裹(obj,assigned = itertools.ifilter(functools.partial(hasattr,obj),attr_names))

>> > def foo():
...无处不在的foo函数....
...
>>> functools.wraps(partial(foo))(foo)()
Traceback(最近一次调用最后一次):
在< module>中的第1行文件< stdin>
文件/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/functools.py,第33行,在update_wrapper中
setattr(wrapper,attr,getattr(wrapped ,attr))
AttributeError:'functools.partial'对象没有属性'__module__'
>>> wraps_safely(partial(foo))(foo)()
>>>

这里我们简单地过滤出所有不存在的属性。



另一种方法是严格处理部分对象,可以用 wraps > singledispatch 并创建包含的属性将从最深的 func 属性获取的部分对象。



有些事情:

  import functools 

def wrappers_partial(wrapper,* args,** kwargs):
创建一个可调用的对象,其属性将从partials嵌套的func属性中设置...
wrapper = wrapper.func
whileinstance(wrapper,functools.partial):
wrapper = wrapper.func
返回functools.wraps(wrapper,* args,** kwargs)

def foo ():
Foo函数。
:return:None
pass

>>> wrappers_partial(partial(partial(foo)))(lambda:None).__ doc__
'Foo Function,returns None'
>>> wrappart_partial(partial(partial(foo)))(lambda:None).__ name__
'foo'
>>> wraps_partial(partial(partial(foo)))(lambda:None)()
>>> pfoo = partial(partial(foo))
>>> @wraps_partial(pfoo)
... def not_foo():
...Not Foo function ...
...
>> ;> not_foo .__ doc__
'Foo函数,返回None'
>>> not_foo .__ name__
'foo'
>>>

这稍微好一点,因为现在我们可以获得原始函数文档,在默认情况下使用部分对象doc字符串。



这可以修改为只搜索当前部分对象是否已经具有set属性,当嵌套许多部分对象时它应该稍微快一些...



UPDATE

似乎python(CPython)3(至少3.4。 3)没有这个问题,因为我不知道,也不应该假设所有版本的python 3或其他实现,比如Jython也在这里分享这个问题是另一个 future ready 方法

 来自functools import wraps,partial,WRAPPER_ASSIGNMENTS 

try:
wrapps(partial(wraps)) (包装)
,除了AttributeError:
@wrap(包装)
def包装(obj,attr_names = WRAPPER_ASSIGNMENTS,包装=包装):
返回包装(obj,assigned =对于attr_names中的名字,如果hasattr(obj,name)))

p>


  • 我们定义一个新的 wrapps function 仅当我们未能包装部分 时,以防未来版本的python2或其他版本修复此问题。 封装以复制文档和其他信息

  • 我们不使用 ifilter 因为它在python3中被删除了,所以timeit有和没有 ifilter 但是结果没有定论,至少在python(CPython)2.7.6中,差异很小最好不管怎样......


I am currently using EndpointsModel to create a RESTful API for all my models on AppEngine. Since it is RESTful, these api have a lot of repeat code which I want to avoid

For Example:

class Reducer(EndpointsModel):
    name = ndb.StringProperty(indexed=False)

@endpoints.api(
    name="bigdata",
    version="v1",
    description="""The BigData API""",
    allowed_client_ids=ALLOWED_CLIENT_IDS,
)
class BigDataApi(remote.Service):
    @Reducer.method(
        path="reducer",
        http_method="POST",
        name="reducer.insert",
        user_required=True,
    )
    def ReducerInsert(self, obj):
        pass

    ## and GET, POST, PUT, DELETE
    ## REPEATED for each model

I want to make them become generic. So I try to dynamic add method to the class. What I have tried so far:

from functools import partial, wraps

def GenericInsert(self, obj, cls):
    obj.owner = endpoints.get_current_user()
    obj.put()
    return obj

# Ignore GenericDelete, GenericGet, GenericUpdate ...

import types
from functools import partial

def register_rest_api(api_server, endpoint_cls):
    name = endpoint_cls.__name__

    # create list method 
    query_method = types.MethodType(
    endpoint_cls.query_method(
        query_fields=('limit', 'pageToken'),
        path="%ss" % name,
        http_method="GET",
        name="%s.list" % name,
        user_required=True
    )(partial(GenericList, cls=endpoint_cls)))

    setattr(api_server, "%sList", query_method)

    # create insert method
    # ...

register_rest_api(BigDataApi, Reducer)

But I got 'functools.partial' object has no attribute '__module__' exception. I think it is because there are some conflicts between endpoints.method's decorator and partial. But no idea how to avoid it.

Traceback (most recent call last):
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/runtime/wsgi.py", line 239, in Handle
    handler = _config_handle.add_wsgi_middleware(self._LoadHandler())
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/runtime/wsgi.py", line 298, in _LoadHandler
    handler, path, err = LoadObject(self._handler)
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/runtime/wsgi.py", line 84, in LoadObject
    obj = __import__(path[0])
  File "/Users/Sylvia/gcdc2013/apis.py", line 795, in <module>
    register_rest_api(BigDataApi, Reducer)
  File "/Users/Sylvia/gcdc2013/apis.py", line 788, in register_rest_api
    )(partial(GenericList, cls=endpoint_cls)))
  File "/Users/Sylvia/gcdc2013/endpoints_proto_datastore/ndb/model.py", line 1544, in RequestToQueryDecorator
    @functools.wraps(api_method)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/functools.py", line 33, in update_wrapper
    setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'functools.partial' object has no attribute '__module__'

related articles:

Update

  1. complete the call stack
  2. Make question shorter

解决方案

I also stumbled upon this, I was really surprised, for me the issue was that partial objects are missing certain attributes, specifically __module__ and __name__

Being that wraps by default uses functools.WRAPPER_ASSIGNMENTS to update attributes, which defaults to ('__module__', '__name__', '__doc__') in python 2.7.6 anyway, there are a couple ways of dealing with this ...

Update only present attributes ...

import functools
import itertools

def wraps_safely(obj, attr_names=functools.WRAPPER_ASSIGNMENTS):
    return wraps(obj, assigned=itertools.ifilter(functools.partial(hasattr, obj), attr_names))

>>> def foo():
...     """ Ubiquitous foo function ...."""
... 
>>> functools.wraps(partial(foo))(foo)()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/functools.py", line 33, in update_wrapper
setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'functools.partial' object has no attribute '__module__'
>>> wraps_safely(partial(foo))(foo)()
>>> 

Here we simply filter out all those attribute which aren't present.

Another approach would be to strictly deal with partial objects only, you could fold wraps with singledispatch and create wrapped partial objects whose attributes would be taken from the deepest func attribute.

Something along the lines:

import functools

def wraps_partial(wrapper, *args, **kwargs):
    """ Creates a callable object whose attributes will be set from the partials nested func attribute ..."""
    wrapper = wrapper.func
    while isinstance(wrapper, functools.partial):
        wrapper = wrapper.func
    return functools.wraps(wrapper, *args, **kwargs)

def foo():
    """ Foo function.
    :return: None """
    pass

>>> wraps_partial(partial(partial(foo)))(lambda : None).__doc__
' Foo Function, returns None '
>>> wraps_partial(partial(partial(foo)))(lambda : None).__name__
'foo'
>>> wraps_partial(partial(partial(foo)))(lambda : None)()
>>> pfoo = partial(partial(foo))
>>> @wraps_partial(pfoo)
... def not_foo():
...     """ Not Foo function ... """
... 
>>> not_foo.__doc__
' Foo Function, returns None '
>>> not_foo.__name__
'foo'
>>>

This is slightly better since now we can get the original functions docs which before defaulted to using the partial objects doc string.

This can be modified to only search if the current partial object doesn't already have the set attribute, which should be slightly faster when nesting many partial objects ...

UPDATE

It seems that python(CPython) 3 (at least 3.4.3) doesn't have this issue, since I don't know nor should I assume all versions of python 3 or other implementations such as Jython also share this issue here is another future ready approach

from functools import wraps, partial, WRAPPER_ASSIGNMENTS

try:
    wraps(partial(wraps))(wraps)
except AttributeError:
    @wraps(wraps)
    def wraps(obj, attr_names=WRAPPER_ASSIGNMENTS, wraps=wraps):
        return wraps(obj, assigned=(name for name in attr_names if hasattr(obj, name))) 

a couple things to note:

  • we define a new wraps function only if we fail to wrap a partial, in case future versions of python2 or other versions fix this issue.
  • we use the original wraps to copy the docs and other info
  • we don't use ifilter since it was removed in python3, I've timeit with and without ifilter but the results where inconclusive, at least in python (CPython) 2.7.6, the difference was marginal at best either way...

这篇关于动态创建方法和装饰器,出现错误'functools.partial'对象没有属性'__module__'的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆