Как я могу проверить, выполняется ли код в записной книжке IPython?

89

У меня есть пример кода Python, которым я хотел бы поделиться, который должен делать что-то другое при выполнении в терминале Python / IPython или в записной книжке IPython.

Как я могу проверить свой код Python, работает ли он в записной книжке IPython?

Кристоф
источник
3
Предлагаю принять ответ Густаво Безерры . Принятый в настоящее время ответ не отвечает на вопрос, и ответ Густаво - это ответ с наивысшей оценкой, который все еще работает в последней версии Jupyter Notebook.
Марк Эмери

Ответы:

9

Вопрос в том, что вы хотите выполнить по-другому.

В IPython мы делаем все возможное, чтобы ядро ​​не знало, к какому типу интерфейса подключен, и на самом деле вы даже можете подключить ядро ​​ко многим различным интерфейсам одновременно. Даже если вы можете взглянуть на типstderr/out чтобы узнать, используете ли вы ядро ​​ZMQ или нет, это не гарантирует вам того, что у вас есть на другой стороне. У вас может вообще не быть фронтендов.

Вам, вероятно, следует писать свой код независимо от внешнего интерфейса, но если вы хотите отображать разные вещи, вы можете использовать систему расширенного отображения (ссылка закреплена на версии 4.x IPython) для отображения разных вещей в зависимости от внешнего интерфейса, но будет выбирать интерфейс, а не библиотека.

