[Python] нужна идея
От: dmz Россия  
Дата: 03.07.07 13:01
Оценка:
Привет, есть такой код:

    @render('task-summary.xsl', debug=True, cache=False)
    @access(Task_Permission, "view_summary")
    def task_summary(self, id):

        def SID():
            return id

        return self._task_summary_inner(id)


task_summary — это метод контроллера, который обрабатывает HTTP запрос.
Фреймворк не важен, наш собственный.

Есть необходимость проверять права доступа в декораторе @access; право доступа к сущности условно
определяются тройкой UID, SID, ACTION.

В access мы передаем класс и action, UID он может взять из http сессии, экшн мы передаем,
а вот как туда втащить SID? SID — это идентификатор ресурса, доступ к которому проверяется, он зависит от контекста.
В данном примере — это параметр GET-запроса id, который передается функции контроллера, но может быть и не так.

Хочется его как-то декларативно описать, откуда декоратору @access брать этот самый SID.
Попытались это сделать путем описания внутренней функции SID(), но она недоступна извне без хака (интроспекции),
да и не функция это должна быть, а замыкание по-сути.

В общем, хочется описать это 1) декларативно, 2) — в контексте функции-обработчика, т.е. не плодить какого-то кода,
специфичного для данного контекста вне этого контекста. Как — вот вопрос.

Как, допустим, добавить атрибут этой функции изнутри нее?





12.12.08 20:36: Перенесено модератором из 'Декларативное программирование' — Кодт
Re: [Python] нужна идея
От: novitk США  
Дата: 03.07.07 15:11
Оценка:
Здравствуйте, dmz, Вы писали:

Можно разделить task_summary явно, пусть возврашает SID и замыкание выполняюшие само действие:
def access(actionid):
    def is_good(actionid, sid):
        return "foo" != sid # replace with whatever

    def decor(func):
        def caller(*argc):
            (sid, g) = func(*argc)
            if is_good(actionid, sid):
                return g(*argc)
            else:
                print "Boo!"
        return caller
    return decor

@access("Whatever")
def task_summary(id):
    def task_summary_inner(*argc):
        print id, "OK"
    return (id, task_summary_inner)

task_summary("foo")
task_summary("bar")
Re[2]: [Python] нужна идея
От: dmz Россия  
Дата: 04.07.07 05:53
Оценка:
N>Можно разделить task_summary явно, пусть возврашает SID и замыкание выполняюшие само действие:
@access("Whatever")
def task_summary(id):
    def task_summary_inner(*argc):
        print id, "OK"
    return (id, task_summary_inner)

task_summary("foo")
task_summary("bar")


нехорошо, таких методов не менее сотни и придется их все переписывать, причем это будет выглядеть
как-то достаточно исскуственно.
Re: [Python] нужна идея
От: . Великобритания  
Дата: 04.07.07 07:57
Оценка:
dmz wrote:

Может можно на keywords перейти.
def access(actionid, sid):
     def is_good(actionid, id):
         return "foo" != id # replace with whatever

     def decor(func):
         def caller(**argc):
             if is_good(actionid, argc[sid]):
                 return func(**argc)
             else:
                 print "Boo!"
         return caller
     return decor

@access("Whatever", "id")
def task_summary(id=None):
     print id, "OK"

task_summary(id="foo")
task_summary(id="bar")
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[2]: [Python] нужна идея
От: dmz Россия  
Дата: 04.07.07 11:13
Оценка:
.>Может можно на keywords перейти.

Понятно, но это не помогает, так как id — контекстно зависимый.
Может поступить как параметр, может быть получен в результате lookup или еще как.

В общем сделал пока тупо, но сомнения остались.
Re[3]: [Python] нужна идея
От: FR  
Дата: 04.07.07 16:20
Оценка:
Здравствуйте, dmz, Вы писали:


.>>Может можно на keywords перейти.


dmz>Понятно, но это не помогает, так как id — контекстно зависимый.

dmz>Может поступить как параметр, может быть получен в результате lookup или еще как.

dmz>В общем сделал пока тупо, но сомнения остались.


