1r"""OS routines for NT or Posix depending on what system we're on.
2
3This exports:
4  - all functions from posix or nt, e.g. unlink, stat, etc.
5  - os.path is either posixpath or ntpath
6  - os.name is either 'posix' or 'nt'
7  - os.curdir is a string representing the current directory (always '.')
8  - os.pardir is a string representing the parent directory (always '..')
9  - os.sep is the (or a most common) pathname separator ('/' or '\\')
10  - os.extsep is the extension separator (always '.')
11  - os.altsep is the alternate pathname separator (None or '/')
12  - os.pathsep is the component separator used in $PATH etc
13  - os.linesep is the line separator in text files ('\r' or '\n' or '\r\n')
14  - os.defpath is the default search path for executables
15  - os.devnull is the file path of the null device ('/dev/null', etc.)
16
17Programs that import and use 'os' stand a better chance of being
18portable between different platforms.  Of course, they must then
19only use functions that are defined by all platforms (e.g., unlink
20and opendir), and leave all pathname manipulation to os.path
21(e.g., split and join).
22"""
23
24#'
25import abc
26import sys
27import stat as st
28
29from _collections_abc import _check_methods
30
31GenericAlias = type(list[int])
32
33_names = sys.builtin_module_names
34
35# Note:  more names are added to __all__ later.
36__all__ = ["altsep", "curdir", "pardir", "sep", "pathsep", "linesep",
37           "defpath", "name", "path", "devnull", "SEEK_SET", "SEEK_CUR",
38           "SEEK_END", "fsencode", "fsdecode", "get_exec_path", "fdopen",
39           "extsep"]
40
41def _exists(name):
42    return name in globals()
43
44def _get_exports_list(module):
45    try:
46        return list(module.__all__)
47    except AttributeError:
48        return [n for n in dir(module) if n[0] != '_']
49
50# Any new dependencies of the os module and/or changes in path separator
51# requires updating importlib as well.
52if 'posix' in _names:
53    name = 'posix'
54    linesep = '\n'
55    from posix import *
56    try:
57        from posix import _exit
58        __all__.append('_exit')
59    except ImportError:
60        pass
61    import posixpath as path
62
63    try:
64        from posix import _have_functions
65    except ImportError:
66        pass
67
68    import posix
69    __all__.extend(_get_exports_list(posix))
70    del posix
71
72elif 'nt' in _names:
73    name = 'nt'
74    linesep = '\r\n'
75    from nt import *
76    try:
77        from nt import _exit
78        __all__.append('_exit')
79    except ImportError:
80        pass
81    import ntpath as path
82
83    import nt
84    __all__.extend(_get_exports_list(nt))
85    del nt
86
87    try:
88        from nt import _have_functions
89    except ImportError:
90        pass
91
92else:
93    raise ImportError('no os specific module found')
94
95sys.modules['os.path'] = path
96from os.path import (curdir, pardir, sep, pathsep, defpath, extsep, altsep,
97    devnull)
98
99del _names
100
101
102if _exists("_have_functions"):
103    _globals = globals()
104    def _add(str, fn):
105        if (fn in _globals) and (str in _have_functions):
106            _set.add(_globals[fn])
107
108    _set = set()
109    _add("HAVE_FACCESSAT",  "access")
110    _add("HAVE_FCHMODAT",   "chmod")
111    _add("HAVE_FCHOWNAT",   "chown")
112    _add("HAVE_FSTATAT",    "stat")
113    _add("HAVE_FUTIMESAT",  "utime")
114    _add("HAVE_LINKAT",     "link")
115    _add("HAVE_MKDIRAT",    "mkdir")
116    _add("HAVE_MKFIFOAT",   "mkfifo")
117    _add("HAVE_MKNODAT",    "mknod")
118    _add("HAVE_OPENAT",     "open")
119    _add("HAVE_READLINKAT", "readlink")
120    _add("HAVE_RENAMEAT",   "rename")
121    _add("HAVE_SYMLINKAT",  "symlink")
122    _add("HAVE_UNLINKAT",   "unlink")
123    _add("HAVE_UNLINKAT",   "rmdir")
124    _add("HAVE_UTIMENSAT",  "utime")
125    supports_dir_fd = _set
126
127    _set = set()
128    _add("HAVE_FACCESSAT",  "access")
129    supports_effective_ids = _set
130
131    _set = set()
132    _add("HAVE_FCHDIR",     "chdir")
133    _add("HAVE_FCHMOD",     "chmod")
134    _add("HAVE_FCHOWN",     "chown")
135    _add("HAVE_FDOPENDIR",  "listdir")
136    _add("HAVE_FDOPENDIR",  "scandir")
137    _add("HAVE_FEXECVE",    "execve")
138    _set.add(stat) # fstat always works
139    _add("HAVE_FTRUNCATE",  "truncate")
140    _add("HAVE_FUTIMENS",   "utime")
141    _add("HAVE_FUTIMES",    "utime")
142    _add("HAVE_FPATHCONF",  "pathconf")
143    if _exists("statvfs") and _exists("fstatvfs"): # mac os x10.3
144        _add("HAVE_FSTATVFS", "statvfs")
145    supports_fd = _set
146
147    _set = set()
148    _add("HAVE_FACCESSAT",  "access")
149    # Some platforms don't support lchmod().  Often the function exists
150    # anyway, as a stub that always returns ENOSUP or perhaps EOPNOTSUPP.
151    # (No, I don't know why that's a good design.)  ./configure will detect
152    # this and reject it--so HAVE_LCHMOD still won't be defined on such
153    # platforms.  This is Very Helpful.
154    #
155    # However, sometimes platforms without a working lchmod() *do* have
156    # fchmodat().  (Examples: Linux kernel 3.2 with glibc 2.15,
157    # OpenIndiana 3.x.)  And fchmodat() has a flag that theoretically makes
158    # it behave like lchmod().  So in theory it would be a suitable
159    # replacement for lchmod().  But when lchmod() doesn't work, fchmodat()'s
160    # flag doesn't work *either*.  Sadly ./configure isn't sophisticated
161    # enough to detect this condition--it only determines whether or not
162    # fchmodat() minimally works.
163    #
164    # Therefore we simply ignore fchmodat() when deciding whether or not
165    # os.chmod supports follow_symlinks.  Just checking lchmod() is
166    # sufficient.  After all--if you have a working fchmodat(), your
167    # lchmod() almost certainly works too.
168    #
169    # _add("HAVE_FCHMODAT",   "chmod")
170    _add("HAVE_FCHOWNAT",   "chown")
171    _add("HAVE_FSTATAT",    "stat")
172    _add("HAVE_LCHFLAGS",   "chflags")
173    _add("HAVE_LCHMOD",     "chmod")
174    if _exists("lchown"): # mac os x10.3
175        _add("HAVE_LCHOWN", "chown")
176    _add("HAVE_LINKAT",     "link")
177    _add("HAVE_LUTIMES",    "utime")
178    _add("HAVE_LSTAT",      "stat")
179    _add("HAVE_FSTATAT",    "stat")
180    _add("HAVE_UTIMENSAT",  "utime")
181    _add("MS_WINDOWS",      "stat")
182    supports_follow_symlinks = _set
183
184    del _set
185    del _have_functions
186    del _globals
187    del _add
188
189
190# Python uses fixed values for the SEEK_ constants; they are mapped
191# to native constants if necessary in posixmodule.c
192# Other possible SEEK values are directly imported from posixmodule.c
193SEEK_SET = 0
194SEEK_CUR = 1
195SEEK_END = 2
196
197# Super directory utilities.
198# (Inspired by Eric Raymond; the doc strings are mostly his)
199
200def makedirs(name, mode=0o777, exist_ok=False):
201    """makedirs(name [, mode=0o777][, exist_ok=False])
202
203    Super-mkdir; create a leaf directory and all intermediate ones.  Works like
204    mkdir, except that any intermediate path segment (not just the rightmost)
205    will be created if it does not exist. If the target directory already
206    exists, raise an OSError if exist_ok is False. Otherwise no exception is
207    raised.  This is recursive.
208
209    """
210    head, tail = path.split(name)
211    if not tail:
212        head, tail = path.split(head)
213    if head and tail and not path.exists(head):
214        try:
215            makedirs(head, exist_ok=exist_ok)
216        except FileExistsError:
217            # Defeats race condition when another thread created the path
218            pass
219        cdir = curdir
220        if isinstance(tail, bytes):
221            cdir = bytes(curdir, 'ASCII')
222        if tail == cdir:           # xxx/newdir/. exists if xxx/newdir exists
223            return
224    try:
225        mkdir(name, mode)
226    except OSError:
227        # Cannot rely on checking for EEXIST, since the operating system
228        # could give priority to other errors like EACCES or EROFS
229        if not exist_ok or not path.isdir(name):
230            raise
231
232def removedirs(name):
233    """removedirs(name)
234
235    Super-rmdir; remove a leaf directory and all empty intermediate
236    ones.  Works like rmdir except that, if the leaf directory is
237    successfully removed, directories corresponding to rightmost path
238    segments will be pruned away until either the whole path is
239    consumed or an error occurs.  Errors during this latter phase are
240    ignored -- they generally mean that a directory was not empty.
241
242    """
243    rmdir(name)
244    head, tail = path.split(name)
245    if not tail:
246        head, tail = path.split(head)
247    while head and tail:
248        try:
249            rmdir(head)
250        except OSError:
251            break
252        head, tail = path.split(head)
253
254def renames(old, new):
255    """renames(old, new)
256
257    Super-rename; create directories as necessary and delete any left
258    empty.  Works like rename, except creation of any intermediate
259    directories needed to make the new pathname good is attempted
260    first.  After the rename, directories corresponding to rightmost
261    path segments of the old name will be pruned until either the
262    whole path is consumed or a nonempty directory is found.
263
264    Note: this function can fail with the new directory structure made
265    if you lack permissions needed to unlink the leaf directory or
266    file.
267
268    """
269    head, tail = path.split(new)
270    if head and tail and not path.exists(head):
271        makedirs(head)
272    rename(old, new)
273    head, tail = path.split(old)
274    if head and tail:
275        try:
276            removedirs(head)
277        except OSError:
278            pass
279
280__all__.extend(["makedirs", "removedirs", "renames"])
281
282def walk(top, topdown=True, onerror=None, followlinks=False):
283    """Directory tree generator.
284
285    For each directory in the directory tree rooted at top (including top
286    itself, but excluding '.' and '..'), yields a 3-tuple
287
288        dirpath, dirnames, filenames
289
290    dirpath is a string, the path to the directory.  dirnames is a list of
291    the names of the subdirectories in dirpath (excluding '.' and '..').
292    filenames is a list of the names of the non-directory files in dirpath.
293    Note that the names in the lists are just names, with no path components.
294    To get a full path (which begins with top) to a file or directory in
295    dirpath, do os.path.join(dirpath, name).
296
297    If optional arg 'topdown' is true or not specified, the triple for a
298    directory is generated before the triples for any of its subdirectories
299    (directories are generated top down).  If topdown is false, the triple
300    for a directory is generated after the triples for all of its
301    subdirectories (directories are generated bottom up).
302
303    When topdown is true, the caller can modify the dirnames list in-place
304    (e.g., via del or slice assignment), and walk will only recurse into the
305    subdirectories whose names remain in dirnames; this can be used to prune the
306    search, or to impose a specific order of visiting.  Modifying dirnames when
307    topdown is false has no effect on the behavior of os.walk(), since the
308    directories in dirnames have already been generated by the time dirnames
309    itself is generated. No matter the value of topdown, the list of
310    subdirectories is retrieved before the tuples for the directory and its
311    subdirectories are generated.
312
313    By default errors from the os.scandir() call are ignored.  If
314    optional arg 'onerror' is specified, it should be a function; it
315    will be called with one argument, an OSError instance.  It can
316    report the error to continue with the walk, or raise the exception
317    to abort the walk.  Note that the filename is available as the
318    filename attribute of the exception object.
319
320    By default, os.walk does not follow symbolic links to subdirectories on
321    systems that support them.  In order to get this functionality, set the
322    optional argument 'followlinks' to true.
323
324    Caution:  if you pass a relative pathname for top, don't change the
325    current working directory between resumptions of walk.  walk never
326    changes the current directory, and assumes that the client doesn't
327    either.
328
329    Example:
330
331    import os
332    from os.path import join, getsize
333    for root, dirs, files in os.walk('python/Lib/email'):
334        print(root, "consumes", end="")
335        print(sum(getsize(join(root, name)) for name in files), end="")
336        print("bytes in", len(files), "non-directory files")
337        if 'CVS' in dirs:
338            dirs.remove('CVS')  # don't visit CVS directories
339
340    """
341    sys.audit("os.walk", top, topdown, onerror, followlinks)
342    return _walk(fspath(top), topdown, onerror, followlinks)
343
344def _walk(top, topdown, onerror, followlinks):
345    dirs = []
346    nondirs = []
347    walk_dirs = []
348
349    # We may not have read permission for top, in which case we can't
350    # get a list of the files the directory contains.  os.walk
351    # always suppressed the exception then, rather than blow up for a
352    # minor reason when (say) a thousand readable directories are still
353    # left to visit.  That logic is copied here.
354    try:
355        # Note that scandir is global in this module due
356        # to earlier import-*.
357        scandir_it = scandir(top)
358    except OSError as error:
359        if onerror is not None:
360            onerror(error)
361        return
362
363    with scandir_it:
364        while True:
365            try:
366                try:
367                    entry = next(scandir_it)
368                except StopIteration:
369                    break
370            except OSError as error:
371                if onerror is not None:
372                    onerror(error)
373                return
374
375            try:
376                is_dir = entry.is_dir()
377            except OSError:
378                # If is_dir() raises an OSError, consider that the entry is not
379                # a directory, same behaviour than os.path.isdir().
380                is_dir = False
381
382            if is_dir:
383                dirs.append(entry.name)
384            else:
385                nondirs.append(entry.name)
386
387            if not topdown and is_dir:
388                # Bottom-up: recurse into sub-directory, but exclude symlinks to
389                # directories if followlinks is False
390                if followlinks:
391                    walk_into = True
392                else:
393                    try:
394                        is_symlink = entry.is_symlink()
395                    except OSError:
396                        # If is_symlink() raises an OSError, consider that the
397                        # entry is not a symbolic link, same behaviour than
398                        # os.path.islink().
399                        is_symlink = False
400                    walk_into = not is_symlink
401
402                if walk_into:
403                    walk_dirs.append(entry.path)
404
405    # Yield before recursion if going top down
406    if topdown:
407        yield top, dirs, nondirs
408
409        # Recurse into sub-directories
410        islink, join = path.islink, path.join
411        for dirname in dirs:
412            new_path = join(top, dirname)
413            # Issue #23605: os.path.islink() is used instead of caching
414            # entry.is_symlink() result during the loop on os.scandir() because
415            # the caller can replace the directory entry during the "yield"
416            # above.
417            if followlinks or not islink(new_path):
418                yield from _walk(new_path, topdown, onerror, followlinks)
419    else:
420        # Recurse into sub-directories
421        for new_path in walk_dirs:
422            yield from _walk(new_path, topdown, onerror, followlinks)
423        # Yield after recursion if going bottom up
424        yield top, dirs, nondirs
425
426__all__.append("walk")
427
428if {open, stat} <= supports_dir_fd and {scandir, stat} <= supports_fd:
429
430    def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir_fd=None):
431        """Directory tree generator.
432
433        This behaves exactly like walk(), except that it yields a 4-tuple
434
435            dirpath, dirnames, filenames, dirfd
436
437        `dirpath`, `dirnames` and `filenames` are identical to walk() output,
438        and `dirfd` is a file descriptor referring to the directory `dirpath`.
439
440        The advantage of fwalk() over walk() is that it's safe against symlink
441        races (when follow_symlinks is False).
442
443        If dir_fd is not None, it should be a file descriptor open to a directory,
444          and top should be relative; top will then be relative to that directory.
445          (dir_fd is always supported for fwalk.)
446
447        Caution:
448        Since fwalk() yields file descriptors, those are only valid until the
449        next iteration step, so you should dup() them if you want to keep them
450        for a longer period.
451
452        Example:
453
454        import os
455        for root, dirs, files, rootfd in os.fwalk('python/Lib/email'):
456            print(root, "consumes", end="")
457            print(sum(os.stat(name, dir_fd=rootfd).st_size for name in files),
458                  end="")
459            print("bytes in", len(files), "non-directory files")
460            if 'CVS' in dirs:
461                dirs.remove('CVS')  # don't visit CVS directories
462        """
463        sys.audit("os.fwalk", top, topdown, onerror, follow_symlinks, dir_fd)
464        top = fspath(top)
465        # Note: To guard against symlink races, we use the standard
466        # lstat()/open()/fstat() trick.
467        if not follow_symlinks:
468            orig_st = stat(top, follow_symlinks=False, dir_fd=dir_fd)
469        topfd = open(top, O_RDONLY, dir_fd=dir_fd)
470        try:
471            if (follow_symlinks or (st.S_ISDIR(orig_st.st_mode) and
472                                    path.samestat(orig_st, stat(topfd)))):
473                yield from _fwalk(topfd, top, isinstance(top, bytes),
474                                  topdown, onerror, follow_symlinks)
475        finally:
476            close(topfd)
477
478    def _fwalk(topfd, toppath, isbytes, topdown, onerror, follow_symlinks):
479        # Note: This uses O(depth of the directory tree) file descriptors: if
480        # necessary, it can be adapted to only require O(1) FDs, see issue
481        # #13734.
482
483        scandir_it = scandir(topfd)
484        dirs = []
485        nondirs = []
486        entries = None if topdown or follow_symlinks else []
487        for entry in scandir_it:
488            name = entry.name
489            if isbytes:
490                name = fsencode(name)
491            try:
492                if entry.is_dir():
493                    dirs.append(name)
494                    if entries is not None:
495                        entries.append(entry)
496                else:
497                    nondirs.append(name)
498            except OSError:
499                try:
500                    # Add dangling symlinks, ignore disappeared files
501                    if entry.is_symlink():
502                        nondirs.append(name)
503                except OSError:
504                    pass
505
506        if topdown:
507            yield toppath, dirs, nondirs, topfd
508
509        for name in dirs if entries is None else zip(dirs, entries):
510            try:
511                if not follow_symlinks:
512                    if topdown:
513                        orig_st = stat(name, dir_fd=topfd, follow_symlinks=False)
514                    else:
515                        assert entries is not None
516                        name, entry = name
517                        orig_st = entry.stat(follow_symlinks=False)
518                dirfd = open(name, O_RDONLY, dir_fd=topfd)
519            except OSError as err:
520                if onerror is not None:
521                    onerror(err)
522                continue
523            try:
524                if follow_symlinks or path.samestat(orig_st, stat(dirfd)):
525                    dirpath = path.join(toppath, name)
526                    yield from _fwalk(dirfd, dirpath, isbytes,
527                                      topdown, onerror, follow_symlinks)
528            finally:
529                close(dirfd)
530
531        if not topdown:
532            yield toppath, dirs, nondirs, topfd
533
534    __all__.append("fwalk")
535
536def execl(file, *args):
537    """execl(file, *args)
538
539    Execute the executable file with argument list args, replacing the
540    current process. """
541    execv(file, args)
542
543def execle(file, *args):
544    """execle(file, *args, env)
545
546    Execute the executable file with argument list args and
547    environment env, replacing the current process. """
548    env = args[-1]
549    execve(file, args[:-1], env)
550
551def execlp(file, *args):
552    """execlp(file, *args)
553
554    Execute the executable file (which is searched for along $PATH)
555    with argument list args, replacing the current process. """
556    execvp(file, args)
557
558def execlpe(file, *args):
559    """execlpe(file, *args, env)
560
561    Execute the executable file (which is searched for along $PATH)
562    with argument list args and environment env, replacing the current
563    process. """
564    env = args[-1]
565    execvpe(file, args[:-1], env)
566
567def execvp(file, args):
568    """execvp(file, args)
569
570    Execute the executable file (which is searched for along $PATH)
571    with argument list args, replacing the current process.
572    args may be a list or tuple of strings. """
573    _execvpe(file, args)
574
575def execvpe(file, args, env):
576    """execvpe(file, args, env)
577
578    Execute the executable file (which is searched for along $PATH)
579    with argument list args and environment env, replacing the
580    current process.
581    args may be a list or tuple of strings. """
582    _execvpe(file, args, env)
583
584__all__.extend(["execl","execle","execlp","execlpe","execvp","execvpe"])
585
586def _execvpe(file, args, env=None):
587    if env is not None:
588        exec_func = execve
589        argrest = (args, env)
590    else:
591        exec_func = execv
592        argrest = (args,)
593        env = environ
594
595    if path.dirname(file):
596        exec_func(file, *argrest)
597        return
598    saved_exc = None
599    path_list = get_exec_path(env)
600    if name != 'nt':
601        file = fsencode(file)
602        path_list = map(fsencode, path_list)
603    for dir in path_list:
604        fullname = path.join(dir, file)
605        try:
606            exec_func(fullname, *argrest)
607        except (FileNotFoundError, NotADirectoryError) as e:
608            last_exc = e
609        except OSError as e:
610            last_exc = e
611            if saved_exc is None:
612                saved_exc = e
613    if saved_exc is not None:
614        raise saved_exc
615    raise last_exc
616
617
618def get_exec_path(env=None):
619    """Returns the sequence of directories that will be searched for the
620    named executable (similar to a shell) when launching a process.
621
622    *env* must be an environment variable dict or None.  If *env* is None,
623    os.environ will be used.
624    """
625    # Use a local import instead of a global import to limit the number of
626    # modules loaded at startup: the os module is always loaded at startup by
627    # Python. It may also avoid a bootstrap issue.
628    import warnings
629
630    if env is None:
631        env = environ
632
633    # {b'PATH': ...}.get('PATH') and {'PATH': ...}.get(b'PATH') emit a
634    # BytesWarning when using python -b or python -bb: ignore the warning
635    with warnings.catch_warnings():
636        warnings.simplefilter("ignore", BytesWarning)
637
638        try:
639            path_list = env.get('PATH')
640        except TypeError:
641            path_list = None
642
643        if supports_bytes_environ:
644            try:
645                path_listb = env[b'PATH']
646            except (KeyError, TypeError):
647                pass
648            else:
649                if path_list is not None:
650                    raise ValueError(
651                        "env cannot contain 'PATH' and b'PATH' keys")
652                path_list = path_listb
653
654            if path_list is not None and isinstance(path_list, bytes):
655                path_list = fsdecode(path_list)
656
657    if path_list is None:
658        path_list = defpath
659    return path_list.split(pathsep)
660
661
662# Change environ to automatically call putenv() and unsetenv()
663from _collections_abc import MutableMapping, Mapping
664
665class _Environ(MutableMapping):
666    def __init__(self, data, encodekey, decodekey, encodevalue, decodevalue):
667        self.encodekey = encodekey
668        self.decodekey = decodekey
669        self.encodevalue = encodevalue
670        self.decodevalue = decodevalue
671        self._data = data
672
673    def __getitem__(self, key):
674        try:
675            value = self._data[self.encodekey(key)]
676        except KeyError:
677            # raise KeyError with the original key value
678            raise KeyError(key) from None
679        return self.decodevalue(value)
680
681    def __setitem__(self, key, value):
682        key = self.encodekey(key)
683        value = self.encodevalue(value)
684        putenv(key, value)
685        self._data[key] = value
686
687    def __delitem__(self, key):
688        encodedkey = self.encodekey(key)
689        unsetenv(encodedkey)
690        try:
691            del self._data[encodedkey]
692        except KeyError:
693            # raise KeyError with the original key value
694            raise KeyError(key) from None
695
696    def __iter__(self):
697        # list() from dict object is an atomic operation
698        keys = list(self._data)
699        for key in keys:
700            yield self.decodekey(key)
701
702    def __len__(self):
703        return len(self._data)
704
705    def __repr__(self):
706        formatted_items = ", ".join(
707            f"{self.decodekey(key)!r}: {self.decodevalue(value)!r}"
708            for key, value in self._data.items()
709        )
710        return f"environ({{{formatted_items}}})"
711
712    def copy(self):
713        return dict(self)
714
715    def setdefault(self, key, value):
716        if key not in self:
717            self[key] = value
718        return self[key]
719
720    def __ior__(self, other):
721        self.update(other)
722        return self
723
724    def __or__(self, other):
725        if not isinstance(other, Mapping):
726            return NotImplemented
727        new = dict(self)
728        new.update(other)
729        return new
730
731    def __ror__(self, other):
732        if not isinstance(other, Mapping):
733            return NotImplemented
734        new = dict(other)
735        new.update(self)
736        return new
737
738def _createenviron():
739    if name == 'nt':
740        # Where Env Var Names Must Be UPPERCASE
741        def check_str(value):
742            if not isinstance(value, str):
743                raise TypeError("str expected, not %s" % type(value).__name__)
744            return value
745        encode = check_str
746        decode = str
747        def encodekey(key):
748            return encode(key).upper()
749        data = {}
750        for key, value in environ.items():
751            data[encodekey(key)] = value
752    else:
753        # Where Env Var Names Can Be Mixed Case
754        encoding = sys.getfilesystemencoding()
755        def encode(value):
756            if not isinstance(value, str):
757                raise TypeError("str expected, not %s" % type(value).__name__)
758            return value.encode(encoding, 'surrogateescape')
759        def decode(value):
760            return value.decode(encoding, 'surrogateescape')
761        encodekey = encode
762        data = environ
763    return _Environ(data,
764        encodekey, decode,
765        encode, decode)
766
767# unicode environ
768environ = _createenviron()
769del _createenviron
770
771
772def getenv(key, default=None):
773    """Get an environment variable, return None if it doesn't exist.
774    The optional second argument can specify an alternate default.
775    key, default and the result are str."""
776    return environ.get(key, default)
777
778supports_bytes_environ = (name != 'nt')
779__all__.extend(("getenv", "supports_bytes_environ"))
780
781if supports_bytes_environ:
782    def _check_bytes(value):
783        if not isinstance(value, bytes):
784            raise TypeError("bytes expected, not %s" % type(value).__name__)
785        return value
786
787    # bytes environ
788    environb = _Environ(environ._data,
789        _check_bytes, bytes,
790        _check_bytes, bytes)
791    del _check_bytes
792
793    def getenvb(key, default=None):
794        """Get an environment variable, return None if it doesn't exist.
795        The optional second argument can specify an alternate default.
796        key, default and the result are bytes."""
797        return environb.get(key, default)
798
799    __all__.extend(("environb", "getenvb"))
800
801def _fscodec():
802    encoding = sys.getfilesystemencoding()
803    errors = sys.getfilesystemencodeerrors()
804
805    def fsencode(filename):
806        """Encode filename (an os.PathLike, bytes, or str) to the filesystem
807        encoding with 'surrogateescape' error handler, return bytes unchanged.
808        On Windows, use 'strict' error handler if the file system encoding is
809        'mbcs' (which is the default encoding).
810        """
811        filename = fspath(filename)  # Does type-checking of `filename`.
812        if isinstance(filename, str):
813            return filename.encode(encoding, errors)
814        else:
815            return filename
816
817    def fsdecode(filename):
818        """Decode filename (an os.PathLike, bytes, or str) from the filesystem
819        encoding with 'surrogateescape' error handler, return str unchanged. On
820        Windows, use 'strict' error handler if the file system encoding is
821        'mbcs' (which is the default encoding).
822        """
823        filename = fspath(filename)  # Does type-checking of `filename`.
824        if isinstance(filename, bytes):
825            return filename.decode(encoding, errors)
826        else:
827            return filename
828
829    return fsencode, fsdecode
830
831fsencode, fsdecode = _fscodec()
832del _fscodec
833
834# Supply spawn*() (probably only for Unix)
835if _exists("fork") and not _exists("spawnv") and _exists("execv"):
836
837    P_WAIT = 0
838    P_NOWAIT = P_NOWAITO = 1
839
840    __all__.extend(["P_WAIT", "P_NOWAIT", "P_NOWAITO"])
841
842    # XXX Should we support P_DETACH?  I suppose it could fork()**2
843    # and close the std I/O streams.  Also, P_OVERLAY is the same
844    # as execv*()?
845
846    def _spawnvef(mode, file, args, env, func):
847        # Internal helper; func is the exec*() function to use
848        if not isinstance(args, (tuple, list)):
849            raise TypeError('argv must be a tuple or a list')
850        if not args or not args[0]:
851            raise ValueError('argv first element cannot be empty')
852        pid = fork()
853        if not pid:
854            # Child
855            try:
856                if env is None:
857                    func(file, args)
858                else:
859                    func(file, args, env)
860            except:
861                _exit(127)
862        else:
863            # Parent
864            if mode == P_NOWAIT:
865                return pid # Caller is responsible for waiting!
866            while 1:
867                wpid, sts = waitpid(pid, 0)
868                if WIFSTOPPED(sts):
869                    continue
870
871                return waitstatus_to_exitcode(sts)
872
873    def spawnv(mode, file, args):
874        """spawnv(mode, file, args) -> integer
875
876Execute file with arguments from args in a subprocess.
877If mode == P_NOWAIT return the pid of the process.
878If mode == P_WAIT return the process's exit code if it exits normally;
879otherwise return -SIG, where SIG is the signal that killed it. """
880        return _spawnvef(mode, file, args, None, execv)
881
882    def spawnve(mode, file, args, env):
883        """spawnve(mode, file, args, env) -> integer
884
885Execute file with arguments from args in a subprocess with the
886specified environment.
887If mode == P_NOWAIT return the pid of the process.
888If mode == P_WAIT return the process's exit code if it exits normally;
889otherwise return -SIG, where SIG is the signal that killed it. """
890        return _spawnvef(mode, file, args, env, execve)
891
892    # Note: spawnvp[e] isn't currently supported on Windows
893
894    def spawnvp(mode, file, args):
895        """spawnvp(mode, file, args) -> integer
896
897Execute file (which is looked for along $PATH) with arguments from
898args in a subprocess.
899If mode == P_NOWAIT return the pid of the process.
900If mode == P_WAIT return the process's exit code if it exits normally;
901otherwise return -SIG, where SIG is the signal that killed it. """
902        return _spawnvef(mode, file, args, None, execvp)
903
904    def spawnvpe(mode, file, args, env):
905        """spawnvpe(mode, file, args, env) -> integer
906
907Execute file (which is looked for along $PATH) with arguments from
908args in a subprocess with the supplied environment.
909If mode == P_NOWAIT return the pid of the process.
910If mode == P_WAIT return the process's exit code if it exits normally;
911otherwise return -SIG, where SIG is the signal that killed it. """
912        return _spawnvef(mode, file, args, env, execvpe)
913
914
915    __all__.extend(["spawnv", "spawnve", "spawnvp", "spawnvpe"])
916
917
918if _exists("spawnv"):
919    # These aren't supplied by the basic Windows code
920    # but can be easily implemented in Python
921
922    def spawnl(mode, file, *args):
923        """spawnl(mode, file, *args) -> integer
924
925Execute file with arguments from args in a subprocess.
926If mode == P_NOWAIT return the pid of the process.
927If mode == P_WAIT return the process's exit code if it exits normally;
928otherwise return -SIG, where SIG is the signal that killed it. """
929        return spawnv(mode, file, args)
930
931    def spawnle(mode, file, *args):
932        """spawnle(mode, file, *args, env) -> integer
933
934Execute file with arguments from args in a subprocess with the
935supplied environment.
936If mode == P_NOWAIT return the pid of the process.
937If mode == P_WAIT return the process's exit code if it exits normally;
938otherwise return -SIG, where SIG is the signal that killed it. """
939        env = args[-1]
940        return spawnve(mode, file, args[:-1], env)
941
942
943    __all__.extend(["spawnl", "spawnle"])
944
945
946if _exists("spawnvp"):
947    # At the moment, Windows doesn't implement spawnvp[e],
948    # so it won't have spawnlp[e] either.
949    def spawnlp(mode, file, *args):
950        """spawnlp(mode, file, *args) -> integer
951
952Execute file (which is looked for along $PATH) with arguments from
953args in a subprocess with the supplied environment.
954If mode == P_NOWAIT return the pid of the process.
955If mode == P_WAIT return the process's exit code if it exits normally;
956otherwise return -SIG, where SIG is the signal that killed it. """
957        return spawnvp(mode, file, args)
958
959    def spawnlpe(mode, file, *args):
960        """spawnlpe(mode, file, *args, env) -> integer
961
962Execute file (which is looked for along $PATH) with arguments from
963args in a subprocess with the supplied environment.
964If mode == P_NOWAIT return the pid of the process.
965If mode == P_WAIT return the process's exit code if it exits normally;
966otherwise return -SIG, where SIG is the signal that killed it. """
967        env = args[-1]
968        return spawnvpe(mode, file, args[:-1], env)
969
970
971    __all__.extend(["spawnlp", "spawnlpe"])
972
973# VxWorks has no user space shell provided. As a result, running
974# command in a shell can't be supported.
975if sys.platform != 'vxworks':
976    # Supply os.popen()
977    def popen(cmd, mode="r", buffering=-1):
978        if not isinstance(cmd, str):
979            raise TypeError("invalid cmd type (%s, expected string)" % type(cmd))
980        if mode not in ("r", "w"):
981            raise ValueError("invalid mode %r" % mode)
982        if buffering == 0 or buffering is None:
983            raise ValueError("popen() does not support unbuffered streams")
984        import subprocess, io
985        if mode == "r":
986            proc = subprocess.Popen(cmd,
987                                    shell=True, text=True,
988                                    stdout=subprocess.PIPE,
989                                    bufsize=buffering)
990            return _wrap_close(proc.stdout, proc)
991        else:
992            proc = subprocess.Popen(cmd,
993                                    shell=True, text=True,
994                                    stdin=subprocess.PIPE,
995                                    bufsize=buffering)
996            return _wrap_close(proc.stdin, proc)
997
998    # Helper for popen() -- a proxy for a file whose close waits for the process
999    class _wrap_close:
1000        def __init__(self, stream, proc):
1001            self._stream = stream
1002            self._proc = proc
1003        def close(self):
1004            self._stream.close()
1005            returncode = self._proc.wait()
1006            if returncode == 0:
1007                return None
1008            if name == 'nt':
1009                return returncode
1010            else:
1011                return returncode << 8  # Shift left to match old behavior
1012        def __enter__(self):
1013            return self
1014        def __exit__(self, *args):
1015            self.close()
1016        def __getattr__(self, name):
1017            return getattr(self._stream, name)
1018        def __iter__(self):
1019            return iter(self._stream)
1020
1021    __all__.append("popen")
1022
1023# Supply os.fdopen()
1024def fdopen(fd, mode="r", buffering=-1, encoding=None, *args, **kwargs):
1025    if not isinstance(fd, int):
1026        raise TypeError("invalid fd type (%s, expected integer)" % type(fd))
1027    import io
1028    if "b" not in mode:
1029        encoding = io.text_encoding(encoding)
1030    return io.open(fd, mode, buffering, encoding, *args, **kwargs)
1031
1032
1033# For testing purposes, make sure the function is available when the C
1034# implementation exists.
1035def _fspath(path):
1036    """Return the path representation of a path-like object.
1037
1038    If str or bytes is passed in, it is returned unchanged. Otherwise the
1039    os.PathLike interface is used to get the path representation. If the
1040    path representation is not str or bytes, TypeError is raised. If the
1041    provided path is not str, bytes, or os.PathLike, TypeError is raised.
1042    """
1043    if isinstance(path, (str, bytes)):
1044        return path
1045
1046    # Work from the object's type to match method resolution of other magic
1047    # methods.
1048    path_type = type(path)
1049    try:
1050        path_repr = path_type.__fspath__(path)
1051    except AttributeError:
1052        if hasattr(path_type, '__fspath__'):
1053            raise
1054        else:
1055            raise TypeError("expected str, bytes or os.PathLike object, "
1056                            "not " + path_type.__name__)
1057    if isinstance(path_repr, (str, bytes)):
1058        return path_repr
1059    else:
1060        raise TypeError("expected {}.__fspath__() to return str or bytes, "
1061                        "not {}".format(path_type.__name__,
1062                                        type(path_repr).__name__))
1063
1064# If there is no C implementation, make the pure Python version the
1065# implementation as transparently as possible.
1066if not _exists('fspath'):
1067    fspath = _fspath
1068    fspath.__name__ = "fspath"
1069
1070
1071class PathLike(abc.ABC):
1072
1073    """Abstract base class for implementing the file system path protocol."""
1074
1075    @abc.abstractmethod
1076    def __fspath__(self):
1077        """Return the file system path representation of the object."""
1078        raise NotImplementedError
1079
1080    @classmethod
1081    def __subclasshook__(cls, subclass):
1082        if cls is PathLike:
1083            return _check_methods(subclass, '__fspath__')
1084        return NotImplemented
1085
1086    __class_getitem__ = classmethod(GenericAlias)
1087
1088
1089if name == 'nt':
1090    class _AddedDllDirectory:
1091        def __init__(self, path, cookie, remove_dll_directory):
1092            self.path = path
1093            self._cookie = cookie
1094            self._remove_dll_directory = remove_dll_directory
1095        def close(self):
1096            self._remove_dll_directory(self._cookie)
1097            self.path = None
1098        def __enter__(self):
1099            return self
1100        def __exit__(self, *args):
1101            self.close()
1102        def __repr__(self):
1103            if self.path:
1104                return "<AddedDllDirectory({!r})>".format(self.path)
1105            return "<AddedDllDirectory()>"
1106
1107    def add_dll_directory(path):
1108        """Add a path to the DLL search path.
1109
1110        This search path is used when resolving dependencies for imported
1111        extension modules (the module itself is resolved through sys.path),
1112        and also by ctypes.
1113
1114        Remove the directory by calling close() on the returned object or
1115        using it in a with statement.
1116        """
1117        import nt
1118        cookie = nt._add_dll_directory(path)
1119        return _AddedDllDirectory(
1120            path,
1121            cookie,
1122            nt._remove_dll_directory
1123        )
1124