标签归档:emacs

使用IPython进行分步调试

问题:使用IPython进行分步调试

根据我的阅读,有两种方法可以在Python中调试代码:

  • 使用传统的调试器,例如pdbipdb。它支持诸如cfor continuenfor step-oversfor step-into等命令,但是您没有直接访问IPython shell的权限,这对于对象检查非常有用。

  • 通过 IPython shell 嵌入代码中来使用 IPython。您可以这样做,然后在您的代码中使用。当您的程序/脚本命中一条语句时,您将进入IPython shell。这允许使用所有IPython好东西对对象进行全面检查并测试Python代码。但是,在使用时,您将无法通过便捷的键盘快捷键逐步完成代码。from ipython import embedembed()embed()embed()

有什么办法可以融合两全其美?即

  1. 能够 使用方便的pdb / ipdb键盘快捷键逐步完成代码。
  2. 在任何这样的步骤(例如,在给定的语句上),都可以访问成熟的IPython shell

如在 MATLAB中一样进行 IPython调试:

在MATLAB中可以找到这种“增强调试”类型的示例,在该示例中,用户始终可以完全访问MATLAB引擎/外壳,并且她仍然可以逐步完成代码,定义条件断点等。我已经与其他用户讨论过,这是人们从MATLAB转移到IPython时最想念的调试功能。

在Emacs和其他编辑器中进行IPython调试:

我不想让这个问题过于具体,但是我主要在Emacs中工作,所以我想知道是否有任何方法可以将此功能引入其中。理想情况下,Emacs(或编辑器)将允许程序员在代码上的任意位置设置断点,并与解释器或调试器进行通信,以使其在您选择的位置停止,并在该位置提供完整的IPython解释器。

From what I have read, there are two ways to debug code in Python:

  • With a traditional debugger such as pdb or ipdb. This supports commands such as c for continue, n for step-over, s for step-into etc.), but you don’t have direct access to an IPython shell which can be extremely useful for object inspection.

  • Using IPython by embedding an IPython shell in your code. You can do from IPython import embed, and then use embed() in your code. When your program/script hits an embed() statement, you are dropped into an IPython shell. This allows the full inspection of objects and testing of Python code using all the IPython goodies. However, when using embed() you can’t step-by-step through the code anymore with handy keyboard shortcuts.

Is there any way to combine the best of both worlds? I.e.

  1. Be able to step-by-step through your code with handy pdb/ipdb keyboard shortcuts.
  2. At any such step (e.g. on a given statement), have access to a full-fledged IPython shell.

IPython debugging as in MATLAB:

An example of this type of “enhanced debugging” can be found in MATLAB, where the user always has full access to the MATLAB engine/shell, and she can still step-by-step through her code, define conditional breakpoints, etc. From what I have discussed with other users, this is the debugging feature that people miss the most when moving from MATLAB to IPython.

IPython debugging in Emacs and other editors:

I don’t want to make the question too specific, but I work mostly in Emacs, so I wonder if there is any way to bring this functionality into it. Ideally, Emacs (or the editor) would allow the programmer to set breakpoints anywhere on the code and communicate with the interpreter or debugger to have it stop in the location of your choice, and bring to a full IPython interpreter on that location.


回答 0

您可以使用IPython的%pdb魔力。只需调用%pdbIPython,当发生错误时,您会自动转到ipdb。虽然您没有立即迈出一步,但您ipdb之后就进入了。

这使调试单个函数变得容易,因为您可以使用加载文件%load然后运行一个函数。您可以assert在正确的位置使用来强制执行错误。

%pdb是线魔术。呼之为%pdb on%pdb 1%pdb off%pdb 0。如果在不带参数的情况下调用它,则它将作为切换。

You can use IPython’s %pdb magic. Just call %pdb in IPython and when an error occurs, you’re automatically dropped to ipdb. While you don’t have the stepping immediately, you’re in ipdb afterwards.

This makes debugging individual functions easy, as you can just load a file with %load and then run a function. You could force an error with an assert at the right position.

%pdb is a line magic. Call it as %pdb on, %pdb 1, %pdb off or %pdb 0. If called without argument it works as a toggle.


回答 1

ipdb.set_trace()呢?在您的代码中:

import ipdb; ipdb.set_trace()

更新:现在在Python 3.7中,我们可以编写breakpoint()。它的工作原理相同,但也遵守PYTHONBREAKPOINT环境变量。此功能来自此PEP

这样可以对代码进行全面检查,并且您可以访问诸如c(继续),n(执行下一行),s(进入当前方法)之类的命令。

