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
76
77# Prefixes for site-packages; add additional prefixes like /usr/local here
78PREFIXES = [sys.prefix, sys.exec_prefix]
79# Enable per user site-packages directory
80# set it to False to disable the feature or True to force the feature
81ENABLE_USER_SITE = None
82
83# for distutils.commands.install
84# These values are initialized by the getuserbase() and getusersitepackages()
85# functions, through the main() function when Python starts.
86USER_SITE = None
87USER_BASE = None
88
89
90def makepath(*paths):
91    dir = os.path.join(*paths)
92    try:
93        dir = os.path.abspath(dir)
94    except OSError:
95        pass
96    return dir, os.path.normcase(dir)
97
98
99def abs_paths():
100    """Set all module __file__ and __cached__ attributes to an absolute path"""
101    for m in set(sys.modules.values()):
102        if (getattr(getattr(m, '__loader__', None), '__module__', None) not in
103                ('_frozen_importlib', '_frozen_importlib_external')):
104            continue   # don't mess with a PEP 302-supplied __file__
105        try:
106            m.__file__ = os.path.abspath(m.__file__)
107        except (AttributeError, OSError, TypeError):
108            pass
109        try:
110            m.__cached__ = os.path.abspath(m.__cached__)
111        except (AttributeError, OSError, TypeError):
112            pass
113
114
115def removeduppaths():
116    """ Remove duplicate entries from sys.path along with making them
117    absolute"""
118    # This ensures that the initial path provided by the interpreter contains
119    # only absolute pathnames, even if we're running from the build directory.
120    L = []
121    known_paths = set()
122    for dir in sys.path:
123        # Filter out duplicate paths (on case-insensitive file systems also
124        # if they only differ in case); turn relative paths into absolute
125        # paths.
126        dir, dircase = makepath(dir)
127        if dircase not in known_paths:
128            L.append(dir)
129            known_paths.add(dircase)
130    sys.path[:] = L
131    return known_paths
132
133
134def _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            if os.path.exists(item):
140                _, itemcase = makepath(item)
141                d.add(itemcase)
142        except TypeError:
143            continue
144    return d
145
146
147def addpackage(sitedir, name, known_paths):
148    """Process a .pth file within the site-packages directory:
149       For each line in the file, either combine it with sitedir to a path
150       and add that to known_paths, or execute it if it starts with 'import '.
151    """
152    if known_paths is None:
153        known_paths = _init_pathinfo()
154        reset = True
155    else:
156        reset = False
157    fullname = os.path.join(sitedir, name)
158    try:
159        f = open(fullname, "r")
160    except OSError:
161        return
162    with f:
163        for n, line in enumerate(f):
164            if line.startswith("#"):
165                continue
166            try:
167                if line.startswith(("import ", "import\t")):
168                    exec(line)
169                    continue
170                line = line.rstrip()
171                dir, dircase = makepath(sitedir, line)
172                if not dircase in known_paths and os.path.exists(dir):
173                    sys.path.append(dir)
174                    known_paths.add(dircase)
175            except Exception:
176                print("Error processing line {:d} of {}:\n".format(n+1, fullname),
177                      file=sys.stderr)
178                import traceback
179                for record in traceback.format_exception(*sys.exc_info()):
180                    for line in record.splitlines():
181                        print('  '+line, file=sys.stderr)
182                print("\nRemainder of file ignored", file=sys.stderr)
183                break
184    if reset:
185        known_paths = None
186    return known_paths
187
188
189def addsitedir(sitedir, known_paths=None):
190    """Add 'sitedir' argument to sys.path if missing and handle .pth files in
191    'sitedir'"""
192    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    names = [name for name in names if name.endswith(".pth")]
206    for name in sorted(names):
207        addpackage(sitedir, name, known_paths)
208    if reset:
209        known_paths = None
210    return known_paths
211
212
213def check_enableusersite():
214    """Check if user site directory is safe for inclusion
215
216    The function tests for the command line flag (including environment var),
217    process uid/gid equal to effective uid/gid.
218
219    None: Disabled for security reasons
220    False: Disabled by user (command line option)
221    True: Safe and enabled
222    """
223    if sys.flags.no_user_site:
224        return False
225
226    if hasattr(os, "getuid") and hasattr(os, "geteuid"):
227        # check process uid == effective uid
228        if os.geteuid() != os.getuid():
229            return None
230    if hasattr(os, "getgid") and hasattr(os, "getegid"):
231        # check process gid == effective gid
232        if os.getegid() != os.getgid():
233            return None
234
235    return True
236
237
238# NOTE: sysconfig and it's dependencies are relatively large but site module
239# needs very limited part of them.
240# To speedup startup time, we have copy of them.
241#
242# See https://bugs.python.org/issue29585
243
244# Copy of sysconfig._getuserbase()
245def _getuserbase():
246    env_base = os.environ.get("PYTHONUSERBASE", None)
247    if env_base:
248        return env_base
249
250    def joinuser(*args):
251        return os.path.expanduser(os.path.join(*args))
252
253    if os.name == "nt":
254        base = os.environ.get("APPDATA") or "~"
255        return joinuser(base, "Python")
256
257    if sys.platform == "darwin" and sys._framework:
258        return joinuser("~", "Library", sys._framework,
259                        "%d.%d" % sys.version_info[:2])
260
261    return joinuser("~", ".local")
262
263
264# Same to sysconfig.get_path('purelib', os.name+'_user')
265def _get_path(userbase):
266    version = sys.version_info
267
268    if os.name == 'nt':
269        return f'{userbase}\\Python{version[0]}{version[1]}\\site-packages'
270
271    if sys.platform == 'darwin' and sys._framework:
272        return f'{userbase}/lib/python/site-packages'
273
274    return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages'
275
276
277def getuserbase():
278    """Returns the `user base` directory path.
279
280    The `user base` directory can be used to store data. If the global
281    variable ``USER_BASE`` is not initialized yet, this function will also set
282    it.
283    """
284    global USER_BASE
285    if USER_BASE is None:
286        USER_BASE = _getuserbase()
287    return USER_BASE
288
289
290def getusersitepackages():
291    """Returns the user-specific site-packages directory path.
292
293    If the global variable ``USER_SITE`` is not initialized yet, this
294    function will also set it.
295    """
296    global USER_SITE
297    userbase = getuserbase() # this will also set USER_BASE
298
299    if USER_SITE is None:
300        USER_SITE = _get_path(userbase)
301
302    return USER_SITE
303
304def addusersitepackages(known_paths):
305    """Add a per user site-package to sys.path
306
307    Each user has its own python directory with site-packages in the
308    home directory.
309    """
310    # get the per user site-package path
311    # this call will also make sure USER_BASE and USER_SITE are set
312    user_site = getusersitepackages()
313
314    if ENABLE_USER_SITE and os.path.isdir(user_site):
315        addsitedir(user_site, known_paths)
316    return known_paths
317
318def getsitepackages(prefixes=None):
319    """Returns a list containing all global site-packages directories.
320
321    For each directory present in ``prefixes`` (or the global ``PREFIXES``),
322    this function will find its `site-packages` subdirectory depending on the
323    system environment, and will return a list of full paths.
324    """
325    sitepackages = []
326    seen = set()
327
328    if prefixes is None:
329        prefixes = PREFIXES
330
331    for prefix in prefixes:
332        if not prefix or prefix in seen:
333            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                                        "site-packages"))
340        else:
341            sitepackages.append(prefix)
342            sitepackages.append(os.path.join(prefix, "lib", "site-packages"))
343    return sitepackages
344
345def addsitepackages(known_paths, prefixes=None):
346    """Add site-packages to sys.path"""
347    for sitedir in getsitepackages(prefixes):
348        if os.path.isdir(sitedir):
349            addsitedir(sitedir, known_paths)
350
351    return known_paths
352
353def setquit():
354    """Define new builtins 'quit' and 'exit'.
355
356    These are objects which make the interpreter exit when called.
357    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)'
364
365    builtins.quit = _sitebuiltins.Quitter('quit', eof)
366    builtins.exit = _sitebuiltins.Quitter('exit', eof)
367
368
369def 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    files, dirs = [], []
381    # Not all modules are required to have a __file__ attribute.  See
382    # PEP 420 for more details.
383    if hasattr(os, '__file__'):
384        here = os.path.dirname(os.__file__)
385        files.extend(["LICENSE.txt", "LICENSE"])
386        dirs.extend([os.path.join(here, os.pardir), here, os.curdir])
387    builtins.license = _sitebuiltins._Printer(
388        "license",
389        "See https://www.python.org/psf/license/",
390        files, dirs)
391
392
393def sethelper():
394    builtins.help = _sitebuiltins._Helper()
395
396def enablerlcompleter():
397    """Enable default readline configuration on interactive prompts, by
398    registering a sys.__interactivehook__.
399
400    If the readline module can be imported, the hook will set the Tab key
401    as completion key and register ~/.python_history as history file.
402    This can be overridden in the sitecustomize or usercustomize module,
403    or in a PYTHONSTARTUP file.
404    """
405    def register_readline():
406        import atexit
407        try:
408            import readline
409            import rlcompleter
410        except ImportError:
411            return
412
413        # Reading the initialization (config) file may not be enough to set a
414        # completion key, so we set one first and then read the file.
415        readline_doc = getattr(readline, '__doc__', '')
416        if readline_doc is not None and 'libedit' in readline_doc:
417            readline.parse_and_bind('bind ^I rl_complete')
418        else:
419            readline.parse_and_bind('tab: complete')
420
421        try:
422            readline.read_init_file()
423        except OSError:
424            # An OSError here could have many causes, but the most likely one
425            # is that there's no .inputrc file (or .editrc file in the case of
426            # Mac OS X + libedit) in the expected location.  In that case, we
427            # want to ignore the exception.
428            pass
429
430        if readline.get_current_history_length() == 0:
431            # If no history was loaded, default to .python_history.
432            # The guard is necessary to avoid doubling history size at
433            # each interpreter exit when readline was already configured
434            # through a PYTHONSTARTUP hook, see:
435            # http://bugs.python.org/issue5845#msg198636
436            history = os.path.join(os.path.expanduser('~'),
437                                   '.python_history')
438            try:
439                readline.read_history_file(history)
440            except OSError:
441                pass
442
443            def write_history():
444                try:
445                    readline.write_history_file(history)
446                except (FileNotFoundError, PermissionError):
447                    # home directory does not exist or is not writable
448                    # https://bugs.python.org/issue19891
449                    pass
450
451            atexit.register(write_history)
452
453    sys.__interactivehook__ = register_readline
454
455def venv(known_paths):
456    global PREFIXES, ENABLE_USER_SITE
457
458    env = os.environ
459    if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in env:
460        executable = sys._base_executable = os.environ['__PYVENV_LAUNCHER__']
461    elif sys.platform == 'win32' and '__PYVENV_LAUNCHER__' in env:
462        executable = sys.executable
463        import _winapi
464        sys._base_executable = _winapi.GetModuleFileName(0)
465        # bpo-35873: Clear the environment variable to avoid it being
466        # inherited by child processes.
467        del os.environ['__PYVENV_LAUNCHER__']
468    else:
469        executable = sys.executable
470    exe_dir, _ = os.path.split(os.path.abspath(executable))
471    site_prefix = os.path.dirname(exe_dir)
472    sys._home = None
473    conf_basename = 'pyvenv.cfg'
474    candidate_confs = [
475        conffile for conffile in (
476            os.path.join(exe_dir, conf_basename),
477            os.path.join(site_prefix, conf_basename)
478            )
479        if os.path.isfile(conffile)
480        ]
481
482    if candidate_confs:
483        virtual_conf = candidate_confs[0]
484        system_site = "true"
485        # Issue 25185: Use UTF-8, as that's what the venv module uses when
486        # writing the file.
487        with open(virtual_conf, encoding='utf-8') as f:
488            for line in f:
489                if '=' in line:
490                    key, _, value = line.partition('=')
491                    key = key.strip().lower()
492                    value = value.strip()
493                    if key == 'include-system-site-packages':
494                        system_site = value.lower()
495                    elif key == 'home':
496                        sys._home = value
497
498        sys.prefix = sys.exec_prefix = site_prefix
499
500        # Doing this here ensures venv takes precedence over user-site
501        addsitepackages(known_paths, [sys.prefix])
502
503        # addsitepackages will process site_prefix again if its in PREFIXES,
504        # but that's ok; known_paths will prevent anything being added twice
505        if system_site == "true":
506            PREFIXES.insert(0, sys.prefix)
507        else:
508            PREFIXES = [sys.prefix]
509            ENABLE_USER_SITE = False
510
511    return known_paths
512
513
514def execsitecustomize():
515    """Run custom site specific code, if available."""
516    try:
517        try:
518            import sitecustomize
519        except ImportError as exc:
520            if exc.name == 'sitecustomize':
521                pass
522            else:
523                raise
524    except Exception as err:
525        if sys.flags.verbose:
526            sys.excepthook(*sys.exc_info())
527        else:
528            sys.stderr.write(
529                "Error in sitecustomize; set PYTHONVERBOSE for traceback:\n"
530                "%s: %s\n" %
531                (err.__class__.__name__, err))
532
533
534def execusercustomize():
535    """Run custom user specific code, if available."""
536    try:
537        try:
538            import usercustomize
539        except ImportError as exc:
540            if exc.name == 'usercustomize':
541                pass
542            else:
543                raise
544    except Exception as err:
545        if sys.flags.verbose:
546            sys.excepthook(*sys.exc_info())
547        else:
548            sys.stderr.write(
549                "Error in usercustomize; set PYTHONVERBOSE for traceback:\n"
550                "%s: %s\n" %
551                (err.__class__.__name__, err))
552
553
554def main():
555    """Add standard site-specific directories to the module search path.
556
557    This function is called automatically when this module is imported,
558    unless the python interpreter was started with the -S flag.
559    """
560    global ENABLE_USER_SITE
561
562    orig_path = sys.path[:]
563    known_paths = removeduppaths()
564    if orig_path != sys.path:
565        # removeduppaths() might make sys.path absolute.
566        # fix __file__ and __cached__ of already imported modules too.
567        abs_paths()
568
569    known_paths = venv(known_paths)
570    if ENABLE_USER_SITE is None:
571        ENABLE_USER_SITE = check_enableusersite()
572    known_paths = addusersitepackages(known_paths)
573    known_paths = addsitepackages(known_paths)
574    setquit()
575    setcopyright()
576    sethelper()
577    if not sys.flags.isolated:
578        enablerlcompleter()
579    execsitecustomize()
580    if ENABLE_USER_SITE:
581        execusercustomize()
582
583# Prevent extending of sys.path when python was started with -S and
584# site is imported later.
585if not sys.flags.no_site:
586    main()
587
588def _script():
589    help = """\
590    %s [--user-base] [--user-site]
591
592    Without arguments print some useful information
593    With arguments print the value of USER_BASE and/or USER_SITE separated
594    by '%s'.
595
596    Exit codes with --user-base or --user-site:
597      0 - user site directory is enabled
598      1 - user site directory is disabled by user
599      2 - uses site directory is disabled by super user
600          or for security reasons
601     >2 - unknown error
602    """
603    args = sys.argv[1:]
604    if not args:
605        user_base = getuserbase()
606        user_site = getusersitepackages()
607        print("sys.path = [")
608        for dir in sys.path:
609            print("    %r," % (dir,))
610        print("]")
611        print("USER_BASE: %r (%s)" % (user_base,
612            "exists" if os.path.isdir(user_base) else "doesn't exist"))
613        print("USER_SITE: %r (%s)" % (user_site,
614            "exists" if os.path.isdir(user_site) else "doesn't exist"))
615        print("ENABLE_USER_SITE: %r" %  ENABLE_USER_SITE)
616        sys.exit(0)
617
618    buffer = []
619    if '--user-base' in args:
620        buffer.append(USER_BASE)
621    if '--user-site' in args:
622        buffer.append(USER_SITE)
623
624    if buffer:
625        print(os.pathsep.join(buffer))
626        if ENABLE_USER_SITE:
627            sys.exit(0)
628        elif ENABLE_USER_SITE is False:
629            sys.exit(1)
630        elif ENABLE_USER_SITE is None:
631            sys.exit(2)
632        else:
633            sys.exit(3)
634    else:
635        import textwrap
636        print(textwrap.dedent(help % (sys.argv[0], os.pathsep)))
637        sys.exit(10)
638
639if __name__ == '__main__':
640    _script()
641