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