• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Path configuration like module_search_path (sys.path) */
2 
3 #include "Python.h"
4 #include "pycore_initconfig.h"    // _PyStatus_OK()
5 #include "pycore_fileutils.h"     // _Py_wgetcwd()
6 #include "pycore_pathconfig.h"
7 #include "pycore_pymem.h"         // _PyMem_SetDefaultAllocator()
8 #include <wchar.h>
9 
10 #include "marshal.h"              // PyMarshal_ReadObjectFromString
11 #include "osdefs.h"               // DELIM
12 
13 #ifdef MS_WINDOWS
14 #  include <windows.h>            // GetFullPathNameW(), MAX_PATH
15 #  include <pathcch.h>
16 #  include <shlwapi.h>
17 #endif
18 
19 
20 /* External interface */
21 
22 /* Stored values set by C API functions */
23 typedef struct _PyPathConfig {
24     /* Full path to the Python program */
25     wchar_t *program_full_path;
26     wchar_t *prefix;
27     wchar_t *exec_prefix;
28     wchar_t *stdlib_dir;
29     /* Set by Py_SetPath */
30     wchar_t *module_search_path;
31     /* Set by _PyPathConfig_UpdateGlobal */
32     wchar_t *calculated_module_search_path;
33     /* Python program name */
34     wchar_t *program_name;
35     /* Set by Py_SetPythonHome() or PYTHONHOME environment variable */
36     wchar_t *home;
37     int _is_python_build;
38 } _PyPathConfig;
39 
40 #  define _PyPathConfig_INIT \
41       {.module_search_path = NULL, ._is_python_build = 0}
42 
43 
44 _PyPathConfig _Py_path_config = _PyPathConfig_INIT;
45 
46 
47 const wchar_t *
_PyPathConfig_GetGlobalModuleSearchPath(void)48 _PyPathConfig_GetGlobalModuleSearchPath(void)
49 {
50     return _Py_path_config.module_search_path;
51 }
52 
53 
54 void
_PyPathConfig_ClearGlobal(void)55 _PyPathConfig_ClearGlobal(void)
56 {
57     PyMemAllocatorEx old_alloc;
58     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
59 
60 #define CLEAR(ATTR) \
61     do { \
62         PyMem_RawFree(_Py_path_config.ATTR); \
63         _Py_path_config.ATTR = NULL; \
64     } while (0)
65 
66     CLEAR(program_full_path);
67     CLEAR(prefix);
68     CLEAR(exec_prefix);
69     CLEAR(stdlib_dir);
70     CLEAR(module_search_path);
71     CLEAR(calculated_module_search_path);
72     CLEAR(program_name);
73     CLEAR(home);
74     _Py_path_config._is_python_build = 0;
75 
76 #undef CLEAR
77 
78     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
79 }
80 
81 PyStatus
_PyPathConfig_ReadGlobal(PyConfig * config)82 _PyPathConfig_ReadGlobal(PyConfig *config)
83 {
84     PyStatus status = _PyStatus_OK();
85 
86 #define COPY(ATTR) \
87     do { \
88         if (_Py_path_config.ATTR && !config->ATTR) { \
89             status = PyConfig_SetString(config, &config->ATTR, _Py_path_config.ATTR); \
90             if (_PyStatus_EXCEPTION(status)) goto done; \
91         } \
92     } while (0)
93 
94 #define COPY2(ATTR, SRCATTR) \
95     do { \
96         if (_Py_path_config.SRCATTR && !config->ATTR) { \
97             status = PyConfig_SetString(config, &config->ATTR, _Py_path_config.SRCATTR); \
98             if (_PyStatus_EXCEPTION(status)) goto done; \
99         } \
100     } while (0)
101 
102 #define COPY_INT(ATTR) \
103     do { \
104         assert(_Py_path_config.ATTR >= 0); \
105         if ((_Py_path_config.ATTR >= 0) && (config->ATTR <= 0)) { \
106             config->ATTR = _Py_path_config.ATTR; \
107         } \
108     } while (0)
109 
110     COPY(prefix);
111     COPY(exec_prefix);
112     COPY(stdlib_dir);
113     COPY(program_name);
114     COPY(home);
115     COPY2(executable, program_full_path);
116     COPY_INT(_is_python_build);
117     // module_search_path must be initialised - not read
118 #undef COPY
119 #undef COPY2
120 #undef COPY_INT
121 
122 done:
123     return status;
124 }
125 
126 PyStatus
_PyPathConfig_UpdateGlobal(const PyConfig * config)127 _PyPathConfig_UpdateGlobal(const PyConfig *config)
128 {
129     PyMemAllocatorEx old_alloc;
130     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
131 
132 #define COPY(ATTR) \
133     do { \
134         if (config->ATTR) { \
135             PyMem_RawFree(_Py_path_config.ATTR); \
136             _Py_path_config.ATTR = _PyMem_RawWcsdup(config->ATTR); \
137             if (!_Py_path_config.ATTR) goto error; \
138         } \
139     } while (0)
140 
141 #define COPY2(ATTR, SRCATTR) \
142     do { \
143         if (config->SRCATTR) { \
144             PyMem_RawFree(_Py_path_config.ATTR); \
145             _Py_path_config.ATTR = _PyMem_RawWcsdup(config->SRCATTR); \
146             if (!_Py_path_config.ATTR) goto error; \
147         } \
148     } while (0)
149 
150 #define COPY_INT(ATTR) \
151     do { \
152         if (config->ATTR > 0) { \
153             _Py_path_config.ATTR = config->ATTR; \
154         } \
155     } while (0)
156 
157     COPY(prefix);
158     COPY(exec_prefix);
159     COPY(stdlib_dir);
160     COPY(program_name);
161     COPY(home);
162     COPY2(program_full_path, executable);
163     COPY_INT(_is_python_build);
164 #undef COPY
165 #undef COPY2
166 #undef COPY_INT
167 
168     PyMem_RawFree(_Py_path_config.module_search_path);
169     _Py_path_config.module_search_path = NULL;
170     PyMem_RawFree(_Py_path_config.calculated_module_search_path);
171     _Py_path_config.calculated_module_search_path = NULL;
172 
173     do {
174         size_t cch = 1;
175         for (Py_ssize_t i = 0; i < config->module_search_paths.length; ++i) {
176             cch += 1 + wcslen(config->module_search_paths.items[i]);
177         }
178 
179         wchar_t *path = (wchar_t*)PyMem_RawMalloc(sizeof(wchar_t) * cch);
180         if (!path) {
181             goto error;
182         }
183         wchar_t *p = path;
184         for (Py_ssize_t i = 0; i < config->module_search_paths.length; ++i) {
185             wcscpy(p, config->module_search_paths.items[i]);
186             p = wcschr(p, L'\0');
187             *p++ = DELIM;
188             *p = L'\0';
189         }
190 
191         do {
192             *p = L'\0';
193         } while (p != path && *--p == DELIM);
194         _Py_path_config.calculated_module_search_path = path;
195     } while (0);
196 
197     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
198     return _PyStatus_OK();
199 
200 error:
201     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
202     return _PyStatus_NO_MEMORY();
203 }
204 
205 
206 static void _Py_NO_RETURN
path_out_of_memory(const char * func)207 path_out_of_memory(const char *func)
208 {
209     _Py_FatalErrorFunc(func, "out of memory");
210 }
211 
212 // Removed in Python 3.13 API, but kept for the stable ABI
213 PyAPI_FUNC(void)
Py_SetPath(const wchar_t * path)214 Py_SetPath(const wchar_t *path)
215 {
216     if (path == NULL) {
217         _PyPathConfig_ClearGlobal();
218         return;
219     }
220 
221     PyMemAllocatorEx old_alloc;
222     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
223 
224     PyMem_RawFree(_Py_path_config.prefix);
225     PyMem_RawFree(_Py_path_config.exec_prefix);
226     PyMem_RawFree(_Py_path_config.stdlib_dir);
227     PyMem_RawFree(_Py_path_config.module_search_path);
228     PyMem_RawFree(_Py_path_config.calculated_module_search_path);
229 
230     _Py_path_config.prefix = _PyMem_RawWcsdup(L"");
231     _Py_path_config.exec_prefix = _PyMem_RawWcsdup(L"");
232     // XXX Copy this from the new module_search_path?
233     if (_Py_path_config.home != NULL) {
234         _Py_path_config.stdlib_dir = _PyMem_RawWcsdup(_Py_path_config.home);
235     }
236     else {
237         _Py_path_config.stdlib_dir = _PyMem_RawWcsdup(L"");
238     }
239     _Py_path_config.module_search_path = _PyMem_RawWcsdup(path);
240     _Py_path_config.calculated_module_search_path = NULL;
241 
242     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
243 
244     if (_Py_path_config.prefix == NULL
245         || _Py_path_config.exec_prefix == NULL
246         || _Py_path_config.stdlib_dir == NULL
247         || _Py_path_config.module_search_path == NULL)
248     {
249         path_out_of_memory(__func__);
250     }
251 }
252 
253 
254 void
Py_SetPythonHome(const wchar_t * home)255 Py_SetPythonHome(const wchar_t *home)
256 {
257     int has_value = home && home[0];
258 
259     PyMemAllocatorEx old_alloc;
260     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
261 
262     PyMem_RawFree(_Py_path_config.home);
263     _Py_path_config.home = NULL;
264 
265     if (has_value) {
266         _Py_path_config.home = _PyMem_RawWcsdup(home);
267     }
268 
269     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
270 
271     if (has_value && _Py_path_config.home == NULL) {
272         path_out_of_memory(__func__);
273     }
274 }
275 
276 
277 void
Py_SetProgramName(const wchar_t * program_name)278 Py_SetProgramName(const wchar_t *program_name)
279 {
280     int has_value = program_name && program_name[0];
281 
282     PyMemAllocatorEx old_alloc;
283     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
284 
285     PyMem_RawFree(_Py_path_config.program_name);
286     _Py_path_config.program_name = NULL;
287 
288     if (has_value) {
289         _Py_path_config.program_name = _PyMem_RawWcsdup(program_name);
290     }
291 
292     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
293 
294     if (has_value && _Py_path_config.program_name == NULL) {
295         path_out_of_memory(__func__);
296     }
297 }
298 
299 
300 wchar_t *
Py_GetPath(void)301 Py_GetPath(void)
302 {
303     /* If the user has provided a path, return that */
304     if (_Py_path_config.module_search_path) {
305         return _Py_path_config.module_search_path;
306     }
307     /* If we have already done calculations, return the calculated path */
308     return _Py_path_config.calculated_module_search_path;
309 }
310 
311 
312 wchar_t *
_Py_GetStdlibDir(void)313 _Py_GetStdlibDir(void)
314 {
315     wchar_t *stdlib_dir = _Py_path_config.stdlib_dir;
316     if (stdlib_dir != NULL && stdlib_dir[0] != L'\0') {
317         return stdlib_dir;
318     }
319     return NULL;
320 }
321 
322 
323 wchar_t *
Py_GetPrefix(void)324 Py_GetPrefix(void)
325 {
326     return _Py_path_config.prefix;
327 }
328 
329 
330 wchar_t *
Py_GetExecPrefix(void)331 Py_GetExecPrefix(void)
332 {
333     return _Py_path_config.exec_prefix;
334 }
335 
336 
337 wchar_t *
Py_GetProgramFullPath(void)338 Py_GetProgramFullPath(void)
339 {
340     return _Py_path_config.program_full_path;
341 }
342 
343 
344 wchar_t*
Py_GetPythonHome(void)345 Py_GetPythonHome(void)
346 {
347     return _Py_path_config.home;
348 }
349 
350 
351 wchar_t *
Py_GetProgramName(void)352 Py_GetProgramName(void)
353 {
354     return _Py_path_config.program_name;
355 }
356 
357 
358 
359 /* Compute module search path from argv[0] or the current working
360    directory ("-m module" case) which will be prepended to sys.argv:
361    sys.path[0].
362 
363    Return 1 if the path is correctly resolved and written into *path0_p.
364 
365    Return 0 if it fails to resolve the full path. For example, return 0 if the
366    current working directory has been removed (bpo-36236) or if argv is empty.
367 
368    Raise an exception and return -1 on error.
369    */
370 int
_PyPathConfig_ComputeSysPath0(const PyWideStringList * argv,PyObject ** path0_p)371 _PyPathConfig_ComputeSysPath0(const PyWideStringList *argv, PyObject **path0_p)
372 {
373     assert(_PyWideStringList_CheckConsistency(argv));
374 
375     if (argv->length == 0) {
376         /* Leave sys.path unchanged if sys.argv is empty */
377         return 0;
378     }
379 
380     wchar_t *argv0 = argv->items[0];
381     int have_module_arg = (wcscmp(argv0, L"-m") == 0);
382     int have_script_arg = (!have_module_arg && (wcscmp(argv0, L"-c") != 0));
383 
384     wchar_t *path0 = argv0;
385     Py_ssize_t n = 0;
386 
387 #ifdef HAVE_REALPATH
388     wchar_t fullpath[MAXPATHLEN];
389 #elif defined(MS_WINDOWS)
390     wchar_t fullpath[MAX_PATH];
391 #endif
392 
393     if (have_module_arg) {
394 #if defined(HAVE_REALPATH) || defined(MS_WINDOWS)
395         if (!_Py_wgetcwd(fullpath, Py_ARRAY_LENGTH(fullpath))) {
396             return 0;
397         }
398         path0 = fullpath;
399 #else
400         path0 = L".";
401 #endif
402         n = wcslen(path0);
403     }
404 
405 #ifdef HAVE_READLINK
406     wchar_t link[MAXPATHLEN + 1];
407     int nr = 0;
408     wchar_t path0copy[2 * MAXPATHLEN + 1];
409 
410     if (have_script_arg) {
411         nr = _Py_wreadlink(path0, link, Py_ARRAY_LENGTH(link));
412     }
413     if (nr > 0) {
414         /* It's a symlink */
415         link[nr] = '\0';
416         if (link[0] == SEP) {
417             path0 = link; /* Link to absolute path */
418         }
419         else if (wcschr(link, SEP) == NULL) {
420             /* Link without path */
421         }
422         else {
423             /* Must join(dirname(path0), link) */
424             wchar_t *q = wcsrchr(path0, SEP);
425             if (q == NULL) {
426                 /* path0 without path */
427                 path0 = link;
428             }
429             else {
430                 /* Must make a copy, path0copy has room for 2 * MAXPATHLEN */
431                 wcsncpy(path0copy, path0, MAXPATHLEN);
432                 q = wcsrchr(path0copy, SEP);
433                 wcsncpy(q+1, link, MAXPATHLEN);
434                 q[MAXPATHLEN + 1] = L'\0';
435                 path0 = path0copy;
436             }
437         }
438     }
439 #endif /* HAVE_READLINK */
440 
441     wchar_t *p = NULL;
442 
443 #if SEP == '\\'
444     /* Special case for Microsoft filename syntax */
445     if (have_script_arg) {
446         wchar_t *q;
447 #if defined(MS_WINDOWS)
448         /* Replace the first element in argv with the full path. */
449         wchar_t *ptemp;
450         if (GetFullPathNameW(path0,
451                            Py_ARRAY_LENGTH(fullpath),
452                            fullpath,
453                            &ptemp)) {
454             path0 = fullpath;
455         }
456 #endif
457         p = wcsrchr(path0, SEP);
458         /* Test for alternate separator */
459         q = wcsrchr(p ? p : path0, '/');
460         if (q != NULL)
461             p = q;
462         if (p != NULL) {
463             n = p + 1 - path0;
464             if (n > 1 && p[-1] != ':')
465                 n--; /* Drop trailing separator */
466         }
467     }
468 #else
469     /* All other filename syntaxes */
470     if (have_script_arg) {
471 #if defined(HAVE_REALPATH)
472         if (_Py_wrealpath(path0, fullpath, Py_ARRAY_LENGTH(fullpath))) {
473             path0 = fullpath;
474         }
475 #endif
476         p = wcsrchr(path0, SEP);
477     }
478     if (p != NULL) {
479         n = p + 1 - path0;
480 #if SEP == '/' /* Special case for Unix filename syntax */
481         if (n > 1) {
482             /* Drop trailing separator */
483             n--;
484         }
485 #endif /* Unix */
486     }
487 #endif /* All others */
488 
489     PyObject *path0_obj = PyUnicode_FromWideChar(path0, n);
490     if (path0_obj == NULL) {
491         return -1;
492     }
493 
494     *path0_p = path0_obj;
495     return 1;
496 }
497