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