Могу предложить еще одно не очень красивое решение. Превратить task_summary в генератор, и тогда будет возможность приостанавливать ход работы функции и получать промежуточные результаты в декораторе:
    @render('task-summary.xsl', debug=True, cache=False)
    @access(Task_Permission, "view_summary")
    def task_summary(self, id):

        def SID():
            return id

        yield SID
   
        yield self._task_summary_inner(id)
Re[3]: [Python] нужна идея
От: . Великобритания  
Дата: 04.07.07 20:26
Оценка:
dmz wrote:

> .>Может можно на keywords перейти.

> Понятно, но это не помогает, так как id — контекстно зависимый.
> Может поступить как параметр, может быть получен в результате lookup или
> еще как.
>
> В общем сделал пока тупо, но сомнения остались.
Может тогда плюнуть на все эти @-прибамбасы и писать в стиле ООП с классами?
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[3]: [Python] нужна идея
От: . Великобритания  
Дата: 04.07.07 20:58
Оценка:
dmz wrote:
> Понятно, но это не помогает, так как id — контекстно зависимый.
> Может поступить как параметр, может быть получен в результате lookup или
> еще как.
>
> В общем сделал пока тупо, но сомнения остались.
Можно вот так, попроще:
def access(actionid):
    def is_good(id):
        if("foo" != id):
            raise Exception("Babah!")

    def decor(func):
        def caller(*argc):
            func(is_good, *argc)
        return caller
    return decor

@access("Whatever")
def task_summary(is_good, id):
    is_good(id)
    print id, "OK"

task_summary("foo")
task_summary("bar")

Однако, не могу придумать как обязать проверять is_good. Хотя это не обязательно, вообще говоря, и предоставляет некую
гибкость.
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: [Python] продолжение банкета
От: dmz Россия  
Дата: 13.07.07 07:30
Оценка:
В общем, ничего хорошего не придумалось, а тем временем появилось
уже два места, где мне требуется похожий функционал.

Попробую формализовать задачу:

Есть некоторый метод, который вызывается по некоторому событию (обрабатывает HTTP запрос,
если это имеет значение). Есть набор декораторов, которые добавляют стандартное поведение:

сформировать и выплюнуть xml для набора объектов, которые этот метод возвращает, наложить на него
xsl и отдать в middleware ( декоратор render), проверить права доступа — декоратор access,
закэшировать — декоратор cache.


@render()
@access()
@cache()
def handle_some_request(self, *params):
   pass


декоратор render пока обходится без контекста — ему достаточно того, что ему передают.

декораторы access и cache — требуют некоторые идентификаторы, которые, вообще говоря, динамические и
зависят от контекста: пример — если мы кэшируем запрос, то ключом является какая-то производная от параметров GET запроса и состояния сессии — что конкретно, и как с этим работать — знает сам метод handle_some_request.

Вопрос все-тот же: как это знание обособить, причем строго внутри метода-обработчика и довести до сведения декоратора.
хочется как-то использовать внутренние функции — потому что они видят параметры, передаваемые методу, но их нельзя
выполнить вне функций, внутри которых они объявлены. Хочется при этом минимально менять логику обработчиков запросов — их весьма много, и это будет уже нетривиальная вещь, которую придется описывать, доводить до сведения разработчиков и т.п.

Чо делать? В принципе, я согласен написать какой-то достаточно злобный __new__, который будет путем интроспекции смотреть, нет ли внутри обработчиков запросов внутренних функций, делать из них лямбды, запоминать их и вызывать в нужном контексте — но не очень представляю, как это сделать, и мучит подозрение, что можно сделать как-то более красиво.

Давайте вместе подумаем — по-моему, будет прикольная тема, если получится.
Re[5]: [Python] продолжение банкета
От: Аноним  
Дата: 14.07.07 21:30
Оценка:
dmz>декораторы access и cache — требуют некоторые идентификаторы, которые, вообще говоря, динамические и
dmz>зависят от контекста: пример — если мы кэшируем запрос, то ключом является какая-то производная от параметров GET запроса и состояния сессии — что конкретно, и как с этим работать — знает сам метод handle_some_request.

dmz>Вопрос все-тот же: как это знание обособить, причем строго внутри метода-обработчика и довести до сведения декоратора.

dmz>хочется как-то использовать внутренние функции — потому что они видят параметры, передаваемые методу, но их нельзя
dmz>выполнить вне функций, внутри которых они объявлены. Хочется при этом минимально менять логику обработчиков запросов — их весьма много, и это будет уже нетривиальная вещь, которую придется описывать, доводить до сведения разработчиков и т.п.

dmz>Чо делать? В принципе, я согласен написать какой-то достаточно злобный __new__, который будет путем интроспекции смотреть, нет ли внутри обработчиков запросов внутренних функций, делать из них лямбды, запоминать их и вызывать в нужном контексте — но не очень представляю, как это сделать, и мучит подозрение, что можно сделать как-то более красиво.


А если поступить ровно наоборот?
Передавать из декоратора колбэк, получит значение контекстной переменной?
Примерно так:
def access(f):
  def wrap(*arg, **kwd):
    sid = []
    def chask_sid(qsid):
      return len(qsid) == 7
    kwd['access'] = chask_sid
    return f(*arg, **kwd)
  return wrap

@access
def fff(arg, access=None):
  '''this fff func'''
  if access:
    if not access(arg + 'sid'):
      print 'opa'
      return
  print 'arg

fff('Ura!')


Ну а кроме того, можно лямбду загнать в параметры по умолчанию, а потом её добыть.
Можно в самой функции установить себе атрибут и его из обвязки получить после первого прогона, который можно и искуственно сделать.
Ещё можно её как текст в док-стринг запихать... Но это уже совсем изврат.
Re[5]: [Python] продолжение банкета
От: . Великобритания  
Дата: 18.07.07 11:10
Оценка:
dmz wrote:

> В общем, ничего хорошего не придумалось, а тем временем появилось

> уже два места, где мне требуется похожий функционал.
Я тебе столько вариантов привёл, ты скажи чем не устраивает?
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[6]: [Python] продолжение банкета
От: dmz Россия  
Дата: 18.07.07 16:44
Оценка:
.>Я тебе столько вариантов привёл, ты скажи чем не устраивает?

ну с кейвордами просто не катит, там достаточно сложные вычисления, про второй
вариант думаю, но тоже как-то проблемно он ложится.

Блин, как бы просто решался вопрос, если бы можно было вызвать вложенную
функцию из декоратора, или при описании функции выставить у нее атрибут.

Почему этого никак не сделать...
Re[7]: [Python] продолжение банкета
От: . Великобритания  
Дата: 19.07.07 08:50
Оценка:
dmz wrote:

> Блин, как бы просто решался вопрос, если бы можно было вызвать вложенную

> функцию из декоратора
Всё вложенное — это дело функции, пока сама не отдаст — никто не может отобрать.

> , или при описании функции выставить у нее атрибут.

Атрибут у функции это как? Ведь функция существует глобально. Нереентерабельно и потоко-небезопасно получится.

> Почему этого никак не сделать...

По-моему тоже. Хотя я питон не знаю, учебник только полистал однажды.
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[8]: [Python] продолжение банкета
От: dmz Россия  
Дата: 19.07.07 12:34
Оценка:
>> , или при описании функции выставить у нее атрибут.
.>Атрибут у функции это как? Ведь функция существует глобально. Нереентерабельно и потоко-небезопасно получится.

Ну и что? Функция — это такой же объект, как все остальное. Соответственно, всегда можно сделать вот так вот:
def a():
    print 'B'

setattr(a, 'test', 42)

print a.test


TurboGears, в частности, так делает.
Re: [Python] нужна идея
От: no4  
Дата: 19.07.07 13:24
Оценка: 1 (1)
Здравствуйте, dmz, Вы писали:

dmz>В access мы передаем класс и action


dmz>В общем, хочется описать это 1) декларативно


Вопрос. А почему просто первой строчкой функции не написать?

assertAccess(Task_Permission, "view_summary", SID)

Еще вариант, сделать второй декоратор, который будет анализировать не отдекорирована ли уже фнукиция первым декоратором, связываться с ним и объяснять ему откуда взять sid

или наоборот
@sid(fromParameter = 'SID')
@access(Task_Permission, "view_summary", SID)


или

@sid(byCallable = getMySid)
@access(Task_Permission, "view_summary", SID)

и т.д.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[9]: [Python] продолжение банкета
От: . Великобритания  
Дата: 19.07.07 13:32
Оценка:
dmz wrote:

> .>Атрибут у функции это как? Ведь функция существует глобально.

> Нереентерабельно и потоко-небезопасно получится.
>
> Ну и что? Функция — это такой же объект, как все остальное.
> Соответственно, всегда можно сделать вот так вот:
Я имею в виду, что вот ты для неё установил из декоратора 42, а потом во время работы этой функции кто-нибудь другой эту
же функцию вызвал и декоратор установил в 38, то 42 просто затрётся, и функция продолжит работать с неверным значением.
Значение получается глобальным, не зависящим от контекста, стека.

> TurboGears, в частности, так делает.

Что именно делает?.. (я никогда не юзал его)
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[10]: [Python] продолжение банкета
От: dmz Россия  
Дата: 19.07.07 13:58
Оценка:
.>же функцию вызвал и декоратор установил в 38, то 42 просто затрётся, и функция продолжит работать с неверным значением.

Угу. С другой стороны, в этот атрибут можно было бы засунуть саму функцию вычисления SID,
а вот параметры передавать ей из контекста — он-то декоратору известен.

Таким образом, все корректно бы работало.
Re[11]: [Python] продолжение банкета
От: . Великобритания  
Дата: 19.07.07 16:26
Оценка: +1
dmz wrote:

> .>же функцию вызвал и декоратор установил в 38, то 42 просто затрётся, и

> функция продолжит работать с неверным значением.
>
> Угу. С другой стороны, в этот атрибут можно было бы засунуть саму
> функцию вычисления SID,
> а вот параметры передавать ей из контекста — он-то декоратору известен.
Функцию-шмункцию... какая разница? Разве можно гарантировать, что каждый раз будет подсовываться одна и та же функция?
Так что с идеологической т.з., с т.з. дизайна языка это паршиво, декораторы не хак таки, чтобы херачить что угодно, а
типа АОП прибамбас.
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: [Python] нужна идея
От: eugals Россия  
Дата: 19.07.07 18:20
Оценка: 6 (1)
Здравствуйте, dmz, Вы писали:

dmz>Хочется его как-то декларативно описать, откуда декоратору @access брать этот самый SID.

dmz>Попытались это сделать путем описания внутренней функции SID(), но она недоступна извне без хака (интроспекции)

Внутрення функкция создается динамически в момент вызова task_summary. Тут тебе никакая интроспекция не поможет. Чтобы получить нормальный SID нужно по-честному вызвать внешнюю функцию. Без вариантов.


dmz>да и не функция это должна быть, а замыкание по-сути.

Ну так и возвращай замыкание (кортеж из двух функций)!
def test(self, id):
    def SID():
        return id
    return SID, lambda: self._task_summary_inner(id)
    
# вызов    
sid, f = obj.test(id)
if sid() == 42:
    f()


Ещё вариант с замыканием:
def test(self, id):
    def SID():
        return id
    yield SID
    yield self._task_summary_inner(id)


dmz>В общем, хочется описать это 1) декларативно, 2) — в контексте функции-обработчика, т.е. не плодить какого-то кода,

dmz>специфичного для данного контекста вне этого контекста. Как — вот вопрос.
Вообще, советую обдумать идею просто вставить assert, как уже выше предалагалось.
Ислючения в питоне бросаются и ловятся достаточно эффективно
... << RSDN@Home 1.2.0 alpha rev. 679>>
Re[3]: [Python] нужна идея
От: achmed Удмуртия https://www.linkedin.com/in/nail-achmedzhanov-9907188/
Дата: 04.12.08 07:42
Оценка:
Здравствуйте, dmz, Вы писали:


.>>Может можно на keywords перейти.


dmz>Понятно, но это не помогает, так как id — контекстно зависимый.

dmz>Может поступить как параметр, может быть получен в результате lookup или еще как.

dmz>В общем сделал пока тупо, но сомнения остались.



Вопрос уже неактуален скорее всего ... но давно не было тем про python


Можно описывать функцию получения id в комментарии метода

    @render('task-summary.xsl', debug=True, cache=False)
    @access(Task_Permission, "view_summary")
    def task_summary(self, id):
    """
    uid:
        return id
    """
        return "the summary ..."
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.