pax_global_header00006660000000000000000000000064141457570750014531gustar00rootroot0000000000000052 comment=1050aad948e2de6cca56d8e14b8dc1cbb7890c9e cog-3.3.0/000077500000000000000000000000001414575707500123045ustar00rootroot00000000000000cog-3.3.0/.coveragerc000066400000000000000000000004651414575707500144320ustar00rootroot00000000000000# coverage configuration for Cog. [run] branch = True parallel = True source = cogapp [report] exclude_lines = pragma: no cover raise CogInternalError\( precision = 2 [html] title = Cog coverage [paths] source = cogapp # GitHub Actions uses a few different home dir styles */cog/cogapp cog-3.3.0/.editorconfig000066400000000000000000000010521414575707500147570ustar00rootroot00000000000000# This file is for unifying the coding style for different editors and IDEs. # More information at http://EditorConfig.org root = true [*] charset = utf-8 end_of_line = lf indent_size = 4 indent_style = space insert_final_newline = true max_line_length = 80 trim_trailing_whitespace = true [*.py] max_line_length = 100 [*.yml] indent_size = 2 [*.rst] max_line_length = 79 [Makefile] indent_style = tab indent_size = 8 [*,cover] trim_trailing_whitespace = false [*.diff] trim_trailing_whitespace = false [.git/*] trim_trailing_whitespace = false cog-3.3.0/.github/000077500000000000000000000000001414575707500136445ustar00rootroot00000000000000cog-3.3.0/.github/workflows/000077500000000000000000000000001414575707500157015ustar00rootroot00000000000000cog-3.3.0/.github/workflows/ci.yml000066400000000000000000000037651414575707500170320ustar00rootroot00000000000000name: "CI" on: push: pull_request: defaults: run: shell: bash jobs: tests: name: "Python ${{ matrix.python }} on ${{ matrix.os }}" runs-on: "${{ matrix.os }}" strategy: fail-fast: false matrix: os: - ubuntu-latest - macos-latest - windows-latest python: # When changing this list, be sure to check the [gh-actions] list in # tox.ini so that tox will run properly. - "2.7" - "3.5" - "3.6" - "3.7" - "3.8" - "3.9" - "3.10" exclude: # Windows 2.7 doesn't work because Microsoft removed stuff we needed. - os: windows-latest python: "2.7" steps: - name: "Check out the repo" uses: "actions/checkout@v2" - name: "Set up Python" uses: "actions/setup-python@v2" with: python-version: "${{ matrix.python }}" - name: "Install dependencies" run: | python -m pip install -r requirements.pip - name: "Run tox for ${{ matrix.python }}" run: | python -m tox python -m coverage debug data - name: "Upload coverage data" uses: actions/upload-artifact@v2 with: name: covdata path: .coverage.* combine: name: "Combine and report coverage" needs: tests runs-on: ubuntu-latest steps: - name: "Check out the repo" uses: "actions/checkout@v2" with: fetch-depth: "0" - name: "Set up Python" uses: "actions/setup-python@v2" with: python-version: "3.8" - name: "Install dependencies" run: | python -m pip install -r requirements.pip - name: "Download coverage data" uses: actions/download-artifact@v2 with: name: covdata - name: "Combine and report" run: | python -m coverage combine python -m coverage report -m cog-3.3.0/.gitignore000066400000000000000000000003471414575707500143000ustar00rootroot00000000000000# Files that can appear anywhere in the tree. *.pyc *.pyo *.pyd *$py.class *.bak # Stuff in the root. build dist .coverage .coverage.* coverage.xml htmlcov MANIFEST setuptools-*.egg cogapp.egg-info .tox .*cache cog-3.3.0/.treerc000066400000000000000000000001421414575707500135660ustar00rootroot00000000000000[default] ignore = .treerc .tox .coverage* htmlcov build *.egg *.egg-info cog-3.3.0/AUTHORS.txt000066400000000000000000000003351414575707500141730ustar00rootroot00000000000000Cog was written by Ned Batchelder (ned@nedbatchelder.com). Contributions have been made by: Alexander Belchenko Anders Hovmöller Blake Winton Doug Hellmann Daniel Murdin Hugh Perkins Jean-François Giraud Petr Gladkiy cog-3.3.0/LICENSE.txt000066400000000000000000000020641414575707500141310ustar00rootroot00000000000000MIT License Copyright (c) 2004-2021 Ned Batchelder Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cog-3.3.0/MANIFEST.in000066400000000000000000000001441414575707500140410ustar00rootroot00000000000000include README.rst include LICENSE.txt recursive-include scripts *.py recursive-include cogapp *.py cog-3.3.0/Makefile000066400000000000000000000024531414575707500137500ustar00rootroot00000000000000# Makefile for cog work. .PHONY: help clean sterile test kit pypi testpypi publish help: ## Show this help. @echo "Available targets:" @grep '^[a-zA-Z]' $(MAKEFILE_LIST) | sort | awk -F ':.*?## ' 'NF==2 {printf " %-26s%s\n", $$1, $$2}' clean: ## Remove artifacts of test execution, installation, etc. -rm -rf build -rm -rf dist -rm -f MANIFEST -rm -f *.pyc */*.pyc */*/*.pyc */*/*/*.pyc -rm -f *.pyo */*.pyo */*/*.pyo */*/*/*.pyo -rm -f *$$py.class */*$$py.class */*/*$$py.class */*/*/*$$py.class -rm -rf __pycache__ */__pycache__ */*/__pycache__ -rm -f *.bak */*.bak */*/*.bak */*/*/*.bak -rm -f .coverage .coverage.* coverage.xml -rm -rf cogapp.egg-info htmlcov sterile: clean ## Remove all non-controlled content. -rm -rf .tox* -rm -rf .pytest_cache .mutmut-cache test: ## Run the test suite. tox kit: ## Build distribution kits. python -m build twine check dist/* pypi: ## Upload kits to PyPI. twine upload dist/* testpypi: ## Upload kits to test PyPI twine upload --repository testpypi dist/* cogdoc: # Normally I'd put this in a comment in index.px, but the # quoting/escaping would be impossible. python -m cogapp -crP --markers='{{{cog }}} {{{end}}}' index.px WEBHOME = ~/web/stellated/pages/code/cog publish: ## Move doc page to nedbat.com home. cp -v *.px $(WEBHOME) cog-3.3.0/README.rst000066400000000000000000000023261414575707500137760ustar00rootroot00000000000000=== Cog === Cog content generation tool. Small bits of computation for static files. | |license| |versions| |status| | |ci-status| |kit| |format| See http://nedbatchelder.com/code/cog for details. Code repository and issue tracker are at `GitHub `_. To run the tests:: $ pip install -r requirements.pip $ tox .. |ci-status| image:: https://github.com/nedbat/cog/actions/workflows/ci.yml/badge.svg?branch=master&event=push :target: https://github.com/nedbat/cog/actions/workflows/ci.yml :alt: CI status .. |kit| image:: https://img.shields.io/pypi/v/cogapp.svg :target: https://pypi.org/project/cogapp/ :alt: PyPI status .. |format| image:: https://img.shields.io/pypi/format/cogapp.svg :target: https://pypi.org/project/cogapp/ :alt: Kit format .. |license| image:: https://img.shields.io/pypi/l/cogapp.svg :target: https://pypi.org/project/cogapp/ :alt: License .. |versions| image:: https://img.shields.io/pypi/pyversions/cogapp.svg :target: https://pypi.org/project/cogapp/ :alt: Python versions supported .. |status| image:: https://img.shields.io/pypi/status/cogapp.svg :target: https://pypi.org/project/cogapp/ :alt: Package stability cog-3.3.0/changes.px000066400000000000000000000246551414575707500143010ustar00rootroot00000000000000 Split out from the main page. 2.1: -U flag More 2.1 stuff Add a pointer to the Russian. Started the 2.2 list. 2.2 2.3 2.4 2.5.1 3.0.0 3.1.0 An older version of this document is also available in Russian.

These are changes to Cog over time.

Version 3.3.0, November 19 2021

Version 3.2.0, November 7 2021

Version 3.1.0, August 31 2021

Version 3.0.0, April 2 2019

Version 2.5.1, October 19 2016

Version 2.5, February 13 2016

Version 2.4, January 11 2015

Version 2.3, February 27 2012

Version 2.2, June 25 2009

Version 2.1, May 22 2008

Version 2.0, October 6 2005

Incompatible changes:

Beneficial changes:

Version 1.4, February 25 2005

Version 1.3, December 30 2004

Version 1.2, December 29 2004

Version 1.12, June 21 2004

Version 1.11, June 5 2004

Just bug fixes:

Version 1.1, March 21 2004

Version 1.0, February 10 2004

First version.

cog-3.3.0/changes_ru.px000066400000000000000000000211051414575707500147720ustar00rootroot00000000000000 Split out from the main page. Alexander's translation from changes.px Alexander's updates to 2.1

Список изменений в версиях Cog.

Version 2.1, 22 мая 2008

Версия 2.0, 6 октября 2005

Несовместимости:

Улучшения:

Версия 1.4, 25 февраля 2005

Версия 1.3, 30 декабря 2004

Версия 1.2, 29 декабря 2004

Версия 1.12, 21 июня 2004

Версия 1.11, 5 июня 2004

Исправлены ошибки:

Версия 1.1, 21 март 2004

Версия 1.0, 10 февраля 2004

Первая версия.

