From 87bf177e5c82e0f3c704f7d6fa60a6462d6a75a9 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 19:42:37 -0700 Subject: [PATCH 01/24] =?UTF-8?q?Bump=20version:=201.2.0=20=E2=86=92=201.2?= =?UTF-8?q?.1-dev?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pvl/__init__.py | 2 +- setup.cfg | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pvl/__init__.py b/pvl/__init__.py index 8189795..bb2847b 100755 --- a/pvl/__init__.py +++ b/pvl/__init__.py @@ -24,7 +24,7 @@ __author__ = "The pvl Developers" __email__ = "rbeyer@rossbeyer.net" -__version__ = "1.2.0" +__version__ = "1.2.1-dev" __all__ = [ "load", "loads", diff --git a/setup.cfg b/setup.cfg index 5bae844..5a85ffe 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.2.0 +current_version = 1.2.1-dev commit = False tag = False parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\-(?P<release>[a-z]+))? diff --git a/setup.py b/setup.py index db7249d..20c1818 100755 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name='pvl', - version='1.2.0', + version='1.2.1-dev', description=( 'Python implementation for PVL (Parameter Value Language) ' 'parsing and encoding.' From 9fe1b32613dd4dfbbc492e3b392bbc96072b35c7 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Thu, 27 May 2021 10:15:31 -0700 Subject: [PATCH 02/24] fix(new.py): Directly raise an ImportError if new.py is attempted to be imported by a user without multidict being installed. --- HISTORY.rst | 5 +++++ Makefile | 3 +++ pvl/new.py | 12 ++++++++++++ 3 files changed, 20 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index fd64b0c..87568ef 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -30,6 +30,11 @@ and the release date, in year-month-day format (see examples below). Unreleased ---------- +Fixed ++++++ +* Attempting to import pvl/new.py without multidict being available, + will now properly yield an ImportError. + 1.2.0 (2021-03-27) ------------------ diff --git a/Makefile b/Makefile index 14251fe..abef2e8 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,9 @@ lint: test: python -m pytest --doctest-modules --doctest-glob='*.rst' +test-min: + python -m pytest --doctest-modules --ignore=pvl/new.py + test-all: tox diff --git a/pvl/new.py b/pvl/new.py index 6244eed..338f9dc 100755 --- a/pvl/new.py +++ b/pvl/new.py @@ -26,6 +26,18 @@ import inspect import urllib.request +try: # noqa: C901 + # In order to access super class attributes for our derived class, we must + # import the native Python version, instead of the default Cython version. + from multidict._multidict_py import MultiDict +except ImportError as err: + raise ImportError( + "The multidict library is not present, so the new PVLMultiDict is not " + "available, and pvl.new can't be imported. In order to do so, install " + "the multidict package", + ImportWarning, + ) from err + from pvl import * # noqa: F401,F403 from pvl import get_text_from, decode_by_char From fe3d4452332d72e4527d5fdb5e471a881c7b7916 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 14:39:08 -0700 Subject: [PATCH 03/24] fix(encoder): Descendent encoders did not have group_class and object_class arguments to their constructors. --- pvl/encoder.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pvl/encoder.py b/pvl/encoder.py index 4c8a916..2a77ac3 100644 --- a/pvl/encoder.py +++ b/pvl/encoder.py @@ -524,6 +524,8 @@ def __init__( aggregation_end=True, end_delimiter=False, newline="\r\n", + group_class=PVLGroup, + object_class=PVLObject ): if grammar is None: @@ -546,6 +548,8 @@ def __init__( aggregation_end, end_delimiter, newline, + group_class=group_class, + object_class=object_class ) def encode(self, module: abc.Mapping) -> str: @@ -862,6 +866,8 @@ def __init__( indent=2, width=80, aggregation_end=True, + group_class=PVLGroup, + object_class=PVLObject, convert_group_to_object=True, tab_replace=4, symbol_single_quote=True, @@ -882,6 +888,8 @@ def __init__( aggregation_end, end_delimiter=False, newline="\r\n", + group_class=group_class, + object_class=object_class ) self.convert_group_to_object = convert_group_to_object @@ -1147,6 +1155,8 @@ def __init__( aggregation_end=True, end_delimiter=False, newline="\n", + group_class=PVLGroup, + object_class=PVLObject ): if grammar is None: @@ -1163,4 +1173,6 @@ def __init__( aggregation_end, end_delimiter, newline, + group_class=group_class, + object_class=object_class ) From f6be449cbc52b524f0c1c9622fa30dc345ae73ac Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 14:40:00 -0700 Subject: [PATCH 04/24] fix(new): Originally didn't think we needed to override dump() and dumps() here, but we do. --- pvl/new.py | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/pvl/new.py b/pvl/new.py index 338f9dc..b181b28 100755 --- a/pvl/new.py +++ b/pvl/new.py @@ -24,7 +24,9 @@ # top level of this library. import inspect +import io import urllib.request +from pathlib import Path try: # noqa: C901 # In order to access super class attributes for our derived class, we must @@ -41,6 +43,7 @@ from pvl import * # noqa: F401,F403 from pvl import get_text_from, decode_by_char +from .encoder import PDSLabelEncoder, PVLEncoder from .parser import PVLParser, OmniParser from .collections import PVLModuleNew, PVLGroupNew, PVLObjectNew @@ -144,3 +147,63 @@ def loads(s: str, parser=None, grammar=None, decoder=None, **kwargs): raise TypeError("The parser must be an instance of pvl.PVLParser.") return parser.parse(s) + + +def dump(module, path, **kwargs): + """Serialize *module* as PVL text to the provided *path*. + + :param module: a ``PVLModule`` or ``dict``-like object to serialize. + :param path: an :class:`os.PathLike` + :param ``**kwargs``: the keyword arguments to pass to :func:`dumps()`. + + If *path* is an :class:`os.PathLike`, it will attempt to be opened + and the serialized module will be written into that file via + the :func:`pathlib.Path.write_text()` function, and will return + what that function returns. + + If *path* is not an :class:`os.PathLike`, it will be assumed to be an + already-opened file object, and ``.write()`` will be applied + on that object to write the serialized module, and will return + what that function returns. + """ + try: + p = Path(path) + return p.write_text(dumps(module, **kwargs)) + + except TypeError: + # Not an os.PathLike, maybe it is an already-opened file object + try: + if isinstance(path, io.TextIOBase): + return path.write(dumps(module, **kwargs)) + else: + return path.write(dumps(module, **kwargs).encode()) + except AttributeError: + # Not a path, not an already-opened file. + raise TypeError( + "Expected an os.PathLike or an already-opened " + "file object for writing, but got neither." + ) + + +def dumps(module, encoder=None, grammar=None, decoder=None, **kwargs) -> str: + """Returns a string where the *module* object has been serialized + to PVL syntax. + + :param module: a ``PVLModule`` or ``dict`` like object to serialize. + :param encoder: defaults to :class:`pvl.parser.PDSLabelEncoder()`. + :param grammar: defaults to :class:`pvl.grammar.ODLGrammar()`. + :param decoder: defaults to :class:`pvl.decoder.ODLDecoder()`. + :param ``**kwargs``: the keyword arguments to pass to the encoder + class if *encoder* is none. + """ + if encoder is None: + encoder = PDSLabelEncoder( + grammar=grammar, + decoder=decoder, + group_class=PVLGroupNew, + object_class=PVLObjectNew, + **kwargs) + elif not isinstance(encoder, PVLEncoder): + raise TypeError("The encoder must be an instance of pvl.PVLEncoder.") + + return encoder.encode(module) From 338c99a18cea3432e716141bfdc9617e1f162503 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 15:07:47 -0700 Subject: [PATCH 05/24] test(new): Created tests. --- tests/test_new.py | 249 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 tests/test_new.py diff --git a/tests/test_new.py b/tests/test_new.py new file mode 100644 index 0000000..99d20d6 --- /dev/null +++ b/tests/test_new.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python +"""This module has unit tests for the pvl __init__ functions.""" + +# Copyright 2019, Ross A. Beyer (rbeyer@seti.org) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from unittest.mock import call, create_autospec, mock_open, patch + +from pathlib import Path + +import pvl +import pvl.new as pvln +from pvl.collections import PVLModuleNew as PVLModule +from pvl.collections import PVLGroupNew as PVLGroup +from pvl.collections import PVLObjectNew as PVLObject + +data_dir = Path("tests/data") + + +class TestLoadS(unittest.TestCase): + def test_loads(self): + some_pvl = """ +a = b +GROUP = c + c = d +END_GROUP +e =false +END""" + decoded = PVLModule(a="b", c=PVLGroup(c="d"), e=False) + self.assertEqual(decoded, pvln.loads(some_pvl)) + + self.assertEqual(PVLModule(a="b"), pvln.loads("a=b")) + + +class TestLoad(unittest.TestCase): + def setUp(self): + self.simple = data_dir / "pds3" / "simple_image_1.lbl" + rawurl = "https://raw.githubusercontent.com/planetarypy/pvl/main/" + self.url = rawurl + str(self.simple) + self.simplePVL = PVLModule( + { + "PDS_VERSION_ID": "PDS3", + "RECORD_TYPE": "FIXED_LENGTH", + "RECORD_BYTES": 824, + "LABEL_RECORDS": 1, + "FILE_RECORDS": 601, + "^IMAGE": 2, + "IMAGE": PVLObject( + { + "LINES": 600, + "LINE_SAMPLES": 824, + "SAMPLE_TYPE": "MSB_INTEGER", + "SAMPLE_BITS": 8, + "MEAN": 51.67785396440129, + "MEDIAN": 50.0, + "MINIMUM": 0, + "MAXIMUM": 255, + "STANDARD_DEVIATION": 16.97019, + "CHECKSUM": 25549531, + } + ), + } + ) + + def test_load_w_open(self): + with open(self.simple) as f: + self.assertEqual(self.simplePVL, pvln.load(f)) + + def test_load_w_Path(self): + self.assertEqual(self.simplePVL, pvln.load(self.simple)) + + def test_load_w_string_path(self): + string_path = str(self.simple) + self.assertEqual(self.simplePVL, pvln.load(string_path)) + + def test_loadu(self): + self.assertEqual(self.simplePVL, pvln.loadu(self.url)) + self.assertEqual( + self.simplePVL, pvln.loadu(self.simple.resolve().as_uri()) + ) + + @patch("pvl.new.loads") + @patch("pvl.new.decode_by_char") + def test_loadu_args(self, m_decode, m_loads): + pvln.loadu(self.url, data=None) + pvln.loadu(self.url, noturlopen="should be passed to loads") + m_decode.assert_called() + self.assertNotIn("data", m_loads.call_args_list[0][1]) + self.assertIn("noturlopen", m_loads.call_args_list[1][1]) + + def test_load_w_quantity(self): + try: + from astropy import units as u + from pvl.decoder import OmniDecoder + + pvl_file = "tests/data/pds3/units1.lbl" + km_upper = u.def_unit("KM", u.km) + m_upper = u.def_unit("M", u.m) + u.add_enabled_units([km_upper, m_upper]) + label = pvln.load( + pvl_file, decoder=OmniDecoder(quantity_cls=u.Quantity) + ) + self.assertEqual(label["FLOAT_UNIT"], u.Quantity(0.414, "KM")) + except ImportError: + pass + + +class TestISIScub(unittest.TestCase): + def setUp(self): + self.cub = data_dir / "pattern.cub" + self.cubpvl = PVLModule( + IsisCube=PVLObject( + Core=PVLObject( + StartByte=65537, + Format="Tile", + TileSamples=128, + TileLines=128, + Dimensions=PVLGroup(Samples=90, Lines=90, Bands=1), + Pixels=PVLGroup( + Type="Real", ByteOrder="Lsb", Base=0.0, Multiplier=1.0 + ), + ) + ), + Label=PVLObject(Bytes=65536), + ) + + def test_load_cub(self): + self.assertEqual(self.cubpvl, pvln.load(self.cub)) + + def test_load_cub_opened(self): + with open(self.cub, "rb") as f: + self.assertEqual(self.cubpvl, pvln.load(f)) + + +class TestDumpS(unittest.TestCase): + def setUp(self): + self.module = PVLModule( + a="b", + staygroup=PVLGroup(c="d"), + obj=PVLGroup(d="e", f=PVLGroup(g="h")), + ) + + def test_dumps_PDS(self): + s = """A = b\r +GROUP = staygroup\r + C = d\r +END_GROUP = staygroup\r +OBJECT = obj\r + D = e\r + GROUP = f\r + G = h\r + END_GROUP = f\r +END_OBJECT = obj\r +END\r\n""" + self.assertEqual(s, pvln.dumps(self.module)) + + def test_dumps_PVL(self): + s = """a = b; +BEGIN_GROUP = staygroup; + c = d; +END_GROUP = staygroup; +BEGIN_GROUP = obj; + d = e; + BEGIN_GROUP = f; + g = h; + END_GROUP = f; +END_GROUP = obj; +END;""" + + self.assertEqual( + s, pvln.dumps( + self.module, + encoder=pvl.encoder.PVLEncoder( + group_class=PVLGroup, object_class=PVLObject + ) + ) + ) + + def test_dumps_ODL(self): + + s = """A = b\r +GROUP = staygroup\r + C = d\r +END_GROUP = staygroup\r +GROUP = obj\r + D = e\r + GROUP = f\r + G = h\r + END_GROUP = f\r +END_GROUP = obj\r +END\r\n""" + + self.assertEqual( + s, pvln.dumps(self.module, encoder=pvl.encoder.ODLEncoder( + group_class=PVLGroup, object_class=PVLObject + )) + ) + + +class TestDump(unittest.TestCase): + def setUp(self): + self.module = PVLModule( + a="b", + staygroup=PVLGroup(c="d"), + obj=PVLGroup(d="e", f=PVLGroup(g="h")), + ) + self.string = """A = b\r +GROUP = staygroup\r + C = d\r +END_GROUP = staygroup\r +OBJECT = obj\r + D = e\r + GROUP = f\r + G = h\r + END_GROUP = f\r +END_OBJECT = obj\r +END\r\n""" + + def test_dump_Path(self): + mock_path = create_autospec(Path) + with patch("pvl.new.Path", autospec=True, return_value=mock_path): + pvln.dump(self.module, Path("dummy")) + self.assertEqual( + [call.write_text(self.string)], mock_path.method_calls + ) + + @patch("builtins.open", mock_open()) + def test_dump_file_object(self): + with open("dummy", "w") as f: + pvln.dump(self.module, f) + self.assertEqual( + [call.write(self.string.encode())], f.method_calls + ) + + def test_not_dumpable(self): + f = 5 + self.assertRaises(TypeError, pvln.dump, self.module, f) From 1910b432fe51e6d3f826ad4baa159e58bc49dd52 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 10:45:17 -0700 Subject: [PATCH 06/24] test(pvl_translate): Created tests. --- pvl/pvl_translate.py | 8 ++---- tests/test_pvl_translate.py | 51 +++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 tests/test_pvl_translate.py diff --git a/pvl/pvl_translate.py b/pvl/pvl_translate.py index 9934625..ba55a9a 100644 --- a/pvl/pvl_translate.py +++ b/pvl/pvl_translate.py @@ -80,14 +80,10 @@ def arg_parser(formats): return parser -def main(): - args = arg_parser(formats).parse_args() +def main(argv=None): + args = arg_parser(formats).parse_args(argv) some_pvl = pvl.load(args.infile) formats[args.output_format].dump(some_pvl, args.outfile) return - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/tests/test_pvl_translate.py b/tests/test_pvl_translate.py new file mode 100644 index 0000000..a91417f --- /dev/null +++ b/tests/test_pvl_translate.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +"""This module has tests for the pvl_translate functions.""" + +# Copyright 2021, ``pvl`` library authors. +# +# Reuse is permitted under the terms of the license. +# The AUTHORS file and the LICENSE file are at the +# top level of this library. + +import argparse +import unittest +from unittest.mock import patch, PropertyMock, MagicMock + +import pvl.pvl_translate as pvl_trans +from pvl.encoder import PDSLabelEncoder + + +class TestMock(unittest.TestCase): + def test_Writer(self): + w = pvl_trans.Writer() + self.assertRaises(Exception, w.dump, dict(a="b"), "dummy.txt") + + @patch("pvl.dump") + def test_PVLWriter(self, m_dump): + e = PDSLabelEncoder() + w = pvl_trans.PVLWriter(e) + d = dict(a="b") + f = "dummy.pathlike" + w.dump(d, f) + m_dump.assert_called_once_with(d, f, encoder=e) + + @patch("json.dump") + def test_JSONWriter(self, m_dump): + w = pvl_trans.JSONWriter() + d = dict(a="b") + f = "dummy.pathlike" + w.dump(d, f) + m_dump.assert_called_once_with(d, f) + + def test_arg_parser(self): + p = pvl_trans.arg_parser(pvl_trans.formats) + self.assertIsInstance(p, argparse.ArgumentParser) + + @patch("pvl.pvl_translate.JSONWriter") + @patch("pvl.pvl_translate.PVLWriter") + @patch("pvl.pvl_translate.arg_parser") + @patch("pvl.load") + def test_main(self, m_load, m_parser, m_PVLWriter, m_JSONWriter): + m_parser().parse_args().output_format = "PDS3" + self.assertIsNone(pvl_trans.main()) + From c31011d4d1a526b1cab854f76941b508c3fc7080 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 13:46:37 -0700 Subject: [PATCH 07/24] test(pvl_validate): Created tests. --- pvl/pvl_validate.py | 8 +-- tests/test_pvl_validate.py | 112 +++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 tests/test_pvl_validate.py diff --git a/pvl/pvl_validate.py b/pvl/pvl_validate.py index 0c74c8d..a576d97 100644 --- a/pvl/pvl_validate.py +++ b/pvl/pvl_validate.py @@ -102,8 +102,8 @@ def arg_parser(): return p -def main(): - args = arg_parser().parse_args() +def main(argv=None): + args = arg_parser().parse_args(argv) logging.basicConfig( format="%(levelname)s: %(message)s", level=(60 - 20 * args.verbose) @@ -252,7 +252,3 @@ def build_line(elements: list, widths: list, sep=" | ") -> str: cells.append("{0:^{width}}".format(e, width=w)) return sep.join(cells) - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/tests/test_pvl_validate.py b/tests/test_pvl_validate.py new file mode 100644 index 0000000..598ec70 --- /dev/null +++ b/tests/test_pvl_validate.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +"""This module has tests for the pvl_validate functions.""" + +# Copyright 2021, ``pvl`` library authors. +# +# Reuse is permitted under the terms of the license. +# The AUTHORS file and the LICENSE file are at the +# top level of this library. + +import argparse +import unittest +from unittest.mock import patch + +import pvl.pvl_validate as pvl_val + + +class TestMock(unittest.TestCase): + def setUp(self): + self.flavors = ["chocolate", "vanilla"] + self.report1 = ( + "choc.txt", {"chocolate": (True, True), "vanilla": (True, False)} + ) + self.report2 = ( + "van.txt", {"chocolate": (False, False), "vanilla": (True, True)} + ) + + def test_arg_parser(self): + p = pvl_val.arg_parser() + self.assertIsInstance(p, argparse.ArgumentParser) + + @patch("pvl.get_text_from", return_value="a=b") + def test_main(self, m_get): + self.assertIsNone(pvl_val.main("dummy.txt")) + + self.assertIsNone(pvl_val.main(["-v", "dummy.txt"])) + + def test_pvl_flavor(self): + dialect = "PDS3" + loads, encodes = pvl_val.pvl_flavor( + "a = b", dialect, pvl_val.dialects[dialect], "dummy.txt" + ) + self.assertEqual(True, loads) + self.assertEqual(True, encodes) + + loads, encodes = pvl_val.pvl_flavor( + "foo", dialect, pvl_val.dialects[dialect], "dummy.txt" + ) + self.assertEqual(False, loads) + self.assertEqual(None, encodes) + + loads, encodes = pvl_val.pvl_flavor( + "set_with_float = {1.5}", + dialect, + pvl_val.dialects[dialect], + "dummy.txt" + ) + self.assertEqual(True, loads) + self.assertEqual(False, encodes) + + with patch("pvl.pvl_validate.pvl.loads", side_effect=Exception("bogus")): + loads, encodes = pvl_val.pvl_flavor( + "a=b", dialect, pvl_val.dialects[dialect], "dummy.txt" + ) + loads, encodes = pvl_val.pvl_flavor( + "a=b", + dialect, + pvl_val.dialects[dialect], + "dummy.txt", + verbose=2 + ) + self.assertEqual(False, loads) + self.assertEqual(None, encodes) + + def test_report(self): + self.assertRaises( + IndexError, + pvl_val.report, + [["report", ], ], + self.flavors + ) + + with patch("pvl.pvl_validate.report_many", return_value="many"): + self.assertEqual( + "many", + pvl_val.report([self.report1, self.report2], self.flavors) + ) + + self.assertEqual( + """\ +chocolate | Loads | Encodes +vanilla | Loads | does NOT encode""", + pvl_val.report([self.report1, ], self.flavors) + ) + + def test_report_many(self): + self.assertEqual( + """\ +---------+-----------+---------- +File | chocolate | vanilla +---------+-----------+---------- +choc.txt | L E | L No E +van.txt | No L No E | L E """, + pvl_val.report_many( + [self.report1, self.report2], self.flavors + ) + ) + + def test_build_line(self): + self.assertEqual( + "a | b ", + pvl_val.build_line(['a', 'b'], [3, 4]) + ) From 6b1058c5148b6a7b308f8881aa7712c2eac2e727 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 15:24:08 -0700 Subject: [PATCH 08/24] test(exceptions): Created tests. --- tests/test_exceptions.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/test_exceptions.py diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py new file mode 100644 index 0000000..f6ec349 --- /dev/null +++ b/tests/test_exceptions.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +"""This module has tests for the pvl.exceptions functions.""" + +# Copyright 2021, ``pvl`` library authors. +# +# Reuse is permitted under the terms of the license. +# The AUTHORS file and the LICENSE file are at the +# top level of this library. + +import unittest + +import pvl.exceptions + + +class TestMock(unittest.TestCase): + + def test_LexerError(self): + e = pvl.exceptions.LexerError("lex error", "This is the document.", 2, "Th") + self.assertEqual( + ( + pvl.exceptions.LexerError, + ("lex error", "This is the document.", 1, "Th") + ), + e.__reduce__() + ) From d9cab20afb3d9a637f177c7d209283fbb1f835de Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 15:30:23 -0700 Subject: [PATCH 09/24] test(lexer): Added a test. --- tests/test_lexer.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_lexer.py b/tests/test_lexer.py index f79559d..8312f41 100644 --- a/tests/test_lexer.py +++ b/tests/test_lexer.py @@ -332,6 +332,22 @@ def test_lex_char(self): ), ) + self.assertRaises( + ValueError, + Lexer.lex_char, + "a", + "b", + "c", + "", + dict(state="bogus preserve state", end="end"), + g, + dict( + chars={"k", "v", "/", "*"}, + single_comments={"k": "v"}, + multi_chars={"/", "*"}, + ), + ) + def test_lexer_recurse(self): def foo(tokens): two = list() From 4a340ab618629fd350b20d14e8b1c7c9729a87a8 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 15:49:29 -0700 Subject: [PATCH 10/24] refactor(grammar): Properly called super class, and provided better exception and error message. --- pvl/grammar.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pvl/grammar.py b/pvl/grammar.py index 7c68521..40f653f 100755 --- a/pvl/grammar.py +++ b/pvl/grammar.py @@ -154,7 +154,10 @@ def char_allowed(self, char): set with some exclusions. """ if len(char) != 1: - raise Exception + raise ValueError( + f"This function only takes single characters and it was given " + f"{len(char)} ('{char}')." + ) o = ord(char) @@ -207,8 +210,7 @@ def char_allowed(self, char): characters than PVL, but appears to allow more control characters to be in quoted strings than PVL does. """ - if len(char) != 1: - raise Exception + super().char_allowed(char) try: char.encode(encoding="ascii") From 822653aa6c7fa997753a6cbcb84054c7b47d17f1 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 15:49:48 -0700 Subject: [PATCH 11/24] test(grammar): Added tests. --- tests/test_grammar.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/test_grammar.py b/tests/test_grammar.py index e9cc98c..c0c40d8 100644 --- a/tests/test_grammar.py +++ b/tests/test_grammar.py @@ -18,7 +18,7 @@ import re import unittest -from pvl.grammar import PVLGrammar +from pvl.grammar import PVLGrammar, ODLGrammar class TestLeapSeconds(unittest.TestCase): @@ -124,11 +124,20 @@ def test_leap_second_Yj_re(self): with self.subTest(string=s): self.assertIsNotNone(self.g.leap_second_Yj_re.fullmatch(s)) + +class TestAllowed(unittest.TestCase): + def test_allowed(self): + g = PVLGrammar() for c in ("a", "b", " ", "\n"): with self.subTest(char=c): - self.assertTrue(self.g.char_allowed(c)) + self.assertTrue(g.char_allowed(c)) for c in ("\b", chr(127)): with self.subTest(char=c): - self.assertFalse(self.g.char_allowed(c)) + self.assertFalse(g.char_allowed(c)) + + self.assertRaises(ValueError, g.char_allowed, "too long") + + odlg = ODLGrammar() + self.assertFalse(odlg.char_allowed("😆")) From aa678b298979070dc5feaa95e120171d4a267b87 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 17:20:50 -0700 Subject: [PATCH 12/24] fix(collections): Needed to wrap a view in a tuple() constructor, and otherwise eliminated some un-called code. --- pvl/collections.py | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/pvl/collections.py b/pvl/collections.py index c5f8c95..5679915 100644 --- a/pvl/collections.py +++ b/pvl/collections.py @@ -18,7 +18,7 @@ is no fundamental Python type for a quantity, so we define the Quantity class (formerly the Units class). """ -# Copyright 2015, 2017, 2019-2020, ``pvl`` library authors. +# Copyright 2015, 2017, 2019-2021, ``pvl`` library authors. # # Reuse is permitted under the terms of the license. # The AUTHORS file and the LICENSE file are at the @@ -268,9 +268,6 @@ def extend(self, *args, **kwargs): if isinstance(iterable, abc.Mapping) or hasattr(iterable, "items"): for key, value in iterable.items(): self.append(key, value) - elif hasattr(iterable, "keys"): - for key in iterable.keys(): - self.append(key, iterable[key]) else: for key, value in iterable: self.append(key, value) @@ -393,20 +390,6 @@ def insert(self, index: int, *args) -> None: return - def __insert_wrapper(func): - """Make sure the arguments given to the insert methods are correct.""" - - def check_func(self, key, new_item, instance=0): - if key not in self.keys(): - raise KeyError(f"{key} not a key in label") - if not isinstance(new_item, (list, OrderedMultiDict)): - raise TypeError("The new item must be a list or PVLModule") - if isinstance(new_item, OrderedMultiDict): - new_item = list(new_item) - return func(self, key, new_item, instance) - - return check_func - def key_index(self, key, instance: int = 0) -> int: """Get the index of the key to insert before or after.""" if key not in self: @@ -586,7 +569,7 @@ def _insert_item( index = index + 1 if is_after else index if isinstance(new_item, abc.Mapping): - tuple_iter = new_item.items() + tuple_iter = tuple(new_item.items()) else: tuple_iter = new_item self.insert(index, tuple_iter) From 68a3283147a0e0cda2ecd83b9d401233e3dbb8f1 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 17:21:18 -0700 Subject: [PATCH 13/24] test(collections): Added tests. --- tests/test_collections.py | 121 +++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/tests/test_collections.py b/tests/test_collections.py index dc916fd..61b5126 100644 --- a/tests/test_collections.py +++ b/tests/test_collections.py @@ -6,12 +6,18 @@ # Reuse is permitted under the terms of the license. # The AUTHORS file and the LICENSE file are at the # top level of this library. - +from abc import ABC from collections import abc import unittest import pvl -from pvl.collections import OrderedMultiDict +from pvl.collections import ( + KeysView, + MappingView, + MutableMappingSequence, + OrderedMultiDict, + ValuesView +) class DictLike(abc.Mapping): @@ -27,6 +33,54 @@ def __iter__(self): def __len__(self): return len(self.list) + def __delitem__(self, key): + pass + + def __setitem__(self, key, value): + pass + + def insert(self): + pass + + +class TestClasses(unittest.TestCase): + def test_MutableMappingSequence(self): + class Concrete(DictLike, MutableMappingSequence, ABC): + def append(self, key, value): + super().append(key, value) + + def getall(self, key): + super().getall(key) + + def popall(self, key): + super().popall(key) + + mms = Concrete() + mms.append("key", "value") + mms.getall("key") + mms.popall("key") + + def test_MappingView(self): + m = MappingView([("a", 1), ("b", 2)]) + self.assertEqual( + "MappingView([('a', 1), ('b', 2)])", + repr(m) + ) + + def test_KeysView(self): + k = KeysView([("a", 1), ("b", 2)]) + self.assertEqual( + "KeysView(['a', 'b'])", + repr(k) + ) + + def test_ValuesView(self): + v = ValuesView([("a", 1), ("b", 2)]) + self.assertEqual( + "ValuesView([1, 2])", + repr(v) + ) + class TestMultiDicts(unittest.TestCase): def setUp(self): @@ -493,6 +547,17 @@ def test_insert_before_after_raises(self): TypeError, module.insert_after, "a", [("fo", "ba"), 2] ) + def test_repr(self): + module = OrderedMultiDict([("a", 1), ("b", 2), ("a", 3)]) + self.assertEqual( + """OrderedMultiDict([ + ('a', 1) + ('b', 2) + ('a', 3) +])""", + repr(module) + ) + class TestDifferences(unittest.TestCase): def test_as_list(self): @@ -764,3 +829,55 @@ def test_equality(self): self.assertEqual(newmod, newobj) except ImportError: pass + + +class TestMultiDict(unittest.TestCase): + + def test_repr(self): + try: + from pvl.collections import PVLMultiDict + the_list = [("a", 1), ("b", 2)] + m = PVLMultiDict(the_list) + self.assertEqual( + "PVLMultiDict([('a', 1), ('b', 2)])", + repr(m) + ) + + except ImportError: + pass + + def test_str(self): + try: + from pvl.collections import PVLMultiDict + the_list = [("a", 1), ("b", 2)] + m = PVLMultiDict(the_list) + self.assertEqual( + """PVLMultiDict([ + ('a', 1) + ('b', 2) +])""", + str(m) + ) + + z = PVLMultiDict() + self.assertEqual( + "PVLMultiDict()", + str(z) + ) + + except ImportError: + pass + + def test_insert(self): + try: + from pvl.collections import PVLMultiDict + the_list = [("a", 1), ("b", 2)] + m = PVLMultiDict(the_list) + m.insert_after("a", {"z": 10, "y": 9}) + self.assertEqual( + PVLMultiDict([("a", 1), ("z", 10), ("y", 9), ("b", 2)]), + m + ) + + except ImportError: + pass \ No newline at end of file From 58009022c1a6249931275a43b74cbf9978ae0ec5 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 18:16:14 -0700 Subject: [PATCH 14/24] fix(token): Implementation bugs in __index__() fixed, __float__() actually returns floats instead of sometimes ints, and some documentation fixed. --- pvl/token.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/pvl/token.py b/pvl/token.py index 21d0271..2f966b9 100644 --- a/pvl/token.py +++ b/pvl/token.py @@ -54,10 +54,17 @@ def __repr__(self): return f"{self.__class__.__name__}('{self}', " f"'{self.grammar}')" def __index__(self): - return self.decoder.decode_non_decimal(str(self)) + if self.is_decimal(): + try: + return self.decoder.decode_non_decimal(str(self)) + except ValueError: + if int(self) == float(self): + return int(self) + + raise ValueError(f"The {self:r} cannot be used as an index.") def __float__(self): - return self.decoder.decode_decimal(str(self)) + return float(self.decoder.decode_decimal(str(self))) def split(self, sep=None, maxsplit=-1) -> list: """Extends ``str.split()`` that calling split() on a Token @@ -151,9 +158,8 @@ def is_comment(self) -> bool: return False def is_quote(self) -> bool: - """Return true if the Token is a comment character (or - multicharacter comment delimiter) according to the - Token's grammar, false otherwise. + """Return true if the Token is a quote character + according to the Token's grammar, false otherwise. """ if self in self.grammar.quotes: return True From 8cb9f34443ba5e9bdec6d8778ec3177be411972e Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 18:16:38 -0700 Subject: [PATCH 15/24] test(token): Added tests. --- tests/test_token.py | 49 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/tests/test_token.py b/tests/test_token.py index ea5bb3e..02a771d 100644 --- a/tests/test_token.py +++ b/tests/test_token.py @@ -18,6 +18,7 @@ import unittest from pvl.grammar import PVLGrammar +from pvl.decoder import PVLDecoder from pvl.token import Token @@ -26,6 +27,11 @@ def test_init(self): s = "token" self.assertEqual(s, Token(s)) self.assertEqual(s, Token(s, grammar=PVLGrammar())) + self.assertEqual(s, Token(s, decoder=PVLDecoder())) + self.assertRaises(TypeError, Token, s, grammar="not a grammar") + self.assertRaises( + TypeError, Token, s, grammar=PVLGrammar(), decoder="not a decoder" + ) def test_is_comment(self): c = Token("/* comment */") @@ -178,6 +184,7 @@ def test_is_space(self): with self.subTest(string=s): t = Token(s) self.assertTrue(t.is_space()) + self.assertTrue(t.isspace()) for s in ("not space", ""): with self.subTest(string=s): @@ -185,7 +192,7 @@ def test_is_space(self): self.assertFalse(t.is_space()) def test_is_WSC(self): - for s in (" /*com*/ ", "/*c1*/\n/*c2*/"): + for s in (" /*com*/ ", "/*c1*/\n/*c2*/", " "): with self.subTest(string=s): t = Token(s) self.assertTrue(t.is_WSC()) @@ -202,13 +209,28 @@ def test_is_delimiter(self): t = Token("not") self.assertFalse(t.is_delimiter()) + def test_is_quote(self): + for s in ('"', "'"): + with self.subTest(string=s): + t = Token(s) + self.assertTrue(t.is_quote()) + + t = Token("not a quote mark") + self.assertFalse(t.is_quote()) + def test_is_unquoted_string(self): for s in ("Hello", "Product", "Group"): with self.subTest(string=s): t = Token(s) self.assertTrue(t.is_unquoted_string()) - for s in ("/*comment*/", "2001-027", '"quoted"'): + for s in ( + "/*comment*/", + "second line of comment*/", + "2001-027", + '"quoted"', + "\t" + ): with self.subTest(string=s): t = Token(s) self.assertFalse(t.is_unquoted_string()) @@ -291,3 +313,26 @@ def test_split(self): for x in t_list: with self.subTest(token=x): self.assertIsInstance(x, Token) + + def test_index(self): + s = "3" + t = Token(s) + self.assertEqual(3, int(t)) + self.assertEqual(3, t.__index__()) + self.assertRaises(ValueError, Token("3.4").__index__) + self.assertRaises(ValueError, Token("a").__index__) + + def test_float(self): + s = "3.14" + t = Token(s) + self.assertEqual(3.14, float(t)) + + def test_lstrip(self): + s = " leftward space " + t = Token(s) + self.assertEqual("leftward space ", t.lstrip()) + + def test_rstrip(self): + s = " rightward space " + t = Token(s) + self.assertEqual(" rightward space", t.rstrip()) \ No newline at end of file From be4f0c0a2e75584bdbe075a447ba2728dd1c9a91 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 18:34:28 -0700 Subject: [PATCH 16/24] docs(various): Updated copyright statements to include 2021. --- docs/conf.py | 2 +- pvl/new.py | 2 +- pvl/pvl_translate.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 0d3aa35..a8f51f5 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -65,7 +65,7 @@ # General information about the project. project = u'pvl' -copyright = u'2015, 2017, 2019-2020, pvl Developers' +copyright = u'2015, 2017, 2019-2021, pvl Developers' # The version info for the project you're documenting, acts as replacement # for |version| and |release|, also used in various other places throughout diff --git a/pvl/new.py b/pvl/new.py index b181b28..8d46692 100755 --- a/pvl/new.py +++ b/pvl/new.py @@ -17,7 +17,7 @@ be the new PVLMultiDict objects. """ -# Copyright 2015, 2017, 2019-2020, ``pvl`` library authors. +# Copyright 2015, 2017, 2019-2021, ``pvl`` library authors. # # Reuse is permitted under the terms of the license. # The AUTHORS file and the LICENSE file are at the diff --git a/pvl/pvl_translate.py b/pvl/pvl_translate.py index ba55a9a..9a786ed 100644 --- a/pvl/pvl_translate.py +++ b/pvl/pvl_translate.py @@ -8,7 +8,7 @@ will raise errors. """ -# Copyright 2020, ``pvl`` library authors. +# Copyright 2020-2021, ``pvl`` library authors. # # Reuse is permitted under the terms of the license. # The AUTHORS file and the LICENSE file are at the From cc7aa53ab30322d6b678f8f7552b29d133a91b9c Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 18:35:33 -0700 Subject: [PATCH 17/24] docs(README): Updated into paragraph (PVL isn't *really* like XML), updated PlanetaryPy link and some alt text. --- README.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 31a6954..c3ef8a8 100644 --- a/README.rst +++ b/README.rst @@ -12,6 +12,7 @@ pvl .. image:: https://codecov.io/gh/planetarypy/pvl/branch/master/graph/badge.svg?token=uWqotcPTGR :target: https://codecov.io/gh/planetarypy/pvl + :alt: Codecov coverage .. image:: https://img.shields.io/pypi/v/pvl.svg?style=flat-square @@ -38,7 +39,7 @@ Python implementation of a PVL (Parameter Value Language) library. * Support for Python 3.6 and higher (avaiable via pypi and conda). * `PlanetaryPy`_ Affiliate Package. -PVL is a markup language, similar to XML, commonly employed for +PVL is a markup language, like JSON or YAML, commonly employed for entries in the Planetary Data System used by NASA to archive mission data, among other uses. This package supports both encoding and decoding a variety of PVL 'flavors' including PVL itself, ODL, @@ -214,7 +215,7 @@ Feedback, issues, and contributions are always gratefully welcomed. See the environment. -.. _PlanetaryPy: https://github.com/planetarypy +.. _PlanetaryPy: https://planetarypy.org .. _USGS ISIS Cube Labels: http://isis.astrogeology.usgs.gov/ .. _NASA PDS 3 Labels: https://pds.nasa.gov .. _image: https://github.com/planetarypy/pvl/raw/master/tests/data/pattern.cub From 26693974b0603150a9dcd5ae262cb943173a32c0 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 18:36:08 -0700 Subject: [PATCH 18/24] docs(HISTORY): Updated with relevant fixes and calling all this version 1.2.1 --- HISTORY.rst | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index 87568ef..0303c97 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -30,10 +30,29 @@ and the release date, in year-month-day format (see examples below). Unreleased ---------- +1.2.1 (2021-05-31) +------------------ + +Added ++++++ +* So many tests, increased coverage by about 10%. + Fixed +++++ -* Attempting to import pvl/new.py without multidict being available, +* Attempting to import `pvl.new` without *multidict* being available, will now properly yield an ImportError. +* The `dump()` and `dumps()` functions now properly overwritten in `pvl.new`. +* All encoders that descended from PVLEncoder didn't properly have group_class and + object_class arguments to their constructors, now they do. +* The `char_allowed()` function in grammar objects now raises a more useful ValueError + than just a generic Exception. +* The new `collections.PVLMultiDict` wasn't correctly inserting Mapping objects with + the `insert_before()` and `insert_after()` methods. +* The `token.Token` class's `__index__()` function didn't always properly return an + index. +* The `token.Token` class's `__float__()` function would return int objects if the + token could be converted to int. Now always returns floats. + 1.2.0 (2021-03-27) ------------------ From 70c453fb7898111d70da2eeb174d86e64db45d4d Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 18:37:13 -0700 Subject: [PATCH 19/24] fix(pvl_validate): Whoops, don't need to import sys anymore. --- pvl/pvl_validate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pvl/pvl_validate.py b/pvl/pvl_validate.py index a576d97..96b9cbb 100644 --- a/pvl/pvl_validate.py +++ b/pvl/pvl_validate.py @@ -20,7 +20,6 @@ import argparse import logging -import sys from collections import OrderedDict import pvl From 4a796541971a122d0f03ad3fd39583a7f46cae62 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 18:38:25 -0700 Subject: [PATCH 20/24] test(new): Eliminate lint error. --- pvl/new.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvl/new.py b/pvl/new.py index 8d46692..4d6ac9b 100755 --- a/pvl/new.py +++ b/pvl/new.py @@ -31,7 +31,7 @@ try: # noqa: C901 # In order to access super class attributes for our derived class, we must # import the native Python version, instead of the default Cython version. - from multidict._multidict_py import MultiDict + from multidict._multidict_py import MultiDict # noqa: F401 except ImportError as err: raise ImportError( "The multidict library is not present, so the new PVLMultiDict is not " From 3984697183b669ca596ad2ac3457d84c482441c4 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 19:23:41 -0700 Subject: [PATCH 21/24] ci(tox.ini & GitHub Workflows): Need to include multidict for testing otherwise get ImportError, even though not required for normal install. --- .github/workflows/code-cover.yml | 1 + .github/workflows/python-test.yml | 1 + tox.ini | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/workflows/code-cover.yml b/.github/workflows/code-cover.yml index de1bb4d..cf615b5 100644 --- a/.github/workflows/code-cover.yml +++ b/.github/workflows/code-cover.yml @@ -26,6 +26,7 @@ jobs: python -m pip install --upgrade pip python -m pip install pytest python -m pip install pytest-cov + python -m pip install multidict - name: Install pvl run: python -m pip install -e . - name: Test with pytest and generate coverage report diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index 9f41856..7b73005 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -30,6 +30,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install pytest flake8 + python -m pip install multidict - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names diff --git a/tox.ini b/tox.ini index 4f1b5a5..bade049 100644 --- a/tox.ini +++ b/tox.ini @@ -8,6 +8,7 @@ commands = pytest deps = pytest + multidict [testenv:py{36, 37, 38}-allopts] deps = From ba67e20e7ff0f74df2c66e1f948d6bb529f39228 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 19:34:08 -0700 Subject: [PATCH 22/24] test(pvl_translate): Fixed mocking for test. --- tests/test_pvl_translate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_pvl_translate.py b/tests/test_pvl_translate.py index a91417f..a450341 100644 --- a/tests/test_pvl_translate.py +++ b/tests/test_pvl_translate.py @@ -45,7 +45,8 @@ def test_arg_parser(self): @patch("pvl.pvl_translate.PVLWriter") @patch("pvl.pvl_translate.arg_parser") @patch("pvl.load") - def test_main(self, m_load, m_parser, m_PVLWriter, m_JSONWriter): + @patch("pvl.dump") + def test_main(self, m_dump, m_load, m_parser, m_PVLWriter, m_JSONWriter): m_parser().parse_args().output_format = "PDS3" self.assertIsNone(pvl_trans.main()) From 3fd375c3197f994dbbfed12df5b26bcddf4cfaf4 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 19:35:05 -0700 Subject: [PATCH 23/24] fix(token): Need to convert self to a plain string before int or float conversion, otherwise python38 correctly complains about a race condition. --- pvl/token.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvl/token.py b/pvl/token.py index 2f966b9..7478554 100644 --- a/pvl/token.py +++ b/pvl/token.py @@ -58,8 +58,8 @@ def __index__(self): try: return self.decoder.decode_non_decimal(str(self)) except ValueError: - if int(self) == float(self): - return int(self) + if int(str(self)) == float(str(self)): + return int(str(self)) raise ValueError(f"The {self:r} cannot be used as an index.") From fa6f1962ece7f3baefd11264cab11fbd0ac1b796 Mon Sep 17 00:00:00 2001 From: Ross Beyer <rbeyer@rossbeyer.net> Date: Mon, 31 May 2021 19:43:57 -0700 Subject: [PATCH 24/24] =?UTF-8?q?Bump=20version:=201.2.1-dev=20=E2=86=92?= =?UTF-8?q?=201.2.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pvl/__init__.py | 2 +- setup.cfg | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pvl/__init__.py b/pvl/__init__.py index bb2847b..f497d21 100755 --- a/pvl/__init__.py +++ b/pvl/__init__.py @@ -24,7 +24,7 @@ __author__ = "The pvl Developers" __email__ = "rbeyer@rossbeyer.net" -__version__ = "1.2.1-dev" +__version__ = "1.2.1" __all__ = [ "load", "loads", diff --git a/setup.cfg b/setup.cfg index 5a85ffe..7f4531d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.2.1-dev +current_version = 1.2.1 commit = False tag = False parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\-(?P<release>[a-z]+))? diff --git a/setup.py b/setup.py index 20c1818..1263ec5 100755 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name='pvl', - version='1.2.1-dev', + version='1.2.1', description=( 'Python implementation for PVL (Parameter Value Language) ' 'parsing and encoding.'