简体   繁体   English

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

[英]How can I check if code is executed in the IPython notebook?

I have some Python code example I'd like to share that should do something different if executed in the terminal Python / IPython or in the IPython notebook.我有一些 Python 代码示例我想分享,如果在终端 Python / IPython 或 IPython 笔记本中执行,应该会做一些不同的事情。

How can I check from my Python code if it's running in the IPython notebook?我如何从我的 Python 代码中检查它是否在 IPython 笔记本中运行?

The following worked for my needs:以下内容满足我的需求:

get_ipython().__class__.__name__

It returns 'TerminalInteractiveShell' on a terminal IPython, 'ZMQInteractiveShell' on Jupyter (notebook AND qtconsole) and fails ( NameError ) on a regular Python interpreter.它在终端 IPython 上返回'TerminalInteractiveShell' ,在 Jupyter(笔记本和 qtconsole)上返回'ZMQInteractiveShell' ,并在常规 Python 解释器上失败( NameError )。 The method get_ipython() seems to be available in the global namespace by default when IPython is started.启动 IPython 时,方法get_ipython()似乎默认在全局命名空间中可用。

Wrapping it in a simple function:将其包装在一个简单的函数中:

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

The above was tested with Python 3.5.2, IPython 5.1.0 and Jupyter 4.2.1 on macOS 10.12 and Ubuntu 14.04.4 LTS以上内容在 macOS 10.12 和 Ubuntu 14.04.4 LTS 上使用 Python 3.5.2、IPython 5.1.0 和 Jupyter 4.2.1 进行了测试

EDIT: This still works fine in 2022 on newer Python/IPython/Jupyter/OS versions编辑:这在 2022 年在较新的 Python/IPython/Jupyter/OS 版本上仍然可以正常工作

To check if you're in a notebook, which can be important eg when determining what sort of progressbar to use, this worked for me:要检查您是否在笔记本中,这可能很重要,例如在确定要使用哪种进度条时,这对我有用:

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

You can check whether python is in interactive mode with the following snippet [1] :您可以使用以下代码段[1]检查 python 是否处于交互模式:

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

I have found this method very useful because I do a lot of prototyping in the notebook.我发现这种方法非常有用,因为我在笔记本中做了很多原型设计。 For testing purposes, I use default parameters.出于测试目的,我使用默认参数。 Otherwise, I read the parameters from sys.argv .否则,我从sys.argv读取参数。

from sys import argv

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

Following the implementation of autonotebook , you can tell whether you are in a notebook using the following code.在执行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

Recently I encountered a bug in Jupyter notebook which needs a workaround, and I wanted to do this without loosing functionality in other shells.最近我在 Jupyter notebook 中遇到了一个需要解决方法的错误,我想在不丢失其他 shell 功能的情况下做到这一点。 I realized that keflavich's solution does not work in this case, because get_ipython() is available only directly from the notebook, and not from imported modules.我意识到keflavich 的解决方案在这种情况下不起作用,因为get_ipython()只能直接从笔记本中获得,而不是从导入的模块中获得。 So I found a way to detect from my module whether it is imported and used from a Jupyter notebook or not:所以我找到了一种从我的模块中检测它是否是从 Jupyter 笔记本导入和使用的方法:

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

Comments are appreciated if this is robust enough.如果这足够强大,我们将不胜感激。

Similar way it is possible to get some info about the client, and IPython version as well:类似的方式可以获得有关客户端的一些信息,以及 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']

Tested for python 3.7.3已针对 python 3.7.3 测试

CPython implementations have the name __builtins__ available as part of their globals which btw. CPython 实现的名称__builtins__可作为​​其全局变量的一部分,顺便说一句。 can be retrieved by the function globals().可以通过函数 globals() 检索。
If a script is running in an Ipython environment then __IPYTHON__ should be an attribute of __builtins__ .如果脚本在 Ipython 环境中运行,那么__IPYTHON__应该是__builtins__的属性。
The code below therefore returns True if run under Ipython or else it gives False因此,如果在 Ipython 下运行,下面的代码返回True ,否则返回False

