Pull to refresh

Comments 26

Хабр зачем-то заменяет foo на http://foo, я уже отписался в саппорт.

Парсер слопал половину спец символов в тексте. Браво.

Tested on py 2.5, 2.6, 2.7, 3.3, 3.4, 3.5 and probably others

Сейчас в мире Python напротив стараются поскорее отрезать все эти 2.5, 2.6, 2.7, 3.3. Это должно приблизить завершение великого перехода на 3.x. И в самом деле, зачем тянуть всё это в новом проекте и испытывать боль? Вы действительно думаете, что кто-то использует 2.5 и сожалеете, что Travis-CI его не поддерживает?

Чтобы получить совместимость с 2.5-2.6 мне пришлось проставить индексы, которые я и так ставлю, в стрингах для format и в одним месте заменить dict comprehension на вызов dict, который подменяется из пакета future. Т. е. времени ушло 2 минуты. Может кому пригодится в легаси проекте.
Веб-сайт, кстати, работает на тройке, хотя может и на двойке — я не специально, видимо привычка писать совместимые приложения очень сильная.

Поделитесь ссылкой, пожалуйста, гугл много чего выдает.

sZam создан для других вещей — Adobe InDesign/InCopy.
Также, есть типограф Муравьева (http://mdash.ru) но он изначально написан на php и как-то ну очень по зверски переведен в python. Но патч из примерно 10 строк позволяет запускать его в продакшене и почти не бояться проблем с кодировкой.
#!/usr/bin/env python2.7

import timeit

from EMT import EMTypograph
from typus import ru_typus

emt = EMTypograph()
text = '"test" (c)'

def test_emt():
    emt.set_text(text)
    return emt.apply()

def test_typus():
    return ru_typus(text)

print timeit.timeit(test_emt, number=1000)
print timeit.timeit(test_typus, number=1000)

$ python test.py
62.5571029186
0.090714931488

Скриптик набросал чтобы избежать оверхеды с инстанцированием типографа Муравьева.

Ещё есть такой типограф github.com/samdark/Typograph

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

https://github.com/Harut/chakert

В имеющихся меня не устраивало, что, во-первых правила замены оформлены в виде регулярок, что лично для меня категорически неудобно (у вас, судя по всему, тоже :( ). Особенно, разбираться в чужих регулярках.

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

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

При этом токены остаются связанными со своими тегами, и при сборке обратно в текст всё само встает на свои места.

Русский + английский. Py2.7, py3.4 (нужно, кстати, освежить...)

Буду рад фидбеку, особенно в виде тест-кейсов :)
PS. В продакшне чуть больше года на нескольких проектах, через типограф в принудительном режиме пропущено пару сотен тысяч текстов новостного формата в виде фильтрованного HTML, и каждый день пропускается порядка нескольких десятков. Пока нареканий со стороны редакторов не было.

Интересно, как это я проморгал ваш проект. По честному гуглил.
Из тестов тут почти все. Всякие "сложные случаи" уже разложены отдельно и я с ходу их не назову, ну может это или вот еще.


Пока я вижу незначительные расхождения в использовании правил, скорее субъективных.


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

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

Пожалуй, еще добавлю, что тайпус понимает миксованный EnRu текст. Разница между ru_typus и en_typus только в кавычках.

Ну вот, добавил апостроф, теперь красиво:


'She yelled, "I\'m going to kill you!"'
'She yelled, “I’m going to kill you!”'

Хороший тест:


'For ones, maybe someone will call me 'sir' without adding, 'you're making a scene.''
“For ones, maybe someone will call me ‘sir’ without adding, ‘you’re making a scene.’”
еще у меня есть ощущение, что английские кавычки расставляются у вас все же неправильно.

Это мы какую-то книжку нашли, в которой были описаны правила. В ридми есть ссылка на нее, правда, на другой фрагмент. Найти сейчас ее наверно будет непросто. Я завтра перепроверю по вашей ссылке.

html тайпус никак не модифицирует, у вас заметил теги в нижнем регистре

Это lxml виноват :)

Пожалуй, еще добавлю, что тайпус понимает миксованный EnRu текст.

chakert опирается на атрибут lang, если он есть. Тут тоже вопрос кейсов, у нас смешанных текстов нет.

Веб-демо нет… pip install chakert lxml

