7575import _sitebuiltins
7676import io
7777import stat
78+ import errno
7879
7980# Prefixes for site-packages; add additional prefixes like /usr/local here
8081PREFIXES = [sys .prefix , sys .exec_prefix ]
@@ -179,35 +180,46 @@ def addpackage(sitedir, name, known_paths):
179180 return
180181 _trace (f"Processing .pth file: { fullname !r} " )
181182 try :
182- # locale encoding is not ideal especially on Windows. But we have used
183- # it for a long time. setuptools uses the locale encoding too.
184- f = io .TextIOWrapper (io .open_code (fullname ), encoding = "locale" )
183+ with io .open_code (fullname ) as f :
184+ pth_content = f .read ()
185185 except OSError :
186186 return
187- with f :
188- for n , line in enumerate (f ):
189- if line .startswith ("#" ):
190- continue
191- if line .strip () == "" :
187+
188+ try :
189+ # Accept BOM markers in .pth files as we do in source files
190+ # (Windows PowerShell 5.1 makes it hard to emit UTF-8 files without a BOM)
191+ pth_content = pth_content .decode ("utf-8-sig" )
192+ except UnicodeDecodeError :
193+ # Fallback to locale encoding for backward compatibility.
194+ # We will deprecate this fallback in the future.
195+ import locale
196+ pth_content = pth_content .decode (locale .getencoding ())
197+ _trace (f"Cannot read { fullname !r} as UTF-8. "
198+ f"Using fallback encoding { locale .getencoding ()!r} " )
199+
200+ for n , line in enumerate (pth_content .splitlines (), 1 ):
201+ if line .startswith ("#" ):
202+ continue
203+ if line .strip () == "" :
204+ continue
205+ try :
206+ if line .startswith (("import " , "import\t " )):
207+ exec (line )
192208 continue
193- try :
194- if line .startswith (("import " , "import\t " )):
195- exec (line )
196- continue
197- line = line .rstrip ()
198- dir , dircase = makepath (sitedir , line )
199- if not dircase in known_paths and os .path .exists (dir ):
200- sys .path .append (dir )
201- known_paths .add (dircase )
202- except Exception as exc :
203- print ("Error processing line {:d} of {}:\n " .format (n + 1 , fullname ),
204- file = sys .stderr )
205- import traceback
206- for record in traceback .format_exception (exc ):
207- for line in record .splitlines ():
208- print (' ' + line , file = sys .stderr )
209- print ("\n Remainder of file ignored" , file = sys .stderr )
210- break
209+ line = line .rstrip ()
210+ dir , dircase = makepath (sitedir , line )
211+ if dircase not in known_paths and os .path .exists (dir ):
212+ sys .path .append (dir )
213+ known_paths .add (dircase )
214+ except Exception as exc :
215+ print (f"Error processing line { n :d} of { fullname } :\n " ,
216+ file = sys .stderr )
217+ import traceback
218+ for record in traceback .format_exception (exc ):
219+ for line in record .splitlines ():
220+ print (' ' + line , file = sys .stderr )
221+ print ("\n Remainder of file ignored" , file = sys .stderr )
222+ break
211223 if reset :
212224 known_paths = None
213225 return known_paths
@@ -270,23 +282,26 @@ def check_enableusersite():
270282#
271283# See https://bugs.python.org/issue29585
272284
285+ # Copy of sysconfig._get_implementation()
286+ def _get_implementation ():
287+ return 'RustPython' # XXX: RustPython; for site-packages
288+
273289# Copy of sysconfig._getuserbase()
274290def _getuserbase ():
275291 env_base = os .environ .get ("PYTHONUSERBASE" , None )
276292 if env_base :
277293 return env_base
278294
279- # Emscripten, VxWorks, and WASI have no home directories
280- if sys .platform in {"emscripten" , "vxworks" , "wasi" }:
295+ # Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories
296+ if sys .platform in {"emscripten" , "ios" , "tvos" , " vxworks" , "wasi" , "watchos " }:
281297 return None
282298
283299 def joinuser (* args ):
284300 return os .path .expanduser (os .path .join (* args ))
285301
286302 if os .name == "nt" :
287303 base = os .environ .get ("APPDATA" ) or "~"
288- # XXX: RUSTPYTHON; please keep this change for site-packages
289- return joinuser (base , "RustPython" )
304+ return joinuser (base , _get_implementation ())
290305
291306 if sys .platform == "darwin" and sys ._framework :
292307 return joinuser ("~" , "Library" , sys ._framework ,
@@ -298,15 +313,22 @@ def joinuser(*args):
298313# Same to sysconfig.get_path('purelib', os.name+'_user')
299314def _get_path (userbase ):
300315 version = sys .version_info
316+ if hasattr (sys , 'abiflags' ) and 't' in sys .abiflags :
317+ abi_thread = 't'
318+ else :
319+ abi_thread = ''
301320
321+ implementation = _get_implementation ()
322+ implementation_lower = implementation .lower ()
302323 if os .name == 'nt' :
303324 ver_nodot = sys .winver .replace ('.' , '' )
304- return f'{ userbase } \\ RustPython { ver_nodot } \\ site-packages'
325+ return f'{ userbase } \\ { implementation } { ver_nodot } \\ site-packages'
305326
306327 if sys .platform == 'darwin' and sys ._framework :
307- return f'{ userbase } /lib/rustpython /site-packages'
328+ return f'{ userbase } /lib/{ implementation_lower } /site-packages'
308329
309- return f'{ userbase } /lib/rustpython{ version [0 ]} .{ version [1 ]} /site-packages'
330+ # XXX: RUSTPYTHON
331+ return f'{ userbase } /lib/rustpython{ version [0 ]} .{ version [1 ]} { abi_thread } /site-packages'
310332
311333
312334def getuserbase ():
@@ -372,15 +394,20 @@ def getsitepackages(prefixes=None):
372394 continue
373395 seen .add (prefix )
374396
397+ implementation = _get_implementation ().lower ()
398+ ver = sys .version_info
399+ if hasattr (sys , 'abiflags' ) and 't' in sys .abiflags :
400+ abi_thread = 't'
401+ else :
402+ abi_thread = ''
375403 if os .sep == '/' :
376404 libdirs = [sys .platlibdir ]
377405 if sys .platlibdir != "lib" :
378406 libdirs .append ("lib" )
379407
380408 for libdir in libdirs :
381409 path = os .path .join (prefix , libdir ,
382- # XXX: RUSTPYTHON; please keep this change for site-packages
383- "rustpython%d.%d" % sys .version_info [:2 ],
410+ f"{ implementation } { ver [0 ]} .{ ver [1 ]} { abi_thread } " ,
384411 "site-packages" )
385412 sitepackages .append (path )
386413 else :
@@ -417,8 +444,9 @@ def setcopyright():
417444 """Set 'copyright' and 'credits' in builtins"""
418445 builtins .copyright = _sitebuiltins ._Printer ("copyright" , sys .copyright )
419446 builtins .credits = _sitebuiltins ._Printer ("credits" , """\
420- Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
421- for supporting Python development. See www.python.org for more information.""" )
447+ Thanks to CWI, CNRI, BeOpen, Zope Corporation, the Python Software
448+ Foundation, and a cast of thousands for supporting Python
449+ development. See www.python.org for more information.""" )
422450 files , dirs = [], []
423451 # Not all modules are required to have a __file__ attribute. See
424452 # PEP 420 for more details.
@@ -437,27 +465,76 @@ def setcopyright():
437465def sethelper ():
438466 builtins .help = _sitebuiltins ._Helper ()
439467
468+
469+ def gethistoryfile ():
470+ """Check if the PYTHON_HISTORY environment variable is set and define
471+ it as the .python_history file. If PYTHON_HISTORY is not set, use the
472+ default .python_history file.
473+ """
474+ if not sys .flags .ignore_environment :
475+ history = os .environ .get ("PYTHON_HISTORY" )
476+ if history :
477+ return history
478+ return os .path .join (os .path .expanduser ('~' ),
479+ '.python_history' )
480+
481+
440482def enablerlcompleter ():
441483 """Enable default readline configuration on interactive prompts, by
442484 registering a sys.__interactivehook__.
485+ """
486+ sys .__interactivehook__ = register_readline
487+
488+
489+ def register_readline ():
490+ """Configure readline completion on interactive prompts.
443491
444492 If the readline module can be imported, the hook will set the Tab key
445493 as completion key and register ~/.python_history as history file.
446494 This can be overridden in the sitecustomize or usercustomize module,
447495 or in a PYTHONSTARTUP file.
448496 """
449- def register_readline ():
450- import atexit
497+ if not sys .flags .ignore_environment :
498+ PYTHON_BASIC_REPL = os .getenv ("PYTHON_BASIC_REPL" )
499+ else :
500+ PYTHON_BASIC_REPL = False
501+
502+ import atexit
503+
504+ try :
451505 try :
452506 import readline
453- import rlcompleter
454507 except ImportError :
455- return
508+ readline = None
509+ else :
510+ import rlcompleter # noqa: F401
511+ except ImportError :
512+ return
456513
514+ try :
515+ if PYTHON_BASIC_REPL :
516+ CAN_USE_PYREPL = False
517+ else :
518+ original_path = sys .path
519+ sys .path = [p for p in original_path if p != '' ]
520+ try :
521+ import _pyrepl .readline
522+ if os .name == "nt" :
523+ import _pyrepl .windows_console
524+ console_errors = (_pyrepl .windows_console ._error ,)
525+ else :
526+ import _pyrepl .unix_console
527+ console_errors = _pyrepl .unix_console ._error
528+ from _pyrepl .main import CAN_USE_PYREPL
529+ finally :
530+ sys .path = original_path
531+ except ImportError :
532+ return
533+
534+ if readline is not None :
457535 # Reading the initialization (config) file may not be enough to set a
458536 # completion key, so we set one first and then read the file.
459- readline_doc = getattr (readline , '__doc__' , '' )
460- if readline_doc is not None and 'libedit' in readline_doc :
537+ if readline .backend == 'editline' :
461538 readline .parse_and_bind ('bind ^I rl_complete' )
462539 else :
463540 readline .parse_and_bind ('tab: complete' )
@@ -471,30 +548,44 @@ def register_readline():
471548 # want to ignore the exception.
472549 pass
473550
474- if readline .get_current_history_length () == 0 :
475- # If no history was loaded, default to .python_history.
476- # The guard is necessary to avoid doubling history size at
477- # each interpreter exit when readline was already configured
478- # through a PYTHONSTARTUP hook, see:
479- # http://bugs.python.org/issue5845#msg198636
480- history = os .path .join (os .path .expanduser ('~' ),
481- '.python_history' )
551+ if readline is None or readline .get_current_history_length () == 0 :
552+ # If no history was loaded, default to .python_history,
553+ # or PYTHON_HISTORY.
554+ # The guard is necessary to avoid doubling history size at
555+ # each interpreter exit when readline was already configured
556+ # through a PYTHONSTARTUP hook, see:
557+ # http://bugs.python.org/issue5845#msg198636
558+ history = gethistoryfile ()
559+
560+ if CAN_USE_PYREPL :
561+ readline_module = _pyrepl .readline
562+ exceptions = (OSError , * console_errors )
563+ else :
564+ if readline is None :
565+ return
566+ readline_module = readline
567+ exceptions = OSError
568+
569+ try :
570+ readline_module .read_history_file (history )
571+ except exceptions :
572+ pass
573+
574+ def write_history ():
482575 try :
483- readline .read_history_file (history )
484- except OSError :
576+ readline_module .write_history_file (history )
577+ except (FileNotFoundError , PermissionError ):
578+ # home directory does not exist or is not writable
579+ # https://bugs.python.org/issue19891
485580 pass
581+ except OSError :
582+ if errno .EROFS :
583+ pass # gh-128066: read-only file system
584+ else :
585+ raise
486586
487- def write_history ():
488- try :
489- readline .write_history_file (history )
490- except OSError :
491- # bpo-19891, bpo-41193: Home directory does not exist
492- # or is not writable, or the filesystem is read-only.
493- pass
494-
495- atexit .register (write_history )
587+ atexit .register (write_history )
496588
497- sys .__interactivehook__ = register_readline
498589
499590def venv (known_paths ):
500591 global PREFIXES , ENABLE_USER_SITE
@@ -679,17 +770,5 @@ def exists(path):
679770 print (textwrap .dedent (help % (sys .argv [0 ], os .pathsep )))
680771 sys .exit (10 )
681772
682- def gethistoryfile ():
683- """Check if the PYTHON_HISTORY environment variable is set and define
684- it as the .python_history file. If PYTHON_HISTORY is not set, use the
685- default .python_history file.
686- """
687- if not sys .flags .ignore_environment :
688- history = os .environ .get ("PYTHON_HISTORY" )
689- if history :
690- return history
691- return os .path .join (os .path .expanduser ('~' ),
692- '.python_history' )
693-
694773if __name__ == '__main__' :
695774 _script ()
0 commit comments