Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 7344 lines (6374 sloc) 263 KB
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Michael Krause ( http://krause-software.com/ ).
#
# You are free to use this code under the MIT license:
# http://opensource.org/licenses/MIT
# This program looks at source code and generates a style definition for code formatters
# like clang-format, indent, etc. so that the reformatted source matches its current
# formatting as closely as possible.
# It should help programmers to begin using a formatting tool right away
# without the need to invest hours of reading the formatting tool
# documentation while still preserving as much of their existing formatting
# style as possible.
#
# The currently supported formatters are clang-format, YAPF, HTML tidy,
# indent, Artistic Style and uncrustify.
#
# The program basically works by reformatting the source with many
# combinations of options and running a diff between the original source
# and the reformatted source code.
# The program chooses the options resulting in the least amount of changes
# between the source and the reformatted version.
# Among a number of candidate styles with the same diff quality
# the one with the least number of explicit options is chosen
# to keep the style definition brief.
r"""
whatstyle can help you with various tasks regarding source code formatting styles.
Here is a list of examples that you can try out to become familiar with whatstyle, you need
the directory tests/examples in addition to whatstyle.py if you want to try the examples.
Substitute the example sources with your own sources when you are ready.
Basic usage: Find the best style for a source file using a suitable formatter that is
installed on your machine:
$ ./whatstyle.py tests/examples/xv6/printf.c
Show which formatters are installed on your machine:
$ ./whatstyle.py --supported # show all formatters
$ ./whatstyle.py --supported .m .cpp # show formatters that support certain extensions
Choosing a specific formatter:
$ ./whatstyle.py --formatter uncrustify tests/examples/xv6/printf.c
Make the style more resilient against formatting variations, this is advised when the best
style is actually going to be used in a project:
$ ./whatstyle.py --formatter clang-format --mode resilient tests/examples/lua/lstate.[ch]
Generate an overview of the effects of every option value variation. This shows style
option differences together with the source code differences that they cause.
$ ./whatstyle.py --variants tests/examples/lua/lstate.[ch]
Remove uninteresting options from the variants:
$ ./whatstyle.py --variants --ignoreopts ColumnLimit,IndentWidth,UseTab \
tests/examples/lua/lstate.[ch]
Show more differences per style variant:
$ ./whatstyle.py --variants --numhunks 5 tests/examples/lua/lstate.[ch]
Show the variants in a browser:
$ ./whatstyle.py --variants --html tests/examples/lua/lstate.[ch]
Show the variants with a dark theme in a browser resembling the ANSI terminal output:
$ ./whatstyle.py --variants --ansihtml tests/examples/lua/lstate.[ch]
You are looking for a specific formatter option without knowing its name. Create a small
file with a code fragment that your current formatter produces but you are unhappy with.
Then create a modified copy (e.g. hello_ref.c) that looks exactly how you would prefer it.
The options below cause whatstyle to find the differences between the style in hello.c
and hello_ref.c that you can add to your style configuration:
$ ./whatstyle.py --references --mode stylediff tests/examples/ref_space_brace/hello.c \
tests/examples/ref_space_brace/hello_ref.c
Show the differences between your current sources and these sources reformatted after
computing their best matching style:
$ ./whatstyle.py --diff tests/examples/xv6/printf.c
You have a code formatter (e.g. clang-format) that supports commonly known styles
(e.g. Mozilla or WebKit) and you'd prefer the closest common style instead of many
individual options:
$ ./whatstyle.py --maxrounds 1 tests/examples/xv6/printf.c
If you want to help the code formatter developers to reproduce formatter crashes, you could
run several formatters and search for negative return codes, the option '--debug popenio'
tells whatstyle to log the interaction with the formatter processes:
$ for f in $(./whatstyle.py --supported .c)
do
python whatstyle.py --formatter "$f" --mode resilient --cache memory \
--keeptempfiles --debug popenio tests/examples/xv6/printf.c >> dump.txt
done ; grep -C 2 "returncode:-" dump.txt
You think 'git diff' can produce superior diffs for the optimization:
$ ./whatstyle.py --difftool gitdiff tests/examples/xv6/printf.c
"""
from __future__ import print_function
__version__ = '0.1.8'
import sys
if (((sys.version_info[0] == 2) and (sys.version_info[1] < 7)) or (
(sys.version_info[0] == 3) and (sys.version_info[1] < 2))):
sys.stderr.write('Error: Python 2.7 or when running on Python 3 at least Python 3.2'
' is required to run whatstyle\n')
sys.exit(1)
import argparse
import cgi
import codecs
import copy
import difflib
import errno
import hashlib
import heapq
import itertools
import json
try:
import multiprocessing.pool # type: ignore
except ImportError:
multiprocessing = None # type: Optional[module]
import operator
import os
import re
import signal
import shutil
try:
import sqlite3
except ImportError:
sqlite3 = None # type: ignore
import subprocess
import tempfile
import threading
import time
try:
from urlparse import urljoin
from urllib import pathname2url # type: ignore
except ImportError:
from urllib.parse import urljoin # type: ignore
from urllib.request import pathname2url
import traceback
import types
import warnings
import webbrowser
import zlib
try:
import xml.etree.cElementTree as ETree
except ImportError:
import xml.etree.ElementTree as ETree # type: ignore
from contextlib import contextmanager
from collections import Counter, OrderedDict, defaultdict, namedtuple
from io import BytesIO
try:
from itertools import izip # type: ignore
from itertools import izip_longest # type: ignore
except ImportError:
from itertools import zip_longest as izip_longest # type: ignore
izip = zip
try:
from typing import TypeVar
from typing import Any, AnyStr, Callable, Dict, Generator, Iterator, Iterable, List
from typing import Optional, Sequence, Text, Tuple, Union, Match, Pattern
from typing import IO
TextPair = Tuple[str, str]
BytesPair = Tuple[bytes, bytes]
OptionValue = Union[str, bool, int, 'Style']
Option = Tuple[str, str, List[OptionValue], Optional['StyleDef']]
StyleDist = Tuple[Optional['Style'], Optional[Sequence[int]]]
CallArgs = Tuple[Sequence[Any], Dict[Any, Any]]
except ImportError:
pass
from pprint import pprint
WINOS = os.getenv('OS') == 'Windows_NT'
if WINOS:
# Enable utf-8 output on Windows
def codec_search(name):
if name == 'cp65001':
return codecs.lookup('utf-8')
return None
codecs.register(codec_search)
MAX_FILESIZE_FOR_MULTIPROCESSING = 256 * 1024
TIMEOUT_SECONDS = 30
CONTEXTLINES = 2
LOWER_COLUMN_LIMIT = 79
UPPER_COLUMN_LIMIT = 120
HUGE_DISTANCE = 2**31 - 1
UNLIMITED = -1
USE_THREADS = False
HASHFUNC = hashlib.sha1
OK = 0
ERROR = 1
PARSING_FAILED = 1
STDERR_OUTPUT = False
CEXTS = '.c .h'
CPPEXTS = '.c++ .h++ .cxx .hxx .cpp .hpp .cc .hh'
CPPCEXTS = CEXTS + ' ' + CPPEXTS
SCALAEXTS = '.sc .scala'
REXTS = '.r .R .RData .rds .rda'
RUSTEXTS = '.rs'
SUPPORTED_EXTS = [
['clang-format', '.m .mm .java .js .ts .proto .protodevel .td ' + CPPCEXTS],
['yapf', '.py'],
['uncrustify', '.cs .m .mm .d .java .p .pawn .sma .vala .sqc ' + CPPCEXTS],
['astyle', '.m .java ' + CPPCEXTS],
['indent', '.c .h'],
['tidy', '.html .htm'],
['scalariform', SCALAEXTS],
['scalafmt', SCALAEXTS],
['rfmt', REXTS],
['rustfmt', RUSTEXTS],
]
FILENAME_SUBST = '#FILENAME#'
DIFF_SPECS = [
# difftoolname, executable, command line arguments
('difflib', sys.executable, ['-u', __file__, '--stdindiff', '--', FILENAME_SUBST]),
('diff', 'diff', ['--text', '--unified=0', '--', FILENAME_SUBST, '-']),
('gitdiff', 'git', ['--no-pager', 'diff', '--text', '--no-ext-diff', '--no-index',
'--unified=0', '--', FILENAME_SUBST, '-']),
]
BUILTIN_DIFF = DIFF_SPECS[-1]
PAGER_SPECS = [('less', ['-F', '-r', '-S', '-X']),
('more', [])] # type: List[Tuple[str, List[str]]]
STTY_CMD = '/bin/stty'
# We use a "Hello, World!" source to which we apply some modifications
# to check for the presence of a usable diff tool.
HELLOWORLD = """\
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("Hello, World!\n");
return 0;
}
"""
LANG_OBJECTIVE_C = 'Objective-C'
OPTION_PRESENT = '<True>'
MODE_NORMAL = 'normal'
MODE_MAXDIFF = 'maxdiff'
MODE_RESILIENT = 'resilient'
MODE_STYLEDIFF = 'stylediff'
MODE_MINIMIZE = 'minimize'
MODE_MAXIMIZE = 'maximize'
METRIC_MINDIFF = 0
METRIC_MAXDIFF = 1
METRIC_MIN = 2
METRIC_MAX = 3
CC_OFF = 'off'
CC_THREADS = 'threads'
CC_PROCESSES = 'processes'
GLOBALTMP = 1
LOCALTMP = 0
args_info = set() # type: Set[str]
args_debug = set() # type: Set[str]
args_verbose = set() # type: Set[str]
LOGFILE = None # type: Optional[str]
LOGFILEFP = None # type: Optional[IO[Any]]
LOGSPLITDIR = None # type: Optional[str]
MESSAGE_CATEGORY_FILES = None # type: Optional[Dict[str, IO[Any]]]
NO_PROGRESS = False
# ----------------------------------------------------------------------
INFO_RESULT = 'result'
INFO_USER = 'user'
INFO_HEURISTICS = 'heuristics'
INFO_TIME = 'time'
INFO_PERF = 'perf'
INFO_ATTEMPT = 'attempt'
INFO_SKIP = 'skip'
INFO_INVALIDS = 'invalids'
INFO_PROCERRORS = 'procerrors'
INFO_PROCEXCEPTIONS = 'procexc'
# INFO_TIME is not included in INFO_ALL to produce easily diffable debug output
# between different runs of whatstyle.
INFO_ALL = [INFO_RESULT, INFO_USER, INFO_HEURISTICS, INFO_PERF, INFO_ATTEMPT, INFO_SKIP,
INFO_INVALIDS, INFO_PROCERRORS, INFO_PROCEXCEPTIONS]
INFO_IMPLIES_DICT = {} # type: Dict[str, List[str]]
DEBUG_OPTIONS = 'options'
DEBUG_STYLEDEF = 'styledef'
DEBUG_POPEN = 'popen'
DEBUG_POPENIO = 'popenio'
DEBUG_ALL = [DEBUG_OPTIONS, DEBUG_STYLEDEF, DEBUG_POPEN, DEBUG_POPENIO]
DEBUG_RUNTIME = 'runtime'
DEBUG_IMPLIES_DICT = {DEBUG_POPENIO: [DEBUG_POPEN]} # type: Dict[str, List[str]]
verbose_categories = {0: [INFO_USER],
1: [INFO_HEURISTICS, INFO_TIME, INFO_PERF],
2: [INFO_ATTEMPT, INFO_SKIP, INFO_PROCERRORS, INFO_PROCEXCEPTIONS],
3: [DEBUG_OPTIONS, DEBUG_STYLEDEF],
4: [DEBUG_POPEN]}
# ----------------------------------------------------------------------
# yapf: disable
# ----------------------------------------------------------------------
# Some functions from the MIT licensed six (https://pypi.python.org/pypi/six/)
# Copyright (c) 2010-2016 Benjamin Peterson <benjamin@python.org>
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
if PY3:
string_types = str,
integer_types = int,
class_types = type,
text_type = str
binary_type = bytes
else:
string_types = basestring,
integer_types = (int, long)
class_types = (type, types.ClassType)
text_type = unicode
binary_type = str
if PY3:
import builtins
exec_ = getattr(builtins, "exec")
def reraise(tp, value, tb=None):
try:
if value is None:
value = tp()
if value.__traceback__ is not tb:
raise value.with_traceback(tb)
raise value
finally:
value = None
tb = None
else:
def exec_(_code_, _globs_=None, _locs_=None):
"""Execute code in a namespace."""
if _globs_ is None:
frame = sys._getframe(1)
_globs_ = frame.f_globals
if _locs_ is None:
_locs_ = frame.f_locals
del frame
elif _locs_ is None:
_locs_ = _globs_
exec ("""exec _code_ in _globs_, _locs_""")
exec_("""def reraise(tp, value, tb=None):
try:
raise tp, value, tb
finally:
tb = None
""")
if sys.version_info[:2] == (3, 2):
exec_("""def raise_from(value, from_value):
try:
if from_value is None:
raise value
raise value from from_value
finally:
value = None
""")
elif sys.version_info[:2] > (3, 2):
exec_("""def raise_from(value, from_value):
try:
raise value from from_value
finally:
value = None
""")
else:
def raise_from(value, from_value):
raise value
if PY3:
unichr = chr
import struct
int2byte = struct.Struct(">B").pack
else:
unichr = unichr
int2byte = chr
# yapf: enable
# ----------------------------------------------------------------------
try:
NARROW_BUILD = len(unichr(0x1f300)) == 2
except ValueError:
NARROW_BUILD = True
def safe_unichr(codepoint):
# type: (int) -> str
if not NARROW_BUILD or codepoint < 0x10000:
return unichr(codepoint)
# create a surrogate pair
codepoint -= 0x10000
return unichr(0xd800 + (codepoint >> 10)) + unichr(0xdc00 + (codepoint & 0x3ff))
HAS_FSCODEC = hasattr(os, 'fsdecode')
def unistr(text, errors='strict'):
# type: (Union[str, bytes], str) -> str
if isinstance(text, text_type):
return text
try:
return text.decode('utf-8', errors=errors)
except UnicodeDecodeError:
if HAS_FSCODEC:
return os.fsdecode(text)
raise
def bytestr(text):
# type: (Union[str, bytes]) -> bytes
if isinstance(text, binary_type):
return text
try:
return text.encode('utf-8')
except UnicodeEncodeError:
if HAS_FSCODEC:
return os.fsencode(text)
raise
def unifilename(filename):
# type: (Union[str, bytes]) -> str
if isinstance(filename, text_type):
return filename
try:
return filename.decode(sys.getfilesystemencoding())
except UnicodeDecodeError:
try:
return filename.decode('utf-8')
except UnicodeDecodeError:
if HAS_FSCODEC:
return os.fsdecode(filename)
else:
raise
def sysfilename(filename):
# type: (Union[str, bytes]) -> bytes
if not isinstance(filename, text_type):
return filename
try:
return filename.encode(sys.getfilesystemencoding())
except UnicodeEncodeError:
try:
return filename.encode('utf-8')
except UnicodeEncodeError:
if HAS_FSCODEC:
return os.fsencode(filename)
else:
raise
if os.sep != '/':
def normsep(s):
return s.replace('/', os.sep)
else:
def normsep(s):
return s
if NARROW_BUILD:
# When we are running on a narrow Python build
# we have to deal with surrogate pairs ourselves.
def iterchars(text):
# type: (str) -> Sequence[str]
idx = 0
chars = []
while idx < len(text):
c = text[idx]
if ord(c) >= 0x100:
highchar = True
if ((0xD800 <= ord(c) <= 0xDBFF) and (idx < len(text) - 1) and
(0xDC00 <= ord(text[idx + 1]) <= 0xDFFF)):
c = text[idx:idx + 2]
# Skip the other half of the lead and trail surrogate
idx += 1
else:
highchar = False
idx += 1
# Add every character except only one half of a surrogate pair.
if not (highchar and len(c) == 1 and 0xD800 <= ord(c) <= 0xDFFF):
chars.append(c)
return chars
def unilen(text):
# type: (str) -> int
return len(iterchars(text))
else:
def iterchars(text):
# type: (str) -> Sequence[str]
return text
def unilen(text):
# type: (str) -> int
return len(text)
if PY3:
uniord = ord
else:
def uniord(s):
"""ord that also works on surrogate pairs.
"""
if not isinstance(s, text_type):
raise TypeError('uniord() expected a unicode character or string of length 2,'
' but %s found' % type(s).__name__)
try:
return ord(s)
except TypeError:
if len(s) != 2:
raise TypeError('uniord() expected a character or string of length 2,'
' but string of length %d found' % len(s))
return 0x10000 + ((ord(s[0]) - 0xd800) << 10) | (ord(s[1]) - 0xdc00)
# ----------------------------------------------------------------------
# win32_unicode_argv.py
# Importing this will replace sys.argv with a full Unicode form.
# Windows only.
# From this site, with adaptations:
# http://stackoverflow.com/questions/846850/read-unicode-characters-from-command-line-arguments-in-python-2-x-on-windows
# http://code.activestate.com/recipes/572200/
# License:
# 'New BSD license'
# http://community.activestate.com/faq/what-license-code-languag
def win32_unicode_argv():
# type: () -> List[str]
"""Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
strings.
Versions 2.x of Python don't support Unicode in sys.argv on
Windows, with the underlying Windows API instead replacing multi-byte
characters with '?'.
"""
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in range(start, argc.value)]
# ----------------------------------------------------------------------
re_keyvalue = re.compile(r'(\w+)\s*=\s*(.*)')
def parse_keyvalue_pairs(styledump):
for line in styledump.splitlines():
if line.startswith('#'):
continue
m = re_keyvalue.match(line)
if m:
k, v = m.groups()
yield k, v.rstrip()
# ----------------------------------------------------------------------
ExcInfo = namedtuple('ExcInfo', 'exc_info')
FileMeta = namedtuple('FileMeta', ['filename', 'size', 'mtime', 'sha', 'version'])
ExeCall = namedtuple('ExeCall', ['exe', 'cmdargs', 'stdindata', 'depfiles'])
ExeResult = namedtuple('ExeResult', ['returncode', 'stdout', 'stderr', 'runtime', 'timedout',
'error'])
def make_execall(exe, cmdargs, stdindata=None, depfiles=()):
# type: (str, Sequence[str], Optional[bytes], Sequence[str]) -> ExeCall
"""If the result of a subprocess call depends on the content of files, their
filenames must be specified as depfiles to prevent stale cache results.
"""
return ExeCall(exe, cmdargs, stdindata, depfiles)
def make_exeresult(returncode=0, # type: int
stdout=None, # type: bytes
stderr=None, # type: bytes
runtime=0.0, # type: float
timedout=False, # type: bool
error=None # type: Optional[str]
):
# type: (...) -> ExeResult
return ExeResult(returncode, stdout, stderr, runtime, timedout, error)
ParameterSet = namedtuple('ParameterSet', ['formatter', 'difftool', 'mode', 'sourcefactor',
'variantsfactor', 'references', 'maxrounds',
'ignoreopts', 'bestofround', 'concat', 'ccmode'])
FormatOption = namedtuple('FormatOption', ['opts'])
FormatStyleAttempt = namedtuple('FormatStyleAttempt',
['formatstyle', 'newoptions', 'prevdist'])
AttemptResult = namedtuple('AttemptResult', ['distance', 'formatstyle'])
# ----------------------------------------------------------------------
# If we would use any Python packages outside the standard library we could
# use colorama as well. But we don't so we include limited ANSI color support here.
COLOR_SUPPORT = None # type: Optional[bool]
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(30, 38)
BACKGROUNDCOLOR_OFFSET = 10
def supports_color():
# type: () -> bool
global COLOR_SUPPORT
if COLOR_SUPPORT is None:
# On Windows colors are only supported in ConEmu or similar.
if WINOS and os.getenv('ANSICON') is None:
COLOR_SUPPORT = False
else:
COLOR_SUPPORT = True
return COLOR_SUPPORT
def ansicolor(col, text):
# type: (int, str) -> str
if not text:
return text
return '\x1b[%sm%s\x1b[m' % (col, text)
def color(col, text):
# type: (int, str) -> str
if not text or not supports_color():
return text
return ansicolor(col, text)
def bgcolor(col, text):
# type: (int, str) -> str
return color(col + BACKGROUNDCOLOR_OFFSET, text)
def red(text):
# type: (str) -> str
return color(RED, text)
def green(text):
# type: (str) -> str
return color(GREEN, text)
def yellow(text):
# type: (str) -> str
return color(YELLOW, text)
def blue(text):
# type: (str) -> str
return color(BLUE, text)
def magenta(text):
# type: (str) -> str
return color(MAGENTA, text)
def cyan(text):
# type: (str) -> str
return color(CYAN, text)
def white(text):
# type: (str) -> str
return color(WHITE, text)
# ----------------------------------------------------------------------
def progresspair(num, total):
# type: (int, int) -> str
totaltext = text_type(total)
numtext = text_type(num)
spaces = len(totaltext) - len(numtext)
text = ' ' * spaces + numtext + '/' + totaltext
if num == total:
text = green(text)
else:
text = yellow(text)
return text
def reporting_progress():
# type: () -> bool
return INFO_USER in args_info and sys.stdout.isatty() and not NO_PROGRESS
def report_text(text, prev=''):
# type: (str, str) -> str
if not reporting_progress():
return ''
newwidth = len(text)
widthdiff = len(prev) - newwidth
if widthdiff > 0:
text += ' ' * widthdiff
write(text)
sys.stdout.flush()
return text
def report_progress(text, fmtnr=0, fmtcount=0, diffnr=None, diffcount=None, prev=''):
# type: (str, int, int, Optional[int], Optional[int], str) -> str
if not reporting_progress():
return ''
text = '\r%s#formatted %s' % (text, progresspair(fmtnr, fmtcount))
if diffnr is not None and diffcount is not None:
text += ' #compared %s' % progresspair(diffnr, diffcount)
return report_text(text, prev=prev)
# ----------------------------------------------------------------------
def copyfile(src, dst):
try:
os.makedirs(os.path.dirname(dst))
except OSError as exc:
if exc.errno != errno.EEXIST:
raise
shutil.copyfile(src, dst)
def rawstream(fp):
# type: (IO[Any]) -> IO[bytes]
if PY3:
try:
return fp.buffer # type: ignore
except AttributeError:
# There might be a BytesIO behind fp.
pass
return fp # type: Optional[IO[bytes]]
def write(s, fp=None):
# type: (Union[str, bytes], Optional[IO[Any]]) -> None
"""Write s to the binary stream fp (default is stdout).
"""
efp = fp if fp is not None else sys.stdout
rawstream(efp).write(bytestr(s))
def outline(s=b'', end=b'\n', fp=None):
# type: (Union[str, bytes], Union[str, bytes], Optional[IO]) -> None
write(bytestr(s) + bytestr(end), fp=fp)
def category_print(categories, categorytype, category, s, prefix='', end='\n', fp=None):
# type: (Set[str], str, str, Union[str, bytes], str, str, Optional[IO]) -> None
if category not in categories:
return
if categorytype == 'info':
msg = prefix
else:
msg = '%s%s_%s: ' % (prefix, categorytype, category)
if MESSAGE_CATEGORY_FILES is not None:
logfilename = 'whatstyle_%s_%s.log' % (categorytype, category)
fp = MESSAGE_CATEGORY_FILES.get(logfilename)
if fp is None:
path = os.path.join(tempfile.gettempdir(), logfilename)
fp = open(path, 'wb')
MESSAGE_CATEGORY_FILES[logfilename] = fp
if fp is None and LOGFILE:
global LOGFILEFP
if not LOGFILEFP:
LOGFILEFP = open(LOGFILE, 'wb')
fp = LOGFILEFP
if fp is None:
fp = rawstream(sys.stderr if STDERR_OUTPUT else sys.stdout)
write(msg, fp=fp)
write(s, fp=fp)
if end:
write(end, fp=fp)
def iprint(category, s, prefix='', end='\n', fp=None):
# type: (str, AnyStr, str, str, Optional[IO[AnyStr]]) -> None
category_print(args_info, 'info', category, s, prefix, end, fp=fp)
def dprint(category, s, prefix='', end='\n', fp=None):
# type: (str, AnyStr, str, str, Optional[IO[AnyStr]]) -> None
category_print(args_debug, 'debug', category, s, prefix, end, fp=fp)
reportmessage = outline
def reporterror(s, fp=None):
# type: (str, Optional[IO[AnyStr]]) -> None
if fp is None:
fp = rawstream(sys.stderr) # type: ignore
reportmessage(s, fp=fp)
reportwarning = reporterror
def alignedblocks(left, right, separator=' ', color_left=None, color_right=None):
# type: (str, str, str, Optional[int], Optional[int]) -> str
"""Create a single text block with a separator symbol between the two texts
left and right.
Both sides can be colored differently.
"""
left_lines = left.splitlines()
right_lines = right.splitlines()
maxwidth = max(len(line) for line in left_lines)
numlines = max(len(left_lines), len(right_lines))
left_lines = left.splitlines()
lines = []
for i in range(numlines):
l = left_lines.pop(0) if left_lines else ''
r = right_lines.pop(0) if right_lines else ''
spc = ' ' * (maxwidth - len(l))
l += spc
if color_left is not None:
l = color(color_left, l)
if color_right is not None:
r = color(color_right, r)
lines.append(l + separator + r)
return '\n'.join(lines)
# ----------------------------------------------------------------------
# http://stackoverflow.com/questions/312443/
# how-do-you-split-a-list-into-evenly-sized-chunks-in-python
def grouper(n, iterable, padvalue=None):
# type: (int, Iterable[Any], Any) -> Iterable[Iterable[Any]]
"""grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"""
return izip_longest(*[iter(iterable)] * n, fillvalue=padvalue)
# ----------------------------------------------------------------------
# Functions to find an executable in the PATH, from:
# http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python
def exename(program):
# type: (str) -> str
if WINOS and not program.lower().endswith('.exe'):
program += '.exe'
return program
def is_executable(filename):
# type: (str) -> bool
return os.path.isfile(filename) and os.access(filename, os.X_OK)
def which(program):
# type: (str) -> Optional[str]
program = exename(program)
fpath, _ = os.path.split(program)
if fpath:
if is_executable(program):
return program
else:
for path in [os.path.abspath(os.curdir)] + os.environ['PATH'].split(os.pathsep):
path = path.strip('"')
exe_file = os.path.join(unifilename(path), unifilename(program))
if is_executable(exe_file):
return exe_file
return None
# ----------------------------------------------------------------------
def systempager_cmdargs():
# type: () -> List[str]
for name, args in PAGER_SPECS:
path = which(name)
if path is not None:
return [path] + list(args)
return []
# ----------------------------------------------------------------------
#
# Functions to support multiprocessing
#
def per_process_init():
# type: () -> None
try:
os.nice(19)
except AttributeError:
# nice is not available everywhere.
pass
except OSError:
# When this program is already running on the nicest level (20) on OS X
# it is not permitted to change the priority.
pass
# A keyboard interrupt disrupts the communication between a
# Python script and its subprocesses when using multiprocessing.
# The child can ignore SIGINT and is properly shut down
# by a pool.terminate() call in case of a keyboard interrupt
# or an early generator exit.
signal.signal(signal.SIGINT, signal.SIG_IGN)
def tracebackwrapper(func, args, kwargs):
# type: (Callable[..., Any], List[Any], Dict[Any, Any]) -> Any
try:
return func(*args, **kwargs)
except Exception as e:
e.traceback = traceback.format_exc() # type: ignore
raise
def iter_parallel_report(func, # type: Callable[..., Any]
args_lists, # type: Sequence[CallArgs]
ccmode=CC_PROCESSES):
# type: (...) -> Iterator[Union[ExeResult, ExcInfo]]
if ccmode == CC_OFF or len(args_lists) <= 1 or not multiprocessing:
for args, kwargs in args_lists:
yield func(*args, **kwargs)
return
processes = min(len(args_lists), multiprocessing.cpu_count())
if ccmode == CC_THREADS:
pool = multiprocessing.pool.ThreadPool(processes=processes)
else:
pool = multiprocessing.Pool(processes=processes, initializer=per_process_init)
try:
async_results = [pool.apply_async(func, args=args, kwds=kwargs)
for args, kwargs in args_lists]
pool.close()
while async_results:
try:
asyncres = async_results.pop(0)
yield asyncres.get()
except (KeyboardInterrupt, GeneratorExit):
raise
except Exception as e:
t, v, tb = sys.exc_info()
try:
# Report the textual traceback of the subprocess rather
# than this local exception which was triggered
# by the other side.
tb = e.traceback # type: ignore
except AttributeError:
pass
yield ExcInfo((t, v, tb))
except GeneratorExit:
pool.terminate()
except KeyboardInterrupt:
pool.terminate()
raise
finally:
pool.join()
def iter_parallel(func, # type: Callable
args_lists, # type: Sequence[CallArgs]
ccmode=CC_PROCESSES):
# type: (...) -> Iterator[Any]
if not args_lists:
return
if ccmode != CC_OFF:
args_lists = [((func, args, kwargs), {}) for args, kwargs in args_lists]
wrappedfunc = tracebackwrapper
else:
wrappedfunc = func
for result in iter_parallel_report(wrappedfunc, args_lists, ccmode=ccmode):
if ccmode == CC_OFF:
yield result
else:
tbtext = None
try:
if isinstance(result, ExcInfo):
t, v, tb = result.exc_info
if not isinstance(tb, types.TracebackType):
tbtext = tb
tb = None
reraise(t, v, tb)
else:
yield result
except Exception:
if tbtext is not None:
raise Exception(tbtext)
else:
traceback.print_exc()
raise
# ----------------------------------------------------------------------
# The data types option and style.
def option_make(optionname, # type: AnyStr
optiontype, # type: AnyStr
configs, # type: Iterable[OptionValue]
nestedopts=None # type: Optional[StyleDef]
):
# type: (...) -> Tuple[str, str, List[OptionValue], Optional[StyleDef]]
configs = [typeconv(c) for c in configs]
return unistr(optionname), unistr(optiontype), configs, nestedopts
def option_name(option):
# type: (Option) -> str
return option[0]
def option_type(option):
# type: (Option) -> str
return option[1]
def option_configs(option):
# type: (Option) -> List[OptionValue]
return option[2]
def option_nestedstyle(option):
# type: (Option) -> Optional[StyleDef]
return option[3]
class StyleDef(OrderedDict):
pass
def styledef_make(options=None):
# type: (Union[dict, Iterable[Option], None]) -> StyleDef
if isinstance(options, dict):
s = styledef_make()
for _, option in sorted(options.items()):
if isinstance(option, dict):
option = styledef_make(option)
styledef_add_option(option, s)
return s
if options is None:
options = []
return StyleDef((option_name(o), o) for o in options)
class Style(OrderedDict):
pass
def style_make(options=None):
# type: (Union[dict, List[Tuple[str, OptionValue]], None]) -> Style
if options is None:
return Style()
if isinstance(options, dict):
s = style_make()
for k, v in sorted(options.items()):
if isinstance(v, dict):
v = style_make(v)
set_option(s, k, v)
return s
raise TypeError('options must be a dict or None')
def styledef_copy(styledef):
# type: (StyleDef) -> StyleDef
return styledef_make(styledef_options(styledef))
def styledef_options(styledef):
# type: (StyleDef) -> List[Option]
return list(styledef.values())
def styledef_option(styledef, optionname):
# type: (StyleDef, str) -> Optional[Option]
if not isinstance(styledef, StyleDef):
raise TypeError('styledef must be a StyleDef, got %r instead' % styledef)
return styledef.get(optionname)
def styledef_add_option(option, styledef):
# type: (Option, StyleDef) -> None
styledef[option_name(option)] = option
def styledef_delete_option(optionname, styledef):
# type: (str, StyleDef) -> None
del styledef[optionname]
# ---------------------------------------------------------------------
def shadigest(data):
# type: (bytes) -> bytes
return HASHFUNC(data).digest()
def shahex(data):
# type: (bytes) -> str
return HASHFUNC(data).hexdigest()
def inclusiverange(start, stop):
# type: (int, int) -> Iterable[int]
return range(start, stop + 1)
def textrepr(value):
# type: (Any) -> str
if isinstance(value, type(True)):
value = 'true' if value else 'false'
return text_type(value)
def normrepr(style):
# type: (Style) -> str
"""Return a YAML-like JSON representation of a style.
This function is also used to get a hashable key from a style.
>>> print(normrepr(style_make({'BraceWrapping': {'AfterClass': 'false'},\
'BreakBeforeBraces': 'Custom'})))
{BraceWrapping: {AfterClass: false}, BreakBeforeBraces: Custom}
"""
if not isinstance(style, Style):
raise TypeError
fragments = []
for key in sorted(style.keys()):
value = style[key]
if isinstance(value, Style):
value = normrepr(value)
else:
value = textrepr(value)
fragments.append(key + ': ' + value)
result = text_type('{' + ', '.join(fragments) + '}')
return result
def stylevariant(optionname, value):
# type: (str, OptionValue) -> Style
return style_make({optionname: typeconv(value)})
def stylevariants(optionname, values):
# type: (str, Iterable[OptionValue]) -> List[Style]
return [stylevariant(optionname, v) for v in values]
re_number = re.compile(r'-?[0-9]+$')
def typeconv(obj):
# type: (OptionValue) -> OptionValue
"""If obj is a string that looks like number or boolean value, its value is returned.
Binary strings are returned as unicode strings, everything else is unchanged.
"""
if not isinstance(obj, string_types):
return obj
if obj == 'true':
return True
if obj == 'false':
return False
if isinstance(obj, string_types):
text = unistr(obj) # type: str
if re_number.match(text):
return int(text)
if isinstance(obj, binary_type):
text = unistr(obj)
return text
return obj
def set_option(style, optionname, optionvalue):
# type: (Style, str, OptionValue) -> None
"""Sets the normalized option value in style.
"""
style[unistr(optionname)] = typeconv(optionvalue)
class CodeFormatter(object):
"""Baseclass for all formatters.
"""
shortname = ''
alternative_names = [] # type: List[str]
_prefer_basestyle = False
base_optionname = ''
invalid_enums = {} # type: Dict[str, Set[str]]
columnlimitname = None # type: str
configfilename = None # type: str
styledump_argument = None # type: str
def __init__(self, exe, cache=None):
# type: (str, Optional[Cache]) -> None
if not os.path.isabs(exe):
exe = which(exe) # type: ignore
self.exe = unifilename(exe)
self.cache = cache
self._styledefinition = styledef_make()
self.allow_encoding_change = False
self.languages = [] # type: List[str]
self.initial_style = style_make()
# The are deleted after one call to minimize_errors
self.globaltempfiles = set() # type: Set[str]
# These are deleted after each round of attempts
self.tempfiles = set() # type: Set[str]
self.keeptempfiles = False
self.version_string = formatter_version(exe)
def register_options(self):
raise NotImplementedError
@property
def prefer_basestyle(self):
# type: () -> bool
return self._prefer_basestyle
def identify_language(self, filenames=(), language=None):
# type: (Sequence[str], Optional[str]) -> None
pass
@classmethod
def executable_names(cls):
# type: () -> List[str]
return [cls.shortname] + cls.alternative_names
@property
def styledefinition(self):
# type: () -> StyleDef
return self._styledefinition
@styledefinition.setter
def styledefinition(self, styledef):
# type: (StyleDef) -> None
self._styledefinition = styledef
if self._styledefinition is None:
return
if DEBUG_STYLEDEF not in args_debug:
return
dprint(DEBUG_STYLEDEF, '# Style definition for "%s"' % unifilename(self.exe))
dprint(DEBUG_STYLEDEF, self.styledef_text(self.styledefinition))
dprint(DEBUG_STYLEDEF, '##########')
def use_startstyle(self, inlinestyletext):
# type: (Optional[str]) -> None
if inlinestyletext:
# Transform YAML-like JSON
# e.g. '{based_on_style: pep8, column_limit: 79}'
# into '{"based_on_style": "pep8", "column_limit": 79}'
# which can be parsed as JSON.
inlinestyletext = re.sub(r'([a-zA-Z_]\w+)', r'"\1"', inlinestyletext)
d = json.JSONDecoder().decode(inlinestyletext) # type: ignore
self.initial_style = style_make(d)
def styledef_text(self, styledef):
# type: (StyleDef) -> str
options = styledef_options(styledef)
fragments = []
for option in options:
optionname = option_name(option)
optiontype = option_type(option)
configs = option_configs(option)
nested = option_nestedstyle(option)
if isinstance(nested, StyleDef):
fragments.append('%s %s' % (optionname, optiontype))
text = self.styledef_text(nested)
for line in text.splitlines():
fragments.append(' %s' % line)
else:
configtext = ''
if configs:
configtext = ' [%s]' % ', '.join([textrepr(c) for c in configs])
fragments.append('%s %s%s' % (optionname, optiontype, configtext))
return '\n'.join(fragments) + '\n'
def is_baseformat(self, name):
# type: (str) -> bool
return name.lower() == self.base_optionname.lower()
def contains_major_style(self, optiongroup):
# type: (Style) -> bool
if not isinstance(optiongroup, Style):
raise TypeError()
if not self.prefer_basestyle:
return False
for optname in optiongroup:
if self.is_baseformat(optname):
return True
return False
def sorted_style(self, style):
# type: (Style) -> Style
"""Return the same style with the major style option appearing first.
"""
newstyle = style_make()
items = sorted(style.items())
for optionname, value in items:
if self.prefer_basestyle and self.is_baseformat(optionname):
set_option(newstyle, optionname, value)
for optionname, value in items:
if not (self.prefer_basestyle and self.is_baseformat(optionname)):
set_option(newstyle, optionname, value)
return newstyle
def effective_style(self, style):
# type: (Style) -> Style
return style_make()
def style_dump(self, style):
# type: (Style) -> Optional[str]
"""Return a dump of style options from the formatter given style.
"""
assert isinstance(style, Style)
if self.styledump_argument is None:
return None
cmdargs = self.cmdargs_for_style(style)
cmdargs.append(self.styledump_argument)
res = run_executable(self.exe, cmdargs)
if res.returncode != 0 or res.stderr:
return None
return unistr(res.stdout)
def attempt_acceptible(self, roundnr, prevdist, newdist):
# type: (int, List[int], List[int]) -> bool
"""Increasing the constant values results in more attempts and less local minima.
"""
if roundnr >= 3 and newdist > prevdist:
# Makes things worse
return False
if roundnr >= 3 and newdist >= prevdist:
# Does not improve things
return False
return True
def complexity(self, style, toplevel=True):
# type: (Style, bool) -> int
"""The complexity is the number of style options times two.
We prefer an explicit major style, e.g. {BasedOnStyle: LLVM} over the
effectively equivalent style {}.
"""
c = 0
values = list(style.values()) # type: List[OptionValue]
for value in values:
c += 2
if isinstance(value, Style):
c += self.complexity(value, toplevel=False)
if toplevel and self.prefer_basestyle and not self.contains_major_style(style):
# Consider the absence of a major style to be more complex that its presence
# but less complex than one with two added style options.
c += 3
return c
def nested_derivations(self, style):
# type: (Style) -> List[Style]
return []
def is_valid_enumvalue(self, optionname, enumvalue):
# type: (str, Any) -> bool
invalids = self.invalid_enums.get(optionname)
return invalids is None or enumvalue not in invalids
def remove_invalid_options(self, styledef):
# type: (StyleDef) -> StyleDef
if not self.invalid_enums:
return styledef
newstyle = styledef_make()
for option in styledef_options(styledef):
optionname = option_name(option)
if optionname not in self.invalid_enums:
styledef_add_option(option, newstyle)
else:
optiontype = option_type(option)
configs = option_configs(option)
invalid_enumvalues = self.invalid_enums[optionname]
configs = [c for c in configs if c not in invalid_enumvalues]
styledef_add_option(option_make(optionname, optiontype, configs), newstyle)
return newstyle
def reporterrors(self, job, jobres):
# type: (ExeCall, ExeResult) -> None
if not self.should_report_error(job, jobres):
return
category = INFO_PROCERRORS
if jobres.error is not None:
iprint(category, red("Error: calling %s caused this error: %s" % (job.exe,
jobres.error)))
else:
iprint(category, red("Error: %s returned code %s" % (job.exe, jobres.returncode)))
iprint(category, " for these arguments: %s" % colored_cmdargs(job.cmdargs, RED))
if jobres.stderr:
text = jobres.stderr
try:
text = unistr(text)
except UnicodeDecodeError:
pass
iprint(INFO_PROCERRORS, 'formatter stderr:"""\\\n%s"""' % red(text))
def should_report_error(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
return jobres.error is not None or (jobres.returncode != 0) or jobres.stderr
def valid_job_result(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
return jobres.error is None and jobres.returncode == 0 and not jobres.stderr
def invalid_cmdline_option(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
return False
def extra_penalty(self, style, complexity):
# type: (Any, int) -> Tuple[int, int]
return complexity, 0
def variants_for(self, option):
raise NotImplementedError
@property
def column_limit_candidates(self):
r = set(list(inclusiverange(LOWER_COLUMN_LIMIT, UPPER_COLUMN_LIMIT)))
r.add(0)
r = sorted(list(r))
return r
def inlinestyletext(self, formatstyle):
# type: (Style) -> str
return normrepr(formatstyle)
def tempfiles_for_mode(self, mode):
# type: (int) -> Set[str]
if mode == LOCALTMP:
return self.tempfiles
else:
return self.globaltempfiles
def add_tempfile(self, tmpfilename, mode=LOCALTMP):
# type: (str, int) -> None
self.tempfiles_for_mode(mode).add(tmpfilename)
def remove_tempfiles(self, mode=None):
# type: (Optional[int]) -> None
if self.keeptempfiles:
return
if mode is None:
self.remove_tempfiles(LOCALTMP)
self.remove_tempfiles(GLOBALTMP)
return
files = self.tempfiles_for_mode(mode)
dirs = set()
while files:
filename = files.pop()
if os.path.isdir(filename):
dirs.add(filename)
continue
try:
os.remove(filename)
except OSError as e:
if e.errno != errno.ENOENT:
reporterror("Error({0}): {1}".format(e.errno, e.strerror))
while dirs:
d = dirs.pop()
try:
os.rmdir(d)
except OSError as exc:
reporterror('Error: cannot delete directory "%s": %s' % (d, str(exc)))
def tempfile_exists(self, tmpfilename, mode=LOCALTMP):
# type: (str, int) -> bool
return tmpfilename in self.tempfiles_for_mode(mode)
def cmdargs_for_style(self, formatstyle, filename=None):
# type: (Style, Optional[str]) -> List[str]
raise NotImplementedError
def styletext(self, style):
raise NotImplementedError
def can_process_in_parallel(self, filenames):
# type: (List[str]) -> bool
"""
Returns False if one of the files is too large to be processed in parallel
with another file.
Returns True if all files are small enough.
"""
result = True
for filename in filenames:
sourcedata = get_cached_file(filename)
if len(sourcedata) > MAX_FILESIZE_FOR_MULTIPROCESSING:
reportwarning('Warning: %s has a size of %s bytes.' % (filename,
len(sourcedata)))
reportwarning(' This may cause memory swapping so we only use'
' a single processor core.')
result = False
return result
def formatcode(self, formatstyle, sourcedata, filename=None):
# type: (Style, bytes, Optional[str]) -> Optional[bytes]
cmdargs = self.cmdargs_for_style(formatstyle, filename)
# The formatter reads the sourcedata from standard input and only uses the filename
# to identify the language from the its extension.
# This is why filename is not listed in the depfiles argument of make_execall.
jobs = [make_execall(self.exe, cmdargs, sourcedata)]
jobresults = list(run_executables(jobs, self.cache, ccmode=CC_OFF))
job, jobres = jobs[0], jobresults[0]
self.reporterrors(job, jobres)
valid_jobres = self.valid_job_result(job, jobres)
if not valid_jobres:
return None
return jobres.stdout
def reformat(self, sourcefile, destfile, configfile):
# type: (str, str, str) -> None
"""Reformats sourcefile according to configfile and writes it to destfile.
This method is only used for testing.
"""
tmpdir = tempfile.mkdtemp(prefix='whatstyle_')
cfg = os.path.join(tmpdir, self.configfilename)
copyfile(configfile, cfg)
tmpfilename = os.path.join(tmpdir, os.path.basename(sourcefile))
copyfile(sourcefile, tmpfilename)
cmdargs = [tmpfilename]
exeresult = run_executable(self.exe, cmdargs)
writebinary(destfile, exeresult.stdout)
os.remove(tmpfilename)
os.remove(cfg)
os.rmdir(tmpdir)
def __del__(self):
# type: () -> None
self.remove_tempfiles()
# ----------------------------------------------------------------------
# The CLANG_FORMAT_EVOLUTION is generated from a clang Git repository by
# tools/create_formatstyle_history.py.
CLANG_FORMAT_EVOLUTION = """\
# Clang 3.5
+ BasedOnStyle string
LLVM
Google
Chromium
Mozilla
WebKit
+ AccessModifierOffset int
+ AlignEscapedNewlinesLeft bool
+ AlignTrailingComments bool
+ AllowAllParametersOfDeclarationOnNextLine bool
+ AllowShortFunctionsOnASingleLine bool
+ AllowShortIfStatementsOnASingleLine bool
+ AllowShortLoopsOnASingleLine bool
+ AlwaysBreakBeforeMultilineStrings bool
+ AlwaysBreakTemplateDeclarations bool
+ BinPackParameters bool
+ BreakBeforeBinaryOperators bool
+ BreakBeforeBraces BraceBreakingStyle
Attach
Linux
Stroustrup
Allman
+ BreakBeforeTernaryOperators bool
+ BreakConstructorInitializersBeforeComma bool
+ ColumnLimit unsigned
+ ConstructorInitializerAllOnOneLineOrOnePerLine bool
+ ConstructorInitializerIndentWidth unsigned
+ ContinuationIndentWidth unsigned
+ Cpp11BracedListStyle bool
+ DerivePointerBinding bool
+ ExperimentalAutoDetectBinPacking bool
+ IndentCaseLabels bool
+ IndentFunctionDeclarationAfterType bool
+ IndentWidth unsigned
+ MaxEmptyLinesToKeep unsigned
+ NamespaceIndentation NamespaceIndentationKind
None
Inner
All
+ ObjCSpaceBeforeProtocolList bool
+ PenaltyBreakBeforeFirstCallParameter unsigned
+ PenaltyBreakComment unsigned
+ PenaltyBreakFirstLessLess unsigned
+ PenaltyBreakString unsigned
+ PenaltyExcessCharacter unsigned
+ PenaltyReturnTypeOnItsOwnLine unsigned
+ PointerBindsToType bool
+ SpaceAfterControlStatementKeyword bool
+ SpaceBeforeAssignmentOperators bool
+ SpaceInEmptyParentheses bool
+ SpacesBeforeTrailingComments unsigned
+ SpacesInAngles bool
+ SpacesInCStyleCastParentheses bool
+ SpacesInParentheses bool
+ Standard LanguageStandard
Cpp03
Cpp11
Auto
+ TabWidth unsigned
+ UseTab UseTabStyle
Never
ForIndentation
Always
# Clang 3.5
+ Language LanguageKind
None
Cpp
JavaScript
# Clang 3.5
- SpaceAfterControlStatementKeyword bool
+ SpaceBeforeParens SpaceBeforeParensOptions
Never
ControlStatements
Always
# Clang 3.5
+ BasedOnStyle string
LLVM
Google
Chromium
Mozilla
WebKit
GNU
+ IndentBlocks bool
# Clang 3.5
- IndentBlocks bool
# Clang 3.5
+ BreakBeforeBraces BraceBreakingStyle
Attach
Linux
Stroustrup
Allman
GNU
# Clang 3.5
+ CommentPragmas std::string
# Clang 3.5
+ SpacesInContainerLiterals bool
# Clang 3.5
+ Language LanguageKind
None
Cpp
JavaScript
Proto
# Clang 3.5
+ ObjCSpaceAfterProperty bool
# Clang 3.5
+ KeepEmptyLinesAtTheStartOfBlocks bool
# Clang 3.5
+ ForEachMacros std::vector<std::string>
# Clang 3.5
+ AllowShortFunctionsOnASingleLine ShortFunctionStyle
None
Inline
All
# Clang 3.5
+ AllowShortBlocksOnASingleLine bool
# Clang 3.5
+ DisableFormat bool
# Clang 3.5
- DerivePointerBinding bool
- PointerBindsToType bool
+ DerivePointerAlignment bool
+ PointerAlignment PointerAlignmentStyle
Left
Right
Middle
# Clang 3.5
- IndentFunctionDeclarationAfterType bool
# Clang 3.5
+ IndentWrappedFunctionNames bool
# Clang 3.6
+ AlwaysBreakAfterDefinitionReturnType bool
# Clang 3.6
+ SpacesInSquareBrackets bool
# Clang 3.6
+ SpaceAfterCStyleCast bool
# Clang 3.6
+ AllowShortCaseLabelsOnASingleLine bool
# Clang 3.6
+ BreakBeforeBinaryOperators BinaryOperatorStyle
None
NonAssignment
All
# Clang 3.6
+ Language LanguageKind
None
Cpp
Java
JavaScript
Proto
# Clang 3.6
+ BinPackArguments bool
# Clang 3.6
+ ObjCBlockIndentWidth unsigned
# Clang 3.6
+ AlignAfterOpenBracket bool
# Clang 3.6
+ AllowShortFunctionsOnASingleLine ShortFunctionStyle
None
Inline
Empty
All
# Clang 3.6
+ AlignOperands bool
# Clang 3.7
+ AlignConsecutiveAssignments bool
# Clang 3.7
+ AllowShortFunctionsOnASingleLine ShortFunctionStyle
None
Empty
Inline
All
# Clang 3.7
+ AlwaysBreakAfterDefinitionReturnType DefinitionReturnTypeBreakingStyle
None
All
TopLevel
# Clang 3.7
+ MacroBlockBegin std::string
+ MacroBlockEnd std::string
# Clang 3.7
+ BreakBeforeBraces BraceBreakingStyle
Attach
Linux
Mozilla
Stroustrup
Allman
GNU
# Clang 3.8
+ BreakBeforeBraces BraceBreakingStyle
Attach
Linux
Mozilla
Stroustrup
Allman
GNU
WebKit
# Clang 3.8
+ IncludeCategories std::vector<std::pair<std::string, unsigned>>
# Clang 3.8
+ BraceWrapping BraceWrappingFlags
bool AfterClass
bool AfterControlStatement
bool AfterEnum
bool AfterFunction
bool AfterNamespace
bool AfterObjCDeclaration
bool AfterStruct
bool AfterUnion
bool BeforeCatch
bool BeforeElse
bool IndentBraces
+ BreakBeforeBraces BraceBreakingStyle
Attach
Linux
Mozilla
Stroustrup
Allman
GNU
WebKit
Custom
# Clang 3.8
+ AlignConsecutiveDeclarations bool
# Clang 3.8
+ IncludeCategories std::vector<IncludeCategory>
# Clang 3.8
+ BreakAfterJavaFieldAnnotations bool
# Clang 3.8
+ AlignAfterOpenBracket BracketAlignmentStyle
Align
DontAlign
AlwaysBreak
# Clang 3.8
+ SortIncludes bool
# Clang 3.8
+ ReflowComments bool
# Clang 3.8
+ AlwaysBreakAfterReturnType ReturnTypeBreakingStyle
None
All
TopLevel
AllDefinitions
TopLevelDefinitions
# Clang 3.8
+ Language LanguageKind
None
Cpp
Java
JavaScript
Proto
TableGen
# Clang 3.9
+ BreakStringLiterals bool
# Clang 3.9
+ JavaScriptQuotes JavaScriptQuoteStyle
Leave
Single
Double
# Clang 3.9
+ IncludeIsMainRegex std::string
# Clang 3.9
+ UseTab UseTabStyle
Never
ForIndentation
ForContinuationAndIndentation
Always
# Clang 3.9
+ JavaScriptWrapImports bool
# Clang 4.0
+ SpaceAfterTemplateKeyword bool
# Clang 4.0
+ Language LanguageKind
None
Cpp
Java
JavaScript
ObjC
Proto
TableGen
# Clang 5
+ FixNamespaceComments bool
# Clang 5
+ BreakBeforeInheritanceComma bool
# Clang 5
- AlignEscapedNewlinesLeft bool
+ AlignEscapedNewlines EscapedNewlineAlignmentStyle
DontAlign
Left
Right
# Clang 5
+ PenaltyBreakAssignment unsigned
# Clang 5
- BreakConstructorInitializersBeforeComma bool
+ AllowShortFunctionsOnASingleLine ShortFunctionStyle
None
InlineOnly
Empty
Inline
All
+ BraceWrapping BraceWrappingFlags
bool AfterClass
bool AfterControlStatement
bool AfterEnum
bool AfterFunction
bool AfterNamespace
bool AfterObjCDeclaration
bool AfterStruct
bool AfterUnion
bool BeforeCatch
bool BeforeElse
bool IndentBraces
bool SplitEmptyFunctionBody
+ BreakConstructorInitializers BreakConstructorInitializersStyle
BeforeColon
BeforeComma
AfterColon
+ CompactNamespaces bool
# Clang 5
+ SortUsingDeclarations bool
# Clang 5
+ BraceWrapping BraceWrappingFlags
bool AfterClass
bool AfterControlStatement
bool AfterEnum
bool AfterFunction
bool AfterNamespace
bool AfterObjCDeclaration
bool AfterStruct
bool AfterUnion
bool BeforeCatch
bool BeforeElse
bool IndentBraces
bool SplitEmptyFunction
bool SplitEmptyRecord
bool SplitEmptyNamespace
# Clang 5
+ Language LanguageKind
None
Cpp
Java
JavaScript
ObjC
Proto
TableGen
TextProto
# Clang 6
+ IndentPPDirectives PPDirectiveIndentStyle
None
AfterHash
# Clang 6
+ BraceWrapping BraceWrappingFlags
bool AfterClass
bool AfterControlStatement
bool AfterEnum
bool AfterFunction
bool AfterNamespace
bool AfterObjCDeclaration
bool AfterStruct
bool AfterUnion
bool AfterExternBlock
bool BeforeCatch
bool BeforeElse
bool IndentBraces
bool SplitEmptyFunction
bool SplitEmptyRecord
bool SplitEmptyNamespace
# Clang 6
+ RawStringFormats std::vector<RawStringFormat>
# Clang 6
+ IncludeBlocks IncludeBlocksStyle
Preserve
Merge
Regroup
# Clang 7
+ ObjCBinPackProtocolList BinPackStyle
Auto
Always
Never
# Clang 7
+ SpaceBeforeCtorInitializerColon bool
+ SpaceBeforeInheritanceColon bool
+ SpaceBeforeRangeBasedForLoopColon bool
# Clang 7
- IncludeBlocks IncludeBlocksStyle
Preserve
Merge
Regroup
- IncludeCategories std::vector<IncludeCategory>
- IncludeIsMainRegex std::string
# Clang 7
+ AlwaysBreakTemplateDeclarations BreakTemplateDeclarationsStyle
No
MultiLine
Yes
+ PenaltyBreakTemplateDeclaration unsigned
# Clang 7
- BreakBeforeInheritanceComma bool
+ BreakInheritanceList BreakInheritanceListStyle
BeforeColon
BeforeComma
AfterColon
# Clang 7
+ SpaceBeforeCpp11BracedList bool
"""
class ClangFormatter(CodeFormatter):
"""Formatter for:
clang-format: A tool to format C/C++/Java/JavaScript/Objective-C/Protobuf code.
(http://clang.llvm.org/docs/ClangFormat.html)
"""
shortname = 'clang-format'
_prefer_basestyle = True
base_optionname = 'BasedOnStyle'
invalid_enums = {'Language': {'None'}}
columnlimitname = 'ColumnLimit'
configfilename = '.clang-format'
styledump_argument = '-dump-config'
def __init__(self, exe, cache=None):
# type: (str, Optional[Cache]) -> None
super(ClangFormatter, self).__init__(exe, cache=cache)
def register_options(self):
# type: () -> None
dump = self.style_dump(style_make())
if dump is None:
reporterror('Error: We could not get a proper dump-config from clang-format')
return
self.register_options_from_dump(dump)
def register_options_from_dump(self, config_dump):
# type: (str) -> None
version, styledef = find_closest_clang_version(unistr(config_dump))
self.styledefinition = self.remove_invalid_options(styledef)
def nested_derivations(self, style):
# type: (Style) -> List[Style]
options = [('BreakBeforeBraces', 'Custom')]
nstyles = []
for optionname, value in options:
optdef = styledef_option(self.styledefinition, optionname)
# We can only use this nested option if the clang version in use supports it.
if optdef is None:
continue
if value not in option_configs(optdef):
continue
if style.get(optionname) != value:
nstyle = Style(copy.deepcopy(style))
set_option(nstyle, optionname, value)
nstyles.append(nstyle)
return nstyles
def styletext(self, style):
# type: (Style) -> str
if not isinstance(style, Style):
raise TypeError()
fragments = []
for optionname, value in self.sorted_style(style).items():
if isinstance(value, Style):
text = self.styletext(value)
fragments.append('%s:' % (optionname, ))
for line in text.splitlines():
fragments.append(' %s' % line)
else:
fragments.append('%s: %s' % (optionname, textrepr(value)))
return '\n'.join(fragments) + '\n'
def cmdargs_for_style(self, formatstyle, filename=None):
# type: (Style, Optional[str]) -> List[str]
assert isinstance(formatstyle, Style)
inlinestyle = self.inlinestyletext(formatstyle)
cmdargs = ['-style=%s' % inlinestyle]
if filename is not None:
cmdargs.append('-assume-filename=' + filename)
return cmdargs
def effective_style(self, style):
# type: (Style) -> Style
dump = self.style_dump(style)
if not dump:
# The style is probably unsuitable
return style_make()
return style_make(parse_clang_dump_config(dump))
def should_report_error(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
"""We do not report known but uncritial errors
"""
if jobres.error is not None:
return True
if jobres.returncode == 0:
if jobres.stderr:
if jobres.stderr.startswith(b'Error parsing -style: Unsuitable'):
return INFO_INVALIDS in args_info
if (jobres.stderr.startswith(b'YAML:') and
b'Error parsing -style: Invalid ' in jobres.stderr):
return INFO_INVALIDS in args_info
return super(ClangFormatter, self).should_report_error(job, jobres)
def extra_penalty(self, style, complexity):
# type: (Style, int) -> Tuple[int, int]
"""Trying longer and longer column limits
without getting better results should be penalized to speed
up the search.
"""
standards = {'ColumnLimit': 80,
'MaxEmptyLinesToKeep': 2, }
penalty = 0
for optionname, value in standards.items():
fvalue = style.get(optionname, value)
if fvalue is not None and fvalue > value:
penalty += fvalue - value
if style.get('BreakBeforeBraces') == 'Custom':
# Rate a commonly known brace breaking style
# better than an equally performing custom style.
penalty += 1
# We would prefer an equally performing style even if we had to
# add another 12 options.
complexity += 12
return complexity, penalty
@staticmethod
def additional_variants(stylename, configs, unextendedname, extendoptions):
combos = []
for c in configs:
if c == unextendedname:
combos.append(stylevariant(stylename, c))
else:
for addopt in extendoptions:
extopt = stylevariant(stylename, c)
extopt.update(addopt)
combos.append(extopt)
return combos
def variants_for(self, option):
# type: (Option) -> List[Style]
"""Generates lists of possible values for this option.
('IndentCaseLabels', 'bool', ())
-> [[('IndentCaseLabels', 'true')], [('IndentCaseLabels', 'false')]]
('Language', 'LanguageKind', ['Cpp', 'Java', 'JavaScript', 'Proto'])
-> [[('Language', 'Cpp')], [('Language', 'Java')],
[('Language', 'JavaScript')], [('Language', 'Proto')]]
('PointerAlignment', 'PointerAlignmentStyle',
('Left', 'Right', 'Middle'))
->
[[('DerivePointerAlignment', 'false'), ('PointerAlignment', 'Left')],
[('DerivePointerAlignment', 'false'), ('PointerAlignment', 'Right')],
[('DerivePointerAlignment', 'false'), ('PointerAlignment', 'Middle')],
[('DerivePointerAlignment', 'true')]]
"""
def kvpairs(vs):
# type: (Iterable[OptionValue]) -> List[Style]
return stylevariants(stylename, vs)
if option is None:
return []
stylename = option_name(option)
styletype = option_type(option)
configs = option_configs(option)
nestedstyle = option_nestedstyle(option)
if nestedstyle is not None:
variants = []
for nopt in styledef_options(nestedstyle):
for nstylevariant in self.variants_for(nopt):
sty = stylevariant(stylename, nstylevariant)
if stylename == 'BraceWrapping':
set_option(sty, 'BreakBeforeBraces', 'Custom')
variants.append(sty)
return variants
if stylename == 'UseTab':
return self.additional_variants(stylename, configs, 'Never',
[stylevariant('TabWidth', i)
for i in inclusiverange(1, 8)])
if stylename == 'BreakBeforeBraces':
# The custom variant is automatically added for the
# BraceWrapping option.
vs = kvpairs(configs)
return [x for x in vs if x.get('BreakBeforeBraces') != 'Custom']
if configs:
if stylename == 'PointerAlignment':
return self.additional_variants('DerivePointerAlignment', [False, True], True,
stylevariants(stylename, configs))
return kvpairs(configs)
if styletype == 'bool':
if stylename == 'DisableFormat':
return kvpairs([False])
if stylename == 'DerivePointerAlignment':
return []
return kvpairs([True, False])
if styletype == 'int':
return []
if styletype == 'unsigned':
if stylename == 'ColumnLimit':
return kvpairs(self.column_limit_candidates)
elif stylename == 'TabWidth':
return kvpairs(inclusiverange(1, 8))
elif stylename == 'IndentWidth':
return kvpairs(inclusiverange(0, 8))
elif stylename.startswith('Penalty'):
# We avoid changing large integers whose purpose
# is not exactly clear for the moment.
return []
else:
return kvpairs(inclusiverange(0, 2))
return []
# ----------------------------------------------------------------------
class IndentFormatter(CodeFormatter):
"""Formatter for:
indent -- indent and format C program source
(http://www.freebsd.org/cgi/man.cgi?query=indent)
The options of the IndentFormatter are based on the union of
options of indent versions for FreeBSD, OpenBSD, OS X and GNU indent.
Many options will not be valid for the indent version that is used.
The complains about unknown options are registered and the offending options won't be
used for subsequent rounds of optimization.
"""
shortname = 'indent'
alternative_names = ['gindent']
base_optionname = 'indent_base_style'
columnlimitname = 'l'
configfilename = '.indent.pro'
# yapf: disable
opts = [('bacc', 'enum', ('bacc', 'nbacc')),
('bad', 'enum', ('bad', 'nbad')),
('bap', 'enum', ('bap', 'nbap')),
('bbb', 'enum', ('bbb', 'nbbb')),
('bbo', 'enum', ('bbo', 'nbbo')),
('bc', 'enum', ('bc', 'nbc')),
('bfda', 'enum', ('bfda', 'nbfda')),
('bfde', 'enum', ('bfde', 'nbfde')),
('bli', 'int', ()),
('br', 'enum', ('br', 'bl')),
('brf', 'enum', ('brf', 'blf')),
('brs', 'enum', ('brs', 'bls')),
('bs', 'enum', ('bs',)),
('c', 'int', ()),
('cbi', 'int', ()),
('cd', 'int', ()),
('cdb', 'enum', ('cdb', 'ncdb')),
('cdw', 'enum', ('cdw', 'ncdw')),
('ce', 'enum', ('ce', 'nce')),
('ci', 'int', ()),
('cli', 'int', ()),
('cp', 'int', ()),
('cs', 'enum', ('cs', 'ncs')),
('d', 'int', ()),
('di', 'int', ()),
('dj', 'enum', ('dj', 'ndj')),
('djn', 'enum', ('djn', 'ndjn')),
('eei', 'enum', ('eei', 'neei')),
('ei', 'enum', ('ei', 'nei')),
('fbs', 'enum', ('fbs', 'nfbs')),
('fc1', 'enum', ('fc1', 'nfc1')),
('fca', 'enum', ('fca', 'nfca')),
('fcb', 'enum', ('fcb', 'nfcb')),
('hnl', 'enum', ('hnl', 'nhnl')),
('i', 'int', ()),
('il', 'int', ()),
('ip', 'int', ()),
('l', 'int', ()),
('lc', 'int', ()),
('ldi', 'int', ()),
('lp', 'enum', ('lp', 'nlp')),
('lps', 'enum', ('lps', 'nlps')),
('npro', 'enum', ('npro',)),
('pcs', 'enum', ('pcs', 'npcs')),
('pi', 'int', ()),
('ppi', 'int', ()),
('prs', 'enum', ('prs', 'nprs')),
('psl', 'enum', ('psl', 'npsl')),
('saf', 'enum', ('saf', 'nsaf')),
('sai', 'enum', ('sai', 'nsai')),
('saw', 'enum', ('saw', 'nsaw')),
('sbi', 'int', ()),
('sc', 'enum', ('sc', 'nsc')),
('sob', 'enum', ('sob', 'nsob')),
('ss', 'enum', ('ss', 'nss')),
('st', 'enum', ('st',)),
('ts', 'int', ()),
('ut', 'enum', ('ut', 'nut'))]
# yapf: enable
def __init__(self, exe, cache=None):
super(IndentFormatter, self).__init__(exe, cache=cache)
@classmethod
def executable_names(cls):
# Change the order to prefer gindent instead of indent if available.
return cls.alternative_names + [cls.shortname]
def register_options(self):
styles = []
gnu_ident = self.prefer_basestyle
for optname, opttype, configs in self.opts:
if not gnu_ident and optname == 'ip':
# The BSD indent is not a numeric but a boolean option.
opttype, configs = 'enum', ('ip', 'nip')
styles.append(option_make(optname, opttype, configs))
if self.prefer_basestyle:
styles.append(option_make(self.base_optionname, 'enum', ('orig', 'linux', 'kr',
'gnu')))
self.styledefinition = styledef_make(styles)
@property
def prefer_basestyle(self):
# type: () -> bool
return self.version_string.startswith('GNU indent')
def cmdlineopt(self, optionname, value):
# type: (str, OptionValue) -> str
option = self.styledefinition[optionname]
styletype = option_type(option)
configs = option_configs(option)
if configs:
return "-%s" % value
if styletype == 'int':
return "-%s%s" % (optionname, value)
else:
raise ValueError
def styletext(self, styles):
# type: (Style) -> str
fragments = []
for optionname, value in self.sorted_style(styles).items():
fragments.append(self.cmdlineopt(optionname, value))
return '\n'.join(fragments) + '\n'
def cmdargs_for_style(self, formatstyle, filename=None):
# type: (Style, Optional[str]) -> List[str]
assert isinstance(formatstyle, Style)
# -npro: ignore .indent.pro files
# -st: read source from stdin, write result to stdout
cmdargs = ['-npro', '-st']
for optname, value in sorted(formatstyle.items()):
cmdargs.append(self.cmdlineopt(optname, value))
return cmdargs
def should_report_error(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
if jobres.error is not None:
return True
if self.invalid_cmdline_option(job, jobres):
return INFO_INVALIDS in args_info
return jobres.returncode != 0 or bool(jobres.stderr)
def valid_job_result(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
return jobres.error is None and jobres.returncode == 0 and not bool(jobres.stderr)
def invalid_cmdline_option(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
if jobres.returncode != 1:
return False
# Handle the known error messages
msg = unistr(jobres.stderr)
if msg.startswith('command line: unknown option'):
return True
if msg.startswith('command line: option'):
return True
if msg.startswith('indent: bad font specification'):
return True
if msg.startswith('indent: ?: unknown parameter'):
return True
if msg.endswith('requires a parameter\n'):
return True
return False
def variants_for(self, option):
# type: (Option) -> List[Style]
def kvpairs(vs):
# type: (Iterable[OptionValue]) -> List[Style]
return stylevariants(stylename, vs)
stylename = option_name(option)
styletype = option_type(option)
configs = option_configs(option)
if configs:
return kvpairs(configs)
if stylename == self.columnlimitname:
return kvpairs(self.column_limit_candidates)
if styletype == 'int':
return kvpairs([0, 1, 2, 4, 8, 16])
return []
def reformat(self, sourcefile, destfile, configfile):
# type: (str, str, str) -> None
tmpdir = tempfile.mkdtemp(prefix='whatstyle_')
cfg = os.path.join(tmpdir, self.configfilename)
copyfile(configfile, cfg)
data = readbinary(sourcefile)
# -st: read source from stdin, write result to stdout
exeresult = run_executable(self.exe, ['-st'], stdindata=data)
writebinary(destfile, exeresult.stdout)
os.remove(cfg)
os.rmdir(tmpdir)
# ----------------------------------------------------------------------
class YapfFormatter(CodeFormatter):
"""Formatter for:
yapf: Formatter for Python code.
(https://github.com/google/yapf)
"""
shortname = 'yapf'
_prefer_basestyle = True
base_optionname = 'based_on_style'
columnlimitname = 'column_limit'
base_styles = 'pep8 chromium google facebook'.split()
configfilename = '.style.yapf'
styledump_argument = '--style-help'
def __init__(self, exe, cache=None):
super(YapfFormatter, self).__init__(exe, cache=cache)
@staticmethod
def typefromvalue(optvalue):
# type: (str) -> str
if optvalue in ['true', 'false']:
return 'bool'
try:
int(optvalue)
return 'int'
except ValueError:
pass
return 'string'
def register_options(self):
# type: () -> None
"""Parse options from text like this
ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=True
Align closing bracket with visual indentation.
BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF=False
Insert a blank line before a 'def' or 'class' immediately nested
"""
styles = [option_make(self.base_optionname, 'string', self.base_styles)]
for optname, optvalue in self.iter_options(style_make()):
styles.append(option_make(optname, self.typefromvalue(optvalue), tuple()))
self.styledefinition = styledef_make(styles)
def effective_style(self, style):
# type: (Style) -> Style
stylevalues = style_make()
for optname, optvalue in self.iter_options(style):
set_option(stylevalues, optname, optvalue)
return stylevalues
def iter_options(self, style):
# type: (Style) -> Iterator[TextPair]
dump = self.style_dump(style)
for optname, optvalue in parse_keyvalue_pairs(dump):
optname = optname.lower()
optvalue = optvalue.lower()
yield optname, optvalue
def styletext(self, styles):
# type: (Style) -> str
fragments = ['[style]']
for optionname, value in self.sorted_style(styles).items():
fragments.append('%s = %s' % (optionname, textrepr(value)))
return '\n'.join(fragments) + '\n'
def cmdargs_for_style(self, formatstyle, filename=None):
# type: (Style, Optional[str]) -> List[str]
assert isinstance(formatstyle, Style)
inlinestyle = self.inlinestyletext(formatstyle)
cmdargs = ['--no-local-style', '--style=%s' % inlinestyle]
if filename is not None:
cmdargs.append(filename)
return cmdargs
def should_report_error(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
# Yapf exits with code 2 when the reformatted output is different
# from the input and with code 0 when the output is unchanged.
if jobres.error is not None:
return True
return jobres.returncode not in [0, 2] or bool(jobres.stderr)
def valid_job_result(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
return jobres.error is None and jobres.returncode in [0, 2] and not jobres.stderr
def variants_for(self, option):
# type: (Option) -> List[Style]
def kvpairs(vs):
# type: (Iterable[OptionValue]) -> List[Style]
return stylevariants(stylename, vs)
stylename = option_name(option)
styletype = option_type(option)
configs = option_configs(option)
if configs:
return kvpairs(configs)
if styletype == 'bool':
return kvpairs([True, False])
if styletype == 'int':
if stylename == 'column_limit':
# Here we can get weird results, for example
# in bottle_sqlalchemy.py is a constructor with
# 8 arguments which are already split between two lines.
# We find an optimum column limit of 126 because this
# has less diff lines than putting each argument on a new
# line. Maybe we should use a different diff metric.
return kvpairs(self.column_limit_candidates)
elif stylename == 'indent_width':
return kvpairs([2, 4, 8])
elif stylename == 'spaces_before_comment':
return kvpairs(inclusiverange(1, 4))
elif stylename.startswith('split_penalty'):
# We avoid changing large integers whose purpose
# is not exactly clear for the moment.
pass
return []
# ----------------------------------------------------------------------
class HtmlTidyFormatter(CodeFormatter):
"""Formatter for:
Tidy - The granddaddy of HTML tools.
(http://www.html-tidy.org)
"""
shortname = 'tidy'
columnlimitname = 'wrap'
configfilename = 'tidy.conf'
styledump_argument = '-show-config'
def __init__(self, exe, cache=None):
super(HtmlTidyFormatter, self).__init__(exe, cache=cache)
style = style_make()
set_option(style, 'indent', 'yes')
self.initial_style = style
def register_options(self):
# type: () -> None
"""Parse options from XML like this:
<?xml version="1.0"?>
<config version="5.1.25">
<option class="print">
<name>indent-spaces</name>
<type>Integer</type>
<default>2</default>
<example>0, 1, 2, ...</example>
<description>This option specifies the number of spaces or tabs that
Tidy uses to indent content when <code>indent</code> is enabled.
<br/>Note that the default value for this option is dependent
upon the value of <code>indent-with-tabs</code> (see also).
</description>
<seealso>indent</seealso>
</option>
</config>
"""
exeresult = run_executable(self.exe, ['-xml-config'], cache=self.cache)
buf = BytesIO(exeresult.stdout)
optionname = None # type: Optional[str]
optiontype = None
example = None
options = []
for event, elem in ETree.iterparse(buf, events=('start', 'end')):
tag = elem.tag
if event == 'end':
if optionname is not None and tag == 'option':
# First ignore some options
if optionname.startswith('show-'):
continue
if optionname.startswith('new-'):
continue
if optionname == 'write-back':
continue
if optionname == 'char-encoding':
continue
if not self.allow_encoding_change and optionname.endswith('-encoding'):
continue
if optiontype in ['AutoBool', 'Boolean', 'Integer']:
options.append(option_make(optionname, optiontype, tuple()))
elif optiontype in ['String']:
continue
else:
# Remove comments from the option values
# e.g. 0 (Tidy Classic), 1 (Priority 1 Checks), ...
if example is not None:
example = re.sub(r'\s*\(.*?\)', '', example)
configs = example.split(', ') # type: List[str]
else:
configs = []
if not configs:
continue
optvalues = [typeconv(c) for c in configs]
options.append(option_make(optionname, optiontype, optvalues))
elif tag == 'name':
optionname = self.safeunistr(elem.text)
elif tag == 'type':
optiontype = self.safeunistr(elem.text)
elif tag == 'example':
example = self.safeunistr(elem.text)
self.styledefinition = styledef_make(options)
@staticmethod
def safeunistr(text):
# type: (Union[str, bytes, None]) -> Optional[str]
if text is None:
return None
return unistr(text)
def styletext(self, styles):
# type: (Style) -> str
fragments = []
for optionname, value in self.sorted_style(styles).items():
fragments.append('%s: %s' % (optionname, value))
return '\n'.join(fragments) + '\n'
@staticmethod
def styleargs(style):
# type: (Style) -> List[str]
args = []
for key in sorted(style.keys()):
value = style[key]
value = str(value)
args.append('--' + key)
args.append(value)
return args
def inlinestyletext(self, style):
# type: (Style) -> str
return ' '.join(HtmlTidyFormatter.styleargs(style))
def cmdargs_for_style(self, formatstyle, filename=None):
# type: (Style, Optional[str]) -> List[str]
assert isinstance(formatstyle, Style)
args = HtmlTidyFormatter.styleargs(formatstyle)
cmdargs = ['--show-warnings', 'no'] + args
return cmdargs
def effective_style(self, style):
# type: (Style) -> Style
stylevalues = style_make()
dump = self.style_dump(style)
if dump is None:
return stylevalues
typepos = -1
valuepos = -1
for line in dump.splitlines():
if typepos < 0:
typepos = line.find('Type')
valuepos = line.find('Current Value')
continue
if len(line) < typepos:
break
optname = line[:typepos].strip()
optvalue = line[valuepos:].strip()
pos = optvalue.find(' ')
if pos >= 0:
# Only keep the value up unto the first space.
optvalue = optvalue[:pos]
set_option(stylevalues, optname, optvalue)
return stylevalues
def should_report_error(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
# tidy exits with code 1 when there are warnings
# and with code 0 if everything is fine.
if jobres.error is not None:
return True
return jobres.returncode not in [0, 1]
def valid_job_result(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
if jobres.error is not None:
return False
if jobres.returncode not in [0, 1]:
return False
if not jobres.stdout:
# For some reason tidy sometimes returns with code 1 (warning)
# but writes no output.
return False
return True
def variants_for(self, option):
# type: (Option) -> List[Style]
stylename = option_name(option)
styletype = option_type(option)
configs = option_configs(option)
def kvpairs(vs):
# type: (Iterable[OptionValue]) -> List[Style]
return stylevariants(stylename, vs)
if configs:
return kvpairs(configs)
if stylename == 'indent':
return kvpairs(['yes'])
if stylename == 'wrap':
return kvpairs([0])
if stylename == 'indent-spaces':
return kvpairs(inclusiverange(0, 8))
if styletype == 'AutoBool':
return kvpairs(['yes', 'no', 'auto'])
if styletype == 'Boolean':
return kvpairs(['yes', 'no'])
return []
def reformat(self, sourcefile, destfile, configfile):
# type: (str, str, str) -> None
run_executable(self.exe, ['-quiet', '--show-errors', '0', '--show-warnings', 'no',
'-output', destfile, '-config', configfile, sourcefile])
# ----------------------------------------------------------------------
class UncrustifyFormatter(CodeFormatter):
"""Formatter for:
uncrustify: Code beautifier.
(https://github.com/uncrustify/uncrustify)
uncrustify 0.63 tells us that:
'There are currently 515 options and minimal documentation.
Try UniversalIndentGUI and good luck.'
Cross fingers.
"""
shortname = 'uncrustify'
columnlimitname = 'code_width'
configfilename = 'uncrustify.cfg'
styledump_argument = '--update-config'
language_exts = [
['C', CEXTS], ['CPP', CPPEXTS], ['D', '.d'], ['CS', '.cs'], ['JAVA', '.java'],
['PAWN', '.p .pawn .sma'], ['OC', '.h .m'], ['OC+', '.h .mm'], ['VALA', '.vala']
]
def __init__(self, exe, cache=None):
super(UncrustifyFormatter, self).__init__(exe, cache=cache)
def register_options(self):
# type: () -> None
"""Parse options from text like this:
# Uncrustify 0.63
#
# General options
#
newlines { Auto, LF, CR, CRLF }
The type of line endings
input_tab_size Number
The original size of tabs in the input
indent_align_string { False, True }
Whether to indent strings broken by '\' so that they line up
# The format changed with uncrustify 0.68 as follows:
# Uncrustify-0.69.0_f
# The type of line endings.
#
# Default: auto
newlines = auto # lf/crlf/cr/auto
# The original size of tabs in the input.
#
# Default: 8
input_tab_size = 8 # unsigned number
# Whether to indent strings broken by '\' so that they line up.
indent_align_string = false # true/false
"""
exeresult = run_executable(self.exe, ['--show-config'], cache=self.cache)
options = []
text = unistr(exeresult.stdout)
for m in re.finditer(r'^(\w+)\s+(.*?)\s*$', text, re.MULTILINE):
optionname, optiondesc = m.group(1), m.group(2)
if optiondesc.startswith('{'):
optiontype = 'Enum'
configs = optiondesc[1:-1].strip().split(', ')
configs = [c.lower() for c in configs]
elif optiondesc in ['Number', 'String', 'Unsigned Number']:
optiontype = optiondesc
configs = []
else:
# New format >= uncrustify 0.68
parts = optiondesc.split('#')
if len(parts) <= 1:
continue
configs = parts[-1].strip().split('/')
if len(configs) >= 2:
# options as in lf/crlf/cr/auto for example.
optiontype = 'Enum'
configs = [c.lower() for c in configs]
else:
# number, unsigned number or string
optiontype = configs[0].title()
configs = []
options.append(option_make(optionname, optiontype, configs))
self.styledefinition = styledef_make(options)
def identify_language(self, filenames=(), language=None):
# type: (Sequence[str], Optional[str]) -> None
"""Identify the languages from the filenames extensions.
"""
if language is None:
exts = set([os.path.splitext(f)[1] for f in filenames])
for lang, extsdescription in UncrustifyFormatter.language_exts:
langexts = set(extsdescription.split())
if exts.issubset(langexts):
self.languages.append(lang)
else:
self.languages.append(language)
def attempt_acceptible(self, roundnr, prevdist, newdist):
# type: (int, Sequence[int], Sequence[int]) -> bool
if roundnr >= 3 and tuple(newdist) > tuple(prevdist):
# Makes things worse
return False
if roundnr >= 3 and tuple(newdist) >= tuple(prevdist):
# Does not improve things
return False
return True
def styletext(self, styles):
# type: (Style) -> str
fragments = []
for optionname, value in self.sorted_style(styles).items():
fragments.append('%s = %s' % (optionname, textrepr(value)))
return '\n'.join(fragments) + '\n'
def inlinestyletext(self, style):
# type: (Style) -> str
return self.styletext(style)
def cmdargs_for_style(self, formatstyle, filename=None):
# type: (Style, Optional[str]) -> List[str]
assert isinstance(formatstyle, Style)
configdata = bytestr(self.styletext(formatstyle))
sha = shahex(configdata)
cfg = os.path.join(tempfile.gettempdir(), 'whatstyle_uncrustify_%s.cfg' % sha)
if not self.tempfile_exists(cfg):
writebinary(cfg, configdata)
self.add_tempfile(cfg)
cmdargs = ['-c', cfg]
# The filename extension might be ambiguous so we choose from the languages
# registered in identify_language.
if self.languages:
lang = self.languages[0]
cmdargs.extend(['-l', lang])
return cmdargs
def effective_style(self, style):
# type: (Style) -> Style
stylevalues = style_make()
dump = self.style_dump(style)
for optname, optvalue in parse_keyvalue_pairs(dump):
set_option(stylevalues, optname, optvalue)
return stylevalues
def should_report_error(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
if jobres.error is not None:
return True
return jobres.returncode != 0
def valid_job_result(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
if jobres.error is not None:
return False
if jobres.returncode != 0:
return False
if not jobres.stdout:
# For some reason tidy returns with code 1 (warning)
# but writes no output.
return False
return True
# Some reasonable numeric values for various options.
ir = inclusiverange
num_tables = [
({'input_tab_size', 'output_tab_size'}, ir(1, 8)),
({'indent_continue', 'indent_var_def_blk'}, ir(-4, 4)),
({'indent_with_tabs', 'indent_brace', 'align_var_def_span',
'align_var_def_star_style', 'align_var_def_amp_style', 'align_var_def_gap'},
ir(0, 2)),
({'indent_namespace_level', 'indent_ctor_init_leading', 'indent_ctor_init',
'indent_switch_case'}, ir(0, 4)),
# These numeric options are rather enums
({'nl_remove_extra_newlines', 'cmt_reflow_mode', 'indent_paren_close'}, ir(0, 2)),
({'align_typedef_func', 'align_typedef_star_style', 'align_typedef_amp_style'},
ir(0, 2)),
] # type: List[Tuple[Set[str], Iterable[int]]]
def variants_for(self, option):
# type: (Option) -> List[Style]
stylename = option_name(option)
styletype = option_type(option)
configs = option_configs(option)
def kvpairs(vs):
# type: (Iterable[OptionValue]) -> List[Style]
return stylevariants(stylename, vs)
if stylename == 'newlines':
# Getting this wrong makes a big mess
return kvpairs(['auto'])
if configs:
# ignore is the default which we don't need to specify.
usefulvalues = [v for v in configs if v != 'ignore']
return kvpairs(usefulvalues)
# All of the following should be options of type number.
if styletype not in ['Number', 'Unsigned Number']:
return []
for nameset, values in self.num_tables:
if stylename in nameset:
return kvpairs(values)
# Some reasonable values according to the documentation
if stylename == 'indent_columns':
return kvpairs([2, 3, 4, 8])
if stylename == 'indent_member':
return kvpairs([0, 1, 2, 3, 4, 8])
if stylename == 'indent_xml_string':
return [style_make({'indent_align_string': 'True',
stylename: c}) for c in inclusiverange(0, 2)]
#
# Line Splitting options
#
if stylename == 'code_width':
return kvpairs(self.column_limit_candidates)
# It would be possible to try out unknown numerical options
# but this would slow everything down,
# so we don't simply return kvpairs(inclusiverange(-8, 8)).
return []
def reformat(self, sourcefile, destfile, configfile):
# type: (str, str, str) -> None
run_executable(self.exe, ['-c', configfile, '-f', sourcefile, '-o', destfile])
# ----------------------------------------------------------------------
class ArtisticStyleFormatter(CodeFormatter):
"""Formatter for:
Artistic Style: A Free, Fast, and Small Automatic Formatter
for C, C++, C++/CLI, Objective‑C, C#, and Java Source Code.
(http://astyle.sourceforge.net)
"""
shortname = 'astyle'
columnlimitname = 'max-code-length'
configfilename = '.astylerc'
def __init__(self, exe, cache=None):
super(ArtisticStyleFormatter, self).__init__(exe, cache=cache)
def register_options(self):
# type: () -> None
"""Parse options from 'astyle --help' like:
--style=allman OR --style=bsd OR --style=break OR -A1
--indent=spaces=# OR -s#
Other Options:
--------------
--suffix=####
"""
exeresult = run_executable(self.exe, ['--help'], cache=self.cache)
options = []
text = unistr(exeresult.stdout)
seen = set() # type: Set[str]
opts = OrderedDict() # type: OrderedDict[str, List[OptionValue]]
accept_options = True
for line in text.splitlines():
if line == 'Other Options:' or '--suffix=' in line:
# No more useable option after this point.
break
if not line or line.startswith('---'):
# Begin of a new option block or paragraph.
accept_options = True
continue
if not accept_options:
# Do not register options in the free form explanation of the options
# described at the beginning of a paragraph.
continue
m = re.match(r'^\s+--(\S+).*$', line)
if m:
argument = m.group(1)
if argument in seen:
continue
seen.add(argument)
pos = argument.find('=')
if pos > 0:
optionname, value = argument[:pos], argument[pos + 1:]
else:
optionname, value = argument, OPTION_PRESENT
values = opts.get(optionname) # type: List[OptionValue]
if values is None:
values = []
opts[optionname] = values
values.append(value)
else:
# Stop accepting options until the next paragraph begins.
accept_options = False
for optionname, configs in opts.items():
options.append(option_make(optionname, 'enum', configs))
self.styledefinition = styledef_make(options)
def optionlist(self, styles):
# type: (Style) -> List[str]
options = []
for optionname, value in self.sorted_style(styles).items():
opt = '--' + optionname
if value != OPTION_PRESENT:
opt += '=' + str(value)
options.append(opt)
return options
def styletext(self, style):
# type: (Style) -> str
return '\n'.join(self.optionlist(style)) + '\n'
def cmdargs_for_style(self, formatstyle, filename=None):
# type: (Style, Optional[str]) -> List[str]
assert isinstance(formatstyle, Style)
cmdargs = ['--options=none', '--quiet'] + self.optionlist(formatstyle)
return cmdargs
def variants_for(self, option):
# type: (Option) -> List[Style]
def kvpairs(vs):
# type: (Iterable[OptionValue]) -> List[Style]
return stylevariants(stylename, vs)
def numreplace(configs, numvalues):
# type: (List[OptionValue], Iterable[int]) -> List[OptionValue]
extconfigs = [] # type: List[OptionValue]
for c in configs:
if isinstance(c, text_type) and '#' in c:
for n in numvalues:
num = str(n)
nc = c.replace('#', num)
extconfigs.append(nc)
else:
extconfigs.append(c)
return extconfigs
stylename = option_name(option)
configs = option_configs(option)
if stylename == self.columnlimitname:
candidates = self.column_limit_candidates
candidates = [c for c in candidates if 50 <= c <= 200]
return kvpairs(candidates)
if stylename == 'indent':
return kvpairs(numreplace(configs, [2, 4, 8]))
if stylename == 'min-conditional-indent':
return kvpairs(numreplace(configs, [0, 1, 2, 3]))
if stylename == 'max-instatement-indent':
return kvpairs(numreplace(configs, inclusiverange(40, 120)))
if stylename == 'mode':
return []
if configs:
return kvpairs(numreplace(configs, [1, 2, 4, 8]))
return []
def reformat(self, sourcefile, destfile, configfile):
# type: (str, str, str) -> None
formatstyle = style_make()
with open(configfile) as fp:
for line in fp.readlines():
line = line.rstrip()
if line.startswith('--'):
line = line[2:]
pos = line.find('=')
if pos > 0:
optionname, value = line[:pos], line[pos + 1:]
else:
optionname, value = line, OPTION_PRESENT
set_option(formatstyle, optionname, value)
sourcedata = readbinary(sourcefile)
data = self.formatcode(formatstyle, sourcedata, filename=sourcefile)
if data is None:
data = b''
writebinary(destfile, data)
# ----------------------------------------------------------------------
class ScalariformFormatter(CodeFormatter):
"""Formatter for:
Scalariform - a code formatter for Scala.
(https://github.com/scala-ide/scalariform)
"""
shortname = 'scalariform'
configfilename = 'formatterPreferences.properties'
language_exts = [['SCALA', SCALAEXTS]]
def __init__(self, exe, cache=None):
super(ScalariformFormatter, self).__init__(exe, cache=cache)
def register_options(self):
# type: () -> None
"""Parse options from text like this:
Preferences:
[+|-]alignArguments Enable/disable ...
...
[+|-]spacesWithinPatternBinders Enable/disable ...
-alignSingleLineCaseStatements.maxArrowIndent=[1-100] Set Maximum number ...
-indentSpaces=[1-10] Set Number of spaces ...
"""
exeresult = run_executable(self.exe, ['--help'], cache=self.cache)
options = []
text = unistr(exeresult.stdout)
for m in re.finditer(r'^ (\[\+\|-\]|-)([a-z][a-zA-Z.]+)(?:=\[(\d+)-(\d+)\])?', text,
re.MULTILINE):
optionprefix, optionname, start, end = m.groups()
if start is None:
optiontype = 'bool'
configs = [True, False] # type: List[OptionValue]
else:
optiontype = 'int'
configs = list(inclusiverange(int(start), int(end)))
options.append(option_make(optionname, optiontype, configs))
self.styledefinition = styledef_make(options)
def styletext(self, style):
# type: (Style) -> str
fragments = []
for optionname, value in self.sorted_style(style).items():
fragments.append('%s=%s' % (optionname, textrepr(value)))
return '\n'.join(fragments) + '\n'
def inlinestyletext(self, style):
# type: (Style) -> str
return self.styletext(style)
def cmdargs_for_style(self, formatstyle, filename=None):
# type: (Style, Optional[str]) -> List[str]
assert isinstance(formatstyle, Style)
cmdargs = ['--stdin', '--stdout']
for optname, value in sorted(formatstyle.items()):
cmdargs.append(self.cmdlineopt(optname, value))
return cmdargs
def cmdlineopt(self, optionname, value):
# type: (str, str, OptionValue) -> str
option = self.styledefinition[optionname]
styletype = option_type(option)
if styletype == 'bool':
prefix = '+' if value else '-'
return prefix + optionname
if styletype == 'int':
return "-%s=%s" % (optionname, value)
else:
raise ValueError
def should_report_error(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
if jobres.error is not None:
return True
return jobres.returncode != 0
def valid_job_result(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
if jobres.error is not None:
return False
if jobres.returncode != 0:
return False
return True
def variants_for(self, option):
# type: (Option) -> List[Style]
stylename = option_name(option)
configs = option_configs(option)
def kvpairs(vs):
# type: (Iterable[OptionValue]) -> List[Style]
return stylevariants(stylename, vs)
if configs:
return kvpairs(configs)
return []
def reformat(self, sourcefile, destfile, configfile):
# type: (str, str, str) -> None
data = readbinary(sourcefile)
exeresult = run_executable(self.exe, ['--preferenceFile=' + unifilename(configfile),
'--stdin'],
stdindata=data)
writebinary(destfile, exeresult.stdout)
# ----------------------------------------------------------------------
SCALAFMT_OPTIONS = """\
maxColumn = 80
project.git = false
align.openParenCallSite = true
align.openParenDefnSite = true
align.ifWhileOpenParen = true
align.arrowEnumeratorGenerator = false
continuationIndent.callSite = 2
continuationIndent.defnSite = 4
continuationIndent.extendSite = 4
optIn.breaksInsideChains = false
optIn.blankLineBeforeDocstring = false
optIn.selfAnnotationNewline = true
optIn.annotationNewlines = true
optIn.breakChainOnFirstMethodDot = true
optIn.configStyleArguments = true
assumeStandardLibraryStripMargin = false
newlines.alwaysBeforeMultilineDef = true
newlines.afterImplicitKWInVerticalMultiline = false
newlines.alwaysBeforeElseAfterCurlyIf = false
newlines.neverInResultType = false
newlines.sometimesBeforeColonInMethodReturnType = true
newlines.alwaysBeforeTopLevelStatements = false
newlines.afterCurlyLambda = never
newlines.penalizeSingleSelectMultiArgList = true
newlines.neverBeforeJsNative = false
newlines.alwaysBeforeCurlyBraceLambdaParams = false
newlines.beforeImplicitKWInVerticalMultiline = false
unindentTopLevelOperators = false
poorMansTrailingCommasInConfigStyle = false
docstrings = ScalaDoc
lineEndings = unix
rewrite.redundantBraces.methodBodies = true
rewrite.redundantBraces.stringInterpolation = false
rewrite.redundantBraces.generalExpressions = false
rewrite.redundantBraces.includeUnitMethods = true
danglingParentheses = false
includeCurlyBraceInSelectChains = true
binPack.unsafeCallSite = false
binPack.unsafeDefnSite = false
binPack.literalArgumentLists = true
binPack.parentConstructors = false
indentYieldKeyword = true
importSelectors = noBinPack
verticalMultilineAtDefinitionSite = false
spaces.inByNameTypes = true
spaces.afterTripleEquals = false
spaces.inImportCurlyBraces = false
spaces.inParentheses = false
spaces.afterKeywordBeforeParen = true
"""
class ScalafmtFormatter(CodeFormatter):
"""Formatter for:
Scalafmt - code formatter for Scala.
(https://github.com/olafurpg/scalafmt)
"""
shortname = 'scalafmt'
_prefer_basestyle = True
base_optionname = 'style'
base_styles = 'default IntelliJ Scala.js'.split()
configfilename = '.scalafmt'
language_exts = [['SCALA', SCALAEXTS]]
def __init__(self, exe, cache=None):
super(ScalafmtFormatter, self).__init__(exe, cache=cache)
def register_options(self):
# type: () -> None
options = [option_make(self.base_optionname, 'string', self.base_styles)]
for line in SCALAFMT_OPTIONS.splitlines():
optionname, optvalue = re.split(r'\s*=\s*', line)
if optionname == 'maxColumn':
optiontype = 'int'
configs = list(inclusiverange(80, 100)) # type: List[OptionValue]
elif optionname.startswith('continuationIndent'):
optiontype = 'int'
configs = [2, 4, 8]
elif optionname == 'docstrings':
optiontype = 'enum'
configs = ['ScalaDoc', 'JavaDoc']
elif optionname == 'lineEndings':
optiontype = 'enum'
configs = ['preserve', 'unix', 'windows']
elif optionname == 'importSelectors':
optiontype = 'enum'
configs = ['binPack', 'noBinPack', 'singleLine']
elif optvalue in ['true', 'false']:
optiontype = 'bool'
configs = [True, False]
else:
continue
options.append(option_make(optionname, optiontype, configs))
self.styledefinition = styledef_make(options)
def styletext(self, style):
# type: (Style) -> str
fragments = []
for optionname, value in self.sorted_style(style).items():
fragments.append('%s = %s' % (optionname, textrepr(value)))
return '\n'.join(fragments) + '\n'
def inlinestyletext(self, style):
# type: (Style) -> str
return self.styletext(style)
def cmdargs_for_style(self, formatstyle, filename=None):
# type: (Style, Optional[str]) -> List[str]
assert isinstance(formatstyle, Style)
cmdargs = []
for optname, value in self.sorted_style(formatstyle).items():
cmdargs.append('%s=%s' % (optname, textrepr(value)))
config_str = '{%s}' % ','.join(cmdargs)
cmdargs = ['--stdin', '--stdout', '--config-str', config_str]
return cmdargs
for optname, value in self.sorted_style(formatstyle).items():
cmdargs.extend(self.cmdlineopts(optname, value))
return cmdargs
def should_report_error(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
if jobres.error is not None:
return True
return jobres.returncode != 0
def valid_job_result(self, job, jobres):
# type: (ExeCall, ExeResult) -> bool
if jobres.error is not None:
return False
if jobres.returncode != 0:
return False
return True
def variants_for(self, option):