<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def delete_with_retention(self, num_to_retain):
 """ Retain the num_to_retain most recent backups and delete all data before them. """

        base_backup_sentinel_depth = self.layout.basebackups().count('/') + 1

        # Sweep over base backup files, collecting sentinel files from
        # completed backups.
        completed_basebackups = []
        for key in self._backup_list(prefix=self.layout.basebackups()):

            key_name = self.layout.key_name(key)
            key_parts = key_name.split('/')
            key_depth = len(key_parts)
            url = '{scheme}://{bucket}/{name}'.format(
                scheme=self.layout.scheme,
                bucket=self._container_name(key),
                name=key_name)

            if key_depth == base_backup_sentinel_depth:
                # This is a key at the depth of a base-backup-sentinel file.
                # Check to see if it matches the known form.
                match = re.match(storage.COMPLETE_BASE_BACKUP_REGEXP,
                                 key_parts[-1])

                # If this isn't a base-backup-sentinel file, just ignore it.
                if match is None:
                    continue

                # This key corresponds to a base-backup-sentinel file and
                # represents a completed backup. Grab its segment number.
                scanned_sn = \
                    self._groupdict_to_segment_number(match.groupdict())
                completed_basebackups.append(dict(
                    scanned_sn=scanned_sn,
                    url=url))

        # Sort the base backups from newest to oldest.
        basebackups = sorted(
                        completed_basebackups,
                        key=lambda backup: backup['scanned_sn'].as_an_integer,
                        reverse=True)
        last_retained = None
        if len(basebackups) <= num_to_retain:
            detail = None
            if len(basebackups) == 0:
                msg = 'Not deleting any data.'
                detail = 'No existing base backups.'
            elif len(basebackups) == 1:
                last_retained = basebackups[-1]
                msg = 'Retaining existing base backup.'
            else:
                last_retained = basebackups[-1]
                msg = "Retaining all %d base backups." % len(basebackups)
        else:
            last_retained = basebackups[num_to_retain - 1]
            num_deleting = len(basebackups) - num_to_retain
            msg = "Deleting %d oldest base backups." % num_deleting
            detail = "Found %d total base backups." % len(basebackups)
        log_message = dict(msg=msg)
        if detail is not None:
            log_message['detail'] = detail
        if last_retained is not None:
            log_message['hint'] = \
                "Deleting keys older than %s." % last_retained['url']
        logger.info(**log_message)

        # This will delete all base backup and WAL data before
        # last_retained['scanned_sn'].
        if last_retained is not None:
            self._delete_base_backups_before(last_retained['scanned_sn'])
            self._delete_wals_before(last_retained['scanned_sn'])

        if self.deleter:
            self.deleter.close()