[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