cog-3.3.0/cog-success.rst000066400000000000000000000236551414575707500152670ustar00rootroot00000000000000============================================= Cog: A Code Generation Tool Written in Python ============================================= :Category: Business :Keywords: cpython, code generation, utility, scripting, companion language :Title: Cog: A Code Generation Tool Written in Python :Author: Ned Batchelder :Date: $Date: 2004/05/25 21:12:37 $ :Websites: http://www.nedbatchelder.com/ :Website: http://www.kubisoftware.com/ :Summary: Cog, a general-purpose Python-based code generation tool, is used to speed development of a collaboration system written in C++. :Logo: images/batchelder-logo.gif Introduction ------------ `Cog`__ is a simple code generation tool written in Python. We use it or its results every day in the production of Kubi. __ http://www.nedbatchelder.com/code/cog `Kubi`__ is a collaboration system embodied in a handful of different products. We have a schema that describes the representation of customers' collaboration data: discussion topics, documents, calendar events, and so on. This data has to be handled in many ways: stored in a number of different data stores, shipped over the wire in an XML representation, manipulated in memory using traditional C++ objects, presented for debugging, and reasoned about to assess data validity, to name a few. __ http://www.kubisoftware.com/ We needed a way to describe this schema once and then reliably produce executable code from it. The Hard Way with C++ --------------------- Our first implementation of this schema involved a fractured collection of representations. The XML protocol module had tables describing the serialization and deserialization of XML streams. The storage modules had other tables describing the mapping from disk to memory structures. The validation module had its own tables containing rules about which properties had to be present on which items. The in-memory objects had getters and setters for each property. It worked, after a fashion, but was becoming unmanageable. Adding a new property to the schema required editing ten tables in different formats in as many source files, as well as adding getters and setters for the new property. There was no single authority in the code for the schema as a whole. Different aspects of the schema were represented in different ways in different files. We tried to simplify the mess using C++ macros. This worked to a degree, but was still difficult to manage. The schema representation was hampered by the simplistic nature of C++ macros, and the possibilities for expansion were extremely limited. The schema tables that could not be created with these primitive macros were still composed and edited by hand. Changing a property in the schema still meant touching a dozen files. This was tedious and error prone. Missing one place might introduce a bug that would go unnoticed for days. Searching for a Better Way -------------------------- It was becoming clear that we needed a better way to manage the property schema. Not only were the existing modifications difficult, but new areas of development were going to require new uses of the schema, and new kinds of modification that would be even more onerous. We'd been using C++ macros to try to turn a declarative description of the schema into executable code. The better way to do it is with code generation: a program that writes programs. We could use a tool to read the schema and generate the C++ code, then compile that generated code into the product. We needed a way to read the schema description file and output pieces of code that could be integrated into our C++ sources to be compiled with the rest of the product. Rather than write a program specific to our problem, I chose instead to write a general-purpose, although simple, code generator tool. It would solve the problem of managing small chunks of generator code sprinkled throughout a large collection of files. We could then use this general purpose tool to solve our specific generation problem. The tool I wrote is called Cog. Its requirements were: * We needed to be able to perform interesting computation on the schema to create the code we needed. Cog would have to provide a powerful language to write the code generators in. An existing language would make it easier for developers to begin using Cog. * I wanted developers to be able to change the schema, and then run the tool without having to understand the complexities of the code generation. Cog would have to make it simple to combine the generated chunks of code with the rest of the C++ source, and it should be simple to run Cog to generate the final code. * The tool shouldn't care about the language of the host file. We originally wanted to generate C++ files, but we were branching out into other languages. The generation process should be a pure text process, without regard to the eventual interpretation of that text. * Because the schema would change infrequently, the generation of code should be an edit-time activity, rather than a build-time activity. This avoided having to run the code generator as part of the build, and meant that the generated code would be available to our IDE and debugger. Code Generation with Python --------------------------- The language I chose for the code generators was, of course, Python. Its simplicity and power are perfect for the job of reading data files and producing code. To simplify the integration with the C++ code, the Python generators are inserted directly into the C++ file as comments. Cog reads a text file (C++ in our case), looking for specially-marked sections of text, that it will use as generators. It executes those sections as Python code, capturing the output. The output is then spliced into the file following the generator code. Because the generator code and its output are both kept in the file, there is no distinction between the input file and output file. Cog reads and writes the same file, and can be run over and over again without losing information. .. figure:: images/cog-web.png :alt: Cog's Processing Model *Cog processes text files, converting specially marked sections of the file into new content without disturbing the rest of the file or the sections that it executes to produce the generated content.* `Zoom in`__ __ images/cog.png In addition to executing Python generators, Cog itself is written in Python. Python's dynamic nature made it simple to execute the Python code Cog found, and its flexibility made it possible to execute it in a properly-constructed environment to get the desired semantics. Much of Cog's code is concerned with getting indentation correct: I wanted the author to be able to organize his generator code to look good in the host file, and produce generated code that looked good as well, without worrying about fiddly whitespace issues. Python's OS-level integration let me execute shell commands where needed. We use Perforce for source control, which keeps files read-only until they need to be edited. When running Cog, it may need to change files that the developer has not edited yet. It can execute a shell command to check out files that are read-only. Lastly, we used XML for our new property schema description, and Python's wide variety of XML processing libraries made parsing the XML a snap. An Example ---------- Here's a concrete but slightly contrived example. The properties are described in an XML file:: We can write a C++ file with inlined Python code:: // SchemaPropEnum.h enum SchemaPropEnum { /* [[[cog import cog, handyxml for p in handyxml.xpath('Properties.xml', '//property'): cog.outl("Property%s," % p.name) ]]] */ // [[[end]]] }; After running this file through Cog, it looks like this:: // SchemaPropEnum.h enum SchemaPropEnum { /* [[[cog import cog, handyxml for p in handyxml.xpath('Properties.xml', '//property'): cog.outl("Property%s," % p.name) ]]] */ PropertyId, PropertyRevNum, PropertySubject, PropertyModDate, // [[[end]]] }; The lines with triple-brackets are marker lines that delimit the sections Cog cares about. The text between the **[[[cog and ]]]** lines is generator Python code. The text between **]]]** and **[[[end]]]** is the output from the last run of Cog (if any). For each chunk of generator code it finds, Cog will: 1. discard the output from the last run, 2. execute the generator code, 3. capture the output, from the cog.outl calls, and 4. insert the output back into the output section. How It Worked Out ----------------- In a word, great. We now have a powerful tool that lets us maintain a single XML file that describes our data schema. Developers changing the schema have a simple tool to run that generates code from the schema, producing output code in four different languages across 50 files. Where we once used a repetitive and aggravating process that was inadequate to our needs, we now have an automated process that lets developers express themselves and have Cog do the hard work. Python's flexibility and power were put to work in two ways: to develop Cog itself, and sprinkled throughout our C++ source code to give our developers a powerful tool to turn static data into running code. Although our product is built in C++, we've used Python to increase our productivity and expressive power, ease maintenance work, and automate error-prone tasks. Our shipping software is built every day with Python hard at work behind the scenes. More information, and Cog itself, is available at http://www.nedbatchelder.com/code/cog About the Author ---------------- *Ned Batchelder is a professional software developer who struggles along with C++, using Python to ease the friction every chance he gets. A previous project of his,* `Natsworld`__, *was the subject of an earlier Python Success Story.* __ /success&story=natsworld cog-3.3.0/cog.png000066400000000000000000000236041414575707500135670ustar00rootroot00000000000000PNG  IHDRls bKGD pHYsNtIME)?u IDATx]r۸PVOd顧3cz>i `T$K( = {X&''3{gd[oߒN(Q[x<<҇>R؀C#Jy<mR؀ݦFJ l۩mR؀͖FJ lSS2 6 XuTG l=B5R1%92TK`x Fۀ=6?5Iht]w~ħygml@Qe #AR؎< m z4v5gm6hU#83;hؠ!Wڴξ(m 1RJ`K#A6\љ[ia $ArȴRNg/m *t(L-2ؠ"wخF۠ Tіw6(\"|(mHO`BE+˝"}hm8O`E)!Nc6(H䑓%?rؠGK\X&ApD,W+Җ lXI"%+KjW; *q$r>m Z&A0~XR+ޠ5QG%?mZ A5rXRVܨ|$D`6q6 />ߧNc<s>:5uO(:q&-myQB6ȨQm[ۖ &mWsRlA #Wl: lZhket"e`9I[y]+"HDڇmkl= lpG!r%:~|j>%z9E`Zyh1 [n>j`[op5 N2:\I`*|h=,I]끥?f6Kil³,W`ckw0Yсs6X!m#j!:']X5:uՂs&mg6`$`;Fئ[KN7Y` F# lGX` 9ۘe lYO!R`[γh5uGG끥*|huGGuFKdFXS_w68l?pKcuճv.E9gZ%Q-kۤ Z۹?ںNpl"*Y &eخ l)sZ"QkۥX[KZ~XiF5mosQ[MSZnS;YP%]Z^P3Y@%7u%FIseZsE`Vj#QkyB`֘="z[wP"XK玂z[(Fh:*!V)FH1wÃ\} ~ l=8eP 0XG#R{(Fr;$ E;DQD!"ظ5h[i^JO"*X=.bA8O9E4'qSj= D$*"ZqыoZuJ",SB-aGRø嬝SJ-e>Gޜ^wظ5sJ+Jgd6 q kXTKJ%kd9rHʚyR;>i>\6y^EyO_B["}__ F8ĚdJ]C-+Q#IM`cR$ZJ>jiQC`c3kRi/(`66)EҲo)ac5tjGHYV#O(Y5(j+|65|O(7$ ^ !$p$5EPKBSV&Hl!u]%6rN-_m+!FDg.=9:߾}'3[ìͥWk!VҨ{GQS[! AQkS8ҪGfH`kLM"Z rZ~$5aM5\S$U+]OPSaDwmsy+UujkB5~ϵ/$UBEIɫe$XVHG{Jvlӏs{ m~Au-EqU|\%~F gZ(ZV(jmG eMz{,-VQ@vQTXj]ZY ֘R1ёVrQ; Ep0B^P#lY3ʣ.CSk]jJjﬣhhhWejmvU#- k@ytpAO']7jMY`jh@h[ekq*V-t5|Z k]LZ>s2a$V5:;F&%ݨ7VvVV~V(Zig%n` &֩UhZY+m-wZZ}[^hE'ebM%;zlZ[^P( ZT#C=KYgBHөP[me~j=։F#ZnYvw7-1kЁ<[oAG`K3 ǻځe?t5谡]d. 2b{bi dM>::F.ŢF'bgA;dICK #dVۉ"3 ljJBFVX3R~cۋz.IhGbjM%Mp?tprџĤ#ܩEsvI -#WrA#O \W* atORҞ7il} :y:/]w"S5tX14i\ID'}\`YSLA;@;}L`pp29ioB[ F!šc/e&|M"EX[rg}Z6P,:u,w>Z.#__~e"z;)og-&l(yx7-*:h)aBGWap4Jw?_ *~TTg+D>,b[ISE_Ƕ] iݵZe1#t$ɺt mXNg,5[Tq;#㷬"PA:8Na@ K]zRUՒk%oRa((u^b+Oə"t`Iij|՞m iB[Jl![ f[;ƴiB[J!.:F&\A{fd\0t[3/$a+%^9ڶP^f,W}k`+!Q92Pศfafw?}glQ+"E,Y:`Ck8ox;daA!k8_5Z"2Vܕm. l^Z倶nQ5ZH 5rf͂JkyJ!ur$GUw :=fMVeA`GmUWÁͨ-(y\6 !- lpG;R/hY+UuwjGB-K6t`{kǯ۾c|oVgrQ5xgt>wR+p.S-믿]?p2B\onZ/Z l:7xgYϖ~nnap#xwdY Aბ.(( J>:(Y\iamߧ@)6k#js;?}%㜰xDMx[S6ZP 4Fl l lMWt磃l l lMWtv㑬ЦQS躋[{^R(Aujz<ID`ޟ=6|ߧwS=]{@KԔ_{ߵQd0\pzpϙkㅥs]<.ݏV Yz hk S4_[B pV>s$mii'ij~%;|&GTb4^c 5EM\f4A> :w#`n:LoN5w[/TKc_yܻ߭/–*X)j N \[&wɉss[:񙳧Τ=>#{6uj g^e@CR/@Jj Q֮ݖ=<ǦvoookuݧSLq~~nGU~R1Dx{{5v;өBDtjt||>+BTU- _/ oO צǦ^6l>~|9SSvt_6}o gEsjjj Q@666 #'mԟORSK`(& 1C6]Ajpzlx]{YKp?5TƎ6ԩ]=l 65\ l)] cv),i;_.칶#h_-Xq)RxyXz>7O]l]PqZbSSCsDJ#4ƨ G`N`N`l68G`N`N`l666668!3G`N`N`l68!3珵'14@Mc'A`6]u6.kǏտmVoKw* x{oC_{*+fVSÁm>6tuĭka?~8ilhxSS`YX"-`yoX"p蠃H ^GK:x<~TWM6oSLgIܵ).[5DjhBWLm϶+)a[kB]\|7GMmE4ܱ屩!74Y5 l 9t{nRR=*ՑPSîvvWqΆ>5EMasa{<t/_d8'۞USXMq-Ql&?3|51\->wP}uԔX?~E%hsr}hmJWmjJRva#~Y㿿L>?6~Ҵ^_zϔrR]~|.H@^j2>5Քl#l? ܩ?6~|csz3~ߵkw{zCSϙ)jkj)tp]NfxosִCM 54Ք;6 \@|tf}3EԔs"daKyN3]l[.lkY5<{t fĤN~CMFM)_KS.Sti[eK榽nΪyKS muJjRW::)/M_#9g;g>3㵪+H))e G&BXڈFMCM'A?L罵 숸SMyZc@QSSSʔ+֌8Ԏif|u[^fiKM=4Sp'5EMiE挻:)2pL \EM!#52\XzΖ65Ȋ֡`>%Y#l@^>ۿc>hal6ج }ׯ_Ƕ<^9q|@Jj l; wlylso[Տԝ j lSi= BeMɢM%W5[] _dj )&&^__?u{XR_5}ۼO1%)j I>-@ܥoMAn_1dj )&-&y9R-Jmj U UjHlքUSHSSԉs<#,XHs>q೚F.`TsZ)4к?3|51ҟ l@@tZOo!J?4 51c5Jsh`tft̂0Μ^%jՃ{kԖ[wjF5+Z?=lZ Sˁۿ3gU5e8a]֕}_75-Z?[_7Z LGwOh[~ysOE3T-]Vy.2~lꤌó[My>o`ennm}_}G|ioBilˆ/=2~l& mqpm+thKK(!)6vtX0y}}}SmrZ-xբo,7RS(A PDB=Dp4ڜ7|< tĹG s#~b%J]MMEKM={i?#A| ]\ke3mؠVg{&+ϡz///E>yʡp*N1wNO~ܭәza>|Ҿ"*~ohKL~pS iϋ5#خ6jRS+E]f Gۆ#8OVS 5fF]\#Rp0W&= ֆ=OJsm._?GM!1kIDATGضc0s8֦e{Yk Th;ROk)Dx.ǣҶ|z///_|}m[v);RS$!@͖GAש)W&ѫX j%H槦p6VG@8f]'4'幀ߝ:ׯݏ?V6|7Ƕ<~V?=uv#(0/QÆ4P-ǏOa{[z|wٕRS9҆ÁmЇ )wC[6ܵ绲&6'ΥMG۫r҆ an {sk"S9߹J)˪o5^Fآ%e^#6hs^__ow(w]]:SS(A8oÂwgs%5e;5~(ݖp\%:5<>fmi?ݕd9G஠ծ{ K`ѐSShW`K/v8K/4CN-X@ Ύ)5x<нw ƾ|qnLj ;RS\K1% Y$'|51t@ l l l l l)z" 8 8  P 8 8  P 8 8  P 8 8  P 8féHBM%M.צ IljJH=㑴 9S?g{mkm)~<%jZRdm5\;^X-K]2ڭ2T\'YVB޲qnn5# &5{w+YVrŽ:WWUtHMIOM)_l-X9}Z *5EM^35=#s:KOp$|ͱHMY\MiCu?{5ggWL;k-T@ԔՔ]_SE6\7@RZnJuvl\?k= ?s@A׽/ ooo%@ [CgGjM3HK` ! #py86 k @y666̦+N`h2Cy666̦+N`O`N`N`N`h2Cy666̦+N`h2Cy666+Rz< l l l lI %4BXr l lPE&A!lh#P6 r l֠Ʀ-lPG52kP?֞(@L}hS])pb`[[矤3@8&Q(XQ*as#K`nTK5!AVO/ yP76@ lЬѤq!(Q΀=LƯm5`#l9J eIENDB`cog-3.3.0/cog.vsd000066400000000000000000003320001414575707500135700ustar00rootroot00000000000000ࡱ> Root EntryRoot EntryFU{>VisioDocumentSummaryInformation( TDocumentSummaryInformation8x  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~VisioInformation" ՜.+,D՜.+,x@HP\t  Kubi Software PagesMasters  Page-1 Rectangle DocumentEllipseDynamic connectorRounded rectangleCircle tangent "NO" sign Flexi-arrowLine-curve connector8_VPID_ALTERNATENAMES>_VPID_PREVIEWS?_PID_LINKBASE8? A Oh+'0$HP\htneddC:\Program Files\Microsoft Office\Visio10\1033\Solutions\Block Diagram\Basic Diagram (US units).vstcogG$| ` EMFlx@VISIODrawingL||} ??d((}Visio (TM) Drawing lpqRp !fffMMM3338 TZ Arial@:NWingdzs@vNPuMonotype Sort m_Symbo^&N1Courier NewllCapsL\v@+ J"Xtnye x5T?? Y@-1U"J:DT1EW-hTT<* /Ub bO0zGz?@8@H2!kWbʁk !k9 +k)kO,O, P/8&9$? CAk&,,'5/G%&X$&  E& y  0)^?Tp2    U1?%aUBEEEUEEE@ON7OIJ`2BEEEEEEE@$O9FAY; ,Q*VV,QgTY 1 W-oW?o\o ' `3kulb6`i#l l l ҕY??\ #TD5r|O,O,!O,N/BQ|USME6A_h8OOOOO@O$O _HOZOlI`7rZW?_ep؂fsU妖jȟxҏk)qĬ T %1Wdv i| 9Si'0UP]y??4zy2Y#Q T~0{j :Ǯ/6c!3v!D//?Qp&v?d4,Qk(YU1%1λ-(kp 5 . $5-Bbi`w# *|)/й//??2 44ӶSPt߆27yՅ:]$NԵb}-?Qcuv")/"%)f'!U+%U2qa0?`?r6#!r6; <<7y/I[ײ|Q@й|Qy ??-?%GZ4%*4c bBF:/L/^/qZ?l3?!  "!@)"IAر1@//6aC,=:ɀBK` Rązh}q`PlFqB,a%u"2u?uWRLurYuMfuFsu Iuxu`⶧uxxuxux\BYF ۲&3ya'@r5M67Zر8g!:tyqA? OO(O:OՑI/F@OJC,Q%]e@Q#O[hW(%{)Ofmooo,$@Rny%qqA͏,OJdtNW3 $O贁NkɟLQo_ѿ~4@Q_EhvP j8഼gzx!m// .%Rd>XWUߟ"4F'9K]Rcϸ$6HZl~ߐߢߴ 2DVh5);Mf`!3E^Yqfx,>o//(/`!G/Y/k/}//////// ??1?C?U?g?y???????? O`sq(O:OLO^OpOOOOOOOO_Ub_afuE_W_i_{_______]oo*oPbtDߜ߮,>PbцÃŪ(:L7)k} 1Cg֯ -IL^p//$,B#h|xn3 bz@pq/'c/&p////%b?Ma4C2?D?V?h?z????????ΧB&K^ SOeO?}O&POOOOOO \_+_=_O_a_s_______<Oto 4B:DBI9o~,`o&~oooo%oZÑo 0BTfx_fx%jO|OOmǏى^ 1CUgy#}x1e/2/Vhz/??ǯٯ!3EWi{ÿտ /AS eϡ؂olf! ϧĹv$&ASi83OONkwȪs2q?8? QA.    1CHpvǚCj6Pb}ĕyI 03N+0"ɡk*yȈy (1<'9K]o/#5Gik}{?1CU?OCD-M //$/6/}%P/b/rԅ/////#D%_??%:3<IeJ5IeZ5I`e?OO ???>)SOs*}!_3_E_W_H/{_l/~/ְZ$_ Y]|53J&o /P/b/t////////??(?:?L?^?p????\\GEOM0T0R0hpdeskj0t520s051,Loc8On:y@D4v C @n"@1:BOTOfOxOOOOOOOO__,_>_P_b_t________oo(o:oLo^opoooooooo$6HZl~f q} DisplayUFDfP h> /T6DUmA@ ?ۿI?Y{O1@3EEbOeZ Hu P(:{W_qTFour-sided rectangle shape that can be customizby dragging any selion handle.mb?贁No?k?4 HD" # "=h-(>T;6UA?u?Q6 u`m u BAD00XA0(2s@sJLsT>5 LO@I5 I`?Copyright (c) 2001 Microsoft Corporation. All "s reserved.`Vis_Sba.chm!#26677Ad9 l>0>Udd !!T 6gbJC2 6D gs?2r4 ?`b"?U1f%a?z?%91U1|;6=!=5E2?;rA QBWCSq,^(;5/V:|L?C%=A,K?EFI=*3AONOOKAf%[6BWWV_Gr_U|\_>ZO_ Inha=J0Hgda*zTF!PF#f",s&exG#nRectangle,Four,sided,customizpdragging,sel{p* ,han}dpBasicpgnal,flow,systems,deq,control,ennpeerpPqf%QCT$@lrH'- !OyaGE-szFd$#..JB .x]`au]@+_D.]3aZ$} bPy  (4'649A;*N >[ UFDfP h> /T6DUmA@ ?[I?3 $- b^]b--x @p#^b fA>M,, '^fO - b, , 'eZ %,UH+ X?C)32IIIIagy ww5ϼ;̉BUse to add a document or filyour system diagram.nformation.mbؿ?8i6ؿ贁N7??k4 UHLD H# >h,JT]]MwMUA? ط?QR > i>[uA` 3u> >bmv v]MB] J\MAJ>@(P 4:*GPG::PGu/N@ To&2L>3#*"@S3#u1z4E;#`Vis_PEA.chm!#33007I`?Copyright (c) 2001 Microsoft Corporation. All 2s reserved.R!?9*#Lp blJ]Uhz ?4 >aBE@*'(Z3*}#$ sUA]#(@^)\ BK38:3*R(@"B/!Qf5@O@0UOAH_ZU52k_E>rM 5RS+BA89G$E_O[B pOOO('T3*A8Y[OFEa XbZ4oGoga^Yk}E}5_/_A_S\WEj_|__y}OOlQPEa_jX [ PAj8?AeqZ(b5)mViCK}F*%#L/\x65#:Lpd7ɟkBe~u]3],EGigBdJnT@A#3;4>1f238cTu1GE3;Document,Use,add,file,system,diagram,EnterprivApoplic2Bf5ST$$@BHu-~ %JgNQ6EZjFO-%#C.!B -]Ja]@+/.]5a-}#PUFDfP h> /T6DUmA@ ?ۿI?Y{O1@3EEbOeZ HG 9V?o)>  `r/`wpw> ȳpke{rpgTYY9 zmAUA[? @ w?Q6 u` ?u ~YA|XA444> !bJfvq >!"q06DNX l>U5 L@v%#A?A ?AL#l%u%?P6 @U  TL#& " " 'R %#W1\!\!5 I"`?Copyright (c) 2001 Microsoft Corporation. All 2s reserved.`Vis_Sba.chm!#26667d(,9 l>(>Ud!(!(ZHU$B$bW* `ao@U+ko5aoPt4![@&@ r/*%A-Op/87"At4![@&@ Ds/x%A-Or/7"A t4![@&@ ds/Ư%A-\37"A(%t4[!@&@ /&A-|s/74t4![@&@ 9W&A-3e7"A6t4!@&@ 9_&A-_97;9t4!@&@ 9_&A-D /';U;<=t4![@&@ l;L'C-O;`7"AU<=>t4!@&@ ;_'C-_;7A @\:%>R@ %>R@D%<R@&9R@\-g&<R@ܠ9&@R@}. 'AR@/b'>R@;'ARH<( H<( H<( H<( H<( H<( H<( H<( H<(  E̽( RE( RE ) RE) REd9#) REt90) REܐ9=) RE|;J) RE;W) RUFDf h-TUUU?@ ?6I?d XboqYk*Qu23u ` Connector ` e1Crw UH ^   -}|5 p`I`V?}`xa833ސ]3σ3u 33?, ?Gxpx^& CThis connector automatically routes between the shapi ts.HD @# =h8T YY9 BR#F oU@? P6 u `u bA@]u  .(#DB uu`h?\hr|uVa@-?bl;'bE-ho'$y( 2rq?@I ?$%? @"U*5L -br  ^vv"(2uI."q28v"uh9Bd&</MS~ #145 |`Vis_SE.chm!#20)@E`;Copyright (@1 Microsoft Corporation. All ;Bs reserved.4U#1#7A,@+4j'TvQ8l>$Ud vE \/4 *&1$b24R(a[z[D ZQi a59 93O'2"q?2g;2GH'w* O`EW )F_[-X#T.$+7B .[,GGdo@D.UFDfP h> /T6DUmA@ ?ۿI?Y{\ $Oc@SeeeZ? $H S &4G ?rp{ri^ARectangle shape with variabcorner rounding.ers on r.mb?贁N? ?4 HDB $# ?h-(>TT9 AUA@V ?w?Q6 V ApL??[([8u`  u>d7 ; B@S(!-$5 I`?Copyright (c) 2001 Microsoft Corporation. All R"s reserved.`Vis_Sba.chm!#26679l> >Ud]!H!=2?!<zr r<%G"b^bQ6J3 . -3!31 #y ,[2):?AԲ??$6#8$3 'di9v20@r"P6 "Q5 3ҡ2G 2GBG22B (;?0D:BE3P81|j)<7G B! D#cG"IEBI3A!K):0A6CPHBFh322p):F%][DR&# YR!CI5XZ3k_AE2_3FT UFdB!J\868DcfJDNo3foH6:J\lq!,^!U/&37784 5" !C|h]Tb(1{w?w rV75x,r!CY^S(1%qÅ|\}!3dA!a| \=op 6j0d; a09F C Bh0JTh]]MzMU@?@AJP >Im>uA` 7?7uB >bqz z~LM ,Y AA J"%j Y""""Z"z",'J6>UIHtbUr &!>b%GJ#*1/4I `?Copyright (c) 2001 Microsoft Corporation. All T2s reserved.>`Vis_PE.chm!#2750O0## GMA"?J*8>KTBFA> T"8zO#"NCB9eud@u`"@S ;Eu]`lJ$JUh!5I +1abRajSqP(#@O"?@ɿt?(]GbrYjP@bBQRwCZ&\¦Rd#B(RT33Py>R ObUcCAcwb QAQT7 L_U LbTrTp_`TF/CaJqOGX|h(TO>;F&@HC~0OhJKN3vODqDr;Htq5}MfM]e 6u)>EpMei+5oGc_K*1M$$qzbRhPHb]c [e̓j`WA g.+ %`Reposit1 Tangent EndpointdACeP5 %Iv:F?P݀,u[?P y?P`?C@ʐ.#S@3UeV7֓Yb䔂֠ [  @ ٢hSФޣcfVz]cRQ&GSGSR#x%X!3?зv-ϻqCSd֠PɥZsTba lh>}g>qWPg$_6_A\zH MnOF%N_l,׎і%nՈ3''2q`Ү"_BeM&M ר]g$h(e7nTA聠[AA2(*1(Tb0hCircle,t,,Drag,add,protruding,line,,sn@wQTool,graphics,architectural,dos5OcTC$@$H n   F24K7#,548F CY]gat64]D+}]TAYY9 #AUA?i@? w?P6 @t#`  3>?Ctp  Uu3UuT3YAbAW@>GU2q??3=)(;\?0" 2)$j @=SBq!$5 I `?Copyright (c) 2001 Microsoft Corporation. All "s reserved.} `l> 0>Udd  Y0X^ Xat w  6(?|? 1[%OO+Oz1ESE JIf+ etfAx+ y P y eMNu;> f "~"+&?&+&?& Mu;N_b@#q&`MA"߼'xƿ$%> @#"uLM- l+f@312u#`. A{Gz?- u"doU1;&%3L>N+NU2>23C@LHC BuS #@Jq3ADI`?Copyright (c) 2001 Microsoft Corporation. All Bs reserved. N`lJ(JU@l1yEI 6 A(@ n:h3-2`C1RC8_T ?AI3sM?@~ o4>&y__B+3 OD(#g?#-2 *dQ4E__T>yQ?@X?  _ `F"rM@#c;jaOl%+`eowjA+a Rp <_N_va'?@,8=v\񿁤#l?K̢Z Xr_h))XV%y?@HѺo l?23#o5o qhdaRi :*Hxwo\f wq@h3qA6Oб`PM; l縪sf xAd] }Olꊹ㥏vHT$YY9 BAU@h4?@\.?@P-DT! -u `u bu  e")Q N_uv` ?|Su8 A @ L UA #'>U@?@F?@h{'v"/%&//: d@&7#J#0zGz?<@#/z?Q(1A}6hB} O"@!IF.z`4? IK 0Q>F}@-"-"746 @s( FS AJ#?Ή tN sT)R #\BJ#IZQ_T5 Ig`?Copyright (c) 2001 Microsoft Corporation. All Rs reserved.`Vis_SE.chm!#27269AWTQO0l> 3Ud(aaĤY?a2">(JJ+ b<O-"Y -"b8WJ#A}?d?8brUgc`Di"r(b55keg"w#~irbk5e3orv#@~irb%0J$>C3Bdaa8Eпf pRC|NE[L}s?aȂUgs;a|2~o;_x |bss p8hha?a553icCeR|gCr=wvC{&  B''"A`Modify Arrowhead Shape8bLǖA%ʧHWsǟpbfb uhȃ g܃N`r| 5\Ӕ>#4?ɯC93ag}gR`3TailCih8daXgDhNE[T]]ױױ 2k2m@ E>(96Qc׳Atq@`6tR- 5g1Kc] "D? }gb ?abagՐBw&U&/(k5ZQUu`fFlexi,a;,adjustable?,t7,Connectors,combiningH@Ps,associRGnjunXjoins>c$@E-!a-!OmܞF?'2olTHu~֠ ( !Oya HE_9bFJ=)Mw#_P=K? YN>aO= OD+Q= =?jG:UFDfP h>$/T 6DUA@ m?I? 3mUT  PYY9 #yAU@?;?@} P!3|@u `u`bwu  - :u`h?mu`b"Y@0S#  AS.A$ A[mY,'>މU@dx-E?@d2L?@?Ȣ?@m6?f?!n'%"x//'/%/B*E4$!g tNzsS K2 Q< 83&7 0Q46 & " "2R z6 LS --1?2rq]I3@(EM1E??r\BbCg*+0`A*@baC SBuUAbWuha4~#`muY`}@ }bPu`#28& "S=#WQ@1@37 `Vis_Sba.chm!#26674I`?Copyright (c) 2001 Microsoft Corporation. All Rs reserv?ed.1TT QyP0l>(>U@ m5 1(X=$9"8^=$L??<bE]fv8 agj92]nha| .u9 TT@1h Ac`Change Arrowhead..P\Bb)Seq4%7@1WQ}[]Jf WqEx ,E`@`_Set As StraRLine-'szF<U)#<W? < Xma<zoOWD+k{oK?ko:_`(,T dNݗSW=+@B!_\#"/"$/?DD'PkDod)t"$s/SX06S/4jW9lLSF=ZUP <<Z_  }k 3?K@w8 @To( "*0^p=*4 D=c #5GYk} C:\Program Files\Microsoft Office\Visio10\1033\Solutions Extras\Backgrounds (US units).vssK=7c"4FXj| D C:\Program Files\Microsoft Office\Visio10\1033\Solutions Extras\Borders and Tit (US units).vssF=c"4FXj|D C:\Program Files\Microsoft Office\Visio10\1033\Solutions\Block Dia\Basic Shapes (US units).vssC=c"4FXj|D C:\Program Files\Microsoft Office\Visio10\1033\Solutions Extras\Connectors (US units).vssL=c"4FXj|D C:\Program Files\Microsoft Office\Visio10\1033\Solutions Extras\Drawing Tool Shapes (US units).vssG=c"4FXj|D C:\Program Files\Microsoft Office\Visio10\1033\Solutions Extras\Embellishments (US units?).vss@=c"4FXj|vD C:\Program Files\Microsoft Office\Visio10\1033\Solutions Extras\Symbols (US units).vss_(Oxf*9aSC!x/k+/H<b/|\<O/]<cc/:d/T:/:?]e/ U*P (3?K@8+ @FDTey ah$^T z U2!@2&@A? ? ? L^pf." u !&U 2'-?9-.(-u`!ol"v"|"u$)!#z2-8?-("(.sU16(6? % ns%G<'!#p5&0`Co&lor Schemes...1`'Set the co1s2 f0this document0} ``CSQ. g>O'PoD(&Tt13 Black & White!o&e?w?#@ԡ9!E 3 Connector DA#R )ZeD) [Q)%!ArA P l!a #Q|!A"1A !%"#A'wT1!#H6%2d=g%2d=g2dA=gE2drA=gU2da=g2d:jQ.hQ=gQ.h A=g A.hQ=gQ.h=g2dQ=gQ.hQ=gQ.hQ=gQ.hl!=gl!.h=g.hQ=gQ.h#Q=g#Q.hQ=gQ.h=g.h|!=g|!.hA=gA.h=g;E2d Q=gQ.hM6.h1=g1.hA=gA.hQ=gQ.hQ=gQ.hQ=gQ.h!=g%2d !=g !.h a=g a.ha=ga.h=g.hQ=gQ.hAA.ha=ga.h=g2d[Q=c[AUCPQ[Q[QQQ4ߡ ! !A a arAaaaaAAQ A1+:Q,GF-TQQ/nQ0{l!111  Q3TQ[Qu=auiAs%rAQ urAGQ&X A!E!$,?@rqG@@7"w?@`?ű?A(:"fRd|!AR(a0O贁CNk+1-),C5G!#2q6Xl1? HQq͛ Pla{;Ma$lpC(Q$/(?PfXE?U?qASwƙ{j(/8BD=%d9%2v A=%I1-tIuvEii4OP(AOVNQ@VaACTQzQU@LU7Ea50C8EP"eB )kʚ?B3B*FDI@CV a7VaKV!OS_VXQsV~hD$@`&` f&rbq@KXRBp[3WX@2+YQART?!?QR)VR[__( oo/b5fVAB7H7source file!8 '!%)w3;1R@5/qp3BbJGdu6DNCr{6{uxP~5@Q y} e 4c֏7_q!/"4HgUgfK_=իZΟK__&@qOs_Zю% tD/4ZQ)ˬ%/ϊ w.@3Ͽ"1  2a۬B-:)1 1C U%ct/!1!/!?$9 &1X!1r-%1%1"DL%)yp @@ 8?@Vq!; T12) Y_YXu*`̓(8߰0O贁NkWֵ3dsA,0TӻK@CG+袋.4OO Buޏ&Bu%+p_g{䠨")Y᪤/ ֋8֊]Acr`d( `qn/Ȉ @!!8 j`CVE؉[0;;P,1)%856S:[(*3F85y5Q< V*3885Q<! ?:qHuP/ʲXy0ʅyN]E/V]ȇUKxW_Ut)%C wW>_aEhCog-Pq6HZ l~ #Tol˱!eC)%ᐱ"LeEDAMdPmo/jE@@9?@?tuhe+$hbu$gs3ZᇔG2?2J d? %6)%Gbi^Hhc_wn-/dUwa߮xߏ꛷=BaOOJOXv/_ o[!ݟp_T$lU_vO_Z蓼__Q쩵"ioIo)$.3Yoko}oom)-ooa54+k import cog, handyxml <..pythonde..> jKV$kE߉Jm/.Sy|@s]))Uݯ} @@rqPkptLNmIUJJ#"jSjBt0_5"/s%t8J\r$6HZl~,jkßql 1xэ]ae.'V *`yu`ހ$?-u`bHY\JG_' /3bCztiZ!z ! N)^/p%\/@/&0?p%m//u?,?s)*OQ&5k??( AO GOend]]]*/Uy﯎ x'J _?*/8 A8ʮҟ1G 1CUgy棯ǯٯ3_Wi˿ݿ%_[oϜPPhc?ϒ*'''M_ߠJ?)/z!$ S, E~ єW(gz!#'2.߂-؂kCBg[OmN_ WHz Ow@!3E<..old PRS output..> A4SBj)U!W(r!oo!3?b#6$4 A>e?݁`0__W(W(-'A'EDVl'zZJQDS,ZiNc@@@@:?t s 1,䑖ev[m8vS3);M_q/ىvx</*/OPObOtOOOOOOO__ :_L_^_ o___oqD_o oVohozoo7soo &8Ud¬3ߤyoz"FXj|@6v)+?/* [[[9K.!Ҕ!şןϸ18@@5`go(@:ͯ߯'9K]ogɿۿЧc/LD*+yϋϝM_R$ߒ/HZlހ ߞ߰߿*uPO!?EWiO _Rr_*YωYjend]]]*/p__o:aeo 2??%?z`*Jaڄ\svbv(:L^pGUo/*//N/,r//~////oo.?@? ϤOv? wʥ??yp??O!4Ob寯|O3(ɟJ$g n$yArtjA5'2.؂-؂R^Utvz|rOo_*_<_N_Weυ_:aY)Y<..new m output..> V O ,7rx<!3Z>4m]7dYo? `Ze rrQHuBaXaBlcX[m#^ei(:j픟ςyquU(}LW G㕑u^YDDDD(%D/8y]E,@5&q^zu)*1CUy9ӏ叹 p 3E4OqQUP\DViSuG`pskQ[u`b\bR#~I dC/H!f'8-L/^%\o/*&R?^%//.u4p«??)D ãLOH5?O)N?>K^EO ߸r_s %7I[mo'iɔ ._@_2%I4ߧo0Zߕ $1zs'o#}nJ`QT! q`qixPsr @@/ @@suP|y-Qui`u`bu q v@"җQx@PⱯ0qrq9aJ!abB/7n*B=ZtRt xtrwlmnb F;Mpq゘@z3rp$՗Ÿ՟ ϵ!.S0K^ͣK0O贁NkcF PZ/i{Qh/U~ *'O ~#*C&o+=Oastq#5GY&r;Ƨs;~ߐ V} " ГaM`"4֎Scr̯ޯ&8Jn}ſ׿ Ϧw"ASewωϛϭτ(:e년 /.6/VQ/c)}U&v//Г=a&//Sa6t"ca6b$a4FXj|6OoxC 1CUgyr rqG'9K]oxAfQVeQUfgfon4goVoi} %v?$6ГPwvcv&?8?J?0f#vbcv?!????OO*OoPoܖeܕed1Lրgy$#;氶[ bϱ#wƊw%7I[mǏُKK!3EWi{ÝQRq!3EWi{wឯ%ɯۯg^ge%$%dJl& $(;:L#5yoTXEe0OoHnW?mcg$tӤ"WmIHTBtt!J#<#3!Q~-?f/3!MPdTA%p_B`"$}HCIH!C9IHJo_q؁!œ!߅rlc) #4SblbO#U?ryhldykd`@_rnpOlCBb"[/R saA5DA VZ[-qD/PO$X %X-DT!鿮ҿϜ]Ëqg1ϊYGY}ɶ&SUЂ !@@þCUph0Ë????O O2OgaSO v~ONi@YeY ww[ew7VypR~$XGӨajGRK5_QAR0'?}?GY(k}+ca,__o___oo$o+~D;=oodo5]gzaUe"8gfi#~Ї ip~sȲ @@8#@@HI?@`߿ٴюϿP[rbyAvsx&۲uIȒ@@ FщQ~ ysPՃ!^_N` vU`h`0Ҡ5"G n#kл`k:d tNp-z@ ңӜE,V?@ iW}ҿ21rBN? 6Nÿ?4b&lZHG(??GUAu9@v?? O?`?,O>OOoON"OOO__(Y˯sp/.@taam_ oOd>қ WUm%Ѯ  Ϳ߿Á/ASewϛϭϿCY-?Qcu`ߙ߽߫D᱈n@@VUr!"@@UX%uwy$}L9,>Pbt //6KR@@#/6.7/6?>T7??IaIB!IOWrEFO3OK]o V V&8J\nRz._% //1/C/U/@rDZ@@r"v!bZ},s)ʡ!/////? =a3?E=v^?p?l5$@vwv?Qwl(_ԂOVԏOrE"&OOO=a×3_E_W_i_{________Y~o3cM &>oPoۡ "mߡ|d r 5QHbaW3 -$ ^5%rART!yF~aȡs T~$ޢIRRc/P>P4FW$`bub}/8s"   ra.$?*}С'ɯۯbotoooooo@F8@@Jh/w tO&(Rx?(60BTfx#9ѾPE=~1q@@B#dQr ?@%59\,F˝ 푂!m |(:L^R3gӐߴ ka0I]'74-(_ "xIl~"K17K6Ю O3&MU@Ɖ2h@{W& '2(N?`5&7X='?(q `I[m(D#(O3gK\Kb&_ULŞhiH݋D;kc@@k8 @@Q?@R?TU=?tLrUB`ActAc@VcKcORVȂ bȄNebȂ(bȂ 2bȂ[m+Of?8G_QQH!F ^A@)E[06Gjx a@UbU!"U#$&(U)*+,U-./0U1234At4!@&@ L._RC-_-7A@-ARH<( Eta R\%f4:@?53.PD6n.PU1( UO"D&aU=QJf )h"Ty+U-x_Ʌ&!Q- H*9(TYkEQ/,AM3AM3PPTPPT1SGView/L A/d A/| A/̝A/ڝAGuideTheDocPage-1Gesture FormatRectangleSchemeNameVisio 90ConnectorVisio 00Visio 01Visio 02Visio 03Visio 10Visio 11Visio 12Visio 13Visio 20Visio 21Visio 22Visio 23Visio 50Visio 51Visio 52Visio 53Visio 70Visio 80BasicBasic Shadow visKeywords visVersionOLE NormalDocumentEllipseRectangle.4Rectangle.5Rectangle.6Rectangle.7Rectangle.9CodeDynamic conn?ectorRectangle.1Rectangle.10Rectangle.11Rectangle.12Rectangle.13Rectangle.14Rectangle.15Rectangle.16Rectangle.17Rectangle.18Ellipse.25Rounded rect?angleHairlineCircle tangentRed fillArial 12pt centered"NO" signArrowFlexi-arrowScaleAntiScaleLine-curve connectorRectangle.41%BB3x E3! E3! E3"G3"E34"E3L"͞ E3d"ڞE3|" E3" E3" E3" E3" E3") E3 #6 E3$#C E3<#P E3T#] E3l#j E3#w E3# E3# E3# E3# E3# E3$ş E3,$ҟ E3<$ܟE3T$E3l$E3$ E3$ E3$( E3$4E3$DE3$TE3 %dE3$%tE3<%xE3T% E3d%G3t-E3-E3-͠E3.ޠE3.E3.E34.E3L."E3d.3E3.DE3܋-UE3/dG3,04 E3|X4G3V9 E3|^9G3l9ΡE3E=ܡ E3m=E3l= E3m=E3t=G3;-E3P;1E3T0=5E  !"#$%&'()*+,-./0123456789:;<=>?@AUvU U UUUU'578:+t4n!@&@ d/_C-l/q7A%t4 s/ A-Op/7AJ@.BR@$.ͨ5RH<( H<( JEt/, RE

