Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-cmd2 for openSUSE:Factory checked in at 2021-06-18 10:13:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-cmd2 (Old) and /work/SRC/openSUSE:Factory/.python-cmd2.new.2625 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-cmd2" Fri Jun 18 10:13:14 2021 rev:38 rq:899979 version:2.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-cmd2/python-cmd2.changes 2021-06-11 22:29:59.842059854 +0200 +++ /work/SRC/openSUSE:Factory/.python-cmd2.new.2625/python-cmd2.changes 2021-06-18 10:13:21.613960455 +0200 @@ -1,0 +2,8 @@ +Mon Jun 14 15:58:07 UTC 2021 - Martin Hauke <[email protected]> + +- Udpate to version 2.1.0 + Enhancements + * Converted persistent history files from pickle to compressed + JSON. + +------------------------------------------------------------------- Old: ---- cmd2-2.0.1.tar.gz New: ---- cmd2-2.1.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-cmd2.spec ++++++ --- /var/tmp/diff_new_pack.gzpglB/_old 2021-06-18 10:13:22.085961079 +0200 +++ /var/tmp/diff_new_pack.gzpglB/_new 2021-06-18 10:13:22.085961079 +0200 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-cmd2 -Version: 2.0.1 +Version: 2.1.0 Release: 0 Summary: Extra features for standard library's cmd module License: MIT ++++++ cmd2-2.0.1.tar.gz -> cmd2-2.1.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/.github/workflows/lint.yml new/cmd2-2.1.0/.github/workflows/lint.yml --- old/cmd2-2.0.1/.github/workflows/lint.yml 2021-04-06 04:23:51.000000000 +0200 +++ new/cmd2-2.1.0/.github/workflows/lint.yml 2021-06-14 17:29:07.000000000 +0200 @@ -23,6 +23,6 @@ with: python-version: ${{ matrix.python-version }} - name: Install python prerequisites - run: pip install -U --user pip setuptools setuptools-scm flake8 + run: pip install -U --user pip setuptools setuptools-scm nox - name: Lint - run: python -m flake8 . + run: python -m nox --non-interactive --session validate-${{ matrix.python-version }} -k flake8 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/.github/workflows/mypy.yml new/cmd2-2.1.0/.github/workflows/mypy.yml --- old/cmd2-2.0.1/.github/workflows/mypy.yml 2021-04-07 02:10:44.000000000 +0200 +++ new/cmd2-2.1.0/.github/workflows/mypy.yml 2021-06-14 17:29:07.000000000 +0200 @@ -23,6 +23,6 @@ with: python-version: ${{ matrix.python-version }} - name: Install python prerequisites - run: pip install -U --user pip setuptools setuptools-scm mypy + run: pip install -U --user pip setuptools setuptools-scm nox - name: MyPy - run: python -m mypy cmd2 + run: python -m nox --non-interactive --session validate-${{ matrix.python-version }} -k mypy # Run nox for mypy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/CHANGELOG.md new/cmd2-2.1.0/CHANGELOG.md --- old/cmd2-2.0.1/CHANGELOG.md 2021-06-07 06:30:12.000000000 +0200 +++ new/cmd2-2.1.0/CHANGELOG.md 2021-06-14 17:34:22.000000000 +0200 @@ -1,3 +1,7 @@ +## 2.1.0 (June 14, 2021) +* Enhancements + * Converted persistent history files from pickle to compressed JSON + ## 2.0.1 (June 7, 2021) * Bug Fixes * Exclude `plugins` and `tests_isolated` directories from tarball published to PyPI for `cmd2` release diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/PKG-INFO new/cmd2-2.1.0/PKG-INFO --- old/cmd2-2.0.1/PKG-INFO 2021-06-07 06:36:34.064932000 +0200 +++ new/cmd2-2.1.0/PKG-INFO 2021-06-14 17:43:16.063191200 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: cmd2 -Version: 2.0.1 +Version: 2.1.0 Summary: cmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python Home-page: https://github.com/python-cmd2/cmd2 Author: Catherine Devlin @@ -407,3 +407,4 @@ Description-Content-Type: text/markdown Provides-Extra: test Provides-Extra: dev +Provides-Extra: validate diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/cmd2/cmd2.py new/cmd2-2.1.0/cmd2/cmd2.py --- old/cmd2-2.0.1/cmd2/cmd2.py 2021-05-25 21:49:52.000000000 +0200 +++ new/cmd2-2.1.0/cmd2/cmd2.py 2021-06-14 17:29:07.000000000 +0200 @@ -34,7 +34,6 @@ import glob import inspect import os -import pickle import pydoc import re import sys @@ -4443,15 +4442,13 @@ def _initialize_history(self, hist_file: str) -> None: """Initialize history using history related attributes - This function can determine whether history is saved in the prior text-based - format (one line of input is stored as one line in the file), or the new-as- - of-version 0.9.13 pickle based format. - - History created by versions <= 0.9.12 is in readline format, i.e. plain text files. - - Initializing history does not effect history files on disk, versions >= 0.9.13 always - write history in the pickle format. + :param hist_file: optional path to persistent history file. If specified, then history from + previous sessions will be included. Additionally, all history will be written + to this file when the application exits. """ + import json + import lzma + self.history = History() # with no persistent history, nothing else in this method is relevant if not hist_file: @@ -4474,36 +4471,31 @@ self.perror(f"Error creating persistent history file directory '{hist_file_dir}': {ex}") return - # first we try and unpickle the history file - history = History() - + # Read and process history file try: with open(hist_file, 'rb') as fobj: - history = pickle.load(fobj) - except ( - AttributeError, - EOFError, - FileNotFoundError, - ImportError, - IndexError, - KeyError, - ValueError, - pickle.UnpicklingError, - ): - # If any of these errors occur when attempting to unpickle, just use an empty history + compressed_bytes = fobj.read() + history_json = lzma.decompress(compressed_bytes).decode(encoding='utf-8') + self.history = History.from_json(history_json) + except FileNotFoundError: + # Just use an empty history pass except OSError as ex: self.perror(f"Cannot read persistent history file '{hist_file}': {ex}") return + except (json.JSONDecodeError, lzma.LZMAError, KeyError, UnicodeDecodeError, ValueError) as ex: + self.perror( + f"Error processing persistent history file '{hist_file}': {ex}\n" + f"The history file will be recreated when this application exits." + ) - self.history = history self.history.start_session() self.persistent_history_file = hist_file # populate readline history if rl_type != RlType.NONE: last = None - for item in history: + for item in self.history: # Break the command into its individual lines for line in item.raw.splitlines(): # readline only adds a single entry for multiple sequential identical lines @@ -4520,14 +4512,19 @@ atexit.register(self._persist_history) def _persist_history(self) -> None: - """Write history out to the history file""" + """Write history out to the persistent history file as compressed JSON""" + import lzma + if not self.persistent_history_file: return self.history.truncate(self._persistent_history_length) try: + history_json = self.history.to_json() + compressed_bytes = lzma.compress(history_json.encode(encoding='utf-8')) + with open(self.persistent_history_file, 'wb') as fobj: - pickle.dump(self.history, fobj) + fobj.write(compressed_bytes) except OSError as ex: self.perror(f"Cannot write persistent history file '{self.persistent_history_file}': {ex}") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/cmd2/history.py new/cmd2-2.1.0/cmd2/history.py --- old/cmd2-2.0.1/cmd2/history.py 2021-04-07 02:10:44.000000000 +0200 +++ new/cmd2-2.1.0/cmd2/history.py 2021-06-14 17:29:07.000000000 +0200 @@ -3,12 +3,15 @@ History management classes """ +import json import re from collections import ( OrderedDict, ) from typing import ( + Any, Callable, + Dict, Iterable, List, Optional, @@ -33,6 +36,9 @@ _listformat = ' {:>4} {}' _ex_listformat = ' {:>4}x {}' + # Used in JSON dictionaries + _statement_field = 'statement' + statement: Statement = attr.ib(default=None, validator=attr.validators.instance_of(Statement)) def __str__(self) -> str: @@ -94,6 +100,22 @@ return ret_str + def to_dict(self) -> Dict[str, Any]: + """Utility method to convert this HistoryItem into a dictionary for use in persistent JSON history files""" + return {HistoryItem._statement_field: self.statement.to_dict()} + + @staticmethod + def from_dict(source_dict: Dict[str, Any]) -> 'HistoryItem': + """ + Utility method to restore a HistoryItem from a dictionary + + :param source_dict: source data dictionary (generated using to_dict()) + :return: HistoryItem object + :raises KeyError: if source_dict is missing required elements + """ + statement_dict = source_dict[HistoryItem._statement_field] + return HistoryItem(Statement.from_dict(statement_dict)) + class History(List[HistoryItem]): """A list of :class:`~cmd2.history.HistoryItem` objects with additional methods @@ -109,6 +131,11 @@ class to gain access to the historical record. """ + # Used in JSON dictionaries + _history_version = '1.0.0' + _history_version_field = 'history_version' + _history_items_field = 'history_items' + def __init__(self, seq: Iterable[HistoryItem] = ()) -> None: super(History, self).__init__(seq) self.session_start_index = 0 @@ -301,3 +328,36 @@ if filter_func is None or filter_func(self[index]): results[index + 1] = self[index] return results + + def to_json(self) -> str: + """Utility method to convert this History into a JSON string for use in persistent history files""" + json_dict = { + History._history_version_field: History._history_version, + History._history_items_field: [hi.to_dict() for hi in self], + } + return json.dumps(json_dict, ensure_ascii=False, indent=2) + + @staticmethod + def from_json(history_json: str) -> 'History': + """ + Utility method to restore History from a JSON string + + :param history_json: history data as JSON string (generated using to_json()) + :return: History object + :raises json.JSONDecodeError: if passed invalid JSON string + :raises KeyError: if JSON is missing required elements + :raises ValueError: if history version in JSON isn't supported + """ + json_dict = json.loads(history_json) + version = json_dict[History._history_version_field] + if version != History._history_version: + raise ValueError( + f"Unsupported history file version: {version}. This application uses version {History._history_version}." + ) + + items = json_dict[History._history_items_field] + history = History() + for hi_dict in items: + history.append(HistoryItem.from_dict(hi_dict)) + + return history diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/cmd2/parsing.py new/cmd2-2.1.0/cmd2/parsing.py --- old/cmd2-2.0.1/cmd2/parsing.py 2021-04-14 00:10:10.000000000 +0200 +++ new/cmd2-2.1.0/cmd2/parsing.py 2021-06-14 17:29:07.000000000 +0200 @@ -147,6 +147,9 @@ # if output was redirected, the destination file token (quotes preserved) output_to: str = attr.ib(default='', validator=attr.validators.instance_of(str)) + # Used in JSON dictionaries + _args_field = 'args' + def __new__(cls, value: object, *pos_args: Any, **kw_args: Any) -> 'Statement': """Create a new instance of Statement. @@ -221,6 +224,31 @@ return rtn + def to_dict(self) -> Dict[str, Any]: + """Utility method to convert this Statement into a dictionary for use in persistent JSON history files""" + return self.__dict__.copy() + + @staticmethod + def from_dict(source_dict: Dict[str, Any]) -> 'Statement': + """ + Utility method to restore a Statement from a dictionary + + :param source_dict: source data dictionary (generated using to_dict()) + :return: Statement object + :raises KeyError: if source_dict is missing required elements + """ + # value needs to be passed as a positional argument. It corresponds to the args field. + try: + value = source_dict[Statement._args_field] + except KeyError as ex: + raise KeyError(f"Statement dictionary is missing {ex} field") + + # Pass the rest at kwargs (minus args) + kwargs = source_dict.copy() + del kwargs[Statement._args_field] + + return Statement(value, **kwargs) + class StatementParser: """Parse user input as a string into discrete command components.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/cmd2.egg-info/PKG-INFO new/cmd2-2.1.0/cmd2.egg-info/PKG-INFO --- old/cmd2-2.0.1/cmd2.egg-info/PKG-INFO 2021-06-07 06:36:33.000000000 +0200 +++ new/cmd2-2.1.0/cmd2.egg-info/PKG-INFO 2021-06-14 17:43:15.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: cmd2 -Version: 2.0.1 +Version: 2.1.0 Summary: cmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python Home-page: https://github.com/python-cmd2/cmd2 Author: Catherine Devlin @@ -407,3 +407,4 @@ Description-Content-Type: text/markdown Provides-Extra: test Provides-Extra: dev +Provides-Extra: validate diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/cmd2.egg-info/SOURCES.txt new/cmd2-2.1.0/cmd2.egg-info/SOURCES.txt --- old/cmd2-2.0.1/cmd2.egg-info/SOURCES.txt 2021-06-07 06:36:33.000000000 +0200 +++ new/cmd2-2.1.0/cmd2.egg-info/SOURCES.txt 2021-06-14 17:43:15.000000000 +0200 @@ -110,7 +110,6 @@ docs/plugins/external_test.rst docs/plugins/index.rst examples/.cmd2rc -examples/.coverage examples/alias_startup.py examples/arg_decorators.py examples/arg_print.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/cmd2.egg-info/requires.txt new/cmd2-2.1.0/cmd2.egg-info/requires.txt --- old/cmd2-2.0.1/cmd2.egg-info/requires.txt 2021-06-07 06:36:33.000000000 +0200 +++ new/cmd2-2.1.0/cmd2.egg-info/requires.txt 2021-06-14 17:43:15.000000000 +0200 @@ -14,17 +14,18 @@ pyreadline3 [dev] -pytest>=4.6 codecov +doc8 +flake8 +invoke +mypy==0.902 +nox +pytest>=4.6 pytest-cov pytest-mock -nox -flake8 sphinx sphinx-rtd-theme sphinx-autobuild -doc8 -invoke twine>=1.11 [test] @@ -36,3 +37,7 @@ [test:sys_platform == "darwin"] gnureadline + +[validate] +flake8 +mypy==0.902 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/docs/conf.py new/cmd2-2.1.0/docs/conf.py --- old/cmd2-2.0.1/docs/conf.py 2021-04-06 04:23:51.000000000 +0200 +++ new/cmd2-2.1.0/docs/conf.py 2021-06-14 17:29:07.000000000 +0200 @@ -181,7 +181,7 @@ ('py:class', 'TextIO'), ('py:class', 'Union[None, Iterable, Callable]'), ('py:class', 'argparse._SubParsersAction'), - ('py:class', '_T'), + ('py:class', 'cmd2.utils._T'), ('py:class', 'StdSim'), ('py:class', 'frame'), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/docs/features/history.rst new/cmd2-2.1.0/docs/features/history.rst --- old/cmd2-2.0.1/docs/features/history.rst 2020-02-26 00:41:35.000000000 +0100 +++ new/cmd2-2.1.0/docs/features/history.rst 2021-06-14 17:29:07.000000000 +0200 @@ -8,7 +8,7 @@ history. :class:`cmd2.Cmd` offers the same ``readline`` capabilities, but also maintains -it's own data structures for the history of all commands entered by the user. +its own data structures for the history of all commands entered by the user. When the class is initialized, it creates an instance of the :class:`cmd2.history.History` class (which is a subclass of ``list``) as :data:`cmd2.Cmd.history`. @@ -20,8 +20,9 @@ ``cmd2`` adds the option of making this history persistent via optional arguments to :meth:`cmd2.Cmd.__init__`. If you pass a filename in the ``persistent_history_file`` argument, the contents of :data:`cmd2.Cmd.history` -will be pickled into that history file. We chose to use pickle instead of plain -text so that we can save the results of parsing all the commands. +will be written as compressed JSON to that history file. We chose this format +instead of plain text to preserve the complete :class:`cmd2.Statement` object +for each command. .. note:: @@ -41,9 +42,7 @@ class, and the :class:`cmd2.history.HistoryItem` class are all part of the public API for :class:`cmd2.Cmd`. You could use these classes to implement write your own ``history`` command (see below for documentation on how the -included ``history`` command works). If you don't like pickled history, you -could implement your own mechanism for saving and loading history from a plain -text file. +included ``history`` command works). For Users diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/examples/.coverage new/cmd2-2.1.0/examples/.coverage --- old/cmd2-2.0.1/examples/.coverage 2019-06-07 05:49:31.000000000 +0200 +++ new/cmd2-2.1.0/examples/.coverage 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -!coverage.py: This is a private format, don't read it directly!{"lines":{}} \ No newline at end of file Binary files old/cmd2-2.0.1/examples/cmd2_history.dat and new/cmd2-2.1.0/examples/cmd2_history.dat differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/noxfile.py new/cmd2-2.1.0/noxfile.py --- old/cmd2-2.0.1/noxfile.py 2021-04-06 04:23:51.000000000 +0200 +++ new/cmd2-2.1.0/noxfile.py 2021-06-14 17:29:07.000000000 +0200 @@ -39,3 +39,10 @@ '--no-pty', '--append-cov', ) + + [email protected](python=['3.8', '3.9']) [email protected]('step', ['mypy', 'flake8']) +def validate(session, step): + session.install('invoke', './[validate]') + session.run('invoke', step) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/setup.py new/cmd2-2.1.0/setup.py --- old/cmd2-2.0.1/setup.py 2021-04-06 04:23:51.000000000 +0200 +++ new/cmd2-2.1.0/setup.py 2021-06-14 17:29:07.000000000 +0200 @@ -66,19 +66,24 @@ ], # development only dependencies: install with 'pip install -e .[dev]' 'dev': [ - "pytest>=4.6", 'codecov', + 'doc8', + 'flake8', + 'invoke', + 'mypy==0.902', + 'nox', + "pytest>=4.6", 'pytest-cov', 'pytest-mock', - 'nox', - 'flake8', 'sphinx', 'sphinx-rtd-theme', 'sphinx-autobuild', - 'doc8', - 'invoke', 'twine>=1.11', ], + 'validate': [ + 'flake8', + 'mypy==0.902', + ], } PACKAGE_DATA = { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/tests/test_history.py new/cmd2-2.1.0/tests/test_history.py --- old/cmd2-2.0.1/tests/test_history.py 2021-05-25 21:49:52.000000000 +0200 +++ new/cmd2-2.1.0/tests/test_history.py 2021-06-14 17:29:07.000000000 +0200 @@ -60,6 +60,72 @@ return h +# Represents the hist fixture's JSON +hist_json = ( + '{\n' + ' "history_version": "1.0.0",\n' + ' "history_items": [\n' + ' {\n' + ' "statement": {\n' + ' "args": "",\n' + ' "raw": "first",\n' + ' "command": "",\n' + ' "arg_list": [],\n' + ' "multiline_command": "",\n' + ' "terminator": "",\n' + ' "suffix": "",\n' + ' "pipe_to": "",\n' + ' "output": "",\n' + ' "output_to": ""\n' + ' }\n' + ' },\n' + ' {\n' + ' "statement": {\n' + ' "args": "",\n' + ' "raw": "second",\n' + ' "command": "",\n' + ' "arg_list": [],\n' + ' "multiline_command": "",\n' + ' "terminator": "",\n' + ' "suffix": "",\n' + ' "pipe_to": "",\n' + ' "output": "",\n' + ' "output_to": ""\n' + ' }\n' + ' },\n' + ' {\n' + ' "statement": {\n' + ' "args": "",\n' + ' "raw": "third",\n' + ' "command": "",\n' + ' "arg_list": [],\n' + ' "multiline_command": "",\n' + ' "terminator": "",\n' + ' "suffix": "",\n' + ' "pipe_to": "",\n' + ' "output": "",\n' + ' "output_to": ""\n' + ' }\n' + ' },\n' + ' {\n' + ' "statement": {\n' + ' "args": "",\n' + ' "raw": "fourth",\n' + ' "command": "",\n' + ' "arg_list": [],\n' + ' "multiline_command": "",\n' + ' "terminator": "",\n' + ' "suffix": "",\n' + ' "pipe_to": "",\n' + ' "output": "",\n' + ' "output_to": ""\n' + ' }\n' + ' }\n' + ' ]\n' + '}' +) + + @pytest.fixture def persisted_hist(): from cmd2.cmd2 import ( @@ -256,6 +322,37 @@ assert hist.get(2).statement.raw == 'fourth' +def test_history_to_json(hist): + assert hist_json == hist.to_json() + + +def test_history_from_json(hist): + import json + + from cmd2.history import ( + History, + ) + + assert hist.from_json(hist_json) == hist + + # Test invalid JSON + with pytest.raises(json.JSONDecodeError): + hist.from_json("") + + # Send JSON with missing required element + with pytest.raises(KeyError): + hist.from_json("{}") + + # Create JSON with invalid history version + backed_up_ver = History._history_version + History._history_version = "BAD_VERSION" + invalid_ver_json = hist.to_json() + History._history_version = backed_up_ver + + with pytest.raises(ValueError): + hist.from_json(invalid_ver_json) + + # # test HistoryItem() # @@ -704,7 +801,7 @@ # @pytest.fixture(scope="session") def hist_file(): - fd, filename = tempfile.mkstemp(prefix='hist_file', suffix='.txt') + fd, filename = tempfile.mkstemp(prefix='hist_file', suffix='.dat') os.close(fd) yield filename # teardown code @@ -764,31 +861,6 @@ assert 'Cannot read' in err -def test_history_file_conversion_no_truncate_on_init(hist_file, capsys): - # make sure we don't truncate the plain text history file on init - # it shouldn't get converted to pickle format until we save history - - # first we need some plain text commands in the history file - with open(hist_file, 'w') as hfobj: - hfobj.write('help\n') - hfobj.write('alias\n') - hfobj.write('alias create s shortcuts\n') - - # Create a new cmd2 app - cmd2.Cmd(persistent_history_file=hist_file) - - # history should be initialized, but the file on disk should - # still be plain text - with open(hist_file, 'r') as hfobj: - histlist = hfobj.readlines() - - assert len(histlist) == 3 - # history.get() is overridden to be one based, not zero based - assert histlist[0] == 'help\n' - assert histlist[1] == 'alias\n' - assert histlist[2] == 'alias create s shortcuts\n' - - def test_history_populates_readline(hist_file): # - create a cmd2 with persistent history app = cmd2.Cmd(persistent_history_file=hist_file) @@ -822,7 +894,7 @@ # # test cmd2's ability to write out history on exit -# we are testing the _persist_history_on_exit() method, and +# we are testing the _persist_history() method, and # we assume that the atexit module will call this method # properly # diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmd2-2.0.1/tests/test_parsing.py new/cmd2-2.1.0/tests/test_parsing.py --- old/cmd2-2.0.1/tests/test_parsing.py 2021-04-06 04:23:51.000000000 +0200 +++ new/cmd2-2.1.0/tests/test_parsing.py 2021-06-14 17:29:07.000000000 +0200 @@ -13,6 +13,7 @@ utils, ) from cmd2.parsing import ( + Statement, StatementParser, shlex_split, ) @@ -944,6 +945,26 @@ statement.raw = 'baz' +def test_statement_as_dict(parser): + # Make sure to_dict() results can be restored to identical Statement + statement = parser.parse("!ls > out.txt") + assert statement == Statement.from_dict(statement.to_dict()) + + statement = parser.parse("!ls | grep text") + assert statement == Statement.from_dict(statement.to_dict()) + + statement = parser.parse("multiline arg; suffix") + assert statement == Statement.from_dict(statement.to_dict()) + + # from_dict() should raise KeyError if required field is missing + statement = parser.parse("command") + statement_dict = statement.to_dict() + del statement_dict[Statement._args_field] + + with pytest.raises(KeyError): + Statement.from_dict(statement_dict) + + def test_is_valid_command_invalid(mocker, parser): # Non-string command # noinspection PyTypeChecker
