Repository: Qiskit/qiskit-ibmq-provider (language: python)

Issue:
update job share level after execution
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues to confirm this idea does not exist. -->

### What is the expected behavior?
Having a command to update the share_level property of a job in the remote database after its execution.

update job share level after execution
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues to confirm this idea does not exist. -->

### What is the expected behavior?
Having a command to update the share_level property of a job in the remote database after its execution.



Discussion / hints:

Fix patch:
diff --git a/qiskit/providers/ibmq/api/clients/account.py b/qiskit/providers/ibmq/api/clients/account.py
index 98dc97c86..0007c79e0 100644
--- a/qiskit/providers/ibmq/api/clients/account.py
+++ b/qiskit/providers/ibmq/api/clients/account.py
@@ -19,8 +19,7 @@
 from typing import List, Dict, Any, Optional, Union
 from datetime import datetime
 
-from qiskit.providers.ibmq.apiconstants import (API_JOB_FINAL_STATES, ApiJobStatus,
-                                                ApiJobShareLevel)
+from qiskit.providers.ibmq.apiconstants import API_JOB_FINAL_STATES, ApiJobStatus
 from qiskit.providers.ibmq.utils.utils import RefreshQueue
 from qiskit.providers.ibmq.credentials import Credentials
 
@@ -180,7 +179,6 @@ def job_submit(
             backend_name: str,
             qobj_dict: Dict[str, Any],
             job_name: Optional[str] = None,
-            job_share_level: Optional[ApiJobShareLevel] = None,
             job_tags: Optional[List[str]] = None,
             experiment_id: Optional[str] = None
     ) -> Dict[str, Any]:
@@ -190,7 +188,6 @@ def job_submit(
             backend_name: The name of the backend.
             qobj_dict: The ``Qobj`` to be executed, as a dictionary.
             job_name: Custom name to be assigned to the job.
-            job_share_level: Level the job should be shared at.
             job_tags: Tags to be assigned to the job.
             experiment_id: Used to add a job to an experiment.
 
@@ -201,14 +198,11 @@ def job_submit(
             RequestsApiError: If an error occurred communicating with the server.
         """
         # pylint: disable=missing-raises-doc
-        # Check for the job share level.
-        _job_share_level = job_share_level.value if job_share_level else None
 
         # Create a remote job instance on the server.
         job_info = self.account_api.create_remote_job(
             backend_name,
             job_name=job_name,
-            job_share_level=_job_share_level,
             job_tags=job_tags,
             experiment_id=experiment_id)
 
diff --git a/qiskit/providers/ibmq/api/rest/account.py b/qiskit/providers/ibmq/api/rest/account.py
index f7c379455..aa02b681a 100644
--- a/qiskit/providers/ibmq/api/rest/account.py
+++ b/qiskit/providers/ibmq/api/rest/account.py
@@ -133,7 +133,6 @@ def create_remote_job(
             self,
             backend_name: str,
             job_name: Optional[str] = None,
-            job_share_level: Optional[str] = None,
             job_tags: Optional[List[str]] = None,
             experiment_id: Optional[str] = None
     ) -> Dict[str, Any]:
@@ -142,7 +141,6 @@ def create_remote_job(
         Args:
             backend_name: The name of the backend.
             job_name: Custom name to be assigned to the job.
-            job_share_level: Level the job should be shared at.
             job_tags: Tags to be assigned to the job.
             experiment_id: Used to add a job to an experiment.
 
@@ -159,9 +157,6 @@ def create_remote_job(
         if job_name:
             payload['name'] = job_name
 
-        if job_share_level:
-            payload['shareLevel'] = job_share_level
-
         if job_tags:
             payload['tags'] = job_tags
 
diff --git a/qiskit/providers/ibmq/ibmqbackend.py b/qiskit/providers/ibmq/ibmqbackend.py
index 81d7cc401..e0c71a011 100644
--- a/qiskit/providers/ibmq/ibmqbackend.py
+++ b/qiskit/providers/ibmq/ibmqbackend.py
@@ -36,7 +36,7 @@
 from qiskit.util import deprecate_arguments
 
 from qiskit.providers.ibmq import accountprovider  # pylint: disable=unused-import
-from .apiconstants import ApiJobShareLevel, ApiJobStatus, API_JOB_FINAL_STATES
+from .apiconstants import ApiJobStatus, API_JOB_FINAL_STATES
 from .api.clients import AccountClient
 from .api.exceptions import ApiError
 from .backendjoblimit import BackendJobLimit
@@ -266,16 +266,9 @@ def run(
         """
         # pylint: disable=arguments-differ
         if job_share_level:
-            try:
-                api_job_share_level = ApiJobShareLevel(job_share_level.lower())
-            except ValueError:
-                valid_job_share_levels_str = ', '.join(level.value for level in ApiJobShareLevel)
-                raise IBMQBackendValueError(
-                    '"{}" is not a valid job share level. '
-                    'Valid job share levels are: {}.'
-                    .format(job_share_level, valid_job_share_levels_str)) from None
-        else:
-            api_job_share_level = ApiJobShareLevel.NONE
+            warnings.warn("The `job_share_level` keyword is no longer supported "
+                          "and will be removed in a future release.",
+                          Warning, stacklevel=3)
 
         validate_job_tags(job_tags, IBMQBackendValueError)
 
@@ -336,7 +329,7 @@ def run(
                           DeprecationWarning, stacklevel=3)
             if validate_qobj:
                 validate_qobj_against_schema(qobj)
-        return self._submit_job(qobj, job_name, api_job_share_level, job_tags, experiment_id)
+        return self._submit_job(qobj, job_name, job_tags, experiment_id)
 
     def _get_run_config(self, **kwargs: Any) -> Dict:
         """Return the consolidated runtime configuration."""
@@ -353,7 +346,6 @@ def _submit_job(
             self,
             qobj: Union[QasmQobj, PulseQobj],
             job_name: Optional[str] = None,
-            job_share_level: Optional[ApiJobShareLevel] = None,
             job_tags: Optional[List[str]] = None,
             experiment_id: Optional[str] = None
     ) -> IBMQJob:
@@ -365,7 +357,6 @@ def _submit_job(
                 name can subsequently be used as a filter in the
                 ``jobs()``method.
                 Job names do not need to be unique.
-            job_share_level: Level the job should be shared at.
             job_tags: Tags to be assigned to the job.
             experiment_id: Used to add a job to an experiment.
 
@@ -391,7 +382,6 @@ def _submit_job(
                 backend_name=self.name(),
                 qobj_dict=qobj_dict,
                 job_name=job_name,
-                job_share_level=job_share_level,
                 job_tags=job_tags,
                 experiment_id=experiment_id)
         except ApiError as ex:
@@ -818,6 +808,10 @@ def run(    # type: ignore[override]
             The job to be executed.
         """
         # pylint: disable=arguments-differ
+        if job_share_level:
+            warnings.warn("The `job_share_level` keyword is no longer supported "
+                          "and will be removed in a future release.",
+                          Warning, stacklevel=3)
         if backend_options is not None:
             warnings.warn("Use of `backend_options` is deprecated and will "
                           "be removed in a future release."
@@ -832,7 +826,7 @@ def run(    # type: ignore[override]
             except AttributeError:
                 pass
         run_config.update(kwargs)
-        return super().run(circuits, job_name=job_name, job_share_level=job_share_level,
+        return super().run(circuits, job_name=job_name,
                            job_tags=job_tags, experiment_id=experiment_id,
                            validate_qobj=validate_qobj,
                            noise_model=noise_model, **run_config)
diff --git a/qiskit/providers/ibmq/job/ibmqjob.py b/qiskit/providers/ibmq/job/ibmqjob.py
index 8b1cd4bc1..99e8cd7ad 100644
--- a/qiskit/providers/ibmq/job/ibmqjob.py
+++ b/qiskit/providers/ibmq/job/ibmqjob.py
@@ -627,9 +627,10 @@ def share_level(self) -> str:
         Returns:
             The share level of the job.
         """
-        if not self._share_level and not self._refreshed:
-            self.refresh()
-        return self._share_level
+        warnings.warn("`IBMQJob.share_level()` method is no longer supported "
+                      "and will be removed in a future release.",
+                      Warning, stacklevel=2)
+        return 'none'
 
     def time_per_step(self) -> Optional[Dict]:
         """Return the date and time information on each step of the job processing.
@@ -777,7 +778,6 @@ def refresh(self) -> None:
         self._use_object_storage = (self._kind == ApiJobKind.QOBJECT_STORAGE)
         self._status, self._queue_info = \
             self._get_status_position(self._api_status, api_response.pop('info_queue', None))
-        self._share_level = api_response.pop('share_level', 'none')
         self._set_client_version(api_response.pop('client_info', None))
         self._set_result(api_response.pop('result', None))
         self._experiment_id = api_response.pop('experiment_id', None)
diff --git a/qiskit/providers/ibmq/managed/ibmqjobmanager.py b/qiskit/providers/ibmq/managed/ibmqjobmanager.py
index 858667b69..16a9473f2 100644
--- a/qiskit/providers/ibmq/managed/ibmqjobmanager.py
+++ b/qiskit/providers/ibmq/managed/ibmqjobmanager.py
@@ -13,12 +13,12 @@
 """Job Manager used to manage jobs for IBM Quantum Experience."""
 
 import logging
+import warnings
 from typing import List, Optional, Union, Any
 from concurrent import futures
 
 from qiskit.circuit import QuantumCircuit
 from qiskit.pulse import Schedule
-from qiskit.providers.ibmq.apiconstants import ApiJobShareLevel
 from qiskit.providers.ibmq.utils import validate_job_tags
 from qiskit.providers.ibmq.accountprovider import AccountProvider
 
@@ -145,17 +145,10 @@ def run(
             raise IBMQJobManagerInvalidStateError(
                 'Pulse schedules found, but the backend does not support pulse schedules.')
 
-        # Validate job share level
         if job_share_level:
-            try:
-                api_job_share_level = ApiJobShareLevel(job_share_level.lower())
-            except ValueError:
-                valid_job_share_levels_str = ', '.join(level.value for level in ApiJobShareLevel)
-                raise IBMQJobManagerInvalidStateError(
-                    '"{}" is not a valid job share level. Valid job share levels are: {}'.format(
-                        job_share_level, valid_job_share_levels_str)) from None
-        else:
-            api_job_share_level = ApiJobShareLevel.NONE
+            warnings.warn("The `job_share_level` keyword is no longer supported "
+                          "and will be removed in a future release.",
+                          Warning, stacklevel=2)
 
         validate_job_tags(job_tags, IBMQJobManagerInvalidStateError)
 
@@ -169,7 +162,7 @@ def run(
 
         job_set = ManagedJobSet(name=name)
         job_set.run(experiment_list, backend=backend, executor=self._executor,
-                    job_share_level=api_job_share_level, job_tags=job_tags, **run_config)
+                    job_tags=job_tags, **run_config)
         self._job_sets.append(job_set)
 
         return job_set
diff --git a/qiskit/providers/ibmq/managed/managedjob.py b/qiskit/providers/ibmq/managed/managedjob.py
index 8e5023232..4c8aecebf 100644
--- a/qiskit/providers/ibmq/managed/managedjob.py
+++ b/qiskit/providers/ibmq/managed/managedjob.py
@@ -81,12 +81,16 @@ def submit(
             job_tags: Tags to be assigned to the job.
             **run_config: Extra arguments used to configure the run.
         """
+        if job_share_level:
+            warnings.warn("The `job_share_level` keyword is no longer supported "
+                          "and will be removed in a future release.",
+                          Warning, stacklevel=2)
 
         # Submit the job in its own future.
         logger.debug("Submitting job %s in future", job_name)
         self.future = executor.submit(
             self._async_submit, circuits=circuits, job_name=job_name, backend=backend,
-            submit_lock=submit_lock, job_share_level=job_share_level, job_tags=job_tags,
+            submit_lock=submit_lock, job_tags=job_tags,
             **run_config)
         logger.debug("Job %s future obtained", job_name)
 
@@ -96,7 +100,6 @@ def _async_submit(
             job_name: str,
             backend: IBMQBackend,
             submit_lock: Lock,
-            job_share_level: ApiJobShareLevel,
             job_tags: Optional[List[str]] = None,
             **run_config: Dict
     ) -> None:
@@ -107,7 +110,6 @@ def _async_submit(
             job_name: Name of the job.
             backend: Backend to execute the experiments on.
             submit_lock: Lock used to synchronize job submission.
-            job_share_level: Job share level.
             job_tags: Tags to be assigned to the job.
             **run_config: Extra arguments used to configure the run.
         """
@@ -121,7 +123,6 @@ def _async_submit(
                     self.job = backend.run(
                         circuits,
                         job_name=job_name,
-                        job_share_level=job_share_level.value,
                         job_tags=job_tags,
                         **run_config)
                 except IBMQBackendJobLimitError:
diff --git a/qiskit/providers/ibmq/managed/managedjobset.py b/qiskit/providers/ibmq/managed/managedjobset.py
index 81f9693f6..a5d3c4a69 100644
--- a/qiskit/providers/ibmq/managed/managedjobset.py
+++ b/qiskit/providers/ibmq/managed/managedjobset.py
@@ -19,6 +19,7 @@
 import logging
 import uuid
 import threading
+import warnings
 
 from qiskit.circuit import QuantumCircuit
 from qiskit.pulse import Schedule
@@ -102,6 +103,11 @@ def run(
         Raises:
             IBMQJobManagerInvalidStateError: If the jobs were already submitted.
         """
+        if job_share_level:
+            warnings.warn("The `job_share_level` keyword is no longer supported "
+                          "and will be removed in a future release.",
+                          Warning, stacklevel=2)
+
         if self._managed_jobs:
             raise IBMQJobManagerInvalidStateError(
                 'The jobs for this managed job set have already been submitted.')
@@ -117,9 +123,8 @@ def run(
             mjob = ManagedJob(experiments_count=len(experiments), start_index=exp_index)
             logger.debug("Submitting job %s/%s for job set %s", i+1, total_jobs, self._name)
             mjob.submit(experiments, job_name=job_name, backend=backend,
-                        executor=executor, job_share_level=job_share_level,
-                        job_tags=self._tags+[self._id_long], submit_lock=self._job_submit_lock,
-                        **run_config)
+                        executor=executor, job_tags=self._tags+[self._id_long],
+                        submit_lock=self._job_submit_lock, **run_config)
             logger.debug("Job %s submitted", i+1)
             self._managed_jobs.append(mjob)
             exp_index += len(experiments)
diff --git a/releasenotes/notes/unsupported-job-share-level-bfb3a505393f1981.yaml b/releasenotes/notes/unsupported-job-share-level-bfb3a505393f1981.yaml
new file mode 100644
index 000000000..b1dee60c4
--- /dev/null
+++ b/releasenotes/notes/unsupported-job-share-level-bfb3a505393f1981.yaml
@@ -0,0 +1,7 @@
+---
+upgrade:
+  - |
+    Job share level is no longer supported due to low adoption and the
+    corresponding interface will be removed in a future release.
+    This means you should no longer pass share_level when creating a job or use
+    :meth:`qiskit.providers.ibmq.job.IBMQJob.share_level` method to get a job's share level.


Test patch:
diff --git a/test/fake_account_client.py b/test/fake_account_client.py
index 7a962a95b..2f88b0146 100644
--- a/test/fake_account_client.py
+++ b/test/fake_account_client.py
@@ -20,7 +20,7 @@
 from concurrent.futures import ThreadPoolExecutor, wait
 
 from qiskit.test.mock.backends.poughkeepsie.fake_poughkeepsie import FakePoughkeepsie
-from qiskit.providers.ibmq.apiconstants import ApiJobStatus, API_JOB_FINAL_STATES, ApiJobShareLevel
+from qiskit.providers.ibmq.apiconstants import ApiJobStatus, API_JOB_FINAL_STATES
 from qiskit.providers.ibmq.api.exceptions import RequestsApiError, UserTimeoutExceededError
 
 
@@ -64,7 +64,7 @@ class BaseFakeJob:
     ]
 
     def __init__(self, executor, job_id, qobj, backend_name, job_tags=None,
-                 share_level=None, job_name=None):
+                 job_name=None):
         """Initialize a fake job."""
         self._job_id = job_id
         self._status = ApiJobStatus.CREATING
@@ -72,7 +72,6 @@ def __init__(self, executor, job_id, qobj, backend_name, job_tags=None,
         self._future = executor.submit(self._auto_progress)
         self._result = None
         self._backend_name = backend_name
-        self._share_level = share_level
         self._job_tags = job_tags
         self._job_name = job_name
 
@@ -103,8 +102,6 @@ def data(self):
             'creation_date': '2019-01-01T13:15:58.425972',
             '_backend_info': {'name': self._backend_name}
         }
-        if self._share_level:
-            data['share_level'] = self._share_level
         if self._job_tags:
             data['tags'] = self._job_tags.copy()
         if self._job_name:
@@ -205,7 +202,7 @@ def list_jobs_statuses(self, limit, skip, descending=True, extra_filter=None):
             job_data.reverse()
         return job_data
 
-    def job_submit(self, backend_name, qobj_dict, job_name, job_share_level,
+    def job_submit(self, backend_name, qobj_dict, job_name,
                    job_tags, *_args, **_kwargs):
         """Submit a Qobj to a device."""
         if self._job_limit != -1 and self._unfinished_jobs() >= self._job_limit:
@@ -214,7 +211,6 @@ def job_submit(self, backend_name, qobj_dict, job_name, job_share_level,
                 'maximum number of concurrent jobs, Error code: 3458.')
 
         new_job_id = uuid.uuid4().hex
-        job_share_level = job_share_level or ApiJobShareLevel.NONE
         job_class = self._job_class.pop() \
             if isinstance(self._job_class, list) else self._job_class
         new_job = job_class(
@@ -222,7 +218,6 @@ def job_submit(self, backend_name, qobj_dict, job_name, job_share_level,
             job_id=new_job_id,
             qobj=qobj_dict,
             backend_name=backend_name,
-            share_level=job_share_level.value,
             job_tags=job_tags,
             job_name=job_name)
         self._jobs[new_job_id] = new_job
diff --git a/test/http_server.py b/test/http_server.py
index fe2620491..e806b8e44 100644
--- a/test/http_server.py
+++ b/test/http_server.py
@@ -14,7 +14,7 @@
 
 import threading
 import json
-from typing import Optional, Dict
+from typing import Dict
 from http.server import BaseHTTPRequestHandler, HTTPServer
 
 
diff --git a/test/ibmq/test_experiment.py b/test/ibmq/test_experiment.py
index e625cfbb1..ea62d96ac 100644
--- a/test/ibmq/test_experiment.py
+++ b/test/ibmq/test_experiment.py
@@ -16,7 +16,7 @@
 import uuid
 from unittest import mock, SkipTest, skipIf
 from datetime import datetime, timedelta
-from typing import Optional, Union, Dict
+from typing import Optional, Union
 
 from qiskit.providers.ibmq.experiment.experiment import Experiment
 from qiskit.providers.ibmq.experiment.analysis_result import AnalysisResult, DeviceComponent
diff --git a/test/ibmq/test_ibmq_job_attributes.py b/test/ibmq/test_ibmq_job_attributes.py
index e5c63eb61..3a30e7318 100644
--- a/test/ibmq/test_ibmq_job_attributes.py
+++ b/test/ibmq/test_ibmq_job_attributes.py
@@ -288,12 +288,6 @@ def test_queue_info(self):
         # Cancel job so it doesn't consume more resources.
         cancel_job(job)
 
-    def test_invalid_job_share_level(self):
-        """Test setting a non existent share level for a job."""
-        with self.assertRaises(IBMQBackendValueError) as context_manager:
-            self.sim_backend.run(self.bell, job_share_level='invalid_job_share_level')
-        self.assertIn('not a valid job share', context_manager.exception.message)
-
     def test_esp_readout_not_enabled(self):
         """Test that an error is thrown is ESP readout is used and the backend does not support it.
         """
@@ -353,14 +347,6 @@ def test_esp_readout_enabled_not_used(self):
             delattr(self.sim_backend._configuration, "measure_esp_enabled")
             self.sim_backend._api_client = saved_api
 
-    def test_share_job_in_project(self):
-        """Test successfully sharing a job within a shareable project."""
-        job = self.sim_backend.run(self.bell, job_share_level='project')
-
-        retrieved_job = self.sim_backend.retrieve_job(job.job_id())
-        self.assertEqual(retrieved_job.share_level(), 'project',
-                         "Job {} has incorrect share level".format(job.job_id()))
-
     def test_job_tags_or(self):
         """Test using job tags with an or operator."""
         # Use a unique tag.
diff --git a/test/ibmq/test_ibmq_jobmanager.py b/test/ibmq/test_ibmq_jobmanager.py
index d52628599..22c5b0660 100644
--- a/test/ibmq/test_ibmq_jobmanager.py
+++ b/test/ibmq/test_ibmq_jobmanager.py
@@ -26,7 +26,7 @@
 from qiskit.providers.ibmq.managed.managedresults import ManagedResults
 from qiskit.providers.ibmq.managed import managedjob
 from qiskit.providers.ibmq.managed.exceptions import (
-    IBMQJobManagerJobNotFound, IBMQManagedResultDataNotAvailable, IBMQJobManagerInvalidStateError)
+    IBMQJobManagerJobNotFound, IBMQManagedResultDataNotAvailable)
 from qiskit.providers.jobstatus import JobStatus
 from qiskit.test.reference_circuits import ReferenceCircuits
 
@@ -238,21 +238,6 @@ def test_retrieve_job_set(self):
                 rjob_set.results()
                 self.assertEqual(rjob_set.statuses(), [JobStatus.DONE]*len(job_set.jobs()))
 
-    def test_share_job_in_project(self):
-        """Test sharing managed jobs within a project."""
-        job_set = self._jm.run([self._qc]*2, backend=self.fake_api_backend,
-                               max_experiments_per_job=1, job_share_level="project")
-        for job in job_set.jobs():
-            job.refresh()
-            self.assertEqual(job.share_level(), 'project',
-                             "Job {} has incorrect share level".format(job.job_id()))
-
-    def test_invalid_job_share_level(self):
-        """Test setting a non existent share level for managed jobs."""
-        self.assertRaises(IBMQJobManagerInvalidStateError, self._jm.run,
-                          [self._qc]*2, backend=self.fake_api_backend,
-                          job_share_level="invalid_job_share_level")
-
     def test_job_tags(self):
         """Test job tags for managed jobs."""
         job_tags = [uuid.uuid4().hex]