PyCharm:在区域上运行`black -S` [英] PyCharm: Run `black -S` on region

查看:68
本文介绍了PyCharm:在区域上运行`black -S`的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们还没有准备好使用

第 2 步.

您需要实现一个脚本来调用 Black 并与 IDE 交互.下面是一个工作示例.需要注意的是:

  1. 四行是特定于 OS/shell 的注释(根据您的环境调整它们应该是微不足道的).
  2. 一些细节可以进一步调整,例如,实现做出了简单的选择.

import os导入路径库导入临时文件导入子流程导入系统def region_to_str(file_path: pathlib.Path, start_line: int, end_line: int) ->字符串:文件 = 打开(文件路径)str_build = list()对于行号,枚举中的行(文件,开始= 1):如果 line_number >end_line:休息elif line_number <起始行:继续别的:str_build.append(line)返回".join(str_build)def black_to_clipboard(py_interpeter, black_cli_options, code_region_str):py_interpreter_path = pathlib.Path(py_interpeter)/python.exe"# 特定于操作系统,Windows 的 .exe.proc = subprocess.Popen([py_interpreter_path, -m", black", *black_cli_options,-c", code_region_str], stdout=subprocess.PIPE)尝试:outs, errs = proc.communicate(timeout=15)除了 TimeoutExpired:proc.kill()outs, errs = proc.communicate()# 默认情况下 Black 输出二进制,解码为默认 Python 模块 utf-8 编码.result = outs.decode('utf-8').replace('\r','') # 特定于操作系统,从 \n\r Windows 新行中删除 \r.tmp_dir_name = tempfile.gettempdir()tmp_file = tempfile.gettempdir() + "\\__run_black_tmp.txt";# 操作系统特定的转义路径分隔符.使用 open(tmp_file, mode='w+', encoding='utf-8', errors='strict') 作为 out_file:out_file.write(result + '\n')命令 = '剪辑 <' + str(tmp_file) # 特定于操作系统,将结果发送到剪贴板进行复制粘贴.操作系统(命令)def main(argv: list[str] = sys.argv[1:]) ->内部:"在代码区域上运行黑色的外部工具脚本.参数:argv[0] (str):包含代码区域的模块的路径.argv[1] (str):代码区域起始行.argv[2] (str):代码区域结束行.argv[3] (str): venv/Scripts 目录的路径.argv[4:] (str):黑色 CLI 选项."# 打印(argv)lines_as_str = region_to_str(argv[0], int(argv[1]), int(argv[2]))black_to_clipboard(argv[3], argv[4:],lines_as_str)如果 __name__ == __main__":主要(sys.argv[1:])

第 3 步.

困难的部分已经完成.让我们使用新功能.

通常在编辑器中选择您想要的行作为代码区域.必须强调这一点,因为之前的 SelectionStartLineSelectionEndLine 宏需要选择才能工作.(见下一个截图).

第 4 步.

运行之前实现的外部工具.这可以通过在编辑器中右键单击并选择 External Tools > the_name_of_your_external_tool 来完成.

第 5 步.

简单粘贴(截图显示了运行外部工具并按Ctrl + v后的结果).步骤 2 中的实现将 Black 的输出复制到您操作系统的剪贴板,这似乎是更可取的解决方案,因为您可以通过这种方式更改编辑器内的文件,从而 Undo Ctrl +z 也会起作用.通过在编辑器外以编程方式覆盖文件来更改文件会不太流畅,并且可能需要在编辑器内刷新它.

第 6 步.

您可以

结束注释.

  1. 如果您需要在步骤 2 中调试功能,请参阅 运行配置 也可以使用与外部工具配置相同的宏进行配置.

  2. 在使用剪贴板时要注意字符编码可以跨层更改,这一点很重要.我决定使用 clip 并直接从临时文件中读入,这是为了避免在命令行中将代码字符串传递给 Black,因为 CMD Windows 编码默认不是 UTF-8.(对于 Linux 用户,这应该更简单,但可能取决于您的系统设置.)

  3. 一个重要的注意事项是,您可以选择一个没有缩进级别的更广泛上下文的代码区域.这意味着,例如,如果您只在一个类中选择 2 个方法,它们将被传递给 Black 并使用模块级函数的缩进级别进行格式化.如果您小心地选择具有适当范围的代码区域,这应该不是问题.这也可以通过从 Step 1 传递额外的宏 SelectionStartColumn - Select text start column number 并在 Step 中的每一行前面加上该数量的空格来轻松解决2 脚本.(理想情况下,此类功能将由 Black 作为 CLI 选项实现.)无论如何,如果需要,使用 Tab 将区域置于适当的缩进级别非常容易.

  4. 问题的主要主题是如何将 Black 与 PyCharm IDE 集成到代码区域,因此演示第二个选项应该足以解决问题,因为第一个选项在大多数情况下仅添加实现特定的复杂性.(答案已经足够长了.实现第一个选项的细节将为 Black 项目提供一个很好的功能/拉取请求.)

We are not ready to automatically format the whole source code with black.

But from time to time I would like to execute black -S on a region via PyCharm.

There is a hint in the docs how to run black (or black -S (what I like)) on the whole file. But ...

How to run black only on a selected region?

解决方案

Using Python Black on a code region in the PyCharm IDE can be done by implementing it as an external tool. Currently Black has two main options to choose the code to format

  1. Run Black on the whole module specifying it on the CLI as the [SRC]...
  2. Passing the code region as a string on the CLI using the -c, --code TEXT option.

The following implementation shows how to do this using the 2nd option. The reason is that applying Black to the whole module is likely to change the number of lines thus making the job of selecting the code region by choosing start and end line numbers more complicated.

Implementing the 1st option can be done, but it would require mapping the initial code region to the final code region after Black formats the entire module.

