What does it do?

Cog transforms files in a very simple way: it finds chunks of Python code
embedded in them, executes the Python code, and inserts its output back
into the original file.
The file can contain whatever text you like around the Python code.
It will usually be source code.

Lines with triple square brackets are marker lines.
The lines between [[[cog and ]]] are the generator Python code.
The lines between ]]] and [[[end]]] are the output from
the generator.

When cog runs, it discards the last generated Python output,
executes the generator Python code,
and writes its generated output into the file.
All text lines outside of the special markers are passed through unchanged.

The cog marker lines can contain any text in addition to the triple square bracket
tokens. This makes it possible to hide the generator Python code from the source file.
In the sample above, the entire chunk of Python code is a C++ comment, so the
Python code can be left in place while the file is treated as C++ code.

Design

Cog is designed to be easy to run.
It writes its results back into the original file while
retaining the code it executed.
This means cog can be run any number of times on the same file.
Rather than have a source generator file, and a separate output file, typically
cog is run with one file serving as both generator and output.

Because the marker lines accommodate any language syntax, the markers
can hide the cog Python code from the source file.
This means cog files can be checked into source control
without worrying about keeping the source files separate from the output
files, without modifying build procedures, and so on.

I experimented with using a templating engine for generating code,
and found myself constantly struggling with white space in the generated
output, and mentally converting from the Python code I could imagine, into
its templating equivalent. The advantages of a templating system (that most
of the code could be entered literally) were lost as the code generation
tasks became more complex, and the generation process needed more logic.

Cog lets you use the full power of Python for text generation,
without a templating system dumbing down your tools for you.

License

Cog is distributed under the
MIT license.
Use it to spread goodness through the world.

Writing the source files

Source files to be run through cog are mostly just plain text that will
be passed through untouched.
The Python code in your source file is standard Python code.
Any way you want to use Python to generate text to go into your file is fine.
Each chunk of Python code (between the [[[cog and ]]] lines)
is called a generator and is executed in sequence.

The output area for each generator (between the ]]] and [[[end]]]
lines) is deleted, and the output of running the Python code is inserted in its place.
To accommodate all source file types, the format of the marker lines is irrelevant.
If the line contains the special character sequence, the whole line is taken as
a marker.
Any of these lines mark the beginning of executable Python code:

Cog can also be used in languages without multi-line comments.
If the marker lines all have the same text before the triple brackets,
and all the lines in the generator code also have this text as a prefix,
then the prefixes are removed from all the generator lines before execution.
For example, in a SQL file, this:

Finally, a compact form can be used for single-line generators.
The begin-code marker and the end-code marker can appear on the same
line, and all the text between them will be taken as a single Python
line:

You can also use this form to simply import a module. The top-level
statements in the module can generate the code.

If you have special requirements for the syntax of your file, you can use
the --markers option to define new markers.

If there are multiple generators in the same file, they are executed
with the same globals dictionary, so it is as if they were all one Python
module.

Cog tries to do the right thing with white space.
Your Python code can be block-indented to match the surrounding text in the
source file, and cog will re-indent the output to fit as well.
All of the output for a generator is collected as a block of text, a common
whitespace prefix is removed, and then the block is indented to match the
indentation of the cog generator. This means the left-most non-whitespace
character in your output will have the same indentation as the begin-code
marker line. Other lines in your output keep their relative indentation.

The cog module

A module called cog provides the functions you call to produce output into
your file. The functions are:

cog.out(sOut='' [, dedent=False][, trimblanklines=False])

Writes text to the output.

sOut is the string to write to the output.

If dedent is True, then common initial white space is removed from the
lines in sOut before adding them to the output.
If trimblanklines is True, then an initial and trailing
blank line are removed from sOut before adding them to the output.
Together, these option arguments make it easier to use multi-line strings,
and they only are useful for multi-line strings:

cog.out(""" These are lines I want to write into my source file.""", dedent=True, trimblanklines=True)

cog.outl

Same as cog.out, but adds a trailing newline.

cog.msg(msg)

