site.py 20.8 KB
Newer Older
1
"""Append module search paths for third-party packages to sys.path.
2

3 4 5
****************************************************************
* This module is automatically imported during initialization. *
****************************************************************
6

7
This will append site-specific paths to the module search path.  On
8 9
Unix (including Mac OSX), it starts with sys.prefix and
sys.exec_prefix (if different) and appends
10
lib/python<version>/site-packages.
11 12
On other platforms (such as Windows), it tries each of the
prefixes directly, as well as with lib/site-packages appended.  The
13 14
resulting directories, if they exist, are appended to sys.path, and
also inspected for path configuration files.
15

16 17
If a file named "pyvenv.cfg" exists one directory above sys.executable,
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 21 22 23 24 25 26 27 28
installation). If "pyvenv.cfg" (a bootstrap configuration file) contains
the key "include-system-site-packages" set to anything other than "false"
(case-insensitive), the system-level prefixes will still also be
searched for site-packages; otherwise they won't.

All of the resulting site-specific directories, if they exist, are
appended to sys.path, and also inspected for path configuration
files.

29 30 31 32 33
A path configuration file is a file whose name has the form
<package>.pth; its contents are additional directories (one per line)
to be added to sys.path.  Non-existing directories (or
non-directories) are never added to sys.path; no directory is added to
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 39 40
with three subdirectories, foo, bar and spam, and two path
configuration files, foo.pth and bar.pth.  Assume foo.pth contains the
following:
41 42 43 44 45 46 47 48 49 50 51 52 53

  # foo package configuration
  foo
  bar
  bletch

and bar.pth contains:

  # bar package configuration
  bar

Then the following directories are added to sys.path, in this order:

54 55
  /usr/local/lib/python2.5/site-packages/bar
  /usr/local/lib/python2.5/site-packages/foo
56 57 58 59

Note that bletch is omitted because it doesn't exist; bar precedes foo
because bar.pth comes alphabetically before foo.pth; and spam is
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 64
sitecustomize, usercustomize or PYTHONSTARTUP.  Starting Python in
isolated mode (-I) disables automatic readline configuration.
65 66

After these operations, an attempt is made to import a module
67 68 69
named sitecustomize, which can perform arbitrary additional
site-specific customizations.  If this import fails with an
ImportError exception, it is silently ignored.
70 71
"""

72 73
import sys
import os
74
import builtins
75
import _sitebuiltins
76

77 78 79 80 81
# Prefixes for site-packages; add additional prefixes like /usr/local here
PREFIXES = [sys.prefix, sys.exec_prefix]
# Enable per user site-packages directory
# set it to False to disable the feature or True to force the feature
ENABLE_USER_SITE = None
82

83
# for distutils.commands.install
84 85
# These values are initialized by the getuserbase() and getusersitepackages()
# functions, through the main() function when Python starts.
86 87 88
USER_SITE = None
USER_BASE = None

89

90
def makepath(*paths):
91 92 93 94 95
    dir = os.path.join(*paths)
    try:
        dir = os.path.abspath(dir)
    except OSError:
        pass
96
    return dir, os.path.normcase(dir)
97

98

Barry Warsaw's avatar
Barry Warsaw committed
99 100
def abs_paths():
    """Set all module __file__ and __cached__ attributes to an absolute path"""
101
    for m in set(sys.modules.values()):
102 103
        if (getattr(getattr(m, '__loader__', None), '__module__', None) not in
                ('_frozen_importlib', '_frozen_importlib_external')):
104
            continue   # don't mess with a PEP 302-supplied __file__
105 106
        try:
            m.__file__ = os.path.abspath(m.__file__)
107
        except (AttributeError, OSError, TypeError):
Barry Warsaw's avatar
Barry Warsaw committed
108 109 110
            pass
        try:
            m.__cached__ = os.path.abspath(m.__cached__)
111
        except (AttributeError, OSError, TypeError):
Barry Warsaw's avatar
Barry Warsaw committed
112
            pass
113

114

115 116 117 118 119 120 121 122 123 124 125 126
def removeduppaths():
    """ Remove duplicate entries from sys.path along with making them
    absolute"""
    # This ensures that the initial path provided by the interpreter contains
    # only absolute pathnames, even if we're running from the build directory.
    L = []
    known_paths = set()
    for dir in sys.path:
        # Filter out duplicate paths (on case-insensitive file systems also
        # if they only differ in case); turn relative paths into absolute
        # paths.
        dir, dircase = makepath(dir)
