ฉันจะตรวจสอบได้อย่างไรว่าโค้ดถูกเรียกใช้ในโน้ตบุ๊ก IPython


89

ฉันมีตัวอย่างรหัส Python ที่ฉันต้องการแชร์ซึ่งควรทำสิ่งที่แตกต่างออกไปหากดำเนินการในเทอร์มินัล Python / IPython หรือในโน้ตบุ๊ก IPython

ฉันจะตรวจสอบจากรหัส Python ของฉันได้อย่างไรว่ามันทำงานในโน้ตบุ๊ก IPython หรือไม่


3
ผมขอแนะนำให้ยอมรับคำตอบ Gustavo Bezerra ของ คำตอบที่ได้รับการยอมรับในปัจจุบันไม่สามารถตอบคำถามได้และคำตอบของ Gustavo คือคำตอบที่ได้คะแนนสูงสุดซึ่งยังคงใช้งานได้ใน Jupyter Notebook เวอร์ชันล่าสุด
Mark Amery

คำตอบ:


8

คำถามคือสิ่งที่คุณต้องการดำเนินการแตกต่างกัน

เราพยายามอย่างดีที่สุดใน 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 () ไม่มีผลเนื่องจากมีการเลือกแบ็กเอนด์แล้ว วิธีแก้ปัญหานี้?
Dr. Goulu