hasattr(__builtins__,'__IPYTHON__')

The question is what do you want execute differently.问题是你想以不同的方式执行什么。

We do our best in IPython prevent the kernel from knowing to which kind of frontend is connected, and actually you can even have a kernel connected to many differents frontends at the same time.我们在 IPython 中尽最大努力防止内核知道连接到哪种前端,实际上您甚至可以让一个内核同时连接到许多不同的前端。 Even if you can take a peek at the type of stderr/out to know wether you are in a ZMQ kernel or not, it does not guaranties you of what you have on the other side.即使您可以查看stderr/out的类型以了解您是否在 ZMQ 内核中,它也不能保证您在另一端拥有什么。 You could even have no frontends at all.你甚至可以根本没有前端。

You should probably write your code in a frontend independent manner, but if you want to display different things, you can use the rich display system (link pinned to version 4.x of IPython) to display different things depending on the frontend, but the frontend will choose, not the library.您可能应该以独立于前端的方式编写代码,但是如果您想显示不同的东西,您可以使用丰富的显示系统(链接固定到 IPython 的 4.x 版)根据前端显示不同的东西,但是前端会选择,而不是库。

All you have to do is to place these two cells at the beginning of your notebook:您所要做的就是将这两个单元格放在笔记本的开头:

Cell 1: (marked as "code"):单元格 1:(标记为“代码”):

is_notebook = True

Cell 2: (marked as "Raw NBConvert"):单元格 2:(标记为“原始 NBConvert”):

is_notebook = False

The first cell will always be executed, but the second cell will only be executed when you export the notebook as a Python script.第一个单元格将始终执行,但第二个单元格仅在您将笔记本导出为 Python 脚本时才会执行。

Later, you can check:稍后,您可以检查:

if is_notebook:
    notebook_code()
else:
    script_code()

Hope this helps.希望这可以帮助。

As far as I know, Here has 3 kinds of ipython that used ipykernel据我所知,这里有 3 种使用 ipykernel 的ipykernel

  1. ipython qtconsole ("qtipython" for short) ipython qtconsole (简称“qtipython”)
  2. IPython in spyder ("spyder" for short) spyder中的IPython(简称“spyder”)
  3. IPython in jupyter notebook ("jn" for short) jupyter notebook 中的 IPython(简称“jn”)

use 'spyder' in sys.modules can distinguish spyder 'spyder' in sys.modules可以区分 spyder

but for qtipython and jn are hard to distinguish because但是对于 qtipython 和 jn 很难区分,因为

they have same sys.modules and same IPython config: get_ipython().config他们有相同的sys.modules和相同的 IPython 配置: get_ipython().config

I find a different between qtipython and jn:我发现 qtipython 和 jn 之间有所不同:

first run os.getpid() in IPython shell to get the pid number首先在 IPython shell 中运行os.getpid()以获取 pid 号

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

my qtipython pid is 8699我的 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

my jn pid is 8832我的 jn pid 是 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

the different of qtipython and jn is the ipython's json name, jn's json name are longer than qtipython's qtipython 和 jn 的不同是 ipython 的 json 名称,jn 的 json 名称比 qtipython 的长

so, we can auto-detect all Python Environment using the following code:因此,我们可以使用以下代码自动检测所有 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)

The source code is here: Detection Python Environment, Especially distinguish Spyder, Jupyter notebook, Qtconsole.py源码在这里: 检测Python环境,特别区分Spyder、Jupyter notebook、Qtconsole.py

The following captures the cases of https://stackoverflow.com/a/50234148/1491619 without needing to parse the output of ps下面截取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

I would recommend avoiding to detect specific frontend because there are too many of them .我建议避免检测特定的前端,因为它们太多了 Instead you can just test if you are running from within iPython environment:相反,您可以测试您是否在 iPython 环境中运行:

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

