将类的所有可能的方法调用包装在try/except块中 [英] Wrapping all possible method calls of a class in a try/except block

查看:84
本文介绍了将类的所有可能的方法调用包装在try/except块中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将现有类(不是我创建的类)的所有方法包装到try/except套件中.可以是任何类,但在这里我将使用 pandas.DataFrame 类作为一个实际示例.

I'm trying to wrap all methods of an existing Class (not of my creation) into a try/except suite. It could be any Class, but I'll use the pandas.DataFrame class here as a practical example.

因此,如果调用的方法成功,我们将继续前进.但是,如果应生成异常,则将其附加到列表中以供以后检查/发现(尽管以下示例为简单起见仅发布了一条打印语句).

So if the invoked method succeeds, we simply move on. But if it should generate an exception, it is appended to a list for later inspection/discovery (although the below example just issues a print statement for simplicity).

(请注意,调用实例上的方法时可能发生的与数据相关的异常的种类尚不清楚;这是进行此练习的原因:发现).

帖子很有帮助(尤其是@martineau Python-3回答),但是我在适应它时遇到了麻烦.在下面,我希望第二次调用(包装的) info()方法以发出打印输出,但不幸的是,它没有.

This post was quite helpful (particularly @martineau Python-3 answer), but I'm having trouble adapting it. Below, I expected the second call to the (wrapped) info() method to emit print output but, sadly, it doesn't.

#!/usr/bin/env python3

import functools, types, pandas

def method_wrapper(method):
    @functools.wraps(method)
    def wrapper(*args, **kwargs): #Note: args[0] points to 'self'.
        try:
            print('Calling: {}.{}()... '.format(args[0].__class__.__name__,
                                                method.__name__))
            return method(*args, **kwargs)
        except Exception:
            print('Exception: %r' % sys.exc_info()) # Something trivial.
            #<Actual code would append that exception info to a list>.
    return wrapper


class MetaClass(type):
    def __new__(mcs, class_name, base_classes, classDict):
        newClassDict = {}
        for attributeName, attribute in classDict.items():
            if type(attribute) == types.FunctionType: # Replace it with a
                attribute = method_wrapper(attribute) # decorated version.
            newClassDict[attributeName] = attribute
        return type.__new__(mcs, class_name, base_classes, newClassDict)

class WrappedDataFrame2(MetaClass('WrappedDataFrame',
                                  (pandas.DataFrame, object,), {}),
                                  metaclass=type):
    pass

print('Unwrapped pandas.DataFrame().info():')
pandas.DataFrame().info()

print('\n\nWrapped pandas.DataFrame().info():')
WrappedDataFrame2().info()
print()

这将输出:

Unwrapped pandas.DataFrame().info():
<class 'pandas.core.frame.DataFrame'>
Index: 0 entries
Empty DataFrame

Wrapped pandas.DataFrame().info():   <-- Missing print statement after this line.
<class '__main__.WrappedDataFrame2'>
Index: 0 entries
Empty WrappedDataFrame2

总而言之,...

>>> unwrapped_object.someMethod(...)
# Should be mirrored by ...

>>> wrapping_object.someMethod(...)
# Including signature, docstring, etc. (i.e. all attributes); except that it
# executes inside a try/except suite (so I can catch exceptions generically).

推荐答案

好久不见了. ;-)实际上已经很长时间了,您可能不再在意,但是万一您(或其他人)愿意...

long time no see. ;-) In fact it's been such a long time you may no longer care, but in case you (or others) do...

这是我认为会做的事.我之前从未回答过您的问题,因为我的系统上没有安装pandas.但是,今天我决定看看是否有一种解决方法,没有它,并创建了一个简单的虚拟模块来模拟它(仅根据我的需要).这是里面唯一的东西:

Here's something I think will do what you want. I've never answered your question before now because I don't have pandas installed on my system. However, today I decided to see if there was a workaround for not having it and created a trivial dummy module to mock it (only as far as I needed). Here's the only thing in it:

mockpandas.py:

""" Fake pandas module. """

class DataFrame:
    def info(self):
        print('pandas.DataFrame.info() called')
        raise RuntimeError('Exception raised')

下面的代码似乎可以实现@Blckknght通过MRO进行迭代的建议,从而满足您的需要-但忽略了他的回答中指出的限制(这样做可能会产生此限制).它不是很漂亮,但是正如我所说,它似乎至少可以与我创建的模拟pandas库一起使用.

Below is code that seems to do what you need by implementing @Blckknght's suggestion of iterating through the MRO—but ignores the limitations noted in his answer that could arise from doing it that way). It ain't pretty, but as I said, it seems to work with at least the mocked pandas library I created.

import functools
import mockpandas as pandas  # mock the library
import sys
import traceback
import types

def method_wrapper(method):
    @functools.wraps(method)
    def wrapper(*args, **kwargs): # Note: args[0] points to 'self'.
        try:
            print('Calling: {}.{}()... '.format(args[0].__class__.__name__,
                                                method.__name__))
            return method(*args, **kwargs)
        except Exception:
            print('An exception occurred in the wrapped method {}.{}()'.format(
                    args[0].__class__.__name__, method.__name__))
            traceback.print_exc(file=sys.stdout)
            # (Actual code would append that exception info to a list)

    return wrapper

class MetaClass(type):
    def __new__(meta, class_name, base_classes, classDict):
        """ See if any of the base classes were created by with_metaclass() function. """
        marker = None
        for base in base_classes:
            if hasattr(base, '_marker'):
                marker = getattr(base, '_marker')  # remember class name of temp base class
                break  # quit looking

        if class_name == marker:  # temporary base class being created by with_metaclass()?
            return  type.__new__(meta, class_name, base_classes, classDict)

        # Temporarily create an unmodified version of class so it's MRO can be used below.
        TempClass = type.__new__(meta, 'TempClass', base_classes, classDict)

        newClassDict = {}
        for cls in TempClass.mro():
            for attributeName, attribute in cls.__dict__.items():
                if isinstance(attribute, types.FunctionType):
                    # Convert it to a decorated version.
                    attribute = method_wrapper(attribute)
                    newClassDict[attributeName] = attribute

        return type.__new__(meta, class_name, base_classes, newClassDict)

def with_metaclass(meta, classname, bases):
    """ Create a class with the supplied bases and metaclass, that has been tagged with a
        special '_marker' attribute.
    """
    return type.__new__(meta, classname, bases, {'_marker': classname})

class WrappedDataFrame2(
        with_metaclass(MetaClass, 'WrappedDataFrame', (pandas.DataFrame, object))):
    pass

print('Unwrapped pandas.DataFrame().info():')
try:
    pandas.DataFrame().info()
except RuntimeError:
    print('  RuntimeError exception was raised as expected')

print('\n\nWrapped pandas.DataFrame().info():')
WrappedDataFrame2().info()

输出:

Unwrapped pandas.DataFrame().info():
pandas.DataFrame.info() called
  RuntimeError exception was raised as expected


Wrapped pandas.DataFrame().info():
Calling: WrappedDataFrame2.info()...
pandas.DataFrame.info() called
An exception occurred in the wrapped method WrappedDataFrame2.info()
Traceback (most recent call last):
  File "test.py", line 16, in wrapper
    return method(*args, **kwargs)
  File "mockpandas.py", line 9, in info
    raise RuntimeError('Exception raised')
RuntimeError: Exception raised

如上所示,包装类的方法正在使用method_wrapper()装饰版本 .

As the above illustrates, the method_wrapper() decoratored version is being used by methods of the wrapped class.

这篇关于将类的所有可能的方法调用包装在try/except块中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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