import asyncio from typing import Any, List from harborapi import HarborAsyncClient class AccumulatingBuffer: def __init__(self, capacity: int): if capacity <= 0: raise ValueError("Capacity must be positive") self.capacity = capacity self.buf = [None] * capacity self.count = 0 # Фактически заполненные слоты def load(self, elements: List[Any]) -> None: """Добавляет элементы: в свободное место - в конец, при полном - перезапись с начала""" L = len(elements) if L > self.capacity: raise IndexError(f"Elements ({L}) exceed capacity ({self.capacity})") if self.count + L <= self.capacity: # Есть свободное место - добавляем в конец for i, elem in enumerate(elements): self.buf[self.count + i] = elem self.count += L # Затираем оставшееся место for i in range(self.count, self.capacity): self.buf[i] = None else: # Буфер полон - перезаписываем с начала for i, elem in enumerate(elements): self.buf[i] = elem self.count = L # Затираем остаток for i in range(L, self.capacity): self.buf[i] = None def get_filled(self) -> List[Any]: """Возвращает только непустые элементы""" return [x for x in self.buf[: self.count] if x is not None] def size(self) -> int: return self.count def filled_size(self) -> int: return len(self.get_filled()) def free_space(self) -> int: return self.capacity - self.count from itertools import islice def chunks(lst, n): for i in range(0, len(lst), n): yield lst[i : i + n] # Генератор вместо списка async def get_artifacts( client: HarborAsyncClient, project_name: str, repository_name: str, page_number: int, page_size: int, ): # номер страницы с артефактами в Harbor harbor_page_number = 1 # номер страницы с артефактами в RIM rim_page_number = 1 # выделяем буфер под "страницу" артефактов artifacts_page_buffer = AccumulatingBuffer(page_size) # начинаем считываение while True: # считываем из Harbor артефакты и загружаем их в буфер artifacts = await client.get_artifacts( project_name=project_name, repository_name=repository_name, page=harbor_page_number, page_size=10, limit=10, ) # нет артефактов if not artifacts: # если страница была неполной, то возвращаем результат if rim_page_number == page_number: return artifacts_page_buffer.get_filled() break for artifact in artifacts: # массив тэгов tags = [] # если есть тэги if artifact.tags: # перебираем все ссылки на тэги for tag_reference in artifact.tags: # формируем имя artifact_name = ( f"{project_name}/{repository_name}:{tag_reference.name}" ) # засовываем в список tags.append(artifact_name) # print(f"Tags {tags}") # print(f"Buffer: {artifacts_page_buffer.get_filled()}") # print( # f"RIM page number: {rim_page_number}, length: {len(artifacts_page_buffer.get_filled())}" # ) for portion in chunks(tags, page_size): # загружаем порцию в буфер artifacts_page_buffer.load(portion) # Проверяем после каждой порции if ( artifacts_page_buffer.filled_size() >= page_size and rim_page_number == page_number ): return artifacts_page_buffer.get_filled() # Считаем полные страницы RIM if artifacts_page_buffer.filled_size() >= page_size: # увеличиваем счетчик страниц в RIM rim_page_number += 1 # увеличиваем счетчик страниц Harbor harbor_page_number += 1 # не страниц, или указана несуществующая страница return [] async def main(): registry_url = "-" registry_login = "-" registry_password = "-" client = HarborAsyncClient( url=registry_url + "/api/v2.0", username=registry_login, secret=registry_password, # ограничение по количеству количества записей о соединениях хранимых клиентом max_logs=0, verify=False, # TODO: remove them ? ) rbuf = AccumulatingBuffer(10) rbuf.load([]) print(rbuf.get_filled()) rbuf.load([1, 2, 3, 4, 5, 6, 7, 8, 9]) print(rbuf.get_filled()) rbuf.load([1, 2, 3, 4]) print(rbuf.get_filled()) # tests # project_name = "sbis-hlha" # repository_name = "charts/sbis-docker-dispatcher" project_name = "corp-base" repository_name = "platform/centos" artifacts1 = set(await get_artifacts(client, project_name, repository_name, 1, 6)) print(artifacts1) print(len(artifacts1)) artifacts2 = set(await get_artifacts(client, project_name, repository_name, 2, 6)) print(artifacts2) print(len(artifacts2)) print(artifacts1 - artifacts2) if __name__ == "__main__": asyncio.run(main())