Above will return False if you are invoking running_from_ipython from usual python command line.如果您从通常的 python 命令行调用running_from_ipython ,上面将返回False When you invoke it from Jupyter Notebook, JupyterHub, iPython shell, Google Colab etc then it will return True .当您从 Jupyter Notebook、JupyterHub、iPython shell、Google Colab 等调用它时,它将返回True

A very simple and efficient solution is to check if the top of the call stack refers to IPython environment, as follows:一个非常简单高效的解决方案是检查调用堆栈的顶部是否引用了 IPython 环境,如下:

import traceback

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

This code works for both Python 2 and 3, on IPython or Jupyter, with no need to check, set or change the environment.此代码适用于 IPython 或 Jupyter 上的 Python 2 和 3,无需检查、设置或更改环境。

I am using Django Shell Plus to launch IPython, and I wanted to make 'running in notebook' available as a Django settings value.我正在使用 Django Shell Plus 来启动 IPython,并且我想让“在笔记本中运行”作为 Django 设置值可用。 get_ipython() is not available when loading settings, so I use this (which is not bulletproof, but good enough for the local development environments it's used in): get_ipython()在加载设置时不可用,所以我使用它(这不是防弹的,但对于它所使用的本地开发环境来说已经足够好了):

import sys

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

Assuming you have control of the Jupyter Notebook you could:假设您可以控制 Jupyter Notebook,您可以:

  1. set an environment value in a cell that uses this as a flag in your code .在将其用作代码中的标志的单元格中设置环境值。 Place a unique comment in that cell (or all cells you want to exclude)在该单元格(或您要排除的所有单元格)中放置唯一注释

    # exclude_from_export # exclude_from_export
    %set_env is_jupyter=1 %set_env is_jupyter=1

  2. Export the notebook as a python script to be used in a different context.将笔记本导出为 python 脚本以在不同的上下文中使用。 The export would exclude the commented cell(s) and subsequently the code that sets the environment value.导出将排除注释的单元格,然后排除设置环境值的代码。 Note: replace your_notebook.ipynb with the name of your actual notebook file.注意:将your_notebook.ipynb替换为您的实际笔记本文件的名称。

    jupyter nbconvert --to script --RegexRemovePreprocessor.patterns="['^# exclude_from_export']" your_notebook.ipynb jupyter nbconvert --to 脚本 --RegexRemovePreprocessor.patterns="['^# exclude_from_export']" your_notebook.ipynb

This will generate a file that will not have the jupyter environment flag set allowing for code that uses it to deterministically execute.这将生成一个没有设置 jupyter 环境标志的文件,允许使用它的代码确定性地执行。

How about something like this:像这样的东西怎么样:

import sys

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

print(inJupyter);

A simple solution that works in modules as well一个也适用于模块的简单解决方案

if '_Jupyter' in globals():
    # Do something special for Jupyter

You can try to eval('__IPYTHON__') , borrowed from pydantic :您可以尝试eval('__IPYTHON__') ,借用自pydantic

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

Check the parent process检查父进程

So far the only solution that worked for me is to check the parent processes using psutil :到目前为止,唯一对我有用的解决方案是使用psutil检查父进程:

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

Or you can set a variable in one line:或者您可以在一行中设置一个变量:

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

RUNNING_IN_JUPYTER is True if you are running a Jupyter Notebook.如果您正在运行 Jupyter Notebook,则RUNNING_IN_JUPYTERTrue

Note that it will be true also if you are running a Colab notebook.请注意,如果您正在运行 Colab 笔记本,这也是如此。

Comparison to the other solutions:与其他解决方案的比较:

get_ipython().__class__.__name__

All the solutions using get_ipython() work only if you don't care if it is running on a QtConsole.所有使用get_ipython()的解决方案只有在您不关心它是否在 QtConsole 上运行时才有效。

Credits: https://stackoverflow.com/a/65498256/2132157学分: https://stackoverflow.com/a/65498256/2132157

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM