<?xml version="1.0" encoding="utf-8" ?>
<page title='Cog' sitemap='no' lang='ru'>
<history>
<what when='20041110T205200'>Alexander Belchenko's translation.</what>
<what when='20041117T111700'>Tweaks from Alexander.</what>
<what when='20050105T173500'>Alexander's updates (to version 1.3).</what>
<what when='20050225T195100'>Ned's updates (adding the English for 1.4).</what>
<what when='20050226T164000'>Alexander's updates (to version 1.4).</what>
<what when='20051009T164000'>Alexander's updates (to version 2.0).</what>
<what when='20080524T091900'>Alexander's updates (to version 2.1).</what>
<what when='20200930T135100'>decorator-factory's updates (to version 3.0).</what>
</history>

<docinfo>Оригинал текста на английском языке <a href='code/cog/index.html'>находится здесь</a>.
<br/>Перевод на русский язык: Александр Бельченко, страница обновлена 19 мая 2008.
<br/>Перевод обновлён decorator-factory 30 сентября 2020.
</docinfo>

<p>Cog <emdash/> это инструмент для препроцессинга файлов. Он позволяет использовать
    фрагменты кода на Python для генерации желаемого текста.</p>


<p>Разделы:</p>
<h1links/>


<h1>Что делает эта утилита?</h1>

<p>Cog преобразует файлы достаточно простым способом:
он находит куски Python-кода, встроенные в файл, выполняет этот Python код,
и затем вставляет результат работы в оригинальный файл.
Файл может содержать любой текст вокруг участков Python-кода.
Обычно это исходный код какой-нибудь программы.</p>

<p>Например, если пропустить следующий файл через Cog:</p>

<code lang='c'><![CDATA[
// This is my C++ file.
...
/*[[[cog
import cog
fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
for fn in fnames:
    cog.outl("void %s();" % fn)
]]]*/
//[[[end]]]
...
]]></code>

<p>в результате получим нечто похожее на это:</p>

<code lang='c'><![CDATA[
// This is my C++ file.
...
/*[[[cog
import cog
fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
for fn in fnames:
    cog.outl("void %s();" % fn)
]]]*/
void DoSomething();
void DoAnotherThing();
void DoLastThing();
//[[[end]]]
...
]]></code>

<p>
Строки, содержащие тройные квадратные скобки, <emdash/> маркеры для Cog.
Строки между <b>[[[cog</b> и <b>]]]</b> содержат код на Python.
Строки между <b>]]]</b> и <b>[[[end]]]</b> содержат результат работы генератора.</p>

<p>Когда Cog начинает работать, он удаляет последний сгенерированный результат,
затем выполняет код на Python и записывает новый результат работы в файл.
Все строки текста, находящиеся за пределами специальных маркеров, остаются нетронутыми.</p>

<p>Строки с Cog-маркерами могут содержать любой текст в дополнение к
тройным квадратным скобкам. Это позволяет спрятать код Python-генератора в исходном файле.
В вышеприведенном примере весь кусок Python-кода <emdash/> это C++ комментарий.
Таким образом Python-код может быть оставлен в тексте,
в то время как файл будет восприниматься как обычный C++ код.</p>

<h1>Дизайн</h1>

<p>Cog спроектирован так, чтобы быть простым и удобным инструментом.
Он записывает результат своей работы в исходный файл,
сохраняя при этом встроенный код генератора.
Это означает, что Cog может быть запущен любое число раз для одного и того же файла.
Вместо того чтобы использовать отдельный исходный файл с кодом генератора и
отдельный выходной файл, Cog в процессе работы использует
только один файл, который является и генератором, и выходным файлом.</p>

<p>Поскольку маркерные строки адаптированы под синтаксис любого языка,
то маркеры могут скрыть Python-код генератора внутри исходного файла.
Это означает, что Cog-файлы могут быть включены в базу контроля версий,
и при этом не нужно беспокоиться о том, чтобы хранить отдельно исходный
файл с генератором и результирующий выходной файл. Это также означает,
что вам не нужно менять сложившуюся методику компиляции-сборки проекта и т.д.</p>

<p>Я экспериментировал с различными инструментами, которые генерируют
некоторый код по шаблону. И я замечал, что мне постоянно приходиться
бороться с появлением нежелательных символов пробелов в генерируемом
выходном файле, и что я мысленно преобразовывал некоторый воображаемый
Python-код в его эквивалент в виде шаблона. Преимущества систем-шаблонизаторов
(в которых большинство кода может быть задано литерально) исчезают,
как только задача генерации кода становится все более и более сложной,
и как только в процессе генерации требуется все больше логики.</p>


