def test_relationship_evaluation(self): # Relationships without versions. relationship_set = parse_depends('python') self.assertTrue(relationship_set.matches('python')) self.assertFalse(relationship_set.matches('python2.7')) self.assertEqual(list(relationship_set.names), ['python']) # Alternatives (OR) without versions. relationship_set = parse_depends('python2.6 | python2.7') self.assertFalse(relationship_set.matches('python2.5')) self.assertTrue(relationship_set.matches('python2.6')) self.assertTrue(relationship_set.matches('python2.7')) self.assertFalse(relationship_set.matches('python3.0')) self.assertEqual(sorted(relationship_set.names), ['python2.6', 'python2.7']) # Combinations (AND) with versions. relationship_set = parse_depends('python (>= 2.6), python (<< 3) | python (>= 3.4)') self.assertFalse(relationship_set.matches('python', '2.5')) self.assertTrue(relationship_set.matches('python', '2.6')) self.assertTrue(relationship_set.matches('python', '2.7')) self.assertFalse(relationship_set.matches('python', '3.0')) self.assertTrue(relationship_set.matches('python', '3.4')) self.assertEqual(list(relationship_set.names), ['python']) # Testing for matches without providing a version is valid (should not # raise an error) but will never match a relationship with a version. relationship_set = parse_depends('python (>= 2.6), python (<< 3)') self.assertTrue(relationship_set.matches('python', '2.7')) self.assertFalse(relationship_set.matches('python')) self.assertEqual(list(relationship_set.names), ['python']) # Distinguishing between packages whose name was matched but whose # version didn't match vs packages whose name wasn't matched. relationship_set = parse_depends('python (>= 2.6), python (<< 3) | python (>= 3.4)') self.assertEqual(relationship_set.matches('python', '2.7'), True) # name and version match self.assertEqual(relationship_set.matches('python', '2.5'), False) # name matched, version didn't self.assertEqual(relationship_set.matches('python2.6'), None) # name didn't match self.assertEqual(relationship_set.matches('python', '3.0'), False) # name in alternative matched, version didn't self.assertEqual(list(relationship_set.names), ['python'])
def test_relationship_parsing(self): # Happy path (no parsing errors). relationship_set = parse_depends('foo, bar (>= 1) | baz') self.assertEqual(relationship_set.relationships[0].name, 'foo') self.assertEqual(relationship_set.relationships[1].relationships[0].name, 'bar') self.assertEqual(relationship_set.relationships[1].relationships[0].operator, '>=') self.assertEqual(relationship_set.relationships[1].relationships[0].version, '1') self.assertEqual(relationship_set.relationships[1].relationships[1].name, 'baz') self.assertEqual(parse_depends('foo (=1.0)'), RelationshipSet(VersionedRelationship(name='foo', operator='=', version='1.0'))) # Unhappy path (parsing errors). self.assertRaises(ValueError, parse_depends, 'foo (bar) (baz)') self.assertRaises(ValueError, parse_depends, 'foo (bar baz qux)')
def test_architecture_restriction_parsing(self): """Test the parsing of architecture restrictions.""" relationship_set = parse_depends('qux [i386 amd64]') assert relationship_set.relationships[0].name == 'qux' assert len(relationship_set.relationships[0].architectures) == 2 assert 'i386' in relationship_set.relationships[0].architectures assert 'amd64' in relationship_set.relationships[0].architectures
def test_custom_pretty_printer(self): """Test pretty printing of deb822 objects and parsed relationships.""" printer = CustomPrettyPrinter() # Test pretty printing of debian.deb822.Deb822 objects. deb822_object = deb822_from_string(''' Package: pretty-printed-control-fields Version: 1.0 Architecture: all ''') formatted_object = printer.pformat(deb822_object) assert normalize_repr_output( formatted_object) == normalize_repr_output(''' {'Architecture': u'all', 'Package': u'pretty-printed-control-fields', 'Version': u'1.0'} ''') # Test pretty printing of RelationshipSet objects. relationship_set = parse_depends( 'python-deb-pkg-tools, python-pip, python-pip-accel') formatted_object = printer.pformat(relationship_set) assert normalize_repr_output( formatted_object) == normalize_repr_output(''' RelationshipSet(Relationship(name='python-deb-pkg-tools', architectures=()), Relationship(name='python-pip', architectures=()), Relationship(name='python-pip-accel', architectures=())) ''')
def test_relationship_unparsing(self): relationship_set = parse_depends('foo, bar(>=1)|baz') self.assertEqual(unicode(relationship_set), 'foo, bar (>= 1) | baz') self.assertEqual( compact(repr(relationship_set)), "RelationshipSet(Relationship(name='foo'), AlternativeRelationship(VersionedRelationship(name='bar', operator='>=', version='1'), Relationship(name='baz')))" )
def test_custom_pretty_printer(self): printer = CustomPrettyPrinter() # Test pretty printing of debian.deb822.Deb822 objects. self.assertEqual( remove_unicode_prefixes( printer.pformat( deb822_from_string(''' Package: pretty-printed-control-fields Version: 1.0 Architecture: all '''))), remove_unicode_prefixes( dedent(''' {'Architecture': u'all', 'Package': u'pretty-printed-control-fields', 'Version': u'1.0'} '''))) # Test pretty printing of RelationshipSet objects. depends_line = 'python-deb-pkg-tools, python-pip, python-pip-accel' self.assertEqual( printer.pformat(parse_depends(depends_line)), dedent(''' RelationshipSet(Relationship(name='python-deb-pkg-tools'), Relationship(name='python-pip'), Relationship(name='python-pip-accel')) '''))
def test_relationship_parsing(self): """Test the parsing of Debian package relationship declarations.""" # Happy path (no parsing errors). relationship_set = parse_depends('foo, bar (>= 1) | baz') assert relationship_set.relationships[0].name == 'foo' assert relationship_set.relationships[1].relationships[0].name == 'bar' assert relationship_set.relationships[1].relationships[0].operator == '>=' assert relationship_set.relationships[1].relationships[0].version == '1' assert relationship_set.relationships[1].relationships[1].name == 'baz' assert parse_depends('foo (=1.0)') == RelationshipSet(VersionedRelationship( name='foo', operator='=', version='1.0', )) # Unhappy path (parsing errors). self.assertRaises(ValueError, parse_depends, 'foo (bar) (baz)') self.assertRaises(ValueError, parse_depends, 'foo (bar baz qux)')
def test_relationship_evaluation(self): """Test the evaluation of package relationships.""" # Relationships without versions. relationship_set = parse_depends('python') assert relationship_set.matches('python') assert not relationship_set.matches('python2.7') assert list(relationship_set.names) == ['python'] # Alternatives (OR) without versions. relationship_set = parse_depends('python2.6 | python2.7') assert not relationship_set.matches('python2.5') assert relationship_set.matches('python2.6') assert relationship_set.matches('python2.7') assert not relationship_set.matches('python3.0') assert sorted(relationship_set.names) == ['python2.6', 'python2.7'] # Combinations (AND) with versions. relationship_set = parse_depends( 'python (>= 2.6), python (<< 3) | python (>= 3.4)') assert not relationship_set.matches('python', '2.5') assert relationship_set.matches('python', '2.6') assert relationship_set.matches('python', '2.7') assert not relationship_set.matches('python', '3.0') assert relationship_set.matches('python', '3.4') assert list(relationship_set.names) == ['python'] # Testing for matches without providing a version is valid (should not # raise an error) but will never match a relationship with a version. relationship_set = parse_depends('python (>= 2.6), python (<< 3)') assert relationship_set.matches('python', '2.7') assert not relationship_set.matches('python') assert list(relationship_set.names) == ['python'] # Distinguishing between packages whose name was matched but whose # version didn't match vs packages whose name wasn't matched. relationship_set = parse_depends( 'python (>= 2.6), python (<< 3) | python (>= 3.4)') assert relationship_set.matches( 'python', '2.7') is True # name and version match assert relationship_set.matches( 'python', '2.5') is False # name matched, version didn't assert relationship_set.matches( 'python2.6') is None # name didn't match assert relationship_set.matches( 'python', '3.0') is False # name in alternative matched, version didn't assert list(relationship_set.names) == ['python']
def test_relationship_evaluation(self): # Relationships without versions. relationship_set = parse_depends('python') self.assertTrue(relationship_set.matches('python')) self.assertFalse(relationship_set.matches('python2.7')) self.assertEqual(list(relationship_set.names), ['python']) # Alternatives (OR) without versions. relationship_set = parse_depends('python2.6 | python2.7') self.assertFalse(relationship_set.matches('python2.5')) self.assertTrue(relationship_set.matches('python2.6')) self.assertTrue(relationship_set.matches('python2.7')) self.assertFalse(relationship_set.matches('python3.0')) self.assertEqual(sorted(relationship_set.names), ['python2.6', 'python2.7']) # Combinations (AND) with versions. relationship_set = parse_depends( 'python (>= 2.6), python (<< 3) | python (>= 3.4)') self.assertFalse(relationship_set.matches('python', '2.5')) self.assertTrue(relationship_set.matches('python', '2.6')) self.assertTrue(relationship_set.matches('python', '2.7')) self.assertFalse(relationship_set.matches('python', '3.0')) self.assertTrue(relationship_set.matches('python', '3.4')) self.assertEqual(list(relationship_set.names), ['python']) # Testing for matches without providing a version is valid (should not # raise an error) but will never match a relationship with a version. relationship_set = parse_depends('python (>= 2.6), python (<< 3)') self.assertTrue(relationship_set.matches('python', '2.7')) self.assertFalse(relationship_set.matches('python')) self.assertEqual(list(relationship_set.names), ['python']) # Distinguishing between packages whose name was matched but whose # version didn't match vs packages whose name wasn't matched. relationship_set = parse_depends( 'python (>= 2.6), python (<< 3) | python (>= 3.4)') self.assertEqual(relationship_set.matches('python', '2.7'), True) # name and version match self.assertEqual(relationship_set.matches('python', '2.5'), False) # name matched, version didn't self.assertEqual(relationship_set.matches('python2.6'), None) # name didn't match self.assertEqual(relationship_set.matches('python', '3.0'), False) # name in alternative matched, version didn't self.assertEqual(list(relationship_set.names), ['python'])
def test_relationship_parsing(self): # Happy path (no parsing errors). relationship_set = parse_depends('foo, bar (>= 1) | baz') self.assertEqual(relationship_set.relationships[0].name, 'foo') self.assertEqual( relationship_set.relationships[1].relationships[0].name, 'bar') self.assertEqual( relationship_set.relationships[1].relationships[0].operator, '>=') self.assertEqual( relationship_set.relationships[1].relationships[0].version, '1') self.assertEqual( relationship_set.relationships[1].relationships[1].name, 'baz') self.assertEqual( parse_depends('foo (=1.0)'), RelationshipSet( VersionedRelationship(name='foo', operator='=', version='1.0'))) # Unhappy path (parsing errors). self.assertRaises(ValueError, parse_depends, 'foo (bar) (baz)') self.assertRaises(ValueError, parse_depends, 'foo (bar baz qux)')
def test_relationship_unparsing(self): """Test the unparsing (serialization) of parsed relationship declarations.""" def strip(text): return re.sub(r'\s+', '', text) relationship_set = parse_depends('foo, bar(>=1)|baz[i386]') assert text_type(relationship_set) == 'foo, bar (>= 1) | baz [i386]' assert strip(repr(relationship_set)) == strip(""" RelationshipSet( Relationship(name='foo', architectures=()), AlternativeRelationship( VersionedRelationship(name='bar', operator='>=', version='1', architectures=()), Relationship(name='baz', architectures=('i386',)) ) ) """)
def test_relationship_evaluation(self): """Test the evaluation of package relationships.""" # Relationships without versions. relationship_set = parse_depends('python') assert relationship_set.matches('python') assert not relationship_set.matches('python2.7') assert list(relationship_set.names) == ['python'] # Alternatives (OR) without versions. relationship_set = parse_depends('python2.6 | python2.7') assert not relationship_set.matches('python2.5') assert relationship_set.matches('python2.6') assert relationship_set.matches('python2.7') assert not relationship_set.matches('python3.0') assert sorted(relationship_set.names) == ['python2.6', 'python2.7'] # Combinations (AND) with versions. relationship_set = parse_depends('python (>= 2.6), python (<< 3) | python (>= 3.4)') assert not relationship_set.matches('python', '2.5') assert relationship_set.matches('python', '2.6') assert relationship_set.matches('python', '2.7') assert not relationship_set.matches('python', '3.0') assert relationship_set.matches('python', '3.4') assert list(relationship_set.names) == ['python'] # Testing for matches without providing a version is valid (should not # raise an error) but will never match a relationship with a version. relationship_set = parse_depends('python (>= 2.6), python (<< 3)') assert relationship_set.matches('python', '2.7') assert not relationship_set.matches('python') assert list(relationship_set.names) == ['python'] # Distinguishing between packages whose name was matched but whose # version didn't match vs packages whose name wasn't matched. relationship_set = parse_depends('python (>= 2.6), python (<< 3) | python (>= 3.4)') assert relationship_set.matches('python', '2.7') is True # name and version match assert relationship_set.matches('python', '2.5') is False # name matched, version didn't assert relationship_set.matches('python2.6') is None # name didn't match assert relationship_set.matches('python', '3.0') is False # name in alternative matched, version didn't assert list(relationship_set.names) == ['python']
def test_custom_pretty_printer(self): printer = CustomPrettyPrinter() # Test pretty printing of debian.deb822.Deb822 objects. self.assertEqual(remove_unicode_prefixes(printer.pformat(deb822_from_string(''' Package: pretty-printed-control-fields Version: 1.0 Architecture: all '''))), remove_unicode_prefixes(dedent(''' {'Architecture': u'all', 'Package': u'pretty-printed-control-fields', 'Version': u'1.0'} '''))) # Test pretty printing of RelationshipSet objects. depends_line = 'python-deb-pkg-tools, python-pip, python-pip-accel' self.assertEqual(printer.pformat(parse_depends(depends_line)), dedent(''' RelationshipSet(Relationship(name='python-deb-pkg-tools'), Relationship(name='python-pip'), Relationship(name='python-pip-accel')) '''))
def test_custom_pretty_printer(self): """Test pretty printing of deb822 objects and parsed relationships.""" printer = CustomPrettyPrinter() # Test pretty printing of debian.deb822.Deb822 objects. deb822_object = deb822_from_string(''' Package: pretty-printed-control-fields Version: 1.0 Architecture: all ''') formatted_object = printer.pformat(deb822_object) assert normalize_repr_output(formatted_object) == normalize_repr_output(''' {'Architecture': u'all', 'Package': u'pretty-printed-control-fields', 'Version': u'1.0'} ''') # Test pretty printing of RelationshipSet objects. relationship_set = parse_depends('python-deb-pkg-tools, python-pip, python-pip-accel') formatted_object = printer.pformat(relationship_set) assert normalize_repr_output(formatted_object) == normalize_repr_output(''' RelationshipSet(Relationship(name='python-deb-pkg-tools', architectures=()), Relationship(name='python-pip', architectures=()), Relationship(name='python-pip-accel', architectures=())) ''')
def test_relationship_sorting(self): relationship_set = parse_depends('foo | bar, baz | qux') self.assertEqual(relationship_set, RelationshipSet( AlternativeRelationship(Relationship(name='baz'), Relationship(name='qux')), AlternativeRelationship(Relationship(name='foo'), Relationship(name='bar'))))
def test_relationship_unparsing(self): relationship_set = parse_depends('foo, bar(>=1)|baz') self.assertEqual(unicode(relationship_set), 'foo, bar (>= 1) | baz') self.assertEqual(compact(repr(relationship_set)), "RelationshipSet(Relationship(name='foo'), AlternativeRelationship(VersionedRelationship(name='bar', operator='>=', version='1'), Relationship(name='baz')))")
def parse_control_fields(input_fields): r""" Parse Debian control file fields. :param input_fields: The dictionary to convert (may be an instance of :class:`debian.deb822.Deb822` but doesn't have to be). :returns: A :class:`dict` object with the converted fields. The :class:`debian.deb822.Deb822` class can be used to parse Debian control files but the result is a simple :class:`dict` with string name/value pairs. This function takes an existing :class:`debian.deb822.Deb822` instance and converts the following fields into friendlier formats: - The values of the fields given by :data:`DEPENDS_LIKE_FIELDS` are parsed into Python data structures using :func:`.parse_depends()`. - The value of the `Installed-Size` field is converted to an integer. Let's look at an example. We start with the raw control file contents so you can see the complete input: >>> from deb_pkg_tools.control import deb822_from_string >>> unparsed_fields = deb822_from_string(''' ... Package: python3.4-minimal ... Version: 3.4.0-1+precise1 ... Architecture: amd64 ... Installed-Size: 3586 ... Pre-Depends: libc6 (>= 2.15) ... Depends: libpython3.4-minimal (= 3.4.0-1+precise1), libexpat1 (>= 1.95.8), libgcc1 (>= 1:4.1.1), zlib1g (>= 1:1.2.0), foo | bar ... Recommends: python3.4 ... Suggests: binfmt-support ... Conflicts: binfmt-support (<< 1.1.2) ... ''') Here are the control file fields as parsed by the :class:`debian.deb822` module: >>> print(repr(unparsed_fields)) {'Architecture': u'amd64', 'Conflicts': u'binfmt-support (<< 1.1.2)', 'Depends': u'libpython3.4-minimal (= 3.4.0-1+precise1), libexpat1 (>= 1.95.8), libgcc1 (>= 1:4.1.1), zlib1g (>= 1:1.2.0), foo | bar', 'Installed-Size': u'3586', 'Package': u'python3.4-minimal', 'Pre-Depends': u'libc6 (>= 2.15)', 'Recommends': u'python3.4', 'Suggests': u'binfmt-support', 'Version': u'3.4.0-1+precise1'} Notice the value of the `Depends` line is a comma separated string, i.e. it hasn't been parsed. Now here are the control file fields parsed by the :func:`parse_control_fields()` function: >>> from deb_pkg_tools.control import parse_control_fields >>> parsed_fields = parse_control_fields(unparsed_fields) >>> print(repr(parsed_fields)) {'Architecture': u'amd64', 'Conflicts': RelationshipSet(VersionedRelationship(name=u'binfmt-support', operator=u'<<', version=u'1.1.2')), 'Depends': RelationshipSet(VersionedRelationship(name=u'libpython3.4-minimal', operator=u'=', version=u'3.4.0-1+precise1'), VersionedRelationship(name=u'libexpat1', operator=u'>=', version=u'1.95.8'), VersionedRelationship(name=u'libgcc1', operator=u'>=', version=u'1:4.1.1'), VersionedRelationship(name=u'zlib1g', operator=u'>=', version=u'1:1.2.0'), AlternativeRelationship(Relationship(name=u'foo'), Relationship(name=u'bar'))), 'Installed-Size': 3586, 'Package': u'python3.4-minimal', 'Pre-Depends': RelationshipSet(VersionedRelationship(name=u'libc6', operator=u'>=', version=u'2.15')), 'Recommends': u'python3.4', 'Suggests': RelationshipSet(Relationship(name=u'binfmt-support')), 'Version': u'3.4.0-1+precise1'} For more information about fields like `Depends` and `Suggests` please refer to the documentation of :func:`.parse_depends()`. """ logger.debug("Parsing %i control fields ..", len(input_fields)) output_fields = {} for name, unparsed_value in input_fields.items(): name = normalize_control_field_name(name) if name in DEPENDS_LIKE_FIELDS: parsed_value = parse_depends(unparsed_value) elif name == 'Installed-Size': parsed_value = int(unparsed_value) else: parsed_value = unparsed_value output_fields[name] = parsed_value logger.debug("Parsed fields: %s", output_fields) return output_fields
def parse_control_fields(input_fields): r""" The :py:class:`debian.deb822.Deb822` class can be used to parse Debian control files but the result is a simple :py:class:`dict` with string name/value pairs. This function takes an existing :py:class:`debian.deb822.Deb822` instance and converts known fields into friendlier formats, for example: - The value of `Depends`, `Provides`, `Replaces` and `Conflicts` fields is converted to a list of strings. - The value of the `Installed-Size` field is converted to an integer. :param input_fields: The dictionary to convert (may be an instance of :py:class:`debian.deb822.Deb822` but doesn't have to be). :returns: A :py:class:`dict` object with the converted fields. Let's look at an example. We start with the raw control file contents so you can see the complete input: >>> from deb_pkg_tools.control import deb822_from_string >>> unparsed_fields = deb822_from_string(''' ... Package: python3.4-minimal ... Version: 3.4.0-1+precise1 ... Architecture: amd64 ... Installed-Size: 3586 ... Pre-Depends: libc6 (>= 2.15) ... Depends: libpython3.4-minimal (= 3.4.0-1+precise1), libexpat1 (>= 1.95.8), libgcc1 (>= 1:4.1.1), zlib1g (>= 1:1.2.0), foo | bar ... Recommends: python3.4 ... Suggests: binfmt-support ... Conflicts: binfmt-support (<< 1.1.2) ... ''') Here are the control file fields as parsed by the :py:class:`debian.deb822` module: >>> print(repr(unparsed_fields)) {'Architecture': u'amd64', 'Conflicts': u'binfmt-support (<< 1.1.2)', 'Depends': u'libpython3.4-minimal (= 3.4.0-1+precise1), libexpat1 (>= 1.95.8), libgcc1 (>= 1:4.1.1), zlib1g (>= 1:1.2.0), foo | bar', 'Installed-Size': u'3586', 'Package': u'python3.4-minimal', 'Pre-Depends': u'libc6 (>= 2.15)', 'Recommends': u'python3.4', 'Suggests': u'binfmt-support', 'Version': u'3.4.0-1+precise1'} Notice the value of the `Depends` line is a comma separated string, i.e. it hasn't been parsed. Now here are the control file fields parsed by the :py:func:`parse_control_fields()` function: >>> from deb_pkg_tools.control import parse_control_fields >>> parsed_fields = parse_control_fields(unparsed_fields) >>> print(repr(parsed_fields)) {'Architecture': u'amd64', 'Conflicts': RelationshipSet(VersionedRelationship(name=u'binfmt-support', operator=u'<<', version=u'1.1.2')), 'Depends': RelationshipSet(VersionedRelationship(name=u'libpython3.4-minimal', operator=u'=', version=u'3.4.0-1+precise1'), VersionedRelationship(name=u'libexpat1', operator=u'>=', version=u'1.95.8'), VersionedRelationship(name=u'libgcc1', operator=u'>=', version=u'1:4.1.1'), VersionedRelationship(name=u'zlib1g', operator=u'>=', version=u'1:1.2.0'), AlternativeRelationship(Relationship(name=u'foo'), Relationship(name=u'bar'))), 'Installed-Size': 3586, 'Package': u'python3.4-minimal', 'Pre-Depends': RelationshipSet(VersionedRelationship(name=u'libc6', operator=u'>=', version=u'2.15')), 'Recommends': u'python3.4', 'Suggests': RelationshipSet(Relationship(name=u'binfmt-support')), 'Version': u'3.4.0-1+precise1'} For more information about fields like `Depends` and `Suggests` please refer to the documentation of :py:func:`.parse_depends()`. """ logger.debug("Parsing %i control fields ..", len(input_fields)) output_fields = {} for name, unparsed_value in input_fields.items(): name = normalize_control_field_name(name) if name in DEPENDS_LIKE_FIELDS: parsed_value = parse_depends(unparsed_value) elif name == "Installed-Size": parsed_value = int(unparsed_value) else: parsed_value = unparsed_value output_fields[name] = parsed_value logger.debug("Parsed fields: %s", output_fields) return output_fields