(requirements, on_failure="warn")
return depends
Suppose you can't confidently figure out a .deb which satisfies a given
    requirement.  If on_failure == 'warn', then log a warning.  If on_failure
    == 'raise' then raise CantSatisfyRequirement exception.  If on_failure ==
    'guess' then guess that python-$FOO will satisfy the dependency and that
    the Python version numbers will apply to the Debian packages (in addition
    to logging a warning message).
Suppose you can't confidently figure out a .deb which satisfies a given
    requirement.  If on_failure == 'warn', then log a warning.  If on_failure
    == 'raise' then raise CantSatisfyRequirement exception.  If on_failure ==
    'guess' then guess that python-$FOO will satisfy the dependency and that
    the Python version numbers will apply to the Debian packages (in addition
    to logging a warning message).
Suppose you can t confidently figure out a . deb which satisfies a given requirement . If on_failure == warn then log a warning . If on_failure == raise then raise CantSatisfyRequirement exception . If on_failure == guess then guess that python - $FOO will satisfy the dependency and that the Python version numbers will apply to the Debian packages ( in addition to logging a warning message ) .
def get_deb_depends_from_setuptools_requires(requirements, on_failure="warn"):
    """
    Suppose you can't confidently figure out a .deb which satisfies a given
    requirement.  If on_failure == 'warn', then log a warning.  If on_failure
    == 'raise' then raise CantSatisfyRequirement exception.  If on_failure ==
    'guess' then guess that python-$FOO will satisfy the dependency and that
    the Python version numbers will apply to the Debian packages (in addition
    to logging a warning message).
    """
    assert on_failure in ("raise", "warn", "guess"), on_failure

    import pkg_resources

    depends = []  # This will be the return value from this function.

    parsed_reqs = []

    for extra, reqs in pkg_resources.split_sections(requirements):
        if extra:
            continue
        parsed_reqs.extend(pkg_resources.parse_requirements(reqs))

    if not parsed_reqs:
        return depends

    if not os.path.exists('/usr/bin/apt-file'):
        raise ValueError('apt-file not in /usr/bin. Please install '
                         'with: sudo apt-get install apt-file')

    # Ask apt-file for any packages which have a .egg-info file by
    # these names.

    # Note that apt-file appears to think that some packages
    # e.g. setuptools itself have "foo.egg-info/BLAH" files but not a
    # "foo.egg-info" directory.

    egginfore = (r"(/(%s)(?:-[^/]+)?(?:-py[0-9]\.[0-9.]+)?\.egg-info)"
                 % '|'.join(
                     req.project_name.replace('-', '_')
                     for req in parsed_reqs))

    args = ["apt-file", "search", "--ignore-case", "--regexp", egginfore]

    if 1:
        # do dry run on apt-file
        dry_run_args = args[:] + ['--dummy', '--non-interactive']
        cmd = subprocess.Popen(dry_run_args, stderr=subprocess.PIPE)
        returncode = cmd.wait()
        if returncode:
            err_output = cmd.stderr.read()
            raise RuntimeError('Error running "apt-file search": ' +
                               err_output.strip())

    try:
        cmd = subprocess.Popen(args, stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE,
                               universal_newlines=True)
    except Exception as le:
        # TODO: catch rc=1 and "E: The cache directory is empty. You need to
        # run 'apt-file update' first.", and tell the user to follow those
        # instructions.
        log.error('ERROR running: %s', ' '.join(args))
        raise RuntimeError('exception %s from subprocess %s' % (le, args))
    returncode = cmd.wait()
    if returncode:
        log.error('ERROR running: %s', ' '.join(args))
        raise RuntimeError('returncode %d from subprocess %s' % (returncode,
                                                                 args))

    inlines = cmd.stdout.readlines()

    dd = {}  # {pydistname: {pydist: set(debpackagename)}}
    E = re.compile(egginfore, re.I)
    D = re.compile("^([^:]*):", re.I)
    eggsndebs = set()
    for line in inlines:
        if line:
            emo = E.search(line)
            assert emo, line
            dmo = D.search(line)
            assert dmo, line
            eggsndebs.add((emo.group(1), dmo.group(1)))

    for (egginfo, debname) in eggsndebs:
        pydist = pkg_resources.Distribution.from_filename(egginfo)
        try:
            dd.setdefault(
                pydist.project_name.lower(), {}).setdefault(
                pydist, set()).add(debname)
        except ValueError as le:
            log.warn("I got an error parsing a .egg-info file named \"%s\" "
                     "from Debian package \"%s\" as a pkg_resources "
                     "Distribution: %s" % (egginfo, debname, le,))
            pass

    # Now for each requirement, see if a Debian package satisfies it.
    ops = {'<': '<<', '>': '>>', '==': '=', '<=': '<=', '>=': '>='}
    for req in parsed_reqs:
        reqname = req.project_name.lower()
        gooddebs = set()
        for pydist, debs in dd.get(reqname, {}).iteritems():
            if pydist in req:
                # log.info("I found Debian packages \"%s\" which provides "
                #          "Python package \"%s\", version \"%s\", which "
                #          "satisfies our version requirements: \"%s\""
                #          % (', '.join(debs), req.project_name, ver, req)
                gooddebs |= (debs)
            else:
                log.info("I found Debian packages \"%s\" which provides "
                         "Python package \"%s\" which "
                         "does not satisfy our version requirements: "
                         "\"%s\" -- ignoring."
                         % (', '.join(debs), req.project_name, req))
        if not gooddebs:
            if on_failure == 'warn':
                log.warn(
                    "I found no Debian package which provides the required "
                    "Python package \"%s\" with version requirements "
                    "\"%s\"." % (req.project_name, req.specs))
            elif on_failure == "raise":
                raise CantSatisfyRequirement(
                    "I found no Debian package which provides the required "
                    "Python package \"%s\" with version requirements "
                    "\"%s\"." % (req.project_name, req.specs), req)
            elif on_failure == "guess":
                log.warn("I found no Debian package which provides the "
                         "required Python package \"%s\" with version "
                         "requirements \"%s\".  Guessing blindly that the "
                         "name \"python-%s\" will be it, and that the Python "
                         "package version number requirements will apply to "
                         "the Debian package." % (req.project_name,
                                                  req.specs, reqname))
                gooddebs.add("python-" + reqname)
        elif len(gooddebs) == 1:
            log.info("I found a Debian package which provides the require "
                     "Python package.  Python package: \"%s\", "
                     "Debian package: \"%s\";  adding Depends specifications "
                     "for the following version(s): \"%s\""
                     % (req.project_name, tuple(gooddebs)[0], req.specs))
        else:
            log.warn("I found multiple Debian packages which provide the "
                     "Python distribution required.  I'm listing them all "
                     "as alternates.  Candidate debs which claim to provide "
                     "the Python package \"%s\" are: \"%s\""
                     % (req.project_name, ', '.join(gooddebs),))

        alts = []
        for deb in gooddebs:
            added_any_alt = False
            for spec in req.specs:
                # Here we blithely assume that the Debian package
                # versions are enough like the Python package versions
                # that the requirement can be ported straight over...
                alts.append("%s (%s %s)" % (deb, ops[spec[0]], spec[1]))
                added_any_alt = True

            if not added_any_alt:
                # No alternates were added, but we have the name of a
                # good package.
                alts.append("%s" % deb)

        if len(alts):
            depends.append(' | '.join(alts))

    return depends
def get_deb_depends_from_setuptools_requires ( requirements , on_failure = "warn" ) : assert on_failure in ( "raise" , "warn" , "guess" ) , on_failure import pkg_resources depends = [ ] # This will be the return value from this function. parsed_reqs = [ ] for extra , reqs in pkg_resources . split_sections ( requirements ) : if extra : continue parsed_reqs . extend ( pkg_resources . parse_requirements ( reqs ) ) if not parsed_reqs : return depends if not os . path . exists ( '/usr/bin/apt-file' ) : raise ValueError ( 'apt-file not in /usr/bin. Please install ' 'with: sudo apt-get install apt-file' ) # Ask apt-file for any packages which have a .egg-info file by # these names. # Note that apt-file appears to think that some packages # e.g. setuptools itself have "foo.egg-info/BLAH" files but not a # "foo.egg-info" directory. egginfore = ( r"(/(%s)(?:-[^/]+)?(?:-py[0-9]\.[0-9.]+)?\.egg-info)" % '|' . join ( req . project_name . replace ( '-' , '_' ) for req in parsed_reqs ) ) args = [ "apt-file" , "search" , "--ignore-case" , "--regexp" , egginfore ] if 1 : # do dry run on apt-file dry_run_args = args [ : ] + [ '--dummy' , '--non-interactive' ] cmd = subprocess . Popen ( dry_run_args , stderr = subprocess . PIPE ) returncode = cmd . wait ( ) if returncode : err_output = cmd . stderr . read ( ) raise RuntimeError ( 'Error running "apt-file search": ' + err_output . strip ( ) ) try : cmd = subprocess . Popen ( args , stdin = subprocess . PIPE , stdout = subprocess . PIPE , universal_newlines = True ) except Exception as le : # TODO: catch rc=1 and "E: The cache directory is empty. You need to # run 'apt-file update' first.", and tell the user to follow those # instructions. log . error ( 'ERROR running: %s' , ' ' . join ( args ) ) raise RuntimeError ( 'exception %s from subprocess %s' % ( le , args ) ) returncode = cmd . wait ( ) if returncode : log . error ( 'ERROR running: %s' , ' ' . join ( args ) ) raise RuntimeError ( 'returncode %d from subprocess %s' % ( returncode , args ) ) inlines = cmd . stdout . readlines ( ) dd = { } # {pydistname: {pydist: set(debpackagename)}} E = re . compile ( egginfore , re . I ) D = re . compile ( "^([^:]*):" , re . I ) eggsndebs = set ( ) for line in inlines : if line : emo = E . search ( line ) assert emo , line dmo = D . search ( line ) assert dmo , line eggsndebs . add ( ( emo . group ( 1 ) , dmo . group ( 1 ) ) ) for ( egginfo , debname ) in eggsndebs : pydist = pkg_resources . Distribution . from_filename ( egginfo ) try : dd . setdefault ( pydist . project_name . lower ( ) , { } ) . setdefault ( pydist , set ( ) ) . add ( debname ) except ValueError as le : log . warn ( "I got an error parsing a .egg-info file named \"%s\" " "from Debian package \"%s\" as a pkg_resources " "Distribution: %s" % ( egginfo , debname , le , ) ) pass # Now for each requirement, see if a Debian package satisfies it. ops = { '<' : '<<' , '>' : '>>' , '==' : '=' , '<=' : '<=' , '>=' : '>=' } for req in parsed_reqs : reqname = req . project_name . lower ( ) gooddebs = set ( ) for pydist , debs in dd . get ( reqname , { } ) . iteritems ( ) : if pydist in req : # log.info("I found Debian packages \"%s\" which provides " #          "Python package \"%s\", version \"%s\", which " #          "satisfies our version requirements: \"%s\"" #          % (', '.join(debs), req.project_name, ver, req) gooddebs |= ( debs ) else : log . info ( "I found Debian packages \"%s\" which provides " "Python package \"%s\" which " "does not satisfy our version requirements: " "\"%s\" -- ignoring." % ( ', ' . join ( debs ) , req . project_name , req ) ) if not gooddebs : if on_failure == 'warn' : log . warn ( "I found no Debian package which provides the required " "Python package \"%s\" with version requirements " "\"%s\"." % ( req . project_name , req . specs ) ) elif on_failure == "raise" : raise CantSatisfyRequirement ( "I found no Debian package which provides the required " "Python package \"%s\" with version requirements " "\"%s\"." % ( req . project_name , req . specs ) , req ) elif on_failure == "guess" : log . warn ( "I found no Debian package which provides the " "required Python package \"%s\" with version " "requirements \"%s\".  Guessing blindly that the " "name \"python-%s\" will be it, and that the Python " "package version number requirements will apply to " "the Debian package." % ( req . project_name , req . specs , reqname ) ) gooddebs . add ( "python-" + reqname ) elif len ( gooddebs ) == 1 : log . info ( "I found a Debian package which provides the require " "Python package.  Python package: \"%s\", " "Debian package: \"%s\";  adding Depends specifications " "for the following version(s): \"%s\"" % ( req . project_name , tuple ( gooddebs ) [ 0 ] , req . specs ) ) else : log . warn ( "I found multiple Debian packages which provide the " "Python distribution required.  I'm listing them all " "as alternates.  Candidate debs which claim to provide " "the Python package \"%s\" are: \"%s\"" % ( req . project_name , ', ' . join ( gooddebs ) , ) ) alts = [ ] for deb in gooddebs : added_any_alt = False for spec in req . specs : # Here we blithely assume that the Debian package # versions are enough like the Python package versions # that the requirement can be ported straight over... alts . append ( "%s (%s %s)" % ( deb , ops [ spec [ 0 ] ] , spec [ 1 ] ) ) added_any_alt = True if not added_any_alt : # No alternates were added, but we have the name of a # good package. alts . append ( "%s" % deb ) if len ( alts ) : depends . append ( ' | ' . join ( alts ) ) return depends