MyTetra Share
Делитесь знаниями!
Препроцессинг кода в Python
Время создания: 05.01.2019 16:14
Автор: br0ke
Текстовые метки: python, preprocessing, coding, fun
Раздел: Информационные технологии - Python
Запись: and-semakin/mytetra_data/master/base/1546686879naw8s6p6i5/text.html на raw.githubusercontent.com

Препроцессинг кода в Python

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

Если вы имели дело с фронтенд-разработкой, должно быть слышали о coffee, babel, sass, less, postcss — всем этом многообразии. Еще препроцессор в явном виде есть в C — он обеспечивает работу таких штук, как includedefineifdef.

Надеюсь, у нас такие инструменты до масштабов современного фронтенда не дойдут. Однако, идея достойна внимания. Можно бесконечно расширять язык, вводить шорткаты и DSL на уровне приложения, причем совершенно прозрачно. Забавы ради я попробовал добавить в python простой аналог сишной директивы define.

# coding: сpreprocessor
# define True False

def main():
    if (1 > 0) is True:
        print("true branch")
    else:
        print("else branch")

Вся магия в первой строке. Она указывает интерпретатору на кодировку текущего файла. Чтобы это заработало, зарегистрируем новую кодировку с собственной функцией-декодером. Кроме, собственно, декодинга она заменит все упоминания True на False в исходном файле.


from encodings import utf_8
import encodings
import codecs
import io
def cpreprocessor_decode(input, errors='strict'):
    stream = io.StringIO(bytes(input).decode('utf-8'))
    output = ''.join(stream.readlines())
    output = preprocess(output)
    return output, len(input)
def preprocess(code):
    for line in code.split('\n'):
        line = line.strip()
        if line.startswith('# define'):
            key, val = line[9:].split()
            code = code.replace(key, val)
    return code
def search_function(encoding):
    if encoding == 'cpreprocessor':
        utf8 = encodings.search_function('utf8')
        return codecs.CodecInfo(
            name='cpreprocessor',
            encode=utf8.encode,
            decode=cpreprocessor_decode,
            incrementalencoder=utf8.incrementalencoder,
            incrementaldecoder=utf_8.IncrementalDecoder,
            streamreader=utf_8.StreamReader,
            streamwriter=utf8.streamwriter)
codecs.register(search_function)

view raw cpreprocessor.py  hosted with ❤ by GitHub

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

import cpreprocessor  # noqa
from app import main

if __name__ == '__main__':
    main()  # will print "false branch"

Воспринимайте это как proof of concept. Можно пойти дальше и заменить отступы на фигурные скобки или заменить self на @ прямо как в этом вашем ruby. Но тут уже не обойтись регулярками, придется сделать AST-парсер и может еще поддержку синтаксиса в IDE, чтобы та не сходила с ума.

На одном из последних Moscow.Python был доклад  про PythonQL  — DSL для запросов к SQL, NoSQL который именно так и работает. Там реально свой язык внутри generator expressions.

Ссылки по теме

  • PEP 263 — Defining Python Source Code Encodings
  • A C-style macro preprocessor written in Python
Так же в этом разделе:
 
MyTetra Share v.0.65
Яндекс индекс цитирования