请参阅ipdb repo命令列表IPython现在称为Jupyter(的一部分)。


ps:请注意,ipdb命令优先于python代码。所以为了写,list(foo)你需要print list(foo)

另外,如果您喜欢ipython提示符(它的emacs和vim模式,历史记录,完成情况等),由于它基于python提示符工具包,因此很容易为您的项目获得相同的名称。

What about ipdb.set_trace() ? In your code :

import ipdb; ipdb.set_trace()

update: now in Python 3.7, we can write breakpoint(). It works the same, but it also obeys to the PYTHONBREAKPOINT environment variable. This feature comes from this PEP.

This allows for full inspection of your code, and you have access to commands such as c (continue), n (execute next line), s (step into the method at point) and so on.

See the ipdb repo and a list of commands. IPython is now called (edit: part of) Jupyter.


ps: note that an ipdb command takes precedence over python code. So in order to write list(foo) you’d need print(list(foo)), or !list(foo) .

Also, if you like the ipython prompt (its emacs and vim modes, history, completions,…) it’s easy to get the same for your project since it’s based on the python prompt toolkit.


回答 2

(2016年5月28日更新)在Emacs中使用RealGUD

对于Emacs中的任何人,此线程都说明了如何使用

  1. Emacs中一个称为RealGUD的新的重要调试器,可以与任何调试器(包括ipdb)一起使用。
  2. Emacs软件包isend-mode

这两个软件包的组合非常强大,可以使它们完全重新创建OP中描述的行为,甚至可以做更多的事情。

有关RealGUD for ipdb 的Wiki文章的更多信息。


原始答案:

在尝试了多种不同的调试Python方法(包括本线程中提到的所有内容)之后,我使用IPython调试Python的首选方法之一是使用嵌入式外壳程序。

定义定制的嵌入式IPython Shell:

将以下内容添加到脚本中PYTHONPATH,以使该方法ipsh()可用。

import inspect

# First import the embed function
from IPython.terminal.embed import InteractiveShellEmbed
from IPython.config.loader import Config

# Configure the prompt so that I know I am in a nested (embedded) shell
cfg = Config()
prompt_config = cfg.PromptManager
prompt_config.in_template = 'N.In <\\#>: '
prompt_config.in2_template = '   .\\D.: '
prompt_config.out_template = 'N.Out<\\#>: '

# Messages displayed when I drop into and exit the shell.
banner_msg = ("\n**Nested Interpreter:\n"
"Hit Ctrl-D to exit interpreter and continue program.\n"
"Note that if you use %kill_embedded, you can fully deactivate\n"
"This embedded instance so it will never turn on again")   
exit_msg = '**Leaving Nested interpreter'

# Wrap it in a function that gives me more context:
def ipsh():
    ipshell = InteractiveShellEmbed(config=cfg, banner1=banner_msg, exit_msg=exit_msg)

    frame = inspect.currentframe().f_back
    msg   = 'Stopped at {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)

    # Go back one level! 
    # This is needed because the call to ipshell is inside the function ipsh()
    ipshell(msg,stack_depth=2)

然后,每当我要调试代码中的某些内容时,就将其放置ipsh()在需要进行对象检查等的位置。例如,说我要在my_function下面调试

使用它:

def my_function(b):
  a = b
  ipsh() # <- This will embed a full-fledged IPython interpreter
  a = 4

然后my_function(2)以下列方式之一调用:

  1. 通过运行从Unix Shell调用此函数的Python程序
  2. 或直接从IPython调用

不管我如何调用它,解释器都会停在所说的行ipsh()。完成后,您可以执行操作Ctrl-D,Python将恢复执行(使用您所做的任何变量更新)。请注意,如果您从常规IPython IPython Shell(上面的案例2)运行代码,那么新的IPython Shell将嵌套在您从中调用它的外壳内,这很好,但是请注意。无论哪种方式,一旦解释器停在的位置ipsh,我都可以检查a2)的值,查看定义了哪些函数和对象,等等。

问题:

上面的解决方案可以使Python在代码中所需的任何位置停止,然后将您带入完整的IPython解释器。不幸的是,调用脚本后,它不允许您添加或删除断点,这非常令人沮丧。在我看来,这是阻止IPython成为Python出色的调试工具的唯一原因

您目前可以做的最好的事情是:

一种解决方法是ipsh()在要Python解释程序启动IPython Shell的不同位置(即breakpoint)放置先验先验。然后,您可以使用以下命令在不同的预定义,硬编码的“断点”之间“跳转”Ctrl-D,这将退出当前的嵌入式IPython shell,并且每当解释器单击下一个调用时再次停止ipsh()

如果走这条路线,退出“调试模式”并忽略所有后续断点的一种方法是使用ipshell.dummy_mode = True,这将使Python忽略ipshell我们在上面创建的对象的任何后续实例化。

(Update on May 28, 2016) Using RealGUD in Emacs

For anyone in Emacs, this thread shows how to accomplish everything described in the OP (and more) using

  1. a new important debugger in Emacs called RealGUD which can operate with any debugger (including ipdb).
  2. The Emacs package isend-mode.

The combination of these two packages is extremely powerful and allows one to recreate exactly the behavior described in the OP and do even more.

More info on the wiki article of RealGUD for ipdb.


Original answer:

After having tried many different methods for debugging Python, including everything mentioned in this thread, one of my preferred ways of debugging Python with IPython is with embedded shells.

Defining a custom embedded IPython shell:

Add the following on a script to your PYTHONPATH, so that the method ipsh() becomes available.

import inspect

# First import the embed function
from IPython.terminal.embed import InteractiveShellEmbed
from IPython.config.loader import Config

# Configure the prompt so that I know I am in a nested (embedded) shell
cfg = Config()
prompt_config = cfg.PromptManager
prompt_config.in_template = 'N.In <\\#>: '
prompt_config.in2_template = '   .\\D.: '
prompt_config.out_template = 'N.Out<\\#>: '

# Messages displayed when I drop into and exit the shell.
banner_msg = ("\n**Nested Interpreter:\n"
"Hit Ctrl-D to exit interpreter and continue program.\n"
"Note that if you use %kill_embedded, you can fully deactivate\n"
"This embedded instance so it will never turn on again")   
exit_msg = '**Leaving Nested interpreter'

# Wrap it in a function that gives me more context:
def ipsh():
    ipshell = InteractiveShellEmbed(config=cfg, banner1=banner_msg, exit_msg=exit_msg)

    frame = inspect.currentframe().f_back
    msg   = 'Stopped at {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)

    # Go back one level! 
    # This is needed because the call to ipshell is inside the function ipsh()
    ipshell(msg,stack_depth=2)

Then, whenever I want to debug something in my code, I place ipsh() right at the location where I need to do object inspection, etc. For example, say I want to debug my_function below

Using it:

def my_function(b):
  a = b
  ipsh() # <- This will embed a full-fledged IPython interpreter
  a = 4

and then I invoke my_function(2) in one of the following ways:

  1. Either by running a Python program that invokes this function from a Unix shell
  2. Or by invoking it directly from IPython

Regardless of how I invoke it, the interpreter stops at the line that says ipsh(). Once you are done, you can do Ctrl-D and Python will resume execution (with any variable updates that you made). Note that, if you run the code from a regular IPython the IPython shell (case 2 above), the new IPython shell will be nested inside the one from which you invoked it, which is perfectly fine, but it’s good to be aware of. Eitherway, once the interpreter stops on the location of ipsh, I can inspect the value of a (which be 2), see what functions and objects are defined, etc.

The problem:

The solution above can be used to have Python stop anywhere you want in your code, and then drop you into a fully-fledged IPython interpreter. Unfortunately it does not let you add or remove breakpoints once you invoke the script, which is highly frustrating. In my opinion, this is the only thing that is preventing IPython from becoming a great debugging tool for Python.

The best you can do for now:

A workaround is to place ipsh() a priori at the different locations where you want the Python interpreter to launch an IPython shell (i.e. a breakpoint). You can then “jump” between different pre-defined, hard-coded “breakpoints” with Ctrl-D, which would exit the current embedded IPython shell and stop again whenever the interpreter hits the next call to ipsh().

If you go this route, one way to exit “debugging mode” and ignore all subsequent breakpoints, is to use ipshell.dummy_mode = True which will make Python ignore any subsequent instantiations of the ipshell object that we created above.


回答 3

您可以从pudb启动IPython会话,然后根据需要返回调试会话。

顺便说一句,ipdb在幕后使用IPython,您实际上可以使用IPython功能,例如TAB完成和魔术命令(以开头的命令%)。如果你是IPDB OK,你可以从IPython中使用命令,如启动%run%debug。从某种意义上讲,ipdb会话实际上比普通的IPython会话要好,因为您可以在堆栈跟踪中上下移动。ipdb中“对象检查”缺少什么?

另外,与Emacs> = 24.3捆绑在一起的python.el具有不错的ipdb支持。

You can start IPython session from pudb and go back to the debugging session as you like.

BTW, ipdb is using IPython behind the scenes and you can actually use IPython functionality such as TAB completion and magic commands (the one starts with %). If you are OK with ipdb you can start it from IPython using commands such as %run and %debug. ipdb session is actually better than plain IPython one in the sense you can go up and down in the stack trace etc. What is missing in ipdb for “object inspection”?

Also, python.el bundled with Emacs >= 24.3 has nice ipdb support.


回答 4

似乎@gaborous的答案中的方法已被弃用

新方法似乎是:

from IPython.core import debugger
debug = debugger.Pdb().set_trace

def buggy_method():
    debug()

Looks like the approach in @gaborous’s answer is deprecated.

The new approach seems to be:

from IPython.core import debugger
debug = debugger.Pdb().set_trace

def buggy_method():
    debug()

回答 5

前缀“!” 在pdb中键入命令的符号似乎与在IPython shell中执行操作具有相同的效果。这适用于访问某些功能甚至变量名的帮助。也许这会对您有所帮助。例如,

ipdb> help(numpy.transpose)
*** No help on (numpy.transpose)

但是!help(numpy.transpose)将为您提供有关numpy.transpose的预期帮助页面。同样,对于变量名,假设您有一个变量l,在pdb中键入“ l”会列出代码,但是!l会打印出l的值。

Prefixing an “!” symbol to commands you type in pdb seems to have the same effect as doing something in an IPython shell. This works for accessing help for a certain function, or even variable names. Maybe this will help you to some extent. For example,

ipdb> help(numpy.transpose)
*** No help on (numpy.transpose)

But !help(numpy.transpose) will give you the expected help page on numpy.transpose. Similarly for variable names, say you have a variable l, typing “l” in pdb lists the code, but !l prints the value of l.


回答 6

您是否尝试过此技巧

或者更好的是,使用ipython并调用:

from IPython.Debugger import Tracer; debug_here = Tracer()

那你就可以用

debug_here()

每当你想设置一个断点

Did you try this tip?

Or better still, use ipython, and call:

from IPython.Debugger import Tracer; debug_here = Tracer()

then you can just use

debug_here()

whenever you want to set a breakpoint


回答 7

您可以IPython 从内部 开始ipdb

引入ipdb调试器1

import idpb; ipdb.set_trace()

输入IPython的从内部ipdb>控制台2

from IPython import embed; embed()

ipdb>从以下位置返回到控制台IPython

exit

如果您有幸使用Emacs,可以使事情变得更加便捷!

这需要使用M-x shell。使用yasnippetbm,定义以下代码段。这将用行替换ipdb编辑器中的文本set-trace。插入代码段后,该行将突出显示,以便易于观察和导航。使用M-x bm-next导航。

# -*- mode: snippet -*-
# name: ipdb
# key: ipdb
# expand-env: ((yas-after-exit-snippet-hook #'bm-toggle))
# --
import ipdb; ipdb.set_trace()

1全部一行,易于删除。由于imports仅发生一次,因此此表单确保ipdb将在您需要时将其导入,而不会产生额外的开销。

2,您可以通过导入自己节省一些打字IPython 的内.pdbrc文件

try:
    from IPython import embed
except:
    pass

这使您可以简单地embed()从内部进行调用ipdb(当然,仅在安装IPython时)。

You can start IPython from within ipdb!

Induce the ipdb debugger1:

import idpb; ipdb.set_trace()

Enter IPython from within in the ipdb> console2:

from IPython import embed; embed()

Return to the ipdb> console from within IPython:

exit

If you’re lucky enough to be using Emacs, things can be made even more convenient!

This requires using M-x shell. Using yasnippet and bm, define the following snippet. This will replace the text ipdb in the editor with the set-trace line. After inserting the snippet, the line will be highlighted so that it is easily noticeable and navigable. Use M-x bm-next to navigate.

# -*- mode: snippet -*-
# name: ipdb
# key: ipdb
# expand-env: ((yas-after-exit-snippet-hook #'bm-toggle))
# --
import ipdb; ipdb.set_trace()

1 All on one line for easy deletion. Since imports only happen once, this form ensures ipdb will be imported when you need it with no extra overhead.

2 You can save yourself some typing by importing IPython within your .pdbrc file:

try:
    from IPython import embed
except:
    pass

This allows you to simply call embed() from within ipdb (of course, only when IPython is installed).


回答 8

一种选择是使用像Spyder这样的IDE ,它应该允许您在调试时与代码进行交互(实际上是使用IPython控制台)。实际上,Spyder非常类似于MATLAB,我认为这是故意的。其中包括变量检查器,变量编辑,对文档的内置访问等。

