Search code examples
pythonpython-3.xaws-clidynamodb-queries

awscli dynamodb not executing queries via task runner but executes properly via shell


I have a strange issue with string escaping: since the values I'm trying to query are byte strings, if I run a command via the shell such as:

aws --endpoint-url=http://localhost:4566 \
   dynamodb query \
  --table-name user_storage \
  --key-condition-expression "id = :v1" \
  --expression-attribute-values '{":v1": {"S": "b\'id-value-here\'"}}'

It executes correctly and prints output:

{
    "Items": [
        {
            "result": {
                "B": "this is the result value"
            },
            "id": {
                "S": "b'id-value-here'"
            },
            "timestamp": {
                "N": "1630340264.7819724"
            }
        }
    ],
    "Count": 1,
    "ScannedCount": 1,
    "ConsumedCapacity": null
}

However, I'm trying to execute it via the Invoke task runner for python. It is the same exact command but I can't seem to get the encoding of the byte string to pass to the shell correctly. I've tried many combinations of encoding and escaping the inner quotes around the id value, nothing seems to work the same as the shell command. (Note: I'm using Python 3.6.9)

@task
def get_user(cmd, user_id):
    c1 = f'''aws --endpoint-url=http://localhost:4566 \
            dynamodb query \
            --table-name user_storage \
            --key-condition-expression "id = :v1" \
            --expression-attribute-values '{{":v1": {{"S": "{user_id.encode()}"}}}}'
    '''
    cmd.run(c1, echo=True)

> invoke get-user id-value-here
aws --endpoint-url=http://localhost:4566 dynamodb query --table-name user_storage --key-condition-expression "id = :v1" --expression-attribute-values '{":v1": {"S": "b'id-value-here'"}}'
    
{
    "Items": [],
    "Count": 0,
    "ScannedCount": 0,
    "ConsumedCapacity": null
}

What gives? I've been banging my head against the wall over this for awhile now. Can't seem to get it to pass the encoded id value properly.


Solution

  • Turns out, this is a quirk with how Python handles f-strings: https://www.python.org/dev/peps/pep-0498/#no-binary-f-strings

    Workaround was to write the payload to a json file and pass that to awscli:

    filename = "key.json"
    payload = '{{":v1": {{"S": "{0}"}}}}'.format(user_id.encode())
    
    with open(f"./{filename}", 'w') as f:
        dump(loads(payload), f)
    
    c1 = '''aws --endpoint-url=http://localhost:4566 \
            dynamodb query \
            --table-name user_storage \
            --key-condition-expression "id = :v1" \
            --expression-attribute-values file://{0}
    '''.format(filename)
    
    cmd.run(c1, echo=True)