1 """Append module search paths for third-party packages to sys.path.
2
3 ****************************************************************
4 * This module is automatically imported during initialization. *
5 ****************************************************************
6
7 This will append site-specific paths to the module search path. On
8 Unix (including Mac OSX), it starts with sys.prefix and
9 sys.exec_prefix (if different) and appends
10 lib/python<version>/site-packages.
11 On other platforms (such as Windows), it tries each of the
12 prefixes directly, as well as with lib/site-packages appended. The
13 resulting directories, if they exist, are appended to sys.path, and
14 also inspected for path configuration files.
15
16 If a file named "pyvenv.cfg" exists one directory above sys.executable,
17 sys.prefix and sys.exec_prefix are set to that directory and
18 it is also checked for site-packages (sys.base_prefix and
19 sys.base_exec_prefix will always be the "real" prefixes of the Python
20 installation). If "pyvenv.cfg" (a bootstrap configuration file) contains
21 the key "include-system-site-packages" set to anything other than "false"
22 (case-insensitive), the system-level prefixes will still also be
23 searched for site-packages; otherwise they won't.
24
25 All of the resulting site-specific directories, if they exist, are
26 appended to sys.path, and also inspected for path configuration
27 files.
28
29 A path configuration file is a file whose name has the form
30 <package>.pth; its contents are additional directories (one per line)
31 to be added to sys.path. Non-existing directories (or
32 non-directories) are never added to sys.path; no directory is added to
33 sys.path more than once. Blank lines and lines beginning with
34 '#' are skipped. Lines starting with 'import' are executed.
35
36 For example, suppose sys.prefix and sys.exec_prefix are set to
37 /usr/local and there is a directory /usr/local/lib/python2.5/site-packages
38 with three subdirectories, foo, bar and spam, and two path
39 configuration files, foo.pth and bar.pth. Assume foo.pth contains the
40 following:
41
42 # foo package configuration
43 foo
44 bar
45 bletch
46
47 and bar.pth contains:
48
49 # bar package configuration
50 bar
51
52 Then the following directories are added to sys.path, in this order:
53
54 /usr/local/lib/python2.5/site-packages/bar
55 /usr/local/lib/python2.5/site-packages/foo
56
57 Note that bletch is omitted because it doesn't exist; bar precedes foo
58 because bar.pth comes alphabetically before foo.pth; and spam is
59 omitted because it is not mentioned in either path configuration file.
60
61 The readline module is also automatically configured to enable
62 completion for systems that support it. This can be overridden in
63 sitecustomize, usercustomize or PYTHONSTARTUP. Starting Python in
64 isolated mode (-I) disables automatic readline configuration.
65
66 After these operations, an attempt is made to import a module
67 named sitecustomize, which can perform arbitrary additional
68 site-specific customizations. If this import fails with an
69 ImportError exception, it is silently ignored.
70 """
71
72 import sys
73 import os
74 import builtins
75 import _sitebuiltins
76 import io
77
78 # Prefixes for site-packages; add additional prefixes like /usr/local here
79 PREFIXES = [sys.prefix, sys.exec_prefix]
80 # Enable per user site-packages directory
81 # set it to False to disable the feature or True to force the feature
82 ENABLE_USER_SITE = None
83
84 # for distutils.commands.install
85 # These values are initialized by the getuserbase() and getusersitepackages()
86 # functions, through the main() function when Python starts.
87 USER_SITE = None
88 USER_BASE = None
89
90
91 def _trace(message):
92 if sys.flags.verbose:
93 print(message, file=sys.stderr)
94
95
96 def makepath(*paths):
97 dir = os.path.join(*paths)
98 try:
99 dir = os.path.abspath(dir)
100 except OSError:
101 pass
102 return dir, os.path.normcase(dir)
103
104
105 def abs_paths():
106 """Set all module __file__ and __cached__ attributes to an absolute path"""
107 for m in set(sys.modules.values()):
108 loader_module = None
109 try:
110 loader_module = m.__loader__.__module__
111 except AttributeError:
112 try:
113 loader_module = m.__spec__.loader.__module__
114 except AttributeError:
115 pass
116 if loader_module not in {'_frozen_importlib', '_frozen_importlib_external'}:
117 continue # don't mess with a PEP 302-supplied __file__
118 try:
119 m.__file__ = os.path.abspath(m.__file__)
120 except (AttributeError, OSError, TypeError):
121 pass
122 try:
123 m.__cached__ = os.path.abspath(m.__cached__)
124 except (AttributeError, OSError, TypeError):
125 pass
126
127
128 def removeduppaths():
129 """ Remove duplicate entries from sys.path along with making them
130 absolute"""
131 # This ensures that the initial path provided by the interpreter contains
132 # only absolute pathnames, even if we're running from the build directory.
133 L = []
134 known_paths = set()
135 for dir in sys.path:
136 # Filter out duplicate paths (on case-insensitive file systems also
137 # if they only differ in case); turn relative paths into absolute
138 # paths.
139 dir, dircase = makepath(dir)
140 if dircase not in known_paths:
141 L.append(dir)
142 known_paths.add(dircase)
143 sys.path[:] = L
144 return known_paths
145
146
147 def _init_pathinfo():
148 """Return a set containing all existing file system items from sys.path."""
149 d = set()
150 for item in sys.path:
151 try:
152 if os.path.exists(item):
153 _, itemcase = makepath(item)
154 d.add(itemcase)
155 except TypeError:
156 continue
157 return d
158
159
160 def addpackage(sitedir, name, known_paths):
161 """Process a .pth file within the site-packages directory:
162 For each line in the file, either combine it with sitedir to a path
163 and add that to known_paths, or execute it if it starts with 'import '.
164 """
165 if known_paths is None:
166 known_paths = _init_pathinfo()
167 reset = True
168 else:
169 reset = False
170 fullname = os.path.join(sitedir, name)
171 _trace(f"Processing .pth file: {fullname!r}")
172 try:
173 # locale encoding is not ideal especially on Windows. But we have used
174 # it for a long time. setuptools uses the locale encoding too.
175 f = io.TextIOWrapper(io.open_code(fullname), encoding="locale")
176 except OSError:
177 return
178 with f:
179 for n, line in enumerate(f):
180 if line.startswith("#"):
181 continue
182 if line.strip() == "":
183 continue
184 try:
185 if line.startswith(("import ", "import\t")):
186 exec(line)
187 continue
188 line = line.rstrip()
189 dir, dircase = makepath(sitedir, line)
190 if not dircase in known_paths and os.path.exists(dir):
191 sys.path.append(dir)
192 known_paths.add(dircase)
193 except Exception as exc:
194 print("Error processing line {:d} of {}:\n".format(n+1, fullname),
195 file=sys.stderr)
196 import traceback
197 for record in traceback.format_exception(exc):
198 for line in record.splitlines():
199 print(' '+line, file=sys.stderr)
200 print("\nRemainder of file ignored", file=sys.stderr)
201 break
202 if reset:
203 known_paths = None
204 return known_paths
205
206
207 def addsitedir(sitedir, known_paths=None):
208 """Add 'sitedir' argument to sys.path if missing and handle .pth files in
209 'sitedir'"""
210 _trace(f"Adding directory: {sitedir!r}")
211 if known_paths is None:
212 known_paths = _init_pathinfo()
213 reset = True
214 else:
215 reset = False
216 sitedir, sitedircase = makepath(sitedir)
217 if not sitedircase in known_paths:
218 sys.path.append(sitedir) # Add path component
219 known_paths.add(sitedircase)
220 try:
221 names = os.listdir(sitedir)
222 except OSError:
223 return
224 names = [name for name in names if name.endswith(".pth")]
225 for name in sorted(names):
226 addpackage(sitedir, name, known_paths)
227 if reset:
228 known_paths = None
229 return known_paths
230
231
232 def check_enableusersite():
233 """Check if user site directory is safe for inclusion
234
235 The function tests for the command line flag (including environment var),
236 process uid/gid equal to effective uid/gid.
237
238 None: Disabled for security reasons
239 False: Disabled by user (command line option)
240 True: Safe and enabled
241 """
242 if sys.flags.no_user_site:
243 return False
244
245 if hasattr(os, "getuid") and hasattr(os, "geteuid"):
246 # check process uid == effective uid
247 if os.geteuid() != os.getuid():
248 return None
249 if hasattr(os, "getgid") and hasattr(os, "getegid"):
250 # check process gid == effective gid
251 if os.getegid() != os.getgid():
252 return None
253
254 return True
255
256
257 # NOTE: sysconfig and it's dependencies are relatively large but site module
258 # needs very limited part of them.
259 # To speedup startup time, we have copy of them.
260 #
261 # See https://bugs.python.org/issue29585
262
263 # Copy of sysconfig._getuserbase()
264 def _getuserbase():
265 env_base = os.environ.get("PYTHONUSERBASE", None)
266 if env_base:
267 return env_base
268
269 # Emscripten, VxWorks, and WASI have no home directories
270 if sys.platform in {"emscripten", "vxworks", "wasi"}:
271 return None
272
273 def joinuser(*args):
274 return os.path.expanduser(os.path.join(*args))
275
276 if os.name == "nt":
277 base = os.environ.get("APPDATA") or "~"
278 return joinuser(base, "Python")
279
280 if sys.platform == "darwin" and sys._framework:
281 return joinuser("~", "Library", sys._framework,
282 "%d.%d" % sys.version_info[:2])
283
284 return joinuser("~", ".local")
285
286
287 # Same to sysconfig.get_path('purelib', os.name+'_user')
288 def _get_path(userbase):
289 version = sys.version_info
290
291 if os.name == 'nt':
292 ver_nodot = sys.winver.replace('.', '')
293 return f'{userbase}\\Python{ver_nodot}\\site-packages'
294
295 if sys.platform == 'darwin' and sys._framework:
296 return f'{userbase}/lib/python/site-packages'
297
298 return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages'
299
300
301 def getuserbase():
302 """Returns the `user base` directory path.
303
304 The `user base` directory can be used to store data. If the global
305 variable ``USER_BASE`` is not initialized yet, this function will also set
306 it.
307 """
308 global USER_BASE
309 if USER_BASE is None:
310 USER_BASE = _getuserbase()
311 return USER_BASE
312
313
314 def getusersitepackages():
315 """Returns the user-specific site-packages directory path.
316
317 If the global variable ``USER_SITE`` is not initialized yet, this
318 function will also set it.
319 """
320 global USER_SITE, ENABLE_USER_SITE
321 userbase = getuserbase() # this will also set USER_BASE
322
323 if USER_SITE is None:
324 if userbase is None:
325 ENABLE_USER_SITE = False # disable user site and return None
326 else:
327 USER_SITE = _get_path(userbase)
328
329 return USER_SITE
330
331 def addusersitepackages(known_paths):
332 """Add a per user site-package to sys.path
333
334 Each user has its own python directory with site-packages in the
335 home directory.
336 """
337 # get the per user site-package path
338 # this call will also make sure USER_BASE and USER_SITE are set
339 _trace("Processing user site-packages")
340 user_site = getusersitepackages()
341
342 if ENABLE_USER_SITE and os.path.isdir(user_site):
343 addsitedir(user_site, known_paths)
344 return known_paths
345
346 def getsitepackages(prefixes=None):
347 """Returns a list containing all global site-packages directories.
348
349 For each directory present in ``prefixes`` (or the global ``PREFIXES``),
350 this function will find its `site-packages` subdirectory depending on the
351 system environment, and will return a list of full paths.
352 """
353 sitepackages = []
354 seen = set()
355
356 if prefixes is None:
357 prefixes = PREFIXES
358
359 for prefix in prefixes:
360 if not prefix or prefix in seen:
361 continue
362 seen.add(prefix)
363
364 if os.sep == '/':
365 libdirs = [sys.platlibdir]
366 if sys.platlibdir != "lib":
367 libdirs.append("lib")
368
369 for libdir in libdirs:
370 path = os.path.join(prefix, libdir,
371 "python%d.%d" % sys.version_info[:2],
372 "site-packages")
373 sitepackages.append(path)
374 else:
375 sitepackages.append(prefix)
376 sitepackages.append(os.path.join(prefix, "Lib", "site-packages"))
377 return sitepackages
378
379 def addsitepackages(known_paths, prefixes=None):
380 """Add site-packages to sys.path"""
381 _trace("Processing global site-packages")
382 for sitedir in getsitepackages(prefixes):
383 if os.path.isdir(sitedir):
384 addsitedir(sitedir, known_paths)
385
386 return known_paths
387
388 def setquit():
389 """Define new builtins 'quit' and 'exit'.
390
391 These are objects which make the interpreter exit when called.
392 The repr of each object contains a hint at how it works.
393
394 """
395 if os.sep == '\\':
396 eof = 'Ctrl-Z plus Return'
397 else:
398 eof = 'Ctrl-D (i.e. EOF)'
399
400 builtins.quit = _sitebuiltins.Quitter('quit', eof)
401 builtins.exit = _sitebuiltins.Quitter('exit', eof)
402
403
404 def setcopyright():
405 """Set 'copyright' and 'credits' in builtins"""
406 builtins.copyright = _sitebuiltins._Printer("copyright", sys.copyright)
407 builtins.credits = _sitebuiltins._Printer("credits", """\
408 Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
409 for supporting Python development. See www.python.org for more information.""")
410 files, dirs = [], []
411 # Not all modules are required to have a __file__ attribute. See
412 # PEP 420 for more details.
413 here = getattr(sys, '_stdlib_dir', None)
414 if not here and hasattr(os, '__file__'):
415 here = os.path.dirname(os.__file__)
416 if here:
417 files.extend(["LICENSE.txt", "LICENSE"])
418 dirs.extend([os.path.join(here, os.pardir), here, os.curdir])
419 builtins.license = _sitebuiltins._Printer(
420 "license",
421 "See https://www.python.org/psf/license/",
422 files, dirs)
423
424
425 def sethelper():
426 builtins.help = _sitebuiltins._Helper()
427
428 def enablerlcompleter():
429 """Enable default readline configuration on interactive prompts, by
430 registering a sys.__interactivehook__.
431
432 If the readline module can be imported, the hook will set the Tab key
433 as completion key and register ~/.python_history as history file.
434 This can be overridden in the sitecustomize or usercustomize module,
435 or in a PYTHONSTARTUP file.
436 """
437 def register_readline():
438 import atexit
439 try:
440 import readline
441 import rlcompleter
442 except ImportError:
443 return
444
445 # Reading the initialization (config) file may not be enough to set a
446 # completion key, so we set one first and then read the file.
447 readline_doc = getattr(readline, '__doc__', '')
448 if readline_doc is not None and 'libedit' in readline_doc:
449 readline.parse_and_bind('bind ^I rl_complete')
450 else:
451 readline.parse_and_bind('tab: complete')
452
453 try:
454 readline.read_init_file()
455 except OSError:
456 # An OSError here could have many causes, but the most likely one
457 # is that there's no .inputrc file (or .editrc file in the case of
458 # Mac OS X + libedit) in the expected location. In that case, we
459 # want to ignore the exception.
460 pass
461
462 if readline.get_current_history_length() == 0:
463 # If no history was loaded, default to .python_history.
464 # The guard is necessary to avoid doubling history size at
465 # each interpreter exit when readline was already configured
466 # through a PYTHONSTARTUP hook, see:
467 # http://bugs.python.org/issue5845#msg198636
468 history = os.path.join(os.path.expanduser('~'),
469 '.python_history')
470 try:
471 readline.read_history_file(history)
472 except OSError:
473 pass
474
475 def write_history():
476 try:
477 readline.write_history_file(history)
478 except OSError:
479 # bpo-19891, bpo-41193: Home directory does not exist
480 # or is not writable, or the filesystem is read-only.
481 pass
482
483 atexit.register(write_history)
484
485 sys.__interactivehook__ = register_readline
486
487 def venv(known_paths):
488 global PREFIXES, ENABLE_USER_SITE
489
490 env = os.environ
491 if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in env:
492 executable = sys._base_executable = os.environ['__PYVENV_LAUNCHER__']
493 else:
494 executable = sys.executable
495 exe_dir = os.path.dirname(os.path.abspath(executable))
496 site_prefix = os.path.dirname(exe_dir)
497 sys._home = None
498 conf_basename = 'pyvenv.cfg'
499 candidate_conf = next(
500 (
501 conffile for conffile in (
502 os.path.join(exe_dir, conf_basename),
503 os.path.join(site_prefix, conf_basename)
504 )
505 if os.path.isfile(conffile)
506 ),
507 None
508 )
509
510 if candidate_conf:
511 virtual_conf = candidate_conf
512 system_site = "true"
513 # Issue 25185: Use UTF-8, as that's what the venv module uses when
514 # writing the file.
515 with open(virtual_conf, encoding='utf-8') as f:
516 for line in f:
517 if '=' in line:
518 key, _, value = line.partition('=')
519 key = key.strip().lower()
520 value = value.strip()
521 if key == 'include-system-site-packages':
522 system_site = value.lower()
523 elif key == 'home':
524 sys._home = value
525
526 sys.prefix = sys.exec_prefix = site_prefix
527
528 # Doing this here ensures venv takes precedence over user-site
529 addsitepackages(known_paths, [sys.prefix])
530
531 # addsitepackages will process site_prefix again if its in PREFIXES,
532 # but that's ok; known_paths will prevent anything being added twice
533 if system_site == "true":
534 PREFIXES.insert(0, sys.prefix)
535 else:
536 PREFIXES = [sys.prefix]
537 ENABLE_USER_SITE = False
538
539 return known_paths
540
541
542 def execsitecustomize():
543 """Run custom site specific code, if available."""
544 try:
545 try:
546 import sitecustomize
547 except ImportError as exc:
548 if exc.name == 'sitecustomize':
549 pass
550 else:
551 raise
552 except Exception as err:
553 if sys.flags.verbose:
554 sys.excepthook(*sys.exc_info())
555 else:
556 sys.stderr.write(
557 "Error in sitecustomize; set PYTHONVERBOSE for traceback:\n"
558 "%s: %s\n" %
559 (err.__class__.__name__, err))
560
561
562 def execusercustomize():
563 """Run custom user specific code, if available."""
564 try:
565 try:
566 import usercustomize
567 except ImportError as exc:
568 if exc.name == 'usercustomize':
569 pass
570 else:
571 raise
572 except Exception as err:
573 if sys.flags.verbose:
574 sys.excepthook(*sys.exc_info())
575 else:
576 sys.stderr.write(
577 "Error in usercustomize; set PYTHONVERBOSE for traceback:\n"
578 "%s: %s\n" %
579 (err.__class__.__name__, err))
580
581
582 def main():
583 """Add standard site-specific directories to the module search path.
584
585 This function is called automatically when this module is imported,
586 unless the python interpreter was started with the -S flag.
587 """
588 global ENABLE_USER_SITE
589
590 orig_path = sys.path[:]
591 known_paths = removeduppaths()
592 if orig_path != sys.path:
593 # removeduppaths() might make sys.path absolute.
594 # fix __file__ and __cached__ of already imported modules too.
595 abs_paths()
596
597 known_paths = venv(known_paths)
598 if ENABLE_USER_SITE is None:
599 ENABLE_USER_SITE = check_enableusersite()
600 known_paths = addusersitepackages(known_paths)
601 known_paths = addsitepackages(known_paths)
602 setquit()
603 setcopyright()
604 sethelper()
605 if not sys.flags.isolated:
606 enablerlcompleter()
607 execsitecustomize()
608 if ENABLE_USER_SITE:
609 execusercustomize()
610
611 # Prevent extending of sys.path when python was started with -S and
612 # site is imported later.
613 if not sys.flags.no_site:
614 main()
615
616 def _script():
617 help = """\
618 %s [--user-base] [--user-site]
619
620 Without arguments print some useful information
621 With arguments print the value of USER_BASE and/or USER_SITE separated
622 by '%s'.
623
624 Exit codes with --user-base or --user-site:
625 0 - user site directory is enabled
626 1 - user site directory is disabled by user
627 2 - user site directory is disabled by super user
628 or for security reasons
629 >2 - unknown error
630 """
631 args = sys.argv[1:]
632 if not args:
633 user_base = getuserbase()
634 user_site = getusersitepackages()
635 print("sys.path = [")
636 for dir in sys.path:
637 print(" %r," % (dir,))
638 print("]")
639 def exists(path):
640 if path is not None and os.path.isdir(path):
641 return "exists"
642 else:
643 return "doesn't exist"
644 print(f"USER_BASE: {user_base!r} ({exists(user_base)})")
645 print(f"USER_SITE: {user_site!r} ({exists(user_site)})")
646 print(f"ENABLE_USER_SITE: {ENABLE_USER_SITE!r}")
647 sys.exit(0)
648
649 buffer = []
650 if '--user-base' in args:
651 buffer.append(USER_BASE)
652 if '--user-site' in args:
653 buffer.append(USER_SITE)
654
655 if buffer:
656 print(os.pathsep.join(buffer))
657 if ENABLE_USER_SITE:
658 sys.exit(0)
659 elif ENABLE_USER_SITE is False:
660 sys.exit(1)
661 elif ENABLE_USER_SITE is None:
662 sys.exit(2)
663 else:
664 sys.exit(3)
665 else:
666 import textwrap
667 print(textwrap.dedent(help % (sys.argv[0], os.pathsep)))
668 sys.exit(10)
669
670 if __name__ == '__main__':
671 _script()