如何检查代码是否在 IPython 笔记本中执行?

我想分享一些 Python 代码示例,如果在终端 Python/IPython 或 IPython 笔记本中执行,它们应该会有所不同。

如果 Python 代码在 IPython 笔记本中运行,我如何从中进行检查?

57446 次浏览

问题是您希望以不同的方式执行什么。

我们在 IPython 中尽力防止内核知道连接到哪种前端,实际上您甚至可以让一个内核同时连接到许多不同的前端。即使您可以查看 stderr/out的类型以了解您是否位于 ZMQ 内核中,它也不能保证您在另一端拥有什么。你甚至可以根本没有前额。

您可能应该以前端独立的方式编写代码,但是如果您想显示不同的内容,您可以使用 富显示系统(链接到 IPython 的4.x 版本)根据前端显示不同的内容,但是前端将选择,而不是库。

您可以使用以下代码片段 [1]检查 python 是否处于 互动模式:

def is_interactive():
import __main__ as main
return not hasattr(main, '__file__')

我发现这个方法非常有用,因为我在笔记本上做了很多原型设计。出于测试目的,我使用默认参数。否则,我从 sys.argv中读取参数。

from sys import argv


if is_interactive():
params = [<list of default parameters>]
else:
params = argv[1:]

在实现 autonotebook之后,您可以使用以下代码判断您是否在笔记本中。

def in_notebook():
try:
from IPython import get_ipython
if 'IPKernelApp' not in get_ipython().config:  # pragma: no cover
return False
except ImportError:
return False
except AttributeError:
return False
return True

为了检查你是否在一个笔记本中,这很重要,例如,当决定使用什么类型的进度条时,这个方法对我很有效:

def in_ipynb():
try:
cfg = get_ipython().config
if cfg['IPKernelApp']['parent_appname'] == 'ipython-notebook':
return True
else:
return False
except NameError:
return False

最近我遇到了一个需要变通方法的 Jupyter 笔记本里的 bug,我想在不损失其他 shell 中的功能的情况下做到这一点。我意识到 凯夫拉维奇溶液在这种情况下不起作用,因为 get_ipython()只能直接从笔记本电脑获得,而不能从导入的模块获得。因此,我找到了一种方法,可以从我的模块中检测出它是否是从 Jupiter 笔记本电脑导入和使用的:

import sys


def in_notebook():
"""
Returns ``True`` if the module is running in IPython kernel,
``False`` if in IPython shell or other Python shell.
"""
return 'ipykernel' in sys.modules


# later I found out this:


def ipython_info():
ip = False
if 'ipykernel' in sys.modules:
ip = 'notebook'
elif 'IPython' in sys.modules:
ip = 'terminal'
return ip

如果这足够健壮,欢迎提出意见。

类似的方法也可以获得一些关于客户端和 IPython 版本的信息:

import sys


if 'ipykernel' in sys.modules:
ip = sys.modules['ipykernel']
ip_version = ip.version_info
ip_client = ip.write_connection_file.__module__.split('.')[0]


# and this might be useful too:


ip_version = IPython.utils.sysinfo.get_sys_info()['ipython_version']

以下方法对我的需要很有效:

get_ipython().__class__.__name__

它在终端 IPython 上返回 'TerminalInteractiveShell',在 Jupiter 上返回 'ZMQInteractiveShell'(笔记本和 qtsole) ,在常规 Python 解释器上返回失败(NameError)。默认情况下,启动 IPython 时,全局命名空间中似乎可以使用 get_ipython()方法。

用一个简单的函数包装它:

def is_notebook() -> bool:
try:
shell = get_ipython().__class__.__name__
if shell == 'ZMQInteractiveShell':
return True   # Jupyter notebook or qtconsole
elif shell == 'TerminalInteractiveShell':
return False  # Terminal running IPython
else:
return False  # Other type (?)
except NameError:
return False      # Probably standard Python interpreter