127
        if dircase not in known_paths:
128 129 130 131 132
            L.append(dir)
            known_paths.add(dircase)
    sys.path[:] = L
    return known_paths

133

134
def _init_pathinfo():
135
    """Return a set containing all existing file system items from sys.path."""
136
    d = set()
137
    for item in sys.path:
138
        try:
139 140 141
            if os.path.exists(item):
                _, itemcase = makepath(item)
                d.add(itemcase)
142
        except TypeError:
143
            continue
144
    return d
145

146

147
def addpackage(sitedir, name, known_paths):
148 149 150 151
    """Process a .pth file within the site-packages directory:
       For each line in the file, either combine it with sitedir to a path
       and add that to known_paths, or execute it if it starts with 'import '.
    """
152
    if known_paths is None:
153
        known_paths = _init_pathinfo()
154
        reset = True
155
    else:
156
        reset = False
157 158
    fullname = os.path.join(sitedir, name)
    try:
159
        f = open(fullname, "r")
160
    except OSError:
161
        return
162
    with f:
163
        for n, line in enumerate(f):
164 165
            if line.startswith("#"):
                continue
166 167 168 169 170 171 172 173 174
            try:
                if line.startswith(("import ", "import\t")):
                    exec(line)
                    continue
                line = line.rstrip()
                dir, dircase = makepath(sitedir, line)
                if not dircase in known_paths and os.path.exists(dir):
                    sys.path.append(dir)
                    known_paths.add(dircase)
175
            except Exception:
176 177
                print("Error processing line {:d} of {}:\n".format(n+1, fullname),
                      file=sys.stderr)
178
                import traceback
179 180 181 182 183
                for record in traceback.format_exception(*sys.exc_info()):
                    for line in record.splitlines():
                        print('  '+line, file=sys.stderr)
                print("\nRemainder of file ignored", file=sys.stderr)
                break
184 185 186 187
    if reset:
        known_paths = None
    return known_paths

188

189
def addsitedir(sitedir, known_paths=None):
190 191 192
    """Add 'sitedir' argument to sys.path if missing and handle .pth files in
    'sitedir'"""
    if known_paths is None:
193
        known_paths = _init_pathinfo()
194
        reset = True
195
    else:
196
        reset = False
197
    sitedir, sitedircase = makepath(sitedir)
198
    if not sitedircase in known_paths:
199
        sys.path.append(sitedir)        # Add path component
200
        known_paths.add(sitedircase)
201
    try:
202
        names = os.listdir(sitedir)
203
    except OSError:
204
        return
205 206 207
    names = [name for name in names if name.endswith(".pth")]
    for name in sorted(names):
        addpackage(sitedir, name, known_paths)
208
    if reset:
209 210 211
        known_paths = None
    return known_paths

212 213 214 215

def check_enableusersite():
    """Check if user site directory is safe for inclusion

216
    The function tests for the command line flag (including environment var),
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
    process uid/gid equal to effective uid/gid.

    None: Disabled for security reasons
    False: Disabled by user (command line option)
    True: Safe and enabled
    """
    if sys.flags.no_user_site:
        return False

    if hasattr(os, "getuid") and hasattr(os, "geteuid"):
        # check process uid == effective uid
        if os.geteuid() != os.getuid():
            return None
    if hasattr(os, "getgid") and hasattr(os, "getegid"):
        # check process gid == effective gid
        if os.getegid() != os.getgid():
            return None

    return True

237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268

# NOTE: sysconfig and it's dependencies are relatively large but site module
# needs very limited part of them.
# To speedup startup time, we have copy of them.
#
# See https://bugs.python.org/issue29585

# Copy of sysconfig._getuserbase()
def _getuserbase():
    env_base = os.environ.get("PYTHONUSERBASE", None)
    if env_base:
        return env_base

    def joinuser(*args):
        return os.path.expanduser(os.path.join(*args))

    if os.name == "nt":
        base = os.environ.get("APPDATA") or "~"
        return joinuser(base, "Python")

    if sys.platform == "darwin" and sys._framework:
        return joinuser("~", "Library", sys._framework,
                        "%d.%d" % sys.version_info[:2])

    return joinuser("~", ".local")


