def test_raise(self): with pytest.raises(exc.CheckbValueError): assert python_utils.iterable([1, 2], 1) with pytest.raises(exc.CheckbValueError): assert python_utils.iterable([1, 2], 'a') with pytest.raises(exc.CheckbValueError): assert python_utils.iterable([1, 2], []) X = type('X', (object, ), {}) with pytest.raises(exc.CheckbValueError): assert python_utils.iterable([1, 2], X())
def test_iterable(self): assert python_utils.iterable([1, 2]) assert python_utils.iterable(['a', 'b']) assert python_utils.iterable(('foo', )) assert python_utils.iterable({'foo', 'bar', 'baz'}) assert python_utils.iterable(set()) assert python_utils.iterable(()) assert python_utils.iterable([u'foo', u'bar']) assert python_utils.iterable({'a': 1, 'b': 2})
def test_iterable_items(self): assert python_utils.iterable([1, 2], int) assert not python_utils.iterable([1, 2], float) assert not python_utils.iterable([1, 2.2], float) assert python_utils.iterable(['a', 'b'], str) assert python_utils.iterable(['a', 'b'], basestring) try: assert not python_utils.iterable(['a', 'b'], unicode) except NameError: # Python 3 no such thing as unicode, str already tested pass assert python_utils.iterable([[], []], list) # empty classes X = type('X', (object, ), {}) Y = type('Y', (X, ), {}) assert python_utils.iterable((X(), X()), X) assert python_utils.iterable((X(), Y()), X) assert not python_utils.iterable((X(), Y()), Y)
def test_not_iterable(self): assert not python_utils.iterable('a') assert not python_utils.iterable(u'a') try: assert not python_utils.iterable(unicode('foo')) except NameError: # Python 3 no such thing as unicode, str already tested pass assert not python_utils.iterable(3) assert not python_utils.iterable(3.14) assert not python_utils.iterable(None) assert not python_utils.iterable(object())
def build2update(self, builds, strict=False): '''Find matching Bodhi updates for provided builds. :param builds: builds to search for in N(E)VR format (``foo-1.2-3.fc20`` or ``foo-4:1.2-3.fc20``) :type builds: iterable of str :param bool strict: if ``False``, incomplete Bodhi updates are allowed. If ``True``, every Bodhi update will be compared with the set of provided builds. If there is an Bodhi update which contains builds not provided in ``builds``, that update is marked as incomplete and removed from the result - i.e. all builds from ``builds`` that were part of this incomplete update are placed in the second dictionary of the result tuple. :return: a tuple of two dictionaries: * The first dict provides mapping between ``builds`` and their updates where no error occured. ``{build (string): Bodhi update (Munch)}`` * The second dict provides mapping between ``builds`` and their updates where some error occured. The value is ``None`` if the matching Bodhi update could not be found (the only possible cause of failure if ``strict=False``). Or the value is a Bodhi update that was incomplete (happens only if ``strict=True``). ``{build (string): Bodhi update (Munch) or None}`` * The set of keys in both these dictionaries correspond exactly to ``builds``. For every build provided in ``builds`` you'll get an answer in either the first or the second dictionary, and there will be no extra builds that you haven't specified. :raise CheckbValueError: if ``builds`` type is incorrect ''' # validate input params if not python_utils.iterable(builds, basestring): raise exc.CheckbValueError( "Param 'builds' must be an iterable of strings, and yours was: %s" % type(builds)) updates = [] build2update = {} failures = {} # Bodhi works with NVR only, but we have to ensure we receive and return # even NEVR format. So we need to convert internally. builds_nvr = set( [rpm_utils.rpmformat(build, 'nvr') for build in builds]) builds_queue = list(builds_nvr) log.info('Querying Bodhi to map %d builds to their updates...', len(builds)) # retrieve all update data while builds_queue: builds_chunk = builds_queue[:self._MULTICALL_REQUEST_SIZE] builds_chunk = ' '.join(builds_chunk) res = self.client.query(builds=builds_chunk) updates.extend(res['updates']) builds_queue = builds_queue[self._MULTICALL_REQUEST_SIZE:] # don't query for builds which were already found for update in res['updates']: for build in update['builds']: if build['nvr'] in builds_queue: builds_queue.remove(build['nvr']) log.info('Bodhi queries done: %d/%d', len(builds_nvr) - len(builds_queue), len(builds_nvr)) # separate builds into OK and failures for update in updates: # all builds listed in the update bodhi_builds = set([build['nvr'] for build in update['builds']]) # builds *not* provided in @param builds but part of the update (NVRs) missing_builds = bodhi_builds.difference(builds_nvr) # builds provided in @param builds and part of the update matched_builds = [ build for build in builds if rpm_utils.rpmformat(build, 'nvr') in bodhi_builds ] # reject incomplete updates when strict if missing_builds and strict: for build in matched_builds: failures[build] = update continue # otherwise the update is complete or we don't care for build in matched_builds: build2update[build] = update # mark builds without any associated update as a failure for build in builds: if build not in build2update and build not in failures: failures[build] = None diff = set(builds).symmetric_difference( set(build2update.keys()).union(set(failures.keys()))) assert not diff, "Returned NVRs different from input NVRs: %s" % diff return (build2update, failures)
def process(self, params, arg_data): if ('package' not in params and 'nvr' not in params) or 'path' not in params \ or 'target_dir' not in params: detected_args = ', '.join(params.keys()) raise exc.CheckbDirectiveError( "The distgit directive requires 'package' (or 'nvr') and 'path' and 'target_dir' arguments." "Detected arguments: %s" % detected_args) package = None gitref = None namespace = None if 'nvr' in params: nvr = params['nvr'] package = rpm_utils.rpmformat(nvr, fmt='n') gitref = rpm_utils.get_dist_tag(nvr).replace('c', '') rawhide_tag = yumrepoinfo.YumRepoInfo(resolve_baseurl=False).get( 'rawhide', 'tag') if gitref == rawhide_tag: gitref = 'master' namespace = 'rpms' # Assign defaults package = params.get('package', package) gitref = params.get('gitref', gitref or 'master') namespace = params.get('namespace', namespace or 'rpms') baseurl = params.get('baseurl', BASEURL) target_dir = params['target_dir'] ignore_missing = params.get('ignore_missing', False) if not python_utils.iterable(params['path']): raise exc.CheckbValueError( "Incorrect value type of the 'path' argument: " "%s" % type(params['path'])) target_path = params['path'] output_data = {} if 'localpath' in params: if not python_utils.iterable(params['localpath']): raise exc.CheckbValueError( "Incorrect value type of the 'localpath' argument: " "%s" % type(params['path'])) if not len(params['path']) == len(params['localpath']): raise exc.CheckbValueError( 'path and localpath lists must be of the same ' 'length.') target_path = params['localpath'] format_fields = { 'package': package, 'gitref': gitref, 'namespace': namespace, 'baseurl': baseurl, } output_data['downloaded_files'] = [] for path, localpath in zip(params['path'], target_path): localpath = os.path.join(target_dir, localpath) file_utils.makedirs(os.path.dirname(localpath)) url = URL_FMT.format(path=path, **format_fields) try: output_data['downloaded_files'].append( file_utils.download(url, '.', localpath)) except exc.CheckbRemoteError as e: if e.errno == 404 and ignore_missing: log.debug('File not found, ignoring: %s', url) else: raise e return output_data