@@ -2021,10 +2021,12 @@ def parsed(self, raw):
20212021class HistoryItem (str ):
20222022 """Class used to represent an item in the History list.
20232023
2024- Thing wrapper around str class which adds a custom format for printing. It also keeps track of its index in the
2025- list as well as a lowercase representation of itself for convenience/efficiency.
2024+ Thin wrapper around str class which adds a custom format for printing. It
2025+ also keeps track of its index in the list as well as a lowercase
2026+ representation of itself for convenience/efficiency.
2027+
20262028 """
2027- listformat = '-------------------------[%d ]\n %s \n '
2029+ listformat = '-------------------------[{} ]\n {} \n '
20282030
20292031 # noinspection PyUnusedLocal
20302032 def __init__ (self , instr ):
@@ -2037,7 +2039,7 @@ def pr(self):
20372039
20382040 :return: str - pretty print string version of a HistoryItem
20392041 """
2040- return self .listformat % (self .idx , str (self ))
2042+ return self .listformat . format (self .idx , str (self ). rstrip ( ))
20412043
20422044
20432045class History (list ):
@@ -2230,12 +2232,6 @@ class Cmd2TestCase(unittest.TestCase):
22302232 that will execute the commands in a transcript file and expect the results shown.
22312233 See example.py"""
22322234 cmdapp = None
2233- regexPattern = pyparsing .QuotedString (quoteChar = r'/' , escChar = '\\ ' , multiline = True , unquoteResults = True )
2234- regexPattern .ignore (pyparsing .cStyleComment )
2235- notRegexPattern = pyparsing .Word (pyparsing .printables )
2236- notRegexPattern .setParseAction (lambda t : re .escape (t [0 ]))
2237- expectationParser = regexPattern | notRegexPattern
2238- anyWhitespace = re .compile (r'\s' , re .DOTALL | re .MULTILINE )
22392235
22402236 def fetchTranscripts (self ):
22412237 self .transcripts = {}
@@ -2295,8 +2291,8 @@ def _test_transcript(self, fname, transcript):
22952291 result = self .cmdapp .stdout .read ()
22962292 # Read the expected result from transcript
22972293 if strip_ansi (line ).startswith (self .cmdapp .visible_prompt ):
2298- message = '\n File %s , line %d \n Command was:\n %r \n Expected: (nothing)\n Got:\n %r \n ' % \
2299- ( fname , line_num , command , result )
2294+ message = '\n File {} , line {} \n Command was:\n {} \n Expected: (nothing)\n Got:\n {} \n ' . format (
2295+ fname , line_num , command , result )
23002296 self .assert_ (not (result .strip ()), message )
23012297 continue
23022298 expected = []
@@ -2309,15 +2305,95 @@ def _test_transcript(self, fname, transcript):
23092305 break
23102306 line_num += 1
23112307 expected = '' .join (expected )
2312- # Compare actual result to expected
2313- message = '\n File %s, line %d\n Command was:\n %s\n Expected:\n %s\n Got:\n %s\n ' % \
2314- (fname , line_num , command , expected , result )
2315- expected = self .expectationParser .transformString (expected )
2316- # checking whitespace is a pain - let's skip it
2317- expected = self .anyWhitespace .sub ('' , expected )
2318- result = self .anyWhitespace .sub ('' , result )
2308+
2309+ # transform the expected text into a valid regular expression
2310+ expected = self ._transform_transcript_expected (expected )
2311+ message = '\n File {}, line {}\n Command was:\n {}\n Expected:\n {}\n Got:\n {}\n ' .format (
2312+ fname , line_num , command , expected , result )
23192313 self .assertTrue (re .match (expected , result , re .MULTILINE | re .DOTALL ), message )
23202314
2315+ def _transform_transcript_expected (self , s ):
2316+ """parse the string with slashed regexes into a valid regex"""
2317+ slash = '/'
2318+ backslash = '\\ '
2319+ regex = ''
2320+ start = 0
2321+
2322+ while True :
2323+ (regex , first_slash_pos , start ) = self ._escaped_find (regex , s , start , False )
2324+ if first_slash_pos == - 1 :
2325+ # no more slashes, add the rest of the string and bail
2326+ regex += re .escape (s [start :])
2327+ break
2328+ else :
2329+ # there is a slash, add everything we have found so far
2330+ # add stuff before the first slash as plain text
2331+ regex += re .escape (s [start :first_slash_pos ])
2332+ start = first_slash_pos + 1
2333+ # and go find the next one
2334+ (regex , second_slash_pos , start ) = self ._escaped_find (regex , s , start , True )
2335+ if second_slash_pos > 0 :
2336+ # add everything between the slashes (but not the slashes)
2337+ # as a regular expression
2338+ regex += s [start :second_slash_pos ]
2339+ # and change where we start looking for slashed on the
2340+ # turn through the loop
2341+ start = second_slash_pos + 1
2342+ else :
2343+ # No closing slash, we have to add the first slash,
2344+ # and the rest of the text
2345+ regex += re .escape (s [start - 1 :])
2346+ break
2347+ return regex
2348+
2349+ def _escaped_find (self , regex , s , start , in_regex ):
2350+ """
2351+ Find the next slash in {s} after {start} that is not preceded by a backslash.
2352+
2353+ If we find an escaped slash, add everything up to and including it to regex,
2354+ updating {start}. {start} therefore serves two purposes, tells us where to start
2355+ looking for the next thing, and also tells us where in {s} we have already
2356+ added things to {regex}
2357+
2358+ {in_regex} specifies whether we are currently searching in a regex, we behave
2359+ differently if we are or if we aren't.
2360+ """
2361+
2362+ while True :
2363+ pos = s .find ('/' , start )
2364+ if pos == - 1 :
2365+ # no match, return to caller
2366+ break
2367+ elif pos == 0 :
2368+ # slash at the beginning of the string, so it can't be
2369+ # escaped. We found it.
2370+ break
2371+ else :
2372+ # check if the slash is preceeded by a backslash
2373+ if s [pos - 1 :pos ] == '\\ ' :
2374+ # it is.
2375+ if in_regex :
2376+ # add everything up to the backslash as a
2377+ # regular expression
2378+ regex += s [start :pos - 1 ]
2379+ # skip the backslash, and add the slash
2380+ regex += s [pos ]
2381+ else :
2382+ # add everything up to the backslash as escaped
2383+ # plain text
2384+ regex += re .escape (s [start :pos - 1 ])
2385+ # and then add the slash as escaped
2386+ # plain text
2387+ regex += re .escape (s [pos ])
2388+ # update start to show we have handled everything
2389+ # before it
2390+ start = pos + 1
2391+ # and continue to look
2392+ else :
2393+ # slash is not escaped, this is what we are looking for
2394+ break
2395+ return (regex , pos , start )
2396+
23212397 def tearDown (self ):
23222398 if self .cmdapp :
23232399 # Restore stdout
0 commit comments