50
50
from .unix_console import UnixConsole as Console , _error
51
51
52
52
ENCODING = sys .getdefaultencoding () or "latin1"
53
+ _EDITLINE_MARKER = "_HiStOrY_V2_"
54
+ _EDITLINE_BYTES_MARKER = b"_HiStOrY_V2_"
53
55
54
56
55
57
# types
60
62
TYPE_CHECKING = False
61
63
62
64
if TYPE_CHECKING :
63
- from typing import Any , Mapping
65
+ from typing import Any , IO , Mapping
64
66
65
67
66
68
MoreLinesCallable = Callable [[str ], bool ]
@@ -425,6 +427,19 @@ def set_history_length(self, length: int) -> None:
425
427
def get_current_history_length (self ) -> int :
426
428
return len (self .get_reader ().history )
427
429
430
+ @staticmethod
431
+ def _analyze_history_file (filename : str | IO [bytes ]) -> tuple [bool , str ]:
432
+ if isinstance (filename , str ):
433
+ if not os .path .exists (filename ):
434
+ return False , "utf-8"
435
+ with open (filename , "rb" ) as f :
436
+ is_editline = f .readline ().startswith (_EDITLINE_BYTES_MARKER )
437
+ else :
438
+ is_editline = f .readline ().startswith (_EDITLINE_BYTES_MARKER )
439
+ if is_editline :
440
+ return True , "unicode-escape"
441
+ return False , "utf-8"
442
+
428
443
def read_history_file (self , filename : str = gethistoryfile ()) -> None :
429
444
# multiline extension (really a hack) for the end of lines that
430
445
# are actually continuations inside a single multiline_input()
@@ -433,12 +448,9 @@ def read_history_file(self, filename: str = gethistoryfile()) -> None:
433
448
history = self .get_reader ().history
434
449
435
450
with open (os .path .expanduser (filename ), 'rb' ) as f :
436
- is_editline = f .readline ().startswith (b"_HiStOrY_V2_" )
437
- if is_editline :
438
- encoding = "unicode-escape"
439
- else :
451
+ is_editline , encoding = self ._analyze_history_file (f )
452
+ if not is_editline :
440
453
f .seek (0 )
441
- encoding = "utf-8"
442
454
443
455
lines = [line .decode (encoding , errors = 'replace' ) for line in f .read ().split (b'\n ' )]
444
456
buffer = []
@@ -457,9 +469,12 @@ def read_history_file(self, filename: str = gethistoryfile()) -> None:
457
469
def write_history_file (self , filename : str = gethistoryfile ()) -> None :
458
470
maxlength = self .saved_history_length
459
471
history = self .get_reader ().get_trimmed_history (maxlength )
460
- f = open (os .path .expanduser (filename ), "w" ,
461
- encoding = "utf-8" , newline = "\n " )
462
- with f :
472
+
473
+ filename = os .path .expanduser (filename )
474
+ is_editline , encoding = self ._analyze_history_file (filename )
475
+ with open (filename , "w" , encoding = encoding , newline = "\n " ) as f :
476
+ if is_editline :
477
+ f .write (f"{ _EDITLINE_MARKER } \n " )
463
478
for entry in history :
464
479
entry = entry .replace ("\n " , "\r \n " ) # multiline history support
465
480
f .write (entry + "\n " )
@@ -469,9 +484,10 @@ def append_history_file(self, filename: str = gethistoryfile()) -> None:
469
484
saved_length = self .get_history_length ()
470
485
length = self .get_current_history_length () - saved_length
471
486
history = reader .get_trimmed_history (length )
472
- f = open (os .path .expanduser (filename ), "a" ,
473
- encoding = "utf-8" , newline = "\n " )
474
- with f :
487
+
488
+ filename = os .path .expanduser (filename )
489
+ _ , encoding = self ._analyze_history_file (filename )
490
+ with open (filename , "a" , encoding = encoding , newline = "\n " ) as f :
475
491
for entry in history :
476
492
entry = entry .replace ("\n " , "\r \n " ) # multiline history support
477
493
f .write (entry + "\n " )
0 commit comments