Vulkan-Docs/scripts/spec_tools/base_printer.py

173 lines
5.6 KiB
Python

"""Provides the BasePrinter base class for MacroChecker/Message output techniques."""
# Copyright (c) 2018-2019 Collabora, Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author(s): Ryan Pavlik <ryan.pavlik@collabora.com>
from abc import ABC, abstractmethod
from pathlib import Path
from .macro_checker import MacroChecker
from .macro_checker_file import MacroCheckerFile
from .shared import EntityData, Message, MessageContext, MessageType
def getColumn(message_context):
"""Return the (zero-based) column number of the message context.
If a group is specified: returns the column of the start of the group.
If no group, but a match is specified: returns the column of the start of the match.
If no match: returns column 0 (whole line).
"""
if not message_context.match:
# whole line
return 0
if message_context.group is not None:
return message_context.match.start(message_context.group)
return message_context.match.start()
class BasePrinter(ABC):
"""Base class for a way of outputting results of a checker execution."""
def __init__(self):
"""Constructor."""
self.cwd = None
def close(self):
"""Write the tail end of the output and close it, if applicable.
Override if you want to print a summary or are writing to a file.
"""
pass
###
# Output methods: these should all print/output directly.
def output(self, obj):
"""Output any object.
Delegates to other output* methods, if type known,
otherwise uses self.outputFallback().
"""
if isinstance(obj, Message):
self.outputMessage(obj)
elif isinstance(obj, MacroCheckerFile):
self.outputCheckerFile(obj)
elif isinstance(obj, MacroChecker):
self.outputChecker(obj)
else:
self.outputFallback(self.formatBrief(obj))
@abstractmethod
def outputResults(self, checker, broken_links=True,
missing_includes=False):
"""Output the full results of a checker run.
Must be implemented. Typically will call self.output()
on the MacroChecker, as well as possibly outputting
broken links (if broken_links==True)
and missing includes (if missing_includes==True).
"""
raise NotImplementedError
def outputChecker(self, checker):
"""Output the contents of a MacroChecker object.
Default implementation calls self.output() on every MacroCheckerFile.
"""
for f in checker.files:
self.output(f)
def outputCheckerFile(self, fileChecker):
"""Output the contents of a MacroCheckerFile object.
Default implementation calls self.output() on every Message.
"""
for m in fileChecker.messages:
self.output(m)
@abstractmethod
def outputMessage(self, msg):
"""Output a Message.
Must be implemented.
"""
raise NotImplementedError
@abstractmethod
def outputFallback(self, msg):
"""Output some text in a general way.
Must be implemented.
"""
raise NotImplementedError
###
# Format methods: these should all return a string.
def formatContext(self, context, message_type=None):
"""Format a message context in a verbose way, if applicable.
May override, default implementation delegates to self.formatContextBrief().
"""
return self.formatContextBrief(context)
def formatContextBrief(self, context, with_color=True):
"""Format a message context in a brief way.
May override, default is relativeFilename:line:column
"""
return '{}:{}:{}'.format(self.getRelativeFilename(context.filename),
context.lineNum, getColumn(context))
def formatMessageTypeBrief(self, message_type, with_color=True):
"""Format a message type in a brief way.
May override, default is message_type:
"""
return '{}:'.format(message_type)
def formatEntityBrief(self, entity_data, with_color=True):
"""Format an entity in a brief way.
May override, default is macro:entity.
"""
return '{}:{}'.format(entity_data.macro, entity_data.entity)
def formatBrief(self, obj, with_color=True):
"""Format any object in a brief way.
Delegates to other format*Brief methods, if known,
otherwise uses str().
"""
if isinstance(obj, MessageContext):
return self.formatContextBrief(obj, with_color)
if isinstance(obj, MessageType):
return self.formatMessageTypeBrief(obj, with_color)
if isinstance(obj, EntityData):
return self.formatEntityBrief(obj, with_color)
return str(obj)
###
# Helper function
def getRelativeFilename(self, fn):
"""Return the given filename relative to the current directory, if possible."""
if not self.cwd:
# lazy init this member.
self.cwd = Path('.').resolve()
try:
return str(Path(fn).relative_to(self.cwd))
except ValueError:
return str(Path(fn))