forked from balisujohn/localwriter
-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathsession_manager.py
More file actions
222 lines (182 loc) · 7.34 KB
/
Copy pathsession_manager.py
File metadata and controls
222 lines (182 loc) · 7.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# WriterAgent - AI Writing Assistant for LibreOffice
# Copyright (c) 2026 KeithCu (modifications and relicensing)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
"""Shared-kernel session ids for Calc =PY(), Writer notebooks, and menubar reset."""
from __future__ import annotations
import logging
import uuid
from typing import Any
from plugin.chatbot.dialogs import msgbox
from plugin.doc.document_helpers import get_document_property, is_calc, is_writer, set_document_property
from plugin.framework.config import get_config_str
from plugin.framework.i18n import _
from plugin.framework.uno_context import get_desktop
from plugin.scripting.venv_worker import reset_python_session
log = logging.getLogger(__name__)
def _has_notebook_registry(doc: Any) -> bool:
"""Optional Writer notebook integration (excluded from LibrePy core bundle)."""
try:
from plugin.notebook.cell_registry import has_notebook_registry
except ImportError:
return False
return has_notebook_registry(doc)
PYTHON_WORKBOOK_SESSION_PROP = "WriterAgentPythonSessionId"
_SESSION_MODE_KEY = "scripting.python_session_mode"
def python_session_mode(ctx: Any) -> str:
"""Return ``isolated`` or ``shared`` from config (default ``isolated``)."""
mode = (get_config_str(_SESSION_MODE_KEY) or "isolated").strip().lower()
if mode == "shared":
return "shared"
return "isolated"
def _active_document(ctx: Any) -> Any | None:
try:
desktop = get_desktop(ctx)
return desktop.getCurrentComponent()
except Exception:
log.debug("session_manager: could not get current component", exc_info=True)
return None
def _calc_document(ctx: Any) -> Any | None:
doc = _active_document(ctx)
if doc is None or not is_calc(doc):
return None
return doc
def _writer_document(ctx: Any) -> Any | None:
doc = _active_document(ctx)
if doc is None or not is_writer(doc):
return None
return doc
def _workbook_session_key(doc: Any) -> str:
url = ""
try:
url = (getattr(doc, "getURL", lambda: "")() or "").strip()
except Exception:
pass
if url:
return url
existing = get_document_property(doc, PYTHON_WORKBOOK_SESSION_PROP)
if existing:
return str(existing)
new_id = str(uuid.uuid4())
set_document_property(doc, PYTHON_WORKBOOK_SESSION_PROP, new_id)
return new_id
def calc_workbook_base_session_id(doc: Any) -> str:
"""Worker session id for shared-kernel ``=PY()`` (not the ``:init`` session)."""
return f"calc:{_workbook_session_key(doc)}"
def calc_init_session_id(doc: Any) -> str:
"""Persistent worker session that runs the workbook init script once."""
return f"{calc_workbook_base_session_id(doc)}:init"
def workbook_session_id(ctx: Any) -> str | None:
"""Return ``calc:…`` session id when shared mode and active doc is Calc, else ``None``."""
from plugin.framework.thread_guard import on_main_thread
from plugin.framework.queue_executor import execute_on_main_thread
def _workbook_session_id_impl() -> str | None:
if python_session_mode(ctx) != "shared":
return None
doc = _calc_document(ctx)
if doc is None:
return None
return calc_workbook_base_session_id(doc)
if not on_main_thread():
return execute_on_main_thread(_workbook_session_id_impl)
return _workbook_session_id_impl()
def notebook_session_id(ctx: Any, doc: Any | None = None) -> str | None:
"""Return ``notebook:…`` for a Writer document (always shared when interactive notebook is used)."""
target = doc if doc is not None else _writer_document(ctx)
if target is None or not is_writer(target):
return None
return f"notebook:{_workbook_session_key(target)}"
def reset_notebook_python_session(ctx: Any) -> None:
"""Menubar path: reset shared Python namespace for the active Writer notebook document."""
doc = _writer_document(ctx)
if doc is None:
msgbox(
ctx,
"WriterAgent",
_(
"Reset Python Session for notebooks applies to LibreOffice Writer. "
"Open a Writer document with an imported Jupyter notebook and try again."
),
)
return
if not _has_notebook_registry(doc):
msgbox(
ctx,
"WriterAgent",
_(
"This Writer document has no imported notebook registry. "
"Use Tools → Import Jupyter Notebook… first."
),
)
return
session_id = notebook_session_id(ctx, doc)
if not session_id:
msgbox(ctx, "WriterAgent", _("Could not resolve notebook Python session."))
return
res = reset_python_session(ctx, session_id)
if res.get("status") == "ok":
msgbox(ctx, "WriterAgent", _("Notebook Python session reset for this document."))
return
msg = res.get("message") or _("Could not reset Python session.")
msgbox(ctx, "WriterAgent", _("Error: {0}").format(msg))
def _reset_calc_python_sessions(ctx: Any) -> None:
doc = _calc_document(ctx)
if doc is None:
msgbox(
ctx,
"WriterAgent",
_(
"Reset Python Session applies to Calc spreadsheets. "
"Open a Calc workbook and try again."
),
)
return
from plugin.scripting.document_scripts import get_calc_init_script
session_id = calc_workbook_base_session_id(doc)
res = reset_python_session(ctx, session_id)
if res.get("status") != "ok":
msg = res.get("message") or _("Could not reset Python session.")
msgbox(ctx, "WriterAgent", _("Error: {0}").format(msg))
return
has_init = bool((get_calc_init_script(doc) or "").strip())
if python_session_mode(ctx) == "shared":
msgbox(ctx, "WriterAgent", _("Python session reset for this workbook."))
elif has_init:
msgbox(
ctx,
"WriterAgent",
_(
"Initialization script and any in-memory init state were reset for this workbook. "
"Cell variables were already isolated per cell."
),
)
else:
msgbox(
ctx,
"WriterAgent",
_(
"Python session mode is Isolated (each =PY() cell uses its own variables). "
"There is no shared cell session to reset. Add an initialization script if you "
"need to clear expensive one-time workbook setup."
),
)
def reset_workbook_python_session(ctx: Any) -> None:
"""Menubar handler: reset notebook kernel (Writer) or shared Calc workbook session."""
doc = _active_document(ctx)
if doc is not None and is_writer(doc) and _has_notebook_registry(doc):
reset_notebook_python_session(ctx)
return
if doc is not None and is_writer(doc):
msgbox(
ctx,
"WriterAgent",
_(
"This Writer document has no imported notebook registry. "
"Use Tools → Import Jupyter Notebook… to enable notebook Python session reset."
),
)
return
_reset_calc_python_sessions(ctx)