Lets take as example the following code that has a number of obvious PEP-8 violations (missing white-spaces and empty lines):

"""
long multi-line
comment
"""
def foo(token:int=None)->None:
  a=token+1

class bar:
  foo:int=None
  
def the_simple_test():
    """the_simple_test"""
    pass

Step 1.

Using Black as an external tool in the IDE can be configured by going to File > Tools > External Tools and clicking the Add or Edit icons.

What is of interesst is passing the right Macros - (see point 3 "Parameter with macros") from the PyCharm IDE to the custom script that calls Black and does the necessary processing. Namely you'll need the Macros

  • FilePath - File Path
  • SelectionStartLine - Selected text start line number
  • SelectionEndLine - Select text end line number
  • PyInterpreterDirectory - The directory containing the Python interpreter selected for the project

But from time to time I would like to execute black -S on a region via PyCharm.

Any additional Black CLI options you want to pass as arguments are best placed at the end of the parameter list.

Since you may have Black installed on a specific venv, the example also uses the PyInterpreterDirectory macro.

The screenshot illustrates the above:

Step 2.

You'll need to implement a script to call Black and interface with the IDE. The following is a working example. It should be noted:

  1. Four lines are OS/shell specific as commented (it should be trivial to adapt them to your environment).
  2. Some details could be further tweaked, for purpose of example the implementation makes simplistic choices.

import os
import pathlib
import tempfile
import subprocess
import sys

def region_to_str(file_path: pathlib.Path, start_line: int, end_line: int) -> str:

    file = open(file_path)
    str_build = list()

    for line_number, line in enumerate(file, start=1):
        if line_number > end_line:
            break
        elif line_number < start_line:
            continue
        else:
            str_build.append(line)

    return "".join(str_build)

def black_to_clipboard(py_interpeter, black_cli_options, code_region_str):

    py_interpreter_path = pathlib.Path(py_interpeter) / "python.exe"  # OS specific, .exe for Windows.

    proc = subprocess.Popen([py_interpreter_path, "-m", "black", *black_cli_options,
                             "-c", code_region_str], stdout=subprocess.PIPE)

    try:
        outs, errs = proc.communicate(timeout=15)
    except TimeoutExpired:
        proc.kill()
        outs, errs = proc.communicate()

    # By default Black outputs binary, decodes to default Python module utf-8 encoding.
    result = outs.decode('utf-8').replace('\r','')  # OS specific, remove \r from \n\r Windows new-line.

    tmp_dir_name = tempfile.gettempdir()
    tmp_file = tempfile.gettempdir() + "\\__run_black_tmp.txt"  # OS specific, escaped path separator.

    with open(tmp_file, mode='w+', encoding='utf-8', errors='strict') as out_file:
        out_file.write(result + '\n')

    command = 'clip < ' + str(tmp_file)  # OS specific, send result to clipboard for copy-paste.
    os.system(command)

def main(argv: list[str] = sys.argv[1:]) -> int:
    """External tool script to run black on a code region.

    Args:
        argv[0] (str): Path to module containing code region.
        argv[1] (str): Code region start line.
        argv[2] (str): Code region end line.
        argv[3] (str): Path to venv /Scripts directory.
        argv[4:] (str): Black CLI options.
    """
    # print(argv)
    lines_as_str = region_to_str(argv[0], int(argv[1]), int(argv[2]))
    black_to_clipboard(argv[3], argv[4:], lines_as_str)

if __name__ == "__main__":
    main(sys.argv[1:])

Step 3.

The hard part is done. Lets use the new functionality.

Normally select the lines you want as your code region in the editor. This has to be emphasized because the previous SelectionStartLine and SelectionEndLine macros need the selection to work. (See the next screenshot).

Step 4.

Run the external tool previously implemented. This can be done by right clicking in the editor and choosing External Tools > the_name_of_your_external_tool.

Step 5.

Simply paste (the screenshot shows the result after running the external tool and pressing Ctrl + v). The implementation in Step 2 copies Black's output to your OS's clipboard, this seemed like the preferable solution since this way you change the file inside the editor thus Undo Ctrl + z will also work. Changing the file by overwrite it programmatically outside the editor would be less seamless and might require refreshing it inside the editor.

Step 6.

You can record a macro of the previous steps and associate it with a keyboard shortcut to have the above functionality in one keystroke (similar to copy-paste Ctrl + c + Ctrl + v).

End Notes.

  1. If you need to debug the functionality in Step 2 a Run Configuration can also be configured using the same macros the external tool configuration did.

  2. It's important to notice when using the clipboard that character encodings can change across the layers. I decided to use clip and read into it directly from a temporary file, this was to avoid passing the code string to Black on the command line because the CMD Windows encoding is not UTF-8 by default. (For Linux users this should be simpler but can depend on your system settings.)

  3. One important note is that you can choose a code region without the broader context of its indentation level. Meaning, for example, if you only choose 2 methods inside a class they will be passed to Black and formatted with the indentation level of module level functions. This shouldn't be a problem if you are careful to select code regions with their proper scope. This could also easily be solved by passing the additional macro SelectionStartColumn - Select text start column number from Step 1 and prepending that number of whitespaces to each line in the Step 2 script. (Ideally such functionality would be implemented by Black as a CLI option.) In any case, if needed, using Tab to put the region in its proper indentation level is easy enough.

  4. The main topic of the question is how to integrating Black with the PyCharm IDE for a code region, so demonstrating the 2nd option should be enough to address the problem because the 1st option would, for the most part, only add implementation specific complexity. (The answer is long enough as it is. The specifics of implementing the 1st option would make a good Feature/Pull Request for the Black project.)

这篇关于PyCharm:在区域上运行`black -S`的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