<p>Cog позволяет использовать все возможности Python для генерации текста без
ограничений, которые накладывают шаблонизаторы.</p>


<h1>Установка</h1>
<p>Cog работает на Python версии 2.7, 3.5, 3.6, 3.7, 3.8 или Jython 2.5.</p>

<p>Cog устанавливается обычным образом, за исключением того, что имя пакета <emdash/> "cogapp", а не "cog":</p>
<code><![CDATA[
$ pip install cogapp
]]></code>

<p>По окончании установки в подкаталоге scripts для Python-скриптов должен появиться скрипт cog.py.
Cog также можно запустить через команду "cog".</p>

<h2>Лицензия</h2>

<p>Cog распространяется согласно
<a href='http://www.opensource.org/licenses/mit-license.php'>MIT лицензии</a>.
Используйте Cog, чтобы сделать этот мир чуточку лучше.</p>


<!-- ================================================================= -->

<h1>Как писать исходные файлы</h1>

<p>Исходные файлы, предназначенные для обработки утилитой Cog, <emdash/> это
чаще всего обычные текстовые файлы, которые должны оставаться нетронутыми.
Код Python-генератора в вашем исходном файле <emdash/> это стандартный Python-код.
Допускаются любые вариации использования Python для генерации необходимого
текста в вашем файле. Каждый участок Python-кода (между строками
<b>[[[cog</b> и <b>]]]</b>) называется генератором,
все генераторы будут выполнены по очереди.</p>

<p>Область вывода для каждого генератора (т.е. строки между <b>]]]</b> и
<b>[[[end]]]</b>) очищается, и на это место вставляется новый результат работы
Python-генератора. Формат маркерных строк не имеет существенного значения,
что позволяет адаптировать их к любому типу исходных файлов.
Если строка содержит последовательности специальных символов, то вся строка
интерпретируется как маркер. Например, любая из этих строк отмечает начало
выполняемого Python-кода:</p>

