YAPF es un formateador de Python basado en clang-format
(desarrollado por Daniel Jasper). En esencia, el algoritmo toma el código y calcula el mejor formato que se ajuste al estilo configurado. Elimina gran parte de la monotonía de mantener su código.
El objetivo final es que el código que produce YAPF sea tan bueno como el código que escribiría un programador si siguiera la guía de estilo.
Nota YAPF no es un producto oficial de Google (experimental o no), es solo un código que resulta ser propiedad de Google.
Para instalar YAPF desde PyPI:
$ pip install yapf
YAPF todavía se considera en etapa "beta" y la versión lanzada puede cambiar con frecuencia; por lo tanto, la mejor manera de mantenerse actualizado con los últimos desarrollos es clonar este repositorio o instalarlo directamente desde github:
$ pip install git+https://github.com/google/yapf.git
Tenga en cuenta que si desea utilizar YAPF como una herramienta de línea de comandos en lugar de una biblioteca, no es necesaria la instalación. YAPF admite la ejecución como directorio por parte del intérprete de Python. Si clonó/descomprimió YAPF en DIR
, es posible ejecutar:
$ PYTHONPATH=DIR python DIR/yapf [options] ...
YAPF cuenta con el respaldo de múltiples editores a través de extensiones o complementos de la comunidad. Consulte Soporte del editor para obtener más información.
YAPF es compatible con Python 3.7+.
usage: yapf [-h] [-v] [-d | -i | -q] [-r | -l START-END] [-e PATTERN]
[--style STYLE] [--style-help] [--no-local-style] [-p] [-m] [-vv]
[files ...]
Formatter for Python code.
positional arguments:
files reads from stdin when no files are specified.
optional arguments:
-h, --help show this help message and exit
-v, --version show program's version number and exit
-d, --diff print the diff for the fixed source
-i, --in-place make changes to files in place
-q, --quiet output nothing and set return value
-r, --recursive run recursively over directories
-l START-END, --lines START-END
range of lines to reformat, one-based
-e PATTERN, --exclude PATTERN
patterns for files to exclude from formatting
--style STYLE specify formatting style: either a style name (for
example "pep8" or "google"), or the name of a file
with style settings. The default is pep8 unless a
.style.yapf or setup.cfg or pyproject.toml file
located in the same directory as the source or one of
its parent directories (for stdin, the current
directory is used).
--style-help show style settings and exit; this output can be saved
to .style.yapf to make your settings permanent
--no-local-style don't search for local style definition
-p, --parallel run YAPF in parallel when formatting multiple files.
-m, --print-modified print out file names of modified files
-vv, --verbose print out file names while processing
Normalmente, YAPF devuelve cero cuando el programa finaliza con éxito y, en caso contrario, un valor distinto de cero.
Si se proporciona --diff
, YAPF devuelve cero cuando no fueron necesarios cambios, distinto de cero en caso contrario (incluido el error del programa). Puede usar esto en un flujo de trabajo de CI para probar que el código tiene formato YAPF.
Además de excluir los patrones proporcionados en la línea de comandos, YAPF busca patrones adicionales especificados en un archivo llamado .yapfignore
o pyproject.toml
ubicado en el directorio de trabajo desde el que se invoca YAPF.
La sintaxis de .yapfignore
es similar a la coincidencia de patrones de nombres de archivos de UNIX:
* matches everything
? matches any single character
[seq] matches any character in seq
[!seq] matches any character not in seq
Tenga en cuenta que ninguna entrada debe comenzar con ./
.
Si usa pyproject.toml
, los patrones de exclusión se especifican mediante la clave ignore_patterns
en la sección [tool.yapfignore]
. Por ejemplo:
[tool.yapfignore]
ignore_patterns = [
" temp/**/*.py " ,
" temp2/*.py "
]
El estilo de formato utilizado por YAPF es configurable y hay muchas "perillas" que se pueden utilizar para ajustar cómo formatea YAPF. Consulte el módulo style.py
para obtener la lista completa.
Para controlar el estilo, ejecute YAPF con el argumento --style
. Acepta uno de los estilos predefinidos (por ejemplo, pep8
o google
), una ruta a un archivo de configuración que especifica el estilo deseado o un diccionario de pares clave/valor.
El archivo de configuración es una lista simple de pares key = value
(que no distingue entre mayúsculas y minúsculas) con un encabezado [style]
. Por ejemplo:
[style]
based_on_style = pep8
spaces_before_comment = 4
split_before_logical_operator = true
La configuración based_on_style
determina en cuál de los estilos predefinidos se basa este estilo personalizado (considérelo como una subclase). Hay cuatro estilos predefinidos:
pep8
(predeterminado)google
(basado en la Guía de estilo de Google Python)yapf
(para usar con proyectos de código abierto de Google)facebook
Consulte _STYLE_NAME_TO_FACTORY
en style.py
para obtener más detalles.
También es posible hacer lo mismo en la línea de comando con un diccionario. Por ejemplo:
--style= ' {based_on_style: pep8, indent_width: 2} '
Esto tomará el estilo base pep8
y lo modificará para que tenga dos sangrías de espacio.
YAPF buscará el estilo de formato de la siguiente manera:
[style]
de un archivo .style.yapf
en el directorio actual o en uno de sus directorios principales.[yapf]
de un archivo setup.cfg
en el directorio actual o en uno de sus directorios principales.[tool.yapf]
de un archivo pyproject.toml
en el directorio actual o en uno de sus directorios principales.[style]
de un archivo ~/.config/yapf/style
en su directorio de inicio.Si no se encuentra ninguno de esos archivos, se utiliza el estilo predeterminado PEP8.
Un ejemplo del tipo de formato que puede hacer YAPF, tomará este feo código:
x = { 'a' : 37 , 'b' : 42 ,
'c' : 927 }
y = 'hello ' 'world'
z = 'hello ' + 'world'
a = 'hello {}' . format ( 'world' )
class foo ( object ):
def f ( self ):
return 37 * - + 2
def g ( self , x , y = 42 ):
return y
def f ( a ) :
return 37 + - + a [ 42 - x : y ** 3 ]
y reformatearlo en:
x = { 'a' : 37 , 'b' : 42 , 'c' : 927 }
y = 'hello ' 'world'
z = 'hello ' + 'world'
a = 'hello {}' . format ( 'world' )
class foo ( object ):
def f ( self ):
return 37 * - + 2
def g ( self , x , y = 42 ):
return y
def f ( a ):
return 37 + - + a [ 42 - x : y ** 3 ]
Las dos API principales para llamar a YAPF son FormatCode
y FormatFile
, que comparten varios argumentos que se describen a continuación:
> >> from yapf . yapflib . yapf_api import FormatCode # reformat a string of code
> >> formatted_code , changed = FormatCode ( "f ( a = 1, b = 2 )" )
> >> formatted_code
'f(a=1, b=2) n '
> >> changed
True
Un argumento style_config
: un nombre de estilo o una ruta a un archivo que contiene configuraciones de estilo de formato. Si se especifica Ninguno, utilice el estilo predeterminado establecido en style.DEFAULT_STYLE_FACTORY
.
> >> FormatCode ( "def g(): n return True" , style_config = 'pep8' )[ 0 ]
'def g(): n return True n '
Un argumento lines
: una lista de tuplas de líneas (ints), [inicio, fin], que queremos formatear. Las líneas están indexadas en base 1. Puede ser utilizado por código de terceros (por ejemplo, IDE) al reformatear un fragmento de código en lugar de un archivo completo.
> >> FormatCode ( "def g( ): n a=1 n b = 2 n return a==b" , lines = [( 1 , 1 ), ( 2 , 3 )])[ 0 ]
'def g(): n a = 1 n b = 2 n return a==b n '
Un print_diff
(bool): en lugar de devolver la fuente formateada, devuelve una diferencia que convierte la fuente formateada en una fuente reformateada.
>>> print(FormatCode("a==b", filename="foo.py", print_diff=True)[0])
--- foo.py (original)
+++ foo.py (reformatted)
@@ -1 +1 @@
- a==b
+ a == b
Nota: el argumento filename
para FormatCode
es lo que se inserta en la diferencia, el valor predeterminado es <unknown>
.
FormatFile
devuelve código reformateado del archivo pasado junto con su codificación:
> >> from yapf . yapflib . yapf_api import FormatFile # reformat a file
> >> print ( open ( "foo.py" ). read ()) # contents of file
a == b
> >> reformatted_code , encoding , changed = FormatFile ( "foo.py" )
> >> formatted_code
'a == b n '
> >> encoding
'utf-8'
> >> changed
True
El argumento in_place
guarda el código reformateado en el archivo:
> >> FormatFile ( "foo.py" , in_place = True )[: 2 ]
( None , 'utf-8' )
> >> print ( open ( "foo.py" ). read ()) # contents of file (now fixed)
a == b
Opciones:
usage: yapf-diff [-h] [-i] [-p NUM] [--regex PATTERN] [--iregex PATTERN][-v]
[--style STYLE] [--binary BINARY]
This script reads input from a unified diff and reformats all the changed
lines. This is useful to reformat all the lines touched by a specific patch.
Example usage for git/svn users:
git diff -U0 --no-color --relative HEAD^ | yapf-diff -i
svn diff --diff-cmd=diff -x-U0 | yapf-diff -p0 -i
It should be noted that the filename contained in the diff is used
unmodified to determine the source file to update. Users calling this script
directly should be careful to ensure that the path in the diff is correct
relative to the current working directory.
optional arguments:
-h, --help show this help message and exit
-i, --in-place apply edits to files instead of displaying a diff
-p NUM, --prefix NUM strip the smallest prefix containing P slashes
--regex PATTERN custom pattern selecting file paths to reformat
(case sensitive, overrides -iregex)
--iregex PATTERN custom pattern selecting file paths to reformat
(case insensitive, overridden by -regex)
-v, --verbose be more verbose, ineffective without -i
--style STYLE specify formatting style: either a style name (for
example "pep8" or "google"), or the name of a file
with style settings. The default is pep8 unless a
.style.yapf or setup.cfg or pyproject.toml file
located in the same directory as the source or one of
its parent directories (for stdin, the current
directory is used).
--binary BINARY location of binary to use for YAPF
ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT
Alinee el soporte de cierre con la sangría visual.
ALLOW_MULTILINE_LAMBDAS
Permitir que las lambdas se formatee en más de una línea.
ALLOW_MULTILINE_DICTIONARY_KEYS
Permitir que existan claves de diccionario en varias líneas. Por ejemplo:
x = {
( 'this is the first element of a tuple' ,
'this is the second element of a tuple' ):
value ,
}
ALLOW_SPLIT_BEFORE_DEFAULT_OR_NAMED_ASSIGNS
Permitir la división antes de una asignación predeterminada/con nombre en una lista de argumentos.
ALLOW_SPLIT_BEFORE_DICT_VALUE
Permitir divisiones antes del valor del diccionario.
ARITHMETIC_PRECEDENCE_INDICATION
Deje que el espaciado indique la precedencia del operador. Por ejemplo:
a = 1 * 2 + 3 / 4
b = 1 / 2 - 3 * 4
c = ( 1 + 2 ) * ( 3 - 4 )
d = ( 1 - 2 ) / ( 3 + 4 )
e = 1 * 2 - 3
f = 1 + 2 + 3 + 4
tendrá el siguiente formato para indicar la precedencia:
a = 1 * 2 + 3 / 4
b = 1 / 2 - 3 * 4
c = ( 1 + 2 ) * ( 3 - 4 )
d = ( 1 - 2 ) / ( 3 + 4 )
e = 1 * 2 - 3
f = 1 + 2 + 3 + 4
BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION
Establece el número de líneas en blanco deseadas que rodean las definiciones de funciones y clases de nivel superior. Por ejemplo:
class Foo :
pass
# <------ having two blank lines here
# <------ is the default setting
class Bar :
pass
BLANK_LINE_BEFORE_CLASS_DOCSTRING
Inserte una línea en blanco antes de una cadena de documentación a nivel de clase.
BLANK_LINE_BEFORE_MODULE_DOCSTRING
Inserte una línea en blanco antes de la cadena de documentación del módulo.
BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF
Inserte una línea en blanco antes de una
def
oclass
inmediatamente anidada dentro de otradef
oclass
. Por ejemplo:
class Foo :
# <------ this blank line
def method ():
pass
BLANK_LINES_BETWEEN_TOP_LEVEL_IMPORTS_AND_VARIABLES
Establece el número de líneas en blanco deseadas entre las importaciones de nivel superior y las definiciones de variables. Útil por compatibilidad con herramientas como isort.
COALESCE_BRACKETS
No divida corchetes consecutivos. Solo es relevante cuando se establece
DEDENT_CLOSING_BRACKETS
oINDENT_CLOSING_BRACKETS
. Por ejemplo:
call_func_that_takes_a_dict (
{
'key1' : 'value1' ,
'key2' : 'value2' ,
}
)
reformatearía a:
call_func_that_takes_a_dict ({
'key1' : 'value1' ,
'key2' : 'value2' ,
})
COLUMN_LIMIT
El límite de columna (o longitud máxima de línea)
CONTINUATION_ALIGN_STYLE
El estilo para la alineación de continuación. Los valores posibles son:
SPACE
: Utilice espacios para la alineación de continuación. Este es el comportamiento predeterminado.FIXED
: use un número fijo (CONTINUATION_INDENT_WIDTH
) de columnas (es decir, pestañasCONTINUATION_INDENT_WIDTH
/INDENT_WIDTH
o espaciosCONTINUATION_INDENT_WIDTH
) para la alineación de continuación.VALIGN-RIGHT
: alinea verticalmente las líneas de continuación con varias columnasINDENT_WIDTH
. Ligeramente a la derecha (una tabulación o algunos espacios) si no se pueden alinear verticalmente las líneas de continuación con los caracteres de sangría.
CONTINUATION_INDENT_WIDTH
Ancho de sangría utilizado para las continuaciones de línea.
DEDENT_CLOSING_BRACKETS
Coloque los corchetes de cierre en una línea separada, con sangría, si la expresión entre corchetes no cabe en una sola línea. Se aplica a todo tipo de corchetes, incluidas definiciones de funciones y llamadas. Por ejemplo:
config = {
'key1' : 'value1' ,
'key2' : 'value2' ,
} # <--- this bracket is dedented and on a separate line
time_series = self . remote_client . query_entity_counters (
entity = 'dev3246.region1' ,
key = 'dns.query_latency_tcp' ,
transform = Transformation . AVERAGE ( window = timedelta ( seconds = 60 )),
start_ts = now () - timedelta ( days = 3 ),
end_ts = now (),
) # <--- this bracket is dedented and on a separate line
DISABLE_ENDING_COMMA_HEURISTIC
Deshabilite la heurística que coloca cada elemento de la lista en una línea separada si la lista termina en coma.
Nota: El comportamiento de esta bandera cambió en v0.40.3. Antes, si esta bandera era verdadera, dividíamos las listas que contenían una coma final o un comentario. Ahora tenemos una bandera separada,
DISABLE_SPLIT_LIST_WITH_COMMENT
, que controla la división cuando una lista contiene un comentario. Para obtener el comportamiento anterior, establezca ambos indicadores en verdadero. Más información en CHANGELOG.md.
DISABLE_SPLIT_LIST_WITH_COMMENT
No coloque cada elemento en una nueva línea dentro de una lista que contenga comentarios intersticiales.
Sin esta bandera (predeterminada):
[ a, b, # c ]
Con esta bandera:
[ a, b, # c ]
Esto refleja el comportamiento del formato clang y es útil para formar "grupos lógicos" de elementos en una lista. También funciona en declaraciones de funciones.
EACH_DICT_ENTRY_ON_SEPARATE_LINE
Coloque cada entrada del diccionario en su propia línea.
FORCE_MULTILINE_DICT
Respete
EACH_DICT_ENTRY_ON_SEPARATE_LINE
incluso si la línea es más corta queCOLUMN_LIMIT
.
I18N_COMMENT
La expresión regular para un comentario de internacionalización. La presencia de este comentario detiene el formateo de esa línea, porque los comentarios deben estar al lado de la cadena que traducen.
I18N_FUNCTION_CALL
La función de internacionalización llama nombres. La presencia de esta función deja de formatear esa línea, porque la cadena que tiene no se puede alejar del comentario i18n.
INDENT_BLANK_LINES
Establezca en
True
para preferir líneas en blanco con sangría en lugar de vacías.
INDENT_CLOSING_BRACKETS
Coloque los corchetes de cierre en una línea separada, con sangría, si la expresión entre corchetes no cabe en una sola línea. Se aplica a todo tipo de corchetes, incluidas definiciones de funciones y llamadas. Por ejemplo:
config = {
'key1' : 'value1' ,
'key2' : 'value2' ,
} # <--- this bracket is indented and on a separate line
time_series = self . remote_client . query_entity_counters (
entity = 'dev3246.region1' ,
key = 'dns.query_latency_tcp' ,
transform = Transformation . AVERAGE ( window = timedelta ( seconds = 60 )),
start_ts = now () - timedelta ( days = 3 ),
end_ts = now (),
) # <--- this bracket is indented and on a separate line
INDENT_DICTIONARY_VALUE
Sangra el valor del diccionario si no puede caber en la misma línea que la clave del diccionario. Por ejemplo:
config = {
'key1' :
'value1' ,
'key2' : value1 +
value2 ,
}
INDENT_WIDTH
El número de columnas que se utilizarán para la sangría.
JOIN_MULTIPLE_LINES
Une líneas cortas en una sola línea. Por ejemplo, declaraciones
if
de una sola línea.
NO_SPACES_AROUND_SELECTED_BINARY_OPERATORS
No incluya espacios alrededor de los operadores binarios seleccionados. Por ejemplo:
1 + 2 * 3 - 4 / 5
se formateará de la siguiente manera cuando se configure con
*
,/
:
1 + 2 * 3 - 4 / 5
SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET
Inserte un espacio entre la coma final y el corchete de cierre de una lista, etc.
SPACE_INSIDE_BRACKETS
Use spaces inside brackets, braces, and parentheses. For example:
method_call ( 1 )
my_dict [ 3 ][ 1 ][ get_index ( * args , ** kwargs ) ]
my_set = { 1 , 2 , 3 }
SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN
Configúrelo en
True
para preferir espacios alrededor del operador de asignación para argumentos predeterminados o de palabras clave.
SPACES_AROUND_DICT_DELIMITERS
Agrega un espacio después de los delimitadores de dictado iniciales '{' y antes del final '}'.
{ 1 : 2 }
tendrá el formato:
{ 1 : 2 }
SPACES_AROUND_LIST_DELIMITERS
Agrega un espacio después de los delimitadores de la lista inicial '[' y antes del final ']'.
[ 1 , 2 ]
tendrá el formato:
[ 1 , 2 ]
SPACES_AROUND_POWER_OPERATOR
Establezca en
True
para preferir usar espacios alrededor de**
.
SPACES_AROUND_SUBSCRIPT_COLON
Utilice espacios alrededor del operador de subíndice/sección. Por ejemplo:
my_list [ 1 : 10 : 2 ]
SPACES_AROUND_TUPLE_DELIMITERS
Agrega un espacio después de los delimitadores de tupla de apertura '(' y antes del final ')'.
( 1 , 2 , 3 )
tendrá el formato:
( 1 , 2 , 3 )
SPACES_BEFORE_COMMENT
El número de espacios necesarios antes de un comentario final. Puede ser un valor único (que representa el número de espacios antes de cada comentario final) o una lista de valores (que representa los valores de las columnas de alineación; los comentarios finales dentro de un bloque se alinearán con el valor de la primera columna que sea mayor que la longitud máxima de línea dentro del bloquear).
Nota: Es posible que sea necesario citar las listas de valores en algunos contextos (por ejemplo, shells o archivos de configuración del editor).
Por ejemplo, con
spaces_before_comment=5
:
1 + 1 # Adding values
tendrá el formato:
1 + 1 # Adding values <-- 5 spaces between the end of the statement and comment
con
spaces_before_comment="15, 20"
:
1 + 1 # Adding values
two + two # More adding
longer_statement # This is a longer statement
short # This is a shorter statement
a_very_long_statement_that_extends_beyond_the_final_column # Comment
short # This is a shorter statement
tendrá el formato:
1 + 1 # Adding values <-- end of line comments in block aligned to col 15
two + two # More adding
longer_statement # This is a longer statement <-- end of line comments in block aligned to col 20
short # This is a shorter statement
a_very_long_statement_that_extends_beyond_the_final_column # Comment <-- the end of line comments are aligned based on the line length
short # This is a shorter statement
SPLIT_ALL_COMMA_SEPARATED_VALUES
Si una lista separada por comas (
dict
,list
,tuple
o functiondef
) está en una línea demasiado larga, divídala de manera que cada elemento esté en una línea separada.
SPLIT_ALL_TOP_LEVEL_COMMA_SEPARATED_VALUES
Variación de
SPLIT_ALL_COMMA_SEPARATED_VALUES
en la que, si una subexpresión con una coma cabe en su línea inicial, entonces la subexpresión no se divide. Esto evita divisiones como la deb
en este código:
abcdef (
aReallyLongThing : int ,
b : [ Int ,
Int ])
con la nueva perilla esto se divide como:
abcdef (
aReallyLongThing : int ,
b : [ Int , Int ])
SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED
Dividir antes de los argumentos si la lista de argumentos termina con una coma.
SPLIT_BEFORE_ARITHMETIC_OPERATOR
Configúrelo en
True
para preferir la división antes de+
,-
,*
,/
,//
o@
en lugar de después.
SPLIT_BEFORE_BITWISE_OPERATOR
Establezca en
True
para preferir la división antes de&
,|
o^
en lugar de después.
SPLIT_BEFORE_CLOSING_BRACKET
Dividir antes del corchete de cierre si un literal
list
odict
no cabe en una sola línea.
SPLIT_BEFORE_DICT_SET_GENERATOR
Dividir antes de un diccionario o generador de conjuntos (
comp_for
). Por ejemplo, observe la división antes defor
:
foo = {
variable : 'Hello world, have a nice day!'
for variable in bar if variable != 42
}
SPLIT_BEFORE_DOT
Dividido antes del
.
si necesitamos dividir una expresión más larga:
foo = ( 'This is a really long string: {}, {}, {}, {}' . format ( a , b , c , d ))
reformatearía a algo como:
foo = ( 'This is a really long string: {}, {}, {}, {}'
. format ( a , b , c , d ))
SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN
Se divide después del par de apertura que rodea una expresión si no cabe en una sola línea.
SPLIT_BEFORE_FIRST_ARGUMENT
Si se va a dividir una lista de argumentos/parámetros, divídala antes del primer argumento.
SPLIT_BEFORE_LOGICAL_OPERATOR
Configúrelo en
True
para preferir la división antesand
/or
en lugar de después.
SPLIT_BEFORE_NAMED_ASSIGNS
Divida las asignaciones con nombre en líneas individuales.
SPLIT_COMPLEX_COMPREHENSION
Para listas por comprensión y expresiones generadoras con múltiples cláusulas (por ejemplo, múltiples llamadas
for
,if
expresiones de filtro) y que deben redistribuirse, divida cada cláusula en su propia línea. Por ejemplo:
result = [
a_var + b_var for a_var in xrange ( 1000 ) for b_var in xrange ( 1000 )
if a_var % b_var ]
reformatearía a algo como:
result = [
a_var + b_var
for a_var in xrange ( 1000 )
for b_var in xrange ( 1000 )
if a_var % b_var ]
SPLIT_PENALTY_AFTER_OPENING_BRACKET
La penalización por dividirse justo después del grupo inicial.
SPLIT_PENALTY_AFTER_UNARY_OPERATOR
La penalización por dividir la línea después de un operador unario.
SPLIT_PENALTY_ARITHMETIC_OPERATOR
La penalización de dividir la línea alrededor de los operadores
+
,-
,*
,/
,//
,%
y@
.
SPLIT_PENALTY_BEFORE_IF_EXPR
La penalización por dividir justo antes de una expresión
if
.
SPLIT_PENALTY_BITWISE_OPERATOR
La pena de dividir la línea alrededor de
&
,|
Operadores , y^
.
SPLIT_PENALTY_COMPREHENSION
La penalización por dividir una lista por comprensión o expresión generadora.
SPLIT_PENALTY_EXCESS_CHARACTER
La penalización por caracteres que superen el límite de columnas.
SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT
La penalización incurrida al agregar una división de línea a la línea lógica. Cuantas más divisiones de línea se agreguen, mayor será la penalización.
SPLIT_PENALTY_IMPORT_NAMES
La penalización de dividir una lista de
import as
nombres. Por ejemplo:
from a_very_long_or_indented_module_name_yada_yad import ( long_argument_1 ,
long_argument_2 ,
long_argument_3 )
reformatearía a algo como:
from a_very_long_or_indented_module_name_yada_yad import (
long_argument_1 , long_argument_2 , long_argument_3 )
SPLIT_PENALTY_LOGICAL_OPERATOR
La penalización de dividir la línea alrededor de los operadores
and
yor
.
USE_TABS
Utilice el carácter Tab para la sangría.
YAPF se esfuerza mucho en conseguir el formato correcto. Pero para algunos códigos, no será tan bueno como formatear manualmente. En particular, los grandes literales de datos pueden quedar horriblemente desfigurados bajo YAPF.
Las razones para esto son muchas. En resumen, YAPF es simplemente una herramienta para ayudar al desarrollo. Formateará las cosas para que coincidan con la guía de estilo, pero eso puede no equivaler a legibilidad.
Lo que se puede hacer para aliviar esta situación es indicar las regiones que YAPF debe ignorar al reformatear algo:
# yapf: disable
FOO = {
# ... some very large, complex data literal.
}
BAR = [
# ... another large data literal.
]
# yapf: enable
También puedes deshabilitar el formato para un solo literal como este:
BAZ = {
( 1 , 2 , 3 , 4 ),
( 5 , 6 , 7 , 8 ),
( 9 , 10 , 11 , 12 ),
} # yapf: disable
Para conservar los bonitos corchetes de cierre dentados, utilice dedent_closing_brackets
en su estilo. Tenga en cuenta que en este caso todos los corchetes, incluidas las definiciones y llamadas de funciones, utilizarán ese estilo. Esto proporciona coherencia en todo el código base formateado.
Queríamos utilizar el algoritmo de reformateo de clang-format. Es muy potente y está diseñado para ofrecer el mejor formato posible. Las herramientas existentes se crearon con diferentes objetivos en mente y requerirían modificaciones extensas para convertirlas al algoritmo de formato clang.
¡Por favor hazlo! YAPF fue diseñado para usarse como biblioteca y como herramienta de línea de comandos. Esto significa que una herramienta o complemento IDE es de uso gratuito para YAPF.
YAPF se esfuerza mucho por cumplir plenamente con PEP 8. Sin embargo, es fundamental no correr el riesgo de alterar la semántica de su código. Por lo tanto, YAPF intenta ser lo más seguro posible y no cambia el flujo de tokens (por ejemplo, agregando paréntesis). Sin embargo, todos estos casos se pueden solucionar fácilmente de forma manual. Por ejemplo,
from my_package import my_function_1 , my_function_2 , my_function_3 , my_function_4 , my_function_5
FOO = my_variable_1 + my_variable_2 + my_variable_3 + my_variable_4 + my_variable_5 + my_variable_6 + my_variable_7 + my_variable_8
no se dividirá, pero puedes hacerlo fácilmente simplemente añadiendo paréntesis:
from my_package import ( my_function_1 , my_function_2 , my_function_3 ,
my_function_4 , my_function_5 )
FOO = ( my_variable_1 + my_variable_2 + my_variable_3 + my_variable_4 +
my_variable_5 + my_variable_6 + my_variable_7 + my_variable_8 )
La estructura de datos principal en YAPF es el objeto LogicalLine
. Contiene una lista de FormatToken
s, que querríamos colocar en una sola línea si no hubiera límite de columnas. Una excepción es un comentario en medio de una declaración de expresión que obligará a que la línea se formatee en más de una línea. El formateador funciona en un objeto LogicalLine
a la vez.
Por lo general, una LogicalLine
no afectará el formato de las líneas anteriores o posteriores. Hay una parte del algoritmo que puede unir dos o más LogicalLine
s en una línea. Por ejemplo, una declaración si-entonces con un cuerpo corto se puede colocar en una sola línea:
if a == 42 : continue
El algoritmo de formato de YAPF crea un árbol ponderado que actúa como espacio de solución para el algoritmo. Cada nodo en el árbol representa el resultado de una decisión de formato, es decir, si dividir o no antes de un token. Cada decisión de formato tiene un costo asociado. Por lo tanto, el coste se realiza en el borde entre dos nodos. (En realidad, el árbol ponderado no tiene objetos de borde separados, por lo que el costo reside en los propios nodos).
Por ejemplo, tome el siguiente fragmento de código Python. Por el bien de este ejemplo, supongamos que la línea (1) viola la restricción de límite de columnas y necesita ser reformateada.
def xxxxxxxxxxx ( aaaaaaaaaaaa , bbbbbbbbb , cccccccc , dddddddd , eeeeee ): # 1
pass # 2
Para la línea (1), el algoritmo construirá un árbol donde cada nodo (un objeto FormattingDecisionState
) es el estado de la línea en ese token dada la decisión de dividirse antes del token o no. Nota: los objetos FormatDecisionState
se copian por valor, por lo que cada nodo del gráfico es único y un cambio en uno no afecta a los demás nodos.
La heurística se utiliza para determinar los costos de dividir o no dividir. Debido a que un nodo mantiene el estado del árbol hasta la inserción de un token, puede determinar fácilmente si una decisión de división violará uno de los requisitos de estilo. Por ejemplo, la heurística puede aplicar una penalización adicional al borde cuando no se divide entre el token anterior y el que se está agregando.
Hay algunos casos en los que nunca querremos dividir la línea, porque hacerlo siempre será perjudicial (es decir, requerirá una barra invertida-nueva línea, lo cual rara vez es deseable). Para la línea (1), nunca querremos dividir los primeros tres tokens: def
, xxxxxxxxxxx
y (
. Tampoco querremos dividir entre )
y :
al final. Se dice que estas regiones son "irrompibles". Esto se refleja en el árbol al no haber una decisión "dividida" (rama izquierda) dentro de la región irrompible.
Ahora que tenemos el árbol, determinamos cuál es el "mejor" formato encontrando la ruta a través del árbol con el costo más bajo.
¡Y eso es todo!