amazon-braket / amazon-braket-sdk-python

A Python SDK for interacting with quantum devices on Amazon Braket
https://aws.amazon.com/braket/
Apache License 2.0
294 stars 118 forks source link

AwsDevice 'str' object has no attribute 'get' #979

Closed rjain37 closed 1 month ago

rjain37 commented 1 month ago

Describe the bug When trying to access AwsDevice, I get the following error:

AttributeError: 'str' object has no attribute 'get'

To reproduce The following code causes this error for me:

from braket.aws import AwsDevice

device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1")

Here is the whole error:

Cell In[2], line 1
----> 1 device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1")

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/braket/aws/aws_device.py:113, in AwsDevice.__init__(self, arn, aws_session, noise_model)
    111 self._poll_interval_seconds = None
    112 self._type = None
--> 113 self._aws_session = self._get_session_and_initialize(aws_session or AwsSession())
    114 self._ports = None
    115 self._frames = None

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/braket/aws/aws_device.py:339, in AwsDevice._get_session_and_initialize(self, session)
    334 def _get_session_and_initialize(self, session: AwsSession) -> AwsSession:
    335     device_region = AwsDevice.get_device_region(self._arn)
    336     return (
    337         self._get_regional_device_session(session)
    338         if device_region
--> 339         else self._get_non_regional_device_session(session)
    340     )

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/braket/aws/aws_device.py:362, in AwsDevice._get_non_regional_device_session(self, session)
    360 current_region = session.region
    361 try:
--> 362     self._populate_properties(session)
    363     return session
    364 except ClientError as e:

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/braket/aws/aws_device.py:381, in AwsDevice._populate_properties(self, session)
    380 def _populate_properties(self, session: AwsSession) -> None:
--> 381     metadata = session.get_device(self._arn)
    382     self._name = metadata.get("deviceName")
    383     self._status = metadata.get("deviceStatus")

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/braket/aws/aws_session.py:634, in AwsSession.get_device(self, arn)
    625 def get_device(self, arn: str) -> dict[str, Any]:
    626     """Calls the Amazon Braket `get_device` API to retrieve device metadata.
    627 
    628     Args:
   (...)
    632         dict[str, Any]: The response from the Amazon Braket `GetDevice` operation.
    633     """
--> 634     return self.braket_client.get_device(deviceArn=arn)

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/botocore/client.py:565, in ClientCreator._create_api_method.<locals>._api_call(self, *args, **kwargs)
    561     raise TypeError(
    562         f"{py_operation_name}() only accepts keyword arguments."
    563     )
    564 # The "self" in this scope is referring to the BaseClient.
--> 565 return self._make_api_call(operation_name, kwargs)

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/botocore/client.py:1001, in BaseClient._make_api_call(self, operation_name, api_params)
    997     maybe_compress_request(
    998         self.meta.config, request_dict, operation_model
    999     )
   1000     apply_request_checksum(request_dict)
