Skip to content

Commit 71642e2

Browse files
committed
- initPython() adapted to allow usage of local modules from plugin installation
- updated settings menu text to python 3.8 - updated docu
1 parent 483443c commit 71642e2

File tree

4 files changed

+149
-71
lines changed

4 files changed

+149
-71
lines changed

PythonScript/res/PythonScript.rc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ BEGIN
9696
COMBOBOX IDC_COMBOINITIALISATION,55,285,69,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
9797
LTEXT "Initialisation:",IDC_STATIC,9,287,44,12
9898
LTEXT "Lazy initialisation initialises Python when it is first used. Use ATSTARTUP if you want your startup.py scripts to run as soon as Notepad++ starts.",IDC_STATIC,131,282,232,21
99-
CONTROL "Prefer installed Python libraries - use only if you have Python 3.7 installed, and you have copied the python37.dll from your Windows directory to the Notepad++ directory. ",IDC_CHECKPREFERINSTALLEDPYTHON,
99+
CONTROL "Prefer installed Python libraries - use only if you have Python 3.8 installed, and you have copied the python38.dll from your Windows directory to the Notepad++ directory. ",IDC_CHECKPREFERINSTALLEDPYTHON,
100100
"Button",BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,11,303,352,19
101101
LTEXT "This has the effect that the directories under Notepad++ that contain Python libraries are searched AFTER the standard Python directories. If in doubt, leave unchecked.",IDC_STATIC,22,321,348,19
102102
CONTROL "Add an additional end of line character for run statements and error messages to the console",IDC_CHECKADDEXTRALINETOOUTPUT,

PythonScript/src/PythonHandler.cpp

Lines changed: 140 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace NppPythonScript
1818
PythonHandler::PythonHandler(TCHAR *pluginsDir, TCHAR *configDir, HINSTANCE hInst, HWND nppHandle, HWND scintilla1Handle, HWND scintilla2Handle, boost::shared_ptr<PythonConsole> pythonConsole)
1919
: PyProducerConsumer<RunScriptArgs>(),
2020
m_nppHandle(nppHandle),
21-
m_scintilla1Handle(scintilla1Handle),
21+
m_scintilla1Handle(scintilla1Handle),
2222
m_scintilla2Handle(scintilla2Handle),
2323
m_hInst(hInst),
2424
m_machineBaseDir(pluginsDir),
@@ -89,99 +89,179 @@ void PythonHandler::initPython()
8989

9090
preinitScintillaModule();
9191

92+
PyStatus status;
93+
94+
PyConfig config;
95+
PyConfig_InitPythonConfig(&config);
96+
97+
// Read all configuration at once
98+
// implicit pre config python
99+
status = PyConfig_Read(&config);
100+
if (PyStatus_Exception(status))
101+
{
102+
PyConfig_Clear(&config);
103+
}
104+
92105
// Don't import site - if Python 2.7 doesn't find it as part of Py_Initialize,
93106
// it does an exit(1) - AGH!
94107
Py_NoSiteFlag = 1;
95108
Py_IgnoreEnvironmentFlag = 1;
96109
Py_NoUserSiteDirectory = 1;
97110

98-
Py_Initialize();
99-
// Initialise threading and create & acquire Global Interpreter Lock
100-
PyEval_InitThreads();
111+
#ifdef DEBUG
112+
Py_VerboseFlag = 1;
113+
#endif
101114

115+
bool configSetFailed = false;
102116

103-
std::shared_ptr<char> machineBaseDir = WcharMbcsConverter::tchar2char(m_machineBaseDir.c_str());
104-
std::shared_ptr<char> configDir = WcharMbcsConverter::tchar2char(m_userBaseDir.c_str());
117+
//appended or prepended below in this order
118+
std::wstring machinelib = m_machineBaseDir + std::wstring(L"lib");
119+
std::wstring userlib = m_userBaseDir + std::wstring(L"lib");
120+
std::wstring machineScripts = m_machineBaseDir + std::wstring(L"scripts");
121+
std::wstring userScripts = m_userBaseDir + std::wstring(L"scripts");
122+
std::wstring machinelibTK = m_machineBaseDir + std::wstring(L"lib\\lib-tk");
105123

