From 250ee413e9f37e36e91ccc5ffec8fb294faf1620 Mon Sep 17 00:00:00 2001 From: "Adam J. Stewart" Date: Mon, 24 Jul 2017 15:02:13 -0500 Subject: Change Version formatting properties and functions to return Version objects (#4834) * Change version.up_to() to return Version() object * Add unit tests for Version.up_to() * Fix packages that expected up_to() to return a string * Ensure that up_to() preserves separator characters * Use version indexing instead of up_to * Make all Version formatting properties return Version objects * Update docs * Tests need to test string representation --- lib/spack/docs/packaging_guide.rst | 15 +++++++ lib/spack/spack/test/versions.py | 56 +++++++++++++++++++++----- lib/spack/spack/version.py | 82 ++++++++++++++++++++++++++++++++------ 3 files changed, 132 insertions(+), 21 deletions(-) (limited to 'lib') diff --git a/lib/spack/docs/packaging_guide.rst b/lib/spack/docs/packaging_guide.rst index b90a0eea17..e4b10cdf53 100644 --- a/lib/spack/docs/packaging_guide.rst +++ b/lib/spack/docs/packaging_guide.rst @@ -490,6 +490,21 @@ version.joined 123 Python properties don't need parentheses. ``version.dashed`` is correct. ``version.dashed()`` is incorrect. +In addition, these version properties can be combined with ``up_to()``. +For example: + +.. code-block:: python + + >>> version = Version('1.2.3') + >>> version.up_to(2).dashed + Version('1-2') + >>> version.underscored.up_to(2) + Version('1_2') + + +As you can see, order is not important. Just keep in mind that ``up_to()`` and +the other version properties return ``Version`` objects, not strings. + If a URL cannot be derived systematically, or there is a special URL for one of its versions, you can add an explicit URL for a particular version: diff --git a/lib/spack/spack/test/versions.py b/lib/spack/spack/test/versions.py index 3b04e1d4aa..72292e4dcd 100644 --- a/lib/spack/spack/test/versions.py +++ b/lib/spack/spack/test/versions.py @@ -204,6 +204,8 @@ def test_underscores(): assert_ver_eq('2_0', '2_0') assert_ver_eq('2.0', '2_0') assert_ver_eq('2_0', '2.0') + assert_ver_eq('2-0', '2_0') + assert_ver_eq('2_0', '2-0') def test_rpm_oddities(): @@ -426,20 +428,56 @@ def test_satisfaction_with_lists(): def test_formatted_strings(): - versions = '1.2.3', '1_2_3', '1-2-3' + versions = ( + '1.2.3b', '1_2_3b', '1-2-3b', + '1.2-3b', '1.2_3b', '1-2.3b', + '1-2_3b', '1_2.3b', '1_2-3b' + ) for item in versions: v = Version(item) - assert v.dotted == '1.2.3' - assert v.dashed == '1-2-3' - assert v.underscored == '1_2_3' - assert v.joined == '123' + assert v.dotted.string == '1.2.3b' + assert v.dashed.string == '1-2-3b' + assert v.underscored.string == '1_2_3b' + assert v.joined.string == '123b' + + assert v.dotted.dashed.string == '1-2-3b' + assert v.dotted.underscored.string == '1_2_3b' + assert v.dotted.dotted.string == '1.2.3b' + assert v.dotted.joined.string == '123b' + + +def test_up_to(): + v = Version('1.23-4_5b') + + assert v.up_to(1).string == '1' + assert v.up_to(2).string == '1.23' + assert v.up_to(3).string == '1.23-4' + assert v.up_to(4).string == '1.23-4_5' + assert v.up_to(5).string == '1.23-4_5b' + + assert v.up_to(-1).string == '1.23-4_5' + assert v.up_to(-2).string == '1.23-4' + assert v.up_to(-3).string == '1.23' + assert v.up_to(-4).string == '1' + + assert v.up_to(2).dotted.string == '1.23' + assert v.up_to(2).dashed.string == '1-23' + assert v.up_to(2).underscored.string == '1_23' + assert v.up_to(2).joined.string == '123' + + assert v.dotted.up_to(2).string == '1.23' == v.up_to(2).dotted.string + assert v.dashed.up_to(2).string == '1-23' == v.up_to(2).dashed.string + assert v.underscored.up_to(2).string == '1_23' + assert v.up_to(2).underscored.string == '1_23' + + assert v.up_to(2).up_to(1).string == '1' def test_repr_and_str(): def check_repr_and_str(vrs): a = Version(vrs) - assert repr(a) == 'Version(\'' + vrs + '\')' + assert repr(a) == "Version('" + vrs + "')" b = eval(repr(a)) assert a == b assert str(a) == vrs @@ -457,17 +495,17 @@ def test_get_item(): b = a[0:2] assert isinstance(b, Version) assert b == Version('0.1') - assert repr(b) == 'Version(\'0.1\')' + assert repr(b) == "Version('0.1')" assert str(b) == '0.1' b = a[0:3] assert isinstance(b, Version) assert b == Version('0.1_2') - assert repr(b) == 'Version(\'0.1_2\')' + assert repr(b) == "Version('0.1_2')" assert str(b) == '0.1_2' b = a[1:] assert isinstance(b, Version) assert b == Version('1_2-3') - assert repr(b) == 'Version(\'1_2-3\')' + assert repr(b) == "Version('1_2-3')" assert str(b) == '1_2-3' # Raise TypeError on tuples with pytest.raises(TypeError): diff --git a/lib/spack/spack/version.py b/lib/spack/spack/version.py index 10fac49e9d..0d8520a0e0 100644 --- a/lib/spack/spack/version.py +++ b/lib/spack/spack/version.py @@ -130,30 +130,90 @@ class Version(object): self.version = tuple(int_if_int(seg) for seg in segments) # Store the separators from the original version string as well. - # last element of separators is '' - self.separators = tuple(re.split(segment_regex, string)[1:-1]) + self.separators = tuple(re.split(segment_regex, string)[1:]) @property def dotted(self): - return '.'.join(str(x) for x in self.version) + """The dotted representation of the version. + + Example: + >>> version = Version('1-2-3b') + >>> version.dotted + Version('1.2.3b') + + Returns: + Version: The version with separator characters replaced by dots + """ + return Version(self.string.replace('-', '.').replace('_', '.')) @property def underscored(self): - return '_'.join(str(x) for x in self.version) + """The underscored representation of the version. + + Example: + >>> version = Version('1.2.3b') + >>> version.underscored + Version('1_2_3b') + + Returns: + Version: The version with separator characters replaced by + underscores + """ + return Version(self.string.replace('.', '_').replace('-', '_')) @property def dashed(self): - return '-'.join(str(x) for x in self.version) + """The dashed representation of the version. + + Example: + >>> version = Version('1.2.3b') + >>> version.dashed + Version('1-2-3b') + + Returns: + Version: The version with separator characters replaced by dashes + """ + return Version(self.string.replace('.', '-').replace('_', '-')) @property def joined(self): - return ''.join(str(x) for x in self.version) + """The joined representation of the version. + + Example: + >>> version = Version('1.2.3b') + >>> version.joined + Version('123b') + + Returns: + Version: The version with separator characters removed + """ + return Version( + self.string.replace('.', '').replace('-', '').replace('_', '')) def up_to(self, index): - """Return a version string up to the specified component, exclusive. - e.g., if this is 10.8.2, self.up_to(2) will return '10.8'. + """The version up to the specified component. + + Examples: + >>> version = Version('1.23-4b') + >>> version.up_to(1) + Version('1') + >>> version.up_to(2) + Version('1.23') + >>> version.up_to(3) + Version('1.23-4') + >>> version.up_to(4) + Version('1.23-4b') + >>> version.up_to(-1) + Version('1.23-4') + >>> version.up_to(-2) + Version('1.23') + >>> version.up_to(-3) + Version('1') + + Returns: + Version: The first index components of the version """ - return '.'.join(str(x) for x in self[:index]) + return self[:index] def lowest(self): return self @@ -204,11 +264,9 @@ class Version(object): return self.version[idx] elif isinstance(idx, slice): - # Currently len(self.separators) == len(self.version) - 1 - extendend_separators = self.separators + ('',) string_arg = [] - pairs = zip(self.version[idx], extendend_separators[idx]) + pairs = zip(self.version[idx], self.separators[idx]) for token, sep in pairs: string_arg.append(str(token)) string_arg.append(str(sep)) -- cgit v1.2.3-60-g2f50