1"""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 _trace(message): 92 if sys.flags.verbose: 93 print(message, file=sys.stderr) 94 95 96def 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 105def 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 128def 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 147def _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 160def 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: 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(*sys.exc_info()): 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 207def 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 232def 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() 264def _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') 288def _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 301def 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 314def 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 331def 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 346def 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 379def 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 388def 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 404def setcopyright(): 405 """Set 'copyright' and 'credits' in builtins""" 406 builtins.copyright = _sitebuiltins._Printer("copyright", sys.copyright) 407 if sys.platform[:4] == 'java': 408 builtins.credits = _sitebuiltins._Printer( 409 "credits", 410 "Jython is maintained by the Jython developers (www.jython.org).") 411 else: 412 builtins.credits = _sitebuiltins._Printer("credits", """\ 413 Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands 414 for supporting Python development. See www.python.org for more information.""") 415 files, dirs = [], [] 416 # Not all modules are required to have a __file__ attribute. See 417 # PEP 420 for more details. 418 here = getattr(sys, '_stdlib_dir', None) 419 if not here and hasattr(os, '__file__'): 420 here = os.path.dirname(os.__file__) 421 if here: 422 files.extend(["LICENSE.txt", "LICENSE"]) 423 dirs.extend([os.path.join(here, os.pardir), here, os.curdir]) 424 builtins.license = _sitebuiltins._Printer( 425 "license", 426 "See https://www.python.org/psf/license/", 427 files, dirs) 428 429 430def sethelper(): 431 builtins.help = _sitebuiltins._Helper() 432 433def enablerlcompleter(): 434 """Enable default readline configuration on interactive prompts, by 435 registering a sys.__interactivehook__. 436 437 If the readline module can be imported, the hook will set the Tab key 438 as completion key and register ~/.python_history as history file. 439 This can be overridden in the sitecustomize or usercustomize module, 440 or in a PYTHONSTARTUP file. 441 """ 442 def register_readline(): 443 import atexit 444 try: 445 import readline 446 import rlcompleter 447 except ImportError: 448 return 449 450 # Reading the initialization (config) file may not be enough to set a 451 # completion key, so we set one first and then read the file. 452 readline_doc = getattr(readline, '__doc__', '') 453 if readline_doc is not None and 'libedit' in readline_doc: 454 readline.parse_and_bind('bind ^I rl_complete') 455 else: 456 readline.parse_and_bind('tab: complete') 457 458 try: 459 readline.read_init_file() 460 except OSError: 461 # An OSError here could have many causes, but the most likely one 462 # is that there's no .inputrc file (or .editrc file in the case of 463 # Mac OS X + libedit) in the expected location. In that case, we 464 # want to ignore the exception. 465 pass 466 467 if readline.get_current_history_length() == 0: 468 # If no history was loaded, default to .python_history. 469 # The guard is necessary to avoid doubling history size at 470 # each interpreter exit when readline was already configured 471 # through a PYTHONSTARTUP hook, see: 472 # http://bugs.python.org/issue5845#msg198636 473 history = os.path.join(os.path.expanduser('~'), 474 '.python_history') 475 try: 476 readline.read_history_file(history) 477 except OSError: 478 pass 479 480 def write_history(): 481 try: 482 readline.write_history_file(history) 483 except OSError: 484 # bpo-19891, bpo-41193: Home directory does not exist 485 # or is not writable, or the filesystem is read-only. 486 pass 487 488 atexit.register(write_history) 489 490 sys.__interactivehook__ = register_readline 491 492def venv(known_paths): 493 global PREFIXES, ENABLE_USER_SITE 494 495 env = os.environ 496 if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in env: 497 executable = sys._base_executable = os.environ['__PYVENV_LAUNCHER__'] 498 else: 499 executable = sys.executable 500 exe_dir, _ = os.path.split(os.path.abspath(executable)) 501 site_prefix = os.path.dirname(exe_dir) 502 sys._home = None 503 conf_basename = 'pyvenv.cfg' 504 candidate_confs = [ 505 conffile for conffile in ( 506 os.path.join(exe_dir, conf_basename), 507 os.path.join(site_prefix, conf_basename) 508 ) 509 if os.path.isfile(conffile) 510 ] 511 512 if candidate_confs: 513 virtual_conf = candidate_confs[0] 514 system_site = "true" 515 # Issue 25185: Use UTF-8, as that's what the venv module uses when 516 # writing the file. 517 with open(virtual_conf, encoding='utf-8') as f: 518 for line in f: 519 if '=' in line: 520 key, _, value = line.partition('=') 521 key = key.strip().lower() 522 value = value.strip() 523 if key == 'include-system-site-packages': 524 system_site = value.lower() 525 elif key == 'home': 526 sys._home = value 527 528 sys.prefix = sys.exec_prefix = site_prefix 529 530 # Doing this here ensures venv takes precedence over user-site 531 addsitepackages(known_paths, [sys.prefix]) 532 533 # addsitepackages will process site_prefix again if its in PREFIXES, 534 # but that's ok; known_paths will prevent anything being added twice 535 if system_site == "true": 536 PREFIXES.insert(0, sys.prefix) 537 else: 538 PREFIXES = [sys.prefix] 539 ENABLE_USER_SITE = False 540 541 return known_paths 542 543 544def execsitecustomize(): 545 """Run custom site specific code, if available.""" 546 try: 547 try: 548 import sitecustomize 549 except ImportError as exc: 550 if exc.name == 'sitecustomize': 551 pass 552 else: 553 raise 554 except Exception as err: 555 if sys.flags.verbose: 556 sys.excepthook(*sys.exc_info()) 557 else: 558 sys.stderr.write( 559 "Error in sitecustomize; set PYTHONVERBOSE for traceback:\n" 560 "%s: %s\n" % 561 (err.__class__.__name__, err)) 562 563 564def execusercustomize(): 565 """Run custom user specific code, if available.""" 566 try: 567 try: 568 import usercustomize 569 except ImportError as exc: 570 if exc.name == 'usercustomize': 571 pass 572 else: 573 raise 574 except Exception as err: 575 if sys.flags.verbose: 576 sys.excepthook(*sys.exc_info()) 577 else: 578 sys.stderr.write( 579 "Error in usercustomize; set PYTHONVERBOSE for traceback:\n" 580 "%s: %s\n" % 581 (err.__class__.__name__, err)) 582 583 584def main(): 585 """Add standard site-specific directories to the module search path. 586 587 This function is called automatically when this module is imported, 588 unless the python interpreter was started with the -S flag. 589 """ 590 global ENABLE_USER_SITE 591 592 orig_path = sys.path[:] 593 known_paths = removeduppaths() 594 if orig_path != sys.path: 595 # removeduppaths() might make sys.path absolute. 596 # fix __file__ and __cached__ of already imported modules too. 597 abs_paths() 598 599 known_paths = venv(known_paths) 600 if ENABLE_USER_SITE is None: 601 ENABLE_USER_SITE = check_enableusersite() 602 known_paths = addusersitepackages(known_paths) 603 known_paths = addsitepackages(known_paths) 604 setquit() 605 setcopyright() 606 sethelper() 607 if not sys.flags.isolated: 608 enablerlcompleter() 609 execsitecustomize() 610 if ENABLE_USER_SITE: 611 execusercustomize() 612 613# Prevent extending of sys.path when python was started with -S and 614# site is imported later. 615if not sys.flags.no_site: 616 main() 617 618def _script(): 619 help = """\ 620 %s [--user-base] [--user-site] 621 622 Without arguments print some useful information 623 With arguments print the value of USER_BASE and/or USER_SITE separated 624 by '%s'. 625 626 Exit codes with --user-base or --user-site: 627 0 - user site directory is enabled 628 1 - user site directory is disabled by user 629 2 - user site directory is disabled by super user 630 or for security reasons 631 >2 - unknown error 632 """ 633 args = sys.argv[1:] 634 if not args: 635 user_base = getuserbase() 636 user_site = getusersitepackages() 637 print("sys.path = [") 638 for dir in sys.path: 639 print(" %r," % (dir,)) 640 print("]") 641 def exists(path): 642 if path is not None and os.path.isdir(path): 643 return "exists" 644 else: 645 return "doesn't exist" 646 print(f"USER_BASE: {user_base!r} ({exists(user_base)})") 647 print(f"USER_SITE: {user_site!r} ({exists(user_site)})") 648 print(f"ENABLE_USER_SITE: {ENABLE_USER_SITE!r}") 649 sys.exit(0) 650 651 buffer = [] 652 if '--user-base' in args: 653 buffer.append(USER_BASE) 654 if '--user-site' in args: 655 buffer.append(USER_SITE) 656 657 if buffer: 658 print(os.pathsep.join(buffer)) 659 if ENABLE_USER_SITE: 660 sys.exit(0) 661 elif ENABLE_USER_SITE is False: 662 sys.exit(1) 663 elif ENABLE_USER_SITE is None: 664 sys.exit(2) 665 else: 666 sys.exit(3) 667 else: 668 import textwrap 669 print(textwrap.dedent(help % (sys.argv[0], os.pathsep))) 670 sys.exit(10) 671 672if __name__ == '__main__': 673 _script() 674