пятница, 15 августа 2014 г.

exrex генератор данных по регулярным выражениям

Всем привет!

На просторах гитлаба наткнулся на небольшую библиотеку exrex, позволяющую генерировать данные по регулярным выражениям.

API крайне простой
count - возвращет количество возможных вариаций
parse - парсер регулярных выражений
getone - возвращает псевдо-рандомное значение
generate - возвражает генератор со всеми вариантами

Примеры использования

На всякий замечу, что у вас по некоторым примером вывод может быть другим

>>> print exrex.getone('[A-Z][a-z]{1,10} [A-Z][a-z]{1,10}')
Boris Yeltsin

>>> print exrex.getone('\+[0-9]\([0-9]{3}\) [0-9]{3}-[0-9]{2}-[0-9]{2}')
+5(777) 790-68-69

>>> print [r for r in exrex.generate('|'.join(str(i) for i in range(5)))]
[u'0', u'1', u'2', u'3', u'4']

>>> exrex.count('[0-9]{1}')
10

>>> print exrex.parse('[0-9]{1,100}')
[('max_repeat', (1, 100, [('in', [('range', (48, 57))])]))]

Есть также возможность запуска из консоли

>>> python -m exrex -r [0-9]{2}
31
Возвращает псевдо-рандомное число из 2 цифр

За более подробной информацией в ссылки.


Ссылки

https://github.com/asciimoo/exrex
http://exrex.readthedocs.org/

пятница, 11 июля 2014 г.

python кодировка исходников

Всем привет!

Я думаю каждый из нас ловил что-то подобное:

 File "python_coding_style.py", line 2
SyntaxError: Non-ASCII character '\xd0' in file python_coding_style.py on line 3, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details

Проблема в том что интерпретатор не знает в какой кодировке у нас файлы исходников. Проблема решается явным указанием их кодировки:
# coding: utf8

Чаще на практике встречаются следующие варианты

# -*- coding: utf8 -*-
# coding: utf8
# coding=utf8

В PEP-0263 декларируется, что для определения кодировки исходников годится все что подпадет под регулярку coding[:=]\s*([-\w.]+)
Например, #This Python file uses the following encoding: utf-8 !!!

Ссылки

http://legacy.python.org/dev/peps/pep-0263/

четверг, 10 июля 2014 г.

Часть 4. py.test debug

Мы уже рассмотрели основные принципы написание тестов, но один важный момент упустили, а именно дебаг тестов.


Использование print

Часто люди пользующие питон дебажат свои скрипты с помощью использования принтов. По умолчанияю pytest пишет весь stout и stderr. В предыдущем посте эта тема рассматривалась. Для отключения этой особенности нужно использовать параметр --capture или маску -s. 
Посмотрим на небольшой (надуманный) пример:

# use_debug.py
@py.test.mark.parametrize("v", range(1, 5))
def test_parity(v):
    print("Value: %d" % v)
    assert v % 2

>>>py.test use_debug.py -s -v --tb line

============================= test session starts =============================
platform win32 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- C:\Python27\python.exe
plugins: teamcity-messages, rerunfailures
collected 4 items

use_debug.py:40: test_parity[1] Value: 1
PASSED
use_debug.py:40: test_parity[2] Value: 2
FAILED
use_debug.py:40: test_parity[3] Value: 3
PASSED
use_debug.py:40: test_parity[4] Value: 4
FAILED

================================== FAILURES ===================================
c:\JOB\3_TestSolution\Blog\use_debug.py:43: assert (2 % 2)
c:\JOB\3_TestSolution\Blog\use_debug.py:43: assert (4 % 2)
===================== 2 failed, 2 passed in 0.03 seconds ======================

Как видим в консоли напечатались входные значения. 



Автовключение дебага

Для этого предусмотрен флаг --pdb. Если тест пофелился, то py.test прогонит его на тех же данных, но уже с включенным питоновским дебагером pdb.

>>>py.test use_debug.py -s -v --tb line --pdb

platform win32 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- C:\Python27\python.exe
plugins: teamcity-messages, rerunfailures
collected 4 items

