From 9a0915453c405eddd56532d691173278e4bcc1e0 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Tue, 19 Oct 2021 23:44:59 -0400 Subject: [PATCH 01/19] wip add limit/offset params --- qiskit_ibm/api/clients/runtime.py | 4 ++-- qiskit_ibm/api/rest/runtime.py | 8 +++++++- qiskit_ibm/runtime/ibm_runtime_service.py | 8 ++++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/qiskit_ibm/api/clients/runtime.py b/qiskit_ibm/api/clients/runtime.py index 4156c4999..fcdebacc6 100644 --- a/qiskit_ibm/api/clients/runtime.py +++ b/qiskit_ibm/api/clients/runtime.py @@ -39,13 +39,13 @@ def __init__( **credentials.connection_parameters()) self.api = Runtime(self._session) - def list_programs(self) -> List[Dict]: + def list_programs(self, limit: int, offset: int) -> List[Dict]: """Return a list of runtime programs. Returns: A list of quantum programs. """ - return self.api.list_programs() + return self.api.list_programs(limit, offset) def program_create( self, diff --git a/qiskit_ibm/api/rest/runtime.py b/qiskit_ibm/api/rest/runtime.py index e8252ee0b..0a474ce41 100644 --- a/qiskit_ibm/api/rest/runtime.py +++ b/qiskit_ibm/api/rest/runtime.py @@ -54,13 +54,19 @@ def program_job(self, job_id: str) -> 'ProgramJob': """ return ProgramJob(self.session, job_id) - def list_programs(self) -> List[Dict]: + def list_programs(self, limit: int, offset: int) -> List[Dict]: """Return a list of runtime programs. Returns: JSON response. """ url = self.get_url('programs') + if limit != 20 and offset == 0: + return self.session.get('{}?limit={}'.format(url, limit)).json() + if offset != 0 and limit == 20: + return self.session.get('{}?offset={}'.format(url, offset)).json() + if limit != 20 and offset != 0: + return self.session.get('{}?limit={}&offset={}'.format(url, limit, offset)).json() return self.session.get(url).json() def create_program( diff --git a/qiskit_ibm/runtime/ibm_runtime_service.py b/qiskit_ibm/runtime/ibm_runtime_service.py index 5d10f1d0e..1209ff33c 100644 --- a/qiskit_ibm/runtime/ibm_runtime_service.py +++ b/qiskit_ibm/runtime/ibm_runtime_service.py @@ -124,7 +124,8 @@ def pprint_programs(self, refresh: bool = False, detailed: bool = False) -> None print(f"Name: {prog.name}") print(f"Description: {prog.description}") - def programs(self, refresh: bool = False) -> List[RuntimeProgram]: + def programs(self, refresh: bool = False, limit: int = 20, + offset: int = 0) -> List[RuntimeProgram]: """Return available runtime programs. Currently only program metadata is returned. @@ -132,13 +133,16 @@ def programs(self, refresh: bool = False) -> List[RuntimeProgram]: Args: refresh: If ``True``, re-query the server for the programs. Otherwise return the cached value. + limit: The number of programs returned at a time. Default and maximum + value of 20. + offset: The number of programs to offset. Returns: A list of runtime programs. """ if not self._programs or refresh: self._programs = {} - response = self._api_client.list_programs() + response = self._api_client.list_programs(limit, offset) for prog_dict in response: program = self._to_program(prog_dict) self._programs[program.program_id] = program From 0353fdb94ca1e047e20d4c2f9948d332b8d659c3 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Wed, 20 Oct 2021 02:16:34 -0400 Subject: [PATCH 02/19] add reno --- .../runtime-program-pagination-8d599ae984a5ce33.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml diff --git a/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml b/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml new file mode 100644 index 000000000..e7225bf5c --- /dev/null +++ b/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml @@ -0,0 +1,8 @@ +--- +upgrade: + - | + Limit and offset parameters have been added to + :meth:`qiskit_ibm.runtime.IBMRuntimeService.programs`. + Limit can be used to set the number of runtime programs returned + and offset is the number of results to offset by when retrieving + programs. From 4b6ad0da8da4e85fe30fef242780d314e6618e03 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Wed, 20 Oct 2021 12:22:39 -0400 Subject: [PATCH 03/19] refactor & update test case --- qiskit_ibm/api/rest/runtime.py | 9 ++------- test/ibm/runtime/fake_runtime_client.py | 8 +++++--- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/qiskit_ibm/api/rest/runtime.py b/qiskit_ibm/api/rest/runtime.py index 0a474ce41..0efd37ee6 100644 --- a/qiskit_ibm/api/rest/runtime.py +++ b/qiskit_ibm/api/rest/runtime.py @@ -61,13 +61,8 @@ def list_programs(self, limit: int, offset: int) -> List[Dict]: JSON response. """ url = self.get_url('programs') - if limit != 20 and offset == 0: - return self.session.get('{}?limit={}'.format(url, limit)).json() - if offset != 0 and limit == 20: - return self.session.get('{}?offset={}'.format(url, offset)).json() - if limit != 20 and offset != 0: - return self.session.get('{}?limit={}&offset={}'.format(url, limit, offset)).json() - return self.session.get(url).json() + response = self.session.get('{}?limit={}&offset={}'.format(url, limit, offset)).json() + return response['programs'] def create_program( self, diff --git a/test/ibm/runtime/fake_runtime_client.py b/test/ibm/runtime/fake_runtime_client.py index 6711d5ee7..f06ec7ae7 100644 --- a/test/ibm/runtime/fake_runtime_client.py +++ b/test/ibm/runtime/fake_runtime_client.py @@ -230,10 +230,12 @@ def set_final_status(self, final_status): """Set job status to passed in final status instantly.""" self._final_status = final_status - def list_programs(self): - """List all progrmas.""" + def list_programs(self, limit, offset): + """List all programs.""" programs = [] - for prog in self._programs.values(): + for index, prog in enumerate(self._programs.values(), start=offset): + if index == limit + offset: + break programs.append(prog.to_dict()) return programs From 41d5506ec7af47a88f1ec6767fe4dcf35858fe89 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Tue, 26 Oct 2021 22:41:34 -0400 Subject: [PATCH 04/19] offset -> skip, implement refresh logic --- qiskit_ibm/api/clients/runtime.py | 4 ++-- qiskit_ibm/api/rest/runtime.py | 4 ++-- qiskit_ibm/runtime/ibm_runtime_service.py | 20 +++++++++++++------ ...e-program-pagination-8d599ae984a5ce33.yaml | 7 ++++--- test/ibm/runtime/fake_runtime_client.py | 6 +++--- 5 files changed, 25 insertions(+), 16 deletions(-) diff --git a/qiskit_ibm/api/clients/runtime.py b/qiskit_ibm/api/clients/runtime.py index fcdebacc6..f1d29bf96 100644 --- a/qiskit_ibm/api/clients/runtime.py +++ b/qiskit_ibm/api/clients/runtime.py @@ -39,13 +39,13 @@ def __init__( **credentials.connection_parameters()) self.api = Runtime(self._session) - def list_programs(self, limit: int, offset: int) -> List[Dict]: + def list_programs(self, limit: int, skip: int) -> List[Dict]: """Return a list of runtime programs. Returns: A list of quantum programs. """ - return self.api.list_programs(limit, offset) + return self.api.list_programs(limit, skip) def program_create( self, diff --git a/qiskit_ibm/api/rest/runtime.py b/qiskit_ibm/api/rest/runtime.py index 0efd37ee6..8b2a828d2 100644 --- a/qiskit_ibm/api/rest/runtime.py +++ b/qiskit_ibm/api/rest/runtime.py @@ -54,14 +54,14 @@ def program_job(self, job_id: str) -> 'ProgramJob': """ return ProgramJob(self.session, job_id) - def list_programs(self, limit: int, offset: int) -> List[Dict]: + def list_programs(self, limit: int, skip: int) -> List[Dict]: """Return a list of runtime programs. Returns: JSON response. """ url = self.get_url('programs') - response = self.session.get('{}?limit={}&offset={}'.format(url, limit, offset)).json() + response = self.session.get('{}?limit={}&offset={}'.format(url, limit, skip)).json() return response['programs'] def create_program( diff --git a/qiskit_ibm/runtime/ibm_runtime_service.py b/qiskit_ibm/runtime/ibm_runtime_service.py index 1209ff33c..b97e94893 100644 --- a/qiskit_ibm/runtime/ibm_runtime_service.py +++ b/qiskit_ibm/runtime/ibm_runtime_service.py @@ -107,15 +107,19 @@ def __init__(self, provider: 'ibm_provider.IBMProvider') -> None: self._ws_url = provider.credentials.runtime_url.replace('https', 'wss') self._programs = {} # type: Dict - def pprint_programs(self, refresh: bool = False, detailed: bool = False) -> None: + def pprint_programs(self, refresh: bool = False, detailed: bool = False, + limit: int = 20, skip: int = 0) -> None: """Pretty print information about available runtime programs. Args: refresh: If ``True``, re-query the server for the programs. Otherwise return the cached value. detailed: If ``True`` print all details about available runtime programs. + limit: The number of programs returned at a time. Default and maximum + value of 20. + skip: The number of programs to skip. """ - programs = self.programs(refresh) + programs = self.programs(refresh, limit, skip) for prog in programs: print("="*50) if detailed: @@ -125,7 +129,7 @@ def pprint_programs(self, refresh: bool = False, detailed: bool = False) -> None print(f"Description: {prog.description}") def programs(self, refresh: bool = False, limit: int = 20, - offset: int = 0) -> List[RuntimeProgram]: + skip: int = 0) -> List[RuntimeProgram]: """Return available runtime programs. Currently only program metadata is returned. @@ -135,17 +139,21 @@ def programs(self, refresh: bool = False, limit: int = 20, return the cached value. limit: The number of programs returned at a time. Default and maximum value of 20. - offset: The number of programs to offset. + skip: The number of programs to skip. Returns: A list of runtime programs. """ - if not self._programs or refresh: + total_programs = limit + skip + if not self._programs or refresh or total_programs > len(self._programs): self._programs = {} - response = self._api_client.list_programs(limit, offset) + response = self._api_client.list_programs(limit, skip) for prog_dict in response: program = self._to_program(prog_dict) self._programs[program.program_id] = program + elif total_programs <= len(self._programs): + programs = list(self._programs.values()) + return programs[skip: total_programs] return list(self._programs.values()) def program(self, program_id: str, refresh: bool = False) -> RuntimeProgram: diff --git a/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml b/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml index e7225bf5c..a2744daa1 100644 --- a/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml +++ b/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml @@ -1,8 +1,9 @@ --- upgrade: - | - Limit and offset parameters have been added to - :meth:`qiskit_ibm.runtime.IBMRuntimeService.programs`. + Limit and skip parameters have been added to + :meth:`qiskit_ibm.runtime.IBMRuntimeService.programs` and + :meth:`qiskit_ibm.runtime.IBMRuntimeService.pprint_programs`. Limit can be used to set the number of runtime programs returned - and offset is the number of results to offset by when retrieving + and skip is the number of results to skip when retrieving programs. diff --git a/test/ibm/runtime/fake_runtime_client.py b/test/ibm/runtime/fake_runtime_client.py index f06ec7ae7..7e3d48a38 100644 --- a/test/ibm/runtime/fake_runtime_client.py +++ b/test/ibm/runtime/fake_runtime_client.py @@ -230,11 +230,11 @@ def set_final_status(self, final_status): """Set job status to passed in final status instantly.""" self._final_status = final_status - def list_programs(self, limit, offset): + def list_programs(self, limit, skip): """List all programs.""" programs = [] - for index, prog in enumerate(self._programs.values(), start=offset): - if index == limit + offset: + for index, prog in enumerate(self._programs.values(), start=skip): + if index == limit + skip: break programs.append(prog.to_dict()) return programs From f6f69024898d574bc1a5135bbdc86f6ead63deff Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Wed, 27 Oct 2021 14:47:53 -0400 Subject: [PATCH 05/19] refresh when skip/limit not default --- qiskit_ibm/runtime/ibm_runtime_service.py | 6 +----- test/ibm/runtime/test_runtime.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/qiskit_ibm/runtime/ibm_runtime_service.py b/qiskit_ibm/runtime/ibm_runtime_service.py index b97e94893..4bc46fd8a 100644 --- a/qiskit_ibm/runtime/ibm_runtime_service.py +++ b/qiskit_ibm/runtime/ibm_runtime_service.py @@ -144,16 +144,12 @@ def programs(self, refresh: bool = False, limit: int = 20, Returns: A list of runtime programs. """ - total_programs = limit + skip - if not self._programs or refresh or total_programs > len(self._programs): + if not self._programs or refresh or limit != 20 or skip != 0: self._programs = {} response = self._api_client.list_programs(limit, skip) for prog_dict in response: program = self._to_program(prog_dict) self._programs[program.program_id] = program - elif total_programs <= len(self._programs): - programs = list(self._programs.values()) - return programs[skip: total_programs] return list(self._programs.values()) def program(self, program_id: str, refresh: bool = False) -> RuntimeProgram: diff --git a/test/ibm/runtime/test_runtime.py b/test/ibm/runtime/test_runtime.py index f60f027fb..0e6a71863 100644 --- a/test/ibm/runtime/test_runtime.py +++ b/test/ibm/runtime/test_runtime.py @@ -267,6 +267,17 @@ def test_list_programs(self): all_ids = [prog.program_id for prog in programs] self.assertIn(program_id, all_ids) + def test_list_programs_with_limit_skip(self): + """Test listing programs with limit and skip.""" + program_1 = self._upload_program() + program_2 = self._upload_program() + program_3 = self._upload_program() + programs = self.runtime.programs(limit=2, skip=1) + all_ids = [prog.program_id for prog in programs] + self.assertNotIn(program_3, all_ids) + self.assertIn(program_1, all_ids) + self.assertIn(program_2, all_ids) + def test_list_program(self): """Test listing a single program.""" program_id = self._upload_program() From 77199e961750d53fb6073e8a42e3f0e581a5c627 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Thu, 28 Oct 2021 15:58:06 -0400 Subject: [PATCH 06/19] Update releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml Co-authored-by: Rathish Cholarajan --- .../notes/runtime-program-pagination-8d599ae984a5ce33.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml b/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml index a2744daa1..e5154ab61 100644 --- a/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml +++ b/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml @@ -1,7 +1,7 @@ --- upgrade: - | - Limit and skip parameters have been added to + ``limit`` and ``skip`` parameters have been added to :meth:`qiskit_ibm.runtime.IBMRuntimeService.programs` and :meth:`qiskit_ibm.runtime.IBMRuntimeService.pprint_programs`. Limit can be used to set the number of runtime programs returned From ac4c506796f133ff8bad64883ea8ced7ed46a8db Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Thu, 28 Oct 2021 15:58:17 -0400 Subject: [PATCH 07/19] Update releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml Co-authored-by: Rathish Cholarajan --- .../notes/runtime-program-pagination-8d599ae984a5ce33.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml b/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml index e5154ab61..2e3d3644d 100644 --- a/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml +++ b/releasenotes/notes/runtime-program-pagination-8d599ae984a5ce33.yaml @@ -4,6 +4,6 @@ upgrade: ``limit`` and ``skip`` parameters have been added to :meth:`qiskit_ibm.runtime.IBMRuntimeService.programs` and :meth:`qiskit_ibm.runtime.IBMRuntimeService.pprint_programs`. - Limit can be used to set the number of runtime programs returned - and skip is the number of results to skip when retrieving + ``limit`` can be used to set the number of runtime programs returned + and ``skip`` is the number of programs to skip when retrieving programs. From d692aeefcd6078315669e1febf36326af3b6e697 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Thu, 28 Oct 2021 23:35:59 -0400 Subject: [PATCH 08/19] add to test case --- test/ibm/runtime/test_runtime.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/ibm/runtime/test_runtime.py b/test/ibm/runtime/test_runtime.py index 0e6a71863..1f5e513c3 100644 --- a/test/ibm/runtime/test_runtime.py +++ b/test/ibm/runtime/test_runtime.py @@ -277,6 +277,9 @@ def test_list_programs_with_limit_skip(self): self.assertNotIn(program_3, all_ids) self.assertIn(program_1, all_ids) self.assertIn(program_2, all_ids) + programs = self.runtime.programs(limit=3) + all_ids = [prog.program_id for prog in programs] + self.assertIn(program_3, all_ids) def test_list_program(self): """Test listing a single program.""" From 498773871834d0e097ac611ab6632a79315f054d Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Fri, 29 Oct 2021 16:28:16 -0400 Subject: [PATCH 09/19] refactor refresh logic --- qiskit_ibm/runtime/ibm_runtime_service.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/qiskit_ibm/runtime/ibm_runtime_service.py b/qiskit_ibm/runtime/ibm_runtime_service.py index 4bc46fd8a..1c674fdaf 100644 --- a/qiskit_ibm/runtime/ibm_runtime_service.py +++ b/qiskit_ibm/runtime/ibm_runtime_service.py @@ -108,7 +108,7 @@ def __init__(self, provider: 'ibm_provider.IBMProvider') -> None: self._programs = {} # type: Dict def pprint_programs(self, refresh: bool = False, detailed: bool = False, - limit: int = 20, skip: int = 0) -> None: + limit: int = None, skip: int = None) -> None: """Pretty print information about available runtime programs. Args: @@ -128,8 +128,8 @@ def pprint_programs(self, refresh: bool = False, detailed: bool = False, print(f"Name: {prog.name}") print(f"Description: {prog.description}") - def programs(self, refresh: bool = False, limit: int = 20, - skip: int = 0) -> List[RuntimeProgram]: + def programs(self, refresh: bool = False, + limit: int = None, skip: int = None) -> List[RuntimeProgram]: """Return available runtime programs. Currently only program metadata is returned. @@ -144,7 +144,11 @@ def programs(self, refresh: bool = False, limit: int = 20, Returns: A list of runtime programs. """ - if not self._programs or refresh or limit != 20 or skip != 0: + if not self._programs or refresh or limit or skip: + if not limit: + limit = 20 + if not skip: + skip = 0 self._programs = {} response = self._api_client.list_programs(limit, skip) for prog_dict in response: From 53f7fc0caa7bbc232bd993cbe3c55b6bcd610fc3 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Wed, 3 Nov 2021 13:50:41 -0400 Subject: [PATCH 10/19] refactor --- qiskit_ibm/api/rest/runtime.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/qiskit_ibm/api/rest/runtime.py b/qiskit_ibm/api/rest/runtime.py index 8b2a828d2..1e795e393 100644 --- a/qiskit_ibm/api/rest/runtime.py +++ b/qiskit_ibm/api/rest/runtime.py @@ -61,7 +61,12 @@ def list_programs(self, limit: int, skip: int) -> List[Dict]: JSON response. """ url = self.get_url('programs') - response = self.session.get('{}?limit={}&offset={}'.format(url, limit, skip)).json() + payload: Dict[str, Union[int, str]] = {} + if limit: + payload['limit'] = limit + if skip: + payload['offset'] = skip + response = self.session.get(url, params=payload).json() return response['programs'] def create_program( From 7bc900b6d11b4b1c22e5b67015494847623e0274 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Wed, 3 Nov 2021 14:02:47 -0400 Subject: [PATCH 11/19] fix lint --- qiskit_ibm/api/rest/runtime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_ibm/api/rest/runtime.py b/qiskit_ibm/api/rest/runtime.py index 1e795e393..1b15aa4d5 100644 --- a/qiskit_ibm/api/rest/runtime.py +++ b/qiskit_ibm/api/rest/runtime.py @@ -66,7 +66,7 @@ def list_programs(self, limit: int, skip: int) -> List[Dict]: payload['limit'] = limit if skip: payload['offset'] = skip - response = self.session.get(url, params=payload).json() + response = self.session.get(url, params=payload).json() return response['programs'] def create_program( From 542d2a810840de563797eb8f8fd4e90774040240 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Fri, 5 Nov 2021 03:01:56 -0400 Subject: [PATCH 12/19] add integration test --- test/ibm/runtime/test_runtime_integration.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/ibm/runtime/test_runtime_integration.py b/test/ibm/runtime/test_runtime_integration.py index 39a0cd2ff..fd3d8dda8 100644 --- a/test/ibm/runtime/test_runtime_integration.py +++ b/test/ibm/runtime/test_runtime_integration.py @@ -139,6 +139,20 @@ def test_list_programs(self): found = True self.assertTrue(found, f"Program {self.program_id} not found!") + def test_list_programs_with_limit_skip(self): + """Test listing programs with limit and skip.""" + program_1 = self._upload_program() + program_2 = self._upload_program() + program_3 = self._upload_program() + programs = self.provider.runtime.programs(limit=2, skip=1) + all_ids = [prog.program_id for prog in programs] + self.assertNotIn(program_3, all_ids) + self.assertIn(program_1, all_ids) + self.assertIn(program_2, all_ids) + programs = self.provider.runtime.programs(limit=3) + all_ids = [prog.program_id for prog in programs] + self.assertIn(program_3, all_ids) + def test_list_program(self): """Test listing a single program.""" program = self.provider.runtime.program(self.program_id) From 6975d6167cdf8f2b387a640095ca6b71d7ef026d Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Fri, 5 Nov 2021 12:45:54 -0400 Subject: [PATCH 13/19] update doc string --- qiskit_ibm/api/clients/runtime.py | 4 ++++ qiskit_ibm/api/rest/runtime.py | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/qiskit_ibm/api/clients/runtime.py b/qiskit_ibm/api/clients/runtime.py index f1d29bf96..c86d3ffb5 100644 --- a/qiskit_ibm/api/clients/runtime.py +++ b/qiskit_ibm/api/clients/runtime.py @@ -42,6 +42,10 @@ def __init__( def list_programs(self, limit: int, skip: int) -> List[Dict]: """Return a list of runtime programs. + Args: + limit: limit: The maxiumum number of programs to return. + skip: The number of programs to skip. + Returns: A list of quantum programs. """ diff --git a/qiskit_ibm/api/rest/runtime.py b/qiskit_ibm/api/rest/runtime.py index 1b15aa4d5..fd932cd06 100644 --- a/qiskit_ibm/api/rest/runtime.py +++ b/qiskit_ibm/api/rest/runtime.py @@ -57,11 +57,15 @@ def program_job(self, job_id: str) -> 'ProgramJob': def list_programs(self, limit: int, skip: int) -> List[Dict]: """Return a list of runtime programs. + Args: + limit: The maxiumum number of programs to return. + skip: The number of programs to skip. + Returns: JSON response. """ url = self.get_url('programs') - payload: Dict[str, Union[int, str]] = {} + payload: Dict[str, Union[int]] = {} if limit: payload['limit'] = limit if skip: From 2c99ad1afa03cd022c1a891294d39ba80833236e Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Sun, 7 Nov 2021 21:51:29 -0500 Subject: [PATCH 14/19] Update qiskit_ibm/api/clients/runtime.py Co-authored-by: Rathish Cholarajan --- qiskit_ibm/api/clients/runtime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_ibm/api/clients/runtime.py b/qiskit_ibm/api/clients/runtime.py index c86d3ffb5..702003a15 100644 --- a/qiskit_ibm/api/clients/runtime.py +++ b/qiskit_ibm/api/clients/runtime.py @@ -43,7 +43,7 @@ def list_programs(self, limit: int, skip: int) -> List[Dict]: """Return a list of runtime programs. Args: - limit: limit: The maxiumum number of programs to return. + limit: The number of programs to return. skip: The number of programs to skip. Returns: From b34db6f3ed9d1834d98f242e5b49a67da8e1b8a3 Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Sun, 7 Nov 2021 22:50:20 -0500 Subject: [PATCH 15/19] cleanup merge --- qiskit_ibm/api/rest/runtime.py | 2 +- qiskit_ibm/runtime/ibm_runtime_service.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit_ibm/api/rest/runtime.py b/qiskit_ibm/api/rest/runtime.py index 24ffd579f..49e1779af 100644 --- a/qiskit_ibm/api/rest/runtime.py +++ b/qiskit_ibm/api/rest/runtime.py @@ -72,7 +72,7 @@ def list_programs(self, limit: int, skip: int) -> List[Dict]: if skip: payload['offset'] = skip response = self.session.get(url, params=payload).json() - return response['programs'] + return response def create_program( self, diff --git a/qiskit_ibm/runtime/ibm_runtime_service.py b/qiskit_ibm/runtime/ibm_runtime_service.py index b9a6b74da..6e964e9dd 100644 --- a/qiskit_ibm/runtime/ibm_runtime_service.py +++ b/qiskit_ibm/runtime/ibm_runtime_service.py @@ -108,7 +108,7 @@ def __init__(self, provider: 'ibm_provider.IBMProvider') -> None: self._programs = {} # type: Dict def pprint_programs(self, refresh: bool = False, detailed: bool = False, - limit: int = None, skip: int = None) -> None: + limit: int = 20, skip: int = 20) -> None: """Pretty print information about available runtime programs. Args: @@ -152,7 +152,7 @@ def programs(self, refresh: bool = False, skip = 0 self._programs = {} response = self._api_client.list_programs(limit, skip) - for prog_dict in response: + for prog_dict in response.get("programs", []): program = self._to_program(prog_dict) self._programs[program.program_id] = program return list(self._programs.values()) From 2a764edf49abd2715301d4150369de8686084a4c Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Sun, 7 Nov 2021 23:33:12 -0500 Subject: [PATCH 16/19] Apply suggestions from code review --- qiskit_ibm/api/clients/runtime.py | 4 ++-- qiskit_ibm/api/rest/runtime.py | 6 +++--- qiskit_ibm/runtime/ibm_runtime_service.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/qiskit_ibm/api/clients/runtime.py b/qiskit_ibm/api/clients/runtime.py index 2d1627692..d317c46ee 100644 --- a/qiskit_ibm/api/clients/runtime.py +++ b/qiskit_ibm/api/clients/runtime.py @@ -39,7 +39,7 @@ def __init__( **credentials.connection_parameters()) self.api = Runtime(self._session) - def list_programs(self, limit: int, skip: int) -> List[Dict]: + def list_programs(self, limit: int = None, skip: int = None) -> List[Dict]: """Return a list of runtime programs. Args: @@ -47,7 +47,7 @@ def list_programs(self, limit: int, skip: int) -> List[Dict]: skip: The number of programs to skip. Returns: - A list of quantum programs. + A list of runtime programs. """ return self.api.list_programs(limit, skip) diff --git a/qiskit_ibm/api/rest/runtime.py b/qiskit_ibm/api/rest/runtime.py index 49e1779af..95e42d3cc 100644 --- a/qiskit_ibm/api/rest/runtime.py +++ b/qiskit_ibm/api/rest/runtime.py @@ -55,15 +55,15 @@ def program_job(self, job_id: str) -> 'ProgramJob': """ return ProgramJob(self.session, job_id) - def list_programs(self, limit: int, skip: int) -> List[Dict]: + def list_programs(self, limit: int = None, skip: int = None) -> List[Dict]: """Return a list of runtime programs. Args: - limit: The maxiumum number of programs to return. + limit: The number of programs to return. skip: The number of programs to skip. Returns: - JSON response. + A list of runtime programs. """ url = self.get_url('programs') payload: Dict[str, int] = {} diff --git a/qiskit_ibm/runtime/ibm_runtime_service.py b/qiskit_ibm/runtime/ibm_runtime_service.py index 6e964e9dd..78c0e23eb 100644 --- a/qiskit_ibm/runtime/ibm_runtime_service.py +++ b/qiskit_ibm/runtime/ibm_runtime_service.py @@ -108,7 +108,7 @@ def __init__(self, provider: 'ibm_provider.IBMProvider') -> None: self._programs = {} # type: Dict def pprint_programs(self, refresh: bool = False, detailed: bool = False, - limit: int = 20, skip: int = 20) -> None: + limit: int = 20, skip: int = 0) -> None: """Pretty print information about available runtime programs. Args: From 221d990b9ff5295dc444f227442a8395e17d4c8d Mon Sep 17 00:00:00 2001 From: Kevin Tian Date: Mon, 8 Nov 2021 00:36:20 -0500 Subject: [PATCH 17/19] fix lint refactor --- qiskit_ibm/api/clients/runtime.py | 2 +- qiskit_ibm/api/rest/runtime.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/qiskit_ibm/api/clients/runtime.py b/qiskit_ibm/api/clients/runtime.py index d317c46ee..5cc4df6b5 100644 --- a/qiskit_ibm/api/clients/runtime.py +++ b/qiskit_ibm/api/clients/runtime.py @@ -39,7 +39,7 @@ def __init__( **credentials.connection_parameters()) self.api = Runtime(self._session) - def list_programs(self, limit: int = None, skip: int = None) -> List[Dict]: + def list_programs(self, limit: int = None, skip: int = None) -> Dict[str, Any]: """Return a list of runtime programs. Args: diff --git a/qiskit_ibm/api/rest/runtime.py b/qiskit_ibm/api/rest/runtime.py index 95e42d3cc..e44265c96 100644 --- a/qiskit_ibm/api/rest/runtime.py +++ b/qiskit_ibm/api/rest/runtime.py @@ -55,7 +55,7 @@ def program_job(self, job_id: str) -> 'ProgramJob': """ return ProgramJob(self.session, job_id) - def list_programs(self, limit: int = None, skip: int = None) -> List[Dict]: + def list_programs(self, limit: int = None, skip: int = None) -> Dict[str, Any]: """Return a list of runtime programs. Args: @@ -71,8 +71,7 @@ def list_programs(self, limit: int = None, skip: int = None) -> List[Dict]: payload['limit'] = limit if skip: payload['offset'] = skip - response = self.session.get(url, params=payload).json() - return response + return self.session.get(url, params=payload).json() def create_program( self, From 8eaf821569cc8a2cde1cd368b5ad8f51e3010073 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Mon, 8 Nov 2021 01:15:20 -0500 Subject: [PATCH 18/19] Fetch all programs upfront 20 at a time and store in cache For subsequent requests paginate and return from cache --- qiskit_ibm/runtime/ibm_runtime_service.py | 36 +++++++++++++++-------- test/ibm/runtime/fake_runtime_client.py | 6 ++-- test/ibm/runtime/test_runtime.py | 6 ++-- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/qiskit_ibm/runtime/ibm_runtime_service.py b/qiskit_ibm/runtime/ibm_runtime_service.py index 78c0e23eb..a131b4298 100644 --- a/qiskit_ibm/runtime/ibm_runtime_service.py +++ b/qiskit_ibm/runtime/ibm_runtime_service.py @@ -130,7 +130,7 @@ def pprint_programs(self, refresh: bool = False, detailed: bool = False, print(f" Description: {prog.description}") def programs(self, refresh: bool = False, - limit: int = None, skip: int = None) -> List[RuntimeProgram]: + limit: int = 20, skip: int = 0) -> List[RuntimeProgram]: """Return available runtime programs. Currently only program metadata is returned. @@ -138,24 +138,34 @@ def programs(self, refresh: bool = False, Args: refresh: If ``True``, re-query the server for the programs. Otherwise return the cached value. - limit: The number of programs returned at a time. Default and maximum - value of 20. + limit: The number of programs returned at a time. ``None`` means no limit. skip: The number of programs to skip. Returns: A list of runtime programs. """ - if not self._programs or refresh or limit or skip: - if not limit: - limit = 20 - if not skip: - skip = 0 + if skip is None: + skip = 0 + if not self._programs or refresh: self._programs = {} - response = self._api_client.list_programs(limit, skip) - for prog_dict in response.get("programs", []): - program = self._to_program(prog_dict) - self._programs[program.program_id] = program - return list(self._programs.values()) + current_page_limit = 20 + offset = 0 + while True: + response = self._api_client.list_programs(limit=current_page_limit, skip=offset) + program_page = response.get("programs", []) + # count is the total number of programs that would be returned if + # there was no limit or skip + count = response.get("count", 0) + for prog_dict in program_page: + program = self._to_program(prog_dict) + self._programs[program.program_id] = program + if len(self._programs) == count: + # Stop if there are no more programs returned by the server. + break + offset += len(program_page) + if limit is None: + limit = len(self._programs) + return list(self._programs.values())[skip:limit+skip] def program(self, program_id: str, refresh: bool = False) -> RuntimeProgram: """Retrieve a runtime program. diff --git a/test/ibm/runtime/fake_runtime_client.py b/test/ibm/runtime/fake_runtime_client.py index b60f9dbab..a8b6643fd 100644 --- a/test/ibm/runtime/fake_runtime_client.py +++ b/test/ibm/runtime/fake_runtime_client.py @@ -245,11 +245,9 @@ def set_final_status(self, final_status): def list_programs(self, limit, skip): """List all programs.""" programs = [] - for index, prog in enumerate(self._programs.values(), start=skip): - if index == limit + skip: - break + for prog in self._programs.values(): programs.append(prog.to_dict()) - return {"programs": programs} + return {"programs": programs[skip:limit+skip], "count": len(self._programs)} def program_create(self, program_data, name, description, max_execution_time, spec=None, is_public=False): diff --git a/test/ibm/runtime/test_runtime.py b/test/ibm/runtime/test_runtime.py index bcdf0b6da..1964ddbd1 100644 --- a/test/ibm/runtime/test_runtime.py +++ b/test/ibm/runtime/test_runtime.py @@ -309,12 +309,12 @@ def test_list_programs_with_limit_skip(self): program_3 = self._upload_program() programs = self.runtime.programs(limit=2, skip=1) all_ids = [prog.program_id for prog in programs] - self.assertNotIn(program_3, all_ids) - self.assertIn(program_1, all_ids) + self.assertNotIn(program_1, all_ids) self.assertIn(program_2, all_ids) + self.assertIn(program_3, all_ids) programs = self.runtime.programs(limit=3) all_ids = [prog.program_id for prog in programs] - self.assertIn(program_3, all_ids) + self.assertIn(program_1, all_ids) def test_list_program(self): """Test listing a single program.""" From 5f3019cc1bc5b75b461e85927bdae21e0c1c0723 Mon Sep 17 00:00:00 2001 From: Rathish Cholarajan Date: Mon, 8 Nov 2021 01:36:54 -0500 Subject: [PATCH 19/19] Fix integration test --- test/ibm/runtime/test_runtime_integration.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/test/ibm/runtime/test_runtime_integration.py b/test/ibm/runtime/test_runtime_integration.py index c57f3ffc9..418be70e4 100644 --- a/test/ibm/runtime/test_runtime_integration.py +++ b/test/ibm/runtime/test_runtime_integration.py @@ -143,17 +143,18 @@ def test_list_programs(self): def test_list_programs_with_limit_skip(self): """Test listing programs with limit and skip.""" - program_1 = self._upload_program() - program_2 = self._upload_program() - program_3 = self._upload_program() - programs = self.provider.runtime.programs(limit=2, skip=1) - all_ids = [prog.program_id for prog in programs] - self.assertNotIn(program_3, all_ids) - self.assertIn(program_1, all_ids) - self.assertIn(program_2, all_ids) + self._upload_program() + self._upload_program() + self._upload_program() programs = self.provider.runtime.programs(limit=3) all_ids = [prog.program_id for prog in programs] - self.assertIn(program_3, all_ids) + self.assertEqual(len(all_ids), 3) + programs = self.provider.runtime.programs(limit=2, skip=1) + some_ids = [prog.program_id for prog in programs] + self.assertEqual(len(some_ids), 2) + self.assertNotIn(all_ids[0], some_ids) + self.assertIn(all_ids[1], some_ids) + self.assertIn(all_ids[2], some_ids) def test_list_program(self): """Test listing a single program."""