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