在 macOS 10.12和 Ubuntu 14.04.4 LTS 上使用 Python 3.5.2、 IPython 5.1.0和 Jupiter 4.2.1测试了上面的代码

编辑: 这在2022年新的 Python/IPython/Jupiter/OS 版本中仍然可以很好地工作

我正在使用 Django Shell Plus 来启动 IPython,我想让“在笔记本中运行”作为一个 Django 设置值。在加载设置时,get_ipython()不可用,所以我使用了这个方法(它不是防弹的,但是对于它所使用的本地开发环境来说已经足够好了) :

import sys


if '--notebook' in sys.argv:
ENVIRONMENT = "notebook"
else:
ENVIRONMENT = "dev"

据我所知,这里有3种使用 ipykernel的蟒蛇

  1. ipython qtconsole(简称“ qtipython”)
  2. Spyder 中的 IPython (简称“ spyder”)
  3. Jupyter 笔记本中的 IPython (简称“ jn”)

使用 'spyder' in sys.modules可以区分蜘蛛

但是对于 qtipython 和 jn 很难区分,因为

它们具有相同的 sys.modules和相同的 IPython 配置: get_ipython().config

我发现了 qtipython 和 jn 之间的不同之处:

首先在 IPython shell 中运行 os.getpid()以获得 pid 号

然后运行 ps -ef|grep [pid number]

我的 qtipython pid 是8699

yanglei   8699  8693  4 20:31 ?        00:00:01 /home/yanglei/miniconda2/envs/py3/bin/python -m ipykernel_launcher -f /run/user/1000/jupyter/kernel-8693.json

我的密码是8832

yanglei   8832  9788 13 20:32 ?        00:00:01 /home/yanglei/miniconda2/bin/python -m ipykernel_launcher -f /run/user/1000/jupyter/kernel-ccb962ec-3cd3-4008-a4b7-805a79576b1b.json

与 qtipython 不同的是 jn 是 ipython 的 json 名称,jn 的 json 名称比 qtipython 的长

因此,我们可以使用以下代码自动检测所有 Python 环境:

import sys,os
def jupyterNotebookOrQtConsole():
env = 'Unknow'
cmd = 'ps -ef'
try:
with os.popen(cmd) as stream:
if not py2:
stream = stream._stream
s = stream.read()
pid = os.getpid()
ls = list(filter(lambda l:'jupyter' in l and str(pid) in l.split(' '), s.split('\n')))
if len(ls) == 1:
l = ls[0]
import re
pa = re.compile(r'kernel-([-a-z0-9]*)\.json')
rs = pa.findall(l)
if len(rs):
r = rs[0]
if len(r)<12:
env = 'qtipython'
else :
env = 'jn'
return env
except:
return env
    

pyv = sys.version_info.major
py3 = (pyv == 3)
py2 = (pyv == 2)
class pyi():
'''
python info
    

plt : Bool
mean plt avaliable
env :
belong [cmd, cmdipython, qtipython, spyder, jn]
'''
pid = os.getpid()
gui = 'ipykernel' in sys.modules
cmdipython = 'IPython' in sys.modules and not gui
ipython = cmdipython or gui
spyder = 'spyder' in sys.modules
if gui:
env = 'spyder' if spyder else jupyterNotebookOrQtConsole()
else:
env = 'cmdipython' if ipython else 'cmd'
    

cmd = not ipython
qtipython = env == 'qtipython'
jn = env == 'jn'
    

plt = gui or 'DISPLAY' in os.environ


print('Python Envronment is %s'%pyi.env)

源代码在这里: 检测 Python 环境,特别区分 Spyder,Jupiter 笔记本,Qtconsole.py

下面捕获了 https://stackoverflow.com/a/50234148/1491619的情况,而不需要解析 ps的输出

def pythonshell():
"""Determine python shell


pythonshell() returns


'shell' (started python on command line using "python")
'ipython' (started ipython on command line using "ipython")
'ipython-notebook' (e.g., running in Spyder or started with "ipython qtconsole")
'jupyter-notebook' (running in a Jupyter notebook)


See also https://stackoverflow.com/a/37661854
"""