One option is to use an IDE like Spyder which should allow you to interact with your code while debugging (using an IPython console, in fact). In fact, Spyder is very MATLAB-like, which I presume was intentional. That includes variable inspectors, variable editing, built-in access to documentation, etc.


回答 9

该问题的正确,简单,酷爽的准确答案是将%run宏与-d标志一起使用。

In [4]: run -d myscript.py
NOTE: Enter 'c' at the ipdb>  prompt to continue execution.        
> /cygdrive/c/Users/mycodefolder/myscript.py(4)<module>()
      2                                                            
      3                        
----> 4 a=1                                            
      5 b=2

the right, easy, cool, exact answer for the question is to use %run macro with -d flag.

In [4]: run -d myscript.py
NOTE: Enter 'c' at the ipdb>  prompt to continue execution.        
> /cygdrive/c/Users/mycodefolder/myscript.py(4)<module>()
      2                                                            
      3                        
----> 4 a=1                                            
      5 b=2

回答 10

如果在embed()控制台中键入exit(),代码将继续并转到下一个embed()行。

If you type exit() in embed() console the code continue and go to the next embed() line.


回答 11

Pyzo IDE具有与OP问类似的功能。您不必以调试模式启动。与MATLAB相似,命令在shell中执行。在某些源代码行中设置断点时,IDE会在此处停止执行,并且您还可以调试和发出常规IPython命令。

但是,除非您设置了另一个断点,否则似乎单步执行(还好吗?)效果不佳(即在一行中停下来,然后步入另一功能)。

仍然来自MATLAB,这似乎是我找到的最佳解决方案。

The Pyzo IDE has similar capabilities as the OP asked for. You don’t have to start in debug mode. Similarly to MATLAB, the commands are executed in the shell. When you set up a break-point in some source code line, the IDE stops the execution there and you can debug and issue regular IPython commands as well.

It does seem however that step-into doesn’t (yet?) work well (i.e. stopping in one line and then stepping into another function) unless you set up another break-point.

Still, coming from MATLAB, this seems the best solution I’ve found.


回答 12

在python 3.2中,您具有interact命令,该命令可让您访问完整的python / ipython命令空间。

From python 3.2, you have the interact command, which gives you access to the full python/ipython command space.


回答 13

从Emacs的IPython-shell和通过pdb.set_trace()设置的断点运行应该可以。

与python-mode.el,Mx ipython RET等一起检查

Running from inside Emacs’ IPython-shell and breakpoint set via pdb.set_trace() should work.

Checked with python-mode.el, M-x ipython RET etc.


回答 14

开发新代码

在IPython中进行调试

  1. 使用Jupyter / IPython单元执行来加速实验迭代
  2. 使用%% debug逐步

单元格示例:

%%debug
...: for n in range(4):
...:    n>2

调试现有代码

IPython内部调试

  1. 调试损坏的单元测试: pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
  2. 调试测试箱子外面:breakpoint()python -m ipdb,等。
  3. IPython.embed()用于在调试器中需要时具有完整的IPython功能

关于Python的想法

我同意OP的观点,MATLAB可以很好地完成许多事情,Python仍然没有,而且确实应该这样做,因为该语言中的几乎所有内容都偏重于开发速度而不是生产速度。也许有一天,我将为CPython贡献一些琐碎的错误修复。

https://github.com/ipython/ipython/commit/f042f3fea7560afcb518a1940daa46a72fbcfa68

另请参见是否可以通过调试在IPython中运行命令?

Developing New Code

Debugging inside IPython

  1. Use Jupyter/IPython cell execution to speed up experiment iterations
  2. Use %%debug for step through

Cell Example:

%%debug
...: for n in range(4):
...:    n>2

Debugging Existing Code

IPython inside debugging

  1. Debugging a broken unit test: pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
  2. Debugging outside of test case: breakpoint(), python -m ipdb, etc.
  3. IPython.embed() for full IPython functionality where needed while in the debugger

Thoughts on Python

I agree with the OP that many things MATLAB does nicely Python still does not have and really should since just about everything in the language favors development speed over production speed. Maybe someday I will contribute more than trivial bug fixes to CPython.

https://github.com/ipython/ipython/commit/f042f3fea7560afcb518a1940daa46a72fbcfa68

See also Is it possible to run commands in IPython with debugging?


这是从哪里来的:-*-编码:utf-8-*-

问题:这是从哪里来的:-*-编码:utf-8-*-

Python将以下内容识别为定义文件编码的指令:

# -*- coding: utf-8 -*-

我确实在(-*- var: value -*-)之前看到过这种说明。它从何而来?完整规范是什么,例如,值可以包含空格,特殊符号,换行符,甚至-*-本身吗?