# Same to sysconfig.get_path('purelib', os.name+'_user')
def _get_path(userbase):
    version = sys.version_info

    if os.name == 'nt':
269
        return f'{userbase}\\Python{version[0]}{version[1]}\\site-packages'
270 271 272 273 274 275 276

    if sys.platform == 'darwin' and sys._framework:
        return f'{userbase}/lib/python/site-packages'

    return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages'


277 278
def getuserbase():
    """Returns the `user base` directory path.
279

280 281 282
    The `user base` directory can be used to store data. If the global
    variable ``USER_BASE`` is not initialized yet, this function will also set
    it.
283
    """
284
    global USER_BASE
285 286
    if USER_BASE is None:
        USER_BASE = _getuserbase()
287 288
    return USER_BASE

289

290 291 292 293 294 295 296
def getusersitepackages():
    """Returns the user-specific site-packages directory path.

    If the global variable ``USER_SITE`` is not initialized yet, this
    function will also set it.
    """
    global USER_SITE
297
    userbase = getuserbase() # this will also set USER_BASE
298

299 300
    if USER_SITE is None:
        USER_SITE = _get_path(userbase)
301

302 303 304 305 306 307 308 309 310 311 312 313 314 315
    return USER_SITE

def addusersitepackages(known_paths):
    """Add a per user site-package to sys.path

    Each user has its own python directory with site-packages in the
    home directory.
    """
    # get the per user site-package path
    # this call will also make sure USER_BASE and USER_SITE are set
    user_site = getusersitepackages()

    if ENABLE_USER_SITE and os.path.isdir(user_site):
        addsitedir(user_site, known_paths)
316 317
    return known_paths

318
def getsitepackages(prefixes=None):
319
    """Returns a list containing all global site-packages directories.
320

321 322 323
    For each directory present in ``prefixes`` (or the global ``PREFIXES``),
    this function will find its `site-packages` subdirectory depending on the
    system environment, and will return a list of full paths.
324 325
    """
    sitepackages = []
326
    seen = set()
327

328 329 330 331
    if prefixes is None:
        prefixes = PREFIXES

    for prefix in prefixes:
332 333
        if not prefix or prefix in seen:
            continue
334
        seen.add(prefix)
335

336
        if os.sep == '/':
337
            sitepackages.append(os.path.join(prefix, "lib",
338
                                        "python%d.%d" % sys.version_info[:2],
339 340
                                        "site-packages"))
        else:
341 342 343
            sitepackages.append(prefix)
            sitepackages.append(os.path.join(prefix, "lib", "site-packages"))
    return sitepackages
344

345
def addsitepackages(known_paths, prefixes=None):
346
    """Add site-packages to sys.path"""
347
    for sitedir in getsitepackages(prefixes):
348 349 350 351
        if os.path.isdir(sitedir):
            addsitedir(sitedir, known_paths)

    return known_paths
352 353

def setquit():
354 355 356 357
    """Define new builtins 'quit' and 'exit'.

    These are objects which make the interpreter exit when called.
    The repr of each object contains a hint at how it works.
358 359

    """
360
    if os.sep == '\\':
361
        eof = 'Ctrl-Z plus Return'
362
    else:
363
        eof = 'Ctrl-D (i.e. EOF)'
Tim Peters's avatar
Tim Peters committed
364

365 366
    builtins.quit = _sitebuiltins.Quitter('quit', eof)
    builtins.exit = _sitebuiltins.Quitter('exit', eof)
367

368

369
def setcopyright():
370
    """Set 'copyright' and 'credits' in builtins"""
371
    builtins.copyright = _sitebuiltins._Printer("copyright", sys.copyright)
372
    if sys.platform[:4] == 'java':
373
        builtins.credits = _sitebuiltins._Printer(
374
            "credits",
375
            "Jython is maintained by the Jython developers (www.jython.org).")
376
    else:
377
        builtins.credits = _sitebuiltins._Printer("credits", """\
378
    Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
379
    for supporting Python development.  See www.python.org for more information.""")