Prints msg to stdout with a "Message: " prefix.

cog.error(msg)

Raises an exception with msg as the text.
No traceback is included, so that non-Python programmers using your code
generators won't be scared.

cog.inFile

An attribute, the path of the input file.

cog.outFile

An attribute, the path of the output file.

cog.firstLineNum

An attribute, the line number of the first line of Python code
in the generator. This can be used to distinguish between two
generators in the same input file, if needed.

cog.previous

An attribute, the text output of the previous run of this
generator. This can be used for whatever purpose you like, including
outputting again with cog.out().

Running cog

Cog is a command-line utility which takes arguments in standard form.

cog - generate code with inlined Python code.

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

INFILE is the name of an input file, '-' will read from stdin.FILELIST is the name of a text file containing file names or other @FILELISTs.

OPTIONS: -c Checksum the output to protect it against accidental change. -d Delete the generator code from the output file. -D name=val Define a global string available to your generator code. -e Warn if a file has no cog code in it. -I PATH Add PATH to the list of directories for data files and modules. -n ENCODING Use ENCODING when reading and writing files. -o OUTNAME Write the output to OUTNAME. -r Replace the input file with the output. -s STRING Suffix all generated output lines with STRING. -U Write the output with Unix newlines (only LF line-endings). -w CMD Use CMD if the output file needs to be made writable. A %s in the CMD will be filled with the filename. -x Excise all the generated output without running the generators. -z The end-output marker can be omitted, and is assumed at eof. -v Print the version of cog and exit. --verbosity=VERBOSITY Control the amount of output. 2 (the default) lists all files, 1 lists only changed files, 0 lists no files. --markers='START END END-OUTPUT' The patterns surrounding cog inline instructions. Should include three values separated by spaces, the start, end, and end-output markers. Defaults to '[[[cog ]]] [[[end]]]'. -h Print this help.

In addition to running cog as a command on the command line:

$ cog [options] [arguments]

you can also invoke it as a module with the Python interpreter:

$ python -m cogapp [options] [arguments]

Note that the Python module is called "cogapp".

Input files

Files on the command line are processed as input files. All input files
are assumed to be UTF-8 encoded. Using a minus for a filename (-) will read
the standard input.

Files can also be listed in a text file named on the command line
with an @:

$ cog @files_to_cog.txt

These @-files can be nested, and each line can contain switches as well
as a file to process.
For example, you can create a file cogfiles.txt:

cogfiles.txt

# These are the files I run through cogmycode.cppmyothercode.cppmyschema.sql -s " --**cogged**"readme.txt -s ""

then invoke cog like this:

cog -s " //**cogged**" @cogfiles.txt

Now cog will process four files, using C++ syntax for markers on all the C++ files,
SQL syntax for the .sql file, and no markers at all on the readme.txt file.

Cog will process template.h twice, creating both data1.h and data2.h. Both executions
would define the variable version as "3.4.1", but the first run would have thefile equal to "data1.xml"
and the second run would have thefile equal to "data2.xml".

Overwriting files

The -r flag tells cog to write the output back to the input file.
If the input file is not writable (for example, because it has not been
checked out of a source control system),
a command to make the file writable can be provided with -w:

$ cog -r -w "p4 edit %s" @files_to_cog.txt

Setting globals

Global values can be set from the command line with the -D flag.
For example, invoking Cog like this:

cog -D thefile=fooey.xml mycode.txt

will run Cog over mycode.txt, but first define a global variable called
thefile with a value of "fooey.xml". This variable can then be referenced in
your generator code. You can provide multiple -D arguments on the command line,
and all will be defined and available.

The value is always interpreted as a Python string, to simplify the problem of quoting.
This means that:

cog -D NUM_TO_DO=12

will define NUM_TO_DO not as the integer 12, but as the string "12", which
are different and not equal values in Python. Use int(NUM_TO_DO) to get the
numeric value.

Checksummed output

If cog is run with the -c flag, then generated output is accompanied by
a checksum:

If the generated code is edited by a misguided developer, the next time cog
is run, the checksum won't match, and cog will stop to avoid overwriting the
edited code.

Output line suffixes

To make it easier to identify generated lines when grepping your source files,
the -s switch provides a suffix which is appended to every non-blank text line generated by
Cog. For example, with this input file (mycode.txt):

mycode.txt

[[[cogcog.outl('Three times:\n')for i in range(3): cog.outl('This is line %d' % i)]]][[[end]]]

invoking cog like this:

cog -s " //(generated)" mycode.txt

will produce this output:

[[[cogcog.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]]]

Miscellaneous

The -n option lets you tell cog what encoding to use when reading and
writing files.

The --verbose option lets you control how much cog should chatter about the
files it is cogging. --verbose=2 is the default: cog will name every file
it considers, and whether it has changed. --verbose=1 will only name the
changed files. --verbose=0 won't mention any files at all.

The --markers option lets you control the syntax of the marker lines. The
value must be a string with two spaces in it. The three markers are the
three pieces separated by the spaces. The default value for markers is
"[[cog ]]] [[[end]]]".

The -x flag tells cog to delete the old generated output without running
the generators. This lets you remove all the generated output from a source
file.

The -d flag tells cog to delete the generators from the output file. This lets
you generate code in a public file but not have to show the generator to your customers.

The -U flag causes the output file to use pure Unix newlines rather than the
platform's native line endings. You can use this on Windows to produce Unix-style
output files.

The -I flag adds a directory to the path used to find Python modules.

The -z flag lets you omit the [[[end]]] marker line, and it will be assumed at the
end of the file.

Comments

I'm using Cog! I use it to do code generation for a library that's implemented in three different languages (C#, c++, Java). Linked from my blog. Thanks for the distribution fix!

Greg Smith 1:08 PM on 17 Feb 2004

This is really nice - normally I don't like the idea of having files which are hand-edited AND machine generated, but this is simple enough to change my mind about this. I really like this.

Some minor issues - the 'cog.py' file in scripts has a cr-lf at the end of the first line, which makes it fail to run, and makes distutils fail to edit in the proper python bin location. Easily fixed.

Running the script with no command line parameters should cause it to print a help message.

How about an option which will cause
cog to fail with an error if the file's
actual autogenerated code doesn't match the live python output. This might make more sense than '-r' for use in a makefile, to ensure that you don't edit the wrong code and have your changes discarded.

I notice that the 'import cog' is not actually required, this is good. It would be very nice if each python snippet were run in the same global dictionary, so you could define a variable (or import a module) at one point and use it throughout.

Finally -- certain languages (VHDL,
Makefiles, Python, for instance) do
not have multi-line comments, so
you can't use it with these, unless
I am missing something. May I suggest:
if the [[[cog is preceded by some text on its line, then all lines between that
and the ]]] are checked to see if they
start with the same text, and if so, that is discarded before further processing.

You can come close with Cog as it is. Remember that you can import any Python module you want into the cog code. By moving all of your Python code into another module, you can reduce the Cog code to a single import statement:

The mission is to eliminate any work PHP might do that relates to application configuration or the environment it's running in - stuff that won't change once an app is deployed so eliminate the runtime overhead.

One particular thing I want to keep is the ability to execute the PHP scripts, while hacking, before they have been run through COG, e.g.

May even ditch the require_once completely and have COG embed the class code directly into the script.

Anyway - thanks.

Theo Burt 3:30 PM on 4 Aug 2005

Just what I'm looking for, thanks! However, am using it to generate multiple source files from each template file (ie. feed a different xml config file in to the template to generate a unique file). Is there any way to pass an argument (e.g. a file name) through the command line invocation?

You know what COG really needs? A "COG-recipes" site. I'm sure people have developed some interesting scripts.

One script I'm interested in (that I'm sure others would be too) is a script that will generate C++ functions that will translate enum values to and from string representations. Having a recipes site would allow me to share my code as well as allow others to give feedback about my script.

