Skip to content

Commit dca6d25

Browse files
authored
Merge pull request #744 from python-cmd2/make_history_directory
Create directory for the persistent history file if it does not already exist
2 parents a5e5282 + 179475c commit dca6d25

File tree

3 files changed

+48
-7
lines changed

3 files changed

+48
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## 0.9.16 (TBD, 2019)
22
* Enhancements
33
* Raise `TypeError` if trying to set choices/completions on argparse action that accepts no arguments
4+
* Create directory for the persistent history file if it does not already exist
45

56
## 0.9.15 (July 24, 2019)
67
* Bug Fixes

cmd2/cmd2.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3603,23 +3603,33 @@ def _initialize_history(self, hist_file):
36033603

36043604
hist_file = os.path.abspath(os.path.expanduser(hist_file))
36053605

3606-
# first we try and unpickle the history file
3607-
history = History()
36083606
# on Windows, trying to open a directory throws a permission
36093607
# error, not a `IsADirectoryError`. So we'll check it ourselves.
36103608
if os.path.isdir(hist_file):
3611-
msg = "persistent history file '{}' is a directory"
3609+
msg = "Persistent history file '{}' is a directory"
36123610
self.perror(msg.format(hist_file))
36133611
return
36143612

3613+
# Create the directory for the history file if it doesn't already exist
3614+
hist_file_dir = os.path.dirname(hist_file)
3615+
try:
3616+
os.makedirs(hist_file_dir, exist_ok=True)
3617+
except OSError as ex:
3618+
msg = "Error creating persistent history file directory '{}': {}".format(hist_file_dir, ex)
3619+
self.pexcept(msg)
3620+
return
3621+
3622+
# first we try and unpickle the history file
3623+
history = History()
3624+
36153625
try:
36163626
with open(hist_file, 'rb') as fobj:
36173627
history = pickle.load(fobj)
36183628
except (AttributeError, EOFError, FileNotFoundError, ImportError, IndexError, KeyError, pickle.UnpicklingError):
36193629
# If any non-operating system error occurs when attempting to unpickle, just use an empty history
36203630
pass
36213631
except OSError as ex:
3622-
msg = "can not read persistent history file '{}': {}"
3632+
msg = "Can not read persistent history file '{}': {}"
36233633
self.pexcept(msg.format(hist_file, ex))
36243634
return
36253635

@@ -3655,7 +3665,7 @@ def _persist_history(self):
36553665
with open(self.persistent_history_file, 'wb') as fobj:
36563666
pickle.dump(self.history, fobj)
36573667
except OSError as ex:
3658-
msg = "can not write persistent history file '{}': {}"
3668+
msg = "Can not write persistent history file '{}': {}"
36593669
self.pexcept(msg.format(self.persistent_history_file, ex))
36603670

36613671
def _generate_transcript(self, history: List[Union[HistoryItem, str]], transcript_file: str) -> None:

tests/test_history.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -638,14 +638,44 @@ def test_history_file_is_directory(capsys):
638638
_, err = capsys.readouterr()
639639
assert 'is a directory' in err
640640

641+
def test_history_can_create_directory(mocker):
642+
# Mock out atexit.register so the persistent file doesn't written when this function
643+
# exists because we will be deleting the directory it needs to go to.
644+
mock_register = mocker.patch('atexit.register')
645+
646+
# Create a temp path for us to use and let it get deleted
647+
with tempfile.TemporaryDirectory() as test_dir:
648+
pass
649+
assert not os.path.isdir(test_dir)
650+
651+
# Add some subdirectories for the complete history file directory
652+
hist_file_dir = os.path.join(test_dir, 'subdir1', 'subdir2')
653+
hist_file = os.path.join(hist_file_dir, 'hist_file')
654+
655+
# Make sure cmd2 creates the history file directory
656+
cmd2.Cmd(persistent_history_file=hist_file)
657+
assert os.path.isdir(hist_file_dir)
658+
659+
# Cleanup
660+
os.rmdir(hist_file_dir)
661+
662+
def test_history_cannot_create_directory(mocker, capsys):
663+
mock_open = mocker.patch('os.makedirs')
664+
mock_open.side_effect = OSError
665+
666+
hist_file_path = os.path.join('fake_dir', 'file')
667+
cmd2.Cmd(persistent_history_file=hist_file_path)
668+
_, err = capsys.readouterr()
669+
assert 'Error creating persistent history file directory' in err
670+
641671
def test_history_file_permission_error(mocker, capsys):
642672
mock_open = mocker.patch('builtins.open')
643673
mock_open.side_effect = PermissionError
644674

645675
cmd2.Cmd(persistent_history_file='/tmp/doesntmatter')
646676
out, err = capsys.readouterr()
647677
assert not out
648-
assert 'can not read' in err
678+
assert 'Can not read' in err
649679

650680
def test_history_file_conversion_no_truncate_on_init(hist_file, capsys):
651681
# make sure we don't truncate the plain text history file on init
@@ -720,4 +750,4 @@ def test_persist_history_permission_error(hist_file, mocker, capsys):
720750
app._persist_history()
721751
out, err = capsys.readouterr()
722752
assert not out
723-
assert 'can not write' in err
753+
assert 'Can not write' in err

0 commit comments

Comments
 (0)