Мэтт
источник
2
Ссылка выше на систему IPython Rich Display System не работает. Вот ссылка на текущую документацию: ipython.org/ipython-doc/dev/config/integrating.html , а вот ссылка на несколько отличных примеров: nbviewer.ipython.org/github/ipython/ipython/blob/master/ …
Who8MyLunch
2
У меня такая проблема в моем модуле рисования . Мне нужно импортировать туда вызов matplotlib.use ("Agg") для travis-ci, чтобы разрешить сохранение чертежей (см. Stackoverflow.com/questions/4706451/… ). Но это генерирует предупреждение в записной книжке. UserWarning: этот вызов matplotlib.use () не действует, потому что бэкэнд уже выбран; Как это решить?
Доктор Гулу
30
У меня есть один пример: индикаторы выполнения. Эмулятор терминала ноутбука Jupyter не поддерживает расширенные символы управления терминалом, такие как \x1b[A(перемещение вверх), поэтому невозможно распечатать вложенные полосы . Нет проблем с ipywidgets , мы можем использовать собственные виджеты Jupyter для отображения индикаторов выполнения. Но тогда у нас есть два разных средства отображения индикатора выполнения, и приложение может захотеть узнать, что такое среда отображения, чтобы адаптировать и распечатать совместимый индикатор.
gaborous 07
2
например, я хочу, чтобы конфигурация IPython всегда запускалась, %matplotlib inlineкогда она работает как ноутбук, но не в терминале, поскольку это не требуется.
Ciprian Tomoiagă
3
Хотя это совершенно обоснованное мнение, этот ответ не отвечает на вопрос. Как бы вы этого ни хотели, иначе всегда будут различия в поведении, которые могут иметь значение.
Кристофер Барбер
69

Следующее сработало для моих нужд:

get_ipython().__class__.__name__

Он возвращается 'TerminalInteractiveShell'на терминал IPython, 'ZMQInteractiveShell'на Jupyter (ноутбук И qtconsole) и терпит неудачу ( NameError) на обычном интерпретаторе Python. Методget_python()Кажется, что доступен в глобальном пространстве имен по умолчанию при запуске IPython.

Обернув его простой функцией:

def isnotebook():
    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

Вышеупомянутое было протестировано с Python 3.5.2, IPython 5.1.0 и Jupyter 4.2.1 на macOS 10.12 и Ubuntu 14.04.4 LTS.

Густаво Безерра
источник
5
On jupyter console, к сожалению, get_ipython()возвращает экземпляр ZMQInteractiveShellтакже
Джош Боде
7
Если кто-то интересуется, работает ли ноутбук в Google Colab, вы можете проверить это:get_ipython().__class__.__module__ == "google.colab._shell"
guiferviz
3
Это работает только для кода в записной книжке. Не работает, если функция находится в импортированном пакете.
Кристофер Барбер
4
@ChristopherBarber Это не то, что я вижу. Если я вставлю эту функцию в файл, а test.pyзатем запустил from test import isnotebook; print(isnotebook())в блокноте Jupyter, она распечатается True. (Проверено на сервере Notebook версий 5.2.1 и 6.0.1.)
Марк Амери
Я думал, что был какой-то случай, который у меня не сработал, но, к сожалению, не помню подробностей. Возможно, это уже не проблема, а может я просто запуталась.
Кристофер Барбер,
41

Чтобы проверить, находитесь ли вы в записной книжке, что может быть важно, например, при определении того, какой тип индикатора выполнения использовать, это сработало для меня:

def in_ipynb():
    try:
        cfg = get_ipython().config 
        if cfg['IPKernelApp']['parent_appname'] == 'ipython-notebook':
            return True
        else:
            return False
    except NameError:
        return False
кефлавич
источник
6
В моем IPython-Notebook (IPython версии 3.1) cfg['IPKernelApp']['parent_appname']есть a IPython.config.loader.LazyConfigValue, который не сравнивается Trueс"iypthon-notebook"
Dux
5
@juanjux get_ipython возвращает IPython.kernel.zmq.zmqshell.ZMQInteractiveShellэкземпляр в ipynb (Jupyter) и IPython.terminal.interactiveshell.TerminalInteractiveShellREPL терминала, если вам нужно различать ноутбуки и терминал / консоли (что влияет на построение графика).
hobs
4
^ поэтому вы можете заменить внутреннюю часть tryблока на:return str(type(get_ipython())) == "<class 'ipykernel.zmqshell.ZMQInteractiveShell'>"
user2561747
Как и @Dux, у меня это не работает; он всегда возвращает false, даже в Notebook. Подозреваю, что этот ответ устарел с введением какой-то ленивой системы загрузки конфигурации.
Марк Эмери
Также обратите внимание, что ваша конфигурация может вернуться как пустой dict, и в этом случае вам нужно будет добавить KeyError в блок except. Однако, вероятно, лучше использовать код, основанный на ответе Густаво Безерры. Несмотря на то, что я получаю пустую конфигурацию, я получаю shell='PyDevTerminalInteractiveShell'при проверке имени класса.
hlongmore
28

Вы можете проверить, находится ли Python в интерактивном режиме, с помощью следующего фрагмента [1] :

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
    return True
Тилль Хоффманн
источник
python -c "def is_interactive ():> import main as main> return not hasattr (main, ' file ')> print is_interactive ()" True
marscher
3
is_interactive()не делает различий между ноутбуком и консолью.
krock 06
1
Еще одно предостережение: выдача a %runиз ipython не является интерактивной. Вы можете возразить, что так и должно быть, но это все равно ошибка.
dirkjot
Для других прототипов в ноутбуке может быть полезен вариант подхода Тилля, представленный здесь .
Уэйн
Вторая половина этого ответа полезна, но первая половина (о is_interactive) мне кажется в основном неуместной для вопроса. Это тоже сомнительной правильности; как указывает @marscher, он считает все, что запущено, python -cкак находящееся в «интерактивном» режиме, даже если это не так. Я не хочу делать это сам, так как это не мой ответ, но я думаю, что это можно было бы улучшить, просто удалив всю первую половину ответа.
Марк Эмери
17

Недавно я столкнулся с ошибкой в ​​записной книжке Jupyter, которая требует обходного пути, и я хотел сделать это без потери функциональности в других оболочках. Я понял, что решение keflavich в данном случае не работает, потому что get_ipython()оно доступно только напрямую с ноутбука, а не из импортных модулей. Итак, я нашел способ определить из моего модуля, импортирован и используется ли он из записной книжки 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

Комментарии приветствуются, если это достаточно надежно.

Аналогичным образом можно получить некоторую информацию о клиенте, а также о версии 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']
Deeenes
источник
Хм, я использую Fedora 23 Jupyter, и там 'Ipython' in sys.modulesоценивается как False. Возможно вы имеете в виду 'IPython' in sys.modules? Это Trueв моей среде Jupyter. В sys.modulesсловаре тоже нет 'ipykernel'ключа - при работе внутри записной книжки.
maxschlepzig
2
ИМО, это пока лучший ответ. Коротко и мило.
Дэниелпкокс,
3

Протестировано на Python 3.7.3

Реализации CPython имеют имя, __builtins__доступное как часть их глобальных переменных, которые, кстати,. можно получить с помощью функции globals ().
Если скрипт выполняется в среде Ipython, он __IPYTHON__должен быть атрибутом __builtins__.
Поэтому приведенный ниже код возвращается, Trueесли выполняется под Ipython, иначе он даетFalse

hasattr(__builtins__,'__IPYTHON__')
Роберт Новак
источник
2

Следующее описывает случаи 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
Боб Вайгель
источник
Для меня это просто показывает jupyter, является ли это jupyter console, jupyter qtconsoleили jupyter notebook.
Люк Дэвис
2

Я бы рекомендовал избегать обнаружения определенных интерфейсов, потому что их слишком много . Вместо этого вы можете просто проверить, работаете ли вы из среды iPython:

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

Вышеупомянутое будет возвращено, Falseесли вы вызываете running_from_ipythonиз обычной командной строки python. Когда вы вызываете его из Jupyter Notebook, JupyterHub, оболочки iPython, Google Colab и т. Д., Он вернется True.

Шитал Шах
источник
У меня не работает - когда я пробую это в Jupyter Notebook на Ubuntu с Python3, get_ipython()возвращается <ipykernel.zmqshell.ZMQInteractiveShell at 0x7f750ba94320>.
главный герой
Проблема с этим подходом заключается в том, что он не решает вопрос OP: «Как я могу проверить из моего кода Python, работает ли он в записной книжке IPython ?» (курсив мой). Оболочка IPython - это не записная книжка, но когда я запускаю ее в консоли Python в PyCharm, я get_ipython() is not Noneвозвращаюсь True.
hlongmore
хм, как я могу определить, работаю ли я на jupyter vs. voila?
пн,
2

Все, что вам нужно сделать, это поместить эти две ячейки в начало вашей записной книжки:

Ячейка 1: (помечена как «код»):

is_notebook = True

Ячейка 2: (помечена как "Raw NBConvert"):

is_notebook = False

Первая ячейка будет выполняться всегда, а вторая ячейка будет выполняться только при экспорте записной книжки в виде сценария Python.

Позже вы сможете проверить:

if is_notebook:
    notebook_code()
else:
    script_code()

Надеюсь это поможет.

Хулио Алвес
источник
2

Как насчет чего-то вроде этого:

import sys

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

print(inJupyter);
user431378
источник
1

Насколько я знаю, здесь есть 3 вида ipython, которые использовали ipykernel

  1. ipython qtconsole (для краткости "qtipython")
  2. IPython в spyder (сокращенно «spyder»)
  3. IPython в jupyter notebook (сокращенно jn)

использовать 'spyder' in sys.modules может отличить spyder

но для qtipython и jn трудно отличить причину

у них одинаковая sys.modulesи та же конфигурация IPython:get_ipython().config

Я считаю, что qtipython и jn отличаются:

при первом запуске os.getpid()в оболочке IPython получите номер 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

мой 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

разница между qtipython и jn - это имя json ipython, имя jn jn длиннее, чем имя 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, Jupyter notebook, Qtconsole.py

Ян
источник
0

Я использую Django Shell Plus для запуска IPython, и я хотел сделать «запуск в записной книжке» доступным в качестве значения настроек Django. get_ipython()недоступен при загрузке настроек, поэтому я использую это (что не является пуленепробиваемым, но достаточно хорошо для локальных сред разработки, в которых он используется):

import sys

if '--notebook' in sys.argv:
    ENVIRONMENT = "notebook"
else:
    ENVIRONMENT = "dev"
user31415629
источник
0

Предполагая, что у вас есть контроль над Jupyter Notebook, вы можете:

  1. установить значение среды в ячейке, которая использует это как флаг в вашем коде . Поместите уникальный комментарий в эту ячейку (или во все ячейки, которые вы хотите исключить)

    # exclude_from_export
    % set_env is_jupyter = 1

  2. Экспортируйте записную книжку как скрипт Python для использования в другом контексте. При экспорте будут исключены закомментированные ячейки, а затем и код, задающий значение среды. Примечание: замените your_notebook.ipynb именем вашего фактического файла записной книжки.

    jupyter nbconvert --to сценарий --RegexRemovePreprocessor.patterns = "['^ # exclude_from_export']" your_notebook.ipynb

Это сгенерирует файл, в котором не будет установлен флаг среды jupyter, позволяющий детерминированному выполнению кода, который его использует.

Рон Симс II
источник