can you use COG to make codes out of videos andpictures and soundfile? for instance on myspace you can go to websites and get html codes for videos, wich resemble COG in a way, would you be able to e-mail me backon this subject?

Hi, this tool certainly looks very promising, I'm going to give it a go to clean up some of the internals used in aqsis (www.aqsis.org). The other devs favour xsltproc at the moment - I'll see if I can convince them and myself...

One thing I noticed after just installing cog-2.0 from the tarball is that cog.out no longer seems to recognise the trimnewlines command. After grepping the source I see that it's apparently been changed to trimblanklines?

kyle 12:02 PM on 13 Apr 2007

is anyone willing to try and teach me this stuff ? i find it really interesting.. but i don't expect anyone to take me up on this, it seems really complicated. thats why im so interested.

Jay: this code is behaving as intended. I've edited the description of indentation to try to make it clearer. The dedent parameter doesn't affect the indentation of the line in the output, just the interpretation of a multi-line string parameter. Cog collects output as a block of text, then indents the block to match the generator. The dedent parameter affects how cog adds the lines to the collected output, but now how that output is finally written.

To see what the dedent parameter does, try putting five spaces at the left of one of your lines, and try it with dedent=True and dedent=False to see the difference.

Sorry for the confusion. If you need the output block indented differently, indent your entire cog block to where you want the output to go.

Bob Lauer 10:41 AM on 22 Apr 2008

Hi Ned,

This is some neat python app :-)

I would like to try it for some Java apps around here. Has anyone tried whether
it works with jython? If so I could easily use it from ant and would not
require people to install Python.

Bob Lauer 10:54 AM on 22 Apr 2008

... I just checked, it seems that jython won't work since cog requires the compiler package (which isn't available on jython).

I've never used Jython, but yes, Cog depends on being able to execute the chunks of Python code it finds.

bussiere 2:57 PM on 24 May 2008

nice tool
thanks

glen worstell 7:35 PM on 27 May 2009

I happily used cog 3 or so years ago on a C# project. Now I need it again! Thanks...

FYI, I have code that users must run, and the code has strings with passwords in them. I don't want users to be able to find passwords by looking at the code they run (strings show up in binary code). I'll use cog to generate encrypted strings, and when the program runs the strings will be decrypted. Users won't be able to see clear text passwords no matter what they do, except if they figure out the decryption code. This is not NSA type security, it is for ordinary users.

Of course I could manually encrypt the password strings; then I'd not need cog. But that is a lot more work as there are several programs that will use this technique, and passwords change from time to time. The python code will be very short.

I prefer Enhanced CWEB and yesweb to do some of these things. (Enhanced CWEB will generate #line directives in the output file, and you can use metamacros and named chunks to do all sorts of things. This program can be used with C and C++.) (yesweb is written entirely in TeX, so you can use any TeX commands to extend its features.)

Hi,
I have code that has Windows line endings and got syntax errors on Linux because of them. Other developers use only Windows so changing the line endings of the file is a bit too tedious. To fix this, I had to modify the evaluate method to convert \r\n line endings to \n before running the code:

I like it. I use it.
I would like the ability to add comments in my C code (like "do not edit" or "generated from somefile.txt"), which is a bit awkward since the closing comment mark terminates the cog code.

I added this to my local version, which would be nice in a future release:

@Brad: I would rather not add a language-specific method like c_comment. Couldn't you output the comment closer as two strings, or with a backslash: cog.outl("/* comment *\/")

Brad Hards 2:07 PM on 22 Dec 2010

@Ned: Sure. I'm ok with keeping it as a local change - just thought it might be an interesting addition.

Splitting the comment isn't very "attractive", which is why I went with a wrapper (not dissimilar to how you don't _really_ need outl() - you could just use out() and add a "\\n" each time.)

Thanks again for providing cog!

Peter N 4:31 PM on 29 Dec 2010

