itom
Loading...
Searching...
No Matches
pythonJediRunner.h
1/* ********************************************************************
2 itom software
3 URL: http://www.uni-stuttgart.de/ito
4 Copyright (C) 2024, Institut für Technische Optik (ITO),
5 Universität Stuttgart, Germany
6
7 This file is part of itom.
8
9 itom is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Library General Public Licence as published by
11 the Free Software Foundation; either version 2 of the Licence, or (at
12 your option) any later version.
13
14 itom is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library
17 General Public Licence for more details.
18
19 You should have received a copy of the GNU Library General Public License
20 along with itom. If not, see <http://www.gnu.org/licenses/>.
21*********************************************************************** */
22
23#pragma once
24
25/* includes */
26#ifndef Q_MOC_RUN
27 #define PY_ARRAY_UNIQUE_SYMBOL itom_ARRAY_API //see numpy help ::array api :: Miscellaneous :: Importing the api (this line must before include global.h)
28 #define NO_IMPORT_ARRAY
29
30 #include "python/pythonWrapper.h"
31#endif
32
33#include <qobject.h>
34#include <qthreadpool.h>
35#include <qscopedpointer.h>
36#include <qqueue.h>
37#include <QRunnable>
38#include <qmutex.h>
39
40#include "pythonJedi.h"
41
42
43namespace ito
44{
45
46//-------------------------------------------------------------------------------------
48/*
49 Every instance of a derived class of JediRunnable has a run() method,
50 that is called by the thread pool (with one thread only) of the PythonJediRunner
51 class. By this run method, a specific request to the Python library Jedi is
52 executed. Since the thread pool only has one thread, Jedi is never called
53 in parallel.
54
55 However, the thread can be run in parallel to any other Python code execution.
56 Therefore, every run method of a runnable has to lock the GIL before starting
57 the request and release it if done.
58
59 It might be, that the script editors enqueue more requests of the same type
60 than can be currently handled. Therefore, the run method skips the call
61 if there is a newer runnable in the queue.
62*/
63class JediRunnable : public QRunnable
64{
65public:
66 enum Type
67 {
68 RunnableCalltip,
69 RunnableCompletion,
70 RunnableGoToAssignment,
71 RunnableGetHelp,
72 RunnableRename
73 };
74
76 const Type &type,
77 PyObject *pPyModJedi,
78 const QString &additionalImportString
79 ) :
80 m_type(type),
81 m_pPyModJedi(pPyModJedi),
82 m_additionalImportString(additionalImportString)
83 {
84 setAutoDelete(true);
85 };
86
87 virtual ~JediRunnable() {};
88
89 virtual unsigned char getCurrentId() const
90 {
91 return m_currentId;
92 }
93
94 virtual unsigned char getMostRecentId() const = 0;
95
96protected:
97 bool isOutdated() const;
98 void startRun();
99 void endRun();
100
101 Type m_type;
102 QString m_additionalImportString;
103 PyObject *m_pPyModJedi;
104 unsigned char m_currentId;
105
106 static QMutex m_mutex;
107};
108
109//-------------------------------------------------------------------------------------
112{
113public:
115 const QString &additionalImportString,
116 PyObject *pPyModJedi,
117 const JediCompletionRequest &request
118 ) :
119 JediRunnable(JediRunnable::RunnableCompletion, pPyModJedi, additionalImportString),
120 m_request(request)
121 {
122 m_mutex.lock();
123
124 if (mostRecentId < 255)
125 {
126 m_currentId = ++mostRecentId;
127 }
128 else
129 {
130 m_currentId = 0;
131 mostRecentId = 0;
132 }
133
134 m_mutex.unlock();
135 };
136
137
138 virtual ~CompletionRunnable() {};
139
140 void run();
141
142 virtual unsigned char getMostRecentId() const
143 {
144 return CompletionRunnable::mostRecentId;
145 }
146
147private:
148 JediCompletionRequest m_request;
149
150 static unsigned char mostRecentId;
151};
152
153//-------------------------------------------------------------------------------------
156{
157public:
159 const QString &additionalImportString,
160 PyObject *pPyModJedi,
161 const JediAssignmentRequest &request
162 ) :
163 JediRunnable(JediRunnable::RunnableGoToAssignment, pPyModJedi, additionalImportString),
164 m_request(request)
165 {
166 m_mutex.lock();
167
168 if (mostRecentId < 255)
169 {
170 m_currentId = ++mostRecentId;
171 }
172 else
173 {
174 m_currentId = 0;
175 mostRecentId = 0;
176 }
177
178 m_mutex.unlock();
179 };
180
181 virtual ~GoToAssignmentRunnable() {};
182
183 void run();
184
185 virtual unsigned char getMostRecentId() const
186 {
187 return GoToAssignmentRunnable::mostRecentId;
188 }
189
190private:
191 JediAssignmentRequest m_request;
192
193 static unsigned char mostRecentId;
194};
195
196//-------------------------------------------------------------------------------------
199{
200public:
202 const QString &additionalImportString,
203 PyObject *pPyModJedi,
204 const JediCalltipRequest &request
205 ) :
206 JediRunnable(JediRunnable::RunnableCalltip, pPyModJedi, additionalImportString) ,
207 m_request(request)
208 {
209 m_mutex.lock();
210
211 if (mostRecentId < 255)
212 {
213 m_currentId = ++mostRecentId;
214 }
215 else
216 {
217 m_currentId = 0;
218 mostRecentId = 0;
219 }
220
221 m_mutex.unlock();
222 };
223
224 virtual ~CalltipRunnable() {};
225
226 void run();
227
228 virtual unsigned char getMostRecentId() const
229 {
230 return CalltipRunnable::mostRecentId;
231 }
232
233private:
234 JediCalltipRequest m_request;
235
236 static unsigned char mostRecentId;
237};
238
239//-------------------------------------------------------------------------------------
242{
243public:
245 const QString &additionalImportString,
246 PyObject *pPyModJedi,
247 const JediGetHelpRequest &request
248 ) :
249 JediRunnable(JediRunnable::RunnableGetHelp, pPyModJedi, additionalImportString),
250 m_request(request)
251 {
252 m_mutex.lock();
253
254 if (mostRecentId < 255)
255 {
256 m_currentId = ++mostRecentId;
257 }
258 else
259 {
260 m_currentId = 0;
261 mostRecentId = 0;
262 }
263
264 m_mutex.unlock();
265 };
266
267 virtual ~GetHelpRunnable() {};
268
269 void run();
270
271 virtual unsigned char getMostRecentId() const
272 {
273 return GetHelpRunnable::mostRecentId;
274 }
275
276private:
277 JediGetHelpRequest m_request;
278
279 static unsigned char mostRecentId;
280};
281
282//-------------------------------------------------------------------------------------
285{
286public:
288 const QString& additionalImportString,
289 PyObject* pPyModJedi,
290 const JediRenameRequest& request) :
291 JediRunnable(JediRunnable::RunnableRename, pPyModJedi, additionalImportString),
292 m_request(request)
293 {
294 m_mutex.lock();
295
296 if (mostRecentId < 255)
297 {
298 m_currentId = ++mostRecentId;
299 }
300 else
301 {
302 m_currentId = 0;
303 mostRecentId = 0;
304 }
305
306 m_mutex.unlock();
307 };
308
309
310 virtual ~RenameRunnable(){};
311
312 void run();
313
314 virtual unsigned char getMostRecentId() const
315 {
316 return RenameRunnable::mostRecentId;
317 }
318
319private:
320 JediRenameRequest m_request;
321
322 static unsigned char mostRecentId;
323};
324
325//-------------------------------------------------------------------------------------
327/* This class is initialized by the PythonEngine as singleton and opened in pythonStartup()
328and closed in pythonShutdown().
329
330Its methods are thread-safe and are usually called via wrapper methods in Python engine.
331The idea is, that jedi is always triggered via the Python C-API from another thread,
332using the Python GIL, such that other Python commands can be executed in parallel.
333
334This is for instance important if somebody enters "import numpy" in the command line
335and presses enter: Since numpy is an intense package, Jedi requires a little bit of time
336when numpy is analyzed for the first time, such that the code execution would have to wait
337for a couple of seconds. Using the thread-pool approach of this class, the execution is
338started much faster.
339
340Hint: The thread pool of this class is limited to one thread, such that no parallel
341requests to jedi can be executed (desired behaviour).
342*/
343class PythonJediRunner : public QObject
344{
345 Q_OBJECT
346
347public:
348 PythonJediRunner(const QString &includeItomImportString);
350
352 /*
353 If the module and package is already imported, true is directly returned.
354 This method is thread-safe.
355 */
356 bool tryToLoadJediIfNotYetDone();
357
359 {
361 }
362
364 void addCalltipRequest(const JediCalltipRequest &request);
365
367 void addCompletionRequest(const JediCompletionRequest &request);
368
371
373 void addGetHelpRequest(const JediGetHelpRequest &request);
374
376 void addRenameRequest(const JediRenameRequest& request);
377
378private:
379 QString additionalImportString() const {
380 return
382 m_includeItomImportString : "");
383 }
384
385 QScopedPointer<QThreadPool> m_threadPool;
386
388 PyObject *m_pyModJedi;
389
392
395
397 QString m_includeItomImportString;
398};
399
400}; //end namespace ito
< runnable that executes a calltip call to Jedi by the thread pool of Python Jedi Runner.
Definition pythonJediRunner.h:199
< runnable that executes a completion call to Jedi by the thread pool of Python Jedi Runner.
Definition pythonJediRunner.h:112
< runnable that executes a calltip call to Jedi by the thread pool of Python Jedi Runner.
Definition pythonJediRunner.h:242
< runnable that executes a goto definition / assignment call to Jedi by the thread pool of Python Jed...
Definition pythonJediRunner.h:156
< base class for all runnables, that are executed with PythonJediRunner
Definition pythonJediRunner.h:64
< Thread-safe helper class for PythonEngine to manage calls to the Python Jedi package.
Definition pythonJediRunner.h:344
void addGetHelpRequest(const JediGetHelpRequest &request)
Adds a new rename request. Thread-safe.
Definition pythonJediRunner.cpp:193
void setIncludeItomImportBeforeCodeAnalysis(bool include)
Adds a new calltip request. Thread-safe.
Definition pythonJediRunner.h:358
void addCalltipRequest(const JediCalltipRequest &request)
Adds a new completion request. Thread-safe.
Definition pythonJediRunner.cpp:142
bool m_pyModJediChecked
decides if itom is automatically included in every source file before it is handed to the syntax chec...
Definition pythonJediRunner.h:391
QScopedPointer< QThreadPool > m_threadPool
Python package Jedi for auto completion and calltips (Jedi is tried to be loaded as late as possible)
Definition pythonJediRunner.h:385
PyObject * m_pyModJedi
defines, if it is already checked if Jedi could be loaded on this computer.
Definition pythonJediRunner.h:388
void addGoToAssignmentRequest(const JediAssignmentRequest &request)
Adds a new get-help request. Thread-safe.
Definition pythonJediRunner.cpp:176
void addCompletionRequest(const JediCompletionRequest &request)
Adds a new goto assignment / definition request. Thread-safe.
Definition pythonJediRunner.cpp:159
~PythonJediRunner()
Tries to import itomJediLib (and the jedi package) and returns true if successful,...
Definition pythonJediRunner.cpp:65
bool m_includeItomImportBeforeCodeAnalysis
string that is prepended to each script before syntax check (if m_includeItomImportBeforeCodeAnalysis...
Definition pythonJediRunner.h:394
< runnable that executes a completion call to Jedi by the thread pool of Python Jedi Runner.
Definition pythonJediRunner.h:285
Definition apiFunctionsGraph.cpp:40
Definition pythonJedi.h:103
Definition pythonJedi.h:36
Definition pythonJedi.h:70
Definition pythonJedi.h:130
Definition pythonJedi.h:154