-> 1001     http, parsed_response = self._make_request(
   1002         operation_model, request_dict, request_context
   1003     )
   1005 self.meta.events.emit(
   1006     'after-call.{service_id}.{operation_name}'.format(
   1007         service_id=service_id, operation_name=operation_name
   (...)
   1012     context=request_context,
   1013 )
   1015 if http.status_code >= 300:

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/botocore/client.py:1027, in BaseClient._make_request(self, operation_model, request_dict, request_context)
   1025 def _make_request(self, operation_model, request_dict, request_context):
   1026     try:
-> 1027         return self._endpoint.make_request(operation_model, request_dict)
   1028     except Exception as e:
   1029         self.meta.events.emit(
   1030             'after-call-error.{service_id}.{operation_name}'.format(
   1031                 service_id=self._service_model.service_id.hyphenize(),
   (...)
   1035             context=request_context,
   1036         )

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/botocore/endpoint.py:119, in Endpoint.make_request(self, operation_model, request_dict)
    113 def make_request(self, operation_model, request_dict):
    114     logger.debug(
    115         "Making request for %s with params: %s",
    116         operation_model,
    117         request_dict,
    118     )
--> 119     return self._send_request(request_dict, operation_model)

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/botocore/endpoint.py:199, in Endpoint._send_request(self, request_dict, operation_model)
    197 self._update_retries_context(context, attempts)
    198 request = self.create_request(request_dict, operation_model)
--> 199 success_response, exception = self._get_response(
    200     request, operation_model, context
    201 )
    202 while self._needs_retry(
    203     attempts,
    204     operation_model,
   (...)
    207     exception,
    208 ):
    209     attempts += 1

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/botocore/endpoint.py:241, in Endpoint._get_response(self, request, operation_model, context)
    235 def _get_response(self, request, operation_model, context):
    236     # This will return a tuple of (success_response, exception)
    237     # and success_response is itself a tuple of
    238     # (http_response, parsed_dict).
    239     # If an exception occurs then the success_response is None.
    240     # If no exception occurs then exception is None.
--> 241     success_response, exception = self._do_get_response(
    242         request, operation_model, context
    243     )
    244     kwargs_to_emit = {
    245         'response_dict': None,
    246         'parsed_response': None,
    247         'context': context,
    248         'exception': exception,
    249     }
    250     if success_response is not None:

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/botocore/endpoint.py:308, in Endpoint._do_get_response(self, request, operation_model, context)
    306 protocol = operation_model.metadata['protocol']
    307 parser = self._response_parser_factory.create_parser(protocol)
--> 308 parsed_response = parser.parse(
    309     response_dict, operation_model.output_shape
    310 )
    311 # Do a second parsing pass to pick up on any modeled error fields
    312 # NOTE: Ideally, we would push this down into the parser classes but
    313 # they currently have no reference to the operation or service model
    314 # The parsers should probably take the operation model instead of
    315 # output shape but we can't change that now
    316 if http_response.status_code >= 300:

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/botocore/parsers.py:250, in ResponseParser.parse(self, response, shape)
    248         return parsed
    249     else:
--> 250         parsed = self._do_error_parse(response, shape)
    251 else:
    252     parsed = self._do_parse(response, shape)

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/botocore/parsers.py:1015, in RestJSONParser._do_error_parse(self, response, shape)
   1014 def _do_error_parse(self, response, shape):
-> 1015     error = super()._do_error_parse(response, shape)
   1016     self._inject_error_code(error, response)
   1017     return error

File /opt/miniconda3/envs/notebooks/lib/python3.11/site-packages/botocore/parsers.py:698, in BaseJSONParser._do_error_parse(self, response, shape)
    689 headers = response['headers']
    690 # Error responses can have slightly different structures for json.
    691 # The basic structure is:
    692 #
   (...)
    696 # The error message can either come in the 'message' or 'Message' key
    697 # so we need to check for both.
--> 698 error['Error']['Message'] = body.get(
    699     'message', body.get('Message', '')
    700 )
    701 # if the message did not contain an error code
    702 # include the response status code
    703 response_code = response.get('status_code')

AttributeError: 'str' object has no attribute 'get'

Expected behavior I would expect this to run without issues at all.

System information A description of your system. Please provide:

Additional context I think that this is an issue related to something on my computer, but I am not sure how to pinpoint it.

math411 commented 1 month ago

Hi @rjain37, looking at the stack trace, it seems there may be an issue with the botocore installation or some downstream dependency. Could you add the output of:

pip freeze | grep boto 
rjain37 commented 1 month ago

@math411 this is what I get as the output of pip freeze | grep boto:

boto3==1.34.116
boto3-stubs==1.34.115
botocore @ git+https://github.com/qBraid/botocore.git@ee5433509fcbbd2d6dd0c04bb3d101db7648eaac
botocore-stubs==1.34.94

Thanks!

math411 commented 1 month ago

botocore-stubs==1.34.94 seems to be an outdated package that has some failing builds - what is your use case with this?

I see you're installing from a qBraid fork for botocore which seems to be up to date but just in case, could you install from the Pypi version?

rjain37 commented 1 month ago

Ah that seems to have fixed it. Thank you so much.