2828
2929from _collections_abc import _check_methods
3030
31+ GenericAlias = type (list [int ])
32+
3133_names = sys .builtin_module_names
3234
3335# Note: more names are added to __all__ later.
3436__all__ = ["altsep" , "curdir" , "pardir" , "sep" , "pathsep" , "linesep" ,
3537 "defpath" , "name" , "path" , "devnull" , "SEEK_SET" , "SEEK_CUR" ,
3638 "SEEK_END" , "fsencode" , "fsdecode" , "get_exec_path" , "fdopen" ,
37- "popen" , " extsep" ]
39+ "extsep" ]
3840
3941def _exists (name ):
4042 return name in globals ()
@@ -336,7 +338,10 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
336338 dirs.remove('CVS') # don't visit CVS directories
337339
338340 """
339- top = fspath (top )
341+ sys .audit ("os.walk" , top , topdown , onerror , followlinks )
342+ return _walk (fspath (top ), topdown , onerror , followlinks )
343+
344+ def _walk (top , topdown , onerror , followlinks ):
340345 dirs = []
341346 nondirs = []
342347 walk_dirs = []
@@ -410,11 +415,11 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
410415 # the caller can replace the directory entry during the "yield"
411416 # above.
412417 if followlinks or not islink (new_path ):
413- yield from walk (new_path , topdown , onerror , followlinks )
418+ yield from _walk (new_path , topdown , onerror , followlinks )
414419 else :
415420 # Recurse into sub-directories
416421 for new_path in walk_dirs :
417- yield from walk (new_path , topdown , onerror , followlinks )
422+ yield from _walk (new_path , topdown , onerror , followlinks )
418423 # Yield after recursion if going bottom up
419424 yield top , dirs , nondirs
420425
@@ -455,8 +460,8 @@ def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir_fd=
455460 if 'CVS' in dirs:
456461 dirs.remove('CVS') # don't visit CVS directories
457462 """
458- if not isinstance ( top , int ) or not hasattr ( top , '__index__' ):
459- top = fspath (top )
463+ sys . audit ( "os.fwalk" , top , topdown , onerror , follow_symlinks , dir_fd )
464+ top = fspath (top )
460465 # Note: To guard against symlink races, we use the standard
461466 # lstat()/open()/fstat() trick.
462467 if not follow_symlinks :
@@ -654,17 +659,15 @@ def get_exec_path(env=None):
654659 return path_list .split (pathsep )
655660
656661
657- # Change environ to automatically call putenv(), unsetenv if they exist.
658- from _collections_abc import MutableMapping
662+ # Change environ to automatically call putenv() and unsetenv()
663+ from _collections_abc import MutableMapping , Mapping
659664
660665class _Environ (MutableMapping ):
661- def __init__ (self , data , encodekey , decodekey , encodevalue , decodevalue , putenv , unsetenv ):
666+ def __init__ (self , data , encodekey , decodekey , encodevalue , decodevalue ):
662667 self .encodekey = encodekey
663668 self .decodekey = decodekey
664669 self .encodevalue = encodevalue
665670 self .decodevalue = decodevalue
666- self .putenv = putenv
667- self .unsetenv = unsetenv
668671 self ._data = data
669672
670673 def __getitem__ (self , key ):
@@ -678,12 +681,12 @@ def __getitem__(self, key):
678681 def __setitem__ (self , key , value ):
679682 key = self .encodekey (key )
680683 value = self .encodevalue (value )
681- self . putenv (key , value )
684+ putenv (key , value )
682685 self ._data [key ] = value
683686
684687 def __delitem__ (self , key ):
685688 encodedkey = self .encodekey (key )
686- self . unsetenv (encodedkey )
689+ unsetenv (encodedkey )
687690 try :
688691 del self ._data [encodedkey ]
689692 except KeyError :
@@ -700,9 +703,11 @@ def __len__(self):
700703 return len (self ._data )
701704
702705 def __repr__ (self ):
703- return 'environ({{{}}})' .format (', ' .join (
704- ('{!r}: {!r}' .format (self .decodekey (key ), self .decodevalue (value ))
705- for key , value in self ._data .items ())))
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 } }})"
706711
707712 def copy (self ):
708713 return dict (self )
@@ -712,21 +717,23 @@ def setdefault(self, key, value):
712717 self [key ] = value
713718 return self [key ]
714719
715- try :
716- _putenv = putenv
717- except NameError :
718- _putenv = lambda key , value : None
719- else :
720- if "putenv" not in __all__ :
721- __all__ .append ("putenv" )
720+ def __ior__ (self , other ):
721+ self .update (other )
722+ return self
722723
723- try :
724- _unsetenv = unsetenv
725- except NameError :
726- _unsetenv = lambda key : _putenv (key , "" )
727- else :
728- if "unsetenv" not in __all__ :
729- __all__ .append ("unsetenv" )
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
730737
731738def _createenviron ():
732739 if name == 'nt' :
@@ -755,8 +762,7 @@ def decode(value):
755762 data = environ
756763 return _Environ (data ,
757764 encodekey , decode ,
758- encode , decode ,
759- _putenv , _unsetenv )
765+ encode , decode )
760766
761767# unicode environ
762768environ = _createenviron ()
@@ -781,8 +787,7 @@ def _check_bytes(value):
781787 # bytes environ
782788 environb = _Environ (environ ._data ,
783789 _check_bytes , bytes ,
784- _check_bytes , bytes ,
785- _putenv , _unsetenv )
790+ _check_bytes , bytes )
786791 del _check_bytes
787792
788793 def getenvb (key , default = None ):
@@ -862,12 +867,8 @@ def _spawnvef(mode, file, args, env, func):
862867 wpid , sts = waitpid (pid , 0 )
863868 if WIFSTOPPED (sts ):
864869 continue
865- elif WIFSIGNALED (sts ):
866- return - WTERMSIG (sts )
867- elif WIFEXITED (sts ):
868- return WEXITSTATUS (sts )
869- else :
870- raise OSError ("Not stopped, signaled or exited???" )
870+
871+ return waitstatus_to_exitcode (sts )
871872
872873 def spawnv (mode , file , args ):
873874 """spawnv(mode, file, args) -> integer
@@ -969,28 +970,30 @@ def spawnlpe(mode, file, *args):
969970
970971 __all__ .extend (["spawnlp" , "spawnlpe" ])
971972
972-
973+ # VxWorks has no user space shell provided. As a result, running
974+ # command in a shell can't be supported.
975+ if sys .platform != 'vxworks' :
973976# Supply os.popen()
974- def popen (cmd , mode = "r" , buffering = - 1 ):
975- if not isinstance (cmd , str ):
976- raise TypeError ("invalid cmd type (%s, expected string)" % type (cmd ))
977- if mode not in ("r" , "w" ):
978- raise ValueError ("invalid mode %r" % mode )
979- if buffering == 0 or buffering is None :
980- raise ValueError ("popen() does not support unbuffered streams" )
981- import subprocess , io
982- if mode == "r" :
983- proc = subprocess .Popen (cmd ,
984- shell = True ,
985- stdout = subprocess .PIPE ,
986- bufsize = buffering )
987- return _wrap_close (io . TextIOWrapper ( proc .stdout ) , proc )
988- else :
989- proc = subprocess .Popen (cmd ,
990- shell = True ,
991- stdin = subprocess .PIPE ,
992- bufsize = buffering )
993- return _wrap_close (io . TextIOWrapper ( proc .stdin ) , proc )
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 )
994997
995998# Helper for popen() -- a proxy for a file whose close waits for the process
996999class _wrap_close :
@@ -1015,12 +1018,18 @@ def __getattr__(self, name):
10151018 def __iter__ (self ):
10161019 return iter (self ._stream )
10171020
1021+ __all__ .append ("popen" )
1022+
10181023# Supply os.fdopen()
1019- def fdopen (fd , * args , ** kwargs ):
1024+ def fdopen (fd , mode = "r" , buffering = - 1 , encoding = None , * args , ** kwargs ):
10201025 if not isinstance (fd , int ):
10211026 raise TypeError ("invalid fd type (%s, expected integer)" % type (fd ))
10221027 import io
1023- return io .open (fd , * args , ** kwargs )
1028+ if "b" not in mode :
1029+ # TODO: RUSTPYTHON (module 'io' has no attribute 'text_encoding')
1030+ # encoding = io.text_encoding(encoding)
1031+ pass
1032+ return io .open (fd , mode , buffering , encoding , * args , ** kwargs )
10241033
10251034
10261035# For testing purposes, make sure the function is available when the C
@@ -1076,6 +1085,8 @@ def __subclasshook__(cls, subclass):
10761085 return _check_methods (subclass , '__fspath__' )
10771086 return NotImplemented
10781087
1088+ __class_getitem__ = classmethod (GenericAlias )
1089+
10791090
10801091if name == 'nt' :
10811092 class _AddedDllDirectory :
0 commit comments