- 1""" Cog content generation tool.
- 2"""
- 3
- 4import copy
- 5import getopt
- 6import glob
- 7import io
- 8import linecache
- 9import os
- 10import re
- 11import shlex
- 12import sys
- 13import traceback
- 14import types
- 15
- 16from .whiteutils import commonPrefix, reindentBlock, whitePrefix
- 17from .utils import NumberedFileReader, Redirectable, change_dir, md5
- 18
- 19__version__ = "3.4.1"
- 20
- 21usage = """\
- 22cog - generate content with inlined Python code.
- 23
- 24cog [OPTIONS] [INFILE | @FILELIST | &FILELIST] ...
- 25
- 26INFILE is the name of an input file, '-' will read from stdin.
- 27FILELIST is the name of a text file containing file names or
- 28other @FILELISTs.
- 29
- 30For @FILELIST, paths in the file list are relative to the working
- 31directory where cog was called. For &FILELIST, paths in the file
- 32list are relative to the file list location.
- 33
- 34OPTIONS:
- 35 -c Checksum the output to protect it against accidental change.
- 36 -d Delete the generator code from the output file.
- 37 -D name=val Define a global string available to your generator code.
- 38 -e Warn if a file has no cog code in it.
- 39 -I PATH Add PATH to the list of directories for data files and modules.
- 40 -n ENCODING Use ENCODING when reading and writing files.
- 41 -o OUTNAME Write the output to OUTNAME.
- 42 -p PROLOGUE Prepend the generator source with PROLOGUE. Useful to insert an
- 43 import line. Example: -p "import math"
- 44 -P Use print() instead of cog.outl() for code output.
- 45 -r Replace the input file with the output.
- 46 -s STRING Suffix all generated output lines with STRING.
- 47 -U Write the output with Unix newlines (only LF line-endings).
- 48 -w CMD Use CMD if the output file needs to be made writable.
- 49 A %s in the CMD will be filled with the filename.
- 50 -x Excise all the generated output without running the generators.
- 51 -z The end-output marker can be omitted, and is assumed at eof.
- 52 -v Print the version of cog and exit.
- 53 --check Check that the files would not change if run again.
- 54 --markers='START END END-OUTPUT'
- 55 The patterns surrounding cog inline instructions. Should
- 56 include three values separated by spaces, the start, end,
- 57 and end-output markers. Defaults to '[[[cog ]]] [[[end]]]'.
- 58 --verbosity=VERBOSITY
- 59 Control the amount of output. 2 (the default) lists all files,
- 60 1 lists only changed files, 0 lists no files.
- 61 -h Print this help.
- 62"""
+ 1"""Cog content generation tool."""
+ 2
+ 3import copy
+ 4import getopt
+ 5import glob
+ 6import io
+ 7import linecache
+ 8import os
+ 9import re
+ 10import shlex
+ 11import sys
+ 12import traceback
+ 13import types
+ 14
+ 15from .whiteutils import common_prefix, reindent_block, white_prefix
+ 16from .utils import NumberedFileReader, Redirectable, change_dir, md5
+ 17
+ 18__version__ = "3.4.1"
+ 19
+ 20usage = """\
+ 21cog - generate content with inlined Python code.
+ 22
+ 23cog [OPTIONS] [INFILE | @FILELIST | &FILELIST] ...
+ 24
+ 25INFILE is the name of an input file, '-' will read from stdin.
+ 26FILELIST is the name of a text file containing file names or
+ 27other @FILELISTs.
+ 28
+ 29For @FILELIST, paths in the file list are relative to the working
+ 30directory where cog was called. For &FILELIST, paths in the file
+ 31list are relative to the file list location.
+ 32
+ 33OPTIONS:
+ 34 -c Checksum the output to protect it against accidental change.
+ 35 -d Delete the generator code from the output file.
+ 36 -D name=val Define a global string available to your generator code.
+ 37 -e Warn if a file has no cog code in it.
+ 38 -I PATH Add PATH to the list of directories for data files and modules.
+ 39 -n ENCODING Use ENCODING when reading and writing files.
+ 40 -o OUTNAME Write the output to OUTNAME.
+ 41 -p PROLOGUE Prepend the generator source with PROLOGUE. Useful to insert an
+ 42 import line. Example: -p "import math"
+ 43 -P Use print() instead of cog.outl() for code output.
+ 44 -r Replace the input file with the output.
+ 45 -s STRING Suffix all generated output lines with STRING.
+ 46 -U Write the output with Unix newlines (only LF line-endings).
+ 47 -w CMD Use CMD if the output file needs to be made writable.
+ 48 A %s in the CMD will be filled with the filename.
+ 49 -x Excise all the generated output without running the generators.
+ 50 -z The end-output marker can be omitted, and is assumed at eof.
+ 51 -v Print the version of cog and exit.
+ 52 --check Check that the files would not change if run again.
+ 53 --markers='START END END-OUTPUT'
+ 54 The patterns surrounding cog inline instructions. Should
+ 55 include three values separated by spaces, the start, end,
+ 56 and end-output markers. Defaults to '[[[cog ]]] [[[end]]]'.
+ 57 --verbosity=VERBOSITY
+ 58 Control the amount of output. 2 (the default) lists all files,
+ 59 1 lists only changed files, 0 lists no files.
+ 60 -h Print this help.
+ 61"""
+ 62
63
64class CogError(Exception):
- 65 """ Any exception raised by Cog.
- 66 """
- 67 def __init__(self, msg, file='', line=0):
+ 65 """Any exception raised by Cog."""
+ 66
+ 67 def __init__(self, msg, file="", line=0):
68 if file:
69 super().__init__(f"{file}({line}): {msg}")
70 else:
71 super().__init__(msg)
72
- 73class CogUsageError(CogError):
- 74 """ An error in usage of command-line arguments in cog.
- 75 """
- 76 pass
- 77
- 78class CogInternalError(CogError):
- 79 """ An error in the coding of Cog. Should never happen.
- 80 """
- 81 pass
+ 73
+ 74class CogUsageError(CogError):
+ 75 """An error in usage of command-line arguments in cog."""
+ 76
+ 77 pass
+ 78
+ 79
+ 80class CogInternalError(CogError):
+ 81 """An error in the coding of Cog. Should never happen."""
82
- 83class CogGeneratedError(CogError):
- 84 """ An error raised by a user's cog generator.
- 85 """
- 86 pass
- 87
- 88class CogUserException(CogError):
- 89 """ An exception caught when running a user's cog generator.
- 90 The argument is the traceback message to print.
- 91 """
- 92 pass
- 93
- 94class CogCheckFailed(CogError):
- 95 """ A --check failed.
- 96 """
- 97 pass
+ 83 pass
+ 84
+ 85
+ 86class CogGeneratedError(CogError):
+ 87 """An error raised by a user's cog generator."""
+ 88
+ 89 pass
+ 90
+ 91
+ 92class CogUserException(CogError):
+ 93 """An exception caught when running a user's cog generator.
+ 94
+ 95 The argument is the traceback message to print.
+ 96
+ 97 """
98
- 99
- 100class CogGenerator(Redirectable):
- 101 """ A generator pulled from a source file.
- 102 """
- 103 def __init__(self, options=None):
- 104 super().__init__()
- 105 self.markers = []
- 106 self.lines = []
- 107 self.options = options or CogOptions()
- 108
- 109 def parseMarker(self, l):
- 110 self.markers.append(l)
- 111
- 112 def parseLine(self, l):
- 113 self.lines.append(l.strip('\n'))
- 114
- 115 def getCode(self):
- 116 """ Extract the executable Python code from the generator.
- 117 """
- 118 # If the markers and lines all have the same prefix
- 119 # (end-of-line comment chars, for example),
- 120 # then remove it from all the lines.
- 121 prefIn = commonPrefix(self.markers + self.lines)
- 122 if prefIn:
- 123 self.markers = [ l.replace(prefIn, '', 1) for l in self.markers ]
- 124 self.lines = [ l.replace(prefIn, '', 1) for l in self.lines ]
- 125
- 126 return reindentBlock(self.lines, '')
- 127
- 128 def evaluate(self, cog, globals, fname):
- 129 # figure out the right whitespace prefix for the output
- 130 prefOut = whitePrefix(self.markers)
- 131
- 132 intext = self.getCode()
- 133 if not intext:
- 134 return ''
- 135
- 136 prologue = "import " + cog.cogmodulename + " as cog\n"
- 137 if self.options.sPrologue: 137 ↛ 138line 137 didn't jump to line 138, because the condition on line 137 was never true
- 138 prologue += self.options.sPrologue + '\n'
- 139 code = compile(prologue + intext, str(fname), 'exec')
- 140
- 141 # Make sure the "cog" module has our state.
- 142 cog.cogmodule.msg = self.msg
- 143 cog.cogmodule.out = self.out
- 144 cog.cogmodule.outl = self.outl
- 145 cog.cogmodule.error = self.error
- 146
- 147 real_stdout = sys.stdout
- 148 if self.options.bPrintOutput: 148 ↛ 149line 148 didn't jump to line 149, because the condition on line 148 was never true
- 149 sys.stdout = captured_stdout = io.StringIO()
- 150
- 151 self.outstring = ''
- 152 try:
- 153 eval(code, globals)
- 154 except CogError: 154 ↛ 155line 154 didn't jump to line 155, because the exception caught by line 154 didn't happen
- 155 raise
- 156 except:
- 157 typ, err, tb = sys.exc_info()
- 158 frames = (tuple(fr) for fr in traceback.extract_tb(tb.tb_next))
- 159 frames = find_cog_source(frames, prologue)
- 160 msg = "".join(traceback.format_list(frames))
- 161 msg += f"{typ.__name__}: {err}"
- 162 raise CogUserException(msg)
- 163 finally:
- 164 sys.stdout = real_stdout
- 165
- 166 if self.options.bPrintOutput: 166 ↛ 167line 166 didn't jump to line 167, because the condition on line 166 was never true
- 167 self.outstring = captured_stdout.getvalue()
- 168
- 169 # We need to make sure that the last line in the output
- 170 # ends with a newline, or it will be joined to the
- 171 # end-output line, ruining cog's idempotency.
- 172 if self.outstring and self.outstring[-1] != '\n':
- 173 self.outstring += '\n'
- 174
- 175 return reindentBlock(self.outstring, prefOut)
- 176
- 177 def msg(self, s):
- 178 self.prout("Message: "+s)
- 179
- 180 def out(self, sOut='', dedent=False, trimblanklines=False):
- 181 """ The cog.out function.
- 182 """
- 183 if trimblanklines and ('\n' in sOut):
- 184 lines = sOut.split('\n')
- 185 if lines[0].strip() == '':
- 186 del lines[0]
- 187 if lines and lines[-1].strip() == '':
- 188 del lines[-1]
- 189 sOut = '\n'.join(lines)+'\n'
- 190 if dedent:
- 191 sOut = reindentBlock(sOut)
- 192 self.outstring += sOut
- 193
- 194 def outl(self, sOut='', **kw):
- 195 """ The cog.outl function.
- 196 """
- 197 self.out(sOut, **kw)
- 198 self.out('\n')
+ 99 pass
+ 100
+ 101
+ 102class CogCheckFailed(CogError):
+ 103 """A --check failed."""
+ 104
+ 105 pass
+ 106
+ 107
+ 108class CogGenerator(Redirectable):
+ 109 """A generator pulled from a source file."""
+ 110
+ 111 def __init__(self, options=None):
+ 112 super().__init__()
+ 113 self.markers = []
+ 114 self.lines = []
+ 115 self.options = options or CogOptions()
+ 116
+ 117 def parse_marker(self, line):
+ 118 self.markers.append(line)
+ 119
+ 120 def parse_line(self, line):
+ 121 self.lines.append(line.strip("\n"))
+ 122
+ 123 def get_code(self):
+ 124 """Extract the executable Python code from the generator."""
+ 125 # If the markers and lines all have the same prefix
+ 126 # (end-of-line comment chars, for example),
+ 127 # then remove it from all the lines.
+ 128 pref_in = common_prefix(self.markers + self.lines)
+ 129 if pref_in:
+ 130 self.markers = [line.replace(pref_in, "", 1) for line in self.markers]
+ 131 self.lines = [line.replace(pref_in, "", 1) for line in self.lines]
+ 132
+ 133 return reindent_block(self.lines, "")
+ 134
+ 135 def evaluate(self, cog, globals, fname):
+ 136 # figure out the right whitespace prefix for the output
+ 137 pref_out = white_prefix(self.markers)
+ 138
+ 139 intext = self.get_code()
+ 140 if not intext:
+ 141 return ""
+ 142
+ 143 prologue = "import " + cog.cogmodulename + " as cog\n"
+ 144 if self.options.prologue: 144 ↛ 145line 144 didn't jump to line 145, because the condition on line 144 was never true
+ 145 prologue += self.options.prologue + "\n"
+ 146 code = compile(prologue + intext, str(fname), "exec")
+ 147
+ 148 # Make sure the "cog" module has our state.
+ 149 cog.cogmodule.msg = self.msg
+ 150 cog.cogmodule.out = self.out
+ 151 cog.cogmodule.outl = self.outl
+ 152 cog.cogmodule.error = self.error
+ 153
+ 154 real_stdout = sys.stdout
+ 155 if self.options.print_output: 155 ↛ 156line 155 didn't jump to line 156, because the condition on line 155 was never true
+ 156 sys.stdout = captured_stdout = io.StringIO()
+ 157
+ 158 self.outstring = ""
+ 159 try:
+ 160 eval(code, globals)
+ 161 except CogError: 161 ↛ 162line 161 didn't jump to line 162, because the exception caught by line 161 didn't happen
+ 162 raise
+ 163 except: # noqa: E722 (we're just wrapping in CogUserException and rethrowing)
+ 164 typ, err, tb = sys.exc_info()
+ 165 frames = (tuple(fr) for fr in traceback.extract_tb(tb.tb_next))
+ 166 frames = find_cog_source(frames, prologue)
+ 167 msg = "".join(traceback.format_list(frames))
+ 168 msg += f"{typ.__name__}: {err}"
+ 169 raise CogUserException(msg)
+ 170 finally:
+ 171 sys.stdout = real_stdout
+ 172
+ 173 if self.options.print_output: 173 ↛ 174line 173 didn't jump to line 174, because the condition on line 173 was never true
+ 174 self.outstring = captured_stdout.getvalue()
+ 175
+ 176 # We need to make sure that the last line in the output
+ 177 # ends with a newline, or it will be joined to the
+ 178 # end-output line, ruining cog's idempotency.
+ 179 if self.outstring and self.outstring[-1] != "\n":
+ 180 self.outstring += "\n"
+ 181
+ 182 return reindent_block(self.outstring, pref_out)
+ 183
+ 184 def msg(self, s):
+ 185 self.prout("Message: " + s)
+ 186
+ 187 def out(self, sOut="", dedent=False, trimblanklines=False):
+ 188 """The cog.out function."""
+ 189 if trimblanklines and ("\n" in sOut):
+ 190 lines = sOut.split("\n")
+ 191 if lines[0].strip() == "":
+ 192 del lines[0]
+ 193 if lines and lines[-1].strip() == "":
+ 194 del lines[-1]
+ 195 sOut = "\n".join(lines) + "\n"
+ 196 if dedent:
+ 197 sOut = reindent_block(sOut)
+ 198 self.outstring += sOut
199
- 200 def error(self, msg='Error raised by cog generator.'):
- 201 """ The cog.error function.
- 202 Instead of raising standard python errors, cog generators can use
- 203 this function. It will display the error without a scary Python
- 204 traceback.
- 205 """
- 206 raise CogGeneratedError(msg)
+ 200 def outl(self, sOut="", **kw):
+ 201 """The cog.outl function."""
+ 202 self.out(sOut, **kw)
+ 203 self.out("\n")
+ 204
+ 205 def error(self, msg="Error raised by cog generator."):
+ 206 """The cog.error function.
207
- 208
- 209class CogOptions:
- 210 """ Options for a run of cog.
- 211 """
- 212 def __init__(self):
- 213 # Defaults for argument values.
- 214 self.args = []
- 215 self.includePath = []
- 216 self.defines = {}
- 217 self.bShowVersion = False
- 218 self.sMakeWritableCmd = None
- 219 self.bReplace = False
- 220 self.bNoGenerate = False
- 221 self.sOutputName = None
- 222 self.bWarnEmpty = False
- 223 self.bHashOutput = False
- 224 self.bDeleteCode = False
- 225 self.bEofCanBeEnd = False
- 226 self.sSuffix = None
- 227 self.bNewlines = False
- 228 self.sBeginSpec = '[[[cog'
- 229 self.sEndSpec = ']]]'
- 230 self.sEndOutput = '[[[end]]]'
- 231 self.sEncoding = "utf-8"
- 232 self.verbosity = 2
- 233 self.sPrologue = ''
- 234 self.bPrintOutput = False
- 235 self.bCheck = False
- 236
- 237 def __eq__(self, other):
- 238 """ Comparison operator for tests to use.
- 239 """
- 240 return self.__dict__ == other.__dict__
- 241
- 242 def clone(self):
- 243 """ Make a clone of these options, for further refinement.
- 244 """
- 245 return copy.deepcopy(self)
- 246
- 247 def addToIncludePath(self, dirs):
- 248 """ Add directories to the include path.
- 249 """
- 250 dirs = dirs.split(os.pathsep)
- 251 self.includePath.extend(dirs)
- 252
- 253 def parseArgs(self, argv):
- 254 # Parse the command line arguments.
- 255 try:
- 256 opts, self.args = getopt.getopt(
- 257 argv,
- 258 'cdD:eI:n:o:rs:p:PUvw:xz',
- 259 [
- 260 'check',
- 261 'markers=',
- 262 'verbosity=',
- 263 ]
- 264 )
- 265 except getopt.error as msg:
- 266 raise CogUsageError(msg)
- 267
- 268 # Handle the command line arguments.
- 269 for o, a in opts:
- 270 if o == '-c':
- 271 self.bHashOutput = True
- 272 elif o == '-d':
- 273 self.bDeleteCode = True
- 274 elif o == '-D':
- 275 if a.count('=') < 1:
- 276 raise CogUsageError("-D takes a name=value argument")
- 277 name, value = a.split('=', 1)
- 278 self.defines[name] = value
- 279 elif o == '-e':
- 280 self.bWarnEmpty = True
- 281 elif o == '-I':
- 282 self.addToIncludePath(os.path.abspath(a))
- 283 elif o == '-n':
- 284 self.sEncoding = a
- 285 elif o == '-o':
- 286 self.sOutputName = a
- 287 elif o == '-r':
- 288 self.bReplace = True
- 289 elif o == '-s':
- 290 self.sSuffix = a
- 291 elif o == '-p':
- 292 self.sPrologue = a
- 293 elif o == '-P':
- 294 self.bPrintOutput = True
- 295 elif o == '-U':
- 296 self.bNewlines = True
- 297 elif o == '-v':
- 298 self.bShowVersion = True
- 299 elif o == '-w':
- 300 self.sMakeWritableCmd = a
- 301 elif o == '-x':
- 302 self.bNoGenerate = True
- 303 elif o == '-z':
- 304 self.bEofCanBeEnd = True
- 305 elif o == '--check':
- 306 self.bCheck = True
- 307 elif o == '--markers':
- 308 self._parse_markers(a)
- 309 elif o == '--verbosity':
- 310 self.verbosity = int(a)
- 311 else:
- 312 # Since getopt.getopt is given a list of possible flags,
- 313 # this is an internal error.
- 314 raise CogInternalError(f"Don't understand argument {o}")
- 315
- 316 def _parse_markers(self, val):
- 317 try:
- 318 self.sBeginSpec, self.sEndSpec, self.sEndOutput = val.split(" ")
- 319 except ValueError:
- 320 raise CogUsageError(
- 321 f"--markers requires 3 values separated by spaces, could not parse {val!r}"
- 322 )
- 323
- 324 def validate(self):
- 325 """ Does nothing if everything is OK, raises CogError's if it's not.
- 326 """
- 327 if self.bReplace and self.bDeleteCode:
- 328 raise CogUsageError("Can't use -d with -r (or you would delete all your source!)")
- 329
- 330 if self.bReplace and self.sOutputName:
- 331 raise CogUsageError("Can't use -o with -r (they are opposites)")
- 332
- 333
- 334class Cog(Redirectable):
- 335 """ The Cog engine.
- 336 """
- 337 def __init__(self):
- 338 super().__init__()
- 339 self.options = CogOptions()
- 340 self._fixEndOutputPatterns()
- 341 self.cogmodulename = "cog"
- 342 self.createCogModule()
- 343 self.bCheckFailed = False
- 344
- 345 def _fixEndOutputPatterns(self):
- 346 end_output = re.escape(self.options.sEndOutput)
- 347 self.reEndOutput = re.compile(end_output + r"(?P<hashsect> *\(checksum: (?P<hash>[a-f0-9]+)\))")
- 348 self.sEndFormat = self.options.sEndOutput + " (checksum: %s)"
+ 208 Instead of raising standard python errors, cog generators can use
+ 209 this function. It will display the error without a scary Python
+ 210 traceback.
+ 211
+ 212 """
+ 213 raise CogGeneratedError(msg)
+ 214
+ 215
+ 216class CogOptions:
+ 217 """Options for a run of cog."""
+ 218
+ 219 def __init__(self):
+ 220 # Defaults for argument values.
+ 221 self.args = []
+ 222 self.include_path = []
+ 223 self.defines = {}
+ 224 self.show_version = False
+ 225 self.make_writable_cmd = None
+ 226 self.replace = False
+ 227 self.no_generate = False
+ 228 self.output_name = None
+ 229 self.warn_empty = False
+ 230 self.hash_output = False
+ 231 self.delete_code = False
+ 232 self.eof_can_be_end = False
+ 233 self.suffix = None
+ 234 self.newlines = False
+ 235 self.begin_spec = "[[[cog"
+ 236 self.end_spec = "]]]"
+ 237 self.end_output = "[[[end]]]"
+ 238 self.encoding = "utf-8"
+ 239 self.verbosity = 2
+ 240 self.prologue = ""
+ 241 self.print_output = False
+ 242 self.check = False
+ 243
+ 244 def __eq__(self, other):
+ 245 """Comparison operator for tests to use."""
+ 246 return self.__dict__ == other.__dict__
+ 247
+ 248 def clone(self):
+ 249 """Make a clone of these options, for further refinement."""
+ 250 return copy.deepcopy(self)
+ 251
+ 252 def add_to_include_path(self, dirs):
+ 253 """Add directories to the include path."""
+ 254 dirs = dirs.split(os.pathsep)
+ 255 self.include_path.extend(dirs)
+ 256
+ 257 def parse_args(self, argv):
+ 258 # Parse the command line arguments.
+ 259 try:
+ 260 opts, self.args = getopt.getopt(
+ 261 argv,
+ 262 "cdD:eI:n:o:rs:p:PUvw:xz",
+ 263 [
+ 264 "check",
+ 265 "markers=",
+ 266 "verbosity=",
+ 267 ],
+ 268 )
+ 269 except getopt.error as msg:
+ 270 raise CogUsageError(msg)
+ 271
+ 272 # Handle the command line arguments.
+ 273 for o, a in opts:
+ 274 if o == "-c":
+ 275 self.hash_output = True
+ 276 elif o == "-d":
+ 277 self.delete_code = True
+ 278 elif o == "-D":
+ 279 if a.count("=") < 1:
+ 280 raise CogUsageError("-D takes a name=value argument")
+ 281 name, value = a.split("=", 1)
+ 282 self.defines[name] = value
+ 283 elif o == "-e":
+ 284 self.warn_empty = True
+ 285 elif o == "-I":
+ 286 self.add_to_include_path(os.path.abspath(a))
+ 287 elif o == "-n":
+ 288 self.encoding = a
+ 289 elif o == "-o":
+ 290 self.output_name = a
+ 291 elif o == "-r":
+ 292 self.replace = True
+ 293 elif o == "-s":
+ 294 self.suffix = a
+ 295 elif o == "-p":
+ 296 self.prologue = a
+ 297 elif o == "-P":
+ 298 self.print_output = True
+ 299 elif o == "-U":
+ 300 self.newlines = True
+ 301 elif o == "-v":
+ 302 self.show_version = True
+ 303 elif o == "-w":
+ 304 self.make_writable_cmd = a
+ 305 elif o == "-x":
+ 306 self.no_generate = True
+ 307 elif o == "-z":
+ 308 self.eof_can_be_end = True
+ 309 elif o == "--check":
+ 310 self.check = True
+ 311 elif o == "--markers":
+ 312 self._parse_markers(a)
+ 313 elif o == "--verbosity":
+ 314 self.verbosity = int(a)
+ 315 else:
+ 316 # Since getopt.getopt is given a list of possible flags,
+ 317 # this is an internal error.
+ 318 raise CogInternalError(f"Don't understand argument {o}")
+ 319
+ 320 def _parse_markers(self, val):
+ 321 try:
+ 322 self.begin_spec, self.end_spec, self.end_output = val.split(" ")
+ 323 except ValueError:
+ 324 raise CogUsageError(
+ 325 f"--markers requires 3 values separated by spaces, could not parse {val!r}"
+ 326 )
+ 327
+ 328 def validate(self):
+ 329 """Does nothing if everything is OK, raises CogError's if it's not."""
+ 330 if self.replace and self.delete_code:
+ 331 raise CogUsageError(
+ 332 "Can't use -d with -r (or you would delete all your source!)"
+ 333 )
+ 334
+ 335 if self.replace and self.output_name:
+ 336 raise CogUsageError("Can't use -o with -r (they are opposites)")
+ 337
+ 338
+ 339class Cog(Redirectable):
+ 340 """The Cog engine."""
+ 341
+ 342 def __init__(self):
+ 343 super().__init__()
+ 344 self.options = CogOptions()
+ 345 self._fix_end_output_patterns()
+ 346 self.cogmodulename = "cog"
+ 347 self.create_cog_module()
+ 348 self.check_failed = False
349
- 350 def showWarning(self, msg):
- 351 self.prout(f"Warning: {msg}")
- 352
- 353 def isBeginSpecLine(self, s):
- 354 return self.options.sBeginSpec in s
- 355
- 356 def isEndSpecLine(self, s):
- 357 return self.options.sEndSpec in s and not self.isEndOutputLine(s)
- 358
- 359 def isEndOutputLine(self, s):
- 360 return self.options.sEndOutput in s
- 361
- 362 def createCogModule(self):
- 363 """ Make a cog "module" object so that imported Python modules
- 364 can say "import cog" and get our state.
- 365 """
- 366 self.cogmodule = types.SimpleNamespace()
- 367 self.cogmodule.path = []
+ 350 def _fix_end_output_patterns(self):
+ 351 end_output = re.escape(self.options.end_output)
+ 352 self.re_end_output = re.compile(
+ 353 end_output + r"(?P<hashsect> *\(checksum: (?P<hash>[a-f0-9]+)\))"
+ 354 )
+ 355 self.end_format = self.options.end_output + " (checksum: %s)"
+ 356
+ 357 def show_warning(self, msg):
+ 358 self.prout(f"Warning: {msg}")
+ 359
+ 360 def is_begin_spec_line(self, s):
+ 361 return self.options.begin_spec in s
+ 362
+ 363 def is_end_spec_line(self, s):
+ 364 return self.options.end_spec in s and not self.is_end_output_line(s)
+ 365
+ 366 def is_end_output_line(self, s):
+ 367 return self.options.end_output in s
368
- 369 def openOutputFile(self, fname):
- 370 """ Open an output file, taking all the details into account.
- 371 """
- 372 opts = {}
- 373 mode = "w"
- 374 opts['encoding'] = self.options.sEncoding
- 375 if self.options.bNewlines:
- 376 opts["newline"] = "\n"
- 377 fdir = os.path.dirname(fname)
- 378 if os.path.dirname(fdir) and not os.path.exists(fdir):
- 379 os.makedirs(fdir)
- 380 return open(fname, mode, **opts)
- 381
- 382 def openInputFile(self, fname):
- 383 """ Open an input file.
- 384 """
- 385 if fname == "-":
- 386 return sys.stdin
- 387 else:
- 388 return open(fname, encoding=self.options.sEncoding)
+ 369 def create_cog_module(self):
+ 370 """Make a cog "module" object.
+ 371
+ 372 Imported Python modules can use "import cog" to get our state.
+ 373
+ 374 """
+ 375 self.cogmodule = types.SimpleNamespace()
+ 376 self.cogmodule.path = []
+ 377
+ 378 def open_output_file(self, fname):
+ 379 """Open an output file, taking all the details into account."""
+ 380 opts = {}
+ 381 mode = "w"
+ 382 opts["encoding"] = self.options.encoding
+ 383 if self.options.newlines:
+ 384 opts["newline"] = "\n"
+ 385 fdir = os.path.dirname(fname)
+ 386 if os.path.dirname(fdir) and not os.path.exists(fdir):
+ 387 os.makedirs(fdir)
+ 388 return open(fname, mode, **opts)
389
- 390 def processFile(self, fIn, fOut, fname=None, globals=None):
- 391 """ Process an input file object to an output file object.
- 392 fIn and fOut can be file objects, or file names.
- 393 """
- 394
- 395 sFileIn = fname or ''
- 396 sFileOut = fname or ''
- 397 fInToClose = fOutToClose = None
- 398 # Convert filenames to files.
- 399 if isinstance(fIn, (bytes, str)): 399 ↛ 401line 399 didn't jump to line 401, because the condition on line 399 was never true
- 400 # Open the input file.
- 401 sFileIn = fIn
- 402 fIn = fInToClose = self.openInputFile(fIn)
- 403 if isinstance(fOut, (bytes, str)): 403 ↛ 405line 403 didn't jump to line 405, because the condition on line 403 was never true
- 404 # Open the output file.
- 405 sFileOut = fOut
- 406 fOut = fOutToClose = self.openOutputFile(fOut)
- 407
- 408 try:
- 409 fIn = NumberedFileReader(fIn)
- 410
- 411 bSawCog = False
- 412
- 413 self.cogmodule.inFile = sFileIn
- 414 self.cogmodule.outFile = sFileOut
- 415 self.cogmodulename = 'cog_' + md5(sFileOut.encode()).hexdigest()
- 416 sys.modules[self.cogmodulename] = self.cogmodule
- 417 # if "import cog" explicitly done in code by user, note threading will cause clashes.
- 418 sys.modules['cog'] = self.cogmodule
- 419
- 420 # The globals dict we'll use for this file.
- 421 if globals is None: 421 ↛ 425line 421 didn't jump to line 425, because the condition on line 421 was never false
- 422 globals = {}
- 423
- 424 # If there are any global defines, put them in the globals.
- 425 globals.update(self.options.defines)
- 426
- 427 # loop over generator chunks
- 428 l = fIn.readline()
- 429 while l:
- 430 # Find the next spec begin
- 431 while l and not self.isBeginSpecLine(l):
- 432 if self.isEndSpecLine(l): 432 ↛ 433line 432 didn't jump to line 433, because the condition on line 432 was never true
- 433 raise CogError(
- 434 f"Unexpected {self.options.sEndSpec!r}",
- 435 file=sFileIn,
- 436 line=fIn.linenumber(),
- 437 )
- 438 if self.isEndOutputLine(l): 438 ↛ 439line 438 didn't jump to line 439, because the condition on line 438 was never true
- 439 raise CogError(
- 440 f"Unexpected {self.options.sEndOutput!r}",
- 441 file=sFileIn,
- 442 line=fIn.linenumber(),
- 443 )
- 444 fOut.write(l)
- 445 l = fIn.readline()
- 446 if not l:
- 447 break
- 448 if not self.options.bDeleteCode: 448 ↛ 452line 448 didn't jump to line 452, because the condition on line 448 was never false
- 449 fOut.write(l)
- 450
- 451 # l is the begin spec
- 452 gen = CogGenerator(options=self.options)
- 453 gen.setOutput(stdout=self.stdout)
- 454 gen.parseMarker(l)
- 455 firstLineNum = fIn.linenumber()
- 456 self.cogmodule.firstLineNum = firstLineNum
- 457
- 458 # If the spec begin is also a spec end, then process the single
- 459 # line of code inside.
- 460 if self.isEndSpecLine(l):
- 461 beg = l.find(self.options.sBeginSpec)
- 462 end = l.find(self.options.sEndSpec)
- 463 if beg > end:
- 464 raise CogError("Cog code markers inverted",
- 465 file=sFileIn, line=firstLineNum)
- 466 else:
- 467 sCode = l[beg+len(self.options.sBeginSpec):end].strip()
- 468 gen.parseLine(sCode)
- 469 else:
- 470 # Deal with an ordinary code block.
- 471 l = fIn.readline()
- 472
- 473 # Get all the lines in the spec
- 474 while l and not self.isEndSpecLine(l):
- 475 if self.isBeginSpecLine(l): 475 ↛ 476line 475 didn't jump to line 476, because the condition on line 475 was never true
- 476 raise CogError(
- 477 f"Unexpected {self.options.sBeginSpec!r}",
- 478 file=sFileIn,
- 479 line=fIn.linenumber(),
- 480 )
- 481 if self.isEndOutputLine(l): 481 ↛ 482line 481 didn't jump to line 482, because the condition on line 481 was never true
- 482 raise CogError(
- 483 f"Unexpected {self.options.sEndOutput!r}",
- 484 file=sFileIn,
- 485 line=fIn.linenumber(),
- 486 )
- 487 if not self.options.bDeleteCode: 487 ↛ 489line 487 didn't jump to line 489, because the condition on line 487 was never false
- 488 fOut.write(l)
- 489 gen.parseLine(l)
- 490 l = fIn.readline()
- 491 if not l: 491 ↛ 492line 491 didn't jump to line 492, because the condition on line 491 was never true
- 492 raise CogError(
- 493 "Cog block begun but never ended.",
- 494 file=sFileIn, line=firstLineNum)
- 495
- 496 if not self.options.bDeleteCode: 496 ↛ 498line 496 didn't jump to line 498, because the condition on line 496 was never false
- 497 fOut.write(l)
- 498 gen.parseMarker(l)
- 499
- 500 l = fIn.readline()
- 501
- 502 # Eat all the lines in the output section. While reading past
- 503 # them, compute the md5 hash of the old output.
- 504 previous = []
- 505 hasher = md5()
- 506 while l and not self.isEndOutputLine(l):
- 507 if self.isBeginSpecLine(l): 507 ↛ 508line 507 didn't jump to line 508, because the condition on line 507 was never true
- 508 raise CogError(
- 509 f"Unexpected {self.options.sBeginSpec!r}",
- 510 file=sFileIn,
- 511 line=fIn.linenumber(),
- 512 )
- 513 if self.isEndSpecLine(l): 513 ↛ 514line 513 didn't jump to line 514, because the condition on line 513 was never true
- 514 raise CogError(
- 515 f"Unexpected {self.options.sEndSpec!r}",
- 516 file=sFileIn,
- 517 line=fIn.linenumber(),
- 518 )
- 519 previous.append(l)
- 520 hasher.update(l.encode("utf-8"))
- 521 l = fIn.readline()
- 522 curHash = hasher.hexdigest()
- 523
- 524 if not l and not self.options.bEofCanBeEnd: 524 ↛ 526line 524 didn't jump to line 526, because the condition on line 524 was never true
- 525 # We reached end of file before we found the end output line.
- 526 raise CogError(
- 527 f"Missing {self.options.sEndOutput!r} before end of file.",
- 528 file=sFileIn,
- 529 line=fIn.linenumber(),
- 530 )
- 531
- 532 # Make the previous output available to the current code
- 533 self.cogmodule.previous = "".join(previous)
- 534
- 535 # Write the output of the spec to be the new output if we're
- 536 # supposed to generate code.
- 537 hasher = md5()
- 538 if not self.options.bNoGenerate: 538 ↛ 544line 538 didn't jump to line 544, because the condition on line 538 was never false
- 539 sFile = f"<cog {sFileIn}:{firstLineNum}>"
- 540 sGen = gen.evaluate(cog=self, globals=globals, fname=sFile)
- 541 sGen = self.suffixLines(sGen)
- 542 hasher.update(sGen.encode("utf-8"))
- 543 fOut.write(sGen)
- 544 newHash = hasher.hexdigest()
- 545
- 546 bSawCog = True
+ 390 def open_input_file(self, fname):
+ 391 """Open an input file."""
+ 392 if fname == "-":
+ 393 return sys.stdin
+ 394 else:
+ 395 return open(fname, encoding=self.options.encoding)
+ 396
+ 397 def process_file(self, file_in, file_out, fname=None, globals=None):
+ 398 """Process an input file object to an output file object.
+ 399
+ 400 `fileIn` and `fileOut` can be file objects, or file names.
+ 401
+ 402 """
+ 403 file_name_in = fname or ""
+ 404 file_name_out = fname or ""
+ 405 file_in_to_close = file_out_to_close = None
+ 406 # Convert filenames to files.
+ 407 if isinstance(file_in, (bytes, str)): 407 ↛ 409line 407 didn't jump to line 409, because the condition on line 407 was never true
+ 408 # Open the input file.
+ 409 file_name_in = file_in
+ 410 file_in = file_in_to_close = self.open_input_file(file_in)
+ 411 if isinstance(file_out, (bytes, str)): 411 ↛ 413line 411 didn't jump to line 413, because the condition on line 411 was never true
+ 412 # Open the output file.
+ 413 file_name_out = file_out
+ 414 file_out = file_out_to_close = self.open_output_file(file_out)
+ 415
+ 416 try:
+ 417 file_in = NumberedFileReader(file_in)
+ 418
+ 419 saw_cog = False
+ 420
+ 421 self.cogmodule.inFile = file_name_in
+ 422 self.cogmodule.outFile = file_name_out
+ 423 self.cogmodulename = "cog_" + md5(file_name_out.encode()).hexdigest()
+ 424 sys.modules[self.cogmodulename] = self.cogmodule
+ 425 # if "import cog" explicitly done in code by user, note threading will cause clashes.
+ 426 sys.modules["cog"] = self.cogmodule
+ 427
+ 428 # The globals dict we'll use for this file.
+ 429 if globals is None: 429 ↛ 433line 429 didn't jump to line 433, because the condition on line 429 was always true
+ 430 globals = {}
+ 431
+ 432 # If there are any global defines, put them in the globals.
+ 433 globals.update(self.options.defines)
+ 434
+ 435 # loop over generator chunks
+ 436 line = file_in.readline()
+ 437 while line:
+ 438 # Find the next spec begin
+ 439 while line and not self.is_begin_spec_line(line):
+ 440 if self.is_end_spec_line(line): 440 ↛ 441line 440 didn't jump to line 441, because the condition on line 440 was never true
+ 441 raise CogError(
+ 442 f"Unexpected {self.options.end_spec!r}",
+ 443 file=file_name_in,
+ 444 line=file_in.linenumber(),
+ 445 )
+ 446 if self.is_end_output_line(line): 446 ↛ 447line 446 didn't jump to line 447, because the condition on line 446 was never true
+ 447 raise CogError(
+ 448 f"Unexpected {self.options.end_output!r}",
+ 449 file=file_name_in,
+ 450 line=file_in.linenumber(),
+ 451 )
+ 452 file_out.write(line)
+ 453 line = file_in.readline()
+ 454 if not line:
+ 455 break
+ 456 if not self.options.delete_code: 456 ↛ 460line 456 didn't jump to line 460, because the condition on line 456 was always true
+ 457 file_out.write(line)
+ 458
+ 459 # l is the begin spec
+ 460 gen = CogGenerator(options=self.options)
+ 461 gen.set_output(stdout=self.stdout)
+ 462 gen.parse_marker(line)
+ 463 first_line_num = file_in.linenumber()
+ 464 self.cogmodule.firstLineNum = first_line_num
+ 465
+ 466 # If the spec begin is also a spec end, then process the single
+ 467 # line of code inside.
+ 468 if self.is_end_spec_line(line):
+ 469 beg = line.find(self.options.begin_spec)
+ 470 end = line.find(self.options.end_spec)
+ 471 if beg > end:
+ 472 raise CogError(
+ 473 "Cog code markers inverted",
+ 474 file=file_name_in,
+ 475 line=first_line_num,
+ 476 )
+ 477 else:
+ 478 code = line[beg + len(self.options.begin_spec) : end].strip()
+ 479 gen.parse_line(code)
+ 480 else:
+ 481 # Deal with an ordinary code block.
+ 482 line = file_in.readline()
+ 483
+ 484 # Get all the lines in the spec
+ 485 while line and not self.is_end_spec_line(line):
+ 486 if self.is_begin_spec_line(line): 486 ↛ 487line 486 didn't jump to line 487, because the condition on line 486 was never true
+ 487 raise CogError(
+ 488 f"Unexpected {self.options.begin_spec!r}",
+ 489 file=file_name_in,
+ 490 line=file_in.linenumber(),
+ 491 )
+ 492 if self.is_end_output_line(line): 492 ↛ 493line 492 didn't jump to line 493, because the condition on line 492 was never true
+ 493 raise CogError(
+ 494 f"Unexpected {self.options.end_output!r}",
+ 495 file=file_name_in,
+ 496 line=file_in.linenumber(),
+ 497 )
+ 498 if not self.options.delete_code: 498 ↛ 500line 498 didn't jump to line 500, because the condition on line 498 was always true
+ 499 file_out.write(line)
+ 500 gen.parse_line(line)
+ 501 line = file_in.readline()
+ 502 if not line: 502 ↛ 503line 502 didn't jump to line 503, because the condition on line 502 was never true
+ 503 raise CogError(
+ 504 "Cog block begun but never ended.",
+ 505 file=file_name_in,
+ 506 line=first_line_num,
+ 507 )
+ 508
+ 509 if not self.options.delete_code: 509 ↛ 511line 509 didn't jump to line 511, because the condition on line 509 was always true
+ 510 file_out.write(line)
+ 511 gen.parse_marker(line)
+ 512
+ 513 line = file_in.readline()
+ 514
+ 515 # Eat all the lines in the output section. While reading past
+ 516 # them, compute the md5 hash of the old output.
+ 517 previous = []
+ 518 hasher = md5()
+ 519 while line and not self.is_end_output_line(line):
+ 520 if self.is_begin_spec_line(line): 520 ↛ 521line 520 didn't jump to line 521, because the condition on line 520 was never true
+ 521 raise CogError(
+ 522 f"Unexpected {self.options.begin_spec!r}",
+ 523 file=file_name_in,
+ 524 line=file_in.linenumber(),
+ 525 )
+ 526 if self.is_end_spec_line(line): 526 ↛ 527line 526 didn't jump to line 527, because the condition on line 526 was never true
+ 527 raise CogError(
+ 528 f"Unexpected {self.options.end_spec!r}",
+ 529 file=file_name_in,
+ 530 line=file_in.linenumber(),
+ 531 )
+ 532 previous.append(line)
+ 533 hasher.update(line.encode("utf-8"))
+ 534 line = file_in.readline()
+ 535 cur_hash = hasher.hexdigest()
+ 536
+ 537 if not line and not self.options.eof_can_be_end: 537 ↛ 539line 537 didn't jump to line 539, because the condition on line 537 was never true
+ 538 # We reached end of file before we found the end output line.
+ 539 raise CogError(
+ 540 f"Missing {self.options.end_output!r} before end of file.",
+ 541 file=file_name_in,
+ 542 line=file_in.linenumber(),
+ 543 )
+ 544
+ 545 # Make the previous output available to the current code
+ 546 self.cogmodule.previous = "".join(previous)
547
- 548 # Write the ending output line
- 549 hashMatch = self.reEndOutput.search(l)
- 550 if self.options.bHashOutput: 550 ↛ 551line 550 didn't jump to line 551, because the condition on line 550 was never true
- 551 if hashMatch:
- 552 oldHash = hashMatch['hash']
- 553 if oldHash != curHash:
- 554 raise CogError("Output has been edited! Delete old checksum to unprotect.",
- 555 file=sFileIn, line=fIn.linenumber())
- 556 # Create a new end line with the correct hash.
- 557 endpieces = l.split(hashMatch.group(0), 1)
- 558 else:
- 559 # There was no old hash, but we want a new hash.
- 560 endpieces = l.split(self.options.sEndOutput, 1)
- 561 l = (self.sEndFormat % newHash).join(endpieces)
- 562 else:
- 563 # We don't want hashes output, so if there was one, get rid of
- 564 # it.
- 565 if hashMatch: 565 ↛ 566line 565 didn't jump to line 566, because the condition on line 565 was never true
- 566 l = l.replace(hashMatch['hashsect'], '', 1)
- 567
- 568 if not self.options.bDeleteCode: 568 ↛ 570line 568 didn't jump to line 570, because the condition on line 568 was never false
- 569 fOut.write(l)
- 570 l = fIn.readline()
- 571
- 572 if not bSawCog and self.options.bWarnEmpty: 572 ↛ 573line 572 didn't jump to line 573, because the condition on line 572 was never true
- 573 self.showWarning(f"no cog code found in {sFileIn}")
- 574 finally:
- 575 if fInToClose: 575 ↛ 576line 575 didn't jump to line 576, because the condition on line 575 was never true
- 576 fInToClose.close()
- 577 if fOutToClose: 577 ↛ 578line 577 didn't jump to line 578, because the condition on line 577 was never true
- 578 fOutToClose.close()
- 579
- 580
- 581 # A regex for non-empty lines, used by suffixLines.
- 582 reNonEmptyLines = re.compile(r"^\s*\S+.*$", re.MULTILINE)
+ 548 # Write the output of the spec to be the new output if we're
+ 549 # supposed to generate code.
+ 550 hasher = md5()
+ 551 if not self.options.no_generate: 551 ↛ 557line 551 didn't jump to line 557, because the condition on line 551 was always true
+ 552 fname = f"<cog {file_name_in}:{first_line_num}>"
+ 553 gen = gen.evaluate(cog=self, globals=globals, fname=fname)
+ 554 gen = self.suffix_lines(gen)
+ 555 hasher.update(gen.encode("utf-8"))
+ 556 file_out.write(gen)
+ 557 new_hash = hasher.hexdigest()
+ 558
+ 559 saw_cog = True
+ 560
+ 561 # Write the ending output line
+ 562 hash_match = self.re_end_output.search(line)
+ 563 if self.options.hash_output: 563 ↛ 564line 563 didn't jump to line 564, because the condition on line 563 was never true
+ 564 if hash_match:
+ 565 old_hash = hash_match["hash"]
+ 566 if old_hash != cur_hash:
+ 567 raise CogError(
+ 568 "Output has been edited! Delete old checksum to unprotect.",
+ 569 file=file_name_in,
+ 570 line=file_in.linenumber(),
+ 571 )
+ 572 # Create a new end line with the correct hash.
+ 573 endpieces = line.split(hash_match.group(0), 1)
+ 574 else:
+ 575 # There was no old hash, but we want a new hash.
+ 576 endpieces = line.split(self.options.end_output, 1)
+ 577 line = (self.end_format % new_hash).join(endpieces)
+ 578 else:
+ 579 # We don't want hashes output, so if there was one, get rid of
+ 580 # it.
+ 581 if hash_match: 581 ↛ 582line 581 didn't jump to line 582, because the condition on line 581 was never true
+ 582 line = line.replace(hash_match["hashsect"], "", 1)
583
- 584 def suffixLines(self, text):
- 585 """ Add suffixes to the lines in text, if our options desire it.
- 586 text is many lines, as a single string.
- 587 """
- 588 if self.options.sSuffix: 588 ↛ 590line 588 didn't jump to line 590, because the condition on line 588 was never true
- 589 # Find all non-blank lines, and add the suffix to the end.
- 590 repl = r"\g<0>" + self.options.sSuffix.replace('\\', '\\\\')
- 591 text = self.reNonEmptyLines.sub(repl, text)
- 592 return text
- 593
- 594 def processString(self, sInput, fname=None):
- 595 """ Process sInput as the text to cog.
- 596 Return the cogged output as a string.
- 597 """
- 598 fOld = io.StringIO(sInput)
- 599 fNew = io.StringIO()
- 600 self.processFile(fOld, fNew, fname=fname)
- 601 return fNew.getvalue()
- 602
- 603 def replaceFile(self, sOldPath, sNewText):
- 604 """ Replace file sOldPath with the contents sNewText
- 605 """
- 606 if not os.access(sOldPath, os.W_OK):
- 607 # Need to ensure we can write.
- 608 if self.options.sMakeWritableCmd:
- 609 # Use an external command to make the file writable.
- 610 cmd = self.options.sMakeWritableCmd.replace('%s', sOldPath)
- 611 with os.popen(cmd) as cmdout:
- 612 self.stdout.write(cmdout.read())
- 613 if not os.access(sOldPath, os.W_OK):
- 614 raise CogError(f"Couldn't make {sOldPath} writable")
- 615 else:
- 616 # Can't write!
- 617 raise CogError(f"Can't overwrite {sOldPath}")
- 618 f = self.openOutputFile(sOldPath)
- 619 f.write(sNewText)
- 620 f.close()
+ 584 if not self.options.delete_code: 584 ↛ 586line 584 didn't jump to line 586, because the condition on line 584 was always true
+ 585 file_out.write(line)
+ 586 line = file_in.readline()
+ 587
+ 588 if not saw_cog and self.options.warn_empty: 588 ↛ 589line 588 didn't jump to line 589, because the condition on line 588 was never true
+ 589 self.show_warning(f"no cog code found in {file_name_in}")
+ 590 finally:
+ 591 if file_in_to_close: 591 ↛ 592line 591 didn't jump to line 592, because the condition on line 591 was never true
+ 592 file_in_to_close.close()
+ 593 if file_out_to_close: 593 ↛ 594line 593 didn't jump to line 594, because the condition on line 593 was never true
+ 594 file_out_to_close.close()
+ 595
+ 596 # A regex for non-empty lines, used by suffixLines.
+ 597 re_non_empty_lines = re.compile(r"^\s*\S+.*$", re.MULTILINE)
+ 598
+ 599 def suffix_lines(self, text):
+ 600 """Add suffixes to the lines in text, if our options desire it.
+ 601
+ 602 `text` is many lines, as a single string.
+ 603
+ 604 """
+ 605 if self.options.suffix: 605 ↛ 607line 605 didn't jump to line 607, because the condition on line 605 was never true
+ 606 # Find all non-blank lines, and add the suffix to the end.
+ 607 repl = r"\g<0>" + self.options.suffix.replace("\\", "\\\\")
+ 608 text = self.re_non_empty_lines.sub(repl, text)
+ 609 return text
+ 610
+ 611 def process_string(self, input, fname=None):
+ 612 """Process `input` as the text to cog.
+ 613
+ 614 Return the cogged output as a string.
+ 615
+ 616 """
+ 617 file_old = io.StringIO(input)
+ 618 file_new = io.StringIO()
+ 619 self.process_file(file_old, file_new, fname=fname)
+ 620 return file_new.getvalue()
621
- 622 def saveIncludePath(self):
- 623 self.savedInclude = self.options.includePath[:]
- 624 self.savedSysPath = sys.path[:]
- 625
- 626 def restoreIncludePath(self):
- 627 self.options.includePath = self.savedInclude
- 628 self.cogmodule.path = self.options.includePath
- 629 sys.path = self.savedSysPath
- 630
- 631 def addToIncludePath(self, includePath):
- 632 self.cogmodule.path.extend(includePath)
- 633 sys.path.extend(includePath)
- 634
- 635 def processOneFile(self, sFile):
- 636 """ Process one filename through cog.
- 637 """
- 638
- 639 self.saveIncludePath()
- 640 bNeedNewline = False
- 641
- 642 try:
- 643 self.addToIncludePath(self.options.includePath)
- 644 # Since we know where the input file came from,
- 645 # push its directory onto the include path.
- 646 self.addToIncludePath([os.path.dirname(sFile)])
- 647
- 648 # How we process the file depends on where the output is going.
- 649 if self.options.sOutputName:
- 650 self.processFile(sFile, self.options.sOutputName, sFile)
- 651 elif self.options.bReplace or self.options.bCheck:
- 652 # We want to replace the cog file with the output,
- 653 # but only if they differ.
- 654 verb = "Cogging" if self.options.bReplace else "Checking"
- 655 if self.options.verbosity >= 2:
- 656 self.prout(f"{verb} {sFile}", end="")
- 657 bNeedNewline = True
+ 622 def replace_file(self, old_path, new_text):
+ 623 """Replace file oldPath with the contents newText"""
+ 624 if not os.access(old_path, os.W_OK):
+ 625 # Need to ensure we can write.
+ 626 if self.options.make_writable_cmd:
+ 627 # Use an external command to make the file writable.
+ 628 cmd = self.options.make_writable_cmd.replace("%s", old_path)
+ 629 with os.popen(cmd) as cmdout:
+ 630 self.stdout.write(cmdout.read())
+ 631 if not os.access(old_path, os.W_OK):
+ 632 raise CogError(f"Couldn't make {old_path} writable")
+ 633 else:
+ 634 # Can't write!
+ 635 raise CogError(f"Can't overwrite {old_path}")
+ 636 f = self.open_output_file(old_path)
+ 637 f.write(new_text)
+ 638 f.close()
+ 639
+ 640 def save_include_path(self):
+ 641 self.saved_include = self.options.include_path[:]
+ 642 self.saved_sys_path = sys.path[:]
+ 643
+ 644 def restore_include_path(self):
+ 645 self.options.include_path = self.saved_include
+ 646 self.cogmodule.path = self.options.include_path
+ 647 sys.path = self.saved_sys_path
+ 648
+ 649 def add_to_include_path(self, include_path):
+ 650 self.cogmodule.path.extend(include_path)
+ 651 sys.path.extend(include_path)
+ 652
+ 653 def process_one_file(self, fname):
+ 654 """Process one filename through cog."""
+ 655
+ 656 self.save_include_path()
+ 657 need_newline = False
658
- 659 try:
- 660 fOldFile = self.openInputFile(sFile)
- 661 sOldText = fOldFile.read()
- 662 fOldFile.close()
- 663 sNewText = self.processString(sOldText, fname=sFile)
- 664 if sOldText != sNewText:
- 665 if self.options.verbosity >= 1:
- 666 if self.options.verbosity < 2:
- 667 self.prout(f"{verb} {sFile}", end="")
- 668 self.prout(" (changed)")
- 669 bNeedNewline = False
- 670 if self.options.bReplace:
- 671 self.replaceFile(sFile, sNewText)
- 672 else:
- 673 assert self.options.bCheck
- 674 self.bCheckFailed = True
- 675 finally:
- 676 # The try-finally block is so we can print a partial line
- 677 # with the name of the file, and print (changed) on the
- 678 # same line, but also make sure to break the line before
- 679 # any traceback.
- 680 if bNeedNewline:
- 681 self.prout("")
- 682 else:
- 683 self.processFile(sFile, self.stdout, sFile)
- 684 finally:
- 685 self.restoreIncludePath()
- 686
- 687 def processWildcards(self, sFile):
- 688 files = glob.glob(sFile)
- 689 if files:
- 690 for sMatchingFile in files:
- 691 self.processOneFile(sMatchingFile)
- 692 else:
- 693 self.processOneFile(sFile)
- 694
- 695 def processFileList(self, sFileList):
- 696 """ Process the files in a file list.
- 697 """
- 698 flist = self.openInputFile(sFileList)
- 699 lines = flist.readlines()
- 700 flist.close()
- 701 for l in lines:
- 702 # Use shlex to parse the line like a shell.
- 703 lex = shlex.shlex(l, posix=True)
- 704 lex.whitespace_split = True
- 705 lex.commenters = '#'
- 706 # No escapes, so that backslash can be part of the path
- 707 lex.escape = ''
- 708 args = list(lex)
- 709 if args:
- 710 self.processArguments(args)
+ 659 try:
+ 660 self.add_to_include_path(self.options.include_path)
+ 661 # Since we know where the input file came from,
+ 662 # push its directory onto the include path.
+ 663 self.add_to_include_path([os.path.dirname(fname)])
+ 664
+ 665 # How we process the file depends on where the output is going.
+ 666 if self.options.output_name:
+ 667 self.process_file(fname, self.options.output_name, fname)
+ 668 elif self.options.replace or self.options.check:
+ 669 # We want to replace the cog file with the output,
+ 670 # but only if they differ.
+ 671 verb = "Cogging" if self.options.replace else "Checking"
+ 672 if self.options.verbosity >= 2:
+ 673 self.prout(f"{verb} {fname}", end="")
+ 674 need_newline = True
+ 675
+ 676 try:
+ 677 file_old_file = self.open_input_file(fname)
+ 678 old_text = file_old_file.read()
+ 679 file_old_file.close()
+ 680 new_text = self.process_string(old_text, fname=fname)
+ 681 if old_text != new_text:
+ 682 if self.options.verbosity >= 1:
+ 683 if self.options.verbosity < 2:
+ 684 self.prout(f"{verb} {fname}", end="")
+ 685 self.prout(" (changed)")
+ 686 need_newline = False
+ 687 if self.options.replace:
+ 688 self.replace_file(fname, new_text)
+ 689 else:
+ 690 assert self.options.check
+ 691 self.check_failed = True
+ 692 finally:
+ 693 # The try-finally block is so we can print a partial line
+ 694 # with the name of the file, and print (changed) on the
+ 695 # same line, but also make sure to break the line before
+ 696 # any traceback.
+ 697 if need_newline:
+ 698 self.prout("")
+ 699 else:
+ 700 self.process_file(fname, self.stdout, fname)
+ 701 finally:
+ 702 self.restore_include_path()
+ 703
+ 704 def process_wildcards(self, fname):
+ 705 files = glob.glob(fname)
+ 706 if files:
+ 707 for matching_file in files:
+ 708 self.process_one_file(matching_file)
+ 709 else:
+ 710 self.process_one_file(fname)
711
- 712 def processArguments(self, args):
- 713 """ Process one command-line.
- 714 """
- 715 saved_options = self.options
- 716 self.options = self.options.clone()
- 717
- 718 self.options.parseArgs(args[1:])
- 719 self.options.validate()
- 720
- 721 if args[0][0] == '@':
- 722 if self.options.sOutputName:
- 723 raise CogUsageError("Can't use -o with @file")
- 724 self.processFileList(args[0][1:])
- 725 elif args[0][0] == '&':
- 726 if self.options.sOutputName:
- 727 raise CogUsageError("Can't use -o with &file")
- 728 file_list = args[0][1:]
- 729 with change_dir(os.path.dirname(file_list)):
- 730 self.processFileList(os.path.basename(file_list))
- 731 else:
- 732 self.processWildcards(args[0])
- 733
- 734 self.options = saved_options
+ 712 def process_file_list(self, file_name_list):
+ 713 """Process the files in a file list."""
+ 714 flist = self.open_input_file(file_name_list)
+ 715 lines = flist.readlines()
+ 716 flist.close()
+ 717 for line in lines:
+ 718 # Use shlex to parse the line like a shell.
+ 719 lex = shlex.shlex(line, posix=True)
+ 720 lex.whitespace_split = True
+ 721 lex.commenters = "#"
+ 722 # No escapes, so that backslash can be part of the path
+ 723 lex.escape = ""
+ 724 args = list(lex)
+ 725 if args:
+ 726 self.process_arguments(args)
+ 727
+ 728 def process_arguments(self, args):
+ 729 """Process one command-line."""
+ 730 saved_options = self.options
+ 731 self.options = self.options.clone()
+ 732
+ 733 self.options.parse_args(args[1:])
+ 734 self.options.validate()
735
- 736 def callableMain(self, argv):
- 737 """ All of command-line cog, but in a callable form.
- 738 This is used by main.
- 739 argv is the equivalent of sys.argv.
- 740 """
- 741 argv = argv[1:]
- 742
- 743 # Provide help if asked for anywhere in the command line.
- 744 if '-?' in argv or '-h' in argv:
- 745 self.prerr(usage, end="")
- 746 return
- 747
- 748 self.options.parseArgs(argv)
- 749 self.options.validate()
- 750 self._fixEndOutputPatterns()
- 751
- 752 if self.options.bShowVersion:
- 753 self.prout(f"Cog version {__version__}")
- 754 return
+ 736 if args[0][0] == "@":
+ 737 if self.options.output_name:
+ 738 raise CogUsageError("Can't use -o with @file")
+ 739 self.process_file_list(args[0][1:])
+ 740 elif args[0][0] == "&":
+ 741 if self.options.output_name:
+ 742 raise CogUsageError("Can't use -o with &file")
+ 743 file_list = args[0][1:]
+ 744 with change_dir(os.path.dirname(file_list)):
+ 745 self.process_file_list(os.path.basename(file_list))
+ 746 else:
+ 747 self.process_wildcards(args[0])
+ 748
+ 749 self.options = saved_options
+ 750
+ 751 def callable_main(self, argv):
+ 752 """All of command-line cog, but in a callable form.
+ 753
+ 754 This is used by main. `argv` is the equivalent of sys.argv.
755
- 756 if self.options.args:
- 757 for a in self.options.args:
- 758 self.processArguments([a])
- 759 else:
- 760 raise CogUsageError("No files to process")
- 761
- 762 if self.bCheckFailed:
- 763 raise CogCheckFailed("Check failed")
- 764
- 765 def main(self, argv):
- 766 """ Handle the command-line execution for cog.
- 767 """
- 768
- 769 try:
- 770 self.callableMain(argv)
- 771 return 0
- 772 except CogUsageError as err:
- 773 self.prerr(err)
- 774 self.prerr("(for help use -h)")
- 775 return 2
- 776 except CogGeneratedError as err:
- 777 self.prerr(f"Error: {err}")
- 778 return 3
- 779 except CogUserException as err:
- 780 self.prerr("Traceback (most recent call last):")
- 781 self.prerr(err.args[0])
- 782 return 4
- 783 except CogCheckFailed as err:
- 784 self.prerr(err)
- 785 return 5
- 786 except CogError as err:
- 787 self.prerr(err)
- 788 return 1
- 789
- 790
- 791def find_cog_source(frame_summary, prologue):
- 792 """Find cog source lines in a frame summary list, for printing tracebacks.
- 793
- 794 Arguments:
- 795 frame_summary: a list of 4-item tuples, as returned by traceback.extract_tb.
- 796 prologue: the text of the code prologue.
- 797
- 798 Returns
- 799 A list of 4-item tuples, updated to correct the cog entries.
- 800
- 801 """
- 802 prolines = prologue.splitlines()
- 803 for filename, lineno, funcname, source in frame_summary:
- 804 if not source: 804 ↛ 816line 804 didn't jump to line 816, because the condition on line 804 was never false
- 805 m = re.search(r"^<cog ([^:]+):(\d+)>$", filename)
- 806 if m: 806 ↛ 807line 806 didn't jump to line 807, because the condition on line 806 was never true
- 807 if lineno <= len(prolines):
- 808 filename = '<prologue>'
- 809 source = prolines[lineno-1]
- 810 lineno -= 1 # Because "import cog" is the first line in the prologue
- 811 else:
- 812 filename, coglineno = m.groups()
- 813 coglineno = int(coglineno)
- 814 lineno += coglineno - len(prolines)
- 815 source = linecache.getline(filename, lineno).strip()
- 816 yield filename, lineno, funcname, source
- 817
- 818
- 819def main():
- 820 """Main function for entry_points to use."""
- 821 return Cog().main(sys.argv)
+ 756 """
+ 757 argv = argv[1:]
+ 758
+ 759 # Provide help if asked for anywhere in the command line.
+ 760 if "-?" in argv or "-h" in argv:
+ 761 self.prerr(usage, end="")
+ 762 return
+ 763
+ 764 self.options.parse_args(argv)
+ 765 self.options.validate()
+ 766 self._fix_end_output_patterns()
+ 767
+ 768 if self.options.show_version:
+ 769 self.prout(f"Cog version {__version__}")
+ 770 return
+ 771
+ 772 if self.options.args:
+ 773 for a in self.options.args:
+ 774 self.process_arguments([a])
+ 775 else:
+ 776 raise CogUsageError("No files to process")
+ 777
+ 778 if self.check_failed:
+ 779 raise CogCheckFailed("Check failed")
+ 780
+ 781 def main(self, argv):
+ 782 """Handle the command-line execution for cog."""
+ 783
+ 784 try:
+ 785 self.callable_main(argv)
+ 786 return 0
+ 787 except CogUsageError as err:
+ 788 self.prerr(err)
+ 789 self.prerr("(for help use -h)")
+ 790 return 2
+ 791 except CogGeneratedError as err:
+ 792 self.prerr(f"Error: {err}")
+ 793 return 3
+ 794 except CogUserException as err:
+ 795 self.prerr("Traceback (most recent call last):")
+ 796 self.prerr(err.args[0])
+ 797 return 4
+ 798 except CogCheckFailed as err:
+ 799 self.prerr(err)
+ 800 return 5
+ 801 except CogError as err:
+ 802 self.prerr(err)
+ 803 return 1
+ 804
+ 805
+ 806def find_cog_source(frame_summary, prologue):
+ 807 """Find cog source lines in a frame summary list, for printing tracebacks.
+ 808
+ 809 Arguments:
+ 810 frame_summary: a list of 4-item tuples, as returned by traceback.extract_tb.
+ 811 prologue: the text of the code prologue.
+ 812
+ 813 Returns
+ 814 A list of 4-item tuples, updated to correct the cog entries.
+ 815
+ 816 """
+ 817 prolines = prologue.splitlines()
+ 818 for filename, lineno, funcname, source in frame_summary:
+ 819 if not source: 819 ↛ 833line 819 didn't jump to line 833, because the condition on line 819 was always true
+ 820 m = re.search(r"^<cog ([^:]+):(\d+)>$", filename)
+ 821 if m: 821 ↛ 822line 821 didn't jump to line 822, because the condition on line 821 was never true
+ 822 if lineno <= len(prolines):
+ 823 filename = "<prologue>"
+ 824 source = prolines[lineno - 1]
+ 825 lineno -= (
+ 826 1 # Because "import cog" is the first line in the prologue
+ 827 )
+ 828 else:
+ 829 filename, coglineno = m.groups()
+ 830 coglineno = int(coglineno)
+ 831 lineno += coglineno - len(prolines)
+ 832 source = linecache.getline(filename, lineno).strip()
+ 833 yield filename, lineno, funcname, source
+ 834
+ 835
+ 836def main():
+ 837 """Main function for entry_points to use."""
+ 838 return Cog().main(sys.argv)
- 1""" Test cogapp.
- 2"""
- 3
- 4import io
- 5import os
- 6import os.path
- 7import random
- 8import re
- 9import shutil
- 10import stat
- 11import sys
- 12import tempfile
- 13import threading
- 14from unittest import TestCase
- 15
- 16from .cogapp import Cog, CogOptions, CogGenerator
- 17from .cogapp import CogError, CogUsageError, CogGeneratedError, CogUserException
- 18from .cogapp import usage, __version__, main
- 19from .makefiles import makeFiles
- 20from .whiteutils import reindentBlock
+ 1"""Test cogapp."""
+ 2
+ 3import io
+ 4import os
+ 5import os.path
+ 6import random
+ 7import re
+ 8import shutil
+ 9import stat
+ 10import sys
+ 11import tempfile
+ 12import threading
+ 13from unittest import TestCase
+ 14
+ 15from .cogapp import Cog, CogOptions, CogGenerator
+ 16from .cogapp import CogError, CogUsageError, CogGeneratedError, CogUserException
+ 17from .cogapp import usage, __version__, main
+ 18from .makefiles import make_files
+ 19from .whiteutils import reindent_block
+ 20
21
- 22
- 23class CogTestsInMemory(TestCase):
- 24 """ Test cases for cogapp.Cog()
- 25 """
- 26
- 27 def testNoCog(self):
- 28 strings = [
- 29 '',
- 30 ' ',
- 31 ' \t \t \tx',
- 32 'hello',
- 33 'the cat\nin the\nhat.',
- 34 'Horton\n\tHears A\n\t\tWho'
- 35 ]
- 36 for s in strings:
- 37 self.assertEqual(Cog().processString(s), s)
- 38
- 39 def testSimple(self):
- 40 infile = """\
- 41 Some text.
- 42 //[[[cog
- 43 import cog
- 44 cog.outl("This is line one\\n")
- 45 cog.outl("This is line two")
- 46 //]]]
- 47 gobbledegook.
- 48 //[[[end]]]
- 49 epilogue.
- 50 """
- 51
- 52 outfile = """\
- 53 Some text.
- 54 //[[[cog
- 55 import cog
- 56 cog.outl("This is line one\\n")
- 57 cog.outl("This is line two")
- 58 //]]]
- 59 This is line one
- 60
- 61 This is line two
- 62 //[[[end]]]
- 63 epilogue.
- 64 """
+ 22class CogTestsInMemory(TestCase):
+ 23 """Test cases for cogapp.Cog()"""
+ 24
+ 25 def test_no_cog(self):
+ 26 strings = [
+ 27 "",
+ 28 " ",
+ 29 " \t \t \tx",
+ 30 "hello",
+ 31 "the cat\nin the\nhat.",
+ 32 "Horton\n\tHears A\n\t\tWho",
+ 33 ]
+ 34 for s in strings:
+ 35 self.assertEqual(Cog().process_string(s), s)
+ 36
+ 37 def test_simple(self):
+ 38 infile = """\
+ 39 Some text.
+ 40 //[[[cog
+ 41 import cog
+ 42 cog.outl("This is line one\\n")
+ 43 cog.outl("This is line two")
+ 44 //]]]
+ 45 gobbledegook.
+ 46 //[[[end]]]
+ 47 epilogue.
+ 48 """
+ 49
+ 50 outfile = """\
+ 51 Some text.
+ 52 //[[[cog
+ 53 import cog
+ 54 cog.outl("This is line one\\n")
+ 55 cog.outl("This is line two")
+ 56 //]]]
+ 57 This is line one
+ 58
+ 59 This is line two
+ 60 //[[[end]]]
+ 61 epilogue.
+ 62 """
+ 63
+ 64 self.assertEqual(Cog().process_string(infile), outfile)
65
- 66 self.assertEqual(Cog().processString(infile), outfile)
- 67
- 68 def testEmptyCog(self):
- 69 # The cog clause can be totally empty. Not sure why you'd want it,
- 70 # but it works.
- 71 infile = """\
- 72 hello
- 73 //[[[cog
- 74 //]]]
- 75 //[[[end]]]
- 76 goodbye
- 77 """
- 78
- 79 infile = reindentBlock(infile)
- 80 self.assertEqual(Cog().processString(infile), infile)
- 81
- 82 def testMultipleCogs(self):
- 83 # One file can have many cog chunks, even abutting each other.
- 84 infile = """\
- 85 //[[[cog
- 86 cog.out("chunk1")
- 87 //]]]
- 88 chunk1
- 89 //[[[end]]]
- 90 //[[[cog
- 91 cog.out("chunk2")
- 92 //]]]
- 93 chunk2
- 94 //[[[end]]]
- 95 between chunks
- 96 //[[[cog
- 97 cog.out("chunk3")
- 98 //]]]
- 99 chunk3
- 100 //[[[end]]]
- 101 """
- 102
- 103 infile = reindentBlock(infile)
- 104 self.assertEqual(Cog().processString(infile), infile)
- 105
- 106 def testTrimBlankLines(self):
- 107 infile = """\
- 108 //[[[cog
- 109 cog.out("This is line one\\n", trimblanklines=True)
- 110 cog.out('''
- 111 This is line two
- 112 ''', dedent=True, trimblanklines=True)
- 113 cog.outl("This is line three", trimblanklines=True)
- 114 //]]]
- 115 This is line one
- 116 This is line two
- 117 This is line three
- 118 //[[[end]]]
- 119 """
- 120
- 121 infile = reindentBlock(infile)
- 122 self.assertEqual(Cog().processString(infile), infile)
- 123
- 124 def testTrimEmptyBlankLines(self):
- 125 infile = """\
- 126 //[[[cog
- 127 cog.out("This is line one\\n", trimblanklines=True)
- 128 cog.out('''
- 129 This is line two
- 130 ''', dedent=True, trimblanklines=True)
- 131 cog.out('', dedent=True, trimblanklines=True)
- 132 cog.outl("This is line three", trimblanklines=True)
- 133 //]]]
- 134 This is line one
- 135 This is line two
- 136 This is line three
- 137 //[[[end]]]
- 138 """
- 139
- 140 infile = reindentBlock(infile)
- 141 self.assertEqual(Cog().processString(infile), infile)
- 142
- 143 def testTrimBlankLinesWithLastPartial(self):
- 144 infile = """\
- 145 //[[[cog
- 146 cog.out("This is line one\\n", trimblanklines=True)
- 147 cog.out("\\nLine two\\nLine three", trimblanklines=True)
- 148 //]]]
- 149 This is line one
- 150 Line two
- 151 Line three
- 152 //[[[end]]]
- 153 """
- 154
- 155 infile = reindentBlock(infile)
- 156 self.assertEqual(Cog().processString(infile), infile)
- 157
- 158 def testCogOutDedent(self):
- 159 infile = """\
- 160 //[[[cog
- 161 cog.out("This is the first line\\n")
- 162 cog.out('''
- 163 This is dedent=True 1
- 164 This is dedent=True 2
- 165 ''', dedent=True, trimblanklines=True)
- 166 cog.out('''
- 167 This is dedent=False 1
- 168 This is dedent=False 2
- 169 ''', dedent=False, trimblanklines=True)
- 170 cog.out('''
- 171 This is dedent=default 1
- 172 This is dedent=default 2
- 173 ''', trimblanklines=True)
- 174 cog.out("This is the last line\\n")
- 175 //]]]
- 176 This is the first line
- 177 This is dedent=True 1
- 178 This is dedent=True 2
- 179 This is dedent=False 1
- 180 This is dedent=False 2
- 181 This is dedent=default 1
- 182 This is dedent=default 2
- 183 This is the last line
- 184 //[[[end]]]
- 185 """
- 186
- 187 infile = reindentBlock(infile)
- 188 self.assertEqual(Cog().processString(infile), infile)
- 189
- 190 def test22EndOfLine(self):
- 191 # In Python 2.2, this cog file was not parsing because the
- 192 # last line is indented but didn't end with a newline.
- 193 infile = """\
- 194 //[[[cog
- 195 import cog
- 196 for i in range(3):
- 197 cog.out("%d\\n" % i)
- 198 //]]]
- 199 0
- 200 1
- 201 2
- 202 //[[[end]]]
- 203 """
- 204
- 205 infile = reindentBlock(infile)
- 206 self.assertEqual(Cog().processString(infile), infile)
- 207
- 208 def testIndentedCode(self):
- 209 infile = """\
- 210 first line
- 211 [[[cog
- 212 import cog
- 213 for i in range(3):
- 214 cog.out("xx%d\\n" % i)
- 215 ]]]
- 216 xx0
- 217 xx1
- 218 xx2
- 219 [[[end]]]
- 220 last line
- 221 """
- 222
- 223 infile = reindentBlock(infile)
- 224 self.assertEqual(Cog().processString(infile), infile)
- 225
- 226 def testPrefixedCode(self):
- 227 infile = """\
- 228 --[[[cog
- 229 --import cog
- 230 --for i in range(3):
- 231 -- cog.out("xx%d\\n" % i)
- 232 --]]]
- 233 xx0
- 234 xx1
- 235 xx2
- 236 --[[[end]]]
- 237 """
- 238
- 239 infile = reindentBlock(infile)
- 240 self.assertEqual(Cog().processString(infile), infile)
- 241
- 242 def testPrefixedIndentedCode(self):
- 243 infile = """\
- 244 prologue
- 245 --[[[cog
- 246 -- import cog
- 247 -- for i in range(3):
- 248 -- cog.out("xy%d\\n" % i)
- 249 --]]]
- 250 xy0
- 251 xy1
- 252 xy2
- 253 --[[[end]]]
- 254 """
- 255
- 256 infile = reindentBlock(infile)
- 257 self.assertEqual(Cog().processString(infile), infile)
- 258
- 259 def testBogusPrefixMatch(self):
- 260 infile = """\
- 261 prologue
- 262 #[[[cog
- 263 import cog
- 264 # This comment should not be clobbered by removing the pound sign.
- 265 for i in range(3):
- 266 cog.out("xy%d\\n" % i)
- 267 #]]]
- 268 xy0
- 269 xy1
- 270 xy2
- 271 #[[[end]]]
- 272 """
- 273
- 274 infile = reindentBlock(infile)
- 275 self.assertEqual(Cog().processString(infile), infile)
- 276
- 277 def testNoFinalNewline(self):
- 278 # If the cog'ed output has no final newline,
- 279 # it shouldn't eat up the cog terminator.
- 280 infile = """\
- 281 prologue
- 282 [[[cog
- 283 import cog
- 284 for i in range(3):
- 285 cog.out("%d" % i)
- 286 ]]]
- 287 012
- 288 [[[end]]]
- 289 epilogue
- 290 """
- 291
- 292 infile = reindentBlock(infile)
- 293 self.assertEqual(Cog().processString(infile), infile)
- 294
- 295 def testNoOutputAtAll(self):
- 296 # If there is absolutely no cog output, that's ok.
- 297 infile = """\
- 298 prologue
- 299 [[[cog
- 300 i = 1
- 301 ]]]
- 302 [[[end]]]
- 303 epilogue
- 304 """
- 305
- 306 infile = reindentBlock(infile)
- 307 self.assertEqual(Cog().processString(infile), infile)
- 308
- 309 def testPurelyBlankLine(self):
- 310 # If there is a blank line in the cog code with no whitespace
- 311 # prefix, that should be OK.
- 312
- 313 infile = """\
- 314 prologue
- 315 [[[cog
- 316 import sys
- 317 cog.out("Hello")
- 318 $
- 319 cog.out("There")
- 320 ]]]
- 321 HelloThere
- 322 [[[end]]]
- 323 epilogue
- 324 """
- 325
- 326 infile = reindentBlock(infile.replace('$', ''))
- 327 self.assertEqual(Cog().processString(infile), infile)
- 328
- 329 def testEmptyOutl(self):
- 330 # Alexander Belchenko suggested the string argument to outl should
- 331 # be optional. Does it work?
- 332
- 333 infile = """\
- 334 prologue
- 335 [[[cog
- 336 cog.outl("x")
- 337 cog.outl()
- 338 cog.outl("y")
- 339 cog.out() # Also optional, a complete no-op.
- 340 cog.outl(trimblanklines=True)
- 341 cog.outl("z")
- 342 ]]]
- 343 x
+ 66 def test_empty_cog(self):
+ 67 # The cog clause can be totally empty. Not sure why you'd want it,
+ 68 # but it works.
+ 69 infile = """\
+ 70 hello
+ 71 //[[[cog
+ 72 //]]]
+ 73 //[[[end]]]
+ 74 goodbye
+ 75 """
+ 76
+ 77 infile = reindent_block(infile)
+ 78 self.assertEqual(Cog().process_string(infile), infile)
+ 79
+ 80 def test_multiple_cogs(self):
+ 81 # One file can have many cog chunks, even abutting each other.
+ 82 infile = """\
+ 83 //[[[cog
+ 84 cog.out("chunk1")
+ 85 //]]]
+ 86 chunk1
+ 87 //[[[end]]]
+ 88 //[[[cog
+ 89 cog.out("chunk2")
+ 90 //]]]
+ 91 chunk2
+ 92 //[[[end]]]
+ 93 between chunks
+ 94 //[[[cog
+ 95 cog.out("chunk3")
+ 96 //]]]
+ 97 chunk3
+ 98 //[[[end]]]
+ 99 """
+ 100
+ 101 infile = reindent_block(infile)
+ 102 self.assertEqual(Cog().process_string(infile), infile)
+ 103
+ 104 def test_trim_blank_lines(self):
+ 105 infile = """\
+ 106 //[[[cog
+ 107 cog.out("This is line one\\n", trimblanklines=True)
+ 108 cog.out('''
+ 109 This is line two
+ 110 ''', dedent=True, trimblanklines=True)
+ 111 cog.outl("This is line three", trimblanklines=True)
+ 112 //]]]
+ 113 This is line one
+ 114 This is line two
+ 115 This is line three
+ 116 //[[[end]]]
+ 117 """
+ 118
+ 119 infile = reindent_block(infile)
+ 120 self.assertEqual(Cog().process_string(infile), infile)
+ 121
+ 122 def test_trim_empty_blank_lines(self):
+ 123 infile = """\
+ 124 //[[[cog
+ 125 cog.out("This is line one\\n", trimblanklines=True)
+ 126 cog.out('''
+ 127 This is line two
+ 128 ''', dedent=True, trimblanklines=True)
+ 129 cog.out('', dedent=True, trimblanklines=True)
+ 130 cog.outl("This is line three", trimblanklines=True)
+ 131 //]]]
+ 132 This is line one
+ 133 This is line two
+ 134 This is line three
+ 135 //[[[end]]]
+ 136 """
+ 137
+ 138 infile = reindent_block(infile)
+ 139 self.assertEqual(Cog().process_string(infile), infile)
+ 140
+ 141 def test_trim_blank_lines_with_last_partial(self):
+ 142 infile = """\
+ 143 //[[[cog
+ 144 cog.out("This is line one\\n", trimblanklines=True)
+ 145 cog.out("\\nLine two\\nLine three", trimblanklines=True)
+ 146 //]]]
+ 147 This is line one
+ 148 Line two
+ 149 Line three
+ 150 //[[[end]]]
+ 151 """
+ 152
+ 153 infile = reindent_block(infile)
+ 154 self.assertEqual(Cog().process_string(infile), infile)
+ 155
+ 156 def test_cog_out_dedent(self):
+ 157 infile = """\
+ 158 //[[[cog
+ 159 cog.out("This is the first line\\n")
+ 160 cog.out('''
+ 161 This is dedent=True 1
+ 162 This is dedent=True 2
+ 163 ''', dedent=True, trimblanklines=True)
+ 164 cog.out('''
+ 165 This is dedent=False 1
+ 166 This is dedent=False 2
+ 167 ''', dedent=False, trimblanklines=True)
+ 168 cog.out('''
+ 169 This is dedent=default 1
+ 170 This is dedent=default 2
+ 171 ''', trimblanklines=True)
+ 172 cog.out("This is the last line\\n")
+ 173 //]]]
+ 174 This is the first line
+ 175 This is dedent=True 1
+ 176 This is dedent=True 2
+ 177 This is dedent=False 1
+ 178 This is dedent=False 2
+ 179 This is dedent=default 1
+ 180 This is dedent=default 2
+ 181 This is the last line
+ 182 //[[[end]]]
+ 183 """
+ 184
+ 185 infile = reindent_block(infile)
+ 186 self.assertEqual(Cog().process_string(infile), infile)
+ 187
+ 188 def test22_end_of_line(self):
+ 189 # In Python 2.2, this cog file was not parsing because the
+ 190 # last line is indented but didn't end with a newline.
+ 191 infile = """\
+ 192 //[[[cog
+ 193 import cog
+ 194 for i in range(3):
+ 195 cog.out("%d\\n" % i)
+ 196 //]]]
+ 197 0
+ 198 1
+ 199 2
+ 200 //[[[end]]]
+ 201 """
+ 202
+ 203 infile = reindent_block(infile)
+ 204 self.assertEqual(Cog().process_string(infile), infile)
+ 205
+ 206 def test_indented_code(self):
+ 207 infile = """\
+ 208 first line
+ 209 [[[cog
+ 210 import cog
+ 211 for i in range(3):
+ 212 cog.out("xx%d\\n" % i)
+ 213 ]]]
+ 214 xx0
+ 215 xx1
+ 216 xx2
+ 217 [[[end]]]
+ 218 last line
+ 219 """
+ 220
+ 221 infile = reindent_block(infile)
+ 222 self.assertEqual(Cog().process_string(infile), infile)
+ 223
+ 224 def test_prefixed_code(self):
+ 225 infile = """\
+ 226 --[[[cog
+ 227 --import cog
+ 228 --for i in range(3):
+ 229 -- cog.out("xx%d\\n" % i)
+ 230 --]]]
+ 231 xx0
+ 232 xx1
+ 233 xx2
+ 234 --[[[end]]]
+ 235 """
+ 236
+ 237 infile = reindent_block(infile)
+ 238 self.assertEqual(Cog().process_string(infile), infile)
+ 239
+ 240 def test_prefixed_indented_code(self):
+ 241 infile = """\
+ 242 prologue
+ 243 --[[[cog
+ 244 -- import cog
+ 245 -- for i in range(3):
+ 246 -- cog.out("xy%d\\n" % i)
+ 247 --]]]
+ 248 xy0
+ 249 xy1
+ 250 xy2
+ 251 --[[[end]]]
+ 252 """
+ 253
+ 254 infile = reindent_block(infile)
+ 255 self.assertEqual(Cog().process_string(infile), infile)
+ 256
+ 257 def test_bogus_prefix_match(self):
+ 258 infile = """\
+ 259 prologue
+ 260 #[[[cog
+ 261 import cog
+ 262 # This comment should not be clobbered by removing the pound sign.
+ 263 for i in range(3):
+ 264 cog.out("xy%d\\n" % i)
+ 265 #]]]
+ 266 xy0
+ 267 xy1
+ 268 xy2
+ 269 #[[[end]]]
+ 270 """
+ 271
+ 272 infile = reindent_block(infile)
+ 273 self.assertEqual(Cog().process_string(infile), infile)
+ 274
+ 275 def test_no_final_newline(self):
+ 276 # If the cog'ed output has no final newline,
+ 277 # it shouldn't eat up the cog terminator.
+ 278 infile = """\
+ 279 prologue
+ 280 [[[cog
+ 281 import cog
+ 282 for i in range(3):
+ 283 cog.out("%d" % i)
+ 284 ]]]
+ 285 012
+ 286 [[[end]]]
+ 287 epilogue
+ 288 """
+ 289
+ 290 infile = reindent_block(infile)
+ 291 self.assertEqual(Cog().process_string(infile), infile)
+ 292
+ 293 def test_no_output_at_all(self):
+ 294 # If there is absolutely no cog output, that's ok.
+ 295 infile = """\
+ 296 prologue
+ 297 [[[cog
+ 298 i = 1
+ 299 ]]]
+ 300 [[[end]]]
+ 301 epilogue
+ 302 """
+ 303
+ 304 infile = reindent_block(infile)
+ 305 self.assertEqual(Cog().process_string(infile), infile)
+ 306
+ 307 def test_purely_blank_line(self):
+ 308 # If there is a blank line in the cog code with no whitespace
+ 309 # prefix, that should be OK.
+ 310
+ 311 infile = """\
+ 312 prologue
+ 313 [[[cog
+ 314 import sys
+ 315 cog.out("Hello")
+ 316 $
+ 317 cog.out("There")
+ 318 ]]]
+ 319 HelloThere
+ 320 [[[end]]]
+ 321 epilogue
+ 322 """
+ 323
+ 324 infile = reindent_block(infile.replace("$", ""))
+ 325 self.assertEqual(Cog().process_string(infile), infile)
+ 326
+ 327 def test_empty_outl(self):
+ 328 # Alexander Belchenko suggested the string argument to outl should
+ 329 # be optional. Does it work?
+ 330
+ 331 infile = """\
+ 332 prologue
+ 333 [[[cog
+ 334 cog.outl("x")
+ 335 cog.outl()
+ 336 cog.outl("y")
+ 337 cog.out() # Also optional, a complete no-op.
+ 338 cog.outl(trimblanklines=True)
+ 339 cog.outl("z")
+ 340 ]]]
+ 341 x
+ 342
+ 343 y
344
- 345 y
- 346
- 347 z
- 348 [[[end]]]
- 349 epilogue
- 350 """
- 351
- 352 infile = reindentBlock(infile)
- 353 self.assertEqual(Cog().processString(infile), infile)
- 354
- 355 def testFirstLineNum(self):
- 356 infile = """\
- 357 fooey
- 358 [[[cog
- 359 cog.outl("started at line number %d" % cog.firstLineNum)
- 360 ]]]
- 361 started at line number 2
- 362 [[[end]]]
- 363 blah blah
- 364 [[[cog
- 365 cog.outl("and again at line %d" % cog.firstLineNum)
- 366 ]]]
- 367 and again at line 8
- 368 [[[end]]]
- 369 """
- 370
- 371 infile = reindentBlock(infile)
- 372 self.assertEqual(Cog().processString(infile), infile)
- 373
- 374 def testCompactOneLineCode(self):
- 375 infile = """\
- 376 first line
- 377 hey: [[[cog cog.outl("hello %d" % (3*3*3*3)) ]]] looky!
- 378 get rid of this!
- 379 [[[end]]]
- 380 last line
- 381 """
- 382
- 383 outfile = """\
- 384 first line
- 385 hey: [[[cog cog.outl("hello %d" % (3*3*3*3)) ]]] looky!
- 386 hello 81
- 387 [[[end]]]
- 388 last line
- 389 """
- 390
- 391 infile = reindentBlock(infile)
- 392 self.assertEqual(Cog().processString(infile), reindentBlock(outfile))
- 393
- 394 def testInsideOutCompact(self):
- 395 infile = """\
- 396 first line
- 397 hey?: ]]] what is this? [[[cog strange!
- 398 get rid of this!
- 399 [[[end]]]
- 400 last line
- 401 """
- 402 with self.assertRaisesRegex(CogError, r"^infile.txt\(2\): Cog code markers inverted$"):
- 403 Cog().processString(reindentBlock(infile), "infile.txt")
+ 345 z
+ 346 [[[end]]]
+ 347 epilogue
+ 348 """
+ 349
+ 350 infile = reindent_block(infile)
+ 351 self.assertEqual(Cog().process_string(infile), infile)
+ 352
+ 353 def test_first_line_num(self):
+ 354 infile = """\
+ 355 fooey
+ 356 [[[cog
+ 357 cog.outl("started at line number %d" % cog.firstLineNum)
+ 358 ]]]
+ 359 started at line number 2
+ 360 [[[end]]]
+ 361 blah blah
+ 362 [[[cog
+ 363 cog.outl("and again at line %d" % cog.firstLineNum)
+ 364 ]]]
+ 365 and again at line 8
+ 366 [[[end]]]
+ 367 """
+ 368
+ 369 infile = reindent_block(infile)
+ 370 self.assertEqual(Cog().process_string(infile), infile)
+ 371
+ 372 def test_compact_one_line_code(self):
+ 373 infile = """\
+ 374 first line
+ 375 hey: [[[cog cog.outl("hello %d" % (3*3*3*3)) ]]] looky!
+ 376 get rid of this!
+ 377 [[[end]]]
+ 378 last line
+ 379 """
+ 380
+ 381 outfile = """\
+ 382 first line
+ 383 hey: [[[cog cog.outl("hello %d" % (3*3*3*3)) ]]] looky!
+ 384 hello 81
+ 385 [[[end]]]
+ 386 last line
+ 387 """
+ 388
+ 389 infile = reindent_block(infile)
+ 390 self.assertEqual(Cog().process_string(infile), reindent_block(outfile))
+ 391
+ 392 def test_inside_out_compact(self):
+ 393 infile = """\
+ 394 first line
+ 395 hey?: ]]] what is this? [[[cog strange!
+ 396 get rid of this!
+ 397 [[[end]]]
+ 398 last line
+ 399 """
+ 400 with self.assertRaisesRegex(
+ 401 CogError, r"^infile.txt\(2\): Cog code markers inverted$"
+ 402 ):
+ 403 Cog().process_string(reindent_block(infile), "infile.txt")
404
- 405 def testSharingGlobals(self):
+ 405 def test_sharing_globals(self):
406 infile = """\
407 first line
408 hey: [[[cog s="hey there" ]]] looky!
@@ -508,10 +508,10 @@
424 last line
425 """
426
- 427 infile = reindentBlock(infile)
- 428 self.assertEqual(Cog().processString(infile), reindentBlock(outfile))
+ 427 infile = reindent_block(infile)
+ 428 self.assertEqual(Cog().process_string(infile), reindent_block(outfile))
429
- 430 def testAssertInCogCode(self):
+ 430 def test_assert_in_cog_code(self):
431 # Check that we can test assertions in cog code in the test framework.
432 infile = """\
433 [[[cog
@@ -519,11 +519,11 @@
435 ]]]
436 [[[end]]]
437 """
- 438 infile = reindentBlock(infile)
+ 438 infile = reindent_block(infile)
439 with self.assertRaisesRegex(CogUserException, "AssertionError: Oops"):
- 440 Cog().processString(infile)
+ 440 Cog().process_string(infile)
441
- 442 def testCogPrevious(self):
+ 442 def test_cog_previous(self):
443 # Check that we can access the previous run's output.
444 infile = """\
445 [[[cog
@@ -546,507 +546,507 @@
462 [[[end]]]
463 """
464
- 465 infile = reindentBlock(infile)
- 466 self.assertEqual(Cog().processString(infile), reindentBlock(outfile))
+ 465 infile = reindent_block(infile)
+ 466 self.assertEqual(Cog().process_string(infile), reindent_block(outfile))
467
468
469class CogOptionsTests(TestCase):
- 470 """ Test the CogOptions class.
- 471 """
- 472
- 473 def testEquality(self):
- 474 o = CogOptions()
- 475 p = CogOptions()
- 476 self.assertEqual(o, p)
- 477 o.parseArgs(['-r'])
- 478 self.assertNotEqual(o, p)
- 479 p.parseArgs(['-r'])
- 480 self.assertEqual(o, p)
- 481
- 482 def testCloning(self):
- 483 o = CogOptions()
- 484 o.parseArgs(['-I', 'fooey', '-I', 'booey', '-s', ' /*x*/'])
- 485 p = o.clone()
- 486 self.assertEqual(o, p)
- 487 p.parseArgs(['-I', 'huey', '-D', 'foo=quux'])
- 488 self.assertNotEqual(o, p)
- 489 q = CogOptions()
- 490 q.parseArgs(['-I', 'fooey', '-I', 'booey', '-s', ' /*x*/', '-I', 'huey', '-D', 'foo=quux'])
- 491 self.assertEqual(p, q)
- 492
- 493 def testCombiningFlags(self):
- 494 # Single-character flags can be combined.
- 495 o = CogOptions()
- 496 o.parseArgs(['-e', '-r', '-z'])
- 497 p = CogOptions()
- 498 p.parseArgs(['-erz'])
- 499 self.assertEqual(o, p)
- 500
- 501 def testMarkers(self):
- 502 o = CogOptions()
- 503 o._parse_markers('a b c')
- 504 self.assertEqual('a', o.sBeginSpec)
- 505 self.assertEqual('b', o.sEndSpec)
- 506 self.assertEqual('c', o.sEndOutput)
- 507
- 508 def testMarkersSwitch(self):
- 509 o = CogOptions()
- 510 o.parseArgs(['--markers', 'a b c'])
- 511 self.assertEqual('a', o.sBeginSpec)
- 512 self.assertEqual('b', o.sEndSpec)
- 513 self.assertEqual('c', o.sEndOutput)
- 514
- 515
- 516class FileStructureTests(TestCase):
- 517 """ Test cases to check that we're properly strict about the structure
- 518 of files.
- 519 """
- 520
- 521 def isBad(self, infile, msg=None):
- 522 infile = reindentBlock(infile)
- 523 with self.assertRaisesRegex(CogError, "^"+re.escape(msg)+"$"):
- 524 Cog().processString(infile, 'infile.txt')
- 525
- 526 def testBeginNoEnd(self):
- 527 infile = """\
- 528 Fooey
- 529 #[[[cog
- 530 cog.outl('hello')
- 531 """
- 532 self.isBad(infile, "infile.txt(2): Cog block begun but never ended.")
- 533
- 534 def testNoEoo(self):
- 535 infile = """\
- 536 Fooey
- 537 #[[[cog
- 538 cog.outl('hello')
- 539 #]]]
- 540 """
- 541 self.isBad(infile, "infile.txt(4): Missing '[[[end]]]' before end of file.")
- 542
- 543 infile2 = """\
- 544 Fooey
- 545 #[[[cog
- 546 cog.outl('hello')
- 547 #]]]
- 548 #[[[cog
- 549 cog.outl('goodbye')
- 550 #]]]
- 551 """
- 552 self.isBad(infile2, "infile.txt(5): Unexpected '[[[cog'")
- 553
- 554 def testStartWithEnd(self):
- 555 infile = """\
- 556 #]]]
- 557 """
- 558 self.isBad(infile, "infile.txt(1): Unexpected ']]]'")
- 559
- 560 infile2 = """\
- 561 #[[[cog
- 562 cog.outl('hello')
- 563 #]]]
- 564 #[[[end]]]
- 565 #]]]
- 566 """
- 567 self.isBad(infile2, "infile.txt(5): Unexpected ']]]'")
- 568
- 569 def testStartWithEoo(self):
- 570 infile = """\
- 571 #[[[end]]]
- 572 """
- 573 self.isBad(infile, "infile.txt(1): Unexpected '[[[end]]]'")
- 574
- 575 infile2 = """\
- 576 #[[[cog
- 577 cog.outl('hello')
- 578 #]]]
- 579 #[[[end]]]
- 580 #[[[end]]]
- 581 """
- 582 self.isBad(infile2, "infile.txt(5): Unexpected '[[[end]]]'")
- 583
- 584 def testNoEnd(self):
- 585 infile = """\
+ 470 """Test the CogOptions class."""
+ 471
+ 472 def test_equality(self):
+ 473 o = CogOptions()
+ 474 p = CogOptions()
+ 475 self.assertEqual(o, p)
+ 476 o.parse_args(["-r"])
+ 477 self.assertNotEqual(o, p)
+ 478 p.parse_args(["-r"])
+ 479 self.assertEqual(o, p)
+ 480
+ 481 def test_cloning(self):
+ 482 o = CogOptions()
+ 483 o.parse_args(["-I", "fooey", "-I", "booey", "-s", " /*x*/"])
+ 484 p = o.clone()
+ 485 self.assertEqual(o, p)
+ 486 p.parse_args(["-I", "huey", "-D", "foo=quux"])
+ 487 self.assertNotEqual(o, p)
+ 488 q = CogOptions()
+ 489 q.parse_args(
+ 490 [
+ 491 "-I",
+ 492 "fooey",
+ 493 "-I",
+ 494 "booey",
+ 495 "-s",
+ 496 " /*x*/",
+ 497 "-I",
+ 498 "huey",
+ 499 "-D",
+ 500 "foo=quux",
+ 501 ]
+ 502 )
+ 503 self.assertEqual(p, q)
+ 504
+ 505 def test_combining_flags(self):
+ 506 # Single-character flags can be combined.
+ 507 o = CogOptions()
+ 508 o.parse_args(["-e", "-r", "-z"])
+ 509 p = CogOptions()
+ 510 p.parse_args(["-erz"])
+ 511 self.assertEqual(o, p)
+ 512
+ 513 def test_markers(self):
+ 514 o = CogOptions()
+ 515 o._parse_markers("a b c")
+ 516 self.assertEqual("a", o.begin_spec)
+ 517 self.assertEqual("b", o.end_spec)
+ 518 self.assertEqual("c", o.end_output)
+ 519
+ 520 def test_markers_switch(self):
+ 521 o = CogOptions()
+ 522 o.parse_args(["--markers", "a b c"])
+ 523 self.assertEqual("a", o.begin_spec)
+ 524 self.assertEqual("b", o.end_spec)
+ 525 self.assertEqual("c", o.end_output)
+ 526
+ 527
+ 528class FileStructureTests(TestCase):
+ 529 """Test that we're properly strict about the structure of files."""
+ 530
+ 531 def is_bad(self, infile, msg=None):
+ 532 infile = reindent_block(infile)
+ 533 with self.assertRaisesRegex(CogError, "^" + re.escape(msg) + "$"):
+ 534 Cog().process_string(infile, "infile.txt")
+ 535
+ 536 def test_begin_no_end(self):
+ 537 infile = """\
+ 538 Fooey
+ 539 #[[[cog
+ 540 cog.outl('hello')
+ 541 """
+ 542 self.is_bad(infile, "infile.txt(2): Cog block begun but never ended.")
+ 543
+ 544 def test_no_eoo(self):
+ 545 infile = """\
+ 546 Fooey
+ 547 #[[[cog
+ 548 cog.outl('hello')
+ 549 #]]]
+ 550 """
+ 551 self.is_bad(infile, "infile.txt(4): Missing '[[[end]]]' before end of file.")
+ 552
+ 553 infile2 = """\
+ 554 Fooey
+ 555 #[[[cog
+ 556 cog.outl('hello')
+ 557 #]]]
+ 558 #[[[cog
+ 559 cog.outl('goodbye')
+ 560 #]]]
+ 561 """
+ 562 self.is_bad(infile2, "infile.txt(5): Unexpected '[[[cog'")
+ 563
+ 564 def test_start_with_end(self):
+ 565 infile = """\
+ 566 #]]]
+ 567 """
+ 568 self.is_bad(infile, "infile.txt(1): Unexpected ']]]'")
+ 569
+ 570 infile2 = """\
+ 571 #[[[cog
+ 572 cog.outl('hello')
+ 573 #]]]
+ 574 #[[[end]]]
+ 575 #]]]
+ 576 """
+ 577 self.is_bad(infile2, "infile.txt(5): Unexpected ']]]'")
+ 578
+ 579 def test_start_with_eoo(self):
+ 580 infile = """\
+ 581 #[[[end]]]
+ 582 """
+ 583 self.is_bad(infile, "infile.txt(1): Unexpected '[[[end]]]'")
+ 584
+ 585 infile2 = """\
586 #[[[cog
- 587 cog.outl("hello")
- 588 #[[[end]]]
- 589 """
- 590 self.isBad(infile, "infile.txt(3): Unexpected '[[[end]]]'")
- 591
- 592 infile2 = """\
- 593 #[[[cog
- 594 cog.outl('hello')
- 595 #]]]
- 596 #[[[end]]]
- 597 #[[[cog
- 598 cog.outl("hello")
- 599 #[[[end]]]
- 600 """
- 601 self.isBad(infile2, "infile.txt(7): Unexpected '[[[end]]]'")
- 602
- 603 def testTwoBegins(self):
- 604 infile = """\
- 605 #[[[cog
- 606 #[[[cog
- 607 cog.outl("hello")
- 608 #]]]
+ 587 cog.outl('hello')
+ 588 #]]]
+ 589 #[[[end]]]
+ 590 #[[[end]]]
+ 591 """
+ 592 self.is_bad(infile2, "infile.txt(5): Unexpected '[[[end]]]'")
+ 593
+ 594 def test_no_end(self):
+ 595 infile = """\
+ 596 #[[[cog
+ 597 cog.outl("hello")
+ 598 #[[[end]]]
+ 599 """
+ 600 self.is_bad(infile, "infile.txt(3): Unexpected '[[[end]]]'")
+ 601
+ 602 infile2 = """\
+ 603 #[[[cog
+ 604 cog.outl('hello')
+ 605 #]]]
+ 606 #[[[end]]]
+ 607 #[[[cog
+ 608 cog.outl("hello")
609 #[[[end]]]
610 """
- 611 self.isBad(infile, "infile.txt(2): Unexpected '[[[cog'")
+ 611 self.is_bad(infile2, "infile.txt(7): Unexpected '[[[end]]]'")
612
- 613 infile2 = """\
- 614 #[[[cog
- 615 cog.outl("hello")
- 616 #]]]
- 617 #[[[end]]]
- 618 #[[[cog
- 619 #[[[cog
- 620 cog.outl("hello")
- 621 #]]]
- 622 #[[[end]]]
- 623 """
- 624 self.isBad(infile2, "infile.txt(6): Unexpected '[[[cog'")
- 625
- 626 def testTwoEnds(self):
- 627 infile = """\
+ 613 def test_two_begins(self):
+ 614 infile = """\
+ 615 #[[[cog
+ 616 #[[[cog
+ 617 cog.outl("hello")
+ 618 #]]]
+ 619 #[[[end]]]
+ 620 """
+ 621 self.is_bad(infile, "infile.txt(2): Unexpected '[[[cog'")
+ 622
+ 623 infile2 = """\
+ 624 #[[[cog
+ 625 cog.outl("hello")
+ 626 #]]]
+ 627 #[[[end]]]
628 #[[[cog
- 629 cog.outl("hello")
- 630 #]]]
+ 629 #[[[cog
+ 630 cog.outl("hello")
631 #]]]
632 #[[[end]]]
633 """
- 634 self.isBad(infile, "infile.txt(4): Unexpected ']]]'")
+ 634 self.is_bad(infile2, "infile.txt(6): Unexpected '[[[cog'")
635
- 636 infile2 = """\
- 637 #[[[cog
- 638 cog.outl("hello")
- 639 #]]]
- 640 #[[[end]]]
- 641 #[[[cog
- 642 cog.outl("hello")
- 643 #]]]
- 644 #]]]
- 645 #[[[end]]]
- 646 """
- 647 self.isBad(infile2, "infile.txt(8): Unexpected ']]]'")
- 648
- 649
- 650class CogErrorTests(TestCase):
- 651 """ Test cases for cog.error().
- 652 """
- 653
- 654 def testErrorMsg(self):
- 655 infile = """\
- 656 [[[cog cog.error("This ain't right!")]]]
- 657 [[[end]]]
- 658 """
+ 636 def test_two_ends(self):
+ 637 infile = """\
+ 638 #[[[cog
+ 639 cog.outl("hello")
+ 640 #]]]
+ 641 #]]]
+ 642 #[[[end]]]
+ 643 """
+ 644 self.is_bad(infile, "infile.txt(4): Unexpected ']]]'")
+ 645
+ 646 infile2 = """\
+ 647 #[[[cog
+ 648 cog.outl("hello")
+ 649 #]]]
+ 650 #[[[end]]]
+ 651 #[[[cog
+ 652 cog.outl("hello")
+ 653 #]]]
+ 654 #]]]
+ 655 #[[[end]]]
+ 656 """
+ 657 self.is_bad(infile2, "infile.txt(8): Unexpected ']]]'")
+ 658
659
- 660 infile = reindentBlock(infile)
- 661 with self.assertRaisesRegex(CogGeneratedError, "^This ain't right!$"):
- 662 Cog().processString(infile)
- 663
- 664 def testErrorNoMsg(self):
- 665 infile = """\
- 666 [[[cog cog.error()]]]
- 667 [[[end]]]
- 668 """
- 669
- 670 infile = reindentBlock(infile)
- 671 with self.assertRaisesRegex(CogGeneratedError, "^Error raised by cog generator.$"):
- 672 Cog().processString(infile)
- 673
- 674 def testNoErrorIfErrorNotCalled(self):
- 675 infile = """\
- 676 --[[[cog
- 677 --import cog
- 678 --for i in range(3):
- 679 -- if i > 10:
- 680 -- cog.error("Something is amiss!")
- 681 -- cog.out("xx%d\\n" % i)
- 682 --]]]
- 683 xx0
- 684 xx1
- 685 xx2
- 686 --[[[end]]]
- 687 """
- 688
- 689 infile = reindentBlock(infile)
- 690 self.assertEqual(Cog().processString(infile), infile)
- 691
- 692
- 693class CogGeneratorGetCodeTests(TestCase):
- 694 """ Unit tests against CogGenerator to see if its getCode() method works
- 695 properly.
- 696 """
- 697
- 698 def setUp(self):
- 699 """ All tests get a generator to use, and short same-length names for
- 700 the functions we're going to use.
- 701 """
- 702 self.gen = CogGenerator()
- 703 self.m = self.gen.parseMarker
- 704 self.l = self.gen.parseLine
- 705
- 706 def testEmpty(self):
- 707 self.m('// [[[cog')
- 708 self.m('// ]]]')
- 709 self.assertEqual(self.gen.getCode(), '')
- 710
- 711 def testSimple(self):
- 712 self.m('// [[[cog')
- 713 self.l(' print "hello"')
- 714 self.l(' print "bye"')
- 715 self.m('// ]]]')
- 716 self.assertEqual(self.gen.getCode(), 'print "hello"\nprint "bye"')
- 717
- 718 def testCompressed1(self):
- 719 # For a while, I supported compressed code blocks, but no longer.
- 720 self.m('// [[[cog: print """')
- 721 self.l('// hello')
- 722 self.l('// bye')
- 723 self.m('// """)]]]')
- 724 self.assertEqual(self.gen.getCode(), 'hello\nbye')
+ 660class CogErrorTests(TestCase):
+ 661 """Test cases for cog.error()."""
+ 662
+ 663 def test_error_msg(self):
+ 664 infile = """\
+ 665 [[[cog cog.error("This ain't right!")]]]
+ 666 [[[end]]]
+ 667 """
+ 668
+ 669 infile = reindent_block(infile)
+ 670 with self.assertRaisesRegex(CogGeneratedError, "^This ain't right!$"):
+ 671 Cog().process_string(infile)
+ 672
+ 673 def test_error_no_msg(self):
+ 674 infile = """\
+ 675 [[[cog cog.error()]]]
+ 676 [[[end]]]
+ 677 """
+ 678
+ 679 infile = reindent_block(infile)
+ 680 with self.assertRaisesRegex(
+ 681 CogGeneratedError, "^Error raised by cog generator.$"
+ 682 ):
+ 683 Cog().process_string(infile)
+ 684
+ 685 def test_no_error_if_error_not_called(self):
+ 686 infile = """\
+ 687 --[[[cog
+ 688 --import cog
+ 689 --for i in range(3):
+ 690 -- if i > 10:
+ 691 -- cog.error("Something is amiss!")
+ 692 -- cog.out("xx%d\\n" % i)
+ 693 --]]]
+ 694 xx0
+ 695 xx1
+ 696 xx2
+ 697 --[[[end]]]
+ 698 """
+ 699
+ 700 infile = reindent_block(infile)
+ 701 self.assertEqual(Cog().process_string(infile), infile)
+ 702
+ 703
+ 704class CogGeneratorGetCodeTests(TestCase):
+ 705 """Tests for CogGenerator.getCode()."""
+ 706
+ 707 def setUp(self):
+ 708 # All tests get a generator to use, and short same-length names for
+ 709 # the functions we're going to use.
+ 710 self.gen = CogGenerator()
+ 711 self.m = self.gen.parse_marker
+ 712 self.parse_line = self.gen.parse_line
+ 713
+ 714 def test_empty(self):
+ 715 self.m("// [[[cog")
+ 716 self.m("// ]]]")
+ 717 self.assertEqual(self.gen.get_code(), "")
+ 718
+ 719 def test_simple(self):
+ 720 self.m("// [[[cog")
+ 721 self.parse_line(' print "hello"')
+ 722 self.parse_line(' print "bye"')
+ 723 self.m("// ]]]")
+ 724 self.assertEqual(self.gen.get_code(), 'print "hello"\nprint "bye"')
725
- 726 def testCompressed2(self):
+ 726 def test_compressed1(self):
727 # For a while, I supported compressed code blocks, but no longer.
728 self.m('// [[[cog: print """')
- 729 self.l('hello')
- 730 self.l('bye')
+ 729 self.parse_line("// hello")
+ 730 self.parse_line("// bye")
731 self.m('// """)]]]')
- 732 self.assertEqual(self.gen.getCode(), 'hello\nbye')
+ 732 self.assertEqual(self.gen.get_code(), "hello\nbye")
733
- 734 def testCompressed3(self):
+ 734 def test_compressed2(self):
735 # For a while, I supported compressed code blocks, but no longer.
- 736 self.m('// [[[cog')
- 737 self.l('print """hello')
- 738 self.l('bye')
+ 736 self.m('// [[[cog: print """')
+ 737 self.parse_line("hello")
+ 738 self.parse_line("bye")
739 self.m('// """)]]]')
- 740 self.assertEqual(self.gen.getCode(), 'print """hello\nbye')
+ 740 self.assertEqual(self.gen.get_code(), "hello\nbye")
741
- 742 def testCompressed4(self):
+ 742 def test_compressed3(self):
743 # For a while, I supported compressed code blocks, but no longer.
- 744 self.m('// [[[cog: print """')
- 745 self.l('hello')
- 746 self.l('bye""")')
- 747 self.m('// ]]]')
- 748 self.assertEqual(self.gen.getCode(), 'hello\nbye""")')
+ 744 self.m("// [[[cog")
+ 745 self.parse_line('print """hello')
+ 746 self.parse_line("bye")
+ 747 self.m('// """)]]]')
+ 748 self.assertEqual(self.gen.get_code(), 'print """hello\nbye')
749
- 750 def testNoCommonPrefixForMarkers(self):
- 751 # It's important to be able to use #if 0 to hide lines from a
- 752 # C++ compiler.
- 753 self.m('#if 0 //[[[cog')
- 754 self.l('\timport cog, sys')
- 755 self.l('')
- 756 self.l('\tprint sys.argv')
- 757 self.m('#endif //]]]')
- 758 self.assertEqual(self.gen.getCode(), 'import cog, sys\n\nprint sys.argv')
- 759
- 760
- 761class TestCaseWithTempDir(TestCase):
- 762
- 763 def newCog(self):
- 764 """ Initialize the cog members for another run.
- 765 """
- 766 # Create a cog engine, and catch its output.
- 767 self.cog = Cog()
- 768 self.output = io.StringIO()
- 769 self.cog.setOutput(stdout=self.output, stderr=self.output)
- 770
- 771 def setUp(self):
- 772 # Create a temporary directory.
- 773 self.tempdir = os.path.join(tempfile.gettempdir(), 'testcog_tempdir_' + str(random.random())[2:])
- 774 os.mkdir(self.tempdir)
- 775 self.olddir = os.getcwd()
- 776 os.chdir(self.tempdir)
- 777 self.newCog()
- 778
- 779 def tearDown(self):
- 780 os.chdir(self.olddir)
- 781 # Get rid of the temporary directory.
- 782 shutil.rmtree(self.tempdir)
- 783
- 784 def assertFilesSame(self, sFName1, sFName2):
- 785 with open(os.path.join(self.tempdir, sFName1), 'rb') as f1:
- 786 text1 = f1.read()
- 787 with open(os.path.join(self.tempdir, sFName2), 'rb') as f2:
- 788 text2 = f2.read()
- 789 self.assertEqual(text1, text2)
- 790
- 791 def assertFileContent(self, fname, content):
- 792 absname = os.path.join(self.tempdir, fname)
- 793 with open(absname, 'rb') as f:
- 794 file_content = f.read()
- 795 self.assertEqual(file_content, content.encode("utf-8"))
- 796
- 797
- 798class ArgumentHandlingTests(TestCaseWithTempDir):
- 799
- 800 def testArgumentFailure(self):
- 801 # Return value 2 means usage problem.
- 802 self.assertEqual(self.cog.main(['argv0', '-j']), 2)
- 803 output = self.output.getvalue()
- 804 self.assertIn("option -j not recognized", output)
- 805 with self.assertRaisesRegex(CogUsageError, r"^No files to process$"):
- 806 self.cog.callableMain(['argv0'])
- 807 with self.assertRaisesRegex(CogUsageError, r"^option -j not recognized$"):
- 808 self.cog.callableMain(['argv0', '-j'])
- 809
- 810 def testNoDashOAndAtFile(self):
- 811 makeFiles({"cogfiles.txt": "# Please run cog"})
- 812 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with @file$"):
- 813 self.cog.callableMain(['argv0', '-o', 'foo', '@cogfiles.txt'])
- 814
- 815 def testNoDashOAndAmpFile(self):
- 816 makeFiles({"cogfiles.txt": "# Please run cog"})
- 817 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with &file$"):
- 818 self.cog.callableMain(['argv0', '-o', 'foo', '&cogfiles.txt'])
- 819
- 820 def testDashV(self):
- 821 self.assertEqual(self.cog.main(['argv0', '-v']), 0)
- 822 output = self.output.getvalue()
- 823 self.assertEqual('Cog version %s\n' % __version__, output)
- 824
- 825 def producesHelp(self, args):
- 826 self.newCog()
- 827 argv = ['argv0'] + args.split()
- 828 self.assertEqual(self.cog.main(argv), 0)
- 829 self.assertEqual(usage, self.output.getvalue())
- 830
- 831 def testDashH(self):
- 832 # -h or -? anywhere on the command line should just print help.
- 833 self.producesHelp("-h")
- 834 self.producesHelp("-?")
- 835 self.producesHelp("fooey.txt -h")
- 836 self.producesHelp("-o -r @fooey.txt -? @booey.txt")
+ 750 def test_compressed4(self):
+ 751 # For a while, I supported compressed code blocks, but no longer.
+ 752 self.m('// [[[cog: print """')
+ 753 self.parse_line("hello")
+ 754 self.parse_line('bye""")')
+ 755 self.m("// ]]]")
+ 756 self.assertEqual(self.gen.get_code(), 'hello\nbye""")')
+ 757
+ 758 def test_no_common_prefix_for_markers(self):
+ 759 # It's important to be able to use #if 0 to hide lines from a
+ 760 # C++ compiler.
+ 761 self.m("#if 0 //[[[cog")
+ 762 self.parse_line("\timport cog, sys")
+ 763 self.parse_line("")
+ 764 self.parse_line("\tprint sys.argv")
+ 765 self.m("#endif //]]]")
+ 766 self.assertEqual(self.gen.get_code(), "import cog, sys\n\nprint sys.argv")
+ 767
+ 768
+ 769class TestCaseWithTempDir(TestCase):
+ 770 def new_cog(self):
+ 771 """Initialize the cog members for another run."""
+ 772 # Create a cog engine, and catch its output.
+ 773 self.cog = Cog()
+ 774 self.output = io.StringIO()
+ 775 self.cog.set_output(stdout=self.output, stderr=self.output)
+ 776
+ 777 def setUp(self):
+ 778 # Create a temporary directory.
+ 779 self.tempdir = os.path.join(
+ 780 tempfile.gettempdir(), "testcog_tempdir_" + str(random.random())[2:]
+ 781 )
+ 782 os.mkdir(self.tempdir)
+ 783 self.olddir = os.getcwd()
+ 784 os.chdir(self.tempdir)
+ 785 self.new_cog()
+ 786
+ 787 def tearDown(self):
+ 788 os.chdir(self.olddir)
+ 789 # Get rid of the temporary directory.
+ 790 shutil.rmtree(self.tempdir)
+ 791
+ 792 def assertFilesSame(self, file_name1, file_name2):
+ 793 with open(os.path.join(self.tempdir, file_name1), "rb") as f1:
+ 794 text1 = f1.read()
+ 795 with open(os.path.join(self.tempdir, file_name2), "rb") as f2:
+ 796 text2 = f2.read()
+ 797 self.assertEqual(text1, text2)
+ 798
+ 799 def assertFileContent(self, fname, content):
+ 800 absname = os.path.join(self.tempdir, fname)
+ 801 with open(absname, "rb") as f:
+ 802 file_content = f.read()
+ 803 self.assertEqual(file_content, content.encode("utf-8"))
+ 804
+ 805
+ 806class ArgumentHandlingTests(TestCaseWithTempDir):
+ 807 def test_argument_failure(self):
+ 808 # Return value 2 means usage problem.
+ 809 self.assertEqual(self.cog.main(["argv0", "-j"]), 2)
+ 810 output = self.output.getvalue()
+ 811 self.assertIn("option -j not recognized", output)
+ 812 with self.assertRaisesRegex(CogUsageError, r"^No files to process$"):
+ 813 self.cog.callable_main(["argv0"])
+ 814 with self.assertRaisesRegex(CogUsageError, r"^option -j not recognized$"):
+ 815 self.cog.callable_main(["argv0", "-j"])
+ 816
+ 817 def test_no_dash_o_and_at_file(self):
+ 818 make_files({"cogfiles.txt": "# Please run cog"})
+ 819 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with @file$"):
+ 820 self.cog.callable_main(["argv0", "-o", "foo", "@cogfiles.txt"])
+ 821
+ 822 def test_no_dash_o_and_amp_file(self):
+ 823 make_files({"cogfiles.txt": "# Please run cog"})
+ 824 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with &file$"):
+ 825 self.cog.callable_main(["argv0", "-o", "foo", "&cogfiles.txt"])
+ 826
+ 827 def test_dash_v(self):
+ 828 self.assertEqual(self.cog.main(["argv0", "-v"]), 0)
+ 829 output = self.output.getvalue()
+ 830 self.assertEqual("Cog version %s\n" % __version__, output)
+ 831
+ 832 def produces_help(self, args):
+ 833 self.new_cog()
+ 834 argv = ["argv0"] + args.split()
+ 835 self.assertEqual(self.cog.main(argv), 0)
+ 836 self.assertEqual(usage, self.output.getvalue())
837
- 838 def testDashOAndDashR(self):
- 839 d = {
- 840 'cogfile.txt': """\
- 841 # Please run cog
- 842 """
- 843 }
+ 838 def test_dash_h(self):
+ 839 # -h or -? anywhere on the command line should just print help.
+ 840 self.produces_help("-h")
+ 841 self.produces_help("-?")
+ 842 self.produces_help("fooey.txt -h")
+ 843 self.produces_help("-o -r @fooey.txt -? @booey.txt")
844
- 845 makeFiles(d)
- 846 with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with -r \(they are opposites\)$"):
- 847 self.cog.callableMain(['argv0', '-o', 'foo', '-r', 'cogfile.txt'])
- 848
- 849 def testDashZ(self):
- 850 d = {
- 851 'test.cog': """\
- 852 // This is my C++ file.
- 853 //[[[cog
- 854 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
- 855 for fn in fnames:
- 856 cog.outl("void %s();" % fn)
- 857 //]]]
- 858 """,
- 859
- 860 'test.out': """\
+ 845 def test_dash_o_and_dash_r(self):
+ 846 d = {
+ 847 "cogfile.txt": """\
+ 848 # Please run cog
+ 849 """
+ 850 }
+ 851
+ 852 make_files(d)
+ 853 with self.assertRaisesRegex(
+ 854 CogUsageError, r"^Can't use -o with -r \(they are opposites\)$"
+ 855 ):
+ 856 self.cog.callable_main(["argv0", "-o", "foo", "-r", "cogfile.txt"])
+ 857
+ 858 def test_dash_z(self):
+ 859 d = {
+ 860 "test.cog": """\
861 // This is my C++ file.
862 //[[[cog
863 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
864 for fn in fnames:
865 cog.outl("void %s();" % fn)
866 //]]]
- 867 void DoSomething();
- 868 void DoAnotherThing();
- 869 void DoLastThing();
- 870 """,
- 871 }
- 872
- 873 makeFiles(d)
- 874 with self.assertRaisesRegex(CogError, r"^test.cog\(6\): Missing '\[\[\[end\]\]\]' before end of file.$"):
- 875 self.cog.callableMain(['argv0', '-r', 'test.cog'])
- 876 self.newCog()
- 877 self.cog.callableMain(['argv0', '-r', '-z', 'test.cog'])
- 878 self.assertFilesSame('test.cog', 'test.out')
- 879
- 880 def testBadDashD(self):
- 881 with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"):
- 882 self.cog.callableMain(['argv0', '-Dfooey', 'cog.txt'])
- 883 with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"):
- 884 self.cog.callableMain(['argv0', '-D', 'fooey', 'cog.txt'])
- 885
- 886 def testBadMarkers(self):
- 887 with self.assertRaisesRegex(CogUsageError, r"^--markers requires 3 values separated by spaces, could not parse 'X'$"):
- 888 self.cog.callableMain(['argv0', '--markers=X'])
- 889 with self.assertRaisesRegex(CogUsageError, r"^--markers requires 3 values separated by spaces, could not parse 'A B C D'$"):
- 890 self.cog.callableMain(['argv0', '--markers=A B C D'])
- 891
- 892
- 893class TestMain(TestCaseWithTempDir):
- 894 def setUp(self):
- 895 super().setUp()
- 896 self.old_argv = sys.argv[:]
- 897 self.old_stderr = sys.stderr
- 898 sys.stderr = io.StringIO()
- 899
- 900 def tearDown(self):
- 901 sys.stderr = self.old_stderr
- 902 sys.argv = self.old_argv
- 903 sys.modules.pop('mycode', None)
- 904 super().tearDown()
- 905
- 906 def test_main_function(self):
- 907 sys.argv = ["argv0", "-Z"]
- 908 ret = main()
- 909 self.assertEqual(ret, 2)
- 910 stderr = sys.stderr.getvalue()
- 911 self.assertEqual(stderr, 'option -Z not recognized\n(for help use -h)\n')
- 912
- 913 files = {
- 914 'test.cog': """\
- 915 //[[[cog
- 916 def func():
- 917 import mycode
- 918 mycode.boom()
- 919 //]]]
- 920 //[[[end]]]
- 921 -----
- 922 //[[[cog
- 923 func()
- 924 //]]]
- 925 //[[[end]]]
- 926 """,
- 927
- 928 'mycode.py': """\
- 929 def boom():
- 930 [][0]
- 931 """,
- 932 }
- 933
- 934 def test_error_report(self):
- 935 self.check_error_report()
- 936
- 937 def test_error_report_with_prologue(self):
- 938 self.check_error_report("-p", "#1\n#2")
- 939
- 940 def check_error_report(self, *args):
- 941 """Check that the error report is right."""
- 942 makeFiles(self.files)
- 943 sys.argv = ["argv0"] + list(args) + ["-r", "test.cog"]
- 944 main()
- 945 expected = reindentBlock("""\
- 946 Traceback (most recent call last):
- 947 File "test.cog", line 9, in <module>
- 948 func()
- 949 File "test.cog", line 4, in func
- 950 mycode.boom()
- 951 File "MYCODE", line 2, in boom
- 952 [][0]
- 953 IndexError: list index out of range
- 954 """)
- 955 expected = expected.replace("MYCODE", os.path.abspath("mycode.py"))
- 956 assert expected == sys.stderr.getvalue()
- 957
- 958 def test_error_in_prologue(self):
- 959 makeFiles(self.files)
- 960 sys.argv = ["argv0", "-p", "import mycode; mycode.boom()", "-r", "test.cog"]
- 961 main()
- 962 expected = reindentBlock("""\
- 963 Traceback (most recent call last):
- 964 File "<prologue>", line 1, in <module>
- 965 import mycode; mycode.boom()
+ 867 """,
+ 868 "test.out": """\
+ 869 // This is my C++ file.
+ 870 //[[[cog
+ 871 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
+ 872 for fn in fnames:
+ 873 cog.outl("void %s();" % fn)
+ 874 //]]]
+ 875 void DoSomething();
+ 876 void DoAnotherThing();
+ 877 void DoLastThing();
+ 878 """,
+ 879 }
+ 880
+ 881 make_files(d)
+ 882 with self.assertRaisesRegex(
+ 883 CogError, r"^test.cog\(6\): Missing '\[\[\[end\]\]\]' before end of file.$"
+ 884 ):
+ 885 self.cog.callable_main(["argv0", "-r", "test.cog"])
+ 886 self.new_cog()
+ 887 self.cog.callable_main(["argv0", "-r", "-z", "test.cog"])
+ 888 self.assertFilesSame("test.cog", "test.out")
+ 889
+ 890 def test_bad_dash_d(self):
+ 891 with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"):
+ 892 self.cog.callable_main(["argv0", "-Dfooey", "cog.txt"])
+ 893 with self.assertRaisesRegex(CogUsageError, r"^-D takes a name=value argument$"):
+ 894 self.cog.callable_main(["argv0", "-D", "fooey", "cog.txt"])
+ 895
+ 896 def test_bad_markers(self):
+ 897 with self.assertRaisesRegex(
+ 898 CogUsageError,
+ 899 r"^--markers requires 3 values separated by spaces, could not parse 'X'$",
+ 900 ):
+ 901 self.cog.callable_main(["argv0", "--markers=X"])
+ 902 with self.assertRaisesRegex(
+ 903 CogUsageError,
+ 904 r"^--markers requires 3 values separated by spaces, could not parse 'A B C D'$",
+ 905 ):
+ 906 self.cog.callable_main(["argv0", "--markers=A B C D"])
+ 907
+ 908
+ 909class TestMain(TestCaseWithTempDir):
+ 910 def setUp(self):
+ 911 super().setUp()
+ 912 self.old_argv = sys.argv[:]
+ 913 self.old_stderr = sys.stderr
+ 914 sys.stderr = io.StringIO()
+ 915
+ 916 def tearDown(self):
+ 917 sys.stderr = self.old_stderr
+ 918 sys.argv = self.old_argv
+ 919 sys.modules.pop("mycode", None)
+ 920 super().tearDown()
+ 921
+ 922 def test_main_function(self):
+ 923 sys.argv = ["argv0", "-Z"]
+ 924 ret = main()
+ 925 self.assertEqual(ret, 2)
+ 926 stderr = sys.stderr.getvalue()
+ 927 self.assertEqual(stderr, "option -Z not recognized\n(for help use -h)\n")
+ 928
+ 929 files = {
+ 930 "test.cog": """\
+ 931 //[[[cog
+ 932 def func():
+ 933 import mycode
+ 934 mycode.boom()
+ 935 //]]]
+ 936 //[[[end]]]
+ 937 -----
+ 938 //[[[cog
+ 939 func()
+ 940 //]]]
+ 941 //[[[end]]]
+ 942 """,
+ 943 "mycode.py": """\
+ 944 def boom():
+ 945 [][0]
+ 946 """,
+ 947 }
+ 948
+ 949 def test_error_report(self):
+ 950 self.check_error_report()
+ 951
+ 952 def test_error_report_with_prologue(self):
+ 953 self.check_error_report("-p", "#1\n#2")
+ 954
+ 955 def check_error_report(self, *args):
+ 956 """Check that the error report is right."""
+ 957 make_files(self.files)
+ 958 sys.argv = ["argv0"] + list(args) + ["-r", "test.cog"]
+ 959 main()
+ 960 expected = reindent_block("""\
+ 961 Traceback (most recent call last):
+ 962 File "test.cog", line 9, in <module>
+ 963 func()
+ 964 File "test.cog", line 4, in func
+ 965 mycode.boom()
966 File "MYCODE", line 2, in boom
967 [][0]
968 IndexError: list index out of range
@@ -1054,1690 +1054,1681 @@
970 expected = expected.replace("MYCODE", os.path.abspath("mycode.py"))
971 assert expected == sys.stderr.getvalue()
972
- 973
- 974
- 975class TestFileHandling(TestCaseWithTempDir):
- 976
- 977 def testSimple(self):
- 978 d = {
- 979 'test.cog': """\
- 980 // This is my C++ file.
- 981 //[[[cog
- 982 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
- 983 for fn in fnames:
- 984 cog.outl("void %s();" % fn)
- 985 //]]]
- 986 //[[[end]]]
- 987 """,
+ 973 def test_error_in_prologue(self):
+ 974 make_files(self.files)
+ 975 sys.argv = ["argv0", "-p", "import mycode; mycode.boom()", "-r", "test.cog"]
+ 976 main()
+ 977 expected = reindent_block("""\
+ 978 Traceback (most recent call last):
+ 979 File "<prologue>", line 1, in <module>
+ 980 import mycode; mycode.boom()
+ 981 File "MYCODE", line 2, in boom
+ 982 [][0]
+ 983 IndexError: list index out of range
+ 984 """)
+ 985 expected = expected.replace("MYCODE", os.path.abspath("mycode.py"))
+ 986 assert expected == sys.stderr.getvalue()
+ 987
988
- 989 'test.out': """\
- 990 // This is my C++ file.
- 991 //[[[cog
- 992 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
- 993 for fn in fnames:
- 994 cog.outl("void %s();" % fn)
- 995 //]]]
- 996 void DoSomething();
- 997 void DoAnotherThing();
- 998 void DoLastThing();
+ 989class TestFileHandling(TestCaseWithTempDir):
+ 990 def test_simple(self):
+ 991 d = {
+ 992 "test.cog": """\
+ 993 // This is my C++ file.
+ 994 //[[[cog
+ 995 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
+ 996 for fn in fnames:
+ 997 cog.outl("void %s();" % fn)
+ 998 //]]]
999 //[[[end]]]
1000 """,
- 1001 }
- 1002
- 1003 makeFiles(d)
- 1004 self.cog.callableMain(['argv0', '-r', 'test.cog'])
- 1005 self.assertFilesSame('test.cog', 'test.out')
- 1006 output = self.output.getvalue()
- 1007 self.assertIn("(changed)", output)
- 1008
- 1009 def testPrintOutput(self):
- 1010 d = {
- 1011 'test.cog': """\
- 1012 // This is my C++ file.
- 1013 //[[[cog
- 1014 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
- 1015 for fn in fnames:
- 1016 print("void %s();" % fn)
- 1017 //]]]
- 1018 //[[[end]]]
- 1019 """,
+ 1001 "test.out": """\
+ 1002 // This is my C++ file.
+ 1003 //[[[cog
+ 1004 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
+ 1005 for fn in fnames:
+ 1006 cog.outl("void %s();" % fn)
+ 1007 //]]]
+ 1008 void DoSomething();
+ 1009 void DoAnotherThing();
+ 1010 void DoLastThing();
+ 1011 //[[[end]]]
+ 1012 """,
+ 1013 }
+ 1014
+ 1015 make_files(d)
+ 1016 self.cog.callable_main(["argv0", "-r", "test.cog"])
+ 1017 self.assertFilesSame("test.cog", "test.out")
+ 1018 output = self.output.getvalue()
+ 1019 self.assertIn("(changed)", output)
1020
- 1021 'test.out': """\
- 1022 // This is my C++ file.
- 1023 //[[[cog
- 1024 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
- 1025 for fn in fnames:
- 1026 print("void %s();" % fn)
- 1027 //]]]
- 1028 void DoSomething();
- 1029 void DoAnotherThing();
- 1030 void DoLastThing();
- 1031 //[[[end]]]
- 1032 """,
- 1033 }
- 1034
- 1035 makeFiles(d)
- 1036 self.cog.callableMain(['argv0', '-rP', 'test.cog'])
- 1037 self.assertFilesSame('test.cog', 'test.out')
- 1038 output = self.output.getvalue()
- 1039 self.assertIn("(changed)", output)
- 1040
- 1041 def testWildcards(self):
- 1042 d = {
- 1043 'test.cog': """\
- 1044 // This is my C++ file.
- 1045 //[[[cog
- 1046 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
- 1047 for fn in fnames:
- 1048 cog.outl("void %s();" % fn)
- 1049 //]]]
- 1050 //[[[end]]]
- 1051 """,
- 1052
- 1053 'test2.cog': """\
- 1054 // This is my C++ file.
- 1055 //[[[cog
- 1056 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
- 1057 for fn in fnames:
- 1058 cog.outl("void %s();" % fn)
- 1059 //]]]
- 1060 //[[[end]]]
- 1061 """,
- 1062
- 1063 'test.out': """\
+ 1021 def test_print_output(self):
+ 1022 d = {
+ 1023 "test.cog": """\
+ 1024 // This is my C++ file.
+ 1025 //[[[cog
+ 1026 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
+ 1027 for fn in fnames:
+ 1028 print("void %s();" % fn)
+ 1029 //]]]
+ 1030 //[[[end]]]
+ 1031 """,
+ 1032 "test.out": """\
+ 1033 // This is my C++ file.
+ 1034 //[[[cog
+ 1035 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
+ 1036 for fn in fnames:
+ 1037 print("void %s();" % fn)
+ 1038 //]]]
+ 1039 void DoSomething();
+ 1040 void DoAnotherThing();
+ 1041 void DoLastThing();
+ 1042 //[[[end]]]
+ 1043 """,
+ 1044 }
+ 1045
+ 1046 make_files(d)
+ 1047 self.cog.callable_main(["argv0", "-rP", "test.cog"])
+ 1048 self.assertFilesSame("test.cog", "test.out")
+ 1049 output = self.output.getvalue()
+ 1050 self.assertIn("(changed)", output)
+ 1051
+ 1052 def test_wildcards(self):
+ 1053 d = {
+ 1054 "test.cog": """\
+ 1055 // This is my C++ file.
+ 1056 //[[[cog
+ 1057 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
+ 1058 for fn in fnames:
+ 1059 cog.outl("void %s();" % fn)
+ 1060 //]]]
+ 1061 //[[[end]]]
+ 1062 """,
+ 1063 "test2.cog": """\
1064 // This is my C++ file.
1065 //[[[cog
1066 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
1067 for fn in fnames:
1068 cog.outl("void %s();" % fn)
1069 //]]]
- 1070 void DoSomething();
- 1071 void DoAnotherThing();
- 1072 void DoLastThing();
- 1073 //[[[end]]]
- 1074 """,
- 1075
- 1076 'not_this_one.cog': """\
- 1077 // This is my C++ file.
- 1078 //[[[cog
- 1079 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
- 1080 for fn in fnames:
- 1081 cog.outl("void %s();" % fn)
- 1082 //]]]
- 1083 //[[[end]]]
- 1084 """,
- 1085
- 1086 'not_this_one.out': """\
- 1087 // This is my C++ file.
- 1088 //[[[cog
- 1089 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
- 1090 for fn in fnames:
- 1091 cog.outl("void %s();" % fn)
- 1092 //]]]
- 1093 //[[[end]]]
- 1094 """,
- 1095 }
- 1096
- 1097 makeFiles(d)
- 1098 self.cog.callableMain(['argv0', '-r', 't*.cog'])
- 1099 self.assertFilesSame('test.cog', 'test.out')
- 1100 self.assertFilesSame('test2.cog', 'test.out')
- 1101 self.assertFilesSame('not_this_one.cog', 'not_this_one.out')
- 1102 output = self.output.getvalue()
- 1103 self.assertIn("(changed)", output)
- 1104
- 1105 def testOutputFile(self):
- 1106 # -o sets the output file.
- 1107 d = {
- 1108 'test.cog': """\
- 1109 // This is my C++ file.
- 1110 //[[[cog
- 1111 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
- 1112 for fn in fnames:
- 1113 cog.outl("void %s();" % fn)
- 1114 //]]]
- 1115 //[[[end]]]
- 1116 """,
- 1117
- 1118 'test.out': """\
- 1119 // This is my C++ file.
- 1120 //[[[cog
- 1121 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
- 1122 for fn in fnames:
- 1123 cog.outl("void %s();" % fn)
- 1124 //]]]
- 1125 void DoSomething();
- 1126 void DoAnotherThing();
- 1127 void DoLastThing();
- 1128 //[[[end]]]
- 1129 """,
- 1130 }
- 1131
- 1132 makeFiles(d)
- 1133 self.cog.callableMain(['argv0', '-o', 'in/a/dir/test.cogged', 'test.cog'])
- 1134 self.assertFilesSame('in/a/dir/test.cogged', 'test.out')
- 1135
- 1136 def testAtFile(self):
- 1137 d = {
- 1138 'one.cog': """\
- 1139 //[[[cog
- 1140 cog.outl("hello world")
- 1141 //]]]
- 1142 //[[[end]]]
- 1143 """,
- 1144
- 1145 'one.out': """\
- 1146 //[[[cog
- 1147 cog.outl("hello world")
- 1148 //]]]
- 1149 hello world
- 1150 //[[[end]]]
- 1151 """,
- 1152
- 1153 'two.cog': """\
- 1154 //[[[cog
- 1155 cog.outl("goodbye cruel world")
- 1156 //]]]
- 1157 //[[[end]]]
- 1158 """,
- 1159
- 1160 'two.out': """\
- 1161 //[[[cog
- 1162 cog.outl("goodbye cruel world")
- 1163 //]]]
- 1164 goodbye cruel world
- 1165 //[[[end]]]
- 1166 """,
- 1167
- 1168 'cogfiles.txt': """\
- 1169 # Please run cog
- 1170 one.cog
- 1171
- 1172 two.cog
- 1173 """
- 1174 }
- 1175
- 1176 makeFiles(d)
- 1177 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
- 1178 self.assertFilesSame('one.cog', 'one.out')
- 1179 self.assertFilesSame('two.cog', 'two.out')
- 1180 output = self.output.getvalue()
- 1181 self.assertIn("(changed)", output)
- 1182
- 1183 def testNestedAtFile(self):
- 1184 d = {
- 1185 'one.cog': """\
- 1186 //[[[cog
- 1187 cog.outl("hello world")
- 1188 //]]]
- 1189 //[[[end]]]
- 1190 """,
- 1191
- 1192 'one.out': """\
- 1193 //[[[cog
- 1194 cog.outl("hello world")
- 1195 //]]]
- 1196 hello world
- 1197 //[[[end]]]
- 1198 """,
- 1199
- 1200 'two.cog': """\
+ 1070 //[[[end]]]
+ 1071 """,
+ 1072 "test.out": """\
+ 1073 // This is my C++ file.
+ 1074 //[[[cog
+ 1075 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
+ 1076 for fn in fnames:
+ 1077 cog.outl("void %s();" % fn)
+ 1078 //]]]
+ 1079 void DoSomething();
+ 1080 void DoAnotherThing();
+ 1081 void DoLastThing();
+ 1082 //[[[end]]]
+ 1083 """,
+ 1084 "not_this_one.cog": """\
+ 1085 // This is my C++ file.
+ 1086 //[[[cog
+ 1087 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
+ 1088 for fn in fnames:
+ 1089 cog.outl("void %s();" % fn)
+ 1090 //]]]
+ 1091 //[[[end]]]
+ 1092 """,
+ 1093 "not_this_one.out": """\
+ 1094 // This is my C++ file.
+ 1095 //[[[cog
+ 1096 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
+ 1097 for fn in fnames:
+ 1098 cog.outl("void %s();" % fn)
+ 1099 //]]]
+ 1100 //[[[end]]]
+ 1101 """,
+ 1102 }
+ 1103
+ 1104 make_files(d)
+ 1105 self.cog.callable_main(["argv0", "-r", "t*.cog"])
+ 1106 self.assertFilesSame("test.cog", "test.out")
+ 1107 self.assertFilesSame("test2.cog", "test.out")
+ 1108 self.assertFilesSame("not_this_one.cog", "not_this_one.out")
+ 1109 output = self.output.getvalue()
+ 1110 self.assertIn("(changed)", output)
+ 1111
+ 1112 def test_output_file(self):
+ 1113 # -o sets the output file.
+ 1114 d = {
+ 1115 "test.cog": """\
+ 1116 // This is my C++ file.
+ 1117 //[[[cog
+ 1118 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
+ 1119 for fn in fnames:
+ 1120 cog.outl("void %s();" % fn)
+ 1121 //]]]
+ 1122 //[[[end]]]
+ 1123 """,
+ 1124 "test.out": """\
+ 1125 // This is my C++ file.
+ 1126 //[[[cog
+ 1127 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
+ 1128 for fn in fnames:
+ 1129 cog.outl("void %s();" % fn)
+ 1130 //]]]
+ 1131 void DoSomething();
+ 1132 void DoAnotherThing();
+ 1133 void DoLastThing();
+ 1134 //[[[end]]]
+ 1135 """,
+ 1136 }
+ 1137
+ 1138 make_files(d)
+ 1139 self.cog.callable_main(["argv0", "-o", "in/a/dir/test.cogged", "test.cog"])
+ 1140 self.assertFilesSame("in/a/dir/test.cogged", "test.out")
+ 1141
+ 1142 def test_at_file(self):
+ 1143 d = {
+ 1144 "one.cog": """\
+ 1145 //[[[cog
+ 1146 cog.outl("hello world")
+ 1147 //]]]
+ 1148 //[[[end]]]
+ 1149 """,
+ 1150 "one.out": """\
+ 1151 //[[[cog
+ 1152 cog.outl("hello world")
+ 1153 //]]]
+ 1154 hello world
+ 1155 //[[[end]]]
+ 1156 """,
+ 1157 "two.cog": """\
+ 1158 //[[[cog
+ 1159 cog.outl("goodbye cruel world")
+ 1160 //]]]
+ 1161 //[[[end]]]
+ 1162 """,
+ 1163 "two.out": """\
+ 1164 //[[[cog
+ 1165 cog.outl("goodbye cruel world")
+ 1166 //]]]
+ 1167 goodbye cruel world
+ 1168 //[[[end]]]
+ 1169 """,
+ 1170 "cogfiles.txt": """\
+ 1171 # Please run cog
+ 1172 one.cog
+ 1173
+ 1174 two.cog
+ 1175 """,
+ 1176 }
+ 1177
+ 1178 make_files(d)
+ 1179 self.cog.callable_main(["argv0", "-r", "@cogfiles.txt"])
+ 1180 self.assertFilesSame("one.cog", "one.out")
+ 1181 self.assertFilesSame("two.cog", "two.out")
+ 1182 output = self.output.getvalue()
+ 1183 self.assertIn("(changed)", output)
+ 1184
+ 1185 def test_nested_at_file(self):
+ 1186 d = {
+ 1187 "one.cog": """\
+ 1188 //[[[cog
+ 1189 cog.outl("hello world")
+ 1190 //]]]
+ 1191 //[[[end]]]
+ 1192 """,
+ 1193 "one.out": """\
+ 1194 //[[[cog
+ 1195 cog.outl("hello world")
+ 1196 //]]]
+ 1197 hello world
+ 1198 //[[[end]]]
+ 1199 """,
+ 1200 "two.cog": """\
1201 //[[[cog
1202 cog.outl("goodbye cruel world")
1203 //]]]
1204 //[[[end]]]
1205 """,
- 1206
- 1207 'two.out': """\
- 1208 //[[[cog
- 1209 cog.outl("goodbye cruel world")
- 1210 //]]]
- 1211 goodbye cruel world
- 1212 //[[[end]]]
- 1213 """,
- 1214
- 1215 'cogfiles.txt': """\
- 1216 # Please run cog
- 1217 one.cog
- 1218 @cogfiles2.txt
- 1219 """,
- 1220
- 1221 'cogfiles2.txt': """\
- 1222 # This one too, please.
- 1223 two.cog
- 1224 """,
- 1225 }
- 1226
- 1227 makeFiles(d)
- 1228 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
- 1229 self.assertFilesSame('one.cog', 'one.out')
- 1230 self.assertFilesSame('two.cog', 'two.out')
- 1231 output = self.output.getvalue()
- 1232 self.assertIn("(changed)", output)
- 1233
- 1234 def testAtFileWithArgs(self):
- 1235 d = {
- 1236 'both.cog': """\
- 1237 //[[[cog
- 1238 cog.outl("one: %s" % ('one' in globals()))
- 1239 cog.outl("two: %s" % ('two' in globals()))
- 1240 //]]]
- 1241 //[[[end]]]
- 1242 """,
- 1243
- 1244 'one.out': """\
- 1245 //[[[cog
- 1246 cog.outl("one: %s" % ('one' in globals()))
- 1247 cog.outl("two: %s" % ('two' in globals()))
- 1248 //]]]
- 1249 one: True // ONE
- 1250 two: False // ONE
- 1251 //[[[end]]]
- 1252 """,
- 1253
- 1254 'two.out': """\
- 1255 //[[[cog
- 1256 cog.outl("one: %s" % ('one' in globals()))
- 1257 cog.outl("two: %s" % ('two' in globals()))
- 1258 //]]]
- 1259 one: False // TWO
- 1260 two: True // TWO
- 1261 //[[[end]]]
+ 1206 "two.out": """\
+ 1207 //[[[cog
+ 1208 cog.outl("goodbye cruel world")
+ 1209 //]]]
+ 1210 goodbye cruel world
+ 1211 //[[[end]]]
+ 1212 """,
+ 1213 "cogfiles.txt": """\
+ 1214 # Please run cog
+ 1215 one.cog
+ 1216 @cogfiles2.txt
+ 1217 """,
+ 1218 "cogfiles2.txt": """\
+ 1219 # This one too, please.
+ 1220 two.cog
+ 1221 """,
+ 1222 }
+ 1223
+ 1224 make_files(d)
+ 1225 self.cog.callable_main(["argv0", "-r", "@cogfiles.txt"])
+ 1226 self.assertFilesSame("one.cog", "one.out")
+ 1227 self.assertFilesSame("two.cog", "two.out")
+ 1228 output = self.output.getvalue()
+ 1229 self.assertIn("(changed)", output)
+ 1230
+ 1231 def test_at_file_with_args(self):
+ 1232 d = {
+ 1233 "both.cog": """\
+ 1234 //[[[cog
+ 1235 cog.outl("one: %s" % ('one' in globals()))
+ 1236 cog.outl("two: %s" % ('two' in globals()))
+ 1237 //]]]
+ 1238 //[[[end]]]
+ 1239 """,
+ 1240 "one.out": """\
+ 1241 //[[[cog
+ 1242 cog.outl("one: %s" % ('one' in globals()))
+ 1243 cog.outl("two: %s" % ('two' in globals()))
+ 1244 //]]]
+ 1245 one: True // ONE
+ 1246 two: False // ONE
+ 1247 //[[[end]]]
+ 1248 """,
+ 1249 "two.out": """\
+ 1250 //[[[cog
+ 1251 cog.outl("one: %s" % ('one' in globals()))
+ 1252 cog.outl("two: %s" % ('two' in globals()))
+ 1253 //]]]
+ 1254 one: False // TWO
+ 1255 two: True // TWO
+ 1256 //[[[end]]]
+ 1257 """,
+ 1258 "cogfiles.txt": """\
+ 1259 # Please run cog
+ 1260 both.cog -o in/a/dir/both.one -s ' // ONE' -D one=x
+ 1261 both.cog -o in/a/dir/both.two -s ' // TWO' -D two=x
1262 """,
- 1263
- 1264 'cogfiles.txt': """\
- 1265 # Please run cog
- 1266 both.cog -o in/a/dir/both.one -s ' // ONE' -D one=x
- 1267 both.cog -o in/a/dir/both.two -s ' // TWO' -D two=x
- 1268 """
- 1269 }
- 1270
- 1271 makeFiles(d)
- 1272 self.cog.callableMain(['argv0', '@cogfiles.txt'])
- 1273 self.assertFilesSame('in/a/dir/both.one', 'one.out')
- 1274 self.assertFilesSame('in/a/dir/both.two', 'two.out')
- 1275
- 1276 def testAtFileWithBadArgCombo(self):
- 1277 d = {
- 1278 'both.cog': """\
- 1279 //[[[cog
- 1280 cog.outl("one: %s" % ('one' in globals()))
- 1281 cog.outl("two: %s" % ('two' in globals()))
- 1282 //]]]
- 1283 //[[[end]]]
- 1284 """,
+ 1263 }
+ 1264
+ 1265 make_files(d)
+ 1266 self.cog.callable_main(["argv0", "@cogfiles.txt"])
+ 1267 self.assertFilesSame("in/a/dir/both.one", "one.out")
+ 1268 self.assertFilesSame("in/a/dir/both.two", "two.out")
+ 1269
+ 1270 def test_at_file_with_bad_arg_combo(self):
+ 1271 d = {
+ 1272 "both.cog": """\
+ 1273 //[[[cog
+ 1274 cog.outl("one: %s" % ('one' in globals()))
+ 1275 cog.outl("two: %s" % ('two' in globals()))
+ 1276 //]]]
+ 1277 //[[[end]]]
+ 1278 """,
+ 1279 "cogfiles.txt": """\
+ 1280 # Please run cog
+ 1281 both.cog
+ 1282 both.cog -d # This is bad: -r and -d
+ 1283 """,
+ 1284 }
1285
- 1286 'cogfiles.txt': """\
- 1287 # Please run cog
- 1288 both.cog
- 1289 both.cog -d # This is bad: -r and -d
- 1290 """
- 1291 }
+ 1286 make_files(d)
+ 1287 with self.assertRaisesRegex(
+ 1288 CogUsageError,
+ 1289 r"^Can't use -d with -r \(or you would delete all your source!\)$",
+ 1290 ):
+ 1291 self.cog.callable_main(["argv0", "-r", "@cogfiles.txt"])
1292
- 1293 makeFiles(d)
- 1294 with self.assertRaisesRegex(CogUsageError, r"^Can't use -d with -r \(or you would delete all your source!\)$"):
- 1295 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
- 1296
- 1297 def testAtFileWithTrickyFilenames(self):
- 1298 def fix_backslashes(files_txt):
- 1299 """Make the contents of a files.txt sensitive to the platform."""
- 1300 if sys.platform != "win32":
- 1301 files_txt = files_txt.replace("\\", "/")
- 1302 return files_txt
- 1303
- 1304 d = {
- 1305 'one 1.cog': """\
- 1306 //[[[cog cog.outl("hello world") ]]]
+ 1293 def test_at_file_with_tricky_filenames(self):
+ 1294 def fix_backslashes(files_txt):
+ 1295 """Make the contents of a files.txt sensitive to the platform."""
+ 1296 if sys.platform != "win32":
+ 1297 files_txt = files_txt.replace("\\", "/")
+ 1298 return files_txt
+ 1299
+ 1300 d = {
+ 1301 "one 1.cog": """\
+ 1302 //[[[cog cog.outl("hello world") ]]]
+ 1303 """,
+ 1304 "one.out": """\
+ 1305 //[[[cog cog.outl("hello world") ]]]
+ 1306 hello world //xxx
1307 """,
- 1308
- 1309 'one.out': """\
- 1310 //[[[cog cog.outl("hello world") ]]]
- 1311 hello world //xxx
- 1312 """,
- 1313
- 1314 'subdir': {
- 1315 'subback.cog': """\
- 1316 //[[[cog cog.outl("down deep with backslashes") ]]]
- 1317 """,
- 1318
- 1319 'subfwd.cog': """\
- 1320 //[[[cog cog.outl("down deep with slashes") ]]]
- 1321 """,
- 1322 },
- 1323
- 1324 'subback.out': """\
- 1325 //[[[cog cog.outl("down deep with backslashes") ]]]
- 1326 down deep with backslashes //yyy
- 1327 """,
- 1328
- 1329 'subfwd.out': """\
- 1330 //[[[cog cog.outl("down deep with slashes") ]]]
- 1331 down deep with slashes //zzz
- 1332 """,
- 1333
- 1334 'cogfiles.txt': fix_backslashes("""\
- 1335 # Please run cog
- 1336 'one 1.cog' -s ' //xxx'
- 1337 subdir\\subback.cog -s ' //yyy'
- 1338 subdir/subfwd.cog -s ' //zzz'
- 1339 """)
- 1340 }
- 1341
- 1342 makeFiles(d)
- 1343 self.cog.callableMain(['argv0', '-z', '-r', '@cogfiles.txt'])
- 1344 self.assertFilesSame('one 1.cog', 'one.out')
- 1345 self.assertFilesSame('subdir/subback.cog', 'subback.out')
- 1346 self.assertFilesSame('subdir/subfwd.cog', 'subfwd.out')
- 1347
- 1348 def testAmpFile(self):
- 1349 d = {
- 1350 'code': {
- 1351 'files_to_cog': """\
- 1352 # A locally resolved file name.
- 1353 test.cog
- 1354 """,
- 1355
- 1356 'test.cog': """\
- 1357 //[[[cog
- 1358 import myampsubmodule
- 1359 //]]]
- 1360 //[[[end]]]
+ 1308 "subdir": {
+ 1309 "subback.cog": """\
+ 1310 //[[[cog cog.outl("down deep with backslashes") ]]]
+ 1311 """,
+ 1312 "subfwd.cog": """\
+ 1313 //[[[cog cog.outl("down deep with slashes") ]]]
+ 1314 """,
+ 1315 },
+ 1316 "subback.out": """\
+ 1317 //[[[cog cog.outl("down deep with backslashes") ]]]
+ 1318 down deep with backslashes //yyy
+ 1319 """,
+ 1320 "subfwd.out": """\
+ 1321 //[[[cog cog.outl("down deep with slashes") ]]]
+ 1322 down deep with slashes //zzz
+ 1323 """,
+ 1324 "cogfiles.txt": fix_backslashes("""\
+ 1325 # Please run cog
+ 1326 'one 1.cog' -s ' //xxx'
+ 1327 subdir\\subback.cog -s ' //yyy'
+ 1328 subdir/subfwd.cog -s ' //zzz'
+ 1329 """),
+ 1330 }
+ 1331
+ 1332 make_files(d)
+ 1333 self.cog.callable_main(["argv0", "-z", "-r", "@cogfiles.txt"])
+ 1334 self.assertFilesSame("one 1.cog", "one.out")
+ 1335 self.assertFilesSame("subdir/subback.cog", "subback.out")
+ 1336 self.assertFilesSame("subdir/subfwd.cog", "subfwd.out")
+ 1337
+ 1338 def test_amp_file(self):
+ 1339 d = {
+ 1340 "code": {
+ 1341 "files_to_cog": """\
+ 1342 # A locally resolved file name.
+ 1343 test.cog
+ 1344 """,
+ 1345 "test.cog": """\
+ 1346 //[[[cog
+ 1347 import myampsubmodule
+ 1348 //]]]
+ 1349 //[[[end]]]
+ 1350 """,
+ 1351 "test.out": """\
+ 1352 //[[[cog
+ 1353 import myampsubmodule
+ 1354 //]]]
+ 1355 Hello from myampsubmodule
+ 1356 //[[[end]]]
+ 1357 """,
+ 1358 "myampsubmodule.py": """\
+ 1359 import cog
+ 1360 cog.outl("Hello from myampsubmodule")
1361 """,
- 1362
- 1363 'test.out': """\
- 1364 //[[[cog
- 1365 import myampsubmodule
- 1366 //]]]
- 1367 Hello from myampsubmodule
- 1368 //[[[end]]]
- 1369 """,
- 1370
- 1371 'myampsubmodule.py': """\
- 1372 import cog
- 1373 cog.outl("Hello from myampsubmodule")
- 1374 """
- 1375 }
- 1376 }
- 1377
- 1378 makeFiles(d)
- 1379 print(os.path.abspath("code/test.out"))
- 1380 self.cog.callableMain(['argv0', '-r', '&code/files_to_cog'])
- 1381 self.assertFilesSame('code/test.cog', 'code/test.out')
- 1382
- 1383 def run_with_verbosity(self, verbosity):
- 1384 d = {
- 1385 'unchanged.cog': """\
- 1386 //[[[cog
- 1387 cog.outl("hello world")
- 1388 //]]]
- 1389 hello world
- 1390 //[[[end]]]
- 1391 """,
- 1392
- 1393 'changed.cog': """\
- 1394 //[[[cog
- 1395 cog.outl("goodbye cruel world")
- 1396 //]]]
- 1397 //[[[end]]]
- 1398 """,
- 1399
- 1400 'cogfiles.txt': """\
- 1401 unchanged.cog
- 1402 changed.cog
- 1403 """
- 1404 }
+ 1362 }
+ 1363 }
+ 1364
+ 1365 make_files(d)
+ 1366 print(os.path.abspath("code/test.out"))
+ 1367 self.cog.callable_main(["argv0", "-r", "&code/files_to_cog"])
+ 1368 self.assertFilesSame("code/test.cog", "code/test.out")
+ 1369
+ 1370 def run_with_verbosity(self, verbosity):
+ 1371 d = {
+ 1372 "unchanged.cog": """\
+ 1373 //[[[cog
+ 1374 cog.outl("hello world")
+ 1375 //]]]
+ 1376 hello world
+ 1377 //[[[end]]]
+ 1378 """,
+ 1379 "changed.cog": """\
+ 1380 //[[[cog
+ 1381 cog.outl("goodbye cruel world")
+ 1382 //]]]
+ 1383 //[[[end]]]
+ 1384 """,
+ 1385 "cogfiles.txt": """\
+ 1386 unchanged.cog
+ 1387 changed.cog
+ 1388 """,
+ 1389 }
+ 1390
+ 1391 make_files(d)
+ 1392 self.cog.callable_main(
+ 1393 ["argv0", "-r", "--verbosity=" + verbosity, "@cogfiles.txt"]
+ 1394 )
+ 1395 output = self.output.getvalue()
+ 1396 return output
+ 1397
+ 1398 def test_verbosity0(self):
+ 1399 output = self.run_with_verbosity("0")
+ 1400 self.assertEqual(output, "")
+ 1401
+ 1402 def test_verbosity1(self):
+ 1403 output = self.run_with_verbosity("1")
+ 1404 self.assertEqual(output, "Cogging changed.cog (changed)\n")
1405
- 1406 makeFiles(d)
- 1407 self.cog.callableMain(['argv0', '-r', '--verbosity='+verbosity, '@cogfiles.txt'])
- 1408 output = self.output.getvalue()
- 1409 return output
- 1410
- 1411 def test_verbosity0(self):
- 1412 output = self.run_with_verbosity("0")
- 1413 self.assertEqual(output, "")
- 1414
- 1415 def test_verbosity1(self):
- 1416 output = self.run_with_verbosity("1")
- 1417 self.assertEqual(output, "Cogging changed.cog (changed)\n")
- 1418
- 1419 def test_verbosity2(self):
- 1420 output = self.run_with_verbosity("2")
- 1421 self.assertEqual(output, "Cogging unchanged.cog\nCogging changed.cog (changed)\n")
- 1422
- 1423
- 1424class CogTestLineEndings(TestCaseWithTempDir):
- 1425 """Tests for -U option (force LF line-endings in output)."""
+ 1406 def test_verbosity2(self):
+ 1407 output = self.run_with_verbosity("2")
+ 1408 self.assertEqual(
+ 1409 output, "Cogging unchanged.cog\nCogging changed.cog (changed)\n"
+ 1410 )
+ 1411
+ 1412
+ 1413class CogTestLineEndings(TestCaseWithTempDir):
+ 1414 """Tests for -U option (force LF line-endings in output)."""
+ 1415
+ 1416 lines_in = [
+ 1417 "Some text.",
+ 1418 "//[[[cog",
+ 1419 'cog.outl("Cog text")',
+ 1420 "//]]]",
+ 1421 "gobbledegook.",
+ 1422 "//[[[end]]]",
+ 1423 "epilogue.",
+ 1424 "",
+ 1425 ]
1426
- 1427 lines_in = ['Some text.',
- 1428 '//[[[cog',
- 1429 'cog.outl("Cog text")',
- 1430 '//]]]',
- 1431 'gobbledegook.',
- 1432 '//[[[end]]]',
- 1433 'epilogue.',
- 1434 '']
- 1435
- 1436 lines_out = ['Some text.',
- 1437 '//[[[cog',
- 1438 'cog.outl("Cog text")',
- 1439 '//]]]',
- 1440 'Cog text',
- 1441 '//[[[end]]]',
- 1442 'epilogue.',
- 1443 '']
- 1444
- 1445 def testOutputNativeEol(self):
- 1446 makeFiles({'infile': '\n'.join(self.lines_in)})
- 1447 self.cog.callableMain(['argv0', '-o', 'outfile', 'infile'])
- 1448 self.assertFileContent('outfile', os.linesep.join(self.lines_out))
- 1449
- 1450 def testOutputLfEol(self):
- 1451 makeFiles({'infile': '\n'.join(self.lines_in)})
- 1452 self.cog.callableMain(['argv0', '-U', '-o', 'outfile', 'infile'])
- 1453 self.assertFileContent('outfile', '\n'.join(self.lines_out))
- 1454
- 1455 def testReplaceNativeEol(self):
- 1456 makeFiles({'test.cog': '\n'.join(self.lines_in)})
- 1457 self.cog.callableMain(['argv0', '-r', 'test.cog'])
- 1458 self.assertFileContent('test.cog', os.linesep.join(self.lines_out))
- 1459
- 1460 def testReplaceLfEol(self):
- 1461 makeFiles({'test.cog': '\n'.join(self.lines_in)})
- 1462 self.cog.callableMain(['argv0', '-U', '-r', 'test.cog'])
- 1463 self.assertFileContent('test.cog', '\n'.join(self.lines_out))
- 1464
- 1465
- 1466class CogTestCharacterEncoding(TestCaseWithTempDir):
- 1467
- 1468 def testSimple(self):
- 1469 d = {
- 1470 'test.cog': b"""\
- 1471 // This is my C++ file.
- 1472 //[[[cog
- 1473 cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)")
- 1474 //]]]
+ 1427 lines_out = [
+ 1428 "Some text.",
+ 1429 "//[[[cog",
+ 1430 'cog.outl("Cog text")',
+ 1431 "//]]]",
+ 1432 "Cog text",
+ 1433 "//[[[end]]]",
+ 1434 "epilogue.",
+ 1435 "",
+ 1436 ]
+ 1437
+ 1438 def test_output_native_eol(self):
+ 1439 make_files({"infile": "\n".join(self.lines_in)})
+ 1440 self.cog.callable_main(["argv0", "-o", "outfile", "infile"])
+ 1441 self.assertFileContent("outfile", os.linesep.join(self.lines_out))
+ 1442
+ 1443 def test_output_lf_eol(self):
+ 1444 make_files({"infile": "\n".join(self.lines_in)})
+ 1445 self.cog.callable_main(["argv0", "-U", "-o", "outfile", "infile"])
+ 1446 self.assertFileContent("outfile", "\n".join(self.lines_out))
+ 1447
+ 1448 def test_replace_native_eol(self):
+ 1449 make_files({"test.cog": "\n".join(self.lines_in)})
+ 1450 self.cog.callable_main(["argv0", "-r", "test.cog"])
+ 1451 self.assertFileContent("test.cog", os.linesep.join(self.lines_out))
+ 1452
+ 1453 def test_replace_lf_eol(self):
+ 1454 make_files({"test.cog": "\n".join(self.lines_in)})
+ 1455 self.cog.callable_main(["argv0", "-U", "-r", "test.cog"])
+ 1456 self.assertFileContent("test.cog", "\n".join(self.lines_out))
+ 1457
+ 1458
+ 1459class CogTestCharacterEncoding(TestCaseWithTempDir):
+ 1460 def test_simple(self):
+ 1461 d = {
+ 1462 "test.cog": b"""\
+ 1463 // This is my C++ file.
+ 1464 //[[[cog
+ 1465 cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)")
+ 1466 //]]]
+ 1467 //[[[end]]]
+ 1468 """,
+ 1469 "test.out": b"""\
+ 1470 // This is my C++ file.
+ 1471 //[[[cog
+ 1472 cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)")
+ 1473 //]]]
+ 1474 // Unicode: \xe1\x88\xb4 (U+1234)
1475 //[[[end]]]
- 1476 """,
- 1477
- 1478 'test.out': b"""\
- 1479 // This is my C++ file.
- 1480 //[[[cog
- 1481 cog.outl("// Unicode: \xe1\x88\xb4 (U+1234)")
- 1482 //]]]
- 1483 // Unicode: \xe1\x88\xb4 (U+1234)
- 1484 //[[[end]]]
- 1485 """.replace(b"\n", os.linesep.encode()),
- 1486 }
- 1487
- 1488 makeFiles(d)
- 1489 self.cog.callableMain(['argv0', '-r', 'test.cog'])
- 1490 self.assertFilesSame('test.cog', 'test.out')
- 1491 output = self.output.getvalue()
- 1492 self.assertIn("(changed)", output)
- 1493
- 1494 def testFileEncodingOption(self):
- 1495 d = {
- 1496 'test.cog': b"""\
- 1497 // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows
- 1498 //[[[cog
- 1499 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")
- 1500 //]]]
- 1501 //[[[end]]]
- 1502 """,
+ 1476 """.replace(b"\n", os.linesep.encode()),
+ 1477 }
+ 1478
+ 1479 make_files(d)
+ 1480 self.cog.callable_main(["argv0", "-r", "test.cog"])
+ 1481 self.assertFilesSame("test.cog", "test.out")
+ 1482 output = self.output.getvalue()
+ 1483 self.assertIn("(changed)", output)
+ 1484
+ 1485 def test_file_encoding_option(self):
+ 1486 d = {
+ 1487 "test.cog": b"""\
+ 1488 // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows
+ 1489 //[[[cog
+ 1490 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")
+ 1491 //]]]
+ 1492 //[[[end]]]
+ 1493 """,
+ 1494 "test.out": b"""\
+ 1495 // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows
+ 1496 //[[[cog
+ 1497 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")
+ 1498 //]]]
+ 1499 \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
+ 1500 //[[[end]]]
+ 1501 """.replace(b"\n", os.linesep.encode()),
+ 1502 }
1503
- 1504 'test.out': b"""\
- 1505 // \xca\xee\xe4\xe8\xf0\xe2\xea\xe0 Windows
- 1506 //[[[cog
- 1507 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")
- 1508 //]]]
- 1509 \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
- 1510 //[[[end]]]
- 1511 """.replace(b"\n", os.linesep.encode()),
- 1512 }
+ 1504 make_files(d)
+ 1505 self.cog.callable_main(["argv0", "-n", "cp1251", "-r", "test.cog"])
+ 1506 self.assertFilesSame("test.cog", "test.out")
+ 1507 output = self.output.getvalue()
+ 1508 self.assertIn("(changed)", output)
+ 1509
+ 1510
+ 1511class TestCaseWithImports(TestCaseWithTempDir):
+ 1512 """Automatic resetting of sys.modules for tests that import modules.
1513
- 1514 makeFiles(d)
- 1515 self.cog.callableMain(['argv0', '-n', 'cp1251', '-r', 'test.cog'])
- 1516 self.assertFilesSame('test.cog', 'test.out')
- 1517 output = self.output.getvalue()
- 1518 self.assertIn("(changed)", output)
+ 1514 When running tests which import modules, the sys.modules list
+ 1515 leaks from one test to the next. This test case class scrubs
+ 1516 the list after each run to keep the tests isolated from each other.
+ 1517
+ 1518 """
1519
- 1520
- 1521class TestCaseWithImports(TestCaseWithTempDir):
- 1522 """ When running tests which import modules, the sys.modules list
- 1523 leaks from one test to the next. This test case class scrubs
- 1524 the list after each run to keep the tests isolated from each other.
- 1525 """
- 1526
- 1527 def setUp(self):
- 1528 super().setUp()
- 1529 self.sysmodulekeys = list(sys.modules)
- 1530
- 1531 def tearDown(self):
- 1532 modstoscrub = [
- 1533 modname
- 1534 for modname in sys.modules
- 1535 if modname not in self.sysmodulekeys
- 1536 ]
- 1537 for modname in modstoscrub:
- 1538 del sys.modules[modname]
- 1539 super().tearDown()
- 1540
- 1541
- 1542class CogIncludeTests(TestCaseWithImports):
- 1543 dincludes = {
- 1544 'test.cog': """\
- 1545 //[[[cog
- 1546 import mymodule
- 1547 //]]]
- 1548 //[[[end]]]
- 1549 """,
- 1550
- 1551 'test.out': """\
- 1552 //[[[cog
- 1553 import mymodule
- 1554 //]]]
- 1555 Hello from mymodule
- 1556 //[[[end]]]
- 1557 """,
- 1558
- 1559 'test2.out': """\
- 1560 //[[[cog
- 1561 import mymodule
- 1562 //]]]
- 1563 Hello from mymodule in inc2
- 1564 //[[[end]]]
- 1565 """,
- 1566
- 1567 'include': {
- 1568 'mymodule.py': """\
+ 1520 def setUp(self):
+ 1521 super().setUp()
+ 1522 self.sysmodulekeys = list(sys.modules)
+ 1523
+ 1524 def tearDown(self):
+ 1525 modstoscrub = [
+ 1526 modname for modname in sys.modules if modname not in self.sysmodulekeys
+ 1527 ]
+ 1528 for modname in modstoscrub:
+ 1529 del sys.modules[modname]
+ 1530 super().tearDown()
+ 1531
+ 1532
+ 1533class CogIncludeTests(TestCaseWithImports):
+ 1534 dincludes = {
+ 1535 "test.cog": """\
+ 1536 //[[[cog
+ 1537 import mymodule
+ 1538 //]]]
+ 1539 //[[[end]]]
+ 1540 """,
+ 1541 "test.out": """\
+ 1542 //[[[cog
+ 1543 import mymodule
+ 1544 //]]]
+ 1545 Hello from mymodule
+ 1546 //[[[end]]]
+ 1547 """,
+ 1548 "test2.out": """\
+ 1549 //[[[cog
+ 1550 import mymodule
+ 1551 //]]]
+ 1552 Hello from mymodule in inc2
+ 1553 //[[[end]]]
+ 1554 """,
+ 1555 "include": {
+ 1556 "mymodule.py": """\
+ 1557 import cog
+ 1558 cog.outl("Hello from mymodule")
+ 1559 """
+ 1560 },
+ 1561 "inc2": {
+ 1562 "mymodule.py": """\
+ 1563 import cog
+ 1564 cog.outl("Hello from mymodule in inc2")
+ 1565 """
+ 1566 },
+ 1567 "inc3": {
+ 1568 "someothermodule.py": """\
1569 import cog
- 1570 cog.outl("Hello from mymodule")
+ 1570 cog.outl("This is some other module.")
1571 """
- 1572 },
- 1573
- 1574 'inc2': {
- 1575 'mymodule.py': """\
- 1576 import cog
- 1577 cog.outl("Hello from mymodule in inc2")
- 1578 """
- 1579 },
- 1580
- 1581 'inc3': {
- 1582 'someothermodule.py': """\
- 1583 import cog
- 1584 cog.outl("This is some other module.")
- 1585 """
- 1586 },
- 1587 }
- 1588
- 1589 def testNeedIncludePath(self):
- 1590 # Try it without the -I, to see that an ImportError happens.
- 1591 makeFiles(self.dincludes)
- 1592 msg = "(ImportError|ModuleNotFoundError): No module named '?mymodule'?"
- 1593 with self.assertRaisesRegex(CogUserException, msg):
- 1594 self.cog.callableMain(['argv0', '-r', 'test.cog'])
+ 1572 },
+ 1573 }
+ 1574
+ 1575 def test_need_include_path(self):
+ 1576 # Try it without the -I, to see that an ImportError happens.
+ 1577 make_files(self.dincludes)
+ 1578 msg = "(ImportError|ModuleNotFoundError): No module named '?mymodule'?"
+ 1579 with self.assertRaisesRegex(CogUserException, msg):
+ 1580 self.cog.callable_main(["argv0", "-r", "test.cog"])
+ 1581
+ 1582 def test_include_path(self):
+ 1583 # Test that -I adds include directories properly.
+ 1584 make_files(self.dincludes)
+ 1585 self.cog.callable_main(["argv0", "-r", "-I", "include", "test.cog"])
+ 1586 self.assertFilesSame("test.cog", "test.out")
+ 1587
+ 1588 def test_two_include_paths(self):
+ 1589 # Test that two -I's add include directories properly.
+ 1590 make_files(self.dincludes)
+ 1591 self.cog.callable_main(
+ 1592 ["argv0", "-r", "-I", "include", "-I", "inc2", "test.cog"]
+ 1593 )
+ 1594 self.assertFilesSame("test.cog", "test.out")
1595
- 1596 def testIncludePath(self):
- 1597 # Test that -I adds include directories properly.
- 1598 makeFiles(self.dincludes)
- 1599 self.cog.callableMain(['argv0', '-r', '-I', 'include', 'test.cog'])
- 1600 self.assertFilesSame('test.cog', 'test.out')
- 1601
- 1602 def testTwoIncludePaths(self):
- 1603 # Test that two -I's add include directories properly.
- 1604 makeFiles(self.dincludes)
- 1605 self.cog.callableMain(['argv0', '-r', '-I', 'include', '-I', 'inc2', 'test.cog'])
- 1606 self.assertFilesSame('test.cog', 'test.out')
- 1607
- 1608 def testTwoIncludePaths2(self):
- 1609 # Test that two -I's add include directories properly.
- 1610 makeFiles(self.dincludes)
- 1611 self.cog.callableMain(['argv0', '-r', '-I', 'inc2', '-I', 'include', 'test.cog'])
- 1612 self.assertFilesSame('test.cog', 'test2.out')
- 1613
- 1614 def testUselessIncludePath(self):
- 1615 # Test that the search will continue past the first directory.
- 1616 makeFiles(self.dincludes)
- 1617 self.cog.callableMain(['argv0', '-r', '-I', 'inc3', '-I', 'include', 'test.cog'])
- 1618 self.assertFilesSame('test.cog', 'test.out')
- 1619
- 1620 def testSysPathIsUnchanged(self):
- 1621 d = {
- 1622 'bad.cog': """\
- 1623 //[[[cog cog.error("Oh no!") ]]]
- 1624 //[[[end]]]
- 1625 """,
- 1626 'good.cog': """\
- 1627 //[[[cog cog.outl("Oh yes!") ]]]
- 1628 //[[[end]]]
- 1629 """,
- 1630 }
- 1631
- 1632 makeFiles(d)
- 1633 # Is it unchanged just by creating a cog engine?
- 1634 oldsyspath = sys.path[:]
- 1635 self.newCog()
+ 1596 def test_two_include_paths2(self):
+ 1597 # Test that two -I's add include directories properly.
+ 1598 make_files(self.dincludes)
+ 1599 self.cog.callable_main(
+ 1600 ["argv0", "-r", "-I", "inc2", "-I", "include", "test.cog"]
+ 1601 )
+ 1602 self.assertFilesSame("test.cog", "test2.out")
+ 1603
+ 1604 def test_useless_include_path(self):
+ 1605 # Test that the search will continue past the first directory.
+ 1606 make_files(self.dincludes)
+ 1607 self.cog.callable_main(
+ 1608 ["argv0", "-r", "-I", "inc3", "-I", "include", "test.cog"]
+ 1609 )
+ 1610 self.assertFilesSame("test.cog", "test.out")
+ 1611
+ 1612 def test_sys_path_is_unchanged(self):
+ 1613 d = {
+ 1614 "bad.cog": """\
+ 1615 //[[[cog cog.error("Oh no!") ]]]
+ 1616 //[[[end]]]
+ 1617 """,
+ 1618 "good.cog": """\
+ 1619 //[[[cog cog.outl("Oh yes!") ]]]
+ 1620 //[[[end]]]
+ 1621 """,
+ 1622 }
+ 1623
+ 1624 make_files(d)
+ 1625 # Is it unchanged just by creating a cog engine?
+ 1626 oldsyspath = sys.path[:]
+ 1627 self.new_cog()
+ 1628 self.assertEqual(oldsyspath, sys.path)
+ 1629 # Is it unchanged for a successful run?
+ 1630 self.new_cog()
+ 1631 self.cog.callable_main(["argv0", "-r", "good.cog"])
+ 1632 self.assertEqual(oldsyspath, sys.path)
+ 1633 # Is it unchanged for a successful run with includes?
+ 1634 self.new_cog()
+ 1635 self.cog.callable_main(["argv0", "-r", "-I", "xyzzy", "good.cog"])
1636 self.assertEqual(oldsyspath, sys.path)
- 1637 # Is it unchanged for a successful run?
- 1638 self.newCog()
- 1639 self.cog.callableMain(['argv0', '-r', 'good.cog'])
+ 1637 # Is it unchanged for a successful run with two includes?
+ 1638 self.new_cog()
+ 1639 self.cog.callable_main(["argv0", "-r", "-I", "xyzzy", "-I", "quux", "good.cog"])
1640 self.assertEqual(oldsyspath, sys.path)
- 1641 # Is it unchanged for a successful run with includes?
- 1642 self.newCog()
- 1643 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', 'good.cog'])
- 1644 self.assertEqual(oldsyspath, sys.path)
- 1645 # Is it unchanged for a successful run with two includes?
- 1646 self.newCog()
- 1647 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', '-I', 'quux', 'good.cog'])
- 1648 self.assertEqual(oldsyspath, sys.path)
- 1649 # Is it unchanged for a failed run?
- 1650 self.newCog()
- 1651 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
- 1652 self.cog.callableMain(['argv0', '-r', 'bad.cog'])
- 1653 self.assertEqual(oldsyspath, sys.path)
- 1654 # Is it unchanged for a failed run with includes?
- 1655 self.newCog()
- 1656 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
- 1657 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', 'bad.cog'])
- 1658 self.assertEqual(oldsyspath, sys.path)
- 1659 # Is it unchanged for a failed run with two includes?
- 1660 self.newCog()
- 1661 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
- 1662 self.cog.callableMain(['argv0', '-r', '-I', 'xyzzy', '-I', 'quux', 'bad.cog'])
- 1663 self.assertEqual(oldsyspath, sys.path)
- 1664
- 1665 def testSubDirectories(self):
- 1666 # Test that relative paths on the command line work, with includes.
- 1667
- 1668 d = {
- 1669 'code': {
- 1670 'test.cog': """\
+ 1641 # Is it unchanged for a failed run?
+ 1642 self.new_cog()
+ 1643 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
+ 1644 self.cog.callable_main(["argv0", "-r", "bad.cog"])
+ 1645 self.assertEqual(oldsyspath, sys.path)
+ 1646 # Is it unchanged for a failed run with includes?
+ 1647 self.new_cog()
+ 1648 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
+ 1649 self.cog.callable_main(["argv0", "-r", "-I", "xyzzy", "bad.cog"])
+ 1650 self.assertEqual(oldsyspath, sys.path)
+ 1651 # Is it unchanged for a failed run with two includes?
+ 1652 self.new_cog()
+ 1653 with self.assertRaisesRegex(CogError, r"^Oh no!$"):
+ 1654 self.cog.callable_main(
+ 1655 ["argv0", "-r", "-I", "xyzzy", "-I", "quux", "bad.cog"]
+ 1656 )
+ 1657 self.assertEqual(oldsyspath, sys.path)
+ 1658
+ 1659 def test_sub_directories(self):
+ 1660 # Test that relative paths on the command line work, with includes.
+ 1661
+ 1662 d = {
+ 1663 "code": {
+ 1664 "test.cog": """\
+ 1665 //[[[cog
+ 1666 import mysubmodule
+ 1667 //]]]
+ 1668 //[[[end]]]
+ 1669 """,
+ 1670 "test.out": """\
1671 //[[[cog
1672 import mysubmodule
1673 //]]]
- 1674 //[[[end]]]
- 1675 """,
- 1676
- 1677 'test.out': """\
- 1678 //[[[cog
- 1679 import mysubmodule
- 1680 //]]]
- 1681 Hello from mysubmodule
- 1682 //[[[end]]]
- 1683 """,
- 1684
- 1685 'mysubmodule.py': """\
- 1686 import cog
- 1687 cog.outl("Hello from mysubmodule")
- 1688 """
- 1689 }
- 1690 }
- 1691
- 1692 makeFiles(d)
- 1693 # We should be able to invoke cog without the -I switch, and it will
- 1694 # auto-include the current directory
- 1695 self.cog.callableMain(['argv0', '-r', 'code/test.cog'])
- 1696 self.assertFilesSame('code/test.cog', 'code/test.out')
- 1697
- 1698
- 1699class CogTestsInFiles(TestCaseWithTempDir):
- 1700
- 1701 def testWarnIfNoCogCode(self):
- 1702 # Test that the -e switch warns if there is no Cog code.
- 1703 d = {
- 1704 'with.cog': """\
- 1705 //[[[cog
- 1706 cog.outl("hello world")
- 1707 //]]]
- 1708 hello world
- 1709 //[[[end]]]
- 1710 """,
- 1711
- 1712 'without.cog': """\
- 1713 There's no cog
- 1714 code in this file.
- 1715 """,
- 1716 }
- 1717
- 1718 makeFiles(d)
- 1719 self.cog.callableMain(['argv0', '-e', 'with.cog'])
- 1720 output = self.output.getvalue()
- 1721 self.assertNotIn("Warning", output)
- 1722 self.newCog()
- 1723 self.cog.callableMain(['argv0', '-e', 'without.cog'])
- 1724 output = self.output.getvalue()
- 1725 self.assertIn("Warning: no cog code found in without.cog", output)
- 1726 self.newCog()
- 1727 self.cog.callableMain(['argv0', 'without.cog'])
- 1728 output = self.output.getvalue()
- 1729 self.assertNotIn("Warning", output)
- 1730
- 1731 def testFileNameProps(self):
- 1732 d = {
- 1733 'cog1.txt': """\
- 1734 //[[[cog
- 1735 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
- 1736 //]]]
- 1737 this is cog1.txt in, cog1.txt out
- 1738 [[[end]]]
- 1739 """,
- 1740
- 1741 'cog1.out': """\
- 1742 //[[[cog
- 1743 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
- 1744 //]]]
- 1745 This is cog1.txt in, cog1.txt out
- 1746 [[[end]]]
- 1747 """,
- 1748
- 1749 'cog1out.out': """\
- 1750 //[[[cog
- 1751 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
- 1752 //]]]
- 1753 This is cog1.txt in, cog1out.txt out
- 1754 [[[end]]]
- 1755 """,
- 1756 }
- 1757
- 1758 makeFiles(d)
- 1759 self.cog.callableMain(['argv0', '-r', 'cog1.txt'])
- 1760 self.assertFilesSame('cog1.txt', 'cog1.out')
- 1761 self.newCog()
- 1762 self.cog.callableMain(['argv0', '-o', 'cog1out.txt', 'cog1.txt'])
- 1763 self.assertFilesSame('cog1out.txt', 'cog1out.out')
- 1764
- 1765 def testGlobalsDontCrossFiles(self):
- 1766 # Make sure that global values don't get shared between files.
- 1767 d = {
- 1768 'one.cog': """\
- 1769 //[[[cog s = "This was set in one.cog" ]]]
- 1770 //[[[end]]]
- 1771 //[[[cog cog.outl(s) ]]]
- 1772 //[[[end]]]
- 1773 """,
- 1774
- 1775 'one.out': """\
- 1776 //[[[cog s = "This was set in one.cog" ]]]
- 1777 //[[[end]]]
- 1778 //[[[cog cog.outl(s) ]]]
- 1779 This was set in one.cog
- 1780 //[[[end]]]
- 1781 """,
- 1782
- 1783 'two.cog': """\
- 1784 //[[[cog
- 1785 try:
- 1786 cog.outl(s)
- 1787 except NameError:
- 1788 cog.outl("s isn't set!")
- 1789 //]]]
- 1790 //[[[end]]]
- 1791 """,
- 1792
- 1793 'two.out': """\
- 1794 //[[[cog
- 1795 try:
- 1796 cog.outl(s)
- 1797 except NameError:
- 1798 cog.outl("s isn't set!")
- 1799 //]]]
- 1800 s isn't set!
- 1801 //[[[end]]]
- 1802 """,
- 1803
- 1804 'cogfiles.txt': """\
- 1805 # Please run cog
- 1806 one.cog
- 1807
- 1808 two.cog
- 1809 """
- 1810 }
- 1811
- 1812 makeFiles(d)
- 1813 self.cog.callableMain(['argv0', '-r', '@cogfiles.txt'])
- 1814 self.assertFilesSame('one.cog', 'one.out')
- 1815 self.assertFilesSame('two.cog', 'two.out')
- 1816 output = self.output.getvalue()
- 1817 self.assertIn("(changed)", output)
- 1818
- 1819 def testRemoveGeneratedOutput(self):
- 1820 d = {
- 1821 'cog1.txt': """\
- 1822 //[[[cog
- 1823 cog.outl("This line was generated.")
- 1824 //]]]
- 1825 This line was generated.
- 1826 //[[[end]]]
- 1827 This line was not.
- 1828 """,
+ 1674 Hello from mysubmodule
+ 1675 //[[[end]]]
+ 1676 """,
+ 1677 "mysubmodule.py": """\
+ 1678 import cog
+ 1679 cog.outl("Hello from mysubmodule")
+ 1680 """,
+ 1681 }
+ 1682 }
+ 1683
+ 1684 make_files(d)
+ 1685 # We should be able to invoke cog without the -I switch, and it will
+ 1686 # auto-include the current directory
+ 1687 self.cog.callable_main(["argv0", "-r", "code/test.cog"])
+ 1688 self.assertFilesSame("code/test.cog", "code/test.out")
+ 1689
+ 1690
+ 1691class CogTestsInFiles(TestCaseWithTempDir):
+ 1692 def test_warn_if_no_cog_code(self):
+ 1693 # Test that the -e switch warns if there is no Cog code.
+ 1694 d = {
+ 1695 "with.cog": """\
+ 1696 //[[[cog
+ 1697 cog.outl("hello world")
+ 1698 //]]]
+ 1699 hello world
+ 1700 //[[[end]]]
+ 1701 """,
+ 1702 "without.cog": """\
+ 1703 There's no cog
+ 1704 code in this file.
+ 1705 """,
+ 1706 }
+ 1707
+ 1708 make_files(d)
+ 1709 self.cog.callable_main(["argv0", "-e", "with.cog"])
+ 1710 output = self.output.getvalue()
+ 1711 self.assertNotIn("Warning", output)
+ 1712 self.new_cog()
+ 1713 self.cog.callable_main(["argv0", "-e", "without.cog"])
+ 1714 output = self.output.getvalue()
+ 1715 self.assertIn("Warning: no cog code found in without.cog", output)
+ 1716 self.new_cog()
+ 1717 self.cog.callable_main(["argv0", "without.cog"])
+ 1718 output = self.output.getvalue()
+ 1719 self.assertNotIn("Warning", output)
+ 1720
+ 1721 def test_file_name_props(self):
+ 1722 d = {
+ 1723 "cog1.txt": """\
+ 1724 //[[[cog
+ 1725 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
+ 1726 //]]]
+ 1727 this is cog1.txt in, cog1.txt out
+ 1728 [[[end]]]
+ 1729 """,
+ 1730 "cog1.out": """\
+ 1731 //[[[cog
+ 1732 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
+ 1733 //]]]
+ 1734 This is cog1.txt in, cog1.txt out
+ 1735 [[[end]]]
+ 1736 """,
+ 1737 "cog1out.out": """\
+ 1738 //[[[cog
+ 1739 cog.outl("This is %s in, %s out" % (cog.inFile, cog.outFile))
+ 1740 //]]]
+ 1741 This is cog1.txt in, cog1out.txt out
+ 1742 [[[end]]]
+ 1743 """,
+ 1744 }
+ 1745
+ 1746 make_files(d)
+ 1747 self.cog.callable_main(["argv0", "-r", "cog1.txt"])
+ 1748 self.assertFilesSame("cog1.txt", "cog1.out")
+ 1749 self.new_cog()
+ 1750 self.cog.callable_main(["argv0", "-o", "cog1out.txt", "cog1.txt"])
+ 1751 self.assertFilesSame("cog1out.txt", "cog1out.out")
+ 1752
+ 1753 def test_globals_dont_cross_files(self):
+ 1754 # Make sure that global values don't get shared between files.
+ 1755 d = {
+ 1756 "one.cog": """\
+ 1757 //[[[cog s = "This was set in one.cog" ]]]
+ 1758 //[[[end]]]
+ 1759 //[[[cog cog.outl(s) ]]]
+ 1760 //[[[end]]]
+ 1761 """,
+ 1762 "one.out": """\
+ 1763 //[[[cog s = "This was set in one.cog" ]]]
+ 1764 //[[[end]]]
+ 1765 //[[[cog cog.outl(s) ]]]
+ 1766 This was set in one.cog
+ 1767 //[[[end]]]
+ 1768 """,
+ 1769 "two.cog": """\
+ 1770 //[[[cog
+ 1771 try:
+ 1772 cog.outl(s)
+ 1773 except NameError:
+ 1774 cog.outl("s isn't set!")
+ 1775 //]]]
+ 1776 //[[[end]]]
+ 1777 """,
+ 1778 "two.out": """\
+ 1779 //[[[cog
+ 1780 try:
+ 1781 cog.outl(s)
+ 1782 except NameError:
+ 1783 cog.outl("s isn't set!")
+ 1784 //]]]
+ 1785 s isn't set!
+ 1786 //[[[end]]]
+ 1787 """,
+ 1788 "cogfiles.txt": """\
+ 1789 # Please run cog
+ 1790 one.cog
+ 1791
+ 1792 two.cog
+ 1793 """,
+ 1794 }
+ 1795
+ 1796 make_files(d)
+ 1797 self.cog.callable_main(["argv0", "-r", "@cogfiles.txt"])
+ 1798 self.assertFilesSame("one.cog", "one.out")
+ 1799 self.assertFilesSame("two.cog", "two.out")
+ 1800 output = self.output.getvalue()
+ 1801 self.assertIn("(changed)", output)
+ 1802
+ 1803 def test_remove_generated_output(self):
+ 1804 d = {
+ 1805 "cog1.txt": """\
+ 1806 //[[[cog
+ 1807 cog.outl("This line was generated.")
+ 1808 //]]]
+ 1809 This line was generated.
+ 1810 //[[[end]]]
+ 1811 This line was not.
+ 1812 """,
+ 1813 "cog1.out": """\
+ 1814 //[[[cog
+ 1815 cog.outl("This line was generated.")
+ 1816 //]]]
+ 1817 //[[[end]]]
+ 1818 This line was not.
+ 1819 """,
+ 1820 "cog1.out2": """\
+ 1821 //[[[cog
+ 1822 cog.outl("This line was generated.")
+ 1823 //]]]
+ 1824 This line was generated.
+ 1825 //[[[end]]]
+ 1826 This line was not.
+ 1827 """,
+ 1828 }
1829
- 1830 'cog1.out': """\
- 1831 //[[[cog
- 1832 cog.outl("This line was generated.")
- 1833 //]]]
- 1834 //[[[end]]]
- 1835 This line was not.
- 1836 """,
- 1837
- 1838 'cog1.out2': """\
- 1839 //[[[cog
- 1840 cog.outl("This line was generated.")
- 1841 //]]]
- 1842 This line was generated.
- 1843 //[[[end]]]
- 1844 This line was not.
- 1845 """,
- 1846 }
- 1847
- 1848 makeFiles(d)
- 1849 # Remove generated output.
- 1850 self.cog.callableMain(['argv0', '-r', '-x', 'cog1.txt'])
- 1851 self.assertFilesSame('cog1.txt', 'cog1.out')
- 1852 self.newCog()
- 1853 # Regenerate the generated output.
- 1854 self.cog.callableMain(['argv0', '-r', 'cog1.txt'])
- 1855 self.assertFilesSame('cog1.txt', 'cog1.out2')
- 1856 self.newCog()
- 1857 # Remove the generated output again.
- 1858 self.cog.callableMain(['argv0', '-r', '-x', 'cog1.txt'])
- 1859 self.assertFilesSame('cog1.txt', 'cog1.out')
- 1860
- 1861 def testMsgCall(self):
- 1862 infile = """\
- 1863 #[[[cog
- 1864 cog.msg("Hello there!")
- 1865 #]]]
- 1866 #[[[end]]]
- 1867 """
- 1868 infile = reindentBlock(infile)
- 1869 self.assertEqual(self.cog.processString(infile), infile)
- 1870 output = self.output.getvalue()
- 1871 self.assertEqual(output, "Message: Hello there!\n")
- 1872
- 1873 def testErrorMessageHasNoTraceback(self):
- 1874 # Test that a Cog error is printed to stderr with no traceback.
- 1875
- 1876 d = {
- 1877 'cog1.txt': """\
- 1878 //[[[cog
- 1879 cog.outl("This line was newly")
- 1880 cog.outl("generated by cog")
- 1881 cog.outl("blah blah.")
- 1882 //]]]
- 1883 Xhis line was newly
- 1884 generated by cog
- 1885 blah blah.
- 1886 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
+ 1830 make_files(d)
+ 1831 # Remove generated output.
+ 1832 self.cog.callable_main(["argv0", "-r", "-x", "cog1.txt"])
+ 1833 self.assertFilesSame("cog1.txt", "cog1.out")
+ 1834 self.new_cog()
+ 1835 # Regenerate the generated output.
+ 1836 self.cog.callable_main(["argv0", "-r", "cog1.txt"])
+ 1837 self.assertFilesSame("cog1.txt", "cog1.out2")
+ 1838 self.new_cog()
+ 1839 # Remove the generated output again.
+ 1840 self.cog.callable_main(["argv0", "-r", "-x", "cog1.txt"])
+ 1841 self.assertFilesSame("cog1.txt", "cog1.out")
+ 1842
+ 1843 def test_msg_call(self):
+ 1844 infile = """\
+ 1845 #[[[cog
+ 1846 cog.msg("Hello there!")
+ 1847 #]]]
+ 1848 #[[[end]]]
+ 1849 """
+ 1850 infile = reindent_block(infile)
+ 1851 self.assertEqual(self.cog.process_string(infile), infile)
+ 1852 output = self.output.getvalue()
+ 1853 self.assertEqual(output, "Message: Hello there!\n")
+ 1854
+ 1855 def test_error_message_has_no_traceback(self):
+ 1856 # Test that a Cog error is printed to stderr with no traceback.
+ 1857
+ 1858 d = {
+ 1859 "cog1.txt": """\
+ 1860 //[[[cog
+ 1861 cog.outl("This line was newly")
+ 1862 cog.outl("generated by cog")
+ 1863 cog.outl("blah blah.")
+ 1864 //]]]
+ 1865 Xhis line was newly
+ 1866 generated by cog
+ 1867 blah blah.
+ 1868 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
+ 1869 """,
+ 1870 }
+ 1871
+ 1872 make_files(d)
+ 1873 stderr = io.StringIO()
+ 1874 self.cog.set_output(stderr=stderr)
+ 1875 self.cog.main(["argv0", "-c", "-r", "cog1.txt"])
+ 1876 self.assertEqual(self.output.getvalue(), "Cogging cog1.txt\n")
+ 1877 self.assertEqual(
+ 1878 stderr.getvalue(),
+ 1879 "cog1.txt(9): Output has been edited! Delete old checksum to unprotect.\n",
+ 1880 )
+ 1881
+ 1882 def test_dash_d(self):
+ 1883 d = {
+ 1884 "test.cog": """\
+ 1885 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
+ 1886 --[[[end]]]
1887 """,
- 1888 }
- 1889
- 1890 makeFiles(d)
- 1891 stderr = io.StringIO()
- 1892 self.cog.setOutput(stderr=stderr)
- 1893 self.cog.main(['argv0', '-c', '-r', "cog1.txt"])
- 1894 self.assertEqual(self.output.getvalue(), "Cogging cog1.txt\n")
- 1895 self.assertEqual(stderr.getvalue(), "cog1.txt(9): Output has been edited! Delete old checksum to unprotect.\n")
- 1896
- 1897 def testDashD(self):
- 1898 d = {
- 1899 'test.cog': """\
- 1900 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
- 1901 --[[[end]]]
- 1902 """,
- 1903
- 1904 'test.kablooey': """\
- 1905 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
- 1906 Defined fooey as kablooey
- 1907 --[[[end]]]
- 1908 """,
- 1909
- 1910 'test.einstein': """\
- 1911 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
- 1912 Defined fooey as e=mc2
- 1913 --[[[end]]]
- 1914 """,
- 1915 }
- 1916
- 1917 makeFiles(d)
- 1918 self.cog.callableMain(['argv0', '-r', '-D', 'fooey=kablooey', 'test.cog'])
- 1919 self.assertFilesSame('test.cog', 'test.kablooey')
- 1920 makeFiles(d)
- 1921 self.cog.callableMain(['argv0', '-r', '-Dfooey=kablooey', 'test.cog'])
- 1922 self.assertFilesSame('test.cog', 'test.kablooey')
- 1923 makeFiles(d)
- 1924 self.cog.callableMain(['argv0', '-r', '-Dfooey=e=mc2', 'test.cog'])
- 1925 self.assertFilesSame('test.cog', 'test.einstein')
- 1926 makeFiles(d)
- 1927 self.cog.callableMain(['argv0', '-r', '-Dbar=quux', '-Dfooey=kablooey', 'test.cog'])
- 1928 self.assertFilesSame('test.cog', 'test.kablooey')
- 1929 makeFiles(d)
- 1930 self.cog.callableMain(['argv0', '-r', '-Dfooey=kablooey', '-Dbar=quux', 'test.cog'])
- 1931 self.assertFilesSame('test.cog', 'test.kablooey')
- 1932 makeFiles(d)
- 1933 self.cog.callableMain(['argv0', '-r', '-Dfooey=gooey', '-Dfooey=kablooey', 'test.cog'])
- 1934 self.assertFilesSame('test.cog', 'test.kablooey')
- 1935
- 1936 def testOutputToStdout(self):
- 1937 d = {
- 1938 'test.cog': """\
- 1939 --[[[cog cog.outl('Hey there!') ]]]
- 1940 --[[[end]]]
- 1941 """
- 1942 }
+ 1888 "test.kablooey": """\
+ 1889 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
+ 1890 Defined fooey as kablooey
+ 1891 --[[[end]]]
+ 1892 """,
+ 1893 "test.einstein": """\
+ 1894 --[[[cog cog.outl("Defined fooey as " + fooey) ]]]
+ 1895 Defined fooey as e=mc2
+ 1896 --[[[end]]]
+ 1897 """,
+ 1898 }
+ 1899
+ 1900 make_files(d)
+ 1901 self.cog.callable_main(["argv0", "-r", "-D", "fooey=kablooey", "test.cog"])
+ 1902 self.assertFilesSame("test.cog", "test.kablooey")
+ 1903 make_files(d)
+ 1904 self.cog.callable_main(["argv0", "-r", "-Dfooey=kablooey", "test.cog"])
+ 1905 self.assertFilesSame("test.cog", "test.kablooey")
+ 1906 make_files(d)
+ 1907 self.cog.callable_main(["argv0", "-r", "-Dfooey=e=mc2", "test.cog"])
+ 1908 self.assertFilesSame("test.cog", "test.einstein")
+ 1909 make_files(d)
+ 1910 self.cog.callable_main(
+ 1911 ["argv0", "-r", "-Dbar=quux", "-Dfooey=kablooey", "test.cog"]
+ 1912 )
+ 1913 self.assertFilesSame("test.cog", "test.kablooey")
+ 1914 make_files(d)
+ 1915 self.cog.callable_main(
+ 1916 ["argv0", "-r", "-Dfooey=kablooey", "-Dbar=quux", "test.cog"]
+ 1917 )
+ 1918 self.assertFilesSame("test.cog", "test.kablooey")
+ 1919 make_files(d)
+ 1920 self.cog.callable_main(
+ 1921 ["argv0", "-r", "-Dfooey=gooey", "-Dfooey=kablooey", "test.cog"]
+ 1922 )
+ 1923 self.assertFilesSame("test.cog", "test.kablooey")
+ 1924
+ 1925 def test_output_to_stdout(self):
+ 1926 d = {
+ 1927 "test.cog": """\
+ 1928 --[[[cog cog.outl('Hey there!') ]]]
+ 1929 --[[[end]]]
+ 1930 """
+ 1931 }
+ 1932
+ 1933 make_files(d)
+ 1934 stderr = io.StringIO()
+ 1935 self.cog.set_output(stderr=stderr)
+ 1936 self.cog.callable_main(["argv0", "test.cog"])
+ 1937 output = self.output.getvalue()
+ 1938 outerr = stderr.getvalue()
+ 1939 self.assertEqual(
+ 1940 output, "--[[[cog cog.outl('Hey there!') ]]]\nHey there!\n--[[[end]]]\n"
+ 1941 )
+ 1942 self.assertEqual(outerr, "")
1943
- 1944 makeFiles(d)
- 1945 stderr = io.StringIO()
- 1946 self.cog.setOutput(stderr=stderr)
- 1947 self.cog.callableMain(['argv0', 'test.cog'])
- 1948 output = self.output.getvalue()
- 1949 outerr = stderr.getvalue()
- 1950 self.assertEqual(output, "--[[[cog cog.outl('Hey there!') ]]]\nHey there!\n--[[[end]]]\n")
- 1951 self.assertEqual(outerr, "")
+ 1944 def test_read_from_stdin(self):
+ 1945 stdin = io.StringIO("--[[[cog cog.outl('Wow') ]]]\n--[[[end]]]\n")
+ 1946
+ 1947 def restore_stdin(old_stdin):
+ 1948 sys.stdin = old_stdin
+ 1949
+ 1950 self.addCleanup(restore_stdin, sys.stdin)
+ 1951 sys.stdin = stdin
1952
- 1953 def testReadFromStdin(self):
- 1954 stdin = io.StringIO("--[[[cog cog.outl('Wow') ]]]\n--[[[end]]]\n")
- 1955 def restore_stdin(old_stdin):
- 1956 sys.stdin = old_stdin
- 1957 self.addCleanup(restore_stdin, sys.stdin)
- 1958 sys.stdin = stdin
- 1959
- 1960 stderr = io.StringIO()
- 1961 self.cog.setOutput(stderr=stderr)
- 1962 self.cog.callableMain(['argv0', '-'])
- 1963 output = self.output.getvalue()
- 1964 outerr = stderr.getvalue()
- 1965 self.assertEqual(output, "--[[[cog cog.outl('Wow') ]]]\nWow\n--[[[end]]]\n")
- 1966 self.assertEqual(outerr, "")
- 1967
- 1968 def testSuffixOutputLines(self):
- 1969 d = {
- 1970 'test.cog': """\
- 1971 Hey there.
- 1972 ;[[[cog cog.outl('a\\nb\\n \\nc') ]]]
- 1973 ;[[[end]]]
- 1974 Good bye.
- 1975 """,
- 1976
- 1977 'test.out': """\
- 1978 Hey there.
- 1979 ;[[[cog cog.outl('a\\nb\\n \\nc') ]]]
- 1980 a (foo)
- 1981 b (foo)
- 1982 """ # These three trailing spaces are important.
- 1983 # The suffix is not applied to completely blank lines.
- 1984 """
- 1985 c (foo)
- 1986 ;[[[end]]]
- 1987 Good bye.
- 1988 """,
- 1989 }
- 1990
- 1991 makeFiles(d)
- 1992 self.cog.callableMain(['argv0', '-r', '-s', ' (foo)', 'test.cog'])
- 1993 self.assertFilesSame('test.cog', 'test.out')
- 1994
- 1995 def testEmptySuffix(self):
- 1996 d = {
- 1997 'test.cog': """\
- 1998 ;[[[cog cog.outl('a\\nb\\nc') ]]]
- 1999 ;[[[end]]]
- 2000 """,
+ 1953 stderr = io.StringIO()
+ 1954 self.cog.set_output(stderr=stderr)
+ 1955 self.cog.callable_main(["argv0", "-"])
+ 1956 output = self.output.getvalue()
+ 1957 outerr = stderr.getvalue()
+ 1958 self.assertEqual(output, "--[[[cog cog.outl('Wow') ]]]\nWow\n--[[[end]]]\n")
+ 1959 self.assertEqual(outerr, "")
+ 1960
+ 1961 def test_suffix_output_lines(self):
+ 1962 d = {
+ 1963 "test.cog": """\
+ 1964 Hey there.
+ 1965 ;[[[cog cog.outl('a\\nb\\n \\nc') ]]]
+ 1966 ;[[[end]]]
+ 1967 Good bye.
+ 1968 """,
+ 1969 "test.out": """\
+ 1970 Hey there.
+ 1971 ;[[[cog cog.outl('a\\nb\\n \\nc') ]]]
+ 1972 a (foo)
+ 1973 b (foo)
+ 1974 """ # These three trailing spaces are important.
+ 1975 # The suffix is not applied to completely blank lines.
+ 1976 """
+ 1977 c (foo)
+ 1978 ;[[[end]]]
+ 1979 Good bye.
+ 1980 """,
+ 1981 }
+ 1982
+ 1983 make_files(d)
+ 1984 self.cog.callable_main(["argv0", "-r", "-s", " (foo)", "test.cog"])
+ 1985 self.assertFilesSame("test.cog", "test.out")
+ 1986
+ 1987 def test_empty_suffix(self):
+ 1988 d = {
+ 1989 "test.cog": """\
+ 1990 ;[[[cog cog.outl('a\\nb\\nc') ]]]
+ 1991 ;[[[end]]]
+ 1992 """,
+ 1993 "test.out": """\
+ 1994 ;[[[cog cog.outl('a\\nb\\nc') ]]]
+ 1995 a
+ 1996 b
+ 1997 c
+ 1998 ;[[[end]]]
+ 1999 """,
+ 2000 }
2001
- 2002 'test.out': """\
- 2003 ;[[[cog cog.outl('a\\nb\\nc') ]]]
- 2004 a
- 2005 b
- 2006 c
- 2007 ;[[[end]]]
- 2008 """,
- 2009 }
- 2010
- 2011 makeFiles(d)
- 2012 self.cog.callableMain(['argv0', '-r', '-s', '', 'test.cog'])
- 2013 self.assertFilesSame('test.cog', 'test.out')
+ 2002 make_files(d)
+ 2003 self.cog.callable_main(["argv0", "-r", "-s", "", "test.cog"])
+ 2004 self.assertFilesSame("test.cog", "test.out")
+ 2005
+ 2006 def test_hellish_suffix(self):
+ 2007 d = {
+ 2008 "test.cog": """\
+ 2009 ;[[[cog cog.outl('a\\n\\nb') ]]]
+ 2010 """,
+ 2011 "test.out": """\
+ 2012 ;[[[cog cog.outl('a\\n\\nb') ]]]
+ 2013 a /\\n*+([)]><
2014
- 2015 def testHellishSuffix(self):
- 2016 d = {
- 2017 'test.cog': """\
- 2018 ;[[[cog cog.outl('a\\n\\nb') ]]]
- 2019 """,
- 2020
- 2021 'test.out': """\
- 2022 ;[[[cog cog.outl('a\\n\\nb') ]]]
- 2023 a /\\n*+([)]><
- 2024
- 2025 b /\\n*+([)]><
- 2026 """,
- 2027 }
- 2028
- 2029 makeFiles(d)
- 2030 self.cog.callableMain(['argv0', '-z', '-r', '-s', r' /\n*+([)]><', 'test.cog'])
- 2031 self.assertFilesSame('test.cog', 'test.out')
- 2032
- 2033 def testPrologue(self):
- 2034 d = {
- 2035 'test.cog': """\
- 2036 Some text.
- 2037 //[[[cog cog.outl(str(math.sqrt(2))[:12])]]]
- 2038 //[[[end]]]
- 2039 epilogue.
- 2040 """,
- 2041
- 2042 'test.out': """\
- 2043 Some text.
- 2044 //[[[cog cog.outl(str(math.sqrt(2))[:12])]]]
- 2045 1.4142135623
- 2046 //[[[end]]]
- 2047 epilogue.
- 2048 """,
- 2049 }
- 2050
- 2051 makeFiles(d)
- 2052 self.cog.callableMain(['argv0', '-r', '-p', 'import math', 'test.cog'])
- 2053 self.assertFilesSame('test.cog', 'test.out')
- 2054
- 2055 def testThreads(self):
- 2056 # Test that the implicitly imported cog module is actually different for
- 2057 # different threads.
- 2058 numthreads = 20
+ 2015 b /\\n*+([)]><
+ 2016 """,
+ 2017 }
+ 2018
+ 2019 make_files(d)
+ 2020 self.cog.callable_main(["argv0", "-z", "-r", "-s", r" /\n*+([)]><", "test.cog"])
+ 2021 self.assertFilesSame("test.cog", "test.out")
+ 2022
+ 2023 def test_prologue(self):
+ 2024 d = {
+ 2025 "test.cog": """\
+ 2026 Some text.
+ 2027 //[[[cog cog.outl(str(math.sqrt(2))[:12])]]]
+ 2028 //[[[end]]]
+ 2029 epilogue.
+ 2030 """,
+ 2031 "test.out": """\
+ 2032 Some text.
+ 2033 //[[[cog cog.outl(str(math.sqrt(2))[:12])]]]
+ 2034 1.4142135623
+ 2035 //[[[end]]]
+ 2036 epilogue.
+ 2037 """,
+ 2038 }
+ 2039
+ 2040 make_files(d)
+ 2041 self.cog.callable_main(["argv0", "-r", "-p", "import math", "test.cog"])
+ 2042 self.assertFilesSame("test.cog", "test.out")
+ 2043
+ 2044 def test_threads(self):
+ 2045 # Test that the implicitly imported cog module is actually different for
+ 2046 # different threads.
+ 2047 numthreads = 20
+ 2048
+ 2049 d = {}
+ 2050 for i in range(numthreads):
+ 2051 d[f"f{i}.cog"] = (
+ 2052 "x\n" * i
+ 2053 + "[[[cog\n"
+ 2054 + f"assert cog.firstLineNum == int(FIRST) == {i+1}\n"
+ 2055 + "]]]\n"
+ 2056 + "[[[end]]]\n"
+ 2057 )
+ 2058 make_files(d)
2059
- 2060 d = {}
- 2061 for i in range(numthreads):
- 2062 d[f'f{i}.cog'] = (
- 2063 "x\n" * i +
- 2064 "[[[cog\n" +
- 2065 f"assert cog.firstLineNum == int(FIRST) == {i+1}\n" +
- 2066 "]]]\n" +
- 2067 "[[[end]]]\n"
- 2068 )
- 2069 makeFiles(d)
- 2070
- 2071 results = []
+ 2060 results = []
+ 2061
+ 2062 def thread_main(num):
+ 2063 try:
+ 2064 ret = Cog().main(
+ 2065 ["cog.py", "-r", "-D", f"FIRST={num+1}", f"f{num}.cog"]
+ 2066 )
+ 2067 assert ret == 0
+ 2068 except Exception as exc: # pragma: no cover (only happens on test failure)
+ 2069 results.append(exc)
+ 2070 else:
+ 2071 results.append(None)
2072
- 2073 def thread_main(num):
- 2074 try:
- 2075 ret = Cog().main(
- 2076 ['cog.py', '-r', '-D', f'FIRST={num+1}', f'f{num}.cog']
- 2077 )
- 2078 assert ret == 0
- 2079 except Exception as exc: # pragma: no cover (only happens on test failure)
- 2080 results.append(exc)
- 2081 else:
- 2082 results.append(None)
- 2083
- 2084 ts = [threading.Thread(target=thread_main, args=(i,)) for i in range(numthreads)]
- 2085 for t in ts:
- 2086 t.start()
- 2087 for t in ts:
- 2088 t.join()
- 2089 assert results == [None] * numthreads
- 2090
- 2091
- 2092class CheckTests(TestCaseWithTempDir):
- 2093 def run_check(self, args, status=0):
- 2094 actual_status = self.cog.main(['argv0', '--check'] + args)
- 2095 print(self.output.getvalue())
- 2096 self.assertEqual(status, actual_status)
- 2097
- 2098 def assert_made_files_unchanged(self, d):
- 2099 for name, content in d.items():
- 2100 content = reindentBlock(content)
- 2101 if os.name == 'nt':
- 2102 content = content.replace("\n", "\r\n")
- 2103 self.assertFileContent(name, content)
- 2104
- 2105 def test_check_no_cog(self):
- 2106 d = {
- 2107 'hello.txt': """\
- 2108 Hello.
- 2109 """,
- 2110 }
- 2111 makeFiles(d)
- 2112 self.run_check(['hello.txt'], status=0)
- 2113 self.assertEqual(self.output.getvalue(), "Checking hello.txt\n")
- 2114 self.assert_made_files_unchanged(d)
- 2115
- 2116 def test_check_good(self):
- 2117 d = {
- 2118 'unchanged.cog': """\
- 2119 //[[[cog
- 2120 cog.outl("hello world")
- 2121 //]]]
- 2122 hello world
- 2123 //[[[end]]]
- 2124 """,
- 2125 }
- 2126 makeFiles(d)
- 2127 self.run_check(['unchanged.cog'], status=0)
- 2128 self.assertEqual(self.output.getvalue(), "Checking unchanged.cog\n")
- 2129 self.assert_made_files_unchanged(d)
- 2130
- 2131 def test_check_bad(self):
- 2132 d = {
- 2133 'changed.cog': """\
- 2134 //[[[cog
- 2135 cog.outl("goodbye world")
- 2136 //]]]
- 2137 hello world
- 2138 //[[[end]]]
- 2139 """,
- 2140 }
- 2141 makeFiles(d)
- 2142 self.run_check(['changed.cog'], status=5)
- 2143 self.assertEqual(self.output.getvalue(), "Checking changed.cog (changed)\nCheck failed\n")
- 2144 self.assert_made_files_unchanged(d)
- 2145
- 2146 def test_check_mixed(self):
- 2147 d = {
- 2148 'unchanged.cog': """\
+ 2073 ts = [
+ 2074 threading.Thread(target=thread_main, args=(i,)) for i in range(numthreads)
+ 2075 ]
+ 2076 for t in ts:
+ 2077 t.start()
+ 2078 for t in ts:
+ 2079 t.join()
+ 2080 assert results == [None] * numthreads
+ 2081
+ 2082
+ 2083class CheckTests(TestCaseWithTempDir):
+ 2084 def run_check(self, args, status=0):
+ 2085 actual_status = self.cog.main(["argv0", "--check"] + args)
+ 2086 print(self.output.getvalue())
+ 2087 self.assertEqual(status, actual_status)
+ 2088
+ 2089 def assert_made_files_unchanged(self, d):
+ 2090 for name, content in d.items():
+ 2091 content = reindent_block(content)
+ 2092 if os.name == "nt":
+ 2093 content = content.replace("\n", "\r\n")
+ 2094 self.assertFileContent(name, content)
+ 2095
+ 2096 def test_check_no_cog(self):
+ 2097 d = {
+ 2098 "hello.txt": """\
+ 2099 Hello.
+ 2100 """,
+ 2101 }
+ 2102 make_files(d)
+ 2103 self.run_check(["hello.txt"], status=0)
+ 2104 self.assertEqual(self.output.getvalue(), "Checking hello.txt\n")
+ 2105 self.assert_made_files_unchanged(d)
+ 2106
+ 2107 def test_check_good(self):
+ 2108 d = {
+ 2109 "unchanged.cog": """\
+ 2110 //[[[cog
+ 2111 cog.outl("hello world")
+ 2112 //]]]
+ 2113 hello world
+ 2114 //[[[end]]]
+ 2115 """,
+ 2116 }
+ 2117 make_files(d)
+ 2118 self.run_check(["unchanged.cog"], status=0)
+ 2119 self.assertEqual(self.output.getvalue(), "Checking unchanged.cog\n")
+ 2120 self.assert_made_files_unchanged(d)
+ 2121
+ 2122 def test_check_bad(self):
+ 2123 d = {
+ 2124 "changed.cog": """\
+ 2125 //[[[cog
+ 2126 cog.outl("goodbye world")
+ 2127 //]]]
+ 2128 hello world
+ 2129 //[[[end]]]
+ 2130 """,
+ 2131 }
+ 2132 make_files(d)
+ 2133 self.run_check(["changed.cog"], status=5)
+ 2134 self.assertEqual(
+ 2135 self.output.getvalue(), "Checking changed.cog (changed)\nCheck failed\n"
+ 2136 )
+ 2137 self.assert_made_files_unchanged(d)
+ 2138
+ 2139 def test_check_mixed(self):
+ 2140 d = {
+ 2141 "unchanged.cog": """\
+ 2142 //[[[cog
+ 2143 cog.outl("hello world")
+ 2144 //]]]
+ 2145 hello world
+ 2146 //[[[end]]]
+ 2147 """,
+ 2148 "changed.cog": """\
2149 //[[[cog
- 2150 cog.outl("hello world")
+ 2150 cog.outl("goodbye world")
2151 //]]]
2152 hello world
2153 //[[[end]]]
2154 """,
- 2155 'changed.cog': """\
- 2156 //[[[cog
- 2157 cog.outl("goodbye world")
- 2158 //]]]
- 2159 hello world
- 2160 //[[[end]]]
- 2161 """,
- 2162 }
- 2163 makeFiles(d)
- 2164 for verbosity, output in [
- 2165 ("0", "Check failed\n"),
- 2166 ("1", "Checking changed.cog (changed)\nCheck failed\n"),
- 2167 ("2", "Checking unchanged.cog\nChecking changed.cog (changed)\nCheck failed\n"),
- 2168 ]:
- 2169 self.newCog()
- 2170 self.run_check(['--verbosity=%s' % verbosity, 'unchanged.cog', 'changed.cog'], status=5)
- 2171 self.assertEqual(self.output.getvalue(), output)
- 2172 self.assert_made_files_unchanged(d)
- 2173
- 2174 def test_check_with_good_checksum(self):
- 2175 d = {
- 2176 'good.txt': """\
- 2177 //[[[cog
- 2178 cog.outl("This line was newly")
- 2179 cog.outl("generated by cog")
- 2180 cog.outl("blah blah.")
- 2181 //]]]
- 2182 This line was newly
- 2183 generated by cog
- 2184 blah blah.
- 2185 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
- 2186 """,
- 2187 }
- 2188 makeFiles(d)
- 2189 # Have to use -c with --check if there are checksums in the file.
- 2190 self.run_check(['-c', 'good.txt'], status=0)
- 2191 self.assertEqual(self.output.getvalue(), "Checking good.txt\n")
- 2192 self.assert_made_files_unchanged(d)
- 2193
- 2194 def test_check_with_bad_checksum(self):
- 2195 d = {
- 2196 'bad.txt': """\
- 2197 //[[[cog
- 2198 cog.outl("This line was newly")
- 2199 cog.outl("generated by cog")
- 2200 cog.outl("blah blah.")
- 2201 //]]]
- 2202 This line was newly
- 2203 generated by cog
- 2204 blah blah.
- 2205 //[[[end]]] (checksum: a9999999e5ad6b95c9e9a184b26f4346)
- 2206 """,
- 2207 }
- 2208 makeFiles(d)
- 2209 # Have to use -c with --check if there are checksums in the file.
- 2210 self.run_check(['-c', 'bad.txt'], status=1)
- 2211 self.assertEqual(self.output.getvalue(), "Checking bad.txt\nbad.txt(9): Output has been edited! Delete old checksum to unprotect.\n")
- 2212 self.assert_made_files_unchanged(d)
- 2213
+ 2155 }
+ 2156 make_files(d)
+ 2157 for verbosity, output in [
+ 2158 ("0", "Check failed\n"),
+ 2159 ("1", "Checking changed.cog (changed)\nCheck failed\n"),
+ 2160 (
+ 2161 "2",
+ 2162 "Checking unchanged.cog\nChecking changed.cog (changed)\nCheck failed\n",
+ 2163 ),
+ 2164 ]:
+ 2165 self.new_cog()
+ 2166 self.run_check(
+ 2167 ["--verbosity=%s" % verbosity, "unchanged.cog", "changed.cog"], status=5
+ 2168 )
+ 2169 self.assertEqual(self.output.getvalue(), output)
+ 2170 self.assert_made_files_unchanged(d)
+ 2171
+ 2172 def test_check_with_good_checksum(self):
+ 2173 d = {
+ 2174 "good.txt": """\
+ 2175 //[[[cog
+ 2176 cog.outl("This line was newly")
+ 2177 cog.outl("generated by cog")
+ 2178 cog.outl("blah blah.")
+ 2179 //]]]
+ 2180 This line was newly
+ 2181 generated by cog
+ 2182 blah blah.
+ 2183 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
+ 2184 """,
+ 2185 }
+ 2186 make_files(d)
+ 2187 # Have to use -c with --check if there are checksums in the file.
+ 2188 self.run_check(["-c", "good.txt"], status=0)
+ 2189 self.assertEqual(self.output.getvalue(), "Checking good.txt\n")
+ 2190 self.assert_made_files_unchanged(d)
+ 2191
+ 2192 def test_check_with_bad_checksum(self):
+ 2193 d = {
+ 2194 "bad.txt": """\
+ 2195 //[[[cog
+ 2196 cog.outl("This line was newly")
+ 2197 cog.outl("generated by cog")
+ 2198 cog.outl("blah blah.")
+ 2199 //]]]
+ 2200 This line was newly
+ 2201 generated by cog
+ 2202 blah blah.
+ 2203 //[[[end]]] (checksum: a9999999e5ad6b95c9e9a184b26f4346)
+ 2204 """,
+ 2205 }
+ 2206 make_files(d)
+ 2207 # Have to use -c with --check if there are checksums in the file.
+ 2208 self.run_check(["-c", "bad.txt"], status=1)
+ 2209 self.assertEqual(
+ 2210 self.output.getvalue(),
+ 2211 "Checking bad.txt\nbad.txt(9): Output has been edited! Delete old checksum to unprotect.\n",
+ 2212 )
+ 2213 self.assert_made_files_unchanged(d)
2214
- 2215class WritabilityTests(TestCaseWithTempDir):
- 2216
+ 2215
+ 2216class WritabilityTests(TestCaseWithTempDir):
2217 d = {
- 2218 'test.cog': """\
+ 2218 "test.cog": """\
2219 //[[[cog
2220 for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']:
2221 cog.outl("void %s();" % fn)
2222 //]]]
2223 //[[[end]]]
2224 """,
- 2225
- 2226 'test.out': """\
- 2227 //[[[cog
- 2228 for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']:
- 2229 cog.outl("void %s();" % fn)
- 2230 //]]]
- 2231 void DoSomething();
- 2232 void DoAnotherThing();
- 2233 void DoLastThing();
- 2234 //[[[end]]]
- 2235 """,
- 2236 }
- 2237
- 2238 if os.name == 'nt': 2238 ↛ 2240line 2238 didn't jump to line 2240, because the condition on line 2238 was never true
- 2239 # for Windows
- 2240 cmd_w_args = 'attrib -R %s'
- 2241 cmd_w_asterisk = 'attrib -R *'
- 2242 else:
- 2243 # for unix-like
- 2244 cmd_w_args = 'chmod +w %s'
- 2245 cmd_w_asterisk = 'chmod +w *'
- 2246
- 2247 def setUp(self):
- 2248 super().setUp()
- 2249 makeFiles(self.d)
- 2250 self.testcog = os.path.join(self.tempdir, 'test.cog')
- 2251 os.chmod(self.testcog, stat.S_IREAD) # Make the file readonly.
- 2252 assert not os.access(self.testcog, os.W_OK)
- 2253
- 2254 def tearDown(self):
- 2255 os.chmod(self.testcog, stat.S_IWRITE) # Make the file writable again.
- 2256 super().tearDown()
- 2257
- 2258 def testReadonlyNoCommand(self):
- 2259 with self.assertRaisesRegex(CogError, "^Can't overwrite test.cog$"):
- 2260 self.cog.callableMain(['argv0', '-r', 'test.cog'])
- 2261 assert not os.access(self.testcog, os.W_OK)
- 2262
- 2263 def testReadonlyWithCommand(self):
- 2264 self.cog.callableMain(['argv0', '-r', '-w', self.cmd_w_args, 'test.cog'])
- 2265 self.assertFilesSame('test.cog', 'test.out')
- 2266 assert os.access(self.testcog, os.W_OK)
- 2267
- 2268 def testReadonlyWithCommandWithNoSlot(self):
- 2269 self.cog.callableMain(['argv0', '-r', '-w', self.cmd_w_asterisk, 'test.cog'])
- 2270 self.assertFilesSame('test.cog', 'test.out')
- 2271 assert os.access(self.testcog, os.W_OK)
- 2272
- 2273 def testReadonlyWithIneffectualCommand(self):
- 2274 with self.assertRaisesRegex(CogError, "^Couldn't make test.cog writable$"):
- 2275 self.cog.callableMain(['argv0', '-r', '-w', 'echo %s', 'test.cog'])
- 2276 assert not os.access(self.testcog, os.W_OK)
+ 2225 "test.out": """\
+ 2226 //[[[cog
+ 2227 for fn in ['DoSomething', 'DoAnotherThing', 'DoLastThing']:
+ 2228 cog.outl("void %s();" % fn)
+ 2229 //]]]
+ 2230 void DoSomething();
+ 2231 void DoAnotherThing();
+ 2232 void DoLastThing();
+ 2233 //[[[end]]]
+ 2234 """,
+ 2235 }
+ 2236
+ 2237 if os.name == "nt": 2237 ↛ 2239line 2237 didn't jump to line 2239, because the condition on line 2237 was never true
+ 2238 # for Windows
+ 2239 cmd_w_args = "attrib -R %s"
+ 2240 cmd_w_asterisk = "attrib -R *"
+ 2241 else:
+ 2242 # for unix-like
+ 2243 cmd_w_args = "chmod +w %s"
+ 2244 cmd_w_asterisk = "chmod +w *"
+ 2245
+ 2246 def setUp(self):
+ 2247 super().setUp()
+ 2248 make_files(self.d)
+ 2249 self.testcog = os.path.join(self.tempdir, "test.cog")
+ 2250 os.chmod(self.testcog, stat.S_IREAD) # Make the file readonly.
+ 2251 assert not os.access(self.testcog, os.W_OK)
+ 2252
+ 2253 def tearDown(self):
+ 2254 os.chmod(self.testcog, stat.S_IWRITE) # Make the file writable again.
+ 2255 super().tearDown()
+ 2256
+ 2257 def test_readonly_no_command(self):
+ 2258 with self.assertRaisesRegex(CogError, "^Can't overwrite test.cog$"):
+ 2259 self.cog.callable_main(["argv0", "-r", "test.cog"])
+ 2260 assert not os.access(self.testcog, os.W_OK)
+ 2261
+ 2262 def test_readonly_with_command(self):
+ 2263 self.cog.callable_main(["argv0", "-r", "-w", self.cmd_w_args, "test.cog"])
+ 2264 self.assertFilesSame("test.cog", "test.out")
+ 2265 assert os.access(self.testcog, os.W_OK)
+ 2266
+ 2267 def test_readonly_with_command_with_no_slot(self):
+ 2268 self.cog.callable_main(["argv0", "-r", "-w", self.cmd_w_asterisk, "test.cog"])
+ 2269 self.assertFilesSame("test.cog", "test.out")
+ 2270 assert os.access(self.testcog, os.W_OK)
+ 2271
+ 2272 def test_readonly_with_ineffectual_command(self):
+ 2273 with self.assertRaisesRegex(CogError, "^Couldn't make test.cog writable$"):
+ 2274 self.cog.callable_main(["argv0", "-r", "-w", "echo %s", "test.cog"])
+ 2275 assert not os.access(self.testcog, os.W_OK)
+ 2276
2277
- 2278
- 2279class ChecksumTests(TestCaseWithTempDir):
- 2280
- 2281 def testCreateChecksumOutput(self):
- 2282 d = {
- 2283 'cog1.txt': """\
- 2284 //[[[cog
- 2285 cog.outl("This line was generated.")
- 2286 //]]]
- 2287 This line was generated.
- 2288 //[[[end]]]
- 2289 This line was not.
- 2290 """,
- 2291
- 2292 'cog1.out': """\
- 2293 //[[[cog
- 2294 cog.outl("This line was generated.")
- 2295 //]]]
- 2296 This line was generated.
- 2297 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893)
- 2298 This line was not.
- 2299 """,
- 2300 }
- 2301
- 2302 makeFiles(d)
- 2303 self.cog.callableMain(['argv0', '-r', '-c', 'cog1.txt'])
- 2304 self.assertFilesSame('cog1.txt', 'cog1.out')
- 2305
- 2306 def testCheckChecksumOutput(self):
- 2307 d = {
- 2308 'cog1.txt': """\
- 2309 //[[[cog
- 2310 cog.outl("This line was newly")
- 2311 cog.outl("generated by cog")
- 2312 cog.outl("blah blah.")
- 2313 //]]]
- 2314 This line was generated.
- 2315 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893)
- 2316 """,
- 2317
- 2318 'cog1.out': """\
- 2319 //[[[cog
- 2320 cog.outl("This line was newly")
- 2321 cog.outl("generated by cog")
- 2322 cog.outl("blah blah.")
- 2323 //]]]
- 2324 This line was newly
- 2325 generated by cog
- 2326 blah blah.
- 2327 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
- 2328 """,
- 2329 }
+ 2278class ChecksumTests(TestCaseWithTempDir):
+ 2279 def test_create_checksum_output(self):
+ 2280 d = {
+ 2281 "cog1.txt": """\
+ 2282 //[[[cog
+ 2283 cog.outl("This line was generated.")
+ 2284 //]]]
+ 2285 This line was generated.
+ 2286 //[[[end]]]
+ 2287 This line was not.
+ 2288 """,
+ 2289 "cog1.out": """\
+ 2290 //[[[cog
+ 2291 cog.outl("This line was generated.")
+ 2292 //]]]
+ 2293 This line was generated.
+ 2294 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893)
+ 2295 This line was not.
+ 2296 """,
+ 2297 }
+ 2298
+ 2299 make_files(d)
+ 2300 self.cog.callable_main(["argv0", "-r", "-c", "cog1.txt"])
+ 2301 self.assertFilesSame("cog1.txt", "cog1.out")
+ 2302
+ 2303 def test_check_checksum_output(self):
+ 2304 d = {
+ 2305 "cog1.txt": """\
+ 2306 //[[[cog
+ 2307 cog.outl("This line was newly")
+ 2308 cog.outl("generated by cog")
+ 2309 cog.outl("blah blah.")
+ 2310 //]]]
+ 2311 This line was generated.
+ 2312 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893)
+ 2313 """,
+ 2314 "cog1.out": """\
+ 2315 //[[[cog
+ 2316 cog.outl("This line was newly")
+ 2317 cog.outl("generated by cog")
+ 2318 cog.outl("blah blah.")
+ 2319 //]]]
+ 2320 This line was newly
+ 2321 generated by cog
+ 2322 blah blah.
+ 2323 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
+ 2324 """,
+ 2325 }
+ 2326
+ 2327 make_files(d)
+ 2328 self.cog.callable_main(["argv0", "-r", "-c", "cog1.txt"])
+ 2329 self.assertFilesSame("cog1.txt", "cog1.out")
2330
- 2331 makeFiles(d)
- 2332 self.cog.callableMain(['argv0', '-r', '-c', 'cog1.txt'])
- 2333 self.assertFilesSame('cog1.txt', 'cog1.out')
- 2334
- 2335 def testRemoveChecksumOutput(self):
- 2336 d = {
- 2337 'cog1.txt': """\
- 2338 //[[[cog
- 2339 cog.outl("This line was newly")
- 2340 cog.outl("generated by cog")
- 2341 cog.outl("blah blah.")
- 2342 //]]]
- 2343 This line was generated.
- 2344 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893) fooey
- 2345 """,
- 2346
- 2347 'cog1.out': """\
- 2348 //[[[cog
- 2349 cog.outl("This line was newly")
- 2350 cog.outl("generated by cog")
- 2351 cog.outl("blah blah.")
- 2352 //]]]
- 2353 This line was newly
- 2354 generated by cog
- 2355 blah blah.
- 2356 //[[[end]]] fooey
- 2357 """,
- 2358 }
- 2359
- 2360 makeFiles(d)
- 2361 self.cog.callableMain(['argv0', '-r', 'cog1.txt'])
- 2362 self.assertFilesSame('cog1.txt', 'cog1.out')
- 2363
- 2364 def testTamperedChecksumOutput(self):
- 2365 d = {
- 2366 'cog1.txt': """\
- 2367 //[[[cog
- 2368 cog.outl("This line was newly")
- 2369 cog.outl("generated by cog")
- 2370 cog.outl("blah blah.")
- 2371 //]]]
- 2372 Xhis line was newly
- 2373 generated by cog
- 2374 blah blah.
- 2375 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
- 2376 """,
- 2377
- 2378 'cog2.txt': """\
- 2379 //[[[cog
- 2380 cog.outl("This line was newly")
- 2381 cog.outl("generated by cog")
- 2382 cog.outl("blah blah.")
- 2383 //]]]
- 2384 This line was newly
- 2385 generated by cog
- 2386 blah blah!
- 2387 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
- 2388 """,
+ 2331 def test_remove_checksum_output(self):
+ 2332 d = {
+ 2333 "cog1.txt": """\
+ 2334 //[[[cog
+ 2335 cog.outl("This line was newly")
+ 2336 cog.outl("generated by cog")
+ 2337 cog.outl("blah blah.")
+ 2338 //]]]
+ 2339 This line was generated.
+ 2340 //[[[end]]] (checksum: 8adb13fb59b996a1c7f0065ea9f3d893) fooey
+ 2341 """,
+ 2342 "cog1.out": """\
+ 2343 //[[[cog
+ 2344 cog.outl("This line was newly")
+ 2345 cog.outl("generated by cog")
+ 2346 cog.outl("blah blah.")
+ 2347 //]]]
+ 2348 This line was newly
+ 2349 generated by cog
+ 2350 blah blah.
+ 2351 //[[[end]]] fooey
+ 2352 """,
+ 2353 }
+ 2354
+ 2355 make_files(d)
+ 2356 self.cog.callable_main(["argv0", "-r", "cog1.txt"])
+ 2357 self.assertFilesSame("cog1.txt", "cog1.out")
+ 2358
+ 2359 def test_tampered_checksum_output(self):
+ 2360 d = {
+ 2361 "cog1.txt": """\
+ 2362 //[[[cog
+ 2363 cog.outl("This line was newly")
+ 2364 cog.outl("generated by cog")
+ 2365 cog.outl("blah blah.")
+ 2366 //]]]
+ 2367 Xhis line was newly
+ 2368 generated by cog
+ 2369 blah blah.
+ 2370 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
+ 2371 """,
+ 2372 "cog2.txt": """\
+ 2373 //[[[cog
+ 2374 cog.outl("This line was newly")
+ 2375 cog.outl("generated by cog")
+ 2376 cog.outl("blah blah.")
+ 2377 //]]]
+ 2378 This line was newly
+ 2379 generated by cog
+ 2380 blah blah!
+ 2381 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
+ 2382 """,
+ 2383 "cog3.txt": """\
+ 2384 //[[[cog
+ 2385 cog.outl("This line was newly")
+ 2386 cog.outl("generated by cog")
+ 2387 cog.outl("blah blah.")
+ 2388 //]]]
2389
- 2390 'cog3.txt': """\
- 2391 //[[[cog
- 2392 cog.outl("This line was newly")
- 2393 cog.outl("generated by cog")
- 2394 cog.outl("blah blah.")
- 2395 //]]]
- 2396
- 2397 This line was newly
- 2398 generated by cog
- 2399 blah blah.
- 2400 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
- 2401 """,
- 2402
- 2403 'cog4.txt': """\
- 2404 //[[[cog
- 2405 cog.outl("This line was newly")
- 2406 cog.outl("generated by cog")
- 2407 cog.outl("blah blah.")
- 2408 //]]]
- 2409 This line was newly
- 2410 generated by cog
- 2411 blah blah..
- 2412 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
- 2413 """,
- 2414
- 2415 'cog5.txt': """\
- 2416 //[[[cog
- 2417 cog.outl("This line was newly")
- 2418 cog.outl("generated by cog")
- 2419 cog.outl("blah blah.")
- 2420 //]]]
- 2421 This line was newly
- 2422 generated by cog
- 2423 blah blah.
- 2424 extra
- 2425 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
- 2426 """,
+ 2390 This line was newly
+ 2391 generated by cog
+ 2392 blah blah.
+ 2393 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
+ 2394 """,
+ 2395 "cog4.txt": """\
+ 2396 //[[[cog
+ 2397 cog.outl("This line was newly")
+ 2398 cog.outl("generated by cog")
+ 2399 cog.outl("blah blah.")
+ 2400 //]]]
+ 2401 This line was newly
+ 2402 generated by cog
+ 2403 blah blah..
+ 2404 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
+ 2405 """,
+ 2406 "cog5.txt": """\
+ 2407 //[[[cog
+ 2408 cog.outl("This line was newly")
+ 2409 cog.outl("generated by cog")
+ 2410 cog.outl("blah blah.")
+ 2411 //]]]
+ 2412 This line was newly
+ 2413 generated by cog
+ 2414 blah blah.
+ 2415 extra
+ 2416 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
+ 2417 """,
+ 2418 "cog6.txt": """\
+ 2419 //[[[cog
+ 2420 cog.outl("This line was newly")
+ 2421 cog.outl("generated by cog")
+ 2422 cog.outl("blah blah.")
+ 2423 //]]]
+ 2424 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
+ 2425 """,
+ 2426 }
2427
- 2428 'cog6.txt': """\
- 2429 //[[[cog
- 2430 cog.outl("This line was newly")
- 2431 cog.outl("generated by cog")
- 2432 cog.outl("blah blah.")
- 2433 //]]]
- 2434 //[[[end]]] (checksum: a8540982e5ad6b95c9e9a184b26f4346)
- 2435 """,
- 2436 }
- 2437
- 2438 makeFiles(d)
- 2439 with self.assertRaisesRegex(CogError,
- 2440 r"^cog1.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"):
- 2441 self.cog.callableMain(['argv0', '-c', "cog1.txt"])
- 2442 with self.assertRaisesRegex(CogError,
- 2443 r"^cog2.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"):
- 2444 self.cog.callableMain(['argv0', '-c', "cog2.txt"])
- 2445 with self.assertRaisesRegex(CogError,
- 2446 r"^cog3.txt\(10\): Output has been edited! Delete old checksum to unprotect.$"):
- 2447 self.cog.callableMain(['argv0', '-c', "cog3.txt"])
- 2448 with self.assertRaisesRegex(CogError,
- 2449 r"^cog4.txt\(9\): Output has been edited! Delete old checksum to unprotect.$"):
- 2450 self.cog.callableMain(['argv0', '-c', "cog4.txt"])
- 2451 with self.assertRaisesRegex(CogError,
- 2452 r"^cog5.txt\(10\): Output has been edited! Delete old checksum to unprotect.$"):
- 2453 self.cog.callableMain(['argv0', '-c', "cog5.txt"])
- 2454 with self.assertRaisesRegex(CogError,
- 2455 r"^cog6.txt\(6\): Output has been edited! Delete old checksum to unprotect.$"):
- 2456 self.cog.callableMain(['argv0', '-c', "cog6.txt"])
- 2457
- 2458 def testArgvIsntModified(self):
- 2459 argv = ['argv0', '-v']
- 2460 orig_argv = argv[:]
- 2461 self.cog.callableMain(argv)
- 2462 self.assertEqual(argv, orig_argv)
- 2463
- 2464
- 2465class CustomMarkerTests(TestCaseWithTempDir):
+ 2428 make_files(d)
+ 2429 with self.assertRaisesRegex(
+ 2430 CogError,
+ 2431 r"^cog1.txt\(9\): Output has been edited! Delete old checksum to unprotect.$",
+ 2432 ):
+ 2433 self.cog.callable_main(["argv0", "-c", "cog1.txt"])
+ 2434 with self.assertRaisesRegex(
+ 2435 CogError,
+ 2436 r"^cog2.txt\(9\): Output has been edited! Delete old checksum to unprotect.$",
+ 2437 ):
+ 2438 self.cog.callable_main(["argv0", "-c", "cog2.txt"])
+ 2439 with self.assertRaisesRegex(
+ 2440 CogError,
+ 2441 r"^cog3.txt\(10\): Output has been edited! Delete old checksum to unprotect.$",
+ 2442 ):
+ 2443 self.cog.callable_main(["argv0", "-c", "cog3.txt"])
+ 2444 with self.assertRaisesRegex(
+ 2445 CogError,
+ 2446 r"^cog4.txt\(9\): Output has been edited! Delete old checksum to unprotect.$",
+ 2447 ):
+ 2448 self.cog.callable_main(["argv0", "-c", "cog4.txt"])
+ 2449 with self.assertRaisesRegex(
+ 2450 CogError,
+ 2451 r"^cog5.txt\(10\): Output has been edited! Delete old checksum to unprotect.$",
+ 2452 ):
+ 2453 self.cog.callable_main(["argv0", "-c", "cog5.txt"])
+ 2454 with self.assertRaisesRegex(
+ 2455 CogError,
+ 2456 r"^cog6.txt\(6\): Output has been edited! Delete old checksum to unprotect.$",
+ 2457 ):
+ 2458 self.cog.callable_main(["argv0", "-c", "cog6.txt"])
+ 2459
+ 2460 def test_argv_isnt_modified(self):
+ 2461 argv = ["argv0", "-v"]
+ 2462 orig_argv = argv[:]
+ 2463 self.cog.callable_main(argv)
+ 2464 self.assertEqual(argv, orig_argv)
+ 2465
2466
- 2467 def testCustomerMarkers(self):
- 2468 d = {
- 2469 'test.cog': """\
- 2470 //{{
- 2471 cog.outl("void %s();" % "MyFunction")
- 2472 //}}
- 2473 //{{end}}
- 2474 """,
- 2475
- 2476 'test.out': """\
+ 2467class CustomMarkerTests(TestCaseWithTempDir):
+ 2468 def test_customer_markers(self):
+ 2469 d = {
+ 2470 "test.cog": """\
+ 2471 //{{
+ 2472 cog.outl("void %s();" % "MyFunction")
+ 2473 //}}
+ 2474 //{{end}}
+ 2475 """,
+ 2476 "test.out": """\
2477 //{{
2478 cog.outl("void %s();" % "MyFunction")
2479 //}}
2480 void MyFunction();
2481 //{{end}}
2482 """,
- 2483 }
+ 2483 }
2484
- 2485 makeFiles(d)
- 2486 self.cog.callableMain([
- 2487 'argv0', '-r',
- 2488 '--markers={{ }} {{end}}',
- 2489 'test.cog'
- 2490 ])
- 2491 self.assertFilesSame('test.cog', 'test.out')
- 2492
- 2493 def testTrulyWackyMarkers(self):
- 2494 # Make sure the markers are properly re-escaped.
- 2495 d = {
- 2496 'test.cog': """\
- 2497 //**(
- 2498 cog.outl("void %s();" % "MyFunction")
- 2499 //**)
- 2500 //**(end)**
- 2501 """,
- 2502
- 2503 'test.out': """\
- 2504 //**(
- 2505 cog.outl("void %s();" % "MyFunction")
- 2506 //**)
- 2507 void MyFunction();
- 2508 //**(end)**
- 2509 """,
- 2510 }
- 2511
- 2512 makeFiles(d)
- 2513 self.cog.callableMain([
- 2514 'argv0', '-r',
- 2515 '--markers=**( **) **(end)**',
- 2516 'test.cog'
- 2517 ])
- 2518 self.assertFilesSame('test.cog', 'test.out')
- 2519
- 2520 def testChangeJustOneMarker(self):
- 2521 d = {
- 2522 'test.cog': """\
- 2523 //**(
- 2524 cog.outl("void %s();" % "MyFunction")
- 2525 //]]]
+ 2485 make_files(d)
+ 2486 self.cog.callable_main(["argv0", "-r", "--markers={{ }} {{end}}", "test.cog"])
+ 2487 self.assertFilesSame("test.cog", "test.out")
+ 2488
+ 2489 def test_truly_wacky_markers(self):
+ 2490 # Make sure the markers are properly re-escaped.
+ 2491 d = {
+ 2492 "test.cog": """\
+ 2493 //**(
+ 2494 cog.outl("void %s();" % "MyFunction")
+ 2495 //**)
+ 2496 //**(end)**
+ 2497 """,
+ 2498 "test.out": """\
+ 2499 //**(
+ 2500 cog.outl("void %s();" % "MyFunction")
+ 2501 //**)
+ 2502 void MyFunction();
+ 2503 //**(end)**
+ 2504 """,
+ 2505 }
+ 2506
+ 2507 make_files(d)
+ 2508 self.cog.callable_main(
+ 2509 ["argv0", "-r", "--markers=**( **) **(end)**", "test.cog"]
+ 2510 )
+ 2511 self.assertFilesSame("test.cog", "test.out")
+ 2512
+ 2513 def test_change_just_one_marker(self):
+ 2514 d = {
+ 2515 "test.cog": """\
+ 2516 //**(
+ 2517 cog.outl("void %s();" % "MyFunction")
+ 2518 //]]]
+ 2519 //[[[end]]]
+ 2520 """,
+ 2521 "test.out": """\
+ 2522 //**(
+ 2523 cog.outl("void %s();" % "MyFunction")
+ 2524 //]]]
+ 2525 void MyFunction();
2526 //[[[end]]]
2527 """,
- 2528
- 2529 'test.out': """\
- 2530 //**(
- 2531 cog.outl("void %s();" % "MyFunction")
- 2532 //]]]
- 2533 void MyFunction();
- 2534 //[[[end]]]
- 2535 """,
- 2536 }
- 2537
- 2538 makeFiles(d)
- 2539 self.cog.callableMain([
- 2540 'argv0', '-r',
- 2541 '--markers=**( ]]] [[[end]]]',
- 2542 'test.cog'
- 2543 ])
- 2544 self.assertFilesSame('test.cog', 'test.out')
- 2545
- 2546
- 2547class BlakeTests(TestCaseWithTempDir):
- 2548
- 2549 # Blake Winton's contributions.
- 2550 def testDeleteCode(self):
- 2551 # -o sets the output file.
- 2552 d = {
- 2553 'test.cog': """\
+ 2528 }
+ 2529
+ 2530 make_files(d)
+ 2531 self.cog.callable_main(
+ 2532 ["argv0", "-r", "--markers=**( ]]] [[[end]]]", "test.cog"]
+ 2533 )
+ 2534 self.assertFilesSame("test.cog", "test.out")
+ 2535
+ 2536
+ 2537class BlakeTests(TestCaseWithTempDir):
+ 2538 # Blake Winton's contributions.
+ 2539 def test_delete_code(self):
+ 2540 # -o sets the output file.
+ 2541 d = {
+ 2542 "test.cog": """\
+ 2543 // This is my C++ file.
+ 2544 //[[[cog
+ 2545 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
+ 2546 for fn in fnames:
+ 2547 cog.outl("void %s();" % fn)
+ 2548 //]]]
+ 2549 Some Sample Code Here
+ 2550 //[[[end]]]Data Data
+ 2551 And Some More
+ 2552 """,
+ 2553 "test.out": """\
2554 // This is my C++ file.
- 2555 //[[[cog
- 2556 fnames = ['DoSomething', 'DoAnotherThing', 'DoLastThing']
- 2557 for fn in fnames:
- 2558 cog.outl("void %s();" % fn)
- 2559 //]]]
- 2560 Some Sample Code Here
- 2561 //[[[end]]]Data Data
- 2562 And Some More
- 2563 """,
- 2564
- 2565 'test.out': """\
- 2566 // This is my C++ file.
- 2567 void DoSomething();
- 2568 void DoAnotherThing();
- 2569 void DoLastThing();
- 2570 And Some More
- 2571 """,
- 2572 }
- 2573
- 2574 makeFiles(d)
- 2575 self.cog.callableMain(['argv0', '-d', '-o', 'test.cogged', 'test.cog'])
- 2576 self.assertFilesSame('test.cogged', 'test.out')
- 2577
- 2578 def testDeleteCodeWithDashRFails(self):
- 2579 d = {
- 2580 'test.cog': """\
- 2581 // This is my C++ file.
- 2582 """
- 2583 }
- 2584
- 2585 makeFiles(d)
- 2586 with self.assertRaisesRegex(CogUsageError, r"^Can't use -d with -r \(or you would delete all your source!\)$"):
- 2587 self.cog.callableMain(['argv0', '-r', '-d', 'test.cog'])
- 2588
- 2589 def testSettingGlobals(self):
- 2590 # Blake Winton contributed a way to set the globals that will be used in
- 2591 # processFile().
- 2592 d = {
- 2593 'test.cog': """\
- 2594 // This is my C++ file.
- 2595 //[[[cog
- 2596 for fn in fnames:
- 2597 cog.outl("void %s();" % fn)
- 2598 //]]]
- 2599 Some Sample Code Here
- 2600 //[[[end]]]""",
- 2601
- 2602 'test.out': """\
- 2603 // This is my C++ file.
- 2604 void DoBlake();
- 2605 void DoWinton();
- 2606 void DoContribution();
- 2607 """,
- 2608 }
- 2609
- 2610 makeFiles(d)
- 2611 globals = {}
- 2612 globals['fnames'] = ['DoBlake', 'DoWinton', 'DoContribution']
- 2613 self.cog.options.bDeleteCode = True
- 2614 self.cog.processFile('test.cog', 'test.cogged', globals=globals)
- 2615 self.assertFilesSame('test.cogged', 'test.out')
- 2616
- 2617
- 2618class ErrorCallTests(TestCaseWithTempDir):
+ 2555 void DoSomething();
+ 2556 void DoAnotherThing();
+ 2557 void DoLastThing();
+ 2558 And Some More
+ 2559 """,
+ 2560 }
+ 2561
+ 2562 make_files(d)
+ 2563 self.cog.callable_main(["argv0", "-d", "-o", "test.cogged", "test.cog"])
+ 2564 self.assertFilesSame("test.cogged", "test.out")
+ 2565
+ 2566 def test_delete_code_with_dash_r_fails(self):
+ 2567 d = {
+ 2568 "test.cog": """\
+ 2569 // This is my C++ file.
+ 2570 """
+ 2571 }
+ 2572
+ 2573 make_files(d)
+ 2574 with self.assertRaisesRegex(
+ 2575 CogUsageError,
+ 2576 r"^Can't use -d with -r \(or you would delete all your source!\)$",
+ 2577 ):
+ 2578 self.cog.callable_main(["argv0", "-r", "-d", "test.cog"])
+ 2579
+ 2580 def test_setting_globals(self):
+ 2581 # Blake Winton contributed a way to set the globals that will be used in
+ 2582 # processFile().
+ 2583 d = {
+ 2584 "test.cog": """\
+ 2585 // This is my C++ file.
+ 2586 //[[[cog
+ 2587 for fn in fnames:
+ 2588 cog.outl("void %s();" % fn)
+ 2589 //]]]
+ 2590 Some Sample Code Here
+ 2591 //[[[end]]]""",
+ 2592 "test.out": """\
+ 2593 // This is my C++ file.
+ 2594 void DoBlake();
+ 2595 void DoWinton();
+ 2596 void DoContribution();
+ 2597 """,
+ 2598 }
+ 2599
+ 2600 make_files(d)
+ 2601 globals = {}
+ 2602 globals["fnames"] = ["DoBlake", "DoWinton", "DoContribution"]
+ 2603 self.cog.options.delete_code = True
+ 2604 self.cog.process_file("test.cog", "test.cogged", globals=globals)
+ 2605 self.assertFilesSame("test.cogged", "test.out")
+ 2606
+ 2607
+ 2608class ErrorCallTests(TestCaseWithTempDir):
+ 2609 def test_error_call_has_no_traceback(self):
+ 2610 # Test that cog.error() doesn't show a traceback.
+ 2611 d = {
+ 2612 "error.cog": """\
+ 2613 //[[[cog
+ 2614 cog.error("Something Bad!")
+ 2615 //]]]
+ 2616 //[[[end]]]
+ 2617 """,
+ 2618 }
2619
- 2620 def testErrorCallHasNoTraceback(self):
- 2621 # Test that cog.error() doesn't show a traceback.
- 2622 d = {
- 2623 'error.cog': """\
- 2624 //[[[cog
- 2625 cog.error("Something Bad!")
- 2626 //]]]
- 2627 //[[[end]]]
- 2628 """,
- 2629 }
- 2630
- 2631 makeFiles(d)
- 2632 self.cog.main(['argv0', '-r', 'error.cog'])
- 2633 output = self.output.getvalue()
- 2634 self.assertEqual(output, "Cogging error.cog\nError: Something Bad!\n")
+ 2620 make_files(d)
+ 2621 self.cog.main(["argv0", "-r", "error.cog"])
+ 2622 output = self.output.getvalue()
+ 2623 self.assertEqual(output, "Cogging error.cog\nError: Something Bad!\n")
+ 2624
+ 2625 def test_real_error_has_traceback(self):
+ 2626 # Test that a genuine error does show a traceback.
+ 2627 d = {
+ 2628 "error.cog": """\
+ 2629 //[[[cog
+ 2630 raise RuntimeError("Hey!")
+ 2631 //]]]
+ 2632 //[[[end]]]
+ 2633 """,
+ 2634 }
2635
- 2636 def testRealErrorHasTraceback(self):
- 2637 # Test that a genuine error does show a traceback.
- 2638 d = {
- 2639 'error.cog': """\
- 2640 //[[[cog
- 2641 raise RuntimeError("Hey!")
- 2642 //]]]
- 2643 //[[[end]]]
- 2644 """,
- 2645 }
- 2646
- 2647 makeFiles(d)
- 2648 self.cog.main(['argv0', '-r', 'error.cog'])
- 2649 output = self.output.getvalue()
- 2650 msg = 'Actual output:\n' + output
- 2651 self.assertTrue(output.startswith("Cogging error.cog\nTraceback (most recent"), msg)
- 2652 self.assertIn("RuntimeError: Hey!", output)
- 2653
- 2654
- 2655# Things not yet tested:
- 2656# - A bad -w command (currently fails silently).
+ 2636 make_files(d)
+ 2637 self.cog.main(["argv0", "-r", "error.cog"])
+ 2638 output = self.output.getvalue()
+ 2639 msg = "Actual output:\n" + output
+ 2640 self.assertTrue(
+ 2641 output.startswith("Cogging error.cog\nTraceback (most recent"), msg
+ 2642 )
+ 2643 self.assertIn("RuntimeError: Hey!", output)
+ 2644
+ 2645
+ 2646# Things not yet tested:
+ 2647# - A bad -w command (currently fails silently).