When calling cog.py, it seems that it passes in sys.argv and modifies it. We like to do things locally that rely on sys.argv (mainly passing in the name of a program to databases so we know what/who's logged in).

Would you consider either having cog.py pass in list(sys.argv) to pass in a copy of sys.argv, or in cogapp.py set argv0=argv[0];argv_rest = argv[1:]; and then use argv_rest for parsing instead of doing argv.pop(0)?

Candy 6:35 PM on 30 Apr 2011

Sorry for asking stupid question. Does this example still work?

import cog
cog.outl(" extern void simple1();")

I am having trouble running this in Eclipse/Pydev. Thanks for your help.

Roger Alexander 11:02 AM on 27 Jun 2011

A useful feature would be to have an option that would allow the output of running cog to accumulate between ]]] and [[[end]]] rather than have it deleted. An example use case for this would be a file whose purpose is to accumulate data that is produced periodically, such as from a weather data generation service. This would be a great application for cog is there were someway to have it not deleted what was generated from previous runs. Further, it would be useful to specify that this behavior should per block rather than per file. This perhaps could be implemented as a meta-variable on the opening delimiter, say something line "p[[[cog{retain}", or something to that effect.

What do you think?

Anr 11:06 PM on 15 Sep 2011

Hi Ned,
A very useful tool.
I would like to know whether, if I import one of my module in one file, then can I use the functions present in that module or any python variable defined in that cog file in other file ?

Spencer Rathbun 12:43 PM on 19 Sep 2011

Hi Ned,

I'm using cog for automating some code generation from a text file, and I love it. It works great for removing some of the repetition from my day. Unfortunately, I've come across a bug. I read my input text file, and store my code lines in a dictionary of lists.

When I come to a specific section I loop over each line in each list, in the appropriate dictionary entry, and print them with cog.outl(). All the lines start with \t to indent them appropriately, but cog strips this out, unless I also print a line with no indent.

Arun 10:03 PM on 6 Dec 2011

I have found a bug (Might not be after all).
Currently with the latest COG available for download, I am unable to indent the code with '\t'(tab) in the beginning.
I am using cog.out and cog.outl for creating the code statements.
Currently I have changed the "reindentBlock" function in "whiteutils.py".
Changed statement to "l = l.replace(oldIndent, '\t', 1)".
Can you please check if it is a bug or am I missing something ?

Great!, this is what I found.
inline template is very interesting to maintain codes.

Stefan Ross 1:35 PM on 23 May 2013

Thanks for the useful tool!

It would be helpful if you added an example for how to use -D parameters. It took me a while to realise that they are visible to the code in the template, but not to the imported code, i.e. I needed to pass in the globals as parameters to my generator code.

Brecht Machiels 9:24 AM on 2 Jul 2013

Very useful tool... thanks! Using it for C++ code generation.

Please consider changing line 637 in cogapp.py to (and importing glob) to allow for wildcards on Windows:

for filename in glob.glob(args[0]):
self.processOneFile(filename)

Jean-François Giraud 8:34 AM on 7 Feb 2014

Thanks for this tool!

It would be usefull to automatically create directory when option -o is used.

Veronika 10:34 AM on 14 Mar 2014

I use cog in my C++ project to encrypt data. And I've got a question.. Does cog work with unicode? The problem is that I try to get character code in unicode project. On my individual python project it works:

Small request: possible to add option to set verbosity, with following possibilities:
- same as now, show changed files, and unchanged files
- show only changes files
- no output at all, completely silent

The advantage is that currently, I've added it to my cmake file, with a wildcard, like 'cog.py *.h', and I'd prefer to only be notified when something change.d

This code will result in a syntax error if it is placed in the beginning of a file encoded in UTF-8. The first line starts with '<BOM>; ' while the others just with '; ', therefore, semicolons are not removed.

You'd better open all files with encoding="utf-8-sig" to avoid such untrackable bugs.

Awesome tool! The only serious limitation I struggle with is the missing ability to "protect" parts of the generated code.
Typically, one may want to generate a skeleton that they can then fill-in with manually written code. If the generation is re-run, the manually written code should be preserved. Do you you think you could add such a feature?
Thanks for the amazing piece of code you wrote! The "dedent" feature alone is worth the price of the ticket ;-)