環境
- Python3.10.2
- bokeh 2.4.2
起きたこと
ColumnDataSource
インスタンスを参照しているFigure
を、複数のHTMLファイルに出力しようとすると、RuntimeError: Models must be owned by only a single document, ColumnDataSource(id='1002', ...) is already in a doc
というエラーが発生しました。
from bokeh.plotting import figure, output_file, save
from bokeh.models import ColumnDataSource
source=ColumnDataSource({"x":[1,2],"y":[1,4]})
print(f"{source=}")
p1 = figure()
print(f"{p1=}")
p1.line(source=source, x="x", y="y")
output_file(filename="p1.html")
save(p1)
p2 = figure()
print(f"{p2=}")
p2.line(source=source, x="x", y="y")
output_file(filename="p2.html")
save(p2)
$ python foo.py
source=ColumnDataSource(id='1002', ...)
p1=Figure(id='1003', ...)
p2=Figure(id='1184', ...)
Traceback (most recent call last):
File "/home/vagrant/Documents/study/python/foo.py", line 16, in <module>
save(p2)
File "/home/vagrant/.pyenv/versions/3.10.2/lib/python3.10/site-packages/bokeh/io/saving.py", line 98, in save
_save_helper(obj, filename, resources, title, template, theme)
File "/home/vagrant/.pyenv/versions/3.10.2/lib/python3.10/site-packages/bokeh/io/saving.py", line 164, in _save_helper
html = file_html(obj, resources, title=title, template=template or FILE, theme=theme)
File "/home/vagrant/.pyenv/versions/3.10.2/lib/python3.10/site-packages/bokeh/embed/standalone.py", line 345, in file_html
with OutputDocumentFor(models_seq, apply_theme=theme, always_new=_always_new) as doc:
File "/home/vagrant/.pyenv/versions/3.10.2/lib/python3.10/contextlib.py", line 135, in __enter__
return next(self.gen)
File "/home/vagrant/.pyenv/versions/3.10.2/lib/python3.10/site-packages/bokeh/embed/util.py", line 153, in OutputDocumentFor
doc.add_root(model)
File "/home/vagrant/.pyenv/versions/3.10.2/lib/python3.10/site-packages/bokeh/document/document.py", line 327, in add_root
with self.models.freeze():
File "/home/vagrant/.pyenv/versions/3.10.2/lib/python3.10/contextlib.py", line 142, in __exit__
next(self.gen)
File "/home/vagrant/.pyenv/versions/3.10.2/lib/python3.10/site-packages/bokeh/document/models.py", line 135, in freeze
self._pop_freeze()
File "/home/vagrant/.pyenv/versions/3.10.2/lib/python3.10/site-packages/bokeh/document/models.py", line 276, in _pop_freeze
self.recompute()
File "/home/vagrant/.pyenv/versions/3.10.2/lib/python3.10/site-packages/bokeh/document/models.py", line 224, in recompute
ma._attach_document(document)
File "/home/vagrant/.pyenv/versions/3.10.2/lib/python3.10/site-packages/bokeh/model/model.py", line 580, in _attach_document
raise RuntimeError(f"Models must be owned by only a single document, {self!r} is already in a doc")
RuntimeError: Models must be owned by only a single document, ColumnDataSource(id='1002', ...) is already in a doc
p1.html
は出力されましたが、p2.html
は出力されませんでした。
原因
以下のサイトによると、ColumnDataSource
は複数のドキュメントで利用できないようです。
The problem here is that your multiple calls to components produces multiple documents, and multiple documents cannot share a ColumnDataSource.
以下のコードのように、ColumnDataSource
を1つのドキュメントからのみ参照するようにすれば、エラーは発生しません。
from bokeh.plotting import figure, output_file, save
from bokeh.models import ColumnDataSource
source1=ColumnDataSource({"x":[1,2],"y":[1,4]})
p1 = figure()
p1.line(source=source1, x="x", y="y")
output_file(filename="p1.html")
save(p1)
source2=ColumnDataSource({"x":[1,2],"y":[1,4]})
p2 = figure()
p2.line(source=source2, x="x", y="y")
output_file(filename="p2.html")
save(p2)
補足:ColumnDataSource
以外でもエラーは発生する
Models must be owned by only a single document
というエラーメッセージは、ColumnDataSource
だけでなくModel
でも同様のエラーが発生します。
たとえば1個のButton
インスタンスを、複数のドキュメントに出力しようとすると、RuntimeError: Models must be owned by only a single document, Button(id='1002', ...) is already in a doc
というエラーが発生します。
from bokeh.plotting import figure, output_file, save
from bokeh.models import Button
from bokeh.layouts import column
button = Button(label="Foo")
print(f"{button=}")
p1 = figure()
print(f"{p1=}")
output_file(filename="p1.html")
save(column([p1, button]))
p2 = figure()
print(f"{p2=}")
output_file(filename="p2.html")
save(column([p2, button]))
補足:IPythonやJupyter Notebookなどインタプリタでは、このエラーに遭遇する確率が高い
1つのColumnDataSource
インスタンスを複数のドキュメントで利用することはあまりないので、通常上記のエラーは見ることはないかと思います。
しかし、IPythonやJupyter Notebookなどのインタプリタでは、動作確認目的で1つのColumnDataSource
インスタンスを複数のドキュメントで利用することがあるので、上記のエラーに遭遇するかもしれません。
In [1]: from bokeh.plotting import figure, show
...: from bokeh.models import ColumnDataSource
In [2]: source=ColumnDataSource({"x":[1,2],"y":[1,4]})
In [3]: p1 = figure()
...: p1.line(source=source, x="x", y="y")
...: show(p1)
In [4]: p1 = figure()
...: p1.line(source=source, x="x", y="y", line_color="red")
...: show(p1)
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
...
RuntimeError: Models must be owned by only a single document, UnionRenderers(id='1048', ...) is already in a doc
参考サイト