import os
env = os.environ
shell = 'shell'
program = os.path.basename(env['_'])


if 'jupyter-notebook' in program:
shell = 'jupyter-notebook'
elif 'JPY_PARENT_PID' in env or 'ipython' in program:
shell = 'ipython'
if 'JPY_PARENT_PID' in env:
shell = 'ipython-notebook'


return shell

我建议避免检测特定的前端,因为有 太多了。相反,你可以只测试你是否在 iPython 环境中运行:

def is_running_from_ipython():
from IPython import get_ipython
return get_ipython() is not None

如果从通常的 python 命令行调用 running_from_ipython,以上将返回 False。当你从 Jupiter Notebook、 Jupiter terHub、 iPython shell、 Google Colab 等调用它时,它将返回 True

针对 python 3.7.3进行了测试

CPython 实现的名称 __builtins__可以作为其全局变量的一部分。可以由函数 globals ()检索。
如果脚本在 Ipython 环境中运行,那么 __IPYTHON__应该是 __builtins__的属性。
因此,如果在 Ipython 下运行,则下面的代码返回 True,否则返回 False

hasattr(__builtins__,'__IPYTHON__')

假设你已经控制了木星笔记本,你可以:

  1. 在使用该值作为 代码中的标志的单元格中设置环境值。在该单元格(或要排除的所有单元格)中放置唯一注释

    排除 _ from _ export
    % set _ env is _ jupyter = 1

  2. 将笔记本导出为一个 Python 脚本,以便在不同的上下文中使用。导出将排除注释单元格以及随后设置环境值的代码。注意: 将 你的笔记本替换为实际笔记本文件的名称。

    转换为脚本—— RegexRemovePreprocessor.pattern = “[’^ #  排除 _ from _ export’]”

这将生成一个不设置 jupyter 环境标志的文件,允许使用该标志确定性地执行代码。

这样如何:

import sys


inJupyter = sys.argv[-1].endswith('json')


print(inJupyter);

你所要做的就是把这两个单元格放在你笔记本的开头:

单元1: (标记为“代码”) :

is_notebook = True

单元2: (标记为“原始 NBConvert”) :

is_notebook = False

第一个单元格将始终执行,但是第二个单元格只有在将笔记本作为 Python 脚本导出时才会执行。

稍后,你可以检查:

if is_notebook:
notebook_code()
else:
script_code()

希望这个能帮上忙。

一个非常简单有效的解决方案是检查调用堆栈的顶部是否引用了 IPython 环境,如下所示:

import traceback


def is_in_notebook():
rstk = traceback.extract_stack(limit=1)[0]
return rstk[0].startswith("<ipython")

这段代码适用于 IPython 或 Jupiter 上的 Python2和3,不需要检查、设置或更改环境。

你可以尝试从 迂腐借用 eval('__IPYTHON__'):

def in_ipython() -> bool:
"""
Check whether we're in an ipython environment, including jupyter notebooks.
"""
try:
eval('__IPYTHON__')
except NameError:
return False
else:  # pragma: no cover
return True

检查父进程

到目前为止,对我来说唯一有效的解决方案是使用 Psutil检查父进程:

import psutil
def runninginJupyterNotebook():
for i in psutil.Process().parent().cmdline():
if i.endswith("bin/jupyter-notebook"):
return True
else:
return False

或者你可以在一行中设置一个变量:

RUNNING_IN_JUPYTER = any([i.endswith("bin/jupyter-notebook") for i in psutil.Process().parent().cmdline()])

如果你使用的是木星笔记本电脑,那么 RUNNING_IN_JUPYTER就是 True

请注意,如果您正在运行 Colab 笔记本,它也将为真。

与其他解决方案比较:

get_ipython().__class__.__name__

所有使用 get_ipython()的解决方案只有在您不关心它是否在 QtConsole 上运行时才能正常工作。

来源: https://stackoverflow.com/a/65498256/2132157