summaryrefslogtreecommitdiff
path: root/coverage/parser.py
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2016-02-07 12:26:34 -0500
committerNed Batchelder <ned@nedbatchelder.com>2016-02-07 12:26:34 -0500
commitdb138cbb6e3d03760a60b064725923de4da5d5d1 (patch)
treebbc4da1298535bc32ededec22bf35f9228eeba09 /coverage/parser.py
parent66372c8e384e29e7345f9eb535b5e6e4edbe5a03 (diff)
downloadpython-coveragepy-git-db138cbb6e3d03760a60b064725923de4da5d5d1.tar.gz
Move AST code together
Diffstat (limited to 'coverage/parser.py')
-rw-r--r--coverage/parser.py153
1 files changed, 77 insertions, 76 deletions
diff --git a/coverage/parser.py b/coverage/parser.py
index 7a468e39..3b25b943 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -315,6 +315,83 @@ class PythonParser(object):
return description
+
+class ByteParser(object):
+ """Parse bytecode to understand the structure of code."""
+
+ @contract(text='unicode')
+ def __init__(self, text, code=None, filename=None):
+ self.text = text
+ if code:
+ self.code = code
+ else:
+ try:
+ self.code = compile_unicode(text, filename, "exec")
+ except SyntaxError as synerr:
+ raise NotPython(
+ u"Couldn't parse '%s' as Python source: '%s' at line %d" % (
+ filename, synerr.msg, synerr.lineno
+ )
+ )
+
+ # Alternative Python implementations don't always provide all the
+ # attributes on code objects that we need to do the analysis.
+ for attr in ['co_lnotab', 'co_firstlineno', 'co_consts']:
+ if not hasattr(self.code, attr):
+ raise CoverageException(
+ "This implementation of Python doesn't support code analysis.\n"
+ "Run coverage.py under CPython for this command."
+ )
+
+ def child_parsers(self):
+ """Iterate over all the code objects nested within this one.
+
+ The iteration includes `self` as its first value.
+
+ """
+ children = CodeObjects(self.code)
+ return (ByteParser(self.text, code=c) for c in children)
+
+ def _bytes_lines(self):
+ """Map byte offsets to line numbers in `code`.
+
+ Uses co_lnotab described in Python/compile.c to map byte offsets to
+ line numbers. Produces a sequence: (b0, l0), (b1, l1), ...
+
+ Only byte offsets that correspond to line numbers are included in the
+ results.
+
+ """
+ # Adapted from dis.py in the standard library.
+ byte_increments = bytes_to_ints(self.code.co_lnotab[0::2])
+ line_increments = bytes_to_ints(self.code.co_lnotab[1::2])
+
+ last_line_num = None
+ line_num = self.code.co_firstlineno
+ byte_num = 0
+ for byte_incr, line_incr in zip(byte_increments, line_increments):
+ if byte_incr:
+ if line_num != last_line_num:
+ yield (byte_num, line_num)
+ last_line_num = line_num
+ byte_num += byte_incr
+ line_num += line_incr
+ if line_num != last_line_num:
+ yield (byte_num, line_num)
+
+ def _find_statements(self):
+ """Find the statements in `self.code`.
+
+ Produce a sequence of line numbers that start statements. Recurses
+ into all code objects reachable from `self.code`.
+
+ """
+ for bp in self.child_parsers():
+ # Get all of the lineno information from this code.
+ for _, l in bp._bytes_lines():
+ yield l
+
+
#
# AST analysis
#
@@ -829,82 +906,6 @@ class AstArcAnalyzer(object):
_code_object__ListComp = _make_oneline_code_method("list comprehension")
-class ByteParser(object):
- """Parse bytecode to understand the structure of code."""
-
- @contract(text='unicode')
- def __init__(self, text, code=None, filename=None):
- self.text = text
- if code:
- self.code = code
- else:
- try:
- self.code = compile_unicode(text, filename, "exec")
- except SyntaxError as synerr:
- raise NotPython(
- u"Couldn't parse '%s' as Python source: '%s' at line %d" % (
- filename, synerr.msg, synerr.lineno
- )
- )
-
- # Alternative Python implementations don't always provide all the
- # attributes on code objects that we need to do the analysis.
- for attr in ['co_lnotab', 'co_firstlineno', 'co_consts']:
- if not hasattr(self.code, attr):
- raise CoverageException(
- "This implementation of Python doesn't support code analysis.\n"
- "Run coverage.py under CPython for this command."
- )
-
- def child_parsers(self):
- """Iterate over all the code objects nested within this one.
-
- The iteration includes `self` as its first value.
-
- """
- children = CodeObjects(self.code)
- return (ByteParser(self.text, code=c) for c in children)
-
- def _bytes_lines(self):
- """Map byte offsets to line numbers in `code`.
-
- Uses co_lnotab described in Python/compile.c to map byte offsets to
- line numbers. Produces a sequence: (b0, l0), (b1, l1), ...
-
- Only byte offsets that correspond to line numbers are included in the
- results.
-
- """
- # Adapted from dis.py in the standard library.
- byte_increments = bytes_to_ints(self.code.co_lnotab[0::2])
- line_increments = bytes_to_ints(self.code.co_lnotab[1::2])
-
- last_line_num = None
- line_num = self.code.co_firstlineno
- byte_num = 0
- for byte_incr, line_incr in zip(byte_increments, line_increments):
- if byte_incr:
- if line_num != last_line_num:
- yield (byte_num, line_num)
- last_line_num = line_num
- byte_num += byte_incr
- line_num += line_incr
- if line_num != last_line_num:
- yield (byte_num, line_num)
-
- def _find_statements(self):
- """Find the statements in `self.code`.
-
- Produce a sequence of line numbers that start statements. Recurses
- into all code objects reachable from `self.code`.
-
- """
- for bp in self.child_parsers():
- # Get all of the lineno information from this code.
- for _, l in bp._bytes_lines():
- yield l
-
-
SKIP_DUMP_FIELDS = ["ctx"]
def _is_simple_value(value):