Тесты посмотрю, спасибо.
Посмотрел по-диагонально test_example. Некоторые вещи, например, замена (с) на © я у себя по-умолчанию включать точно не буду, потому что здесь не исключены ложные срабатывания, а через типограф у нас тексты проходят все и всегда. Лучше оставить спецсимволы на совести редактора, чем ненароком заменить не то. Надо бы мне подумать, как кастомайзить список модификаций.

Еще бросается в глаза смешение работы в текстовом режиме и html. Например, сокращать пробелы до одного и переводы строк до двух максимум имеет смысл для plain text, в то же время, там же есть и теги. Конечно, вряд ли это может создать проблемы, и в некоторых случаях даже подходит, например, для редактора комментов хабра :) Но какой-то червячок внутри меня мне упорно твердит, что разделение методов работы с plain text и html в вебе — это хорошая практика.

Ну и здесь у меня разные издевательства над HTML посреди текста. Вполне вероятные кейсы для данных, поступающих из визивига. Это главная причина, почему я написал свой типограф, потому что сделать такое на регулярках довольно непросто. Неясно, поддерживается ли это у вас, по крайней мере, тестов не вижу…
А, вижу что-то похожее, но кажется, всего один случай.

Например, мой вот этот кейс у вас не проходит, судя по всему (если я всё правильно сделал в демо) — вместо одного неразрывного получается один обычный и один неразрывный.

А не расскажите как это у вас происходит, что теги не мешают? Я сходу не разобрался.

Как работает типограф HTML:

1. Парсим с помощью lxml.html

2. Проходимся по дереву: каждый абзац типографируется независимо от остальных, граница абзацев — начало и конец блочного тега. Содержимое Некоторых тегов игнорируются.

3. Весь текст разбивается на токены в соотвествии с содержащейся в классе токена регуляркой. При этом каждый токен сохраняет ссылку на место в дереве lxml ElementTree, из которого он взят ([1], [2]).

4. Затем мы проходимся по списку всех токенов и вызываем для каждого функцию преобразования. На входе — итераторы следующих и предыдущих токенов. Итератор возвращает токены-фрагменты текста вне зависимости от тегов, так что при применении правил теги игнорируются.

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

6. Записываем изменения в ElementTree

7. Выгружаем ElementTree обратно в HTML.

Интересная реализация, спасибо. Скажите, а такой кейс обработается?


<div>"foo </div>
<div>bar"</div>

Чтобы убедиться, что я правильно понял.

Интересный кейс, не приходило в голову. несмотря на то, что используются блочные теги, стек кавычек ведётся глобальный для всего текста, поэтому кавычки заменяются. При переходе от одного блока к другому обнуляется итератор предыдущих и следующих элементов, может что-то еще.
Я не прав, если вы успели прочитать мой коммент, то забудьте :). Оно действительно заменяется, но не так, как я описал, а просто потому что кавычка привязана к слову слева или справа, блоки тут ни при чем.
self.assertHtml('<p>"начало "текста </p><p>конец" текста"</p>',
                '<p>«начало „текста </p><p>конец“ текста»</p>')


Такой кейс уже не отрабатывает, поскольку стек кавычек сбрасывается вместе со всем остальным контекстом. На выходе даёт

'<p>«начало „текста </p><p>конец» текста»</p>'


Должен ли он отрабатывать — вопрос сложный. С одной стороны вроде бы логично, но с другой, (а) непонятно, допустимы ли в нормах типографики вложенные цитаты на несколько абзацев, и (б) не хотелось бы, чтобы лишняя кавычка в длинном тексте сдвигала расстановку кавычек в непредсказуемом месте, а то и во всём остальном тексте. Пожалуй, минусы перевешивают, и оставлю-ка как есть, на совести редакторов :)

Да, это я и имел ввиду. Похоже, я понял идею и смогу частично реализовать ее в тайпусе, правда, на все тех же регулярках.
А искать закрывающие и открывающие кавычки не совсем корректно, если вы захотите внести поддержку дюймов <div>Диагональ 22"</div>.
Еще всякие редакторы делают так “тест” — т. е. ставят "неправильные" кавычки, не проверял ваш типограф на этот кейс.


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

Цитата может быть в несколько абзацев, но да, случай крайне редкий.

Only those users with full accounts are able to leave comments. Log in, please.