class PythonVersion(PythonMicroVersion): """A complete, specific Python version. This version scheme has four segments that identify a specific version of Python. See the link in the module documentation for details about the Python version scheme. """ SEGMENT_DEFINITIONS = PythonMicroVersion.SEGMENT_DEFINITIONS + ( SegmentDefinition( name='suffix', optional=True, separator='', fields=( SegmentField( type=str, name='releaselevel', re_pattern='[+abc]', ), SegmentField( name='serial', re_pattern='(?<=[+])|(?<![+])(?:0|[1-9][0-9]*)', render=lambda x: "" if x is None else str(x), ), ), ), ) @property def is_nondevelopment(self): """Whether this version represents a non-development release. This simply says whether it is equivalent to its `micro_version`; that is, whether the `SUFFIX`-index value is `None`. >>> assert(PythonVersion('3.4.1').is_nondevelopment) >>> assert(not PythonVersion('3.4.1c1').is_nondevelopment) >>> assert(not PythonVersion('3.4.1+').is_nondevelopment) """ return self[SUFFIX] is None @property def is_release(self): """Whether this version represents a release. This simply says whether the `SUFFIX`-index value is not '+'. A '+' indicates that it is an unreleased version, built directly from the Subversion trunk; anything else is a release (be it development or non-development). >>> assert(PythonVersion('3.4.1').is_release) >>> assert(PythonVersion('3.4.1c1').is_release) >>> assert(not PythonVersion('3.4.1+').is_release) """ suffix = self[SUFFIX] return suffix is None or suffix.releaselevel != '+'
def test_validate_value_partial(self): sd = SegmentDefinition(fields=( SegmentField(name='a'), SegmentField(name='b', re_pattern='(?<=[0])|(?<![0])(?:0|[1-9][0-9]*)', render=lambda x: "" if x is None else str(x)))) self.assertEqual((0, None), sd.validate_value((0, None))) self.assertEqual((0, None), sd.validate_value((0, ))) self.assertRaises(ValueError, sd.validate_value, (0, 3)) self.assertEqual((1, 2), sd.validate_value((1, 2))) self.assertRaises(ValueError, sd.validate_value, (1, ))
def test_init_render(self): # Valid patterns succeed. for value in (dict, callable, isinstance, lambda x: x + 1): SegmentField(render=value) # Invalid instances raise TypeError. for value in (1, 'a', [dict], None): self.assertRaises(TypeError, SegmentField, render=value)
class Version1(Version): SEGMENT_DEFINITIONS = ( SegmentDefinition( optional=True, default=0, ), SegmentDefinition(separator='', fields=SegmentField(type=str, re_pattern='[a-z]')), )
def test_init_type(self): # Valid types succeed. class C(object): pass for value in (dict, C): SegmentField(type=value) # Invalid types raise TypeError. for value in (1, 'a', [dict]): self.assertRaises(TypeError, SegmentField, type=value)
def test_init_re_pattern(self): # Valid patterns succeed. for value in ("valid", u'[Pp]atterns?', r"\.", '[0-9]+'): SegmentField(re_pattern=value) # Invalid instances raise TypeError. for value in (1, dict, ['a'], re.compile('[0-9]+')): self.assertRaises(TypeError, SegmentField, re_pattern=value) # Invalid regular expression patterns raise re.error. for value in ("parenthesis(mismatch", 'invalid(?pattern)'): self.assertRaises(re.error, SegmentField, re_pattern=value)
def test_init_name(self): # Valid names succeed. for value in ("valid", u'identifiers'): SegmentField(name=value) # Invalid name instances raise TypeError. for value in (1, dict, [dict]): self.assertRaises(TypeError, SegmentField, name=value) # Invalid name values raise ValueError. for value in ("", "invalid-identifier", '_underscore'): self.assertRaises(ValueError, SegmentField, name=value)
def test_ne_name(self): self.assertNotEqual(SegmentField(), SegmentField(name='different'))
def test_ne_type(self): self.assertNotEqual(SegmentField(), SegmentField(type=str))
def test_eq_type_and_name_and_re_pattern(self): self.assertEqual(SegmentField(type=str, name='same', re_pattern='x+'), SegmentField(type=str, name='same', re_pattern='x+'))
def test_eq_name_and_re_pattern(self): self.assertEqual(SegmentField(name='same', re_pattern='[0-9]*'), SegmentField(name='same', re_pattern='[0-9]*'))
def test_eq_re_pattern(self): self.assertEqual(SegmentField(re_pattern='[0-9]*'), SegmentField(re_pattern='[0-9]*'))
def test_eq_name(self): self.assertEqual(SegmentField(name='same'), SegmentField(name='same'))
def test_init_fields_name_duplicate_default(self): fields = (SegmentField(), SegmentField()) self.assertRaises(ValueError, SegmentDefinition, fields=fields)
def test_ne_re_pattern(self): self.assertNotEqual(SegmentField(), SegmentField(re_pattern='[0-5]'))
class VersionString(Version): SEGMENT_DEFINITIONS = (SegmentDefinition(fields=SegmentField( type=str, re_pattern='[0-9]+(?:[.][0-9]+)*')), )
def test_render_muliple_fields(self): sd = SegmentDefinition(fields=(SegmentField(name='a'), SegmentField(name='b'))) self.assertEqual('138', sd.render((13, 8)))
def test_validate_value(self): sd = SegmentDefinition(fields=(SegmentField(name='a'), SegmentField(name='b'))) self.assertEqual((1, 2), sd.validate_value((1, 2))) self.assertEqual((4, 3), sd.validate_value([4, 3]))
def test_init_fields_noniterable(self): self.assertEqual(SegmentDefinition(), SegmentDefinition(fields=SegmentField()))
def test_eq_default(self): self.assertEqual(SegmentField(), SegmentField())
def test_eq_type(self): self.assertEqual(SegmentField(type=str), SegmentField(type=str))
def test_init_fields_name_duplicate(self): fields = (SegmentField(name='oops'), SegmentField(name='oops')) self.assertRaises(ValueError, SegmentDefinition, fields=fields)