creeper-adventure/.venv/lib/python3.8/site-packages/pyflyby/_format.py
2022-03-31 20:20:07 -05:00

180 lines
6.6 KiB
Python

# pyflyby/_format.py.
# Copyright (C) 2011, 2012, 2013, 2014 Karl Chen.
# License: MIT http://opensource.org/licenses/MIT
from __future__ import (absolute_import, division, print_function,
with_statement)
import six
class FormatParams(object):
max_line_length = 79
wrap_paren = True
indent = 4
hanging_indent = 'never'
use_black = False
def __new__(cls, *args, **kwargs):
if not kwargs and len(args) == 1 and isinstance(args[0], cls):
return args[0]
self = object.__new__(cls)
# TODO: be more careful here
dicts = []
for arg in args:
if arg is None:
pass
elif isinstance(arg, cls):
dicts.append(arg.__dict__)
else:
raise TypeError
if kwargs:
dicts.append(kwargs)
for kwargs in dicts:
for key, value in six.iteritems(kwargs):
if hasattr(self, key):
setattr(self, key, value)
else:
raise ValueError("bad kwarg %r" % (key,))
return self
def fill(tokens, sep=(", ", ""), prefix="", suffix="", newline="\n",
max_line_length=80):
r"""
Given a sequences of strings, fill them into a single string with up to
``max_line_length`` characters each.
>>> fill(["'hello world'", "'hello two'"],
... prefix=("print ", " "), suffix=(" \\", ""),
... max_line_length=25)
"print 'hello world', \\\n 'hello two'\n"
:param tokens:
Sequence of strings to fill. There must be at least one token.
:param sep:
Separator string to append to each token. If a 2-element tuple, then
indicates the separator between tokens and the separator after the last
token. Trailing whitespace is removed from each line before appending
the suffix, but not from between tokens on the same line.
:param prefix:
String to prepend at the beginning of each line. If a 2-element tuple,
then indicates the prefix for the first line and prefix for subsequent
lines.
:param suffix:
String to append to the end of each line. If a 2-element tuple, then
indicates the suffix for all lines except the last, and the suffix for
the last line.
:return:
Filled string.
"""
N = max_line_length
assert len(tokens) > 0
if isinstance(prefix, tuple):
first_prefix, cont_prefix = prefix
else:
first_prefix = cont_prefix = prefix
if isinstance(suffix, tuple):
nonterm_suffix, term_suffix = suffix
else:
nonterm_suffix = term_suffix = suffix
if isinstance(sep, tuple):
nonterm_sep, term_sep = sep
else:
nonterm_sep = term_sep = sep
lines = [first_prefix + tokens[0]]
for token, is_last in zip(tokens[1:], [False]*(len(tokens)-2) + [True]):
suffix = term_suffix if is_last else nonterm_suffix
sep = (term_sep if is_last else nonterm_sep).rstrip()
# Does the next token fit?
if len(lines[-1] + nonterm_sep + token + sep + suffix) <= N:
# Yes; add it.
lines[-1] += nonterm_sep + token
else:
# No; break into new line.
lines[-1] += nonterm_sep.rstrip() + nonterm_suffix + newline
lines.append(cont_prefix + token)
lines[-1] += term_sep.rstrip() + term_suffix + newline
return ''.join(lines)
def pyfill(prefix, tokens, params=FormatParams()):
"""
Fill a Python statement.
>>> print(pyfill('print ', ["foo.bar", "baz", "quux", "quuuuux"]), end='')
print foo.bar, baz, quux, quuuuux
>>> print(pyfill('print ', ["foo.bar", "baz", "quux", "quuuuux"],
... FormatParams(max_line_length=15, hanging_indent='auto')), end='')
print (foo.bar,
baz,
quux,
quuuuux)
>>> print(pyfill('print ', ["foo.bar", "baz", "quux", "quuuuux"],
... FormatParams(max_line_length=14, hanging_indent='auto')), end='')
print (
foo.bar,
baz, quux,
quuuuux)
:param prefix:
Prefix for first line.
:param tokens:
Sequence of string tokens
:type params:
`FormatParams`
:rtype:
``str``
"""
N = params.max_line_length
if params.wrap_paren:
# Check how we will break up the tokens.
len_full = sum(len(tok) for tok in tokens) + 2 * (len(tokens)-1)
if len(prefix) + len_full <= N:
# The entire thing fits on one line; no parens needed. We check
# this first because breaking into lines adds paren overhead.
#
# Output looks like:
# from foo import abc, defgh, ijkl, mnopq, rst
return prefix + ", ".join(tokens) + "\n"
if params.hanging_indent == "never":
hanging_indent = False
elif params.hanging_indent == "always":
hanging_indent = True
elif params.hanging_indent == "auto":
# Decide automatically whether to do hanging-indent mode. If any
# line would exceed the max_line_length, then do hanging indent;
# else don't.
#
# In order to use non-hanging-indent mode, the first line would
# have an overhead of 2 because of "(" and ",". We check the
# longest token since even if the first token fits, we still want
# to avoid later tokens running over N.
maxtoklen = max(len(token) for token in tokens)
hanging_indent = (len(prefix) + maxtoklen + 2 > N)
else:
raise ValueError("bad params.hanging_indent=%r"
% (params.hanging_indent,))
if hanging_indent:
# Hanging indent mode. We need a single opening paren and
# continue all imports on separate lines.
#
# Output looks like:
# from foo import (
# abc, defgh, ijkl,
# mnopq, rst)
return (prefix + "(\n"
+ fill(tokens, max_line_length=N,
prefix=(" " * params.indent), suffix=("", ")")))
else:
# Non-hanging-indent mode.
#
# Output looks like:
# from foo import (abc, defgh,
# ijkl, mnopq,
# rst)
pprefix = prefix + "("
return fill(tokens, max_line_length=N,
prefix=(pprefix, " " * len(pprefix)), suffix=("", ")"))
else:
raise NotImplementedError