Обернуть каждую функцию каждого класса модуля - PullRequest
0 голосов
/ 22 апреля 2020

Цель

Обернуть каждую функцию каждого класса модуля gspread.

Я знаю, что на эту тему существует бесчисленное количество сообщений, и большинство единодушно рекомендуют использовать декораторы. Я не слишком знаком с декораторами и чувствую, что такой подход не такой цельный, как я надеялся. возможно, я не правильно понял.

Но я нашел этот ответ , который "чувствовал", что я ищу.

(плохо) Попытка

import gspread
from oauth2client.service_account import ServiceAccountCredentials
import os
import inspect

class GoogleSheetAPI:
    def __init__(self):
        f = os.path.join(os.path.dirname(__file__), 'credentials.json')
        os.environ.setdefault('GOOGLE_APPLICATION_CREDENTIALS', f)
        scope = ['https://spreadsheets.google.com/feeds',
                 'https://www.googleapis.com/auth/drive']
        credentials = ServiceAccountCredentials.from_json_keyfile_name(f, scope)
        self.client = gspread.authorize(credentials)
        self.client.login()

def SafeCall(f):
    try:
        print 'before call'
        f()
        print 'after call'
    except:
        print 'exception caught'
        return None

for class_name, c in inspect.getmembers(gspread, inspect.isclass):
    for method_name, f in inspect.getmembers(c, inspect.ismethod):            
        setattr(c, f, SafeCall(f)) # TypeError: attribute name must be string, not 'instancemethod'

g = GoogleSheetAPI()
spreadsheet = g.client.open_by_key('<ID>') # calls a function in gspread.Client
worksheet = spreadsheet.get_worksheet(0)  # calls a function in gspread.Spreadsheet
worksheet.add_rows(['key','value'])  # calls a function in gspread.Worksheet

Примечания

  1. Когда я использую слово " seamless ", я имею в виду, что, учитывая, что в моем коде много вызовов многих функций gspread, я хочу изменить как можно меньше. Использование inspect / setattr кажется идеальным / бесшовным трюком.

1 Ответ

1 голос
/ 22 апреля 2020

Есть три очевидных проблемы с вашим кодом на самом деле.

Первая - это TypeError - которую легко решить FWIW: поскольку сообщение об ошибке (повышается с помощью setattr() состояний), имя атрибута должно быть string, а не 'instancemethod' ". И вы действительно пытаетесь использовать f (сам метод) вместо method_name. Здесь вам, конечно, нужно:

setattr(c, method_name, SafeCall(f))

Вторая проблема в том, что ваш SafeCall «декоратор» НЕ является декоратором. Декоратор (ну, по крайней мере, тот тип декоратора, который вы хотите здесь по крайней мере) возвращает функцию, которая оборачивает исходную, ваша текущая реализация просто вызывает исходную функцию. На самом деле, это почти то, что на самом деле должен возвращать SafeCall. Пример правильного декоратора:

def decorator(func):
    def wrapper(*args, **kw):
        print("before calling {}".format(func))
        result = func(*args, **kw)
        print("after calling {}".format(func))
        return result
    return wrapper

И, наконец, третья очевидная проблема здесь:

except:
    print 'exception caught'
    return None

Вы, конечно, не хотите этого. Этот

1 / будет перехватывать абсолютно все (включая SysExit, то есть то, что Python поднимает при вызовах sys.exit() и StopIteration, как сигналы итераторов они исчерпаны),

2 / откажитесь от всей очень полезной информации об отладке - сделав невозможной диагностику того, что на самом деле пошло не так

3 / верните что-то, что может быть просто непригодным для использования, так что вы получите чтобы проверить возвращаемое значение каждого вызова метода, и так как вы не будете знать, что пошло не так, вы не сможете решить проблему иначе, чем печатать «упс, что-то пошло не так, но не спрашивайте меня, что ни где, ни почему »и выход из программы, что определенно не лучше, чем позволить распространению исключения - программа выполнит sh в обоих случаях, но, по крайней мере, если вы оставите это исключение, у вас будут некоторые намеки на причину проблемы.

4 / или, что еще хуже, возвращает допустимое возвращаемое значение для метода (да, довольно много методов предназначены для изменения состояния и возврата None), так что вы даже не узнаете, что что-то пошло не так и счастливо продолжить выполнение - это верный способ получить неверный результат и поврежденные данные.

5 / не говоря уже о том, что методы, которые вы декорируете таким образом, очень вероятно вызывают друг друга и используют (ожидаемые) исключения внутренне (с правильной обработкой исключений), так что вы на самом деле внесение ошибок в работающую (или в основном работающую) библиотеку.

IOW, это, вероятно, худший антипаттерн, о котором вы когда-либо могли подумать ...

...