66 PATH RULES FOR WINDOWS:
77 This describes how sys.path is formed on Windows. It describes the
88 functionality, not the implementation (ie, the order in which these
9- are actually fetched is different). The presence of a sys.path file
10- alongside the program overrides these rules - see below.
9+ are actually fetched is different). The presence of a python._pth or
10+ pythonXY._pth file alongside the program overrides these rules - see
11+ below.
1112
1213 * Python always adds an empty entry at the start, which corresponds
1314 to the current directory.
3738 used (eg. .\Lib;.\DLLs, etc)
3839
3940
40- If a sys.path file exists adjacent to python.exe, it must contain a
41- list of paths to add to sys.path, one per line (like a .pth file but without
42- the ability to execute arbitrary code). Each path is relative to the
43- directory containing the file. No other paths are added to the search path,
44- and the registry finder is not enabled.
41+ If a '._pth' file exists adjacent to the executable with the same base name
42+ (e.g. python._pth adjacent to python.exe) or adjacent to the shared library
43+ (e.g. python36._pth adjacent to python36.dll), it is used in preference to
44+ the above process. The shared library file takes precedence over the
45+ executable. The path file must contain a list of paths to add to sys.path,
46+ one per line. Each path is relative to the directory containing the file.
47+ Blank lines and comments beginning with '#' are permitted.
48+
49+ In the presence of this ._pth file, no other paths are added to the search
50+ path, the registry finder is not enabled, site.py is not imported and
51+ isolated mode is enabled. The site package can be enabled by including a
52+ line reading "import site"; no other imports are recognized. Any invalid
53+ entry (other than directories that do not exist) will result in immediate
54+ termination of the program.
55+
4556
4657 The end result of all this is:
4758 * When running python.exe, or any other .exe in the main Python directory
6172 * An embedding application can use Py_SetPath() to override all of
6273 these automatic path computations.
6374
64- * An isolation install of Python can disable all implicit paths by
65- providing a sys.path file.
75+ * An install of Python can fully specify the contents of sys.path using
76+ either a 'EXENAME._pth' or 'DLLNAME._pth' file, optionally including
77+ "import site" to enable the site module.
6678
6779 ---------------------------------------------------------------- */
6880
@@ -135,6 +147,33 @@ reduce(wchar_t *dir)
135147 dir [i ] = '\0' ;
136148}
137149
150+ static int
151+ change_ext (wchar_t * dest , const wchar_t * src , const wchar_t * ext )
152+ {
153+ size_t src_len = wcsnlen_s (src , MAXPATHLEN + 1 );
154+ size_t i = src_len ;
155+ if (i >= MAXPATHLEN + 1 )
156+ Py_FatalError ("buffer overflow in getpathp.c's reduce()" );
157+
158+ while (i > 0 && src [i ] != '.' && !is_sep (src [i ]))
159+ -- i ;
160+
161+ if (i == 0 ) {
162+ dest [0 ] = '\0' ;
163+ return -1 ;
164+ }
165+
166+ if (is_sep (src [i ]))
167+ i = src_len ;
168+
169+ if (wcsncpy_s (dest , MAXPATHLEN + 1 , src , i ) ||
170+ wcscat_s (dest , MAXPATHLEN + 1 , ext )) {
171+ dest [0 ] = '\0' ;
172+ return -1 ;
173+ }
174+
175+ return 0 ;
176+ }
138177
139178static int
140179exists (wchar_t * filename )
@@ -499,12 +538,17 @@ find_env_config_value(FILE * env_file, const wchar_t * key, wchar_t * value)
499538}
500539
501540static int
502- read_sys_path_file (const wchar_t * path , const wchar_t * prefix )
541+ read_pth_file (const wchar_t * path , wchar_t * prefix , int * isolated , int * nosite )
503542{
504543 FILE * sp_file = _Py_wfopen (path , L"r" );
505544 if (sp_file == NULL )
506545 return -1 ;
507546
547+ wcscpy_s (prefix , MAXPATHLEN + 1 , path );
548+ reduce (prefix );
549+ * isolated = 1 ;
550+ * nosite = 1 ;
551+
508552 size_t bufsiz = MAXPATHLEN ;
509553 size_t prefixlen = wcslen (prefix );
510554
@@ -516,16 +560,25 @@ read_sys_path_file(const wchar_t *path, const wchar_t *prefix)
516560 char * p = fgets (line , MAXPATHLEN + 1 , sp_file );
517561 if (!p )
518562 break ;
563+ if (* p == '\0' || * p == '#' )
564+ continue ;
565+ while (* ++ p ) {
566+ if (* p == '\r' || * p == '\n' ) {
567+ * p = '\0' ;
568+ break ;
569+ }
570+ }
519571
520- DWORD n = strlen (line );
521- if (n == 0 || p [n - 1 ] != '\n' )
522- break ;
523- if (n > 2 && p [n - 1 ] == '\r' )
524- -- n ;
572+ if (strcmp (line , "import site" ) == 0 ) {
573+ * nosite = 0 ;
574+ continue ;
575+ } else if (strncmp (line , "import " , 7 ) == 0 ) {
576+ Py_FatalError ("only 'import site' is supported in ._pth file" );
577+ }
525578
526- DWORD wn = MultiByteToWideChar (CP_UTF8 , 0 , line , n - 1 , NULL , 0 );
579+ DWORD wn = MultiByteToWideChar (CP_UTF8 , 0 , line , - 1 , NULL , 0 );
527580 wchar_t * wline = (wchar_t * )PyMem_RawMalloc ((wn + 1 ) * sizeof (wchar_t ));
528- wn = MultiByteToWideChar (CP_UTF8 , 0 , line , n - 1 , wline , wn );
581+ wn = MultiByteToWideChar (CP_UTF8 , 0 , line , - 1 , wline , wn + 1 );
529582 wline [wn ] = '\0' ;
530583
531584 while (wn + prefixlen + 4 > bufsiz ) {
@@ -539,8 +592,8 @@ read_sys_path_file(const wchar_t *path, const wchar_t *prefix)
539592
540593 if (buf [0 ])
541594 wcscat_s (buf , bufsiz , L";" );
595+
542596 wchar_t * b = & buf [wcslen (buf )];
543-
544597 wcscat_s (buf , bufsiz , prefix );
545598 join (b , wline );
546599
@@ -586,13 +639,12 @@ calculate_path(void)
586639 {
587640 wchar_t spbuffer [MAXPATHLEN + 1 ];
588641
589- wcscpy_s (spbuffer , MAXPATHLEN + 1 , argv0_path );
590- join (spbuffer , L"sys.path" );
591- if (exists (spbuffer ) && read_sys_path_file (spbuffer , argv0_path ) == 0 ) {
592- wcscpy_s (prefix , MAXPATHLEN + 1 , argv0_path );
593- Py_IsolatedFlag = 1 ;
594- Py_NoSiteFlag = 1 ;
595- return ;
642+ if ((dllpath [0 ] && !change_ext (spbuffer , dllpath , L"._pth" ) && exists (spbuffer )) ||
643+ (progpath [0 ] && !change_ext (spbuffer , progpath , L"._pth" ) && exists (spbuffer ))) {
644+
645+ if (!read_pth_file (spbuffer , prefix , & Py_IsolatedFlag , & Py_NoSiteFlag )) {
646+ return ;
647+ }
596648 }
597649 }
598650
@@ -631,16 +683,7 @@ calculate_path(void)
631683 }
632684
633685 /* Calculate zip archive path from DLL or exe path */
634- if (wcscpy_s (zip_path , MAXPATHLEN + 1 , dllpath [0 ] ? dllpath : progpath )) {
635- /* exceeded buffer length - ignore zip_path */
636- zip_path [0 ] = '\0' ;
637- } else {
638- wchar_t * dot = wcsrchr (zip_path , '.' );
639- if (!dot || wcscpy_s (dot , MAXPATHLEN + 1 - (dot - zip_path ), L".zip" )) {
640- /* exceeded buffer length - ignore zip_path */
641- zip_path [0 ] = L'\0' ;
642- }
643- }
686+ change_ext (zip_path , dllpath [0 ] ? dllpath : progpath , L".zip" );
644687
645688 if (pythonhome == NULL || * pythonhome == '\0' ) {
646689 if (zip_path [0 ] && exists (zip_path )) {
0 commit comments