106-
bool machineIsUnicode = containsExtendedChars(machineBaseDir.get());
107-
bool userIsUnicode = containsExtendedChars(configDir.get());
124+
// If the user wants to use their installed python version, append the paths.
125+
// If not (and they want to use the bundled python install), the default, then prepend the paths
126+
if (ConfigFile::getInstance()->getSetting(_T("PREFERINSTALLEDPYTHON")) == _T("1"))
127+
{
128+
/* Append our custom search path to sys.path */
129+
status = PyWideStringList_Append(&config.module_search_paths,
130+
machinelib.c_str());
108131

132+
if (PyStatus_Exception(status))
133+
{
134+
configSetFailed = true;
135+
}
109136

110-
std::string smachineDir(machineBaseDir.get());
111-
std::string suserDir(configDir.get());
137+
/* Append our custom search path to sys.path */
138+
status = PyWideStringList_Append(&config.module_search_paths,
139+
userlib.c_str());
112140

141+
if (PyStatus_Exception(status))
142+
{
143+
configSetFailed = true;
144+
}
113145

114-
// Init paths
115-
char initBuffer[1024];
116-
char pathCommands[500];
146+
/* Append our custom search path to sys.path */
147+
status = PyWideStringList_Append(&config.module_search_paths,
148+
machineScripts.c_str());
117149

118-
// If the user wants to use their installed python version, append the paths.
119-
// If not (and they want to use the bundled python install), the default, then prepend the paths
120-
if (ConfigFile::getInstance()->getSetting(_T("PREFERINSTALLEDPYTHON")) == _T("1")) {
121-
strcpy_s<500>(pathCommands, "import sys\n"
122-
"sys.path.append(r'%slib'%s)\n"
123-
"sys.path.append(r'%slib'%s)\n"
124-
"sys.path.append(r'%sscripts'%s)\n"
125-
"sys.path.append(r'%sscripts'%s)\n"
126-
"sys.path.append(r'%slib\\lib-tk'%s)\n" );
127-
} else {
128-
strcpy_s<500>(pathCommands, "import sys\n"
129-
"sys.path.insert(0,r'%slib'%s)\n"
130-
"sys.path.insert(1,r'%slib'%s)\n"
131-
"sys.path.insert(2,r'%sscripts'%s)\n"
132-
"sys.path.insert(3,r'%sscripts'%s)\n"
133-
"sys.path.insert(4,r'%slib\\lib-tk'%s)\n"
134-
);
150+
if (PyStatus_Exception(status))
151+
{
152+
configSetFailed = true;
153+
}
154+
155+
/* Append our custom search path to sys.path */
156+
status = PyWideStringList_Append(&config.module_search_paths,
157+
userScripts.c_str());
158+
159+
if (PyStatus_Exception(status))
160+
{
161+
configSetFailed = true;
162+
}
163+
164+
/* Append our custom search path to sys.path */
165+
status = PyWideStringList_Append(&config.module_search_paths,
166+
machinelibTK.c_str());
167+
168+
if (PyStatus_Exception(status))
169+
{
170+
configSetFailed = true;
171+
}
135172
}
173+
else
174+
{
175+
/* Prepend via insert our custom search path to sys.path */
176+
status = PyWideStringList_Insert(&config.module_search_paths, 0,
177+
machinelib.c_str());
136178

137-
_snprintf_s(initBuffer, 1024, 1024,
138-
pathCommands,
139-
smachineDir.c_str(),
140-
machineIsUnicode ? ".decode('utf8')" : "",
179+
if (PyStatus_Exception(status))
180+
{
181+
configSetFailed = true;
182+
}
141183

142-
suserDir.c_str(),
143-
userIsUnicode ? ".decode('utf8')" : "",
184+
/* Prepend via insert our custom search path to sys.path */
185+
status = PyWideStringList_Insert(&config.module_search_paths, 1,
186+
userlib.c_str());
144187

145-
smachineDir.c_str(),
146-
machineIsUnicode ? ".decode('utf8')" : "",
188+
if (PyStatus_Exception(status))
189+
{
190+
PyConfig_Clear(&config);
191+
}
192+
193+
/* Prepend via insert our custom search path to sys.path */
194+
status = PyWideStringList_Insert(&config.module_search_paths, 2,
195+
machineScripts.c_str());
196+
197+
if (PyStatus_Exception(status))
198+
{
199+
configSetFailed = true;
200+
}
201+
202+
/* Prepend via insert our custom search path to sys.path */
203+
status = PyWideStringList_Insert(&config.module_search_paths, 3,
204+
userScripts.c_str());
205+
206+
if (PyStatus_Exception(status))
207+
{
208+
PyConfig_Clear(&config);
209+
}
210+
211+
/* Prepend via insert our custom search path to sys.path */
212+
status = PyWideStringList_Insert(&config.module_search_paths, 4,
213+
machinelibTK.c_str());
214+
215+
if (PyStatus_Exception(status))
216+
{
217+
configSetFailed = true;
218+
}
219+
}
220+
221+
if (!configSetFailed)
222+
{
223+
status = Py_InitializeFromConfig(&config);
224+
if (PyStatus_Exception(status))
225+
{
226+
PyConfig_Clear(&config);
227+
}
228+
}
147229

148-
suserDir.c_str(),
149-
userIsUnicode ? ".decode('utf8')" : "",
230+
// Initialise threading and create & acquire Global Interpreter Lock
231+
PyEval_InitThreads();
150232

151-
smachineDir.c_str(),
152-
machineIsUnicode ? ".decode('utf8')" : ""
153-
);
233+
std::string importSys("import sys\n");
154234

155-
PyRun_SimpleString(initBuffer);
235+
PyRun_SimpleString(importSys.c_str());
156236

157-
initSysArgv();
237+
initSysArgv();
158238

159239

160240
// Init Notepad++/Scintilla modules
161241
initModules();
162242

163-
mp_mainThreadState = PyEval_SaveThread();
243+
mp_mainThreadState = PyEval_SaveThread();
164244

165245
}
166246

