Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/pythonparser
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 6f4c35eacf7c
Choose a base ref
...
head repository: m-labs/pythonparser
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 5cb107bbe3de
Choose a head ref
  • 2 commits
  • 2 files changed
  • 1 contributor

Commits on Jul 7, 2016

  1. Fix python default argument idiocy.

    whitequark committed Jul 7, 2016
    Copy the full SHA
    03d87cb View commit details
  2. diagnostic.Engine: allow adding contextual notes.

    whitequark committed Jul 7, 2016
    Copy the full SHA
    5cb107b View commit details
Showing with 47 additions and 1 deletion.
  1. +19 −1 pythonparser/diagnostic.py
  2. +28 −0 pythonparser/test/test_diagnostic.py
20 changes: 19 additions & 1 deletion pythonparser/diagnostic.py
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@

from __future__ import absolute_import, division, print_function, unicode_literals
from functools import reduce
from contextlib import contextmanager
import sys, re

class Diagnostic:
@@ -38,10 +39,15 @@ class Diagnostic:
"""

def __init__(self, level, reason, arguments, location,
highlights=[], notes=[]):
highlights=None, notes=None):
if level not in self.LEVELS:
raise ValueError("level must be one of Diagnostic.LEVELS")

if highlights is None:
highlights = []
if notes is None:
notes = []

if len(set(map(lambda x: x.source_buffer,
[location] + highlights))) > 1:
raise ValueError("location and highlights must refer to the same source buffer")
@@ -145,16 +151,28 @@ class Engine:
"""
def __init__(self, all_errors_are_fatal=False):
self.all_errors_are_fatal = all_errors_are_fatal
self._appended_notes = []

def process(self, diagnostic):
"""
The default implementation of :meth:`process` renders non-fatal
diagnostics to ``sys.stderr``, and raises fatal ones as a :class:`Error`.
"""
diagnostic.notes += self._appended_notes
self.render_diagnostic(diagnostic)
if diagnostic.level == "fatal" or \
(self.all_errors_are_fatal and diagnostic.level == "error"):
raise Error(diagnostic)

@contextmanager
def context(self, note):
"""
A context manager that appends ``note`` to every diagnostic processed by
this engine.
"""
self._appended_notes.append(note)
yield
self._appended_notes.pop()

def render_diagnostic(self, diagnostic):
sys.stderr.write("\n".join(diagnostic.render()) + "\n")
28 changes: 28 additions & 0 deletions pythonparser/test/test_diagnostic.py
Original file line number Diff line number Diff line change
@@ -25,3 +25,31 @@ def test_render(self):
"x + (1 + 'a')",
" ~ ^ ~~~ "],
diag.render())

class DiagnosticEngineTestCase(unittest.TestCase):

def setUp(self):
self.buffer = source.Buffer("x + (1 + 'a')\n")
self.last_diagnostic = None
self.engine = diagnostic.Engine()
def render_diagnostic(diag):
self.last_diagnostic = diag
self.engine.render_diagnostic = render_diagnostic

def test_context(self):
note = diagnostic.Diagnostic(
"note", "broken here", {},
source.Range(self.buffer, 0, 0))

with self.engine.context(note):
diag = diagnostic.Diagnostic(
"error", "{x} doesn't work", {"x": "everything"},
source.Range(self.buffer, 0, 0))
self.engine.process(diag)
self.assertEqual(self.last_diagnostic.notes, [note])

diag = diagnostic.Diagnostic(
"error", "{x} doesn't work", {"x": "everything"},
source.Range(self.buffer, 0, 0))
self.engine.process(diag)
self.assertEqual(self.last_diagnostic.notes, [])