我的程序将编写纯文本文件,我想使用这种格式在其中包含一些元数据。

Python recognizes the following as instruction which defines file’s encoding:

# -*- coding: utf-8 -*-

I definitely saw this kind of instructions before (-*- var: value -*-). Where does it come from? What is the full specification, e.g. can the value include spaces, special symbols, newlines, even -*- itself?

My program will be writing plain text files and I’d like to include some metadata in them using this format.


回答 0

这种指定Python文件编码的方式来自PEP 0263-定义Python源代码编码

GNU Emacs也可以识别它(请参阅Python语言参考,2.1.4编码声明),尽管我不知道它是否是第一个使用该语法的程序。

This way of specifying the encoding of a Python file comes from PEP 0263 – Defining Python Source Code Encodings.

It is also recognized by GNU Emacs (see Python Language Reference, 2.1.4 Encoding declarations), though I don’t know if it was the first program to use that syntax.


回答 1

# -*- coding: utf-8 -*-是Python 2的东西。在Python 3+中,源文件默认编码已经是UTF-8,并且该行是无用的。

请参阅:我应该在Python 3中使用编码声明吗?

pyupgrade是一个可以在代码上运行的工具,用于从Python 2中删除这些注释和其他不再有用的遗留物,例如让所有类都继承自object

# -*- coding: utf-8 -*- is a Python 2 thing. In Python 3+, the default encoding of source files is already UTF-8 and that line is useless.

See: Should I use encoding declaration in Python 3?

pyupgrade is a tool you can run on your code to remove those comments and other no-longer-useful leftovers from Python 2, like having all your classes inherit from object.


回答 2

这就是所谓的文件局部变量,Emacs可以理解并相应地进行设置。请参阅Emacs手册中的相应部分 -您可以在文件的页眉或页脚中定义它们

This is so called file local variables, that are understood by Emacs and set correspondingly. See corresponding section in Emacs manual – you can define them either in header or in footer of file


回答 3

在PyCharm中,我将其省略。它将关闭底部的UTF-8指示器,并警告该编码为硬编码。不要以为您需要上面提到的PyCharm评论。

In PyCharm, I’d leave it out. It turns off the UTF-8 indicator at the bottom with a warning that the encoding is hard-coded. Don’t think you need the PyCharm comment mentioned above.


Emacs适用于Python的批量缩进

问题:Emacs适用于Python的批量缩进

如果我想在代码块中添加try / except,则在Emacs中使用Python,我经常发现我必须逐行缩进整个代码块。在Emacs中,如何立即缩进整个块。

我不是经验丰富的Emacs用户,但是发现它是通过ssh工作的最佳工具。我在命令行(Ubuntu)上使用Emacs,而不是作为gui,如果有什么不同的话。

Working with Python in Emacs if I want to add a try/except to a block of code, I often find that I am having to indent the whole block, line by line. In Emacs, how do you indent the whole block at once.

I am not an experienced Emacs user, but just find it is the best tool for working through ssh. I am using Emacs on the command line(Ubuntu), not as a gui, if that makes any difference.


回答 0

如果您正在使用Emacs编程Python,那么您可能应该使用python-mode。使用python-mode,在标记代码块之后,

C-c >C-c C-l 将区域右移4个空格

C-c <C-c C-r 将区域向左移动4个空格

如果您需要将代码缩进两个级别,或者需要一定程度的缩编,则可以在命令前加上一个参数:

C-u 8 C-c > 将区域右移8个空格

C-u 8 C-c < 将区域向左移动8个空格

另一种选择是使用M-x indent-rigidly绑定到C-x TAB

C-u 8 C-x TAB 将区域右移8个空格

C-u -8 C-x TAB 将区域向左移动8个空格

用于文本矩形而不是文本行的矩形命令也很有用。

例如,在标记矩形区域后,

C-x r o 插入空格以填充矩形区域(有效地向右移动代码)

C-x r k 杀死矩形区域(有效地将代码向左移动)

C-x r t提示输入一个字符串来替换矩形。输入C-u 8 <space>后将输入8个空格。

PS。使用Ubuntu,要将python-mode设置为所有.py文件的默认模式,只需安装该python-mode软件包。

If you are programming Python using Emacs, then you should probably be using python-mode. With python-mode, after marking the block of code,

C-c > or C-c C-l shifts the region 4 spaces to the right

C-c < or C-c C-r shifts the region 4 spaces to the left

If you need to shift code by two levels of indention, or some arbitary amount you can prefix the command with an argument:

C-u 8 C-c > shifts the region 8 spaces to the right