167247
void PythonHandler::initSysArgv()
168248
{
169-
LPWSTR commandLine = ::GetCommandLineW();
170-
int argc;
171-
LPWSTR* argv = ::CommandLineToArgvW(commandLine, &argc);
249+
LPWSTR commandLine = ::GetCommandLineW();
250+
int argc;
251+
LPWSTR* argv = ::CommandLineToArgvW(commandLine, &argc);
172252

173253

174-
boost::python::list argvList;
175-
for(int currentArg = 0; currentArg != argc; ++currentArg)
254+
boost::python::list argvList;
255+
for(int currentArg = 0; currentArg != argc; ++currentArg)
176256
{
177-
std::shared_ptr<char> argInUtf8 = WcharMbcsConverter::wchar2char(argv[currentArg]);
178-
PyObject* unicodeArg = PyUnicode_FromString(argInUtf8.get());
257+
std::shared_ptr<char> argInUtf8 = WcharMbcsConverter::wchar2char(argv[currentArg]);
258+
PyObject* unicodeArg = PyUnicode_FromString(argInUtf8.get());
179259

180260
argvList.append(boost::python::handle<>(unicodeArg));
181-
}
261+
}
182262

183-
boost::python::object sysModule(boost::python::handle<>(boost::python::borrowed(PyImport_AddModule("sys"))));
184-
sysModule.attr("argv") = argvList;
263+
boost::python::object sysModule(boost::python::handle<>(boost::python::borrowed(PyImport_AddModule("sys"))));
264+
sysModule.attr("argv") = argvList;
185265

186266

187267
}
@@ -286,7 +366,7 @@ void PythonHandler::consume(std::shared_ptr<RunScriptArgs> args)
286366
void PythonHandler::runScriptWorker(const std::shared_ptr<RunScriptArgs>& args)
287367
{
288368

289-
GILLock gilLock;
369+
GILLock gilLock;
290370

291371
if (args->m_isStatement)
292372
{
@@ -296,7 +376,7 @@ void PythonHandler::runScriptWorker(const std::shared_ptr<RunScriptArgs>& args)
296376
{
297377
mp_console->writeText(boost::python::str("\n"));
298378
}
299-
379+
300380
if (ConfigFile::getInstance()->getSetting(_T("OPENCONSOLEONERROR")) == _T("1"))
301381
{
302382
mp_console->pythonShowDialog();
@@ -314,7 +394,7 @@ void PythonHandler::runScriptWorker(const std::shared_ptr<RunScriptArgs>& args)
314394
{
315395
mp_console->writeText(boost::python::str("\n"));
316396
}
317-
397+
318398
if (ConfigFile::getInstance()->getSetting(_T("OPENCONSOLEONERROR")) == _T("1"))
319399
{
320400
mp_console->pythonShowDialog();
@@ -368,7 +448,7 @@ void PythonHandler::stopScript()
368448

369449
void PythonHandler::stopScriptWorker(PythonHandler *handler)
370450
{
371-
GILLock gilLock;
451+
GILLock gilLock;
372452

373453
PyThreadState_SetAsyncExc((long)handler->getExecutingThreadID(), PyExc_KeyboardInterrupt);
374454

docs/source/compiling.rst

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,15 @@ There's lots more info on the boost website about building Boost.Python.
4141
Building Python
4242
---------------
4343

44-
You need to have a valid python27.dll (or python26.dll for PythonScript 0.7), and the corresponding lib (python27.lib).
45-
The "official" PythonScript is built with a custom Python27.dll, that has the /MT flag enabled, as do all the modules.
46-
Note that VS2008 is used to build Python, as that's what the official distribution uses.
47-
48-
This is slightly more involved that just setting the /MT or /MTd flag (instead of /MD or /MDd respectively) in the
49-
C/C++ Code Generation options. Python compiles some utilities along the way that help with the building of the modules,
50-
and in some (maybe one) of these tools, the /MD or /MDd is hard coded. Other than that, it's just a matter of setting the
51-
library paths in the .props file. Depending on the modules you want to build, you may need to build the library the module
44+
You need to have a valid python38.dll and the corresponding lib (python38.lib).
45+
The official build uses nuget packages therefore.
46+
47+
Other than that, it's just a matter of setting the library paths in the .props file.
48+
Depending on the modules you want to build, you may need to build the library the module
5249
uses first (for example the tcl/tk library).
5350

54-
Again, the libraries are available on the download site, so you don't have to build a python27.dll & python27.lib if you
51+
The libraries are available from nuget or part of the python installation,
52+
so you don't have to build a python38.dll & python38.lib if you
5553
don't need to actually debug through Python (or change something etc).
5654

5755
Building PythonScript itself

docs/source/usage.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ The file layout is as follows::
2020
| |
2121
| |-- PythonScript.dll
2222
| |
23-
| |-- python27.dll (maybe not necessary if you have a Python installation already, in which case it's probably in C:\windows )
23+
| |-- python38.dll (maybe not necessary if you have a Python installation already, in which case it's probably in C:\windows )
2424
| |
2525
| |-- lib
2626
| | \

0 commit comments

Comments
 (0)