Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Используем вероятности питона на полную. Часть 1

Anna | 16.06.2014 | нет комментариев
На тему резких вероятностей питона на прогре был теснее не один пост. Но мой индивидуальный навыкпоказал, что даже прошаренные в питоне люди не до конца понимают когда дозволено и когда необходимо их применять. Следственно данный пост будет посвящён экстраординарно фактической стороне вопроса.

Мы напишем небольшой уютненький блог применяя Flask и MongoDB. К слову, применять мы будем экзотические для многих функциональные элементы языка, правда не только их. Чего же здесь ненормального? Фактически каждый код, за исключением маленького бутстрапа, будет храниться в БД.

Кстати, одна условность: мы запилим нагой JSON-интерфейс (дозволено назвать REST, но эталонам он вряд ли будет соответствовать всецело). Темплейтами пускай занимается кто-нибудь иной :)

Выходит, поехали.
Создаём болванку Flask-плана.

from flask import Flask

app = Flask(__name__)

@app.route('/')
def goodbye_world():
    return 'Goodbye world!'

if __name__ == '__main__':
    app.run()

Сейчас делаем легкой коннект к БД. А куда ж мы без БД?

from pymongo import MongoClient

db = MongoClient()['rapira']

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

Выходит, а вот и оно:

def execute(code_object, needful_objects, presets={}):
    local_storage = presets
    exec code_object in local_storage
    return dict(
        filter(
            None,
            map(lambda x: (x, local_storage[x])
                    if x in local_storage
                    else None,
                needful_objects)
        )
    )

Предлагаю разобрать данный фрагмент. Вы теснее знаете, что exec может трудиться в произвольных областях видимости. Вы также знаете, что exec может принимать скомпилированный фрагмент кода в качестве первого параметра. Именно так мы его тут и используем: создаём локальную область видимости, и исполняем предварительно скомпилированный кусок кода в ней. Позже этого формируем словарь, в котором содержатся все объекты, которые у нас запросили (список имён надобных объектов предъявляется в итерабельном needful_objects).

Проверим как это работает:

>>> c = compile('''
x=5
y=6
''', '<string>', 'exec')
>>> execute(c, ('x', 'y'))
{'y': 6, 'x': 5}
>>> execute(c, ('x', 'y', 'asdsad'))
{'y': 6, 'x': 5}

Как мы видим, всё в порядке. Едем дальше. Выходит, мы теснее можем выполнить произвольный скомпилированный код, но нам необходимо иметь вероятность комфортно скомпилить произвольный код. До этой функции додумайтесь сами, это не трудно.

Отчего бы нам не объединить compile и execute в одно целое? Потому что скомпиленный код исполняется стремительней, чем нагой текст. Следственно функция compile будет вызываться гораздо реже, чем execute. Такие дела.

Выходит, у нас теснее есть экзекутор, есть коннект с базой и есть интерфейс для связи с внешним миром. Пришло время позаботиться об инструментарии для работы с БД.

def create(collection_name):
    def decorator(func):
        def wr(**kwargs):
            return db[collection_name].save(func(**kwargs))
        return wr
    return decorator

def change(collection_name):
    def decorator(func):
        def wr(**kwargs):
            doc = db[collection_name].find_one(kwargs.pop('_id'))
            changed_doc = func(doc, **kwargs)
            return db[collection_name].save(changed_doc)
        return wr
    return decorator

Вот эти два простеньких декоратора гораздо упростят нам жизнь. К слову, 2-й нам пока не дюже необходим, но пускай будет раз теснее написал.

Сейчас нам необходимо описать свой 1-й тип данных. Пока что это придётся сделать вне базы, но но теснее фактически тем же методом, каким типы данных будут описываться в базе. С тем лишь различием, что к базе мы обращаться не будем.

create_code = """
@db.create('code')
def create(code=str, name=str, type=str, act=str):
    return {'text': text, 'name': name, 'type': type, 'act': act}
"""

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

doc = {'text': create_code, 'name': "create", 'type': "code", 'act': "POST"}

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

compiled_struct = {}

functions = db.db.code.find()
if functions.count() == 0:
    import _code
    t = executor.execute(executor.comp(_code.doc['text']), ('create',), {'db': db})
    t['create'](**_code.doc)
    functions = db.db.code.find()

structure = make_structure(functions)

for function in structure:
    if not function['type'] in compiled_struct:
        compiled_struct[function['type']] = {}
    if not function['name'] in compiled_struct[function['type']]:
        compiled_struct[function['type']][function['name']] = {}
    compiled_struct[function['type']][function['name']][function['method']] = executor.comp(function['text'])

Собственно, вот и оно. Сейчас напишем примитивный роут ко каждому этому добру:

@app.route('/<type_name>/<act_name>', methods=['GET', 'POST'])
def main(type_name, act_name):
    if not type_name in compiled_struct or not act_name in compiled_struct[type_name]:
        return abort(404)
    return jsonify({'response': executor.execute(compiled_struct[type_name][act_name][request.method],
                                                 (act_name,),
                                                 {'db': db})[act_name](**request.form.to_dict(flat=True))
    })

и вуаля, всё работает. Бутстрап готов.
Если отправить POST-запрос по адресу /code/create с параметрами text=«def hello():n return ‘hello yopta’» name=«hello» type=«lol» method=«GET», то позже перезапуска дозволено зайти на /lol/hello и увидеть:

{
  "response": "hello yopta"
}

На этом сегодня и завершим. Ваше домашнее задание — разобрать два последних куска кода и сделать 1-й из них изящней.

В дальнейший раз мы поработаем над инструментарием для создания верификаторов детализированной конструкции данных, сделаем механический апдейт путей при изменении их состояния в базе, прикрутим хэндлеры к способам, сотворим тип «юзеры» и сделаем механизм для проверки прав доступа к тем либо другим функциям.

А, чуть не позабыл. Код вы можете забрать в репозитории.

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

Источник: programmingmaster.ru
Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB