[py-svn] r7915 - py/dist/py/code

hpk at codespeak.net hpk at codespeak.net
Sat Dec 18 12:24:21 CET 2004


Author: hpk
Date: Sat Dec 18 12:24:20 2004
New Revision: 7915

Modified:
   py/dist/py/code/source.py
   py/dist/py/code/test_source.py
Log:
some more refactoring of the Source object, 
holding sourcecode lines.  



Modified: py/dist/py/code/source.py
==============================================================================
--- py/dist/py/code/source.py	(original)
+++ py/dist/py/code/source.py	Sat Dec 18 12:24:20 2004
@@ -1,4 +1,3 @@
-
 class Source(object):
     """ a mutable object holding a source code fragment, 
         automatically deindenting it. 
@@ -15,12 +14,85 @@
                 part = part.rstrip()
                 lines.extend(deindent(part)) 
 
+    def __getitem__(self, key): 
+        if isinstance(key, slice): 
+            newsource = Source()
+            newsource.lines = self.lines[key] 
+            return newsource 
+        else:
+            return self.lines[key]
+
     def putaround(self, before='', after=''):
-        """ return a new source object embedded/indented between before and after. """
+        """ modifies a new source object embedded/indented 
+            between before and after. 
+        """
         before = Source(before) 
         after = Source(after) 
+        newsource = Source() 
         lines = ['    ' + line for line in self.lines]
-        self.lines = before.lines + lines +  after.lines 
+        newsource.lines = before.lines + lines +  after.lines 
+        return newsource 
+
+    def getsource_tryparsing(self, lineno_hint):
+        # the famous try-parsing technique is back! :-) 
+        # first we go forward, afterwards we try parsing backwards, i don't
+        # see how we could do better ...  given that a multiline assert
+        # statement has its frame.f_lineno set to the first line, while
+        # 'A(x,\n  y)' will have its frame.f_lineno set to the second line 
+        for end in range(lineno_hint, len(self)): 
+            trysource = self[:end+1]
+            if trysource.isparseable(): 
+                break 
+        else: 
+            return None 
+        for start in range(end, -1, -1): 
+            trysource = self[start:end+1]
+            if trysource.isparseable(): 
+                return trysource 
+
+    def deindent(self, offset=None): 
+        """ return new source deindented by offset.  If offset is 
+            None then guess an indentation offset from the 
+            first non-blank line.  Subsequent lines which have a 
+            lower indentation offset will be copied verbatim as 
+            they are assumed to be part of multilines. 
+        """
+        # XXX maybe use the tokenizer to properly handle multiline 
+        #     strings etc.pp? 
+        if offset is None: 
+            for line in self.lines: 
+                s = line.lstrip()
+                if s: 
+                    offset = len(line)-len(s) 
+                    break 
+            else: 
+                offset = 0 
+        newlines = []
+        for line in self.lines: 
+            #line = line.expandtabs()
+            s = line.lstrip()
+            if s and line[:offset].isspace(): 
+                line = line[offset:]
+            newlines.append(line) 
+        newsource = Source()
+        newsource.lines[:] = newlines 
+        return newsource 
+
+    def __len__(self): 
+        return len(self.lines) 
+
+    def isparseable(self, deindent=True): 
+        import parser 
+        if deindent: 
+            source = str(self.deindent())
+        else: 
+            source = str(self) 
+        try:
+            parser.suite(source) 
+        except (parser.ParserError, SyntaxError):
+            return False
+        else:
+            return True 
 
     def __str__(self):
         return "\n".join(self.lines) 
@@ -33,6 +105,8 @@
         be copied verbatim as they are assumed to be part of
         multilines. 
     """
+    # XXX maybe use the tokenizer to properly handle multiline 
+    #     strings etc.pp? 
     lines = []
     indentsize = 0
     for line in pysource.split('\n'):
@@ -52,6 +126,9 @@
     #    line = lines.pop()
     #    if not line.strip():
     #        continue
+    #    if not line.endswith('\n'):
+    #        line = line + '\n'
     #    lines.append(line)
     #    break
     return lines
+

Modified: py/dist/py/code/test_source.py
==============================================================================
--- py/dist/py/code/test_source.py	(original)
+++ py/dist/py/code/test_source.py	Sat Dec 18 12:24:20 2004
@@ -15,7 +15,7 @@
 
 def test_source_indent_simple():
     source = Source("raise ValueError")
-    source.putaround(
+    source = source.putaround(
         "try:", 
       """
         except ValueError:
@@ -29,7 +29,49 @@
     x = 42
 else:
     x = 23"""
-  
+
+def checkparseable(source): 
+    assert source.isparseable()
+
+def test_isparseable(): 
+    assert Source("hello").isparseable()
+    assert Source("if 1:\n  pass").isparseable()
+    assert Source(" \nif 1:\n  pass").isparseable()
+    assert not Source(" \nif 1:\npass").isparseable()
+
+class TestSliceAccess: 
+    source = Source("""
+        def f(x):
+            pass
+        def g(x):
+            pass
+    """)
+    def test_getrange(self): 
+        x = self.source[0:2]
+        assert x.isparseable() 
+        assert len(x.lines) == 2
+        assert str(x) == "def f(x):\n    pass" 
+
+    def test_getline(self): 
+        x = self.source[0]
+        assert x == "def f(x):" 
+
+class TestParsing: 
+    source = Source("""
+        def f(x):
+            assert (x == 
+                    3 and 
+                    4)
+    """)
+    def test_getsource_tryparsing(self):
+        print str(self.source)
+        ass = str(self.source[1:]) 
+        for i in range(1, 4): 
+            print "trying start in line %r" % self.source[i]
+            s = self.source.getsource_tryparsing(i) 
+            #x = s.deindent()
+            assert str(s) == ass 
+     
 def XXXtest_source_indent_simple():
     x = StrSource("x=3")
     assert not x.isindented()



More information about the pytest-commit mailing list