Top Banner
Осторожно , DSL! Цыганов Иван Positive Technologies
66

PyconRu 2016. Осторожно, DSL!

Apr 16, 2017

Download

Technology

Ivan Tsyganov
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: PyconRu 2016. Осторожно, DSL!

Осторожно, DSL!

Цыганов Иван Positive Technologies

Page 2: PyconRu 2016. Осторожно, DSL!

server { location / { proxy_pass http://localhost:8080/; }

location ~ \.(gif|jpg|png)$ { root /data/images; }}

Page 3: PyconRu 2016. Осторожно, DSL!

version: '2'services: db: image: postgres web: build: . command: python manage.py runserver 0.0.0.0:8000 volumes: - .:/code ports: - "8000:8000" depends_on: - db

DOCKER COMPOSE

Page 4: PyconRu 2016. Осторожно, DSL!

LOGROTATE

compress

"/var/log/httpd/access.log" /var/log/httpd/error.log { rotate 5 size=100k sharedscripts nocompress postrotate /sbin/killall -HUP httpd endscript}

/var/log/messages { rotate 5 weekly postrotate /sbin/killall -HUP syslogd endscript}

Page 5: PyconRu 2016. Осторожно, DSL!

compress

"/var/log/httpd/access.log" /var/log/httpd/error.log { rotate 5 size=100k sharedscripts nocompress postrotate /sbin/killall -HUP httpd endscript}

Page 6: PyconRu 2016. Осторожно, DSL!

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 102400, 'compress': False, 'sharedscripts': True, 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

compress

"/var/log/httpd/access.log" /var/log/httpd/error.log { rotate 5 size=100k sharedscripts nocompress postrotate /sbin/killall -HUP httpd endscript}

Page 7: PyconRu 2016. Осторожно, DSL!

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 102400, 'compress': False, 'sharedscripts': True, 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

compress

"/var/log/httpd/access.log" /var/log/httpd/error.log { rotate 5 size=100k sharedscripts nocompress postrotate /sbin/killall -HUP httpd endscript}

size=100k

'size': 102400,

Page 8: PyconRu 2016. Осторожно, DSL!

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 102400, 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

'size': '100 KB',

Page 9: PyconRu 2016. Осторожно, DSL!

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 102400, 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

'size': '100 KB',

def get_size(self, value): size_value, _, size_unit = value.partition(' ') return self.units_to_bytes(size_value, size_unit)

Page 10: PyconRu 2016. Осторожно, DSL!

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 102400, 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

'size': '1MB + 100KB',

Page 11: PyconRu 2016. Осторожно, DSL!

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 102400, 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

'size': '1MB + 100KB',

parse_size = re.compile( pattern=r''' (?P<Value>\d+)\s* (?P<ValueUnit>KB|MB|GB)?\s* ((?P<Operator>[-+/*])\s*)? ( (?P<Delta>\d+)\s* (?P<DeltaUnit>KB|MB|GB)? )? ''', flags=re.IGNORECASE | re.VERBOSE).search

Page 12: PyconRu 2016. Осторожно, DSL!

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 102400, 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

'size': ‘100MB - 2 * (1MB + 100KB)’,

Page 13: PyconRu 2016. Осторожно, DSL!

Some people, when confronted with a problem, think "I know, I’ll use regular expressions." Now they have two problems.

— Jamie Zawinski

Page 14: PyconRu 2016. Осторожно, DSL!

Осторожно, DSL!

Page 15: PyconRu 2016. Осторожно, DSL!

– Мартин Фаулер

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

Page 16: PyconRu 2016. Осторожно, DSL!

✤ SQL✤ REGEXP✤ TeX/LaTeX

Виды DSL

DSL

ВнутренниеВнешние✤ PonyORM✤ WTForm✤ Django models

Page 17: PyconRu 2016. Осторожно, DSL!

Что будет дальше?

✤ Внутренние DSL

✤ Внешние DSL

✤ Инструменты для создания анализаторов

Page 18: PyconRu 2016. Осторожно, DSL!

Внутренние DSL

Все возможности базового языка

Привычный синтаксис

Ограничен базовым языком

'size': 100MB - 2 * (1MB - 100KB)

Page 19: PyconRu 2016. Осторожно, DSL!

GB = lambda x: x*2**30 MB = lambda x: x*2**20 KB = lambda x: x*2**10

GB = 2**30 MB = 2**20 KB = 2**10

config = { (‘/var/log/nginx/access.log',): { 'size': 100*MB - 2 * (1*MB - 100*KB) }, (‘/var/log/nginx/error.log',): { 'size': 100*MB - 2 * (1*MB - 100*KB) } }

'size': MB(100) - 2*(MB(1) + KB(100))

'size': 100*MB - 2 * (1*MB - 100*KB)

Page 20: PyconRu 2016. Осторожно, DSL!

Внешние DSL

Сами выбираем синтаксис

Необходимо разрабатывать анализаторы

Page 21: PyconRu 2016. Осторожно, DSL!

compress: truerules:- files: [/var/log/nginx/access.log, /var/log/nginx/error.log] rotate: 5 size: 100MB - 2 * (1MB + 100KB) postrotate: [/sbin/killall -HUP syslogd]

config = { 'compress': True, ('/var/log/nginx/access.log', '/var/log/nginx/error.log'): { 'rotate': 5, 'size': 100*MB - 2 * (1*MB - 100*KB) 'postrotate': [ '/sbin/killall -HUP syslogd' ] }}

Page 22: PyconRu 2016. Осторожно, DSL!

Python Lex-Yacc (PLY)

Page 23: PyconRu 2016. Осторожно, DSL!

ply.lex

ply.yacc

ASTRunCode

source

Page 24: PyconRu 2016. Осторожно, DSL!

ply.lex

Type ValuePLUS + MINUS - MUL *DIV /UNIT GB|MB|KB|BDIGIT \d+LPAREN (RPAREN )

Page 25: PyconRu 2016. Осторожно, DSL!

ply.lex 2 * (1KB + 1KB)

DIGIT = 2

MUL = *

LPAREN = (

DIGIT = 1

UNIT = KB

PLUS = +

DIGIT = 1

UNIT = KB

RPAREN = )

Page 26: PyconRu 2016. Осторожно, DSL!

ply.lex 1KB1KB-MB

DIGIT = 1

DIGIT = 1

UNIT = KB

MINUS = -

UNIT = MB

UNIT = KB

Page 27: PyconRu 2016. Осторожно, DSL!

ply.yacc

expression : expression PLUS expression | expression MINUS expression | expression MUL expression | expression DIV expression

expression : LPAREN expression RPAREN

expression : DIGIT UNIT

expression : DIGIT

Page 28: PyconRu 2016. Осторожно, DSL!

ply.yacc

def p_expression(self, p): ''' expression : expression PLUS expression | expression MINUS expression | expression MUL expression | expression DIV expression ''' if p[2] == '+': p[0] = p[1] + p[3] if p[2] == '-': p[0] = p[1] - p[3] if p[2] == '*': p[0] = p[1] * p[3] if p[2] == '/': p[0] = p[1] / p[3]

Page 29: PyconRu 2016. Осторожно, DSL!

ply.yacc

precedence = ( ('left', 'PLUS', 'MINUS'), ('left', 'MUL', 'DIV'), )

def p_expression(self, p): ''' expression : expression PLUS expression | expression MINUS expression | expression MUL expression | expression DIV expression ''' if p[2] == '+': p[0] = p[1] + p[3] if p[2] == '-': p[0] = p[1] - p[3] if p[2] == '*': p[0] = p[1] * p[3] if p[2] == '/': p[0] = p[1] / p[3]

Page 30: PyconRu 2016. Осторожно, DSL!

PLY

Гибкость

Отладка

Обработка ошибок

Понятный код библиотеки

Высокий порог входа

Многословный

Page 31: PyconRu 2016. Осторожно, DSL!

funcparserlib

Page 32: PyconRu 2016. Осторожно, DSL!

def tokenize(str): specs = [ ('Spaces', (r'[ \s\t\r\n]+',)), ('PLUS', (r'\+',)), ('MINUS', (r'-',)), ('MUL', (r'\*',)), ('DIV', (r'/',)), ('UNIT', (r'GB|MB|KB|B',)), ('DIGIT', (r'\d+',)), ('LPAREN', (r'\(',)), ('RPAREN', (r'\)',)), ] return list(filter( lambda t: t.type != 'Spaces', (t for t in make_tokenizer(specs)(str)) ))

funcparserlib

Page 33: PyconRu 2016. Осторожно, DSL!

number = value_of('DIGIT') >> intunit = number + value_of('UNIT') >> to_bytesoperand = unit | numbermakeop = lambda s, f: skip_token(s) >> const(f)add = makeop('PLUS', operator.add)sub = makeop('MINUS', operator.sub)mul = makeop('MUL', operator.mul)div = makeop('DIV', operator.floordiv) mul_op = mul | divadd_op = add | sub

funcparserlib

Page 34: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

Page 35: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

2+2*2

Page 36: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

2+2*2

maybe(expr)

Page 37: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

2+2*2

term

Page 38: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

2+2*2

primary

Page 39: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

2+2*22

operand

Page 40: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

2+2*22

many(mul_op + primary)

Page 41: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

many(add_op + term)

2+2*22+

Page 42: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

primary

2+2*22+

Page 43: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

operand

2+2*22+2

Page 44: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

2+2*22+2*

many(mul_op + primary)

Page 45: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

operand

2+2*22+2*2

Page 46: PyconRu 2016. Осторожно, DSL!

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

eval_expr

2+2*22+2*2

Page 47: PyconRu 2016. Осторожно, DSL!

2+4

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

eval_expr

Page 48: PyconRu 2016. Осторожно, DSL!

6

primary = with_forward_decls( lambda: operand | (skip_token('LPAREN') + expr + skip_token('RPAREN')) ) term = primary + many(mul_op + primary) >> eval_exprexpr = term + many(add_op + term) >> eval_exprparser = maybe(expr)

funcparserlib

maybe(expr)

Page 49: PyconRu 2016. Осторожно, DSL!

funcparserlib

Компактный

Гибкий

Для любителей функционального программирования :)

Многое приходится делать руками

Для любителей функционального программирования :)

Page 50: PyconRu 2016. Осторожно, DSL!

pyparsing

Page 51: PyconRu 2016. Осторожно, DSL!

pyparsing

plusop = oneOf('+ -') multop = oneOf('* /') digit = Word(nums)unit = digit + oneOf('GB MB KB’) operand = unit | digitparser = Forward()primary = operand | Literal('(') + parser + Literal(')') term = (primary + ZeroOrMore(multop + primary)).setParseAction(eval_expr)expr = (term + ZeroOrMore(plusop + term)).setParseAction(eval_expr)parser << Optional(expr)

Page 52: PyconRu 2016. Осторожно, DSL!

pyparsing

plusop = oneOf('+ -') multop = oneOf('* /')

digit = Word(nums)unit = digit + oneOf('GB MB KB’)

operand = unit | digit parser = operatorPrecedence( operand, [ (multop, 2, opAssoc.LEFT, calculate), (plusop, 2, opAssoc.LEFT, calculate) ])

Page 53: PyconRu 2016. Осторожно, DSL!

pyparsing

Понятный код

Базовые компоненты

Свои компоненты

Документация

Отладка

Page 54: PyconRu 2016. Осторожно, DSL!

А что насчет быстродействия?

Page 55: PyconRu 2016. Осторожно, DSL!

Скорость

Page 56: PyconRu 2016. Осторожно, DSL!

Скорость

Page 57: PyconRu 2016. Осторожно, DSL!

А что же пользователи?

Page 58: PyconRu 2016. Осторожно, DSL!

Сообщения об ошибках

Traceback (most recent call last): File "size_parser/on_ply.py", line 100, in parse return parser.parse(string) File "size_parser/on_ply.py", line 94, in parse p = self._parser.parse(data, debug=self._debug) File ".env/site-packages/ply/yacc.py", line 331, in parse return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc) File ".env/site-packages/ply/yacc.py", line 1049, in parseopt_notrack lookahead = get_token() # Get the next token File ".env/site-packages/ply/lex.py", line 396, in token raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos], lexpos), lexdata[lexpos:]) ply.lex.LexError: Illegal character '_' at index 5

Page 59: PyconRu 2016. Осторожно, DSL!

Traceback (most recent call last): File "size_parser/on_ply.py", line 100, in parse return parser.parse(string) File "size_parser/on_ply.py", line 94, in parse p = self._parser.parse(data, debug=self._debug) File ".env/site-packages/ply/yacc.py", line 331, in parse return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc) File ".env/site-packages/ply/yacc.py", line 1049, in parseopt_notrack lookahead = get_token() # Get the next token File ".env/site-packages/ply/lex.py", line 396, in token raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos], lexpos), lexdata[lexpos:]) ply.lex.LexError: Illegal character '_' at index 5

Сообщения об ошибках

Page 60: PyconRu 2016. Осторожно, DSL!

ply

def t_error(self, t): raise ParserException(t, self.source)def p_error(self, p): raise ParserException(p, self.source)

>>> parse(‘1MB+_GB-100KB’)Unexpected "_GB-100KB" at position 4:1MB+_GB-100KB ^^^

Page 61: PyconRu 2016. Осторожно, DSL!

Так что же выбрать?

Page 62: PyconRu 2016. Осторожно, DSL!

Что же выбрать?

pyparsing✤ Хочу легко все описать✤ Быстродействие не главное

Page 63: PyconRu 2016. Осторожно, DSL!

Что же выбрать?

funcparserlib

pyparsing✤ Хочу легко все описать✤ Быстродействие не главное

✤ Люблю функциональное программирование

✤ Быстродействие не главное

Page 64: PyconRu 2016. Осторожно, DSL!

Что же выбрать?

PLY

funcparserlib

pyparsing

✤ Хочу как в учебнике✤ Скорость работы - главное!

✤ Хочу легко все описать✤ Быстродействие не главное

✤ Люблю функциональное программирование

✤ Быстродействие не главное

Page 65: PyconRu 2016. Осторожно, DSL!

И что в итоге?

✤ Для простых задач попробуйте:

✤ Средства самого языка

✤ Регулярные выражения

✤ Если задача сложная:

✤ Внутренние DSL

✤ Yaml, Json, XML, …

✤ Внешние DSL

Page 66: PyconRu 2016. Осторожно, DSL!

Спасибо за внимание!Вопросы?

http://tsyganov-ivan.com/