380 381 382 383 384 385 386
    files, dirs = [], []
    # Not all modules are required to have a __file__ attribute.  See
    # PEP 420 for more details.
    if hasattr(os, '__file__'):
        here = os.path.dirname(os.__file__)
        files.extend(["LICENSE.txt", "LICENSE"])
        dirs.extend([os.path.join(here, os.pardir), here, os.curdir])
387
    builtins.license = _sitebuiltins._Printer(
388
        "license",
Benjamin Peterson's avatar
Benjamin Peterson committed
389
        "See https://www.python.org/psf/license/",
390
        files, dirs)
391 392 393


def sethelper():
394
    builtins.help = _sitebuiltins._Helper()
395

396 397 398 399 400 401
def enablerlcompleter():
    """Enable default readline configuration on interactive prompts, by
    registering a sys.__interactivehook__.

    If the readline module can be imported, the hook will set the Tab key
    as completion key and register ~/.python_history as history file.
402
    This can be overridden in the sitecustomize or usercustomize module,
403 404 405 406 407 408 409
    or in a PYTHONSTARTUP file.
    """
    def register_readline():
        import atexit
        try:
            import readline
            import rlcompleter
410
        except ImportError:
411 412 413
            return

        # Reading the initialization (config) file may not be enough to set a
414 415 416
        # completion key, so we set one first and then read the file.
        readline_doc = getattr(readline, '__doc__', '')
        if readline_doc is not None and 'libedit' in readline_doc:
417 418 419
            readline.parse_and_bind('bind ^I rl_complete')
        else:
            readline.parse_and_bind('tab: complete')
420 421 422 423 424 425 426 427 428

        try:
            readline.read_init_file()
        except OSError:
            # An OSError here could have many causes, but the most likely one
            # is that there's no .inputrc file (or .editrc file in the case of
            # Mac OS X + libedit) in the expected location.  In that case, we
            # want to ignore the exception.
            pass
429

430
        if readline.get_current_history_length() == 0:
431 432 433 434 435 436 437 438 439
            # If no history was loaded, default to .python_history.
            # The guard is necessary to avoid doubling history size at
            # each interpreter exit when readline was already configured
            # through a PYTHONSTARTUP hook, see:
            # http://bugs.python.org/issue5845#msg198636
            history = os.path.join(os.path.expanduser('~'),
                                   '.python_history')
            try:
                readline.read_history_file(history)
440
            except OSError:
441
                pass
442 443 444 445 446 447 448 449 450 451

            def write_history():
                try:
                    readline.write_history_file(history)
                except (FileNotFoundError, PermissionError):
                    # home directory does not exist or is not writable
                    # https://bugs.python.org/issue19891
                    pass

            atexit.register(write_history)
452 453 454

    sys.__interactivehook__ = register_readline

455 456 457 458
def venv(known_paths):
    global PREFIXES, ENABLE_USER_SITE

    env = os.environ
459 460
    if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in env:
        executable = os.environ['__PYVENV_LAUNCHER__']
461 462
    else:
        executable = sys.executable
463 464
    exe_dir, _ = os.path.split(os.path.abspath(executable))
    site_prefix = os.path.dirname(exe_dir)
465 466 467 468
    sys._home = None
    conf_basename = 'pyvenv.cfg'
    candidate_confs = [
        conffile for conffile in (
469
            os.path.join(exe_dir, conf_basename),
470 471 472 473 474 475 476 477
            os.path.join(site_prefix, conf_basename)
            )
        if os.path.isfile(conffile)
        ]

    if candidate_confs:
        virtual_conf = candidate_confs[0]
        system_site = "true"
478 479 480
        # Issue 25185: Use UTF-8, as that's what the venv module uses when
        # writing the file.
        with open(virtual_conf, encoding='utf-8') as f:
481
            for line in f:
482 483 484 485
                if '=' in line:
                    key, _, value = line.partition('=')
                    key = key.strip().lower()
                    value = value.strip()
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
                    if key == 'include-system-site-packages':
                        system_site = value.lower()
                    elif key == 'home':
                        sys._home = value

        sys.prefix = sys.exec_prefix = site_prefix

        # Doing this here ensures venv takes precedence over user-site
        addsitepackages(known_paths, [sys.prefix])

        # addsitepackages will process site_prefix again if its in PREFIXES,
        # but that's ok; known_paths will prevent anything being added twice
        if system_site == "true":
            PREFIXES.insert(0, sys.prefix)
        else:
            PREFIXES = [sys.prefix]
            ENABLE_USER_SITE = False

    return known_paths


