question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

DynamoDB: boto3 doesn't work with the local server: timestamp parsing problems

See original GitHub issue

Software versions:

Python 2.7.15 boto3 1.7.41 botocore 1.10.41 Windows 10

Using the local server, as obtained from here.

The problem appears to be that the local server returns zero timestamps for the “LastIncreaseDateTime” and “LastDecreaseDateTime” properties of the “ProvisionedThroughput” property in the response, and boto3 chokes on it. I have tracked the problem as far as the botocore parse_timestamp function. It wants to convert to the local time zone. I am on the US eastern time zone. I think those AWS timestamps are to be interpreted as epoch seconds, i.e. seconds since 1/1/1970 00:00:00 UTC. That means a zero timestamp converts to a local date/time before the epoch. I.e. at 1/1/1970 00:00:00 UTC, in my timezone, it’s still 1969, and some internal values in the dateutil implementation go negative. Some time-related functions don’t like being passed a negative number.

So here’s a proposed fix: don’t try to convert to the local time zone. Leave them as UTC. I tried changing those tzlocal() calls to tzutc(), and voila, problem solved.

Here’s a simple demo to show the problem:

import boto3
import logging

logging.basicConfig(
    level="DEBUG"
)

aws_auth = {
    "aws_access_key_id": "a_secret_key_id",
    "aws_secret_access_key": "a_secret_access_key",
    "endpoint_url": "http://localhost:8000",
    "region_name": "elbonia-1"
}

ddb = boto3.resource(
    "dynamodb",
    **aws_auth
)

my_table = ddb.create_table(
    TableName="MyTable",
    AttributeDefinitions=[
        {
            "AttributeName": "id",
            "AttributeType": "S"
        }
    ],
    KeySchema=[
        {
            "AttributeName": "id",
            "KeyType": "HASH"
        }
    ],
    ProvisionedThroughput={
        "ReadCapacityUnits": 10,
        "WriteCapacityUnits": 10
    }
)

The stack trace:

Traceback (most recent call last):
  File "C:/Programming/python/test/boto_bug/demo_bug.py", line 36, in <module>
    "WriteCapacityUnits": 10
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\boto3\resources\factory.py", line 520, in do_action
    response = action(self, *args, **kwargs)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\boto3\resources\action.py", line 83, in __call__
    response = getattr(parent.meta.client, operation_name)(**params)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\client.py", line 314, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\client.py", line 599, in _make_api_call
    operation_model, request_dict)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\endpoint.py", line 148, in make_request
    return self._send_request(request_dict, operation_model)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\endpoint.py", line 175, in _send_request
    request, operation_model, attempts)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\endpoint.py", line 256, in _get_response
    response_dict, operation_model.output_shape)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\parsers.py", line 242, in parse
    parsed = self._do_parse(response, shape)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\parsers.py", line 633, in _do_parse
    parsed = self._parse_shape(shape, original_parsed)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\parsers.py", line 295, in _parse_shape
    return handler(shape, node)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\parsers.py", line 560, in _handle_structure
    raw_value)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\parsers.py", line 295, in _parse_shape
    return handler(shape, node)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\parsers.py", line 560, in _handle_structure
    raw_value)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\parsers.py", line 295, in _parse_shape
    return handler(shape, node)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\parsers.py", line 560, in _handle_structure
    raw_value)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\parsers.py", line 295, in _parse_shape
    return handler(shape, node)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\parsers.py", line 577, in _handle_timestamp
    return self._timestamp_parser(value)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\botocore\utils.py", line 362, in parse_timestamp
    return datetime.datetime.fromtimestamp(value, tzlocal())
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\dateutil\tz\_common.py", line 140, in fromutc
    return f(self, dt)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\dateutil\tz\_common.py", line 254, in fromutc
    dt_wall = self._fromutc(dt)
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\dateutil\tz\_common.py", line 234, in _fromutc
    dtdst = enfold(dt, fold=1).dst()
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\dateutil\tz\_common.py", line 119, in enfold
    args = dt.timetuple()[:6]
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\dateutil\tz\tz.py", line 219, in dst
    if self._isdst(dt):
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\dateutil\tz\tz.py", line 282, in _isdst
    if self.is_ambiguous(dt):
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\dateutil\tz\tz.py", line 244, in is_ambiguous
    (naive_dst != self._naive_is_dst(dt - self._dst_saved)))
  File "C:\Programming\python\test\boto_bug_venv_py2\lib\site-packages\dateutil\tz\tz.py", line 248, in _naive_is_dst
    return time.localtime(timestamp + time.timezone).tm_isdst
