概要
上記の投稿記事で作成したCustom Magicの機能を専用のkernelを作成して実現しようとする投稿記事です。なので、上記の記事の内容を知っているものとして書きます。さらに、ソースコードも流用します。
kernelを作ったとしても使い方は上記の記事とほぼ同じです。違いはmagicの表し方とmagicを指定していない時の動作です。
なら、何のためにわざわざkernelを作るかというと、Cell内でのコードのハイライトがC/C++に出来て入力時のインデントもC/C++になり、 {}
のペアでインデントされます。
後は、kernel作成の勉強にもなりました。
実行環境
> sw_vers
ProductName: macOS
ProductVersion: 15.4.1
BuildVersion: 24E263
-- 以下 anaconda3-2024.10-1 ----
> python --version
Python 3.12.7
> jupyter --version
Selected Jupyter core packages...
IPython : 8.27.0
ipykernel : 6.28.0
ipywidgets : 7.8.1
jupyter_client : 8.6.0
jupyter_core : 5.7.2
jupyter_server : 2.14.1
jupyterlab : 4.2.5
nbclient : 0.8.0
nbconvert : 7.16.4
nbformat : 5.10.4
notebook : 7.2.2
qtconsole : 5.5.1
traitlets : 5.14.3
テストはJupyterLab
で行います。
Custom Kernelを作ろう
サンプルKernel
上記に記載されているechokernel.py
をそのまま実装します。
from ipykernel.kernelbase import Kernel
class EchoKernel(Kernel):
implementation = 'Echo'
implementation_version = '1.0'
language = 'no-op'
language_version = '0.1'
language_info = {'mimetype': 'text/plain'}
banner = "Echo kernel - as useful as a parrot"
def do_execute(self, code, silent, store_history=True, user_expressions=None,
allow_stdin=False):
if not silent:
stream_content = {'name': 'stdout', 'text': code}
self.send_response(self.iopub_socket, 'stream', stream_content)
return {'status': 'ok',
# The base class increments the execution count
'execution_count': self.execution_count,
'payload': [],
'user_expressions': {},
}
if __name__ == '__main__':
from ipykernel.kernelapp import IPKernelApp
IPKernelApp.launch_instance(kernel_class=EchoKernel)
cellの内容はdo_execute
のcode
に渡されます。
ブラウザの出力するには、基本的にstream_content
の内容を作って、send_response
するだけのようです。
kernel.json
kernelの起動等に必要な情報であるkernel.json
を以下のように作成します。
{
"argv":["python","-m","echokernel", "-f", "{connection_file}"],
"display_name":"Echo",
"env": {
"PYTHONPATH": "/Users/xxxx/qiita/kernel"
}
}
-
echokernel
は.py
無しで指定 -
display_name
がJupyter notebookにkernel名として表示 -
PYTHONPATH
はechokernel.py
がある場所にパスが通っていないときに必要- パスが通っていれば不要
echo
ディレクトリ(名前は何でも良い)を作ってkernel.json
をその下に移します。
echo/
└── kernel.json
kernelをインストールします。
> jupyter kernelspec install echo --user
[InstallKernelSpec] Installed kernelspec echo in /Users/xxxx/Library/Jupyter/kernels/echo
install
の後はディレクトリ名(echo
)を指定します。
--user
を付けた時のkernel
のインストール先は次のディレクトリです。
- Linux
- ~/.local/share/jupyter/kernels
- Max
- ~/Library/Jupyter/kernels
- Windows
- %APPDATA%\jupyter\kernels
Pythonやその他のpackageの組み合わせによっては以下のメッセージが出ますが動きます。
私が今現在使っている環境ではこれらのメッセージは出ません。
/Users/xxxx/qiita/kernel/echokernel.py:1: DeprecationWarning: Parsing dates
involving a day of month without a year specified is ambiguious
and fails to parse leap day. The default behavior will change in Python 3.15
to either always raise an exception or to use a different default year (TBD).
To avoid trouble, add a specific year to the input & format.
See https://github.com/python/cpython/issues/70647.
from ipykernel.kernelbase import Kernel
~~省略~~
[IPKernelApp] WARNING | Unknown message type: 'comm_open'
[IPKernelApp] WARNING | Unknown message type: 'comm_msg'
テストをします。 kernel名が右上にちゃんと表示されてます。
Costuom KernelなのでPythonのkernelで使えた %lsmagic、%%script、Custom magicなどのmagicコマンドは一切使えません
Jupyter console
ではプログラムのbanner
で設定した内容がトップに表示されています。しかし、Jupyter console
は使うことはないと思います。
gcc Kernel
簡単なgcc Kernel
上記のサンプルを元にgcc
でコンパイル&実行だけの簡単なkernelをつくります。前回の投稿記事のgcc
でコンパイル&実行部分のコードはCustom magic
とほぼ同じコードになります。
後はstderr
がサンプルに無いコードです。
import os
import sys
import subprocess
from ipykernel.kernelbase import Kernel
OBJFILE = "./a.out"
COMPILE_EXEC_CMD = "gcc -xc - -o {0} && {0}".format(OBJFILE)
class GccKernel(Kernel):
implementation = 'gcc'
implementation_version = '1.0'
language_info = {
'name': 'c',
'mimetype': 'text/x-csrc',
'file_extension': '.c',
'version': '17.0.0',
}
banner = "Compile by gcc and execute binary"
def do_execute(self, code, silent, store_history=True, user_expressions=None,
allow_stdin=False):
if not silent:
rc = self.compile_and_exec(code)
stream_content = {'name': 'stdout', 'text': rc.stdout}
self.send_response(self.iopub_socket, 'stream', stream_content)
if rc.stderr != "":
stream_content = {'name': 'stderr', 'text': rc.stderr}
self.send_response(self.iopub_socket, 'stream', stream_content)
return {'status': 'ok',
'execution_count': self.execution_count,
'payload': [],
'user_expressions': {},
}
def compile_and_exec(self, src):
rc = subprocess.run(COMPILE_EXEC_CMD, text=True,
capture_output=True, shell=True, input=src)
if os.path.isfile(OBJFILE):
os.remove(OBJFILE)
return rc
if __name__ == '__main__':
from ipykernel.kernelapp import IPKernelApp
IPKernelApp.launch_instance(kernel_class=GccKernel)
implementation
からbanner
については以下のサイトを参照してください。
また、language_info
のname
、mimetype
、file_extension
については以下のサイトを参照してください。これが一致していない(特にname
)とハイライトなどがC言語になりません。他のいろいろプログラミング言語についても載っています。
kernel.json
gcc
用のkernel.json
を作ります。
language
はc
にします。C++
のときはC++
にしてください。もしかしたら、無くても良いかもしれません。これよりはプログラムのlanguage_info
のname
が大事です。
{
"argv":["python","-m","gcckernel", "-f", "{connection_file}"],
"display_name":"gcc",
"language": "c",
"env": {
"PYTHONPATH": "/Users/xxxx/qiita/kernel"
}
}
gcc
ディレクトリを作ってkernel.json
をその下に移します。
gcc/
└── kernel.json
kernelをインストールします。
jupyter kernelspec install gcc --user
[InstallKernelSpec] Installed kernelspec gcc in /Users/xxxx/Library/Jupyter/kernels/gcc
テストです。
コンパイルエラーと実行結果がちゃんと表示されています。また、ハイライトがC言語になっています。cell magicでは#include
がpythonのコメントに該当するのでイタリック体になっていましたが、ちゃんと普通に表示されています。
Custom Magicと同等の機能をもったgcc Kernel
今度はコンパイラをg++
にしますが、gcc
とそんなに違いはありません。
また、Custom Magicで作成したclass GCCMagics
を一部変更して流用します。
magicの表現を%%gcc
から//gcc
と%
を/
に変更します。
ソースを前半と後半部分に分けて解説します。2つを一緒のソースにして使ってください。
import re
import sys
import subprocess
from ipykernel.kernelbase import Kernel
OBJFILE = "./a.out"
COMPILE_CMD = "g++ -xc++ - -std=c++17"
COMPILE_EXEC_CMD = "{0} -o {1} && {1}".format(COMPILE_CMD, OBJFILE)
ALL_SOURCE = """
#include <iostream>
{0}
int main(int argc, char *argv[]) {{
{1}
return 0;
}}
"""
def CompletedProcess(stdout='', stderr=''):
return subprocess.CompletedProcess("", 0, stdout=stdout, stderr=stderr)
class GCCMagics:
main_source = {} # main関数内のソース
other_source = {} # main関数外のソース
def gcc(self, line, cell):
rc = self.compile_and_exec(cell)
return rc
def gmain(self, line, cell):
src_key = line.strip()
if src_key != "":
self.main_source[src_key] = cell
rc = self.compile_and_exec(self.make_source())
else:
rc = self.compile_and_exec(ALL_SOURCE.format("", cell))
return rc
def gfunc(self, line, cell):
src_key = line.strip()
if src_key == "":
rc = CompletedProcess(stderr="lineパラメータがありません")
else:
self.other_source[src_key] = cell
rc = self.compile_and_exec(self.make_source(), exec=False)
return rc
def glist(self, line):
return CompletedProcess(stdout=self.make_source())
def grun(self, line):
return self.compile_and_exec(self.make_source())
def make_source(self, src_key=None):
other_src = ""
main_src = ""
for key in sorted(self.other_source):
other_src += self.other_source[key]
for key in sorted(self.main_source):
main_src += self.main_source[key]
src = ALL_SOURCE.format(other_src, main_src)
return src
def compile_and_exec(self, src, exec=True):
if exec:
rc = subprocess.run(COMPILE_EXEC_CMD, text=True, capture_output=True, shell=True, input=src)
else:
rc = subprocess.run(COMPILE_CMD, text=True, capture_output=True, shell=True, input=src)
if os.path.isfile(OBJFILE):
os.remove(OBJFILE)
return rc
上記のコードの解説
-
def CompletedProcess
はsubprocess.run
が返す戻り値のクラスであるので、subprocess.run
を使わない処理でもstdout
やstderr
を返すためと、簡略化のための関数 -
GCCMagics
クラスはCustom Magic
で作成したクラスを流用したもので以下の主な変更点-
Magics
の継承をしない -
@cell_magic
などのデコレーションやDocstring
を消す -
print
文をなくしてrc
などのCompletedProcess
にstdout
,stderr
を設定してretrun
で返すように変更
-
実はGCCMagicsを全く変更しなくても(デコレーションを消さない)使えますが、Custom Magicには依存していないと示したかったので消しました。そのまま使ったコードはAppendixに記載します。
# GCC用Custom Kernel
class GccKernel(Kernel):
# cellデータの解析結果
class MagicData:
def __init__(self, magic="gcc", cell="", line="", iscell=True):
self.magic = magic
self.cell = cell
self.line = line
self.iscell = iscell
implementation = 'gcc'
implementation_version = '1.0'
language_info = {
'name': 'c++',
'mimetype': 'text/x-c++src',
'file_extension': '.cpp',
'version': '17.0.0',
}
banner = "Compile by gcc and execute binary"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
magic_obj = GCCMagics()
self.line_magics = {"glist": magic_obj.glist,
"grun": magic_obj.grun}
self.cell_magics = {"gcc": magic_obj.gcc,
"gmain": magic_obj.gmain,
"gfunc": magic_obj.gfunc}
self.magic_obj = magic_obj
def parse(self, code):
mline = re.match("^.*$", code, re.MULTILINE)
pos = mline.span()[1] + 1 # +1は改行コードを除くため
cell = code[pos:]
line = mline.group()
magic_re = re.match(r" *(/{1,2})(\w+)", line)
iscell = True
if magic_re == None:
# 先頭行がmagicでないとき
magic = "gcc"
cell = code
line = ''
else:
line_pos = magic_re.span()[1] + 1
line = line[line_pos:]
magic_names = magic_re.groups()
magic = magic_names[1]
if magic_names[0] == "/":
iscell = False
return self.MagicData(magic, cell, line, iscell)
def exec_magic(self, magic_data):
if magic_data.iscell:
if magic_data.magic in self.cell_magics:
func = self.cell_magics[magic_data.magic]
return func(magic_data.line, magic_data.cell)
else:
return CompletedProcess(stderr="該当するcell magicはありません")
else:
if magic_data.magic in self.line_magics:
func = self.line_magics[magic_data.magic]
return func(magic_data.line)
else:
return CompletedProcess(stderr="該当するline magicはありません")
def do_execute(self, code, silent, store_history=True, user_expressions=None,
allow_stdin=False):
if not silent:
src = code if code[-1] == "\n" else code + "\n"
magic_data = self.parse(src)
rc = self.exec_magic(magic_data)
stream_content = {'name': 'stdout', 'text': rc.stdout}
self.send_response(self.iopub_socket, 'stream', stream_content)
if rc.stderr != "":
stream_content = {'name': 'stderr', 'text': rc.stderr}
self.send_response(self.iopub_socket, 'stream', stream_content)
return {'status': 'ok',
'execution_count': self.execution_count,
'payload': [],
'user_expressions': {},
}
if __name__ == '__main__':
from ipykernel.kernelapp import IPKernelApp
IPKernelApp.launch_instance(kernel_class=GccKernel)
上記コードの解説
-
def __init__
はクラスGCCMagicsで定義されているline magic、cell magicの処理メソッドの辞書を作る -
def parse
はcellのデータから1行目をmagicとして解析する- 解析した結果は以下の項目をMagicDataクラスで返す
- magic名
- cellデータ
- lineデータ
- line magicかcell magicのフラグ
-
re.match("^.*$", code, re.MULTILINE)
は1行目を取り出すために正規表現 -
re.match(r" *(/{1,2})(\w+)", line)
は/
や//
で始まるmagic名を取り出すための正規表現- groups()で取り出すと例えば
'//gcc arg1 arg2'
は('//', 'gcc')
のタプルになるのでgroups()[1]
がmagic名になる
- groups()で取り出すと例えば
- 解析した結果は以下の項目をMagicDataクラスで返す
-
def exec_magic
はdef parse
解析したデータをもとにGCCMagics
クラスの各magicメソッドを実行- 解析したmagicがline magicかcell magicによって対応する辞書から処理メソッドを取得し、処理を実行
- 対応する処理メソッドが無いときはエラーメッセージを表示
kernel.json
gcc
用でC++のkernel.json
を作ります。
{
"argv":["python","-m","gcckernel", "-f", "{connection_file}"],
"display_name":"C++",
"language": "c++",
"env": {
"PYTHONPATH": "/Users/xxxx/qiita/kernel"
}
}
gcc
ディレクトリを作ってkernel.json
をその下に移します。
gcc/
└── kernel.json
kernelをインストールします。
jupyter kernelspec install gcc --user
[InstallKernelSpec] Installed kernelspec gcc in /Users/xxxx/Library/Jupyter/kernels/gcc
テストも下記の通り大丈夫なようです。
終わりに
全部でCell Magicの3部作となりましたが、Pythonはメイン言語ではないので時間が掛かったところもあります。しかし、知らないことを学びそれなりの成果がでると嬉しいものです。自己満足ですが。
Appendix
自分ために以下の2つのコードを残します。
GCCMagicsをそのまま使ったコード
-
print
は使えないので自分で再定義 - line magic、cell magicのメソッド名とメソッドオブジェクトの辞書は
GCCMagics
のmagics
変数に登録されているので、自分で辞書を作らなくてもOK-
@cell_magic
や@line_magic
とデコレーションすることで、辞書が作成されるようです
-
Custom Magicとしては使っていないので、親クラスのMagics
やデコレータが何も悪さしないと良いのですが、とにかく動きました。
ソースコード1
import os
import re
import sys
import subprocess
from ipykernel.kernelbase import Kernel
from IPython.core.magic import (Magics, magics_class, line_magic,
cell_magic, line_cell_magic)
OBJFILE = "./a.out"
COMPILE_CMD = "g++ -xc++ - -std=c++17"
COMPILE_EXEC_CMD = "{0} -o {1} && {1}".format(COMPILE_CMD, OBJFILE)
ALL_SOURCE = """
#include <iostream>
{0}
int main(int argc, char *argv[]) {{
{1}
return 0;
}}
"""
def CompletedProcess(stdout='', stderr=''):
return subprocess.CompletedProcess("", 0, stdout=stdout, stderr=stderr)
GccKernel_obj = None
def print(str, file=None, end="\n"):
if file == sys.stderr:
stream_content = {'name': 'stderr', 'text': str+end}
else:
stream_content = {'name': 'stdout', 'text': str+end}
GccKernel_obj.send_response(GccKernel_obj.iopub_socket,
'stream', stream_content)
@magics_class
class GCCMagics(Magics):
main_source = {} # main関数内のソース
other_source = {} # main関数外のソース
@cell_magic
def gcc(self, line, cell):
"""
%%gcc コメント
"""
rc = self.compile_and_exec(cell)
print(rc.stdout, end="")
if rc.stderr != "":
print(rc.stderr, file=sys.stderr, end="")
@cell_magic
def gmain(self, line, cell):
"""
%%gmain コメント
"""
src_key = line.strip()
if src_key != "":
self.main_source[src_key] = cell
rc = self.compile_and_exec(self.make_source())
else:
rc = self.compile_and_exec(ALL_SOURCE.format("", cell))
print(rc.stdout, end="")
if rc.stderr != "":
print(rc.stderr, file=sys.stderr, end="")
@cell_magic
def gfunc(self, line, cell):
"""
%%gfunc コメント
"""
src_key = line.strip()
if src_key == "":
print("lineパラメータがありません", file=sys.stderr)
return
else:
self.other_source[src_key] = cell
rc = self.compile_and_exec(self.make_source(), exec=False)
print(rc.stdout, end="")
if rc.stderr != "":
print(rc.stderr, file=sys.stderr, end="")
@line_magic
def glist(self, line):
"""
%glist コメント
"""
print(self.make_source(), end="")
@line_magic
def grun(self, line):
"""
%grun コメント
"""
rc = self.compile_and_exec(self.make_source())
print(rc.stdout, end="")
if rc.stderr != "":
print(rc.stderr, file=sys.stderr, end="")
def make_source(self, src_key=None):
other_src = ""
main_src = ""
for key in sorted(self.other_source):
other_src += self.other_source[key]
for key in sorted(self.main_source):
main_src += self.main_source[key]
src = ALL_SOURCE.format(other_src, main_src)
return src
def compile_and_exec(self, src, exec=True):
if exec:
rc = subprocess.run(COMPILE_EXEC_CMD, text=True, capture_output=True, shell=True, input=src)
else:
rc = subprocess.run(COMPILE_CMD, text=True, capture_output=True, shell=True, input=src)
if os.path.isfile(OBJFILE):
os.remove(OBJFILE)
return rc
# GCC用Custom Kernel
class GccKernel(Kernel):
# cellデータの解析結果
class MagicData:
def __init__(self, magic="gcc", cell="", line="", iscell=True):
self.magic = magic
self.cell = cell
self.line = line
self.iscell = iscell
implementation = 'gcc'
implementation_version = '1.0'
language_info = {
'name': 'c++',
'mimetype': 'text/x-c++src',
'file_extension': '.cpp',
'version': '17.0.0',
}
banner = "Compile by gcc and execute binary"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.magic_obj = GCCMagics()
self.line_magics = self.magic_obj.magics["line"]
self.cell_magics = self.magic_obj.magics["cell"]
global GccKernel_obj
GccKernel_obj = self
def parse(self, code):
mline = re.match("^.*$", code, re.MULTILINE)
pos = mline.span()[1] + 1 # +1は改行コードを除くため
cell = code[pos:]
line = mline.group()
magic_re = re.match(r" *(/{1,2})(\w+)", line)
iscell = True
if magic_re == None:
# 先頭行がmagicでないとき
magic = "gcc"
cell = code
line = ''
else:
line_pos = magic_re.span()[1] + 1
line = line[line_pos:]
magic_names = magic_re.groups()
magic = magic_names[1]
if magic_names[0] == "/":
iscell = False
return self.MagicData(magic, cell, line, iscell)
def exec_magic(self, magic_data):
if magic_data.iscell:
if magic_data.magic in self.cell_magics:
func = self.cell_magics[magic_data.magic]
return func(magic_data.line, magic_data.cell)
else:
return CompletedProcess(stderr="該当するcell magicはありません")
else:
if magic_data.magic in self.line_magics:
func = self.line_magics[magic_data.magic]
return func(magic_data.line)
else:
return CompletedProcess(stderr="該当するline magicはありません")
def do_execute(self, code, silent, store_history=True, user_expressions=None,
allow_stdin=False):
if not silent:
src = code if code[-1] == "\n" else code + "\n"
magic_data = self.parse(src)
rc = self.exec_magic(magic_data)
if rc != None:
stream_content = {'name': 'stdout', 'text': rc.stdout}
self.send_response(self.iopub_socket, 'stream', stream_content)
if rc.stderr != "":
stream_content = {'name': 'stderr', 'text': rc.stderr}
self.send_response(self.iopub_socket, 'stream', stream_content)
return {'status': 'ok',
'execution_count': self.execution_count,
'payload': [],
'user_expressions': {},
}
if __name__ == '__main__':
from ipykernel.kernelapp import IPKernelApp
IPKernelApp.launch_instance(kernel_class=GccKernel)
GCCMagicsをそのまま使うが、親クラスやデコレータを自分で作ってみる
gccをでコンパイルする目的としてはこれを作るメリットがあまりないが、デコレーションの勉強にはなるので作ってみた。デコレーションについては使ったことはあるが、どう動作するかまでは調べなかったので今回調べてみて勉強になった。
ソースコード1からの変更点はPython.core.magic
のimportを削除して##--------
で囲った箇所に自作したデコレータのコードを追加した。
デコレータについて
- クラスのインスタンスを生成しなくても関数のデコレータ、クラスのデコレータの順に呼び出される
- 関数デコレータに渡される
func
はクラスメソッドでオブジェクトメソッドではないのでオブジェクトメソッドとしては使えないのでメソッド名だけ取って、オブジェクトが生成されたときにオブジェクトメソッドとして取得した
その他として、getattr、callableを始めて知った。
ソースコード2
import os
import re
import sys
import subprocess
from ipykernel.kernelbase import Kernel
OBJFILE = "./a.out"
COMPILE_CMD = "g++ -xc++ - -std=c++17"
COMPILE_EXEC_CMD = "{0} -o {1} && {1}".format(COMPILE_CMD, OBJFILE)
ALL_SOURCE = """
#include <iostream>
{0}
int main(int argc, char *argv[]) {{
{1}
return 0;
}}
"""
##----------------------------------------------------------------------------------
g_line_magics = []
g_cell_magics = []
def cell_magic(func):
g_cell_magics.append(func.__name__)
def _wrapper(self,*args, **kwargs):
return func(self,*args,**kwargs)
return _wrapper
def line_magic(func):
g_line_magics.append(func.__name__)
def _wrapper(self,*args, **kwargs):
return func(self,*args,**kwargs)
return _wrapper
class Magics:
pass
def magics_class(cls):
class NewClass(cls):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
cell_magics = {}
line_magics = {}
for fname in g_cell_magics:
func = getattr(self, fname, False)
if callable(func):
cell_magics[fname] = func
for fname in g_line_magics:
func = getattr(self, fname, False)
if callable(func):
line_magics[fname] = func
self.magics = {'cell': cell_magics, 'line': line_magics}
return NewClass
##----------------------------------------------------------------------------------
def CompletedProcess(stdout='', stderr=''):
return subprocess.CompletedProcess("", 0, stdout=stdout, stderr=stderr)
GccKernel_obj = None
def print(str, file=None, end="\n"):
if file == sys.stderr:
stream_content = {'name': 'stderr', 'text': str+end}
else:
stream_content = {'name': 'stdout', 'text': str+end}
GccKernel_obj.send_response(GccKernel_obj.iopub_socket,
'stream', stream_content)
@magics_class
class GCCMagics(Magics):
main_source = {} # main関数内のソース
other_source = {} # main関数外のソース
@cell_magic
def gcc(self, line, cell):
"""
%%gcc コメント
"""
rc = self.compile_and_exec(cell)
print(rc.stdout, end="")
if rc.stderr != "":
print(rc.stderr, file=sys.stderr, end="")
@cell_magic
def gmain(self, line, cell):
"""
%%gmain コメント
"""
src_key = line.strip()
if src_key != "":
self.main_source[src_key] = cell
rc = self.compile_and_exec(self.make_source())
else:
rc = self.compile_and_exec(ALL_SOURCE.format("", cell))
print(rc.stdout, end="")
if rc.stderr != "":
print(rc.stderr, file=sys.stderr, end="")
@cell_magic
def gfunc(self, line, cell):
"""
%%gfunc コメント
"""
src_key = line.strip()
if src_key == "":
print("lineパラメータがありません", file=sys.stderr)
return
else:
self.other_source[src_key] = cell
rc = self.compile_and_exec(self.make_source(), exec=False)
print(rc.stdout, end="")
if rc.stderr != "":
print(rc.stderr, file=sys.stderr, end="")
@line_magic
def glist(self, line):
"""
%glist コメント
"""
print(self.make_source(), end="")
@line_magic
def grun(self, line):
"""
%grun コメント
"""
rc = self.compile_and_exec(self.make_source())
print(rc.stdout, end="")
if rc.stderr != "":
print(rc.stderr, file=sys.stderr, end="")
def make_source(self, src_key=None):
other_src = ""
main_src = ""
for key in sorted(self.other_source):
other_src += self.other_source[key]
for key in sorted(self.main_source):
main_src += self.main_source[key]
src = ALL_SOURCE.format(other_src, main_src)
return src
def compile_and_exec(self, src, exec=True):
if exec:
rc = subprocess.run(COMPILE_EXEC_CMD, text=True, capture_output=True, shell=True, input=src)
else:
rc = subprocess.run(COMPILE_CMD, text=True, capture_output=True, shell=True, input=src)
if os.path.isfile(OBJFILE):
os.remove(OBJFILE)
return rc
# GCC用Custom Kernel
class GccKernel(Kernel):
# cellデータの解析結果
class MagicData:
def __init__(self, magic="gcc", cell="", line="", iscell=True):
self.magic = magic
self.cell = cell
self.line = line
self.iscell = iscell
implementation = 'gcc'
implementation_version = '1.0'
language_info = {
'name': 'c++',
'mimetype': 'text/x-c++src',
'file_extension': '.cpp',
'version': '17.0.0',
}
banner = "Compile by gcc and execute binary"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.magic_obj = GCCMagics()
self.line_magics = self.magic_obj.magics["line"]
self.cell_magics = self.magic_obj.magics["cell"]
global GccKernel_obj
GccKernel_obj = self
def parse(self, code):
mline = re.match("^.*$", code, re.MULTILINE)
pos = mline.span()[1] + 1 # +1は改行コードを除くため
cell = code[pos:]
line = mline.group()
magic_re = re.match(r" *(/{1,2})(\w+)", line)
iscell = True
if magic_re == None:
# 先頭行がmagicでないとき
magic = "gcc"
cell = code
line = ''
else:
line_pos = magic_re.span()[1] + 1
line = line[line_pos:]
magic_names = magic_re.groups()
magic = magic_names[1]
if magic_names[0] == "/":
iscell = False
return self.MagicData(magic, cell, line, iscell)
def exec_magic(self, magic_data):
if magic_data.iscell:
if magic_data.magic in self.cell_magics:
func = self.cell_magics[magic_data.magic]
return func(magic_data.line, magic_data.cell)
else:
return CompletedProcess(stderr="該当するcell magicはありません")
else:
if magic_data.magic in self.line_magics:
func = self.line_magics[magic_data.magic]
return func(magic_data.line)
else:
return CompletedProcess(stderr="該当するline magicはありません")
def do_execute(self, code, silent, store_history=True, user_expressions=None,
allow_stdin=False):
if not silent:
src = code if code[-1] == "\n" else code + "\n"
magic_data = self.parse(src)
rc = self.exec_magic(magic_data)
if rc != None:
stream_content = {'name': 'stdout', 'text': rc.stdout}
self.send_response(self.iopub_socket, 'stream', stream_content)
if rc.stderr != "":
stream_content = {'name': 'stderr', 'text': rc.stderr}
self.send_response(self.iopub_socket, 'stream', stream_content)
return {'status': 'ok',
'execution_count': self.execution_count,
'payload': [],
'user_expressions': {},
}
if __name__ == '__main__':
from ipykernel.kernelapp import IPKernelApp
IPKernelApp.launch_instance(kernel_class=GccKernel)