C-u 8 C-c < shifts the region 8 spaces to the left

Another alternative is to use M-x indent-rigidly which is bound to C-x TAB:

C-u 8 C-x TAB shifts the region 8 spaces to the right

C-u -8 C-x TAB shifts the region 8 spaces to the left

Also useful are the rectangle commands that operate on rectangles of text instead of lines of text.

For example, after marking a rectangular region,

C-x r o inserts blank space to fill the rectangular region (effectively shifting code to the right)

C-x r k kills the rectangular region (effectively shifting code to the left)

C-x r t prompts for a string to replace the rectangle with. Entering C-u 8 <space> will then enter 8 spaces.

PS. With Ubuntu, to make python-mode the default mode for all .py files, simply install the python-mode package.


回答 1

除了默认情况下indent-region映射到C-M-\的,矩形编辑命令对Python很有用。将区域标记为正常,然后:

  • C-x r tstring-rectangle):将提示您输入要插入每行的字符;非常适合插入一定数量的空格
  • C-x r kkill-rectangle):删除矩形区域;非常适合去除压痕

您也可以C-x r yyank-rectangle),但这很少有用。

In addition to indent-region, which is mapped to C-M-\ by default, the rectangle edit commands are very useful for Python. Mark a region as normal, then:

  • C-x r t (string-rectangle): will prompt you for characters you’d like to insert into each line; great for inserting a certain number of spaces
  • C-x r k (kill-rectangle): remove a rectangle region; great for removing indentation

You can also C-x r y (yank-rectangle), but that’s only rarely useful.


回答 2

indent-region映射C-M-\应该可以解决问题。

indent-region mapped to C-M-\ should do the trick.


回答 3

我一直在使用此功能来处理缩进和缩进:

(defun unindent-dwim (&optional count-arg)
  "Keeps relative spacing in the region.  Unindents to the next multiple of the current tab-width"
  (interactive)
  (let ((deactivate-mark nil)
        (beg (or (and mark-active (region-beginning)) (line-beginning-position)))
        (end (or (and mark-active (region-end)) (line-end-position)))
        (min-indentation)
        (count (or count-arg 1)))
    (save-excursion
      (goto-char beg)
      (while (< (point) end)
        (add-to-list 'min-indentation (current-indentation))
        (forward-line)))
    (if (< 0 count)
        (if (not (< 0 (apply 'min min-indentation)))
            (error "Can't indent any more.  Try `indent-rigidly` with a negative arg.")))
    (if (> 0 count)
        (indent-rigidly beg end (* (- 0 tab-width) count))
      (let (
            (indent-amount
             (apply 'min (mapcar (lambda (x) (- 0 (mod x tab-width))) min-indentation))))
        (indent-rigidly beg end (or
                                 (and (< indent-amount 0) indent-amount)
                                 (* (or count 1) (- 0 tab-width))))))))

然后将其分配给键盘快捷键:

(global-set-key (kbd "s-[") 'unindent-dwim)
(global-set-key (kbd "s-]") (lambda () (interactive) (unindent-dwim -1)))

I’ve been using this function to handle my indenting and unindenting:

(defun unindent-dwim (&optional count-arg)
  "Keeps relative spacing in the region.  Unindents to the next multiple of the current tab-width"
  (interactive)
  (let ((deactivate-mark nil)
        (beg (or (and mark-active (region-beginning)) (line-beginning-position)))
        (end (or (and mark-active (region-end)) (line-end-position)))
        (min-indentation)
        (count (or count-arg 1)))
    (save-excursion
      (goto-char beg)
      (while (< (point) end)
        (add-to-list 'min-indentation (current-indentation))
        (forward-line)))
    (if (< 0 count)
        (if (not (< 0 (apply 'min min-indentation)))
            (error "Can't indent any more.  Try `indent-rigidly` with a negative arg.")))
    (if (> 0 count)
        (indent-rigidly beg end (* (- 0 tab-width) count))
      (let (
            (indent-amount
             (apply 'min (mapcar (lambda (x) (- 0 (mod x tab-width))) min-indentation))))
        (indent-rigidly beg end (or
                                 (and (< indent-amount 0) indent-amount)
                                 (* (or count 1) (- 0 tab-width))))))))

And then I assign it to a keyboard shortcut:

(global-set-key (kbd "s-[") 'unindent-dwim)
(global-set-key (kbd "s-]") (lambda () (interactive) (unindent-dwim -1)))

回答 4

我是Emacs的新手,因此此答案可能对您毫无用处。

到目前为止,所提到的答案都没有覆盖字面量如dict或的重新缩进list。例如,如果您剪切并粘贴了以下文字并需要对其进行合理的缩进,则M-x indent-regionor或M-x python-indent-shift-rightcompany不会提供帮助:

    foo = {
  'bar' : [
     1,
    2,
        3 ],
      'baz' : {
     'asdf' : {
        'banana' : 1,
        'apple' : 2 } } }

感觉M-x indent-region应该在中做一些明智的事情python-mode,但是还不是这样。

对于将文字放在方括号中的特定情况,在相关行上使用TAB即可获得所需的内容(因为空格不起作用)。

因此,在这种情况下,我一直在快速记录键盘宏,例如<f3> C-n TAB <f4>F3,Ctrl-n(或向下箭头),TAB,F4,然后重复使用F4来应用宏可以节省几次击键。或者,您可以将C-u 10 C-x e其应用10次。

(我知道这听起来不多,但是尝试重新缩进100行垃圾文字而不丢失向下箭头,然后不得不上升5行并重复一遍;)。

I’m an Emacs newb, so this answer it probably bordering on useless.

None of the answers mentioned so far cover re-indentation of literals like dict or list. E.g. M-x indent-region or M-x python-indent-shift-right and company aren’t going to help if you’ve cut-and-pasted the following literal and need it to be re-indented sensibly:

    foo = {
  'bar' : [
     1,
    2,
        3 ],
      'baz' : {
     'asdf' : {
        'banana' : 1,
        'apple' : 2 } } }

It feels like M-x indent-region should do something sensibly in python-mode, but that’s not (yet) the case.

For the specific case where your literals are bracketed, using TAB on the lines in question gets what you want (because whitespace doesn’t play a role).

So what I’ve been doing in such cases is quickly recording a keyboard macro like <f3> C-n TAB <f4> as in F3, Ctrl-n (or down arrow), TAB, F4, and then using F4 repeatedly to apply the macro can save a couple of keystrokes. Or you can do C-u 10 C-x e to apply it 10 times.

(I know it doesn’t sound like much, but try re-indenting 100 lines of garbage literal without missing down-arrow, and then having to go up 5 lines and repeat things ;) ).


回答 5

我使用以下代码段。当选项卡处于非活动状态时,在选项卡上缩进当前行(通常如此);当选择处于非活动状态时,它将使整个区域向右缩进。

(defun my-python-tab-command (&optional _)
  "If the region is active, shift to the right; otherwise, indent current line."
  (interactive)
  (if (not (region-active-p))
      (indent-for-tab-command)
    (let ((lo (min (region-beginning) (region-end)))
          (hi (max (region-beginning) (region-end))))
      (goto-char lo)
      (beginning-of-line)
      (set-mark (point))
      (goto-char hi)
      (end-of-line)
      (python-indent-shift-right (mark) (point)))))
(define-key python-mode-map [remap indent-for-tab-command] 'my-python-tab-command)

I use the following snippet. On tab when the selection is inactive, it indents the current line (as it normally does); when the selection is inactive, it indents the whole region to the right.

(defun my-python-tab-command (&optional _)
  "If the region is active, shift to the right; otherwise, indent current line."
  (interactive)
  (if (not (region-active-p))
      (indent-for-tab-command)
    (let ((lo (min (region-beginning) (region-end)))
          (hi (max (region-beginning) (region-end))))
      (goto-char lo)
      (beginning-of-line)
      (set-mark (point))
      (goto-char hi)
      (end-of-line)
      (python-indent-shift-right (mark) (point)))))
(define-key python-mode-map [remap indent-for-tab-command] 'my-python-tab-command)

回答 6

交互进行缩进。

  1. 选择要缩进的区域。
  2. Cx TAB
  3. 使用箭头(<-->)进行交互缩进。
  4. Esc完成所需的缩进后,按三次。

从我的文章中复制:在Emacs中缩进几行

Do indentation interactively.

  1. Select the region to be indented.
  2. C-x TAB.
  3. Use arrows (<- and ->) to indent interactively.
  4. Press Esc three times when you are done with the required indentation.

Copied from my post in: Indent several lines in Emacs


回答 7

我普遍做这样的事情

;; intent whole buffer 
(defun iwb ()
  "indent whole buffer"
  (interactive)
  ;;(delete-trailing-whitespace)
  (indent-region (point-min) (point-max) nil)
  (untabify (point-min) (point-max)))

I do something like this universally

;; intent whole buffer 
(defun iwb ()
  "indent whole buffer"
  (interactive)
  ;;(delete-trailing-whitespace)
  (indent-region (point-min) (point-max) nil)
  (untabify (point-min) (point-max)))