<code><![CDATA[
//[[[cog
/* cog starts now: [[[cog */
-- [[[cog (this is cog Python code)
#if 0 // [[[cog
]]></code>

<p>Cog также может использоваться в языках, в которых отсутствуют многострочные
комментарии. Если все маркерные строки содержат некоторый текст перед тройной
скобкой, и все строки кода генератора также содержат тот же текст в качестве
префикса, то этот префикс удаляется перед выполнением кода Python-генератора.
Например, имеется следующий SQL-файл:</p>

<code><![CDATA[
--[[[cog
--   import cog
--   for table in ['customers', 'orders', 'suppliers']:
--      cog.outl("drop table %s;" % table)
--]]]
--[[[end]]]
]]></code>

<p>который в результате работы Cog станет таким:</p>

<code><![CDATA[
--[[[cog
--   import cog
--   for table in ['customers', 'orders', 'suppliers']:
--      cog.outl("drop table %s;" % table)
--]]]
drop table customers;
drop table orders;
drop table suppliers;
--[[[end]]]
]]></code>

<p>Кроме того, для однострочных генераторов может быть использована компактная форма.
Маркер начала кода, и маркер конца кода могут находиться на одной строке,
и весь текст между этими маркерами будет рассматриваться как одиночная строка
Python-кода:</p>

<code><![CDATA[
// blah blah
//[[[cog import MyModule as m; m.generateCode() ]]]
//[[[end]]]
]]></code>

<p>Эта форма записи также может использоваться для простого импорта модуля.
Исполняемые операторы верхнего уровня в таком модуле могут производить действия
по генерации кода.</p>

<p>При желании можно изменить маркеры с помощью аргумента командной строки "--markers"
(см. python3 -m cogapp -h для справки).</p>

<p>Если в одном и том же файле присутствует несколько вставок с генераторами, то они будут
исполняться с использованием единого словаря глобальных имён (параметр "globals" для exec, доступный
через вызов функции "globals()").
Таким образом, их поведение будет аналогичным
тому, как если бы они все были в одном Python-модуле.</p>

<p>Cog старается соблюдать ваши правила по использованию пробелов.
Ваш Python-код может иметь блочный отступ от левого края в соответствии
с окружающим его текстом. При этом Cog также будет добавлять необходимые
отступы в своем выводе, чтобы соблюсти эту закономерность.
Весь вывод генератора собирается в единый блок текста, общий отступ для всех
строк удаляется и затем блок текста выравнивается отступами согласно отступу
самого cog-генератора. Это значит, что самый крайний слева непробельный символ
в вашем выводе будет иметь такой же отступ, как и маркер начала кода
cog-генератора. Остальные строки в вашем выводе будут сохранять свои отступы
относительно друг друга.
</p>


<h1>Модуль Cog</h1>

<p>Модуль Cog предоставляет вам для использования функции, при помощи которых
можно выводить желаемые строки в ваш файл. Ниже приведено описание этих функций.</p>

<dl>
<dt><b>cog.out</b><i>(sOut='' [, dedent=False][, trimblanklines=False])</i></dt>
<dd>Пишет текст в область вывода.</dd>
<dd><i>sOut</i> <emdash/> это строковая переменная для вывода.</dd>
<dd>Если аргумент <i>dedent</i> равен True, то все пробельные символы
в начале строк в <i>sOut</i> удаляются перед добавлением к блоку вывода.
Если аргумент trimblanklines равен True, то все пустые строки
в начале и конце sOut удаляются перед добавлением к блоку вывода.
Вместе, эти две опции облегчают
использование многострочных строковых переменных:</dd>
<code><![CDATA[
cog.out("""
    These are lines I
    want to write into my source file.
""", dedent=True, trimblankline=True)
]]></code>

<dt><b>cog.outl</b></dt>
<dd>Аналогично <b>cog.out</b>, но добавляет в конце перевод строки.</dd>

<dt><b>cog.msg</b><i>(msg)</i></dt>
<dd>Печатает строку <i>msg</i> в консольном окне (поток stdout) с префиксом "Message: ".</dd>

<dt><b>cog.error</b><i>(msg)</i></dt>
<dd>Возбуждает исключение с текстовым сообщением <i>msg</i>.
В консольном окне выводится сообщение <i>msg</i> без вывода стека вызовов Python.
Таким образом, программисты не знакомые с Python, которые используют ваш Cog-генератор,
не будут пугаться длинного traceback-а.</dd>

<dt><b>cog.inFile</b></dt>
<dd>Атрибут, представляющий имя входного файла.</dd>

<dt><b>cog.outFile</b></dt>
<dd>Атрибут, представляющий имя выходного файла.</dd>

<dt><b>cog.firstLineNum</b></dt>
<dd>Атрибут, представляющий номер строки с первым Python-оператором в генераторе.
Этот атрибут может при необходимости использоваться, чтобы различать два генератора
в одном и том же входном файле.</dd>

</dl>

<p>В дистрибутив Cog включен модуль <a href='code/modules/handyxml.html'>handyxml</a>,
поскольку я использую файлы данных в формате XML в своих собственных задачах генерации кода.</p>


<h1>Запуск Cog</h1>

<p>Запуск Cog выполняется из командной строки, аргументы передаются стандартным образом.</p>
<code><![CDATA[
$ cog -h

cog - генерация исходных текстов при помощи встроенного Python-кода.

cog [OPTIONS] [INFILE | @FILELIST] ...

INFILE - имя входного файла, используйте '-' для чтения из stdin
FILELIST - имя текстового файла, который содержит набор имен файлов
или ссылки на другие @FILELIST.

OPTIONS:
    -c          Добавление контрольной суммы к генерируемому тексту
                для защиты от случайных изменений.
    -d          Удаление кода генератора из выходного файла.
    -D name=val Определение глобальной строки, доступной для использования
                в коде вашего генератора.
    -e          Выдать предупреждение, если файл не содержит cog-кода.
    -I PATH     Добавить PATH к списку каталогов, в которых будет
                производиться поиск модулей и файлов с данными.
    -n ENCODING Кодировка, используемая для чтения и записи файлов.
    -o OUTNAME  Записать вывод в файл OUTNAME.
    -p PROLOGUE Добавить в начало каждого отрывка код, например: -p "import math"
    -r          Записать вывод назад во входной файл.
    -s STRING   Ко всем сгенерированным строкам добавляется суффикс STRING.
    -U          Записывать в файл с Unix-концовками строк (только символ LF).
    -w CMD      Использовать команду CMD, если необходимо разрешить
                запись в выходной файл.
                    Вместо %s в CMD будет подставлено имя файла.
    -x          Удалить все автоматически сгенерированные строки
                без запуска генератора.
    -z          Маркер конца может быть опущен, предполагается, что он
                находится в конце файла.
    -v          Напечатать версию Cog и выйти.
    --verbosity=VERBOSITY
                Управлять подробностью вывода. 2 (по умолчанию) выводит все файлы,
                1 выводит только изменённые, 0 не выводит файлы.
    --markers='START END END-OUTPUT'
                Маркеры, используемые для нахождения кода на Python. Строка
                Должна включать три значения, разделённые пробелами. Значение
                По умолчанию -- '[[[cog ]] [[[end]]]'.
    -h          Распечатать краткую справку.
]]></code>

<p>Указанные в командной строке имена файлов используются в качестве входных файлов
и обрабатываются Cog. Имена файлов также могут быть переданы в виде списка
в текстовом файле, который передается как аргумент с префиксом &#64;:</p>

<code>$ cog @files_to_cog.txt</code>

<p>Эти @-файлы могут быть вложенными, и каждая строка может содержать
опции командной строки вместе с именем файла для обработки.
Например, вы можете создать файл cogfiles.txt:
</p>

<code name='cogfiles.txt'><![CDATA[
# Файлы, предназначенные для обработки
mycode.cpp
myothercode.cpp
myschema.sql -s " --**cogged**"
readme.txt -s ""
]]></code>

<p>и затем запустить Cog следующим образом:</p>

<code><![CDATA[
cog -s " //**cogged**" @cogfiles.txt
]]></code>

<p>В этом случае Cog будет обрабатывать 4 файла, используя синтаксис C++
для маркирования генерируемых строк во всех C++ файлах,
SQL синтаксис для .sql файла, и вообще без маркеров в файле readme.txt.</p>

<p>В качестве другого примера рассмотрим файл cogfiles2.txt:</p>

<code name='cogfiles2.txt'><![CDATA[
template.h -D thefile=data1.xml -o data1.h
template.h -D thefile=data2.xml -o data2.h
]]></code>

<p>Если запустить Cog следующей командой:</p>

<code><![CDATA[
cog -D version=3.4.1 @cogfiles2.txt
]]></code>

<p>То Cog обработает файл template.h дважды, создав два файла data1.h и data2.h.
Для каждой обработки будет определена переменная version со значением "3.4.1",
но для первого запуска переменная thefile будет равна "data1.xml",
а для второго запуска переменная thefile будет равна "data2.xml".</p>


<h2>Перезапись файлов</h2>

<p>Флаг -r указывает Cog о необходимости записи вывода назад во входной файл.
Если входной файл защищен от записи (например, если он не был извлечен
из системы контроля версий для редактирования), то при помощи флага -w
можно указать команду, которая выполнит необходимые действия
для разрешения записи в файл:</p>

<code>$ cog -r -w "p4 edit %s" @files_to_cog.txt</code>


<h2>Установка глобальных переменных</h2>

<p>Значения глобальных переменных могут быть установлены в командной строке
при помощи ключа -D.
Например, если запустить Cog следующим образом:
</p>

<code><![CDATA[
cog -D thefile=fooey.xml mycode.txt
]]></code>

<p>то Cog начнет обработку файла mycode.txt, но предварительно определит
глобальную переменную с именем
thefile и присвоит ей значение "fooey.xml". Эта переменная затем может
быть использована в ваших генераторах кода.
Вы можете указывать ключ -D многократно в командной строке,
определяя несколько глобальных переменных.
</p>

<p>Значение всегда интерпретируется как Python-строка, для упрощения проблем
с обработкой кавычек.
Это означает что:
</p>

<code><![CDATA[
cog -D NUM_TO_DO=12
]]></code>

<p>определит переменную NUM_TO_DO не как целое значение 12, а как строку "12".
Эти два значения различаются по типу и не эквиваленты по значению в языке Python.
Используйте код int(NUM_TO_DO) для приведения строки к числовому значению.
</p>


<h2>Добавление контрольной суммы к генерируемому тексту</h2>

<p>Если Cog запущен с флагом -c, то генерируемый текст будет сопровождаться указанием
его контрольной суммы:</p>

<code><![CDATA[
--[[[cog
--   import cog
--   for i in range(10):
--      cog.out("%d " % i)
--]]]
0 1 2 3 4 5 6 7 8 9
--[[[end]]] (checksum: bd7715304529f66c4d3493e786bb0f1f)
]]></code>

<p>Если сгенерированный код будет отредактирован разработчиком,
которого не проинформировали о назначении кодогенератора,
то при следующем запуске Cog обнаружит, что контрольная сумма не сошлась,
и прекратит обработку, чтобы избежать перезаписывания отредактированного
участка кода.</p>


<h2>Добавление суффикса к генерируемым строкам</h2>

<p>Для того, чтобы было проще отличать сгенерированные строки когда вы
проводите поиск в файлах при помощи grep,
ключ командной строки -s позволяет указать суффикс, который будет добавляться
к каждой непустой строке, генерируемой Cog.
Например, имеется следующий входной файл (mycode.txt):</p>

<code name='mycode.txt'><![CDATA[
[[[cog
cog.outl('Three times:\n')
for i in range(3):
    cog.outl('This is line %d' % i)
]]]
[[[end]]]
]]></code>

<p>Запуская Cog такой командой:</p>

<code><![CDATA[
cog -s " //(generated)" mycode.txt
]]></code>

<p>получим такой выходной файл:</p>

<code><![CDATA[
[[[cog
cog.outl('Three times:\n')
for i in range(3):
    cog.outl('This is line %d' % i)
]]]
Three times: //(generated)

This is line 0 //(generated)
This is line 1 //(generated)
This is line 2 //(generated)
[[[end]]]
]]></code>


<h2>Разное</h2>

<p>Опция -n позволяет изменить кодировку, которая используется при чтении и записи файлов.</p>

<p>Опция --verbose позволяет управлять количеством вывода, который производит cog.
    --verbose=2 <emdash/> опция по умолчанию <emdash/> с ней cog напечатает имя
    каждого файла, который он просмотрел, и укажет, изменил ли он его.
    С --verbose=1 cog напечатает только изменённые файлы.
    С --verbose=0 cog не напечатает информацию о файлах</p>

<p>Опция --markers позволяет управлять синтаксисом маркерных линий. Это
полезно, если синтаксис файла не позволяет использовать разделители по умолчанию.
Значение опции должно состоять из трёх частей, разделённых пробелами.
Значение по умолчанию <emdash/> "[[[cog ]]] [[[end]]]"</p>

<p>Флаг -x указывает утилите Cog удалить весь ранее сгенерированный текст
без запуска кодогенераторов. Этот флаг позволяет вам удалить весь автоматически
сгенерированный код из исходного файла.</p>

<p>Флаг -d указывает утилите Cog удалить все генераторы из выходного файла.
Этот флаг позволяет вам генерировать текст в общедоступных файлах,
не показывая генератор своим клиентам.</p>

<p>Флаг -U заставляет Cog записывать выходные файлы с использованием Unix
символа перевода строки (LF) вместо концовок строк, принятых на конкретной
платформе. Вы можете использовать этот флаг в ОС Windows для создания
выходных файлов в Unix-стиле.</p>

<p>Флаг -I добавляет каталог к списку путей, в которых ведется поиск
Python-модулей, и в которых ведется поиск файлов для обработки модулем handyxml.</p>

<p>Флаг -p позволяет указать преамбулу <emdash/> фрагмент кода, который добавляется
к  коду на Python. Это позволяет убрать общие импорты из кода.</p>

<p>Флаг -z  позволяет опускать строку с маркером [[[end]]], при этом предполагается,
что он находится в конце файла.</p>

<!-- ================================================================= -->

<h1>История</h1>

<p>Список изменений в версиях Cog <a href='code/cog/changes_ru.html'>приведен на отдельной странице</a>.</p>

 <!-- ================================================================= -->

<h1>Обратная связь</h1>

<p>Мне будет приятно услышать о ваших успехах или трудностях при использовании Cog.
Оставьте комментарий здесь,
или <a href="javascript:nospam(%22ned%22,%22nedbatchelder.com?subject=Cog%22);">напишите
мне небольшое письмо</a>. Пожалуйста, пишите на английском языке.</p>


<h1>Ссылки</h1>

<p>Имеется также несколько других воплощений Cog-идей:</p>

<ul>
<li><a href='http://rubyforge.org/projects/argent/'>Argent</a> <emdash/> реализация на языке Ruby.</li>
<li><a href='http://babylon.idlevice.co.uk/php/precog/'>Precog</a> <emdash/> реализация на языке PHP.</li>
<li><a href='http://www.crazilocks.com/pcg.html'>PCG</a> <emdash/> реализация на языке Perl.</li>
<li><a href='http://sourceforge.net/projects/templarian'>Templarian</a> <emdash/> похожий инструмент, также написан на Python.</li>
<li><a href='http://www.larryhastings.com/programming/nocog/'>Nocog</a> <emdash/> инструмент для автоматического
определения тех файлов, которые должны обрабатываться при помощи Cog.</li>
</ul>

<p>Возможно, вы захотите прочитать:</p>
<ul>
<li><a href="http://www.python.org/about/success/cog/">Cog:
A Code Generation Tool Written in Python</a>, я написал историю про утилиту Cog,
как пример успешного применения Python.</li>
<li><a href="blog/index.html">Мой блог</a>, на страницах которого я рассуждаю
о программном обеспечении, детях и других интересных для меня вещах.</li>
</ul>

<googleads/>
<pagecomments/>

</page>