ValueError: (22, 'Invalid argument')

Here is the actual response I got from the local Dynamo server

{
    "TableDescription": {
        "AttributeDefinitions": [
            {
                "AttributeName": "id",
                "AttributeType": "S"
            }
        ],
        "TableName": "MyTable",
        "KeySchema": [
            {
                "AttributeName": "id",
                "KeyType": "HASH"
            }
        ],
        "TableStatus": "ACTIVE",
        "CreationDateTime": 1529528737.279,
        "ProvisionedThroughput": {
            "LastIncreaseDateTime": 0.000,
            "LastDecreaseDateTime": 0.000,
            "NumberOfDecreasesToday": 0,
            "ReadCapacityUnits": 10,
            "WriteCapacityUnits": 10
        },
        "TableSizeBytes": 0,
        "ItemCount": 0,
        "TableArn": "arn:aws:dynamodb:ddblocal:000000000000:table/MyTable"
    }
}

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:4
  • Comments:19 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
SimonFrost-Armcommented, Feb 5, 2019

r4nyc’s solution doesn’t work in the UK (timezone 0). The catch all soln is a change in tz.py (I found returning False if time is the epoch in is_ambiguous() works well). However, for a botocore workaround, the following works: in Utils.py, parse_timestamp()

if isinstance(value, (int, float)): # Possibly an epoch time. if value + time.altzone < 0: return datetime.datetime.utcfromtimestamp(int(value)) else: return datetime.datetime.fromtimestamp(value, tzlocal())

3reactions
r4nyccommented, Sep 18, 2018

AWS libraries often pass in the int 0 when the value isn’t set. That may not seem like a crazy value but it is enough to cause an exception on Windows if your timezone causes time.timezone tor return a positive value.

Internally fromtimestamp tries subtracting the time.timezone from the time when it is determining if daylight savings time is in effect. The library python calls on windows can’t handle an epoch value less than 0. Setting the min_time to time.timezone * 2 guarantees the value will not go negative as it tries variations,

You can see the error easily on a Windows machine with the following code import datetime from dateutil.tz import tzlocal datetime.datetime.fromtimestamp(0, tzlocal())

site-packages\dateutil\tz\tz.py", line 248, in _naive_is_dst return time.localtime(timestamp + time.timezone).tm_isdst OSError: [Errno 22] Invalid argument

Without a patch this makes many boto scripts unrunnable on windows machines unless you change yoru environment to UTC.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Is it possible to save datetime to DynamoDB? - Stack Overflow
Okay, I see that DynamoDB does not support any date types. So the only solution is to use unix-like time as integer, or...
Read more >
DynamoDB — Boto3 Docs 1.26.34 documentation - AWS
When designing your application, keep in mind that DynamoDB does not return items in any particular order. To help parse the response by...
Read more >
Working with timestamp with time zone in your Amazon S3 ...
We use the SQL command line client tool psql to query the results in Amazon Redshift. COPY the parquet file to Redshift table...
Read more >
Python Boto3 and Amazon DynamoDB Programming Tutorial
Note: For the Python code to work, we must import Boto3 dependency in our scripts using ... We are also connecting to DynamoDB...
Read more >
How-To Work With Timestamps in DynamoDB [Ultimate Guide]
However, this downside can easily be mitigated by building a script to parse the data and convert it to a human-readable string. Timestamp...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found