3
3
History management classes
4
4
"""
5
5
6
+ import json
6
7
import re
7
8
from collections import (
8
9
OrderedDict ,
9
10
)
10
11
from typing import (
12
+ Any ,
11
13
Callable ,
14
+ Dict ,
12
15
Iterable ,
13
16
List ,
14
17
Optional ,
@@ -33,6 +36,9 @@ class HistoryItem:
33
36
_listformat = ' {:>4} {}'
34
37
_ex_listformat = ' {:>4}x {}'
35
38
39
+ # Used in JSON dictionaries
40
+ _statement_field = 'statement'
41
+
36
42
statement : Statement = attr .ib (default = None , validator = attr .validators .instance_of (Statement ))
37
43
38
44
def __str__ (self ) -> str :
@@ -94,6 +100,22 @@ def pr(self, idx: int, script: bool = False, expanded: bool = False, verbose: bo
94
100
95
101
return ret_str
96
102
103
+ def to_dict (self ) -> Dict [str , Any ]:
104
+ """Utility method to convert this HistoryItem into a dictionary for use in persistent JSON history files"""
105
+ return {HistoryItem ._statement_field : self .statement .to_dict ()}
106
+
107
+ @staticmethod
108
+ def from_dict (source_dict : Dict [str , Any ]) -> 'HistoryItem' :
109
+ """
110
+ Utility method to restore a HistoryItem from a dictionary
111
+
112
+ :param source_dict: source data dictionary (generated using to_dict())
113
+ :return: HistoryItem object
114
+ :raises KeyError: if source_dict is missing required elements
115
+ """
116
+ statement_dict = source_dict [HistoryItem ._statement_field ]
117
+ return HistoryItem (Statement .from_dict (statement_dict ))
118
+
97
119
98
120
class History (List [HistoryItem ]):
99
121
"""A list of :class:`~cmd2.history.HistoryItem` objects with additional methods
@@ -109,6 +131,11 @@ class History(List[HistoryItem]):
109
131
class to gain access to the historical record.
110
132
"""
111
133
134
+ # Used in JSON dictionaries
135
+ _history_version = '1.0.0'
136
+ _history_version_field = 'history_version'
137
+ _history_items_field = 'history_items'
138
+
112
139
def __init__ (self , seq : Iterable [HistoryItem ] = ()) -> None :
113
140
super (History , self ).__init__ (seq )
114
141
self .session_start_index = 0
@@ -301,3 +328,36 @@ def _build_result_dictionary(
301
328
if filter_func is None or filter_func (self [index ]):
302
329
results [index + 1 ] = self [index ]
303
330
return results
331
+
332
+ def to_json (self ) -> str :
333
+ """Utility method to convert this History into a JSON string for use in persistent history files"""
334
+ json_dict = {
335
+ History ._history_version_field : History ._history_version ,
336
+ History ._history_items_field : [hi .to_dict () for hi in self ],
337
+ }
338
+ return json .dumps (json_dict , ensure_ascii = False , indent = 2 )
339
+
340
+ @staticmethod
341
+ def from_json (history_json : str ) -> 'History' :
342
+ """
343
+ Utility method to restore History from a JSON string
344
+
345
+ :param history_json: history data as JSON string (generated using to_json())
346
+ :return: History object
347
+ :raises json.JSONDecodeError: if passed invalid JSON string
348
+ :raises KeyError: if JSON is missing required elements
349
+ :raises ValueError: if history version in JSON isn't supported
350
+ """
351
+ json_dict = json .loads (history_json )
352
+ version = json_dict [History ._history_version_field ]
353
+ if version != History ._history_version :
354
+ raise ValueError (
355
+ f"Unsupported history file version: { version } . This application uses version { History ._history_version } ."
356
+ )
357
+
358
+ items = json_dict [History ._history_items_field ]
359
+ history = History ()
360
+ for hi_dict in items :
361
+ history .append (HistoryItem .from_dict (hi_dict ))
362
+
363
+ return history
0 commit comments