From d10ec59e7035b8ae473d549d20edc91961512041 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Tue, 5 Aug 2025 14:31:30 -0700 Subject: [PATCH] Prep for type annotations - Remove unused pyver var - Fix ret_string type for ECStripingDriver - Clean up ECDriver signature - Fix AttributeError in ECDriver repr when no ec_type specified - Fix typo in verify_stripe_metadata docstring - Ensure positive_int_value works properly regardless of optimization level Change-Id: If20196d067ff42a1179923b422a6f0bed9917d45 --- pyeclib/core.py | 5 +- pyeclib/ec_iface.py | 124 ++++++++++++++++++++------------------- pyeclib/utils.py | 7 ++- test/test_pyeclib_api.py | 12 ++-- test/test_pyeclib_c.py | 4 +- 5 files changed, 78 insertions(+), 74 deletions(-) diff --git a/pyeclib/core.py b/pyeclib/core.py index f9d5268..3dcea74 100644 --- a/pyeclib/core.py +++ b/pyeclib/core.py @@ -28,9 +28,6 @@ from .ec_iface import PyECLib_FRAGHDRCHKSUM_Types import math import pyeclib_c -import sys - -pyver = float('%s.%s' % sys.version_info[:2]) class ECPyECLibDriver(object): @@ -254,7 +251,7 @@ class ECStripingDriver(object): "Decode requires %d fragments, %d fragments were given" % (len(fragment_payloads), self.k)) - ret_string = '' + ret_string = b'' for fragment in fragment_payloads: ret_string += fragment diff --git a/pyeclib/ec_iface.py b/pyeclib/ec_iface.py index 5c941e9..71cd034 100644 --- a/pyeclib/ec_iface.py +++ b/pyeclib/ec_iface.py @@ -93,7 +93,15 @@ class PyECLib_FRAGHDRCHKSUM_Types(Enum): class ECDriver(object): '''A driver to encode, decode, and reconstruct erasure-coded data.''' - def __init__(self, **kwargs): + def __init__( + self, *, + ec_type=None, + library_import_str='pyeclib.core.ECPyECLibDriver', + k, + m, + chksum_type='none', + validate=False, + ): ''' :param ec_type: the erasure coding type to use for this driver. :param k: number of data fragments to use. Required. @@ -112,68 +120,63 @@ class ECDriver(object): self.hd = -1 self.ec_type = None self.chksum_type = None - self.validate = False - for required in ('k', 'm'): - if required not in kwargs: - raise ECDriverError( - "Invalid Argument: %s is required" % required) - - if 'ec_type' not in kwargs and 'library_import_str' not in kwargs: + if ( + ec_type is None and + library_import_str == 'pyeclib.core.ECPyECLibDriver' + ): raise ECDriverError( "Invalid Argument: either ec_type or library_import_str " "must be provided") - for (key, value) in kwargs.items(): - if key == "k": - try: - self.k = positive_int_value(value) - except ValueError: - raise ECDriverError( - "Invalid number of data fragments (k)") - elif key == "m": - try: - self.m = positive_int_value(value) - except ValueError: - raise ECDriverError( - "Invalid number of parity fragments (m)") - elif key == "ec_type": - if value in ["flat_xor_hd", "flat_xor_hd_3", "flat_xor_hd_4"]: - if value == "flat_xor_hd" or value == "flat_xor_hd_3": - self.hd = 3 - elif value == "flat_xor_hd_4": - self.hd = 4 - value = "flat_xor_hd" - elif value == "libphazr": - self.hd = 1 + try: + self.k = positive_int_value(k) + except ValueError: + raise ECDriverError( + "Invalid number of data fragments (k)") - if value in PyECLib_EC_Types.__members__: - self.ec_type = PyECLib_EC_Types[value] - if self.ec_type in (PyECLib_EC_Types.jerasure_rs_vand, - PyECLib_EC_Types.jerasure_rs_cauchy): - warnings.warn('Jerasure support is deprecated and ' - 'may be removed in a future release', - FutureWarning, stacklevel=2) + try: + self.m = positive_int_value(m) + except ValueError: + raise ECDriverError( + "Invalid number of parity fragments (m)") - else: - raise ECBackendNotSupported( - "%s is not a valid EC type for PyECLib!" % value) - elif key == "chksum_type": - if value in PyECLib_FRAGHDRCHKSUM_Types.__members__: - self.chksum_type = \ - PyECLib_FRAGHDRCHKSUM_Types[value] - else: - raise ECDriverError( - "%s is not a valid checksum type for PyECLib!" % value) - elif key == "validate": - # validate if the ec type is available (runtime check) - self.validate = value + if ec_type: + if ec_type in ["flat_xor_hd", "flat_xor_hd_3", "flat_xor_hd_4"]: + if ec_type == "flat_xor_hd" or ec_type == "flat_xor_hd_3": + self.hd = 3 + elif ec_type == "flat_xor_hd_4": + self.hd = 4 + ec_type = "flat_xor_hd" + elif ec_type == "libphazr": + self.hd = 1 + + if ec_type in PyECLib_EC_Types.__members__: + self.ec_type = PyECLib_EC_Types[ec_type] + if self.ec_type in (PyECLib_EC_Types.jerasure_rs_vand, + PyECLib_EC_Types.jerasure_rs_cauchy): + warnings.warn('Jerasure support is deprecated and ' + 'may be removed in a future release', + FutureWarning, stacklevel=2) + + else: + raise ECBackendNotSupported( + "%s is not a valid EC type for PyECLib!" % ec_type) + + if chksum_type in PyECLib_FRAGHDRCHKSUM_Types.__members__: + self.chksum_type = \ + PyECLib_FRAGHDRCHKSUM_Types[chksum_type] + else: + raise ECDriverError( + "%s is not a valid checksum type for PyECLib!" + % chksum_type) + + self.validate = validate if self.hd == -1: self.hd = self.m - self.library_import_str = kwargs.pop('library_import_str', - 'pyeclib.core.ECPyECLibDriver') + self.library_import_str = library_import_str # # Instantiate EC backend driver # @@ -211,12 +214,15 @@ class ECDriver(object): "in %s: %s" % (self.library_import_str, missing_methods)) def __repr__(self): - return '%s(ec_type=%r, k=%r, m=%r)' % ( - type(self).__name__, - 'flat_xor_hd_%s' % self.hd if self.ec_type.name == 'flat_xor_hd' - else self.ec_type.name, - self.k, - self.m) + if self.ec_type is None: + ec_type = 'None' + elif self.ec_type.name == 'flat_xor_hd': + ec_type = f'flat_xor_hd_{self.hd}' + else: + ec_type = self.ec_type.name + + return (f'{type(self).__name__}(' + f'ec_type={ec_type!r}, k={self.k}, m={self.m})') def close(self): self.ec_lib_reference.close() @@ -313,7 +319,7 @@ class ECDriver(object): Verify a subset of fragments generated by encode() :param fragment_metadata_list: a list of buffers representing the - metadata from a subset of the framgments + metadata from a subset of the fragments generated by encode(). :returns: 'None' if the metadata is consistent. a list of fragment indexes corresponding to inconsistent diff --git a/pyeclib/utils.py b/pyeclib/utils.py index accbcc5..02c134c 100644 --- a/pyeclib/utils.py +++ b/pyeclib/utils.py @@ -30,10 +30,11 @@ def positive_int_value(param): # Returns value as a positive int or raises ValueError otherwise try: value = int(param) - assert value > 0 - except (TypeError, ValueError, AssertionError): + if value <= 0: + raise ValueError + except (TypeError, ValueError): # Handle: TypeError for 'None', ValueError for non-int strings - # and AssertionError for values <= 0 + # and values <= 0 raise ValueError('Must be an integer > 0, not "%s".' % param) return value diff --git a/test/test_pyeclib_api.py b/test/test_pyeclib_api.py index a443778..360dbbc 100644 --- a/test/test_pyeclib_api.py +++ b/test/test_pyeclib_api.py @@ -124,16 +124,16 @@ class TestPyECLibDriver(unittest.TestCase): for ec_type in VALID_EC_TYPES: # missing k - with self.assertRaises(ECDriverError) as err_context: + with self.assertRaises(TypeError) as err_context: ECDriver(ec_type=ec_type, m=1) - self.assertEqual(str(err_context.exception), - "Invalid Argument: k is required") + self.assertIn("missing 1 required keyword-only argument: 'k'", + str(err_context.exception)) # missing m - with self.assertRaises(ECDriverError) as err_context: + with self.assertRaises(TypeError) as err_context: ECDriver(ec_type=ec_type, k=1) - self.assertEqual(str(err_context.exception), - "Invalid Argument: m is required") + self.assertIn("missing 1 required keyword-only argument: 'm'", + str(err_context.exception)) def test_invalid_km_args(self): for ec_type in VALID_EC_TYPES: diff --git a/test/test_pyeclib_c.py b/test/test_pyeclib_c.py index e2bab31..bfa0ccc 100644 --- a/test/test_pyeclib_c.py +++ b/test/test_pyeclib_c.py @@ -38,8 +38,8 @@ from pyeclib.ec_iface import VALID_EC_TYPES class Timer: def __init__(self): - self.start_time = 0 - self.end_time = 0 + self.start_time = 0.0 + self.end_time = 0.0 def start(self): self.start_time = time.time()