Story #2637


As a user, I'm able to import Debian packages with their dependencies represented correctly.

Added by over 7 years ago. Updated over 5 years ago.

Start date:
Due date:
% Done:


Estimated time:
Platform Release:
Target Release - Debian:
Sprint Candidate:
Pulp 2



Debian dependency relationships should be represented as fields on the unit model, so that a developer could resolve dependencies simply by querying the available units, without having to download the package.

This story does not imply such a dependency resolution would be implemented anywhere in the plugin - only that we need to make the data available for other potential consumers.


Pulp currently represents Requires and Provides for rpm packages as lists of dictionaries:

:ivar provides: List of packages/libraries this package provides. Each entry is a
dictionary with the "release", "epoch", "version", "flags", and "name"
:type provides: list of dict

:ivar requires: List of packages/libraries this package requires. Each entry is a dictionary
with the "release", "epoch", "version", "flags", and "name" field.
:type requires: list of dict

flags is one of None, EQ, LT, LE, GT, GE.

Debian package dependencies are "different". They support version comparisons, just like rpm packages. In addition to that, they support architecture matching (positive or negative), build profile matching (positive or negative). They also support disjunction (I want python2 or python3).

The python-debian library has a deb822 module that will parse these dependencies and return some format that I don't want to store directly in pulp, because it's far too distant from what we have in rpm.

Here is the documentation from deb822:

def relations(self):
"""Return a dictionary of inter-package relationships among the current
and other packages.

Dictionary keys depend on the package kind. Binary packages have keys
like 'depends', 'recommends', ... while source packages have keys like
'build-depends', 'build-depends-indep' and so on. See the Debian policy
for the comprehensive field list.

Dictionary values are package relationships returned as lists of lists
of dictionaries (see below for some examples).
The encoding of package relationships is as follows:
- the top-level lists corresponds to the comma-separated list of
Deb822, their components form a conjunction, i.e. they have to be
AND-ed together
- the inner lists corresponds to the pipe-separated list of Deb822,
their components form a disjunction, i.e. they have to be OR-ed
- member of the inner lists are dictionaries with the following keys:
- name: package (or virtual package) name
- version: A pair <operator, version> if the relationship is
versioned, None otherwise. operator is one of "<<",
"<=", "=", ">=", ">>"; version is the given version as
a string.
- arch: A list of pairs <enabled, arch> if the
relationship is architecture specific, None otherwise.
Enabled is a boolean (false if the architecture is
negated with "!", true otherwise), arch the
Debian architecture name as a string.
- restrictions: A list of lists of tuples <enabled, profile>
if there is a restriction formula defined, None
otherwise. Each list of tuples represents a restriction
list while each tuple represents an individual term
within the restriction list. Enabled is a boolean
(false if the restriction is negated with "!", true
otherwise). The profile is the name of the build

The arch and restrictions tuples are available as named tuples so
elements are available as term[0] or alternatively as
term.enabled (and so forth).


"emacs | emacsen, make, debianutils (>= 1.7)" becomes
[ [ {'name': 'emacs'}, {'name': 'emacsen'} ],
[ {'name': 'make'} ],
[ {'name': 'debianutils', 'version': ('>=', '1.7')} ] ]

"tcl8.4-dev, procps [!hurd-i386]" becomes
[ [ {'name': 'tcl8.4-dev'} ],
[ {'name': 'procps', 'arch': (false, 'hurd-i386')} ] ]

"texlive <!cross>" becomes
[ [ {'name': 'texlive',
'restriction': [[(false, 'cross')]]} ] ]

I personally don't like the list of (disjunction of lists) representation, given that it's a relatively uncommon case. Also, I don't like breaking out the arch and profile as tuples just to represent positive or negative matches.

My proposal is to reuse as much as possible from Yum's syntax. Like so:

  • single package is a dictionary, conjunction is a list of packages (i.e. dictionaries).
  • flag represents the operator of the relationship if one exists, and use the same string values as rpm: GT, GE, LT, LE, EQ. arch
  • arch is a list of one or more strings. Negation is represented with a leading exclamation mark
  • restriction, if present, is a list of one or more strings, with the same meaning as arch.

So, for the examples in the deb822 package:

"emacs | emacsen, make, debianutils (>= 1.7)"
deb822: [ [ {'name': 'emacs'}, {'name': 'emacsen'} ], [ {'name': 'make'} ], [ {'name': 'debianutils', 'version': ('>=', '1.7')} ] ]
pulp: [ [{'name': 'emacs'}, {'name': 'emacsen'} ], {'name': 'make'}, {'name': 'debianutils', 'version': '1.7', 'flag': 'GE'} ]
"tcl8.4-dev, procps [!hurd-i386]"
deb822: [ [ {'name': 'tcl8.4-dev'} ], [ {'name': 'procps', 'arch': (false, 'hurd-i386')} ] ]
pulp: [ {'name': 'tcl8.4-dev'}, {'name': 'procps', 'arch': ['!hurd-i383']} ]
"texlive <!cross>"
deb822: [ [ {'name': 'texlive', 'restriction': [[(false, 'cross')]]} ] ]
pulp: [ {'name': 'texlive', 'restriction': ['!cross']} ]

Acceptance Criteria

  • PASS: import debian package with some dependency data. pulp-admin should display the dependency data.

Also available in: Atom PDF