`o3wFgߑe3t.F5_R$`=C/ܒPM)de'L5h1=.^}2UFT?y*PD\ F6%>`cog-3.3.0/cogapp/000077500000000000000000000000001414575707500135555ustar00rootroot00000000000000cog-3.3.0/cogapp/__init__.py000066400000000000000000000002641414575707500156700ustar00rootroot00000000000000""" Cog content generation tool. http://nedbatchelder.com/code/cog Copyright 2004-2015, Ned Batchelder. """ from __future__ import absolute_import from .cogapp import * cog-3.3.0/cogapp/__main__.py000066400000000000000000000001641414575707500156500ustar00rootroot00000000000000"""Make Cog runnable directly from the module.""" import sys from cogapp import Cog sys.exit(Cog().main(sys.argv)) cog-3.3.0/cogapp/backward.py000066400000000000000000000017751414575707500157170ustar00rootroot00000000000000"""Compatibility between Py2 and Py3.""" import sys import unittest PY3 = sys.version_info[0] == 3 if PY3: string_types = (str,bytes) bytes_types = (bytes,) def to_bytes(s): return s.encode('utf8') else: string_types = (basestring,) bytes_types = (str,) def to_bytes(s): return s # Pythons 2 and 3 differ on where to get StringIO try: from cStringIO import StringIO except ImportError: from io import StringIO def unittest_has(method): """Does `unittest.TestCase` have `method` defined?""" return hasattr(unittest.TestCase, method) class TestCase(unittest.TestCase): """Just like unittest.TestCase, but with assert methods added. Designed to be compatible with 3.1 unittest. Methods are only defined if `unittest` doesn't have them. """ # pylint: disable=missing-docstring if not unittest_has('assertRaisesRegex'): def assertRaisesRegex(self, *args, **kwargs): return self.assertRaisesRegexp(*args, **kwargs) cog-3.3.0/cogapp/cogapp.py000066400000000000000000000726251414575707500154140ustar00rootroot00000000000000# coding: utf8 """ Cog content generation tool. http://nedbatchelder.com/code/cog Copyright 2004-2021, Ned Batchelder. """ from __future__ import absolute_import, print_function import copy import getopt import glob import hashlib import linecache import os import re import shlex import sys import traceback from .backward import PY3, StringIO, string_types, to_bytes __all__ = ['Cog', 'CogUsageError', 'main'] __version__ = '3.3.0' usage = """\ cog - generate content 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. -p PROLOGUE Prepend the generator source with PROLOGUE. Useful to insert an import line. Example: -p "import math" -P Use print() instead of cog.outl() for code output. -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. --check Check that the files would not change if run again. --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]]]'. --verbosity=VERBOSITY Control the amount of output. 2 (the default) lists all files, 1 lists only changed files, 0 lists no files. -h Print this help. """ # Other package modules from .whiteutils import * class CogError(Exception): """ Any exception raised by Cog. """ def __init__(self, msg, file='', line=0): if file: Exception.__init__(self, "%s(%d): %s" % (file, line, msg)) else: Exception.__init__(self, msg) class CogUsageError(CogError): """ An error in usage of command-line arguments in cog. """ pass class CogInternalError(CogError): """ An error in the coding of Cog. Should never happen. """ pass class CogGeneratedError(CogError): """ An error raised by a user's cog generator. """ pass class CogUserException(CogError): """ An exception caught when running a user's cog generator. The argument is the traceback message to print. """ pass class CogCheckFailed(CogError): """ A --check failed. """ pass class Redirectable: """ An object with its own stdout and stderr files. """ def __init__(self): self.stdout = sys.stdout self.stderr = sys.stderr def setOutput(self, stdout=None, stderr=None): """ Assign new files for standard out and/or standard error. """ if stdout: self.stdout = stdout if stderr: self.stderr = stderr def prout(self, s, end="\n"): print(s, file=self.stdout, end=end) def prerr(self, s, end="\n"): print(s, file=self.stderr, end=end) class CogGenerator(Redirectable): """ A generator pulled from a source file. """ def __init__(self, options=None): Redirectable.__init__(self) self.markers = [] self.lines = [] self.options = options or CogOptions() def parseMarker(self, l): self.markers.append(l) def parseLine(self, l): self.lines.append(l.strip('\n')) def getCode(self): """ Extract the executable Python code from the generator. """ # If the markers and lines all have the same prefix # (end-of-line comment chars, for example), # then remove it from all the lines. prefIn = commonPrefix(self.markers + self.lines) if prefIn: self.markers = [ l.replace(prefIn, '', 1) for l in self.markers ] self.lines = [ l.replace(prefIn, '', 1) for l in self.lines ] return reindentBlock(self.lines, '') def evaluate(self, cog, globals, fname): # figure out the right whitespace prefix for the output prefOut = whitePrefix(self.markers) intext = self.getCode() if not intext: return '' prologue = "import " + cog.cogmodulename + " as cog\n" if self.options.sPrologue: prologue += self.options.sPrologue + '\n' code = compile(prologue + intext, str(fname), 'exec') # Make sure the "cog" module has our state. cog.cogmodule.msg = self.msg cog.cogmodule.out = self.out cog.cogmodule.outl = self.outl cog.cogmodule.error = self.error real_stdout = sys.stdout if self.options.bPrintOutput: sys.stdout = captured_stdout = StringIO() self.outstring = '' try: eval(code, globals) except CogError: raise except: typ, err, tb = sys.exc_info() frames = (tuple(fr) for fr in traceback.extract_tb(tb.tb_next)) frames = find_cog_source(frames, prologue) msg = "".join(traceback.format_list(frames)) msg += "{}: {}".format(typ.__name__, err) raise CogUserException(msg) finally: sys.stdout = real_stdout if self.options.bPrintOutput: self.outstring = captured_stdout.getvalue() # We need to make sure that the last line in the output # ends with a newline, or it will be joined to the # end-output line, ruining cog's idempotency. if self.outstring and self.outstring[-1] != '\n': self.outstring += '\n' return reindentBlock(self.outstring, prefOut) def msg(self, s): self.prout("Message: "+s) def out(self, sOut='', dedent=False, trimblanklines=False): """ The cog.out function. """ if trimblanklines and ('\n' in sOut): lines = sOut.split('\n') if lines[0].strip() == '': del lines[0] if lines and lines[-1].strip() == '': del lines[-1] sOut = '\n'.join(lines)+'\n' if dedent: sOut = reindentBlock(sOut) self.outstring += sOut def outl(self, sOut='', **kw): """ The cog.outl function. """ self.out(sOut, **kw) self.out('\n') def error(self, msg='Error raised by cog generator.'): """ The cog.error function. Instead of raising standard python errors, cog generators can use this function. It will display the error without a scary Python traceback. """ raise CogGeneratedError(msg) class NumberedFileReader: """ A decorator for files that counts the readline()'s called. """ def __init__(self, f): self.f = f self.n = 0 def readline(self): l = self.f.readline() if l: self.n += 1 return l def linenumber(self): return self.n class CogOptions: """ Options for a run of cog. """ def __init__(self): # Defaults for argument values. self.args = [] self.includePath = [] self.defines = {} self.bShowVersion = False self.sMakeWritableCmd = None self.bReplace = False self.bNoGenerate = False self.sOutputName = None self.bWarnEmpty = False self.bHashOutput = False self.bDeleteCode = False self.bEofCanBeEnd = False self.sSuffix = None self.bNewlines = False self.sBeginSpec = '[[[cog' self.sEndSpec = ']]]' self.sEndOutput = '[[[end]]]' self.sEncoding = "utf-8" self.verbosity = 2 self.sPrologue = '' self.bPrintOutput = False self.bCheck = False def __eq__(self, other): """ Comparison operator for tests to use. """ return self.__dict__ == other.__dict__ def clone(self): """ Make a clone of these options, for further refinement. """ return copy.deepcopy(self) def addToIncludePath(self, dirs): """ Add directories to the include path. """ dirs = dirs.split(os.pathsep) self.includePath.extend(dirs) def parseArgs(self, argv): # Parse the command line arguments. try: opts, self.args = getopt.getopt( argv, 'cdD:eI:n:o:rs:p:PUvw:xz', [ 'check', 'markers=', 'verbosity=', ] ) except getopt.error as msg: raise CogUsageError(msg) # Handle the command line arguments. for o, a in opts: if o == '-c': self.bHashOutput = True elif o == '-d': self.bDeleteCode = True elif o == '-D': if a.count('=') < 1: raise CogUsageError("-D takes a name=value argument") name, value = a.split('=', 1) self.defines[name] = value elif o == '-e': self.bWarnEmpty = True elif o == '-I': self.addToIncludePath(os.path.abspath(a)) elif o == '-n': self.sEncoding = a elif o == '-o': self.sOutputName = a elif o == '-r': self.bReplace = True elif o == '-s': self.sSuffix = a elif o == '-p': self.sPrologue = a elif o == '-P': self.bPrintOutput = True elif o == '-U': self.bNewlines = True elif o == '-v': self.bShowVersion = True elif o == '-w': self.sMakeWritableCmd = a elif o == '-x': self.bNoGenerate = True elif o == '-z': self.bEofCanBeEnd = True elif o == '--check': self.bCheck = True elif o == '--markers': self._parse_markers(a) elif o == '--verbosity': self.verbosity = int(a) else: # Since getopt.getopt is given a list of possible flags, # this is an internal error. raise CogInternalError("Don't understand argument %s" % o) def _parse_markers(self, val): try: self.sBeginSpec, self.sEndSpec, self.sEndOutput = val.split(' ') except ValueError: raise CogUsageError( '--markers requires 3 values separated by spaces, could not parse %r' % val ) def validate(self): """ Does nothing if everything is OK, raises CogError's if it's not. """ if self.bReplace and self.bDeleteCode: raise CogUsageError("Can't use -d with -r (or you would delete all your source!)") if self.bReplace and self.sOutputName: raise CogUsageError("Can't use -o with -r (they are opposites)") class Cog(Redirectable): """ The Cog engine. """ def __init__(self): Redirectable.__init__(self) self.options = CogOptions() self._fixEndOutputPatterns() self.cogmodulename = "cog" self.createCogModule() self.bCheckFailed = False def _fixEndOutputPatterns(self): end_output = re.escape(self.options.sEndOutput) self.reEndOutput = re.compile(end_output + r'(?P *\(checksum: (?P[a-f0-9]+)\))') self.sEndFormat = self.options.sEndOutput + ' (checksum: %s)' def showWarning(self, msg): self.prout("Warning: "+msg) def isBeginSpecLine(self, s): return self.options.sBeginSpec in s def isEndSpecLine(self, s): return self.options.sEndSpec in s and not self.isEndOutputLine(s) def isEndOutputLine(self, s): return self.options.sEndOutput in s def createCogModule(self): """ Make a cog "module" object so that imported Python modules can say "import cog" and get our state. """ class DummyModule(object): """Modules don't have to be anything special, just an object will do.""" pass self.cogmodule = DummyModule() self.cogmodule.path = [] def openOutputFile(self, fname): """ Open an output file, taking all the details into account. """ opts = {} mode = "w" if PY3: opts['encoding'] = self.options.sEncoding if self.options.bNewlines: if PY3: opts['newline'] = "\n" else: mode = "wb" fdir = os.path.dirname(fname) if os.path.dirname(fdir) and not os.path.exists(fdir): os.makedirs(fdir) return open(fname, mode, **opts) def openInputFile(self, fname): """ Open an input file. """ if fname == "-": return sys.stdin else: opts = {} if PY3: opts['encoding'] = self.options.sEncoding return open(fname, "r", **opts) def processFile(self, fIn, fOut, fname=None, globals=None): """ Process an input file object to an output file object. fIn and fOut can be file objects, or file names. """ sFileIn = fname or '' sFileOut = fname or '' fInToClose = fOutToClose = None # Convert filenames to files. if isinstance(fIn, string_types): # Open the input file. sFileIn = fIn fIn = fInToClose = self.openInputFile(fIn) if isinstance(fOut, string_types): # Open the output file. sFileOut = fOut fOut = fOutToClose = self.openOutputFile(fOut) try: fIn = NumberedFileReader(fIn) bSawCog = False self.cogmodule.inFile = sFileIn self.cogmodule.outFile = sFileOut self.cogmodulename = 'cog_' + hashlib.md5(sFileOut.encode()).hexdigest() sys.modules[self.cogmodulename] = self.cogmodule # if "import cog" explicitly done in code by user, note threading will cause clashes. sys.modules['cog'] = self.cogmodule # The globals dict we'll use for this file. if globals is None: globals = {} # If there are any global defines, put them in the globals. globals.update(self.options.defines) # loop over generator chunks l = fIn.readline() while l: # Find the next spec begin while l and not self.isBeginSpecLine(l): if self.isEndSpecLine(l): raise CogError("Unexpected '%s'" % self.options.sEndSpec, file=sFileIn, line=fIn.linenumber()) if self.isEndOutputLine(l): raise CogError("Unexpected '%s'" % self.options.sEndOutput, file=sFileIn, line=fIn.linenumber()) fOut.write(l) l = fIn.readline() if not l: break if not self.options.bDeleteCode: fOut.write(l) # l is the begin spec gen = CogGenerator(options=self.options) gen.setOutput(stdout=self.stdout) gen.parseMarker(l) firstLineNum = fIn.linenumber() self.cogmodule.firstLineNum = firstLineNum # If the spec begin is also a spec end, then process the single # line of code inside. if self.isEndSpecLine(l): beg = l.find(self.options.sBeginSpec) end = l.find(self.options.sEndSpec) if beg > end: raise CogError("Cog code markers inverted", file=sFileIn, line=firstLineNum) else: sCode = l[beg+len(self.options.sBeginSpec):end].strip() gen.parseLine(sCode) else: # Deal with an ordinary code block. l = fIn.readline() # Get all the lines in the spec while l and not self.isEndSpecLine(l): if self.isBeginSpecLine(l): raise CogError("Unexpected '%s'" % self.options.sBeginSpec, file=sFileIn, line=fIn.linenumber()) if self.isEndOutputLine(l): raise CogError("Unexpected '%s'" % self.options.sEndOutput, file=sFileIn, line=fIn.linenumber()) if not self.options.bDeleteCode: fOut.write(l) gen.parseLine(l) l = fIn.readline() if not l: raise CogError( "Cog block begun but never ended.", file=sFileIn, line=firstLineNum) if not self.options.bDeleteCode: fOut.write(l) gen.parseMarker(l) l = fIn.readline() # Eat all the lines in the output section. While reading past # them, compute the md5 hash of the old output. previous = "" hasher = hashlib.md5() while l and not self.isEndOutputLine(l): if self.isBeginSpecLine(l): raise CogError("Unexpected '%s'" % self.options.sBeginSpec, file=sFileIn, line=fIn.linenumber()) if self.isEndSpecLine(l): raise CogError("Unexpected '%s'" % self.options.sEndSpec, file=sFileIn, line=fIn.linenumber()) previous += l hasher.update(to_bytes(l)) l = fIn.readline() curHash = hasher.hexdigest() if not l and not self.options.bEofCanBeEnd: # We reached end of file before we found the end output line. raise CogError("Missing '%s' before end of file." % self.options.sEndOutput, file=sFileIn, line=fIn.linenumber()) # Make the previous output available to the current code self.cogmodule.previous = previous # Write the output of the spec to be the new output if we're # supposed to generate code. hasher = hashlib.md5() if not self.options.bNoGenerate: sFile = "" % (sFileIn, firstLineNum) sGen = gen.evaluate(cog=self, globals=globals, fname=sFile) sGen = self.suffixLines(sGen) hasher.update(to_bytes(sGen)) fOut.write(sGen) newHash = hasher.hexdigest() bSawCog = True # Write the ending output line hashMatch = self.reEndOutput.search(l) if self.options.bHashOutput: if hashMatch: oldHash = hashMatch.groupdict()['hash'] if oldHash != curHash: raise CogError("Output has been edited! Delete old checksum to unprotect.", file=sFileIn, line=fIn.linenumber()) # Create a new end line with the correct hash. endpieces = l.split(hashMatch.group(0), 1) else: # There was no old hash, but we want a new hash. endpieces = l.split(self.options.sEndOutput, 1) l = (self.sEndFormat % newHash).join(endpieces) else: # We don't want hashes output, so if there was one, get rid of # it. if hashMatch: l = l.replace(hashMatch.groupdict()['hashsect'], '', 1) if not self.options.bDeleteCode: fOut.write(l) l = fIn.readline() if not bSawCog and self.options.bWarnEmpty: self.showWarning("no cog code found in %s" % sFileIn) finally: if fInToClose: fInToClose.close() if fOutToClose: fOutToClose.close() # A regex for non-empty lines, used by suffixLines. reNonEmptyLines = re.compile(r"^\s*\S+.*$", re.MULTILINE) def suffixLines(self, text): """ Add suffixes to the lines in text, if our options desire it. text is many lines, as a single string. """ if self.options.sSuffix: # Find all non-blank lines, and add the suffix to the end. repl = r"\g<0>" + self.options.sSuffix.replace('\\', '\\\\') text = self.reNonEmptyLines.sub(repl, text) return text def processString(self, sInput, fname=None): """ Process sInput as the text to cog. Return the cogged output as a string. """ fOld = StringIO(sInput) fNew = StringIO() self.processFile(fOld, fNew, fname=fname) return fNew.getvalue() def replaceFile(self, sOldPath, sNewText): """ Replace file sOldPath with the contents sNewText """ if not os.access(sOldPath, os.W_OK): # Need to ensure we can write. if self.options.sMakeWritableCmd: # Use an external command to make the file writable. cmd = self.options.sMakeWritableCmd.replace('%s', sOldPath) self.stdout.write(os.popen(cmd).read()) if not os.access(sOldPath, os.W_OK): raise CogError("Couldn't make %s writable" % sOldPath) else: # Can't write! raise CogError("Can't overwrite %s" % sOldPath) f = self.openOutputFile(sOldPath) f.write(sNewText) f.close() def saveIncludePath(self): self.savedInclude = self.options.includePath[:] self.savedSysPath = sys.path[:] def restoreIncludePath(self): self.options.includePath = self.savedInclude self.cogmodule.path = self.options.includePath sys.path = self.savedSysPath def addToIncludePath(self, includePath): self.cogmodule.path.extend(includePath) sys.path.extend(includePath) def processOneFile(self, sFile): """ Process one filename through cog. """ self.saveIncludePath() bNeedNewline = False try: self.addToIncludePath(self.options.includePath) # Since we know where the input file came from, # push its directory onto the include path. self.addToIncludePath([os.path.dirname(sFile)]) # How we process the file depends on where the output is going. if self.options.sOutputName: self.processFile(sFile, self.options.sOutputName, sFile) elif self.options.bReplace or self.options.bCheck: # We want to replace the cog file with the output, # but only if they differ. verb = "Cogging" if self.options.bReplace else "Checking" if self.options.verbosity >= 2: self.prout("%s %s" % (verb, sFile), end="") bNeedNewline = True try: fOldFile = self.openInputFile(sFile) sOldText = fOldFile.read() fOldFile.close() sNewText = self.processString(sOldText, fname=sFile) if sOldText != sNewText: if self.options.verbosity >= 1: if self.options.verbosity < 2: self.prout("%s %s" % (verb, sFile), end="") self.prout(" (changed)") bNeedNewline = False if self.options.bReplace: self.replaceFile(sFile, sNewText) else: assert self.options.bCheck self.bCheckFailed = True finally: # The try-finally block is so we can print a partial line # with the name of the file, and print (changed) on the # same line, but also make sure to break the line before # any traceback. if bNeedNewline: self.prout("") else: self.processFile(sFile, self.stdout, sFile) finally: self.restoreIncludePath() def processWildcards(self, sFile): files = glob.glob(sFile) if files: for sMatchingFile in files: self.processOneFile(sMatchingFile) else: self.processOneFile(sFile) def processFileList(self, sFileList): """ Process the files in a file list. """ flist = self.openInputFile(sFileList) lines = flist.readlines() flist.close() for l in lines: # Use shlex to parse the line like a shell. lex = shlex.shlex(l, posix=True) lex.whitespace_split = True lex.commenters = '#' # No escapes, so that backslash can be part of the path lex.escape = '' args = list(lex) if args: self.processArguments(args) def processArguments(self, args): """ Process one command-line. """ saved_options = self.options self.options = self.options.clone() self.options.parseArgs(args[1:]) self.options.validate() if args[0][0] == '@': if self.options.sOutputName: raise CogUsageError("Can't use -o with @file") self.processFileList(args[0][1:]) else: self.processWildcards(args[0]) self.options = saved_options def callableMain(self, argv): """ All of command-line cog, but in a callable form. This is used by main. argv is the equivalent of sys.argv. """ argv = argv[1:] # Provide help if asked for anywhere in the command line. if '-?' in argv or '-h' in argv: self.prerr(usage, end="") return self.options.parseArgs(argv) self.options.validate() self._fixEndOutputPatterns() if self.options.bShowVersion: self.prout("Cog version %s" % __version__) return if self.options.args: for a in self.options.args: self.processArguments([a]) else: raise CogUsageError("No files to process") if self.bCheckFailed: raise CogCheckFailed("Check failed") def main(self, argv): """ Handle the command-line execution for cog. """ try: self.callableMain(argv) return 0 except CogUsageError as err: self.prerr(err) self.prerr("(for help use -h)") return 2 except CogGeneratedError as err: self.prerr("Error: %s" % err) return 3 except CogUserException as err: self.prerr("Traceback (most recent call last):") self.prerr(err.args[0]) return 4 except CogCheckFailed as err: self.prerr(err) return 5 except CogError as err: self.prerr(err) return 1 def find_cog_source(frame_summary, prologue): """Find cog source lines in a frame summary list, for printing tracebacks. Arguments: frame_summary: a list of 4-item tuples, as returned by traceback.extract_tb. prologue: the text of the code prologue. Returns A list of 4-item tuples, updated to correct the cog entries. """ prolines = prologue.splitlines() for filename, lineno, funcname, source in frame_summary: if not source: m = re.search(r"^$", filename) if m: if lineno <= len(prolines): filename = '' source = prolines[lineno-1] lineno -= 1 # Because "import cog" is the first line in the prologue else: filename, coglineno = m.groups() coglineno = int(coglineno) lineno += coglineno - len(prolines) source = linecache.getline(filename, lineno).strip() yield filename, lineno, funcname, source def main(): """Main function for entry_points to use.""" return Cog().main(sys.argv) cog-3.3.0/cogapp/makefiles.py000066400000000000000000000026771414575707500161030ustar00rootroot00000000000000""" Dictionary-to-filetree functions, to create test files for testing. http://nedbatchelder.com/code/cog Copyright 2004-2019, Ned Batchelder. """ from __future__ import absolute_import import os.path from .backward import string_types, bytes_types from .whiteutils import reindentBlock __all__ = ['makeFiles', 'removeFiles'] def makeFiles(d, basedir='.', bytes=False): """ Create files from the dictionary `d`, in the directory named by `basedir`. If `bytes` is true, then treat bytestrings as bytes, else as text. """ for name, contents in d.items(): child = os.path.join(basedir, name) if isinstance(contents, string_types): mode = 'w' if bytes and isinstance(contents, bytes_types): mode += "b" f = open(child, mode) contents = reindentBlock(contents) f.write(contents) f.close() else: if not os.path.exists(child): os.mkdir(child) makeFiles(contents, child) def removeFiles(d, basedir='.'): """ Remove the files created by makeFiles. Directories are removed if they are empty. """ for name, contents in d.items(): child = os.path.join(basedir, name) if isinstance(contents, string_types): os.remove(child) else: removeFiles(contents, child) if not os.listdir(child): os.rmdir(child) cog-3.3.0/cogapp/test_cogapp.py000066400000000000000000002410061414575707500164420ustar00rootroot00000000000000""" Test cogapp. http://nedbatchelder.com/code/cog Copyright 2004-2021, Ned Batchelder. """ from __future__ import absolute_import import os import os.path import random import re import shutil import stat import sys import tempfile import threading from .backward import StringIO, to_bytes, TestCase, PY3 from .cogapp import Cog, CogOptions, CogGenerator from .cogapp import CogError, CogUsageError, CogGeneratedError, CogUserException from .cogapp import usage, __version__, main from .makefiles import * from .whiteutils import reindentBlock class CogTestsInMemory(TestCase): """ Test cases for cogapp.Cog() """ def testNoCog(self): strings = [ '', ' ', ' \t \t \tx', 'hello', 'the cat\nin the\nhat.', 'Horton\n\tHears A\n\t\tWho' ] for s in strings: self.assertEqual(Cog().processString(s), s) def testSimple(self): infile = """\ Some text. //[[[cog import cog cog.outl("This is line one\\n") cog.outl("This is line two") //]]] gobbledegook. //[[[end]]] epilogue. """ outfile = """\ Some text. //[[[cog import cog cog.outl("This is line one\\n") cog.outl("This is line two") //]]] This is line one This is line two //[[[end]]] epilogue. """ self.assertEqual(Cog().processString(infile), outfile) def testEmptyCog(self): # The cog clause can be totally empty. Not sure why you'd want it, # but it works. infile = """\ hello //[[[cog //]]] //[[[end]]] goodbye """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def testMultipleCogs(self): # One file can have many cog chunks, even abutting each other. infile = """\ //[[[cog cog.out("chunk1") //]]] chunk1 //[[[end]]] //[[[cog cog.out("chunk2") //]]] chunk2 //[[[end]]] between chunks //[[[cog cog.out("chunk3") //]]] chunk3 //[[[end]]] """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def testTrimBlankLines(self): infile = """\ //[[[cog cog.out("This is line one\\n", trimblanklines=True) cog.out(''' This is line two ''', dedent=True, trimblanklines=True) cog.outl("This is line three", trimblanklines=True) //]]] This is line one This is line two This is line three //[[[end]]] """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def testTrimEmptyBlankLines(self): infile = """\ //[[[cog cog.out("This is line one\\n", trimblanklines=True) cog.out(''' This is line two ''', dedent=True, trimblanklines=True) cog.out('', dedent=True, trimblanklines=True) cog.outl("This is line three", trimblanklines=True) //]]] This is line one This is line two This is line three //[[[end]]] """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def testTrimBlankLinesWithLastPartial(self): infile = """\ //[[[cog cog.out("This is line one\\n", trimblanklines=True) cog.out("\\nLine two\\nLine three", trimblanklines=True) //]]] This is line one Line two Line three //[[[end]]] """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def testCogOutDedent(self): infile = """\ //[[[cog cog.out("This is the first line\\n") cog.out(''' This is dedent=True 1 This is dedent=True 2 ''', dedent=True, trimblanklines=True) cog.out(''' This is dedent=False 1 This is dedent=False 2 ''', dedent=False, trimblanklines=True) cog.out(''' This is dedent=default 1 This is dedent=default 2 ''', trimblanklines=True) cog.out("This is the last line\\n") //]]] This is the first line This is dedent=True 1 This is dedent=True 2 This is dedent=False 1 This is dedent=False 2 This is dedent=default 1 This is dedent=default 2 This is the last line //[[[end]]] """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def test22EndOfLine(self): # In Python 2.2, this cog file was not parsing because the # last line is indented but didn't end with a newline. infile = """\ //[[[cog import cog for i in range(3): cog.out("%d\\n" % i) //]]] 0 1 2 //[[[end]]] """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def testIndentedCode(self): infile = """\ first line [[[cog import cog for i in range(3): cog.out("xx%d\\n" % i) ]]] xx0 xx1 xx2 [[[end]]] last line """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def testPrefixedCode(self): infile = """\ --[[[cog --import cog --for i in range(3): -- cog.out("xx%d\\n" % i) --]]] xx0 xx1 xx2 --[[[end]]] """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def testPrefixedIndentedCode(self): infile = """\ prologue --[[[cog -- import cog -- for i in range(3): -- cog.out("xy%d\\n" % i) --]]] xy0 xy1 xy2 --[[[end]]] """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def testBogusPrefixMatch(self): infile = """\ prologue #[[[cog import cog # This comment should not be clobbered by removing the pound sign. for i in range(3): cog.out("xy%d\\n" % i) #]]] xy0 xy1 xy2 #[[[end]]] """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def testNoFinalNewline(self): # If the cog'ed output has no final newline, # it shouldn't eat up the cog terminator. infile = """\ prologue [[[cog import cog for i in range(3): cog.out("%d" % i) ]]] 012 [[[end]]] epilogue """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def testNoOutputAtAll(self): # If there is absolutely no cog output, that's ok. infile = """\ prologue [[[cog i = 1 ]]] [[[end]]] epilogue """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def testPurelyBlankLine(self): # If there is a blank line in the cog code with no whitespace # prefix, that should be OK. infile = """\ prologue [[[cog import sys cog.out("Hello") $ cog.out("There") ]]] HelloThere [[[end]]] epilogue """ infile = reindentBlock(infile.replace('$', '')) self.assertEqual(Cog().processString(infile), infile) def testEmptyOutl(self): # Alexander Belchenko suggested the string argument to outl should # be optional. Does it work? infile = """\ prologue [[[cog cog.outl("x") cog.outl() cog.outl("y") cog.out() # Also optional, a complete no-op. cog.outl(trimblanklines=True) cog.outl("z") ]]] x y z [[[end]]] epilogue """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def testFirstLineNum(self): infile = """\ fooey [[[cog cog.outl("started at line number %d" % cog.firstLineNum) ]]] started at line number 2 [[[end]]] blah blah [[[cog cog.outl("and again at line %d" % cog.firstLineNum) ]]] and again at line 8 [[[end]]] """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) def testCompactOneLineCode(self): infile = """\ first line hey: [[[cog cog.outl("hello %d" % (3*3*3*3)) ]]] looky! get rid of this! [[[end]]] last line """ outfile = """\ first line hey: [[[cog cog.outl("hello %d" % (3*3*3*3)) ]]] looky! hello 81 [[[end]]] last line """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), reindentBlock(outfile)) def testInsideOutCompact(self): infile = """\ first line hey?: ]]] what is this? [[[cog strange! get rid of this! [[[end]]] last line """ with self.assertRaisesRegex(CogError, r"^infile.txt\(2\): Cog code markers inverted$"): Cog().processString(reindentBlock(infile), "infile.txt") def testSharingGlobals(self): infile = """\ first line hey: [[[cog s="hey there" ]]] looky! [[[end]]] more literal junk. [[[cog cog.outl(s) ]]] [[[end]]] last line """ outfile = """\ first line hey: [[[cog s="hey there" ]]] looky! [[[end]]] more literal junk. [[[cog cog.outl(s) ]]] hey there [[[end]]] last line """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), reindentBlock(outfile)) def testAssertInCogCode(self): # Check that we can test assertions in cog code in the test framework. infile = """\ [[[cog assert 1 == 2, "Oops" ]]] [[[end]]] """ infile = reindentBlock(infile) with self.assertRaisesRegex(CogUserException, "AssertionError: Oops"): Cog().processString(infile) def testCogPrevious(self): # Check that we can access the previous run's output. infile = """\ [[[cog assert cog.previous == "Hello there!\\n", "WTF??" cog.out(cog.previous) cog.outl("Ran again!") ]]] Hello there! [[[end]]] """ outfile = """\ [[[cog assert cog.previous == "Hello there!\\n", "WTF??" cog.out(cog.previous) cog.outl("Ran again!") ]]] Hello there! Ran again! [[[end]]] """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), reindentBlock(outfile)) class CogOptionsTests(TestCase): """ Test the CogOptions class. """ def testEquality(self): o = CogOptions() p = CogOptions() self.assertEqual(o, p) o.parseArgs(['-r']) self.assertNotEqual(o, p) p.parseArgs(['-r']) self.assertEqual(o, p) def testCloning(self): o = CogOptions() o.parseArgs(['-I', 'fooey', '-I', 'booey', '-s', ' /*x*/']) p = o.clone() self.assertEqual(o, p) p.parseArgs(['-I', 'huey', '-D', 'foo=quux']) self.assertNotEqual(o, p) q = CogOptions() q.parseArgs(['-I', 'fooey', '-I', 'booey', '-s', ' /*x*/', '-I', 'huey', '-D', 'foo=quux']) self.assertEqual(p, q) def testCombiningFlags(self): # Single-character flags can be combined. o = CogOptions() o.parseArgs(['-e', '-r', '-z']) p = CogOptions() p.parseArgs(['-erz']) self.assertEqual(o, p) def testMarkers(self): o = CogOptions() o._parse_markers('a b c') self.assertEqual('a', o.sBeginSpec) self.assertEqual('b', o.sEndSpec) self.assertEqual('c', o.sEndOutput) def testMarkersSwitch(self): o = CogOptions() o.parseArgs(['--markers', 'a b c']) self.assertEqual('a', o.sBeginSpec) self.assertEqual('b', o.sEndSpec) self.assertEqual('c', o.sEndOutput) class FileStructureTests(TestCase): """ Test cases to check that we're properly strict about the structure of files. """ def isBad(self, infile, msg=None): infile = reindentBlock(infile) with self.assertRaisesRegex(CogError, "^"+re.escape(msg)+"$"): Cog().processString(infile, 'infile.txt') def testBeginNoEnd(self): infile = """\ Fooey #[[[cog cog.outl('hello') """ self.isBad(infile, "infile.txt(2): Cog block begun but never ended.") def testNoEoo(self): infile = """\ Fooey #[[[cog cog.outl('hello') #]]] """ self.isBad(infile, "infile.txt(4): Missing '[[[end]]]' before end of file.") infile2 = """\ Fooey #[[[cog cog.outl('hello') #]]] #[[[cog cog.outl('goodbye') #]]] """ self.isBad(infile2, "infile.txt(5): Unexpected '[[[cog'") def testStartWithEnd(self): infile = """\ #]]] """ self.isBad(infile, "infile.txt(1): Unexpected ']]]'") infile2 = """\ #[[[cog cog.outl('hello') #]]] #[[[end]]] #]]] """ self.isBad(infile2, "infile.txt(5): Unexpected ']]]'") def testStartWithEoo(self): infile = """\ #[[[end]]] """ self.isBad(infile, "infile.txt(1): Unexpected '[[[end]]]'") infile2 = """\ #[[[cog cog.outl('hello') #]]] #[[[end]]] #[[[end]]] """ self.isBad(infile2, "infile.txt(5): Unexpected '[[[end]]]'") def testNoEnd(self): infile = """\ #[[[cog cog.outl("hello") #[[[end]]] """ self.isBad(infile, "infile.txt(3): Unexpected '[[[end]]]'") infile2 = """\ #[[[cog cog.outl('hello') #]]] #[[[end]]] #[[[cog cog.outl("hello") #[[[end]]] """ self.isBad(infile2, "infile.txt(7): Unexpected '[[[end]]]'") def testTwoBegins(self): infile = """\ #[[[cog #[[[cog cog.outl("hello") #]]] #[[[end]]] """ self.isBad(infile, "infile.txt(2): Unexpected '[[[cog'") infile2 = """\ #[[[cog cog.outl("hello") #]]] #[[[end]]] #[[[cog #[[[cog cog.outl("hello") #]]] #[[[end]]] """ self.isBad(infile2, "infile.txt(6): Unexpected '[[[cog'") def testTwoEnds(self): infile = """\ #[[[cog cog.outl("hello") #]]] #]]] #[[[end]]] """ self.isBad(infile, "infile.txt(4): Unexpected ']]]'") infile2 = """\ #[[[cog cog.outl("hello") #]]] #[[[end]]] #[[[cog cog.outl("hello") #]]] #]]] #[[[end]]] """ self.isBad(infile2, "infile.txt(8): Unexpected ']]]'") class CogErrorTests(TestCase): """ Test cases for cog.error(). """ def testErrorMsg(self): infile = """\ [[[cog cog.error("This ain't right!")]]] [[[end]]] """ infile = reindentBlock(infile) with self.assertRaisesRegex(CogGeneratedError, "^This ain't right!$"): Cog().processString(infile) def testErrorNoMsg(self): infile = """\ [[[cog cog.error()]]] [[[end]]] """ infile = reindentBlock(infile) with self.assertRaisesRegex(CogGeneratedError, "^Error raised by cog generator.$"): Cog().processString(infile) def testNoErrorIfErrorNotCalled(self): infile = """\ --[[[cog --import cog --for i in range(3): -- if i > 10: -- cog.error("Something is amiss!") -- cog.out("xx%d\\n" % i) --]]] xx0 xx1 xx2 --[[[end]]] """ infile = reindentBlock(infile) self.assertEqual(Cog().processString(infile), infile) class CogGeneratorGetCodeTests(TestCase): """ Unit tests against CogGenerator to see if its getCode() method works properly. """ def setUp(self): """ All tests get a generator to use, and short same-length names for the functions we're going to use. """ self.gen = CogGenerator() self.m = self.gen.parseMarker self.l = self.gen.parseLine def testEmpty(self): self.m('// [[[cog') self.m('// ]]]') self.assertEqual(self.gen.getCode(), '') def testSimple(self): self.m('// [[[cog') self.l(' print "hello"') self.l(' print "bye"') self.m('// ]]]') self.assertEqual(self.gen.getCode(), 'print "hello"\nprint "bye"') def testCompressed1(self): # For a while, I supported compressed code blocks, but no longer. self.m('// [[[cog: print """') self.l('// hello') self.l('// bye') self.m('// """)]]]') self.assertEqual(self.gen.getCode(), 'hello\nbye') def testCompressed2(self): # For a while, I supported compressed code blocks, but no longer. self.m('// [[[cog: print """') self.l('hello') self.l('bye') self.m('// """)]]]') self.assertEqual(self.gen.getCode(), 'hello\nbye') def testCompressed3(self): # For a while, I supported compressed code blocks, but no longer. self.m('// [[[cog') self.l('print """hello') self.l('bye') self.m('// """)]]]') self.assertEqual(self.gen.getCode(), 'print """hello\nbye') def testCompressed4(self): # For a while, I supported compressed code blocks, but no longer. self.m('// [[[cog: print """') self.l('hello') self.l('bye""")') self.m('// ]]]') self.assertEqual(self.gen.getCode(), 'hello\nbye""")') def testNoCommonPrefixForMarkers(self): # It's important to be able to use #if 0 to hide lines from a # C++ compiler. self.m('#if 0 //[[[cog') self.l('\timport cog, sys') self.l('') self.l('\tprint sys.argv') self.m('#endif //]]]') self.assertEqual(self.gen.getCode(), 'import cog, sys\n\nprint sys.argv') class TestCaseWithTempDir(TestCase): def newCog(self): """ Initialize the cog members for another run. """ # Create a cog engine, and catch its output. self.cog = Cog() self.output = StringIO() self.cog.setOutput(stdout=self.output, stderr=self.output) def setUp(self): # Create a temporary directory. self.tempdir = os.path.join(tempfile.gettempdir(), 'testcog_tempdir_' + str(random.random())[2:]) os.mkdir(self.tempdir) self.olddir = os.getcwd() os.chdir(self.tempdir) self.newCog() def tearDown(self): os.chdir(self.olddir) # Get rid of the temporary directory. shutil.rmtree(self.tempdir) def assertFilesSame(self, sFName1, sFName2): text1 = open(os.path.join(self.tempdir, sFName1), 'rb').read() text2 = open(os.path.join(self.tempdir, sFName2), 'rb').read() self.assertEqual(text1, text2) def assertFileContent(self, sFName, sContent): sAbsName = os.path.join(self.tempdir, sFName) f = open(sAbsName, 'rb') try: sFileContent = f.read() finally: f.close() self.assertEqual(sFileContent, to_bytes(sContent)) class ArgumentHandlingTests(TestCaseWithTempDir): def testArgumentFailure(self): # Return value 2 means usage problem. self.assertEqual(self.cog.main(['argv0', '-j']), 2) output = self.output.getvalue() self.assertIn("option -j not recognized", output) with self.assertRaisesRegex(CogUsageError, r"^No files to process$"): self.cog.callableMain(['argv0']) with self.assertRaisesRegex(CogUsageError, r"^option -j not recognized$"): self.cog.callableMain(['argv0', '-j']) def testNoDashOAndAtFile(self): d = { 'cogfiles.txt': """\ # Please run cog """ } makeFiles(d) with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with @file$"): self.cog.callableMain(['argv0', '-o', 'foo', '@cogfiles.txt']) def testDashV(self): self.assertEqual(self.cog.main(['argv0', '-v']), 0) output = self.output.getvalue() self.assertEqual('Cog version %s\n' % __version__, output) def producesHelp(self, args): self.newCog() argv = ['argv0'] + args.split() self.assertEqual(self.cog.main(argv), 0) self.assertEqual(usage, self.output.getvalue()) def testDashH(self): # -h or -? anywhere on the command line should just print help. self.producesHelp("-h") self.producesHelp("-?") self.producesHelp("fooey.txt -h") self.producesHelp("-o -r @fooey.txt -? @booey.txt") def testDashOAndDashR(self): d = { 'cogfile.txt': """\ # Please run cog """ } makeFiles(d) with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with -r \(they are opposites\)$"): self.cog.callableMain(['argv0', '-o', 'foo', '-r', 'cogfile.txt']) def testDashZ(self): d = { 'test.cog': """\ // This is my C++ file. //[[[cog fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] for fn in fnames: cog.outl("void %s();" % fn) //]]] """, 'test.out': """\ // This is my C++ file. //[[[cog fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] for fn in fnames: cog.outl("void %s();" % fn) //]]] void DoSomething(); void DoAnotherThing(); void DoLastThing(); """, } makeFiles(d) with self.assertRaisesRegex(CogError, r"^test.cog\(6\): Missing '\[\[\[end\]\]\]' before end of file.$"): self.cog.callableMain(['argv0', '-r', 'test.cog']) self.newCog() self.cog.callableMain(['argv0', '-r', '-z', 'test.cog']) self.assertFilesSame('test.cog', 'test.out') def testBadDashD(self): with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"): self.cog.callableMain(['argv0', '-Dfooey', 'cog.txt']) with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"): self.cog.callableMain(['argv0', '-D', 'fooey', 'cog.txt']) def testBadMarkers(self): with self.assertRaisesRegex(CogUsageError, r"^--markers requires 3 values separated by spaces, could not parse 'X'$"): self.cog.callableMain(['argv0', '--markers=X']) with self.assertRaisesRegex(CogUsageError, r"^--markers requires 3 values separated by spaces, could not parse 'A B C D'$"): self.cog.callableMain(['argv0', '--markers=A B C D']) class TestMain(TestCaseWithTempDir): def setUp(self): super(TestMain, self).setUp() self.old_argv = sys.argv[:] self.old_stderr = sys.stderr sys.stderr = StringIO() def tearDown(self): sys.stderr = self.old_stderr sys.argv = self.old_argv sys.modules.pop('mycode', None) super(TestMain, self).tearDown() def test_main_function(self): sys.argv = ["argv0", "-Z"] ret = main() self.assertEqual(ret, 2) stderr = sys.stderr.getvalue() self.assertEqual(stderr, 'option -Z not recognized\n(for help use -h)\n') files = { 'test.cog': """\ //[[[cog def func(): import mycode mycode.boom() //]]] //[[[end]]] ----- //[[[cog func() //]]] //[[[end]]] """, 'mycode.py': """\ def boom(): [][0] """, } def test_error_report(self): self.check_error_report() def test_error_report_with_prologue(self): self.check_error_report("-p", "#1\n#2") def check_error_report(self, *args): """Check that the error report is right.""" makeFiles(self.files) sys.argv = ["argv0"] + list(args) + ["-r", "test.cog"] main() expected = reindentBlock("""\ Traceback (most recent call last): File "test.cog", line 9, in func() File "test.cog", line 4, in func mycode.boom() File "MYCODE", line 2, in boom [][0] IndexError: list index out of range """) if PY3: expected = expected.replace("MYCODE", os.path.abspath("mycode.py")) else: expected = expected.replace("MYCODE", "mycode.py") assert expected == sys.stderr.getvalue() def test_error_in_prologue(self): makeFiles(self.files) sys.argv = ["argv0", "-p", "import mycode; mycode.boom()", "-r", "test.cog"] main() expected = reindentBlock("""\ Traceback (most recent call last): File "", line 1, in import mycode; mycode.boom() File "MYCODE", line 2, in boom [][0] IndexError: list index out of range """) if PY3: expected = expected.replace("MYCODE", os.path.abspath("mycode.py")) else: expected = expected.replace("MYCODE", "mycode.py") assert expected == sys.stderr.getvalue() class TestFileHandling(TestCaseWithTempDir): def testSimple(self): d = { 'test.cog': """\ // This is my C++ file. //[[[cog fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] for fn in fnames: cog.outl("void %s();" % fn) //]]] //[[[end]]] """, 'test.out': """\ // This is my C++ file. //[[[cog fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] for fn in fnames: cog.outl("void %s();" % fn) //]]] void DoSomething(); void DoAnotherThing(); void DoLastThing(); //[[[end]]] """, } makeFiles(d) self.cog.callableMain(['argv0', '-r', 'test.cog']) self.assertFilesSame('test.cog', 'test.out') output = self.output.getvalue() self.assertIn("(changed)", output) def testPrintOutput(self): d = { 'test.cog': """\ // This is my C++ file. //[[[cog fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] for fn in fnames: print("void %s();" % fn) //]]] //[[[end]]] """, 'test.out': """\ // This is my C++ file. //[[[cog fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] for fn in fnames: print("void %s();" % fn) //]]] void DoSomething(); void DoAnotherThing(); void DoLastThing(); //[[[end]]] """, } makeFiles(d) self.cog.callableMain(['argv0', '-rP', 'test.cog']) self.assertFilesSame('test.cog', 'test.out') output = self.output.getvalue() self.assertIn("(changed)", output) def testWildcards(self): d = { 'test.cog': """\ // This is my C++ file. //[[[cog fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] for fn in fnames: cog.outl("void %s();" % fn) //]]] //[[[end]]] """, 'test2.cog': """\ // This is my C++ file. //[[[cog fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] for fn in fnames: cog.outl("void %s();" % fn) //]]] //[[[end]]] """, 'test.out': """\ // This is my C++ file. //[[[cog fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] for fn in fnames: cog.outl("void %s();" % fn) //]]] void DoSomething(); void DoAnotherThing(); void DoLastThing(); //[[[end]]] """, 'not_this_one.cog': """\ // This is my C++ file. //[[[cog fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] for fn in fnames: cog.outl("void %s();" % fn) //]]] //[[[end]]] """, 'not_this_one.out': """\ // This is my C++ file. //[[[cog fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] for fn in fnames: cog.outl("void %s();" % fn) //]]] //[[[end]]] """, } makeFiles(d) self.cog.callableMain(['argv0', '-r', 't*.cog']) self.assertFilesSame('test.cog', 'test.out') self.assertFilesSame('test2.cog', 'test.out') self.assertFilesSame('not_this_one.cog', 'not_this_one.out') output = self.output.getvalue() self.assertIn("(changed)", output) def testOutputFile(self): # -o sets the output file. d = { 'test.cog': """\ // This is my C++ file. //[[[cog fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] for fn in fnames: cog.outl("void %s();" % fn) //]]] //[[[end]]] """, 'test.out': """\ // This is my C++ file. //[[[cog fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] for fn in fnames: cog.outl("void %s();" % fn) //]]] void DoSomething(); void DoAnotherThing(); void DoLastThing(); //[[[end]]] """, } makeFiles(d) self.cog.callableMain(['argv0', '-o', 'in/a/dir/test.cogged', 'test.cog']) self.assertFilesSame('in/a/dir/test.cogged', 'test.out') def testAtFile(self): d = { 'one.cog': """\ //[[[cog cog.outl("hello world") //]]] //[[[end]]] """, 'one.out': """\ //[[[cog cog.outl("hello world") //]]] hello world //[[[end]]] """, 'two.cog': """\ //[[[cog cog.outl("goodbye cruel world") //]]] //[[[end]]] """, 'two.out': """\ //[[[cog cog.outl("goodbye cruel world") //]]] goodbye cruel world //[[[end]]] """, 'cogfiles.txt': """\ # Please run cog one.cog two.cog """ } makeFiles(d) self.cog.callableMain(['argv0', '-r', '@cogfiles.txt']) self.assertFilesSame('one.cog', 'one.out') self.assertFilesSame('two.cog', 'two.out') output = self.output.getvalue() self.assertIn("(changed)", output) def testNestedAtFile(self): d = { 'one.cog': """\ //[[[cog cog.outl("hello world") //]]] //[[[end]]] """, 'one.out': """\ //[[[cog cog.outl("hello world") //]]] hello world //[[[end]]] """, 'two.cog': """\ //[[[cog cog.outl("goodbye cruel world") //]]] //[[[end]]] """, 'two.out': """\ //[[[cog cog.outl("goodbye cruel world") //]]] goodbye cruel world //[[[end]]] """, 'cogfiles.txt': """\ # Please run cog one.cog @cogfiles2.txt """, 'cogfiles2.txt': """\ # This one too, please. two.cog """, } makeFiles(d) self.cog.callableMain(['argv0', '-r', '@cogfiles.txt']) self.assertFilesSame('one.cog', 'one.out') self.assertFilesSame('two.cog', 'two.out') output = self.output.getvalue() self.assertIn("(changed)", output) def testAtFileWithArgs(self): d = { 'both.cog': """\ //[[[cog cog.outl("one: %s" % ('one' in globals())) cog.outl("two: %s" % ('two' in globals())) //]]] //[[[end]]] """, 'one.out': """\ //[[[cog cog.outl("one: %s" % ('one' in globals())) cog.outl("two: %s" % ('two' in globals())) //]]] one: True // ONE two: False // ONE //[[[end]]] """, 'two.out': """\ //[[[cog cog.outl("one: %s" % ('one' in globals())) cog.outl("two: %s" % ('two' in globals())) //]]] one: False // TWO two: True // TWO //[[[end]]] """, 'cogfiles.txt': """\ # Please run cog both.cog -o in/a/dir/both.one -s ' // ONE' -D one=x both.cog -o in/a/dir/both.two -s ' // TWO' -D two=x """ } makeFiles(d) self.cog.callableMain(['argv0', '@cogfiles.txt']) self.assertFilesSame('in/a/dir/both.one', 'one.out') self.assertFilesSame('in/a/dir/both.two', 'two.out') def testAtFileWithBadArgCombo(self): d = { 'both.cog': """\ //[[[cog cog.outl("one: %s" % ('one' in globals())) cog.outl("two: %s" % ('two' in globals())) //]]] //[[[end]]] """, 'cogfiles.txt': """\ # Please run cog both.cog both.cog -d # This is bad: -r and -d """ } makeFiles(d) with self.assertRaisesRegex(CogUsageError, r"^Can't use -d with -r \(or you would delete all your source!\)$"): self.cog.callableMain(['argv0', '-r', '@cogfiles.txt']) def testAtFileWithTrickyFilenames(self): def fix_backslashes(files_txt): """Make the contents of a files.txt sensitive to the platform.""" if sys.platform != "win32": files_txt = files_txt.replace("\\", "/") return files_txt d = { 'one 1.cog': """\ //[[[cog cog.outl("hello world") ]]] """, 'one.out': """\ //[[[cog cog.outl("hello world") ]]] hello world //xxx """, 'subdir': { 'subback.cog': """\ //[[[cog cog.outl("down deep with backslashes") ]]] """, 'subfwd.cog': """\ //[[[cog cog.outl("down deep with slashes") ]]] """, }, 'subback.out': """\ //[[[cog cog.outl("down deep with backslashes") ]]] down deep with backslashes //yyy """, 'subfwd.out': """\ //[[[cog cog.outl("down deep with slashes") ]]] down deep with slashes //zzz """, 'cogfiles.txt': fix_backslashes("""\ # Please run cog 'one 1.cog' -s ' //xxx' subdir\\subback.cog -s ' //yyy' subdir/subfwd.cog -s ' //zzz' """) } makeFiles(d) self.cog.callableMain(['argv0', '-z', '-r', '@cogfiles.txt']) self.assertFilesSame('one 1.cog', 'one.out') self.assertFilesSame('subdir/subback.cog', 'subback.out') self.assertFilesSame('subdir/subfwd.cog', 'subfwd.out') def run_with_verbosity(self, verbosity): d = { 'unchanged.cog': """\ //[[[cog cog.outl("hello world") //]]] hello world //[[[end]]] """, 'changed.cog': """\ //[[[cog cog.outl("goodbye cruel world") //]]] //[[[end]]] """, 'cogfiles.txt': """\ unchanged.cog changed.cog """ } makeFiles(d) self.cog.callableMain(['argv0', '-r', '--verbosity='+verbosity, '@cogfiles.txt']) output = self.output.getvalue() return output def test_verbosity0(self): output = self.run_with_verbosity("0") self.assertEqual(output, "") def test_verbosity1(self): output = self.run_with_verbosity("1") self.assertEqual(output, "Cogging changed.cog (changed)\n") def test_verbosity2(self): output = self.run_with_verbosity("2") self.assertEqual(output, "Cogging unchanged.cog\nCogging changed.cog (changed)\n") class CogTestLineEndings(TestCaseWithTempDir): """Tests for -U option (force LF line-endings in output).""" lines_in = ['Some text.', '//[[[cog', 'cog.outl("Cog text")', '//]]]', 'gobbledegook.', '//[[[end]]]', 'epilogue.', ''] lines_out = ['Some text.', '//[[[cog', 'cog.outl("Cog text")', '//]]]', 'Cog text', '//[[[end]]]', 'epilogue.', ''] def testOutputNativeEol(self): makeFiles({'infile': '\n'.join(self.lines_in)}) self.cog.callableMain(['argv0', '-o', 'outfile', 'infile']) self.assertFileContent('outfile', os.linesep.join(self.lines_out)) def testOutputLfEol(self): makeFiles({'infile': '\n'.join(self.lines_in)}) self.cog.callableMain(['argv0', '-U', '-o', 'outfile', 'infile']) self.assertFileContent('outfile', '\n'.join(self.lines_out)) def testReplaceNativeEol(self): makeFiles({'test.cog': '\n'.join(self.lines_in)}) self.cog.callableMain(['argv0', '-r', 'test.cog']) self.assertFileContent('test.cog', os.linesep.join(self.lines_out)) def testReplaceLfEol(self): makeFiles({'test.cog': '\n'.join(self.lines_in)}) self.cog.callableMain(['argv0', '-U', '-r', 'test.cog']) self.assertFileContent('test.cog', '\n'.join(self.lines_out)) class CogTestCharacterEncoding(TestCaseWithTempDir): def testSimple(self): d = { 'test.cog': b"""\ // This is my C++ file. //[[[cog cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)") //]]] //[[[end]]] """, 'test.out': b"""\ // This is my C++ file. //[[[cog cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)") //]]] // Unicode: \xe1\x88\xb4 (U+1234) //[[[end]]] """.replace(b"\n", os.linesep.encode()), } makeFiles(d, bytes=True) self.cog.callableMain(['argv0', '-r', 'test.cog']) self.assertFilesSame('test.cog', 'test.out') output = self.output.getvalue() self.assertIn("(changed)", output) def testFileEncodingOption(self): d = { 'test.cog': b"""\ // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows //[[[cog cog.outl("\xd1\xfa\xe5\xf8\xfc \xe5\xf9\xb8 \xfd\xf2\xe8\xf5 \xec\xff\xe3\xea\xe8\xf5 \xf4\xf0\xe0\xed\xf6\xf3\xe7\xf1\xea\xe8\xf5 \xe1\xf3\xeb\xee\xea \xe4\xe0 \xe2\xfb\xef\xe5\xe9 \xf7\xe0\xfe") //]]] //[[[end]]] """, 'test.out': b"""\ // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows //[[[cog cog.outl("\xd1\xfa\xe5\xf8\xfc \xe5\xf9\xb8 \xfd\xf2\xe8\xf5 \xec\xff\xe3\xea\xe8\xf5 \xf4\xf0\xe0\xed\xf6\xf3\xe7\xf1\xea\xe8\xf5 \xe1\xf3\xeb\xee\xea \xe4\xe0 \xe2\xfb\xef\xe5\xe9 \xf7\xe0\xfe") //]]] \xd1\xfa\xe5\xf8\xfc \xe5\xf9\xb8 \xfd\xf2\xe8\xf5 \xec\xff\xe3\xea\xe8\xf5 \xf4\xf0\xe0\xed\xf6\xf3\xe7\xf1\xea\xe8\xf5 \xe1\xf3\xeb\xee\xea \xe4\xe0 \xe2\xfb\xef\xe5\xe9 \xf7\xe0\xfe //[[[end]]] """.replace(b"\n", os.linesep.encode()), } makeFiles(d, bytes=True) self.cog.callableMain(['argv0', '-n', 'cp1251', '-r', 'test.cog']) self.assertFilesSame('test.cog', 'test.out') output = self.output.getvalue() self.assertIn("(changed)", output) class TestCaseWithImports(TestCaseWithTempDir): """ When running tests which import modules, the sys.modules list leaks from one test to the next. This test case class scrubs the list after each run to keep the tests isolated from each other. """ def setUp(self): super(TestCaseWithImports, self).setUp() self.sysmodulekeys = list(sys.modules) def tearDown(self): modstoscrub = [ modname for modname in sys.modules if modname not in self.sysmodulekeys ] for modname in modstoscrub: del sys.modules[modname] super(TestCaseWithImports, self).tearDown() class CogIncludeTests(TestCaseWithImports): dincludes = { 'test.cog': """\ //[[[cog import mymodule //]]] //[[[end]]] """, 'test.out': """\ //[[[cog import mymodule //]]] Hello from mymodule //[[[end]]] """, 'test2.out': """\ //[[[cog import mymodule //]]] Hello from mymodule in inc2 //[[[end]]] """, 'include': { 'mymodule.py': """\ import cog cog.outl("Hello from mymodule") """ }, 'inc2': { 'mymodule.py': """\ import cog cog.outl("Hello from mymodule in inc2") """ }, 'inc3': { 'someothermodule.py': """\ import cog cog.outl("This is some other module.") """ }, } def testNeedIncludePath(self): # Try it without the -I, to see that an ImportError happens. makeFiles(self.dincludes) msg = "(ImportError|ModuleNotFoundError): No module named '?mymodule'?" with self.assertRaisesRegex(CogUserException, msg): self.cog.callableMain(['argv0', '-r', 'test.cog']) def testIncludePath(self): # Test that -I adds include directories properly. makeFiles(self.dincludes) self.cog.callableMain(['argv0', '-r', '-I', 'include', 'test.cog']) self.assertFilesSame('test.cog', 'test.out') def testTwoIncludePaths(self): # Test that two -I's add include directories properly. makeFiles(self.dincludes) self.cog.callableMain(['argv0', '-r', '-I', 'include', '-I', 'inc2', 'test.cog']) self.assertFilesSame('test.cog', 'test.out') def testTwoIncludePaths2(self): # Test that two -I's add include directories properly. makeFiles(self.dincludes) self.cog.callableMain(['argv0', '-r', '-I', 'inc2', '-I', 'include', 'test.cog']) self.assertFilesSame('test.cog', 'test2.out') def testUselessIncludePath(self): # Test that the search will continue past the first directory. makeFiles(self.dincludes) self.cog.callableMain(['argv0', '-r', '-I', 'inc3', '-I', 'include', 'test.cog']) self.assertFilesSame('test.cog', 'test.out') def testSysPathIsUnchanged(self): d = { 'bad.cog': """\ //[[[cog cog.error("Oh no!") ]]] //[[[end]]] """, 'good.cog': """\ //[[[cog cog.outl("Oh yes!") ]]] //[[[end]]] """, } makeFiles(d) # Is it unchanged just by creating a cog engine? oldsyspath = sys.path[:] self.newCog() self.assertEqual(oldsyspath, sys.path) # Is it unchanged for a successful run? self.newCog() self.cog.callableMain(['argv0', '-r', 'good.cog']) self.assertEqual(oldsyspath, sys.path) # Is it unchanged for a successful run with includes? self.newCog() self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', 'good.cog']) self.assertEqual(oldsyspath, sys.path) # Is it unchanged for a successful run with two includes? self.newCog() self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', '-I', 'quux', 'good.cog']) self.assertEqual(oldsyspath, sys.path) # Is it unchanged for a failed run? self.newCog() with self.assertRaisesRegex(CogError, r"^Oh no!$"): self.cog.callableMain(['argv0', '-r', 'bad.cog']) self.assertEqual(oldsyspath, sys.path) # Is it unchanged for a failed run with includes? self.newCog() with self.assertRaisesRegex(CogError, r"^Oh no!$"): self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', 'bad.cog']) self.assertEqual(oldsyspath, sys.path) # Is it unchanged for a failed run with two includes? self.newCog() with self.assertRaisesRegex(CogError, r"^Oh no!$"): self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', '-I', 'quux', 'bad.cog']) self.assertEqual(oldsyspath, sys.path) def testSubDirectories(self): # Test that relative paths on the command line work, with includes. d = { 'code': { 'test.cog': """\ //[[[cog import mysubmodule //]]] //[[[end]]] """, 'test.out': """\ //[[[cog import mysubmodule //]]] Hello from mysubmodule //[[[end]]] """, 'mysubmodule.py': """\ import cog cog.outl("Hello from mysubmodule") """ } } makeFiles(d) # We should be able to invoke cog without the -I switch, and it will # auto-include the current directory self.cog.callableMain(['argv0', '-r', 'code/test.cog']) self.assertFilesSame('code/test.cog', 'code/test.out') class CogTestsInFiles(TestCaseWithTempDir): def testWarnIfNoCogCode(self): # Test that the -e switch warns if there is no Cog code. d = { 'with.cog': """\ //[[[cog cog.outl("hello world") //]]] hello world //[[[end]]] """, 'without.cog': """\ There's no cog code in this file. """, } makeFiles(d) self.cog.callableMain(['argv0', '-e', 'with.cog']) output = self.output.getvalue() self.assertNotIn("Warning", output) self.newCog() self.cog.callableMain(['argv0', '-e', 'without.cog']) output = self.output.getvalue() self.assertIn("Warning: no cog code found in without.cog", output) self.newCog() self.cog.callableMain(['argv0', 'without.cog']) output = self.output.getvalue() self.assertNotIn("Warning", output) def testFileNameProps(self): d = { 'cog1.txt': """\ //[[[cog cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile)) //]]] this is cog1.txt in, cog1.txt out [[[end]]] """, 'cog1.out': """\ //[[[cog cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile)) //]]] This is cog1.txt in, cog1.txt out [[[end]]] """, 'cog1out.out': """\ //[[[cog cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile)) //]]] This is cog1.txt in, cog1out.txt out [[[end]]] """, } makeFiles(d) self.cog.callableMain(['argv0', '-r', 'cog1.txt']) self.assertFilesSame('cog1.txt', 'cog1.out') self.newCog() self.cog.callableMain(['argv0', '-o', 'cog1out.txt', 'cog1.txt']) self.assertFilesSame('cog1out.txt', 'cog1out.out') def testGlobalsDontCrossFiles(self): # Make sure that global values don't get shared between files. d = { 'one.cog': """\ //[[[cog s = "This was set in one.cog" ]]] //[[[end]]] //[[[cog cog.outl(s) ]]] //[[[end]]] """, 'one.out': """\ //[[[cog s = "This was set in one.cog" ]]] //[[[end]]] //[[[cog cog.outl(s) ]]] This was set in one.cog //[[[end]]] """, 'two.cog': """\ //[[[cog try: cog.outl(s) except NameError: cog.outl("s isn't set!") //]]] //[[[end]]] """, 'two.out': """\ //[[[cog try: cog.outl(s) except NameError: cog.outl("s isn't set!") //]]] s isn't set! //[[[end]]] """, 'cogfiles.txt': """\ # Please run cog one.cog two.cog """ } makeFiles(d) self.cog.callableMain(['argv0', '-r', '@cogfiles.txt']) self.assertFilesSame('one.cog', 'one.out') self.assertFilesSame('two.cog', 'two.out') output = self.output.getvalue() self.assertIn("(changed)", output) def testRemoveGeneratedOutput(self): d = { 'cog1.txt': """\ //[[[cog cog.outl("This line was generated.") //]]] This line was generated. //[[[end]]] This line was not. """, 'cog1.out': """\ //[[[cog cog.outl("This line was generated.") //]]] //[[[end]]] This line was not. """, 'cog1.out2': """\ //[[[cog cog.outl("This line was generated.") //]]] This line was generated. //[[[end]]] This line was not. """, } makeFiles(d) # Remove generated output. self.cog.callableMain(['argv0', '-r', '-x', 'cog1.txt']) self.assertFilesSame('cog1.txt', 'cog1.out') self.newCog() # Regenerate the generated output. self.cog.callableMain(['argv0', '-r', 'cog1.txt']) self.assertFilesSame('cog1.txt', 'cog1.out2') self.newCog() # Remove the generated output again. self.cog.callableMain(['argv0', '-r', '-x', 'cog1.txt']) self.assertFilesSame('cog1.txt', 'cog1.out') def testMsgCall(self): infile = """\ #[[[cog cog.msg("Hello there!") #]]] #[[[end]]] """ infile = reindentBlock(infile) self.assertEqual(self.cog.processString(infile), infile) output = self.output.getvalue() self.assertEqual(output, "Message: Hello there!\n") def testErrorMessageHasNoTraceback(self): # Test that a Cog error is printed to stderr with no traceback. d = { 'cog1.txt': """\ //[[[cog cog.outl("This line was newly") cog.outl("generated by cog") cog.outl("blah blah.") //]]] Xhis line was newly generated by cog blah blah. //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) """, } makeFiles(d) stderr = StringIO() self.cog.setOutput(stderr=stderr) self.cog.main(['argv0', '-c', '-r', "cog1.txt"]) self.assertEqual(self.output.getvalue(), "Cogging cog1.txt\n") self.assertEqual(stderr.getvalue(), "cog1.txt(9): Output has been edited! Delete old checksum to unprotect.\n") def testDashD(self): d = { 'test.cog': """\ --[[[cog cog.outl("Defined fooey as " + fooey) ]]] --[[[end]]] """, 'test.kablooey': """\ --[[[cog cog.outl("Defined fooey as " + fooey) ]]] Defined fooey as kablooey --[[[end]]] """, 'test.einstein': """\ --[[[cog cog.outl("Defined fooey as " + fooey) ]]] Defined fooey as e=mc2 --[[[end]]] """, } makeFiles(d) self.cog.callableMain(['argv0', '-r', '-D', 'fooey=kablooey', 'test.cog']) self.assertFilesSame('test.cog', 'test.kablooey') makeFiles(d) self.cog.callableMain(['argv0', '-r', '-Dfooey=kablooey', 'test.cog']) self.assertFilesSame('test.cog', 'test.kablooey') makeFiles(d) self.cog.callableMain(['argv0', '-r', '-Dfooey=e=mc2', 'test.cog']) self.assertFilesSame('test.cog', 'test.einstein') makeFiles(d) self.cog.callableMain(['argv0', '-r', '-Dbar=quux', '-Dfooey=kablooey', 'test.cog']) self.assertFilesSame('test.cog', 'test.kablooey') makeFiles(d) self.cog.callableMain(['argv0', '-r', '-Dfooey=kablooey', '-Dbar=quux', 'test.cog']) self.assertFilesSame('test.cog', 'test.kablooey') makeFiles(d) self.cog.callableMain(['argv0', '-r', '-Dfooey=gooey', '-Dfooey=kablooey', 'test.cog']) self.assertFilesSame('test.cog', 'test.kablooey') def testOutputToStdout(self): d = { 'test.cog': """\ --[[[cog cog.outl('Hey there!') ]]] --[[[end]]] """ } makeFiles(d) stderr = StringIO() self.cog.setOutput(stderr=stderr) self.cog.callableMain(['argv0', 'test.cog']) output = self.output.getvalue() outerr = stderr.getvalue() self.assertEqual(output, "--[[[cog cog.outl('Hey there!') ]]]\nHey there!\n--[[[end]]]\n") self.assertEqual(outerr, "") def testReadFromStdin(self): stdin = StringIO("--[[[cog cog.outl('Wow') ]]]\n--[[[end]]]\n") def restore_stdin(old_stdin): sys.stdin = old_stdin self.addCleanup(restore_stdin, sys.stdin) sys.stdin = stdin stderr = StringIO() self.cog.setOutput(stderr=stderr) self.cog.callableMain(['argv0', '-']) output = self.output.getvalue() outerr = stderr.getvalue() self.assertEqual(output, "--[[[cog cog.outl('Wow') ]]]\nWow\n--[[[end]]]\n") self.assertEqual(outerr, "") def testSuffixOutputLines(self): d = { 'test.cog': """\ Hey there. ;[[[cog cog.outl('a\\nb\\n \\nc') ]]] ;[[[end]]] Good bye. """, 'test.out': """\ Hey there. ;[[[cog cog.outl('a\\nb\\n \\nc') ]]] a (foo) b (foo) """ # These three trailing spaces are important. # The suffix is not applied to completely blank lines. """ c (foo) ;[[[end]]] Good bye. """, } makeFiles(d) self.cog.callableMain(['argv0', '-r', '-s', ' (foo)', 'test.cog']) self.assertFilesSame('test.cog', 'test.out') def testEmptySuffix(self): d = { 'test.cog': """\ ;[[[cog cog.outl('a\\nb\\nc') ]]] ;[[[end]]] """, 'test.out': """\ ;[[[cog cog.outl('a\\nb\\nc') ]]] a b c ;[[[end]]] """, } makeFiles(d) self.cog.callableMain(['argv0', '-r', '-s', '', 'test.cog']) self.assertFilesSame('test.cog', 'test.out') def testHellishSuffix(self): d = { 'test.cog': """\ ;[[[cog cog.outl('a\\n\\nb') ]]] """, 'test.out': """\ ;[[[cog cog.outl('a\\n\\nb') ]]] a /\\n*+([)]>< b /\\n*+([)]>< """, } makeFiles(d) self.cog.callableMain(['argv0', '-z', '-r', '-s', r' /\n*+([)]><', 'test.cog']) self.assertFilesSame('test.cog', 'test.out') def testPrologue(self): d = { 'test.cog': """\ Some text. //[[[cog cog.outl(str(math.sqrt(2))[:12])]]] //[[[end]]] epilogue. """, 'test.out': """\ Some text. //[[[cog cog.outl(str(math.sqrt(2))[:12])]]] 1.4142135623 //[[[end]]] epilogue. """, } makeFiles(d) self.cog.callableMain(['argv0', '-r', '-p', 'import math', 'test.cog']) self.assertFilesSame('test.cog', 'test.out') def testThreads(self): # Test that the implicitly imported cog module is actually different for # different threads. numthreads = 20 d = {} for i in range(numthreads): d['f{}.cog'.format(i)] = ( "x\n" * i + "[[[cog\n" + "assert cog.firstLineNum == int(FIRST) == {}\n".format(i+1) + "]]]\n" + "[[[end]]]\n" ) makeFiles(d) results = [] def thread_main(num): try: ret = Cog().main( ['cog.py', '-r', '-D', 'FIRST={}'.format(num+1), 'f{}.cog'.format(num)] ) assert ret == 0 except Exception as exc: # pragma: no cover (only happens on test failure) results.append(exc) else: results.append(None) ts = [threading.Thread(target=thread_main, args=(i,)) for i in range(numthreads)] for t in ts: t.start() for t in ts: t.join() assert results == [None] * numthreads class CheckTests(TestCaseWithTempDir): def run_check(self, args, status=0): actual_status = self.cog.main(['argv0', '--check'] + args) print(self.output.getvalue()) self.assertEqual(status, actual_status) def assert_made_files_unchanged(self, d): for name, content in d.items(): content = reindentBlock(content) if os.name == 'nt': content = content.replace("\n", "\r\n") self.assertFileContent(name, content) def test_check_no_cog(self): d = { 'hello.txt': """\ Hello. """, } makeFiles(d) self.run_check(['hello.txt'], status=0) self.assertEqual(self.output.getvalue(), "Checking hello.txt\n") self.assert_made_files_unchanged(d) def test_check_good(self): d = { 'unchanged.cog': """\ //[[[cog cog.outl("hello world") //]]] hello world //[[[end]]] """, } makeFiles(d) self.run_check(['unchanged.cog'], status=0) self.assertEqual(self.output.getvalue(), "Checking unchanged.cog\n") self.assert_made_files_unchanged(d) def test_check_bad(self): d = { 'changed.cog': """\ //[[[cog cog.outl("goodbye world") //]]] hello world //[[[end]]] """, } makeFiles(d) self.run_check(['changed.cog'], status=5) self.assertEqual(self.output.getvalue(), "Checking changed.cog (changed)\nCheck failed\n") self.assert_made_files_unchanged(d) def test_check_mixed(self): d = { 'unchanged.cog': """\ //[[[cog cog.outl("hello world") //]]] hello world //[[[end]]] """, 'changed.cog': """\ //[[[cog cog.outl("goodbye world") //]]] hello world //[[[end]]] """, } makeFiles(d) for verbosity, output in [ ("0", "Check failed\n"), ("1", "Checking changed.cog (changed)\nCheck failed\n"), ("2", "Checking unchanged.cog\nChecking changed.cog (changed)\nCheck failed\n"), ]: self.newCog() self.run_check(['--verbosity=%s' % verbosity, 'unchanged.cog', 'changed.cog'], status=5) self.assertEqual(self.output.getvalue(), output) self.assert_made_files_unchanged(d) def test_check_with_good_checksum(self): d = { 'good.txt': """\ //[[[cog cog.outl("This line was newly") cog.outl("generated by cog") cog.outl("blah blah.") //]]] This line was newly generated by cog blah blah. //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) """, } makeFiles(d) # Have to use -c with --check if there are checksums in the file. self.run_check(['-c', 'good.txt'], status=0) self.assertEqual(self.output.getvalue(), "Checking good.txt\n") self.assert_made_files_unchanged(d) def test_check_with_bad_checksum(self): d = { 'bad.txt': """\ //[[[cog cog.outl("This line was newly") cog.outl("generated by cog") cog.outl("blah blah.") //]]] This line was newly generated by cog blah blah. //[[[end]]] (checksum: a9999999e5ad6b95c9e9a184b26f4346) """, } makeFiles(d) # Have to use -c with --check if there are checksums in the file. self.run_check(['-c', 'bad.txt'], status=1) self.assertEqual(self.output.getvalue(), "Checking bad.txt\nbad.txt(9): Output has been edited! Delete old checksum to unprotect.\n") self.assert_made_files_unchanged(d) class WritabilityTests(TestCaseWithTempDir): d = { 'test.cog': """\ //[[[cog for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']: cog.outl("void %s();" % fn) //]]] //[[[end]]] """, 'test.out': """\ //[[[cog for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']: cog.outl("void %s();" % fn) //]]] void DoSomething(); void DoAnotherThing(); void DoLastThing(); //[[[end]]] """, } if os.name == 'nt': # for Windows cmd_w_args = 'attrib -R %s' cmd_w_asterisk = 'attrib -R *' else: # for unix-like cmd_w_args = 'chmod +w %s' cmd_w_asterisk = 'chmod +w *' def setUp(self): super(WritabilityTests, self).setUp() makeFiles(self.d) self.testcog = os.path.join(self.tempdir, 'test.cog') os.chmod(self.testcog, stat.S_IREAD) # Make the file readonly. assert not os.access(self.testcog, os.W_OK) def tearDown(self): os.chmod(self.testcog, stat.S_IWRITE) # Make the file writable again. super(WritabilityTests, self).tearDown() def testReadonlyNoCommand(self): with self.assertRaisesRegex(CogError, "^Can't overwrite test.cog$"): self.cog.callableMain(['argv0', '-r', 'test.cog']) assert not os.access(self.testcog, os.W_OK) def testReadonlyWithCommand(self): self.cog.callableMain(['argv0', '-r', '-w', self.cmd_w_args, 'test.cog']) self.assertFilesSame('test.cog', 'test.out') assert os.access(self.testcog, os.W_OK) def testReadonlyWithCommandWithNoSlot(self): self.cog.callableMain(['argv0', '-r', '-w', self.cmd_w_asterisk, 'test.cog']) self.assertFilesSame('test.cog', 'test.out') assert os.access(self.testcog, os.W_OK) def testReadonlyWithIneffectualCommand(self): with self.assertRaisesRegex(CogError, "^Couldn't make test.cog writable$"): self.cog.callableMain(['argv0', '-r', '-w', 'echo %s', 'test.cog']) assert not os.access(self.testcog, os.W_OK) class ChecksumTests(TestCaseWithTempDir): def testCreateChecksumOutput(self): d = { 'cog1.txt': """\ //[[[cog cog.outl("This line was generated.") //]]] This line was generated. //[[[end]]] This line was not. """, 'cog1.out': """\ //[[[cog cog.outl("This line was generated.") //]]] This line was generated. //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893) This line was not. """, } makeFiles(d) self.cog.callableMain(['argv0', '-r', '-c', 'cog1.txt']) self.assertFilesSame('cog1.txt', 'cog1.out') def testCheckChecksumOutput(self): d = { 'cog1.txt': """\ //[[[cog cog.outl("This line was newly") cog.outl("generated by cog") cog.outl("blah blah.") //]]] This line was generated. //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893) """, 'cog1.out': """\ //[[[cog cog.outl("This line was newly") cog.outl("generated by cog") cog.outl("blah blah.") //]]] This line was newly generated by cog blah blah. //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) """, } makeFiles(d) self.cog.callableMain(['argv0', '-r', '-c', 'cog1.txt']) self.assertFilesSame('cog1.txt', 'cog1.out') def testRemoveChecksumOutput(self): d = { 'cog1.txt': """\ //[[[cog cog.outl("This line was newly") cog.outl("generated by cog") cog.outl("blah blah.") //]]] This line was generated. //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893) fooey """, 'cog1.out': """\ //[[[cog cog.outl("This line was newly") cog.outl("generated by cog") cog.outl("blah blah.") //]]] This line was newly generated by cog blah blah. //[[[end]]] fooey """, } makeFiles(d) self.cog.callableMain(['argv0', '-r', 'cog1.txt']) self.assertFilesSame('cog1.txt', 'cog1.out') def testTamperedChecksumOutput(self): d = { 'cog1.txt': """\ //[[[cog cog.outl("This line was newly") cog.outl("generated by cog") cog.outl("blah blah.") //]]] Xhis line was newly generated by cog blah blah. //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) """, 'cog2.txt': """\ //[[[cog cog.outl("This line was newly") cog.outl("generated by cog") cog.outl("blah blah.") //]]] This line was newly generated by cog blah blah! //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) """, 'cog3.txt': """\ //[[[cog cog.outl("This line was newly") cog.outl("generated by cog") cog.outl("blah blah.") //]]] This line was newly generated by cog blah blah. //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) """, 'cog4.txt': """\ //[[[cog cog.outl("This line was newly") cog.outl("generated by cog") cog.outl("blah blah.") //]]] This line was newly generated by cog blah blah.. //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) """, 'cog5.txt': """\ //[[[cog cog.outl("This line was newly") cog.outl("generated by cog") cog.outl("blah blah.") //]]] This line was newly generated by cog blah blah. extra //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) """, 'cog6.txt': """\ //[[[cog cog.outl("This line was newly") cog.outl("generated by cog") cog.outl("blah blah.") //]]] //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346) """, } makeFiles(d) with self.assertRaisesRegex(CogError, r"^cog1.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"): self.cog.callableMain(['argv0', '-c', "cog1.txt"]) with self.assertRaisesRegex(CogError, r"^cog2.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"): self.cog.callableMain(['argv0', '-c', "cog2.txt"]) with self.assertRaisesRegex(CogError, r"^cog3.txt\(10\): Output has been edited! Delete old checksum to unprotect.$"): self.cog.callableMain(['argv0', '-c', "cog3.txt"]) with self.assertRaisesRegex(CogError, r"^cog4.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"): self.cog.callableMain(['argv0', '-c', "cog4.txt"]) with self.assertRaisesRegex(CogError, r"^cog5.txt\(10\): Output has been edited! Delete old checksum to unprotect.$"): self.cog.callableMain(['argv0', '-c', "cog5.txt"]) with self.assertRaisesRegex(CogError, r"^cog6.txt\(6\): Output has been edited! Delete old checksum to unprotect.$"): self.cog.callableMain(['argv0', '-c', "cog6.txt"]) def testArgvIsntModified(self): argv = ['argv0', '-v'] orig_argv = argv[:] self.cog.callableMain(argv) self.assertEqual(argv, orig_argv) class CustomMarkerTests(TestCaseWithTempDir): def testCustomerMarkers(self): d = { 'test.cog': """\ //{{ cog.outl("void %s();" % "MyFunction") //}} //{{end}} """, 'test.out': """\ //{{ cog.outl("void %s();" % "MyFunction") //}} void MyFunction(); //{{end}} """, } makeFiles(d) self.cog.callableMain([ 'argv0', '-r', '--markers={{ }} {{end}}', 'test.cog' ]) self.assertFilesSame('test.cog', 'test.out') def testTrulyWackyMarkers(self): # Make sure the markers are properly re-escaped. d = { 'test.cog': """\ //**( cog.outl("void %s();" % "MyFunction") //**) //**(end)** """, 'test.out': """\ //**( cog.outl("void %s();" % "MyFunction") //**) void MyFunction(); //**(end)** """, } makeFiles(d) self.cog.callableMain([ 'argv0', '-r', '--markers=**( **) **(end)**', 'test.cog' ]) self.assertFilesSame('test.cog', 'test.out') def testChangeJustOneMarker(self): d = { 'test.cog': """\ //**( cog.outl("void %s();" % "MyFunction") //]]] //[[[end]]] """, 'test.out': """\ //**( cog.outl("void %s();" % "MyFunction") //]]] void MyFunction(); //[[[end]]] """, } makeFiles(d) self.cog.callableMain([ 'argv0', '-r', '--markers=**( ]]] [[[end]]]', 'test.cog' ]) self.assertFilesSame('test.cog', 'test.out') class BlakeTests(TestCaseWithTempDir): # Blake Winton's contributions. def testDeleteCode(self): # -o sets the output file. d = { 'test.cog': """\ // This is my C++ file. //[[[cog fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing'] for fn in fnames: cog.outl("void %s();" % fn) //]]] Some Sample Code Here //[[[end]]]Data Data And Some More """, 'test.out': """\ // This is my C++ file. void DoSomething(); void DoAnotherThing(); void DoLastThing(); And Some More """, } makeFiles(d) self.cog.callableMain(['argv0', '-d', '-o', 'test.cogged', 'test.cog']) self.assertFilesSame('test.cogged', 'test.out') def testDeleteCodeWithDashRFails(self): d = { 'test.cog': """\ // This is my C++ file. """ } makeFiles(d) with self.assertRaisesRegex(CogUsageError, r"^Can't use -d with -r \(or you would delete all your source!\)$"): self.cog.callableMain(['argv0', '-r', '-d', 'test.cog']) def testSettingGlobals(self): # Blake Winton contributed a way to set the globals that will be used in # processFile(). d = { 'test.cog': """\ // This is my C++ file. //[[[cog for fn in fnames: cog.outl("void %s();" % fn) //]]] Some Sample Code Here //[[[end]]]""", 'test.out': """\ // This is my C++ file. void DoBlake(); void DoWinton(); void DoContribution(); """, } makeFiles(d) globals = {} globals['fnames'] = ['DoBlake', 'DoWinton', 'DoContribution'] self.cog.options.bDeleteCode = True self.cog.processFile('test.cog', 'test.cogged', globals=globals) self.assertFilesSame('test.cogged', 'test.out') class ErrorCallTests(TestCaseWithTempDir): def testErrorCallHasNoTraceback(self): # Test that cog.error() doesn't show a traceback. d = { 'error.cog': """\ //[[[cog cog.error("Something Bad!") //]]] //[[[end]]] """, } makeFiles(d) self.cog.main(['argv0', '-r', 'error.cog']) output = self.output.getvalue() self.assertEqual(output, "Cogging error.cog\nError: Something Bad!\n") def testRealErrorHasTraceback(self): # Test that a genuine error does show a traceback. d = { 'error.cog': """\ //[[[cog raise RuntimeError("Hey!") //]]] //[[[end]]] """, } makeFiles(d) self.cog.main(['argv0', '-r', 'error.cog']) output = self.output.getvalue() msg = 'Actual output:\n' + output self.assertTrue(output.startswith("Cogging error.cog\nTraceback (most recent"), msg) self.assertIn("RuntimeError: Hey!", output) # Things not yet tested: # - A bad -w command (currently fails silently). cog-3.3.0/cogapp/test_makefiles.py000066400000000000000000000071071414575707500171330ustar00rootroot00000000000000""" Test the cogapp.makefiles modules http://nedbatchelder.com/code/cog Copyright 2004-2019, Ned Batchelder. """ from __future__ import absolute_import import shutil import os import random import tempfile from . import makefiles from .backward import TestCase class SimpleTests(TestCase): def setUp(self): # Create a temporary directory. my_dir = 'testmakefiles_tempdir_' + str(random.random())[2:] self.tempdir = os.path.join(tempfile.gettempdir(), my_dir) os.mkdir(self.tempdir) def tearDown(self): # Get rid of the temporary directory. shutil.rmtree(self.tempdir) def exists(self, dname, fname): return os.path.exists(os.path.join(dname, fname)) def checkFilesExist(self, d, dname): for fname in d.keys(): assert(self.exists(dname, fname)) if type(d[fname]) == type({}): self.checkFilesExist(d[fname], os.path.join(dname, fname)) def checkFilesDontExist(self, d, dname): for fname in d.keys(): assert(not self.exists(dname, fname)) def testOneFile(self): fname = 'foo.txt' notfname = 'not_here.txt' d = { fname: "howdy" } assert(not self.exists(self.tempdir, fname)) assert(not self.exists(self.tempdir, notfname)) makefiles.makeFiles(d, self.tempdir) assert(self.exists(self.tempdir, fname)) assert(not self.exists(self.tempdir, notfname)) makefiles.removeFiles(d, self.tempdir) assert(not self.exists(self.tempdir, fname)) assert(not self.exists(self.tempdir, notfname)) def testManyFiles(self): d = { 'top1.txt': "howdy", 'top2.txt': "hello", 'sub': { 'sub1.txt': "inside", 'sub2.txt': "inside2", }, } self.checkFilesDontExist(d, self.tempdir) makefiles.makeFiles(d, self.tempdir) self.checkFilesExist(d, self.tempdir) makefiles.removeFiles(d, self.tempdir) self.checkFilesDontExist(d, self.tempdir) def testOverlapping(self): d1 = { 'top1.txt': "howdy", 'sub': { 'sub1.txt': "inside", }, } d2 = { 'top2.txt': "hello", 'sub': { 'sub2.txt': "inside2", }, } self.checkFilesDontExist(d1, self.tempdir) self.checkFilesDontExist(d2, self.tempdir) makefiles.makeFiles(d1, self.tempdir) makefiles.makeFiles(d2, self.tempdir) self.checkFilesExist(d1, self.tempdir) self.checkFilesExist(d2, self.tempdir) makefiles.removeFiles(d1, self.tempdir) makefiles.removeFiles(d2, self.tempdir) self.checkFilesDontExist(d1, self.tempdir) self.checkFilesDontExist(d2, self.tempdir) def testContents(self): fname = 'bar.txt' cont0 = "I am bar.txt" d = { fname: cont0 } makefiles.makeFiles(d, self.tempdir) fcont1 = open(os.path.join(self.tempdir, fname)) assert(fcont1.read() == cont0) fcont1.close() def testDedent(self): fname = 'dedent.txt' d = { fname: """\ This is dedent.txt \tTabbed in. spaced in. OK. """, } makefiles.makeFiles(d, self.tempdir) fcont = open(os.path.join(self.tempdir, fname)) assert(fcont.read() == "This is dedent.txt\n\tTabbed in.\n spaced in.\nOK.\n") fcont.close() cog-3.3.0/cogapp/test_whiteutils.py000066400000000000000000000076541414575707500174030ustar00rootroot00000000000000""" Test the cogapp.whiteutils module. http://nedbatchelder.com/code/cog Copyright 2004-2019, Ned Batchelder. """ from __future__ import absolute_import from .backward import TestCase from .whiteutils import * class WhitePrefixTests(TestCase): """ Test cases for cogapp.whiteutils. """ def testSingleLine(self): self.assertEqual(whitePrefix(['']), '') self.assertEqual(whitePrefix([' ']), '') self.assertEqual(whitePrefix(['x']), '') self.assertEqual(whitePrefix([' x']), ' ') self.assertEqual(whitePrefix(['\tx']), '\t') self.assertEqual(whitePrefix([' x']), ' ') self.assertEqual(whitePrefix([' \t \tx ']), ' \t \t') def testMultiLine(self): self.assertEqual(whitePrefix([' x',' x',' x']), ' ') self.assertEqual(whitePrefix([' y',' y',' y']), ' ') self.assertEqual(whitePrefix([' y',' y',' y']), ' ') def testBlankLinesAreIgnored(self): self.assertEqual(whitePrefix([' x',' x','',' x']), ' ') self.assertEqual(whitePrefix(['',' x',' x',' x']), ' ') self.assertEqual(whitePrefix([' x',' x',' x','']), ' ') self.assertEqual(whitePrefix([' x',' x',' ',' x']), ' ') def testTabCharacters(self): self.assertEqual(whitePrefix(['\timport sys', '', '\tprint sys.argv']), '\t') def testDecreasingLengths(self): self.assertEqual(whitePrefix([' x',' x',' x']), ' ') self.assertEqual(whitePrefix([' x',' x',' x']), ' ') class ReindentBlockTests(TestCase): """ Test cases for cogapp.reindentBlock. """ def testNonTermLine(self): self.assertEqual(reindentBlock(''), '') self.assertEqual(reindentBlock('x'), 'x') self.assertEqual(reindentBlock(' x'), 'x') self.assertEqual(reindentBlock(' x'), 'x') self.assertEqual(reindentBlock('\tx'), 'x') self.assertEqual(reindentBlock('x', ' '), ' x') self.assertEqual(reindentBlock('x', '\t'), '\tx') self.assertEqual(reindentBlock(' x', ' '), ' x') self.assertEqual(reindentBlock(' x', '\t'), '\tx') self.assertEqual(reindentBlock(' x', ' '), ' x') def testSingleLine(self): self.assertEqual(reindentBlock('\n'), '\n') self.assertEqual(reindentBlock('x\n'), 'x\n') self.assertEqual(reindentBlock(' x\n'), 'x\n') self.assertEqual(reindentBlock(' x\n'), 'x\n') self.assertEqual(reindentBlock('\tx\n'), 'x\n') self.assertEqual(reindentBlock('x\n', ' '), ' x\n') self.assertEqual(reindentBlock('x\n', '\t'), '\tx\n') self.assertEqual(reindentBlock(' x\n', ' '), ' x\n') self.assertEqual(reindentBlock(' x\n', '\t'), '\tx\n') self.assertEqual(reindentBlock(' x\n', ' '), ' x\n') def testRealBlock(self): self.assertEqual( reindentBlock('\timport sys\n\n\tprint sys.argv\n'), 'import sys\n\nprint sys.argv\n' ) class CommonPrefixTests(TestCase): """ Test cases for cogapp.commonPrefix. """ def testDegenerateCases(self): self.assertEqual(commonPrefix([]), '') self.assertEqual(commonPrefix(['']), '') self.assertEqual(commonPrefix(['','','','','']), '') self.assertEqual(commonPrefix(['cat in the hat']), 'cat in the hat') def testNoCommonPrefix(self): self.assertEqual(commonPrefix(['a','b']), '') self.assertEqual(commonPrefix(['a','b','c','d','e','f']), '') self.assertEqual(commonPrefix(['a','a','a','a','a','x']), '') def testUsualCases(self): self.assertEqual(commonPrefix(['ab', 'ac']), 'a') self.assertEqual(commonPrefix(['aab', 'aac']), 'aa') self.assertEqual(commonPrefix(['aab', 'aab', 'aab', 'aac']), 'aa') def testBlankLine(self): self.assertEqual(commonPrefix(['abc', 'abx', '', 'aby']), '') def testDecreasingLengths(self): self.assertEqual(commonPrefix(['abcd', 'abc', 'ab']), 'ab') cog-3.3.0/cogapp/whiteutils.py000066400000000000000000000041011414575707500163240ustar00rootroot00000000000000""" Indentation utilities for Cog. http://nedbatchelder.com/code/cog Copyright 2004-2019, Ned Batchelder. """ from __future__ import absolute_import import re from .backward import string_types, bytes_types, to_bytes def whitePrefix(strings): """ Determine the whitespace prefix common to all non-blank lines in the argument list. """ # Remove all blank lines from the list strings = [s for s in strings if s.strip() != ''] if not strings: return '' # Find initial whitespace chunk in the first line. # This is the best prefix we can hope for. pat = r'\s*' if isinstance(strings[0], bytes_types): pat = to_bytes(pat) prefix = re.match(pat, strings[0]).group(0) # Loop over the other strings, keeping only as much of # the prefix as matches each string. for s in strings: for i in range(len(prefix)): if prefix[i] != s[i]: prefix = prefix[:i] break return prefix def reindentBlock(lines, newIndent=''): """ Take a block of text as a string or list of lines. Remove any common whitespace indentation. Re-indent using newIndent, and return it as a single string. """ sep, nothing = '\n', '' if isinstance(lines, bytes_types): sep, nothing = b'\n', b'' if isinstance(lines, string_types): lines = lines.split(sep) oldIndent = whitePrefix(lines) outLines = [] for l in lines: if oldIndent: l = l.replace(oldIndent, nothing, 1) if l and newIndent: l = newIndent + l outLines.append(l) return sep.join(outLines) def commonPrefix(strings): """ Find the longest string that is a prefix of all the strings. """ if not strings: return '' prefix = strings[0] for s in strings: if len(s) < len(prefix): prefix = prefix[:len(s)] if not prefix: return '' for i in range(len(prefix)): if prefix[i] != s[i]: prefix = prefix[:i] break return prefix cog-3.3.0/index.px000066400000000000000000000507421414575707500137740ustar00rootroot00000000000000

Created. Version 1.1. Minor edits for clarity. Updated to cog 1.11, added a See Also section, and fixed a sample. Updated to cog 1.12. Updated to cog 1.2. Updated to cog 1.3. Updated to cog 1.4. Added links to other Cog implementations. Added links to 2.0 beta 2. Updating for 2.0. Added PCG. Added an explicit mention of the license: MIT. Added links to 3rd-party packages. Clarified -D value types, and fixed a 3rd-party link. Tried to explain better about indentation, and fixed an incorrect parameter name. Added -U switch from Alexander Belchenko. Fixed the russian pointer to be to a current document. Removed handyxml, files are now at pypi. Python 3 is supported! Polish up Cog 2.3 Version 2.4 Version 3.0.0 Version 3.2.0 Version 3.3.0 An older version of this document is also available in Russian.

Cog is a file generation tool. It lets you use pieces of Python code as generators in your source files to generate whatever text you need.

This page describes version 3.3.0, released November 19, 2021.

The sections below are:

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.

For example, if you run this file through cog:

it will come out like this:

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.

Output is written with cog.outl, or if you use the -P option, normal print() calls.

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.

Installation

Cog requires Python 2.7, 3.5, 3.6, 3.7, 3.8, or Jython 2.5.

Cog is installed in the usual way, except the name is "cogapp", not "cog":

You should now have a "cog" command, or cog.py in your Python scripts directory.

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:

will produce 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.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.

In addition to running cog as a command on the command line, 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:

then invoke cog like this:

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.

As another example, cogfiles2.txt could be:

with cog invoked like this:

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:

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:

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.

Continuous integration

You can use the --check option to run cog just to check that the files would not change if run again. This is useful in continuous integration to check that your files have been updated properly.

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):

invoking cog like this:

will produce this output:

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 content 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 -p option specifies Python text to prepend to embdedded generator source, which can keep common imports out of source files.

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

History

Cog's change log is on a separate change page.

Feedback

I'd love to hear about your successes or difficulties using cog. Comment here, or send me a note.

See Also

There are a handful of other implementations of the ideas in Cog:

  • Argent is a Ruby implementation.
  • Precog is a PHP implementation.
  • PCG is a Perl implementation.
  • Templarian is a similar tool, also in Python.
  • Nocog is a build tool to detect files that should be run through cog.

You might like to read:

cog-3.3.0/index_ru.px000066400000000000000000000772171414575707500145100ustar00rootroot00000000000000 Alexander Belchenko's translation. Tweaks from Alexander. Alexander's updates (to version 1.3). Ned's updates (adding the English for 1.4). Alexander's updates (to version 1.4). Alexander's updates (to version 2.0). Alexander's updates (to version 2.1). decorator-factory's updates (to version 3.0). Оригинал текста на английском языке находится здесь.
Перевод на русский язык: Александр Бельченко, страница обновлена 19 мая 2008.
Перевод обновлён decorator-factory 30 сентября 2020.

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

Разделы:

Что делает эта утилита?

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

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

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

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

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

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

Дизайн

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

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

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

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

Установка

Cog работает на Python версии 2.7, 3.5, 3.6, 3.7, 3.8 или Jython 2.5.

Cog устанавливается обычным образом, за исключением того, что имя пакета "cogapp", а не "cog":

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

Лицензия

Cog распространяется согласно MIT лицензии. Используйте Cog, чтобы сделать этот мир чуточку лучше.

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

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

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

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

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

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

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

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

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

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

Модуль Cog

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

cog.out(sOut='' [, dedent=False][, trimblanklines=False])
Пишет текст в область вывода.
sOut это строковая переменная для вывода.
Если аргумент dedent равен True, то все пробельные символы в начале строк в sOut удаляются перед добавлением к блоку вывода. Если аргумент trimblanklines равен True, то все пустые строки в начале и конце sOut удаляются перед добавлением к блоку вывода. Вместе, эти две опции облегчают использование многострочных строковых переменных:
cog.outl
Аналогично cog.out, но добавляет в конце перевод строки.
cog.msg(msg)
Печатает строку msg в консольном окне (поток stdout) с префиксом "Message: ".
cog.error(msg)
Возбуждает исключение с текстовым сообщением msg. В консольном окне выводится сообщение msg без вывода стека вызовов Python. Таким образом, программисты не знакомые с Python, которые используют ваш Cog-генератор, не будут пугаться длинного traceback-а.
cog.inFile
Атрибут, представляющий имя входного файла.
cog.outFile
Атрибут, представляющий имя выходного файла.
cog.firstLineNum
Атрибут, представляющий номер строки с первым Python-оператором в генераторе. Этот атрибут может при необходимости использоваться, чтобы различать два генератора в одном и том же входном файле.

В дистрибутив Cog включен модуль handyxml, поскольку я использую файлы данных в формате XML в своих собственных задачах генерации кода.

Запуск Cog

Запуск Cog выполняется из командной строки, аргументы передаются стандартным образом.

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

$ cog @files_to_cog.txt

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

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

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

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

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

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

Перезапись файлов

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

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

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

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

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

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

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

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

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

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

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

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

Запуская Cog такой командой:

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

Разное

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

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

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

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

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

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

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

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

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

История

Список изменений в версиях Cog приведен на отдельной странице.

Обратная связь

Мне будет приятно услышать о ваших успехах или трудностях при использовании Cog. Оставьте комментарий здесь, или напишите мне небольшое письмо. Пожалуйста, пишите на английском языке.

Ссылки

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

  • Argent реализация на языке Ruby.
  • Precog реализация на языке PHP.
  • PCG реализация на языке Perl.
  • Templarian похожий инструмент, также написан на Python.
  • Nocog инструмент для автоматического определения тех файлов, которые должны обрабатываться при помощи Cog.

Возможно, вы захотите прочитать:

  • Cog: A Code Generation Tool Written in Python, я написал историю про утилиту Cog, как пример успешного применения Python.
  • Мой блог, на страницах которого я рассуждаю о программном обеспечении, детях и других интересных для меня вещах.
