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