33
ฉันมีตัวอย่างหนึ่ง: แถบความคืบหน้า โปรแกรมจำลองเทอร์มินัลโน้ตบุ๊ก Jupyter ไม่รองรับอักขระควบคุมเทอร์มินัลเพิ่มเติมเช่น\x1b[A(เลื่อนขึ้น) ดังนั้นจึงไม่สามารถพิมพ์แถบที่ซ้อนกันได้ ไม่มีปัญหากับipywidgetsเราสามารถใช้วิดเจ็ต Jupyter ดั้งเดิมเพื่อแสดงแถบความคืบหน้า แต่เรามีวิธีการแสดงแถบความคืบหน้าสองวิธีที่แตกต่างกันและแอปพลิเคชันอาจต้องการทราบว่าสภาพแวดล้อมการแสดงผลคืออะไรเพื่อปรับและพิมพ์แถบที่เข้ากันได้
พูดไม่ออก

2
ตัวอย่างเช่นฉันต้องการตั้งค่าการกำหนดค่า IPython ให้ทำงานเสมอ%matplotlib inlineเมื่อทำหน้าที่เป็นโน้ตบุ๊ก แต่ไม่ใช่ในเทอร์มินัลเนื่องจากไม่จำเป็นต้องใช้
Ciprian Tomoiagă

3
แม้ว่านี่จะเป็นความคิดเห็นที่ถูกต้อง แต่คำตอบนี้ไม่สามารถตอบคำถามที่แท้จริงได้ ไม่ว่าคุณจะปรารถนามากแค่ไหนก็ตามจะมีความแตกต่างในพฤติกรรมที่อาจสำคัญเสมอ
Christopher Barber

72

สิ่งต่อไปนี้เหมาะกับความต้องการของฉัน:

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
ในวันที่jupyter consoleน่าเสียดายที่get_ipython()ส่งคืนอินสแตนซ์ของZMQInteractiveShellด้วย
Josh Bode

7
หากมีผู้สนใจตรวจสอบว่าโน้ตบุ๊กกำลังทำงานบน Google Colab หรือไม่คุณสามารถตรวจสอบสิ่งนี้ได้:get_ipython().__class__.__module__ == "google.colab._shell"
guiferviz

3
ใช้ได้กับโค้ดในโน้ตบุ๊กเท่านั้น จะไม่ทำงานหากฟังก์ชันอยู่ในแพ็คเกจที่นำเข้า
Christopher Barber

4
@ChristopherBarber นั่นไม่ใช่สิ่งที่ฉันเห็น ถ้าฉันวางฟังก์ชันนี้ลงในไฟล์test.pyแล้วเรียกใช้from test import isnotebook; print(isnotebook())ใน Jupyter Notebook มันจะพิมพ์Trueออกมา (ทดสอบบนเซิร์ฟเวอร์ Notebook เวอร์ชัน 5.2.1 และ 6.0.1)
Mark Amery

ฉันคิดว่ามีบางกรณีที่ไม่ได้ผลสำหรับฉัน แต่น่าเสียดายที่ฉันจำรายละเอียดไม่ได้ บางทีมันอาจจะไม่ใช่ปัญหาอีกต่อไปหรือบางทีฉันก็สับสน
Christopher Barber

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.TerminalInteractiveShellในเทอร์มินัล REPL ในกรณีที่คุณต้องการแยกความแตกต่างระหว่างโน้ตบุ๊กและเทอร์มินัล / คอนโซล (ซึ่งมีผลต่อการลงจุด)
เตา

4
^ ดังนั้นคุณสามารถแทนที่ด้านในของtryบล็อกด้วย:return str(type(get_ipython())) == "<class 'ipykernel.zmqshell.ZMQInteractiveShell'>"
user2561747

เหมือน @Dux นี่ใช้ไม่ได้สำหรับฉัน มันจะส่งคืนเท็จเสมอแม้ใน Notebook สงสัยว่าคำตอบนี้ล้าสมัยด้วยการเปิดตัวระบบโหลด config แบบขี้เกียจ
Mark Amery

โปรดทราบว่าการกำหนดค่าของคุณอาจกลับมาเป็นคำสั่งที่ว่างเปล่าซึ่งในกรณีนี้คุณจะต้องเพิ่ม KeyError ลงในบล็อกยกเว้น น่าจะดีกว่าอย่างไรก็ตามหากใช้รหัสตามคำตอบของ Gustavo Bezerra แม้ว่าฉันจะได้รับการกำหนดค่าว่างเปล่า แต่ฉันได้รับshell='PyDevTerminalInteractiveShell'เมื่อตรวจสอบชื่อคลาส
hlongmore

29

คุณสามารถตรวจสอบว่า 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เป็น main> return not hasattr (main, ' file ')> print is_interactive ()" True
marscher

3
is_interactive()ไม่แยกความแตกต่างระหว่างโน้ตบุ๊กและคอนโซล
krock

1
ข้อแม้อีกประการหนึ่งคือการออก%runจาก ipython ไม่โต้ตอบ คุณสามารถโต้แย้งว่ามันควรจะเป็น แต่มันก็ยังคงเป็น gotcha
dirkjot

สำหรับการสร้างต้นแบบอื่น ๆ ในโน้ตบุ๊กรูปแบบของแนวทางของ Till ที่แสดงไว้ที่นี่อาจเป็นประโยชน์
Wayne

ครึ่งหลังของคำตอบนี้มีประโยชน์ แต่สำหรับฉันครึ่งแรก (ประมาณis_interactive) ดูเหมือนจะไม่เกี่ยวข้องกับคำถาม นอกจากนี้ยังมีความถูกต้องที่น่าสงสัย ตามที่ @marscher ชี้ให้เห็นว่ามันนับทุกสิ่งที่ทำงานโดยใช้python -cในโหมด "โต้ตอบ" แม้ว่าจะไม่เป็นความจริงก็ตาม ฉันไม่อยากทำเองเพราะมันไม่ใช่คำตอบของฉัน แต่ฉันคิดว่าสิ่งนี้จะได้รับการปรับปรุงโดยเพียงแค่ลบคำตอบครึ่งแรกทั้งหมด
Mark Amery

17

เมื่อเร็ว ๆ นี้ฉันพบข้อบกพร่องในสมุดบันทึก Jupyterซึ่งต้องการวิธีแก้ปัญหาชั่วคราวและฉันต้องการทำสิ่งนี้โดยไม่สูญเสียฟังก์ชันการทำงานในเชลล์อื่น ๆ ฉันตระหนักว่าโซลูชันของเคฟลาวิชใช้ไม่ได้ในกรณีนี้เนื่องจาก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']

อืมฉันใช้ Fedora 23 Jupyter และมีการ'Ipython' in sys.modulesประเมินFalseว่า บางทีคุณอาจหมายถึง'IPython' in sys.modules? นี่คือTrueสภาพแวดล้อม Jupyter ของฉัน sys.modulesพจนานุกรมยังไม่รวม'ipykernel'ที่สำคัญ - เมื่อทำงานภายในโน๊ตบุ๊ค
maxschlepzig

2
นี่คือคำตอบที่ดีที่สุด IMO สั้นและหวาน
danielpcox

3

ทดสอบสำหรับ python 3.7.3

การใช้งาน CPython มีชื่อ__builtins__เป็นส่วนหนึ่งของ globals ซึ่ง btw สามารถเรียกดูได้โดยฟังก์ชัน 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 โน๊ตบุ๊ค, JupyterHub, IPython เปลือก Google Colab ฯลฯ Trueแล้วก็จะกลับมา


ไม่ทำงานสำหรับฉัน - เมื่อฉันพยายามใน Jupyter โน๊ตบุ๊คบน Ubuntu กับ Python3, ผลตอบแทนget_ipython() <ipykernel.zmqshell.ZMQInteractiveShell at 0x7f750ba94320>
ตัวเอก

ปัญหาของวิธีนี้คือไม่สามารถแก้คำถามของ OP "ฉันจะตรวจสอบจากรหัส Python ของฉันได้อย่างไรว่ามันทำงานในโน้ตบุ๊ก IPython " (เน้นของฉัน) IPython เปลือกไม่ได้โน๊ตบุ๊ค แต่เมื่อฉันทำงานในคอนโซลหลามของฉันใน PyCharm ฉันได้รับกลับมาget_ipython() is not None True
hlongmore

หืมฉันจะตรวจจับได้อย่างไรว่ากำลังวิ่งบน jupyter vs. voila
ntg

2

สิ่งที่คุณต้องทำคือวางเซลล์ทั้งสองนี้ไว้ที่จุดเริ่มต้นของสมุดบันทึกของคุณ:

เซลล์ 1: (ทำเครื่องหมายเป็น "รหัส"):

is_notebook = True

เซลล์ 2: (ทำเครื่องหมายเป็น "Raw NBConvert"):

is_notebook = False

เซลล์แรกจะถูกเรียกใช้งานเสมอ แต่เซลล์ที่สองจะถูกดำเนินการก็ต่อเมื่อคุณส่งออกสมุดบันทึกเป็นสคริปต์ Python

ในภายหลังคุณสามารถตรวจสอบ:

if is_notebook:
    notebook_code()
else:
    script_code()

หวังว่านี่จะช่วยได้



1

เท่าที่ฉันรู้ที่นี่มี 3 ประเภทของ ipython ที่ใช้ ipykernel

  1. ipython qtconsole ("qtipython" สั้น ๆ )
  2. IPython ใน spyder (เรียกสั้น ๆ ว่า "spyder")
  3. IPython ในสมุดบันทึก jupyter ("jn" สำหรับตัวย่อ)

การใช้งาน'spyder' in sys.modulesสามารถแยกแยะสปายเดอร์

แต่สำหรับ qtipython และ jn นั้นยากที่จะแยกแยะสาเหตุ

พวกเขามีการกำหนดค่าsys.modulesIPython ที่เหมือนกันและเหมือนกัน: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 ชื่อ json ของ 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, Qtconsole.py


0

ฉันใช้ Django Shell Plus เพื่อเปิดใช้งาน IPython และฉันต้องการให้ 'ทำงานในโน้ตบุ๊ก' เป็นค่าการตั้งค่า Django get_ipython()ไม่สามารถใช้งานได้เมื่อโหลดการตั้งค่าดังนั้นฉันจึงใช้สิ่งนี้ (ซึ่งไม่สามารถกันกระสุน แต่ดีพอสำหรับสภาพแวดล้อมการพัฒนาท้องถิ่นที่ใช้):

import sys

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

0

สมมติว่าคุณมีการควบคุม Jupyter Notebook คุณสามารถ:

  1. ตั้งค่าสภาพแวดล้อมในเซลล์ที่ใช้นี้เป็นเป็นธงในรหัสของคุณ ใส่ความคิดเห็นที่ไม่ซ้ำกันในเซลล์นั้น (หรือเซลล์ทั้งหมดที่คุณต้องการยกเว้น)

    # exclude_from_export
    % set_env is_jupyter = 1

  2. ส่งออกสมุดบันทึกเป็นสคริปต์ python เพื่อใช้ในบริบทอื่น การส่งออกจะไม่รวมเซลล์ที่แสดงความคิดเห็นและตามมาด้วยรหัสที่กำหนดค่าสภาพแวดล้อม หมายเหตุ: แทนที่your_notebook.ipynbด้วยชื่อไฟล์สมุดบันทึกจริงของคุณ

    jupyter nbconvert - เป็นสคริปต์ --RegexRemovePreprocessor.patterns = "['^ # exclude_from_export']" your_notebook.ipynb

สิ่งนี้จะสร้างไฟล์ที่จะไม่มีการตั้งค่าสถานะสภาพแวดล้อม jupyter เพื่อให้โค้ดที่ใช้มันดำเนินการอย่างกำหนด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.