use_debug.py:64: test_parity[1]
>>>>>>>>>>>>>>>>>>> PDB set_trace (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>
> c:\job\3_testsolution\blog\use_debug.py(67)test_parity()
-> assert v % 2
(Pdb)

Брейкпоинты


Из коробки py.test предоставляет возможность в коде явно указать брекпоинт.

# use_debug.py
@py.test.mark.parametrize("v", range(1, 5))
def test_parity(v):
    py.test.set_trace()
    assert v % 2

При каджом вызове тестовой функции test_parity в месте устанвки брейкпоинта py.test будет запускать pdb

>>>py.test use_debug.py -s -v --tb line

use_debug.py:62: test_v[1]
>>>>>>>>>>>>>>>>>>> PDB set_trace (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>
> c:\job\3_testsolution\blog\use_debug.py(65)test_v()
-> assert v % 2
(Pdb)


Ссылки

http://pytest.org/latest/usage.html#dropping-to-pdb-python-debugger-on-failures
http://pytest.org/latest/usage.html#setting-a-breakpoint-aka-set-trace

четверг, 26 июня 2014 г.

python copy/deepcopy

Всем привет!

Продолжаем ломать копья об питоновскую парадигму изменяемых/неизменяемых объектов.
Разберем поверхностное и глубокое копирование словаря.

>>>s = {"str": "1", "list": [1, 2], "dict": {"first": "test1"}}
>>>f = s
>>>f['second'] = '2'

>>>print s

Надеюсь понятно какой вывод будет в консоли. Если нет, то смотрим ссылки.
Для того чтобы не ссылаться на один объект, нужно скопировать словарь s.

Но метод copy обладает неприятной особенностью, он поверхностно копирует словарь.
Смотрим на пример (s из предыдущего примера).

>>>f = s.copy()
>>>f['second'] = '3'
>>>f['dict']['third'] = '3'
>>>print s

{'dict': {'third': '3', 'first': 'test1'}, 'list': [1, 2], 'str': '1'}

Проблема решается использованием модуля copy.

>>>import copy

>>>f = copy.deepcopy(s)
>>>f['second'] = '3'
>>>f['dict']['third'] = '3'
>>>print s

>>>print f

Как работает copy.deepcopy

Начнем с аргументов

deepcopy(x, memo=None, _nil=[])

x - копируемый объект. Копируемым объектом может быть любой стандартный тип данных, а также кастомных объектов.
memo - словарь, в котором будут сопоставлены id созданных объектов и их значения. По-умолчанию None(если не понятно почему, то смотрим в ссылки [3]). При желании можно реализовать собственный "копир", если есть потребность в специфичном поведении копирования.
_nil - вспомогательный атрибут, используемый во время копирования

Пара примеров для понимания работы deepcopy

>>>s = {"i": 1, "s": "2", "l": [1, 2, 3]}
>>>memo = {}
>>>d = copy.deepcopy(s, memo)

>>>print id(s)
44501728

>>>print id(d)
44502304

>>>print d
{'i': 1, 's': '2', 'l': [1, 2, 3]}

>>>print id(memo)
44502016

>>>print memo

{44501728: {'i': 1, 's': '2', 'l': [1, 2, 3]}, 31711104: '2', 31565540: 2, 44502016: [1, 'i', '2', 's', 2, 3, [1, 2, 3], 'l', {'i': 1, 's': '2', 'l': [1, 2, 3]}], 31266408: 'i', 31565552: 1, 31565528: 3, 31268304: 's', 31708488: 'l', 44497976: [1, 2, 3]}

Словарь memo хранит id всех копируемых объектов, их значения., а также свой id.

>>>dt = copy.deepcopy(s, memo)

>>>print id(dt)
44502304

print dt
{'i': 1, 's': '2', 'l': [1, 2, 3]}

Как видим, deepcopy возвращает уже копировавшийся объект d

>>>s1 = {"i": 1, "s": "2", "l": [1, 2, 3]}
>>>dth = copy.deepcopy(s1, memo)
>>>print id(s1)
44516240

>>>print(id(dth))
44517248

>>>print(dth)
{'i': 1, 's': '2', 'l': [1, 2, 3]}

>>>print(id(memo))
44502016

>>>print(memo)
{44501728: {'i': 1, s': '2', 'l': [1, 2, 3]}, 31711104: '2', 44516240: {'i': 1,  's': '2', 'l': [1, 2, 3]}, 31565540: 2, 44502016: [1, 'i', '2', 's', 2, 3, [1, 2, 3], 'l', {'i': 1, 's': '2', 'l': [1, 2, 3]}, [1, 2, 3], {'i': 1, 's': '2', 'l': [1, 2, 3]}], 31266408: 'i', 31565552: 1, 31565528: 3, 44498856: [1, 2, 3], 31268304: 's', 31708488: 'l', 44497976: [1, 2, 3]}

Реализация deepcopy

Сначала происходит инициализация memo, если словарь не задан. Если memo задан тогда проверяется наличие id копируемого объекта, если такой объект уже есть в memo, то происходит его возврат.

d = id(x)
y = memo.get(d, _nil)
if y is not _nil:
    return y

Т.е. если после копирования нужно все время получать новый объект, то тогда поле memo нужно оставлять по-умолчанию (это мы видели на примере выше).

Далее определяется тип копируемого объекта и если он есть в описании стандартных типов, то вызывается функция копирования

copier = _deepcopy_dispatch.get(cls)
if copier:
    y = copier(x, memo)

Например, для словаря вызывается _deepcopy_dict

def _deepcopy_dict(x, memo):
    y = {}
    memo[id(x)] = y
    for key, value in x.iteritems():
        y[deepcopy(key, memo)] = deepcopy(value, memo)
    return y

Код довольно простой, создается новый словарь, в цикле копируются значения. Обратите внимание копирование идет - рекурсивным вызовом deepcopy, сделано это на случай того, если словарь составной и содержит вложенные объекты.
После успешного копирования обновляется словарь memo и вызывается функция _keep_alive, которая записывает в memo, сам копируемый объект, т.е. если вызвать memo[id(memo)], то получим список всех объектов, которые копировались.

Ссылки
[1]https://docs.python.org/2/library/stdtypes.html#dict.copy
[2]https://docs.python.org/2/library/copy.html
[3]http://evgenqa.blogspot.ru/2014/05/blog-post_23.html