Package lepl :: Package matchers :: Module variables
[hide private]
[frames] | no frames]

Source Code for Module lepl.matchers.variables

  1   
  2  # The contents of this file are subject to the Mozilla Public License 
  3  # (MPL) Version 1.1 (the "License"); you may not use this file except 
  4  # in compliance with the License. You may obtain a copy of the License 
  5  # at http://www.mozilla.org/MPL/ 
  6  # 
  7  # Software distributed under the License is distributed on an "AS IS" 
  8  # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 
  9  # the License for the specific language governing rights and 
 10  # limitations under the License. 
 11  # 
 12  # The Original Code is LEPL (http://www.acooke.org/lepl) 
 13  # The Initial Developer of the Original Code is Andrew Cooke. 
 14  # Portions created by the Initial Developer are Copyright (C) 2009-2010 
 15  # Andrew Cooke (andrew@acooke.org). All Rights Reserved. 
 16  # 
 17  # Alternatively, the contents of this file may be used under the terms 
 18  # of the LGPL license (the GNU Lesser General Public License, 
 19  # http://www.gnu.org/licenses/lgpl.html), in which case the provisions 
 20  # of the LGPL License are applicable instead of those above. 
 21  # 
 22  # If you wish to allow use of your version of this file only under the 
 23  # terms of the LGPL License and not to allow others to use your version 
 24  # of this file under the MPL, indicate your decision by deleting the 
 25  # provisions above and replace them with the notice and other provisions 
 26  # required by the LGPL License.  If you do not delete the provisions 
 27  # above, a recipient may use your version of this file under either the 
 28  # MPL or the LGPL License. 
 29   
 30  ''' 
 31  Display information when matchers that are bound to variables are called. 
 32   
 33  This is possible thanks to a neat trick suggested by Carl Banks on c.l.p  
 34  ''' 
 35   
 36  from __future__ import generators, print_function 
 37  from contextlib import contextmanager 
 38  from sys import stderr, _getframe 
 39   
 40  from lepl.stream.core import s_debug, s_line, s_kargs 
 41  from lepl.matchers.support import trampoline_matcher_factory 
 42  from lepl.support.lib import fmt, str 
43 44 45 @trampoline_matcher_factory() 46 -def NamedResult(name, matcher, out=stderr):
47 48 def fmt_stream(stream): 49 try: 50 (line, _) = s_line(stream, False) 51 text = str(line) 52 if len(text) > 20: 53 text = text[:17] + '...' 54 return repr(text) 55 except StopIteration: 56 return '<EOS>'
57 58 def record_success(count, stream_in, result): 59 (value, stream_out) = result 60 count_desc = fmt(' ({0})', count) if count > 1 else '' 61 # Python bug #4618 62 print(fmt('{0}{1} = {2}\n {3} -> {4}', 63 name, count_desc, value, 64 fmt_stream(stream_in), fmt_stream(stream_out)), 65 file=out, end=str('\n')) 66 67 def record_failure(count, stream_in): 68 # Python bug #4618 69 print(fmt('! {0} (after {1} matches)\n {2}', name, count, 70 fmt_stream(stream_in)), 71 file=out, end=str('\n')) 72 73 def match(support, stream): 74 count = 0 75 generator = matcher._match(stream) 76 try: 77 while True: 78 value = yield generator 79 count += 1 80 record_success(count, stream, value) 81 yield value 82 except StopIteration: 83 record_failure(count, stream) 84 85 return match 86
87 88 -def _adjust(text, width, pad=False, left=False):
89 if len(text) > width: 90 text = text[:width-3] + '...' 91 if pad and len(text) < width: 92 space = ' ' * (width - len(text)) 93 if left: 94 text = space + text 95 else: 96 text = text + space 97 return text
98
99 100 -def name(name, show_failures=True, width=80, out=stderr):
101 102 left = 3 * width // 5 - 1 103 right = 2 * width // 5 - 1 104 105 def namer(stream_in, matcher): 106 try: 107 (result, stream_out) = matcher() 108 except StopIteration: 109 if show_failures: 110 stream = \ 111 _adjust(fmt('stream = {rest}', **s_kargs(stream_in)), 112 right) 113 str_name = _adjust(name, left // 4, True, True) 114 match = _adjust(fmt(' {0} failed', str_name), left, True) 115 # Python bug #4618 116 print(match + ' ' + stream, file=out, end=str('\n')) 117 raise StopIteration 118 else: 119 try: 120 try: 121 rest = fmt('{rest}', **s_kargs(stream_out)) 122 except StopIteration: 123 rest = '<EOS>' 124 stream = _adjust(fmt('stream = {0}', rest), right) 125 str_name = _adjust(name, left // 4, True, True) 126 match = _adjust(fmt(' {0} = {1}', str_name, result), left, True) 127 # Python bug #4618 128 print(match + ' ' + stream, file=out, end=str('\n')) 129 return (result, stream_out) 130 except Exception as e: 131 print('Error in trace', file=out, end=str('\n')) 132 print(repr(e), file=out, end=str('\n')) 133 return (result, stream_out)
134 135 return namer 136
137 138 @contextmanager 139 -def TraceVariables(on=True, show_failures=True, width=80, out=stderr):
140 ''' 141 Add this as a context (`with TraceVariables():`) and you will see 142 debug logging indicating how variables are bound during matching. 143 ''' 144 if on: 145 before = _getframe(2).f_locals.copy() 146 yield None 147 if on: 148 after = _getframe(2).f_locals 149 for key in after: 150 value = after[key] 151 if key not in before or value != before[key]: 152 try: 153 try: 154 value.wrapper.append(name(key, show_failures, width, out)) 155 except AttributeError: 156 value.trace_variables = name(key, show_failures, width, out) 157 except: # what exception? 158 print('Unfortunately the following matchers cannot ' 159 'be tracked:', end=str('\n')) 160 print(fmt(' {0} = {1}', key, value), end=str('\n'))
161