def test_dpkg(self): self.set_dpkg_retval(b"python3-barfoo: /usr/lib/python3/dist-packages/foobar-2.3.0.egg-info") dep = pydist.guess_dependency('cpython3', 'foobar') self.assertEqual(dep, 'python3-barfoo') self.popen.assert_called_once_with( "/usr/bin/dpkg -S *python3/*/[Ff][Oo][Oo][Bb][Aa][Rr]-?*\\.egg-info", shell=True, stdout=PIPE, stderr=PIPE)
def test_dpkg(self): self.set_dpkg_retval( b"python3-barfoo: /usr/lib/python3/dist-packages/foobar-2.3.0.egg-info" ) dep = pydist.guess_dependency('cpython3', 'foobar') self.assertEqual(dep, 'python3-barfoo') self.popen.assert_called_once_with( "/usr/bin/dpkg -S *python3/*/[Ff][Oo][Oo][Bb][Aa][Rr]-?*\\.egg-info", shell=True, stdout=PIPE, stderr=PIPE)
def parse(self, stats, options): log.debug('generating dependencies for package %s', self.package) tpl = self.ipkg_tpl vtpl = self.ipkg_vtpl tpl_ma = self.ipkg_tpl_ma vtpl_ma = self.ipkg_vtpl_ma vrange = options.vrange if vrange and any( (stats['compile'], stats['public_vers'], stats['ext_vers'], stats['ext_no_version'], stats['shebangs'])): if any( (stats['compile'], stats['public_vers'], stats['shebangs'])): tpl_tmp = tpl_ma else: tpl_tmp = tpl minv = vrange.minver # note it's an open interval (i.e. do not add 1 here!): maxv = vrange.maxver if minv == maxv: self.depend(vtpl % minv) minv = maxv = None if minv: self.depend("%s (>= %s~)" % (tpl_tmp, minv)) if maxv: self.depend("%s (<< %s)" % (tpl_tmp, maxv)) if self.impl == 'cpython2' and stats['public_vers']: # additional Depends to block python package transitions sorted_vers = sorted(stats['public_vers']) minv = sorted_vers[0] maxv = sorted_vers[-1] if minv <= default(self.impl): self.depend("%s (>= %s~)" % (tpl_ma, minv)) if maxv >= default(self.impl): self.depend("%s (<< %s)" % (tpl_ma, maxv + 1)) if self.impl == 'pypy' and stats.get('ext_soabi'): # TODO: make sure alternative is used only for the same extension names # ie. for foo.ABI1.so, foo.ABI2.so, bar.ABI3,so, bar.ABI4.so generate: # pypy-abi-ABI1 | pypy-abi-ABI2, pypy-abi-ABI3 | pypy-abi-ABI4 self.depend('|'.join( soabi.replace('-', '-abi-') for soabi in sorted(stats['ext_soabi']))) if stats['ext_vers']: # TODO: what about extensions with stable ABI? sorted_vers = sorted(stats['ext_vers']) minv = sorted_vers[0] maxv = sorted_vers[-1] #self.depend('|'.join(vtpl % i for i in stats['ext_vers'])) if minv <= default(self.impl): self.depend("%s (>= %s~)" % (tpl, minv)) if maxv >= default(self.impl): self.depend("%s (<< %s)" % (tpl, maxv + 1)) # make sure py{,3}compile binary is available if stats['compile'] and self.impl in MINPYCDEP: self.depend(MINPYCDEP[self.impl]) for ipreter in stats['shebangs']: self.depend("%s%s" % (ipreter, '' if self.impl == 'pypy' else ':any')) supported_versions = supported(self.impl) default_version = default(self.impl) for private_dir, details in stats['private_dirs'].items(): versions = list(i.version for i in details.get('shebangs', []) if i.version and i.version.minor) for v in versions: if v in supported_versions: self.depend(vtpl_ma % v) else: log.info( 'dependency on %s (from shebang) ignored' ' - it\'s not supported anymore', vtpl % v) # /usr/bin/python{,3} shebang → add python{,3} to Depends if any(True for i in details.get('shebangs', []) if i.version is None): self.depend(tpl_ma) extensions = sorted(details.get('ext_vers', set())) #self.depend('|'.join(vtpl % i for i in extensions)) if extensions: self.depend("%s (>= %s~)" % (tpl, extensions[0])) self.depend("%s (<< %s)" % (tpl, extensions[-1] + 1)) elif details.get('ext_no_version'): # assume unrecognized extension was built for default interpreter version self.depend("%s (>= %s~)" % (tpl, default_version)) self.depend("%s (<< %s)" % (tpl, default_version + 1)) if details.get('compile'): if self.impl in MINPYCDEP: self.depend(MINPYCDEP[self.impl]) args = '' if extensions: args += "-V %s" % VersionRange(minver=extensions[0], maxver=extensions[-1]) elif len(versions) == 1: # only one version from shebang #if versions[0] in supported_versions: args += "-V %s" % versions[0] # ... otherwise compile with default version elif details.get('ext_no_version'): # assume unrecognized extension was built for default interpreter version args += "-V %s" % default_version elif vrange: args += "-V %s" % vrange if vrange.minver == vrange.maxver: self.depend(vtpl % vrange.minver) else: if vrange.minver: # minimum version specified self.depend("%s (>= %s~)" % (tpl_ma, vrange.minver)) if vrange.maxver: # maximum version specified self.depend("%s (<< %s)" % (tpl_ma, vrange.maxver + 1)) for pattern in options.regexpr or []: args += " -X '%s'" % pattern.replace("'", r"'\''") self.rtscript((private_dir, args)) if options.guess_deps: for fn in stats['requires.txt']: # TODO: should options.recommends and options.suggests be # removed from requires.txt? for i in parse_pydep(self.impl, fn): self.depend(i) for fpath in stats['egg-info']: with open(fpath, 'r', encoding='utf-8') as fp: for line in fp: if line.startswith('Requires: '): req = line[10:].strip() self.depend(guess_dependency(self.impl, req)) # add dependencies from --depends for item in options.depends or []: self.depend(guess_dependency(self.impl, item)) # add dependencies from --recommends for item in options.recommends or []: self.recommend(guess_dependency(self.impl, item)) # add dependencies from --suggests for item in options.suggests or []: self.suggest(guess_dependency(self.impl, item)) # add dependencies from --requires for fn in options.requires or []: if not exists(fn): log.warn('cannot find requirements file: %s', fn) continue for i in parse_pydep(self.impl, fn): self.depend(i) log.debug(self)
def test_sensible_guess(self): dep = pydist.guess_dependency('cpython3', 'foobar') self.assertEqual(dep, 'python3-foobar')
def control(dpath, ctx, env): desc = [] code_line = False first_line = True for line in ctx['description'].split('\n'): if first_line: if line.lower() == ctx['name'].lower(): continue if not line.strip().replace('=', '').replace('-', '').replace( '~', ''): continue first_line = False if not line.strip(): desc.append(' .') else: if line.startswith(('* ', '>>> ', '... ', '.. ', '$ ')) \ or code_line or line == '...': if line.startswith('>>> '): # next line should get extra space char as well code_line = True elif code_line: code_line = False line = ' ' + line elif line.strip().lower() in DESC_STOP_KEYWORDS: break line = line.replace('\t', ' ') desc.append(' ' + line) for key, value in { 'short_desc': ctx['summary'][:80], 'long_desc': '\n'.join(desc), }.items(): if key not in ctx: ctx[key] = value req = set() if 'requires' in ctx: for impl in ctx['interpreters']: impl = INTERPRETER_MAP.get(impl, impl) try: dependency = guess_dependency(impl, line) if dependency: ctx['build_depends'].add(dependency) except Exception as err: log.warn('cannot parse build dependency: %s', err) else: for i in listdir(dpath): if i.endswith('.egg-info') and exists( join(dpath, i, 'requires.txt')): req.add(join(dpath, i, 'requires.txt')) if i == 'requirements.txt': req.add(join(dpath, 'requirements.txt')) for fpath in req: for impl in ctx['interpreters']: impl = INTERPRETER_MAP.get(impl, impl) try: for i in parse_pydep(impl, fpath)['depends']: ctx['build_depends'].add(i) except Exception as err: log.warn('cannot parse build dependency: %s', err) if exists(join(dpath, 'setup.py')): with open(join(dpath, 'setup.py')) as fp: for line in fp: if line.startswith('#'): continue if 'setuptools' in line: for interpreter in ctx['interpreters']: ctx['build_depends'].add( '{}-setuptools'.format(interpreter)) if 'python' in ctx['interpreters']: ctx['build_depends'].add( 'python-all%s' % ('-dev' if ctx['binary_arch'] == 'any' else '')) if 'python3' in ctx['interpreters']: ctx['build_depends'].add( 'python3-all%s' % ('-dev' if ctx['binary_arch'] == 'any' else '')) if 'pypy' in ctx['interpreters']: ctx['build_depends'].add('pypy') return ctx
def parse(self, stats, options): log.debug('generating dependencies for package %s', self.package) tpl = self.ipkg_tpl vtpl = self.ipkg_vtpl tpl_ma = self.ipkg_tpl_ma vtpl_ma = self.ipkg_vtpl_ma vrange = options.vrange if vrange and any((stats['compile'], stats['public_vers'], stats['ext_vers'], stats['ext_no_version'], stats['shebangs'])): if any((stats['compile'], stats['public_vers'], stats['shebangs'])): tpl_tmp = tpl_ma else: tpl_tmp = tpl minv = vrange.minver # note it's an open interval (i.e. do not add 1 here!): maxv = vrange.maxver if minv == maxv: self.depend(vtpl % minv) minv = maxv = None if minv: self.depend("%s (>= %s~)" % (tpl_tmp, minv)) if maxv: self.depend("%s (<< %s)" % (tpl_tmp, maxv)) if self.impl == 'cpython2' and stats['public_vers']: # additional Depends to block python package transitions sorted_vers = sorted(stats['public_vers']) minv = sorted_vers[0] maxv = sorted_vers[-1] if minv <= default(self.impl): self.depend("%s (>= %s~)" % (tpl_ma, minv)) if maxv >= default(self.impl): self.depend("%s (<< %s)" % (tpl_ma, maxv + 1)) if self.impl == 'pypy' and stats.get('ext_soabi'): # TODO: make sure alternative is used only for the same extension names # ie. for foo.ABI1.so, foo.ABI2.so, bar.ABI3,so, bar.ABI4.so generate: # pypy-abi-ABI1 | pypy-abi-ABI2, pypy-abi-ABI3 | pypy-abi-ABI4 self.depend('|'.join(soabi.replace('-', '-abi-') for soabi in sorted(stats['ext_soabi']))) if stats['ext_vers']: # TODO: what about extensions with stable ABI? sorted_vers = sorted(stats['ext_vers']) minv = sorted_vers[0] maxv = sorted_vers[-1] #self.depend('|'.join(vtpl % i for i in stats['ext_vers'])) if minv <= default(self.impl): self.depend("%s (>= %s~)" % (tpl, minv)) if maxv >= default(self.impl): self.depend("%s (<< %s)" % (tpl, maxv + 1)) # make sure py{,3}compile binary is available if stats['compile'] and self.impl in MINPYCDEP: self.depend(MINPYCDEP[self.impl]) for ipreter in stats['shebangs']: self.depend("%s%s" % (ipreter, '' if self.impl == 'pypy' else ':any')) supported_versions = supported(self.impl) default_version = default(self.impl) for private_dir, details in stats['private_dirs'].items(): versions = list(i.version for i in details.get('shebangs', []) if i.version and i.version.minor) for v in versions: if v in supported_versions: self.depend(vtpl_ma % v) else: log.info('dependency on %s (from shebang) ignored' ' - it\'s not supported anymore', vtpl % v) # /usr/bin/python{,3} shebang → add python{,3} to Depends if any(True for i in details.get('shebangs', []) if i.version is None): self.depend(tpl_ma) extensions = False if self.python_dev_in_bd: extensions = sorted(details.get('ext_vers', set())) #self.depend('|'.join(vtpl % i for i in extensions)) if extensions: self.depend("%s (>= %s~)" % (tpl, extensions[0])) self.depend("%s (<< %s)" % (tpl, extensions[-1] + 1)) elif details.get('ext_no_version'): # assume unrecognized extension was built for default interpreter version self.depend("%s (>= %s~)" % (tpl, default_version)) self.depend("%s (<< %s)" % (tpl, default_version + 1)) if details.get('compile'): if self.impl in MINPYCDEP: self.depend(MINPYCDEP[self.impl]) args = '' if extensions: args += "-V %s" % VersionRange(minver=extensions[0], maxver=extensions[-1]) elif len(versions) == 1: # only one version from shebang #if versions[0] in supported_versions: args += "-V %s" % versions[0] # ... otherwise compile with default version elif details.get('ext_no_version'): # assume unrecognized extension was built for default interpreter version args += "-V %s" % default_version elif vrange: args += "-V %s" % vrange if vrange.minver == vrange.maxver: self.depend(vtpl % vrange.minver) else: if vrange.minver: # minimum version specified self.depend("%s (>= %s~)" % (tpl_ma, vrange.minver)) if vrange.maxver: # maximum version specified self.depend("%s (<< %s)" % (tpl_ma, vrange.maxver + 1)) for pattern in options.regexpr or []: args += " -X '%s'" % pattern.replace("'", r"'\''") self.rtscript((private_dir, args)) section_options = { 'depends_sec': options.depends_section, 'recommends_sec': options.recommends_section, 'suggests_sec': options.suggests_section, } if options.guess_deps: for fn in stats['requires.txt']: # TODO: should options.recommends and options.suggests be # removed from requires.txt? deps = parse_pydep(self.impl, fn, bdep=self.bdep, **section_options) [self.depend(i) for i in deps['depends']] [self.recommend(i) for i in deps['recommends']] [self.suggest(i) for i in deps['suggests']] for fpath in stats['egg-info']: with open(fpath, 'r', encoding='utf-8') as fp: for line in fp: if line.startswith('Requires: '): req = line[10:].strip() self.depend(guess_dependency(self.impl, req, bdep=self.bdep)) # add dependencies from --depends for item in options.depends or []: self.depend(guess_dependency(self.impl, item, bdep=self.bdep)) # add dependencies from --recommends for item in options.recommends or []: self.recommend(guess_dependency(self.impl, item, bdep=self.bdep)) # add dependencies from --suggests for item in options.suggests or []: self.suggest(guess_dependency(self.impl, item, bdep=self.bdep)) # add dependencies from --requires for fn in options.requires or []: fpath = join('debian', self.package, fn) if not exists(fpath): fpath = fn if not exists(fpath): log.warn('cannot find requirements file: %s', fn) continue deps = parse_pydep(self.impl, fpath, bdep=self.bdep, **section_options) [self.depend(i) for i in deps['depends']] [self.recommend(i) for i in deps['recommends']] [self.suggest(i) for i in deps['suggests']] log.debug(self)
def control(dpath, ctx, env): desc = [] code_line = False for line in ctx['description'].split('\n'): if not line.strip(): desc.append(' .') else: if line.startswith(('* ', '>>> ', '... ', '.. ', '$ ')) \ or code_line or line == '...': if line.startswith('>>> '): # next line should get extra space char as well code_line = True elif code_line: code_line = False line = ' ' + line line = line.replace('\t', ' ') desc.append(' ' + line) for key, value in { 'short_desc': ctx['summary'][:80], 'long_desc': '\n'.join(desc), }.items(): if key not in ctx: ctx[key] = value req = set() if 'requires' in ctx: for impl in ctx['interpreters']: impl = INTERPRETER_MAP.get(impl, impl) try: dependency = guess_dependency(impl, line) if dependency: ctx['build_depends'].add(dependency) except Exception as err: log.warn('cannot parse build dependency: %s', err) else: for i in listdir(dpath): if i.endswith('.egg-info') and exists(join(dpath, i, 'requires.txt')): req.add(join(dpath, i, 'requires.txt')) if i == 'requirements.txt': req.add(join(dpath, 'requirements.txt')) for fpath in req: for impl in ctx['interpreters']: impl = INTERPRETER_MAP.get(impl, impl) try: for i in parse_pydep(impl, fpath): ctx['build_depends'].add(i) except Exception as err: log.warn('cannot parse build dependency: %s', err) if exists(join(dpath, 'setup.py')): with open(join(dpath, 'setup.py')) as fp: for line in fp: if line.startswith('#'): continue if 'setuptools' in line: for interpreter in ctx['interpreters']: ctx['build_depends'].add('{}-setuptools'.format(interpreter)) if 'python' in ctx['interpreters']: ctx['build_depends'].add( 'python-all%s' % ('-dev' if ctx['binary_arch'] == 'any' else '')) if 'python3' in ctx['interpreters']: ctx['build_depends'].add( 'python3-all%s' % ('-dev' if ctx['binary_arch'] == 'any' else '')) if 'pypy' in ctx['interpreters']: ctx['build_depends'].add('pypy') return ctx
def parse(self, stats, options): log.debug("generating dependencies for package %s", self.package) tpl = self.ipkg_tpl vtpl = self.ipkg_vtpl tpl_ma = self.ipkg_tpl_ma vtpl_ma = self.ipkg_vtpl_ma vrange = options.vrange if vrange and any( (stats["compile"], stats["public_vers"], stats["ext_vers"], stats["ext_no_version"], stats["shebangs"]) ): if any((stats["compile"], stats["public_vers"], stats["shebangs"])): tpl_tmp = tpl_ma else: tpl_tmp = tpl minv = vrange.minver # note it's an open interval (i.e. do not add 1 here!): maxv = vrange.maxver if minv == maxv: self.depend(vtpl % minv) minv = maxv = None if minv: self.depend("%s (>= %s~)" % (tpl_tmp, minv)) if maxv: self.depend("%s (<< %s)" % (tpl_tmp, maxv)) if self.impl == "cpython2" and stats["public_vers"]: # additional Depends to block python package transitions sorted_vers = sorted(stats["public_vers"]) minv = sorted_vers[0] maxv = sorted_vers[-1] if minv <= default(self.impl): self.depend("%s (>= %s~)" % (tpl_ma, minv)) if maxv >= default(self.impl): self.depend("%s (<< %s)" % (tpl_ma, maxv + 1)) if stats["ext_vers"]: # TODO: what about extensions with stable ABI? sorted_vers = sorted(stats["ext_vers"]) minv = sorted_vers[0] maxv = sorted_vers[-1] # self.depend('|'.join(vtpl % i for i in stats['ext_vers'])) if minv <= default(self.impl): self.depend("%s (>= %s~)" % (tpl, minv)) if maxv >= default(self.impl): self.depend("%s (<< %s)" % (tpl, maxv + 1)) # make sure py{,3}compile binary is available if stats["compile"] and self.impl in MINPYCDEP: self.depend(MINPYCDEP[self.impl]) for ipreter in stats["shebangs"]: self.depend("%s%s" % (ipreter, ":any" if self.impl == "pypy" else "")) supported_versions = supported(self.impl) default_version = default(self.impl) for private_dir, details in stats["private_dirs"].items(): versions = list(i.version for i in details.get("shebangs", []) if i.version and i.version.minor) for v in versions: if v in supported_versions: self.depend(vtpl_ma % v) else: log.info("dependency on %s (from shebang) ignored" " - it's not supported anymore", vtpl % v) # /usr/bin/python{,3} shebang → add python{,3} to Depends if any(True for i in details.get("shebangs", []) if i.version is None): self.depend(tpl_ma) extensions = sorted(details.get("ext_vers", set())) # self.depend('|'.join(vtpl % i for i in extensions)) if extensions: self.depend("%s (>= %s~)" % (tpl, extensions[0])) self.depend("%s (<< %s)" % (tpl, extensions[-1] + 1)) elif details.get("ext_no_version"): # assume unrecognized extension was built for default interpreter version self.depend("%s (>= %s~)" % (tpl, default_version)) self.depend("%s (<< %s)" % (tpl, default_version + 1)) if details.get("compile"): if self.impl in MINPYCDEP: self.depend(MINPYCDEP[self.impl]) args = "" if extensions: args += "-V %s" % VersionRange(minver=extensions[0], maxver=extensions[-1]) elif len(versions) == 1: # only one version from shebang # if versions[0] in supported_versions: args += "-V %s" % versions[0] # ... otherwise compile with default version elif details.get("ext_no_version"): # assume unrecognized extension was built for default interpreter version args += "-V %s" % default_version elif vrange: args += "-V %s" % vrange if vrange.minver == vrange.maxver: self.depend(vtpl % vrange.minver) else: if vrange.minver: # minimum version specified self.depend("%s (>= %s~)" % (tpl_ma, vrange.minver)) if vrange.maxver: # maximum version specified self.depend("%s (<< %s)" % (tpl_ma, vrange.maxver + 1)) for pattern in options.regexpr or []: args += " -X '%s'" % pattern.replace("'", r"'\''") self.rtscript((private_dir, args)) if options.guess_deps: for fn in stats["requires.txt"]: # TODO: should options.recommends and options.suggests be # removed from requires.txt? for i in parse_pydep(self.impl, fn): self.depend(i) # add dependencies from --depends for item in options.depends or []: self.depend(guess_dependency(self.impl, item)) # add dependencies from --recommends for item in options.recommends or []: self.recommend(guess_dependency(self.impl, item)) # add dependencies from --suggests for item in options.suggests or []: self.suggest(guess_dependency(self.impl, item)) # add dependencies from --requires for fn in options.requires or []: if not exists(fn): log.warn("cannot find requirements file: %s", fn) continue for i in parse_pydep(self.impl, fn): self.depend(i) log.debug(self)