cog-3.3.0/requirements.pip000066400000000000000000000000501414575707500155340ustar00rootroot00000000000000build coverage tox tox-gh-actions twine cog-3.3.0/scripts/000077500000000000000000000000001414575707500137735ustar00rootroot00000000000000cog-3.3.0/scripts/cog.py000066400000000000000000000003161414575707500151150ustar00rootroot00000000000000#!/usr/bin/env python """ Cog content generation tool. http://nedbatchelder.com/code/cog Copyright 2004-2019, Ned Batchelder. """ import sys from cogapp import Cog sys.exit(Cog().main(sys.argv)) cog-3.3.0/setup.cfg000066400000000000000000000002561414575707500141300ustar00rootroot00000000000000[bdist_wheel] universal = 1 [tool:pytest] addopts = -q -rfe [mutmut] paths_to_mutate = cogapp/cogapp.py runner = sh -c '.tox/py27/bin/pytest -x && .tox/py36/bin/pytest -x' cog-3.3.0/setup.py000066400000000000000000000036071414575707500140240ustar00rootroot00000000000000#!/usr/bin/python """ Setup.py for Cog http://nedbatchelder.com/code/cog Copyright 2004-2021, Ned Batchelder. """ from setuptools import setup with open("README.rst") as readme: long_description = readme.read() setup( name = 'cogapp', # Because there's already a Cog in pypi! :( version = '3.3.0', url = 'http://nedbatchelder.com/code/cog', author = 'Ned Batchelder', author_email = 'ned@nedbatchelder.com', description = 'Cog: A content generator for executing Python snippets in source files.', long_description = long_description, long_description_content_type = 'text/x-rst', project_urls={ 'Documentation': 'http://nedbatchelder.com/code/cog', 'Code': 'http://github.com/nedbat/cog', 'Issues': 'https://github.com/nedbat/cog/issues', 'Funding': 'https://github.com/users/nedbat/sponsorship', 'Twitter': 'https://twitter.com/nedbat', }, classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Topic :: Software Development :: Code Generators", ], python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*", license = 'MIT', packages = [ 'cogapp', ], scripts = [ 'scripts/cog.py', ], entry_points={ 'console_scripts': [ 'cog = cogapp:main', ], }, ) cog-3.3.0/tox.ini000066400000000000000000000006461414575707500136250ustar00rootroot00000000000000# tox configuration for Cog. [tox] envlist = py27,py35,py36,py37,py38,py39,py310,coverage [testenv] deps = coverage pytest commands = coverage run -m pytest {posargs} usedevelop = True [testenv:coverage] skip_install = True commands = coverage combine -q coverage report -m [gh-actions] python = 2.7: py27 3.5: py35 3.6: py36 3.7: py37 3.8: py38 3.9: py39 3.10: py310