507 508 509
def execsitecustomize():
    """Run custom site specific code, if available."""
    try:
510 511 512 513 514 515 516
        try:
            import sitecustomize
        except ImportError as exc:
            if exc.name == 'sitecustomize':
                pass
            else:
                raise
517
    except Exception as err:
518
        if sys.flags.verbose:
519 520 521 522 523 524
            sys.excepthook(*sys.exc_info())
        else:
            sys.stderr.write(
                "Error in sitecustomize; set PYTHONVERBOSE for traceback:\n"
                "%s: %s\n" %
                (err.__class__.__name__, err))
525 526


527 528 529
def execusercustomize():
    """Run custom user specific code, if available."""
    try:
530 531 532 533 534 535 536
        try:
            import usercustomize
        except ImportError as exc:
            if exc.name == 'usercustomize':
                pass
            else:
                raise
537
    except Exception as err:
538
        if sys.flags.verbose:
539 540 541 542 543 544
            sys.excepthook(*sys.exc_info())
        else:
            sys.stderr.write(
                "Error in usercustomize; set PYTHONVERBOSE for traceback:\n"
                "%s: %s\n" %
                (err.__class__.__name__, err))
545 546


547
def main():
548 549 550 551 552
    """Add standard site-specific directories to the module search path.

    This function is called automatically when this module is imported,
    unless the python interpreter was started with the -S flag.
    """
553 554
    global ENABLE_USER_SITE

555
    orig_path = sys.path[:]
556
    known_paths = removeduppaths()
557 558 559 560 561
    if orig_path != sys.path:
        # removeduppaths() might make sys.path absolute.
        # fix __file__ and __cached__ of already imported modules too.
        abs_paths()

562
    known_paths = venv(known_paths)
563 564 565 566
    if ENABLE_USER_SITE is None:
        ENABLE_USER_SITE = check_enableusersite()
    known_paths = addusersitepackages(known_paths)
    known_paths = addsitepackages(known_paths)
567 568 569
    setquit()
    setcopyright()
    sethelper()
570 571
    if not sys.flags.isolated:
        enablerlcompleter()
572
    execsitecustomize()
573 574
    if ENABLE_USER_SITE:
        execusercustomize()
575

576
# Prevent extending of sys.path when python was started with -S and
577 578 579
# site is imported later.
if not sys.flags.no_site:
    main()
580

581 582 583 584 585 586 587 588 589 590
def _script():
    help = """\
    %s [--user-base] [--user-site]

    Without arguments print some useful information
    With arguments print the value of USER_BASE and/or USER_SITE separated
    by '%s'.

    Exit codes with --user-base or --user-site:
      0 - user site directory is enabled
591
      1 - user site directory is disabled by user
592 593 594 595 596 597
      2 - uses site directory is disabled by super user
          or for security reasons
     >2 - unknown error
    """
    args = sys.argv[1:]
    if not args:
598 599
        user_base = getuserbase()
        user_site = getusersitepackages()
600 601 602 603
        print("sys.path = [")
        for dir in sys.path:
            print("    %r," % (dir,))
        print("]")
604 605 606 607
        print("USER_BASE: %r (%s)" % (user_base,
            "exists" if os.path.isdir(user_base) else "doesn't exist"))
        print("USER_SITE: %r (%s)" % (user_site,
            "exists" if os.path.isdir(user_site) else "doesn't exist"))
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
        print("ENABLE_USER_SITE: %r" %  ENABLE_USER_SITE)
        sys.exit(0)

    buffer = []
    if '--user-base' in args:
        buffer.append(USER_BASE)
    if '--user-site' in args:
        buffer.append(USER_SITE)

    if buffer:
        print(os.pathsep.join(buffer))
        if ENABLE_USER_SITE:
            sys.exit(0)
        elif ENABLE_USER_SITE is False:
            sys.exit(1)
        elif ENABLE_USER_SITE is None:
            sys.exit(2)
        else:
            sys.exit(3)
    else:
        import textwrap
        print(textwrap.dedent(help % (sys.argv[0], os.pathsep)))
        sys.exit(10)
631 632

if __name__ == '__main__':
633
    _script()