{ "info": { "author": "Boris Serebrov", "author_email": "dynamo_objects@googlegroups.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.5", "Topic :: Database" ], "description": "================================\ndynamo_objects\n================================\n\n.. image:: https://travis-ci.org/serebrov/dynamo_objects.png?branch=master\n :target: https://travis-ci.org/serebrov/dynamo_objects\n.. image:: https://coveralls.io/repos/serebrov/dynamo_objects/badge.svg?branch=master&service=github \n :target: https://coveralls.io/github/serebrov/dynamo_objects?branch=master\n.. image:: https://img.shields.io/pypi/v/dynamo-objects.svg\n :target: https://pypi.python.org/pypi/dynamo-objects/\n.. image:: https://img.shields.io/pypi/dm/dynamo-objects.svg\n :target: https://pypi.python.org/pypi/dynamo-objects/\n\ndynamo_objects is a set of tools to work with DynamoDB in python.\n\nIt is based on `boto `_ and provides following features:\n\n* a simple object mapper - use object notation to work with dynamo records\n* new tables are automatically created in the database, so you just write \n and deploy the new code\n* transparent support for table prefixes (multiple databases or multiple environments), you don't need to handle table prefixes in code, just set the prefix during the database connection\n* simple in-memory dynamodb mock for fast unit tests\n* supports `DynamoDB local `_ for slower tests\n* in-memory cached tables to speedup computational operations on top of DynamoDB - all data is read only once and then results are flushed back in a batch\n* additional tools - copy data from table to table, a context manager to update table throughputs and set back once operation is completed\n\n`Discussion group `_\n\n================================\nInstallation\n================================\n\n.. code-block:: bash\n\n $ pip install dynamo_objects\n\n\n================================\nDB Connection and Table Prefixes\n================================\n\nDatabase connection method adds table prefix support on top of boto's connect_to_region method.\nUsing the table prefix it is possible to switch the application to different set of tables for different environments (or just use different prefixes for different applications).\n\nUse the following snippet to connect to the database:\n\n.. code-block:: python\n\n from dynamo_objects import DynamoDatabase\n\n DynamoDatabase().connect(\n region_name='your-aws-region-name-here',\n aws_access_key_id='access-key-id',\n aws_secret_access_key='secret-access-key',\n table_prefix='my_prefix_')\n\nRegion name, and aws credentials are passed to the boto's connect_to_region method, so you can use other ways `suppored by boto `_ to specify aws credentials.\nFor example, it is not necessary to specify access key id and secret if you use IAM role.\n\nThe :code:`table_prefix` parameter is used to prefix all the table names with the string you specify.\n\nLike if you set the table_prefix to :code:`staging_`, the application will use tables like :code:`staging_user` and :code:`staging_post`. And if you set the prefix to :code:`dev_` then application will use :code:`dev_user`, :code:`dev_post`.\n\nIf you leave the table_prefix empty then it will be just :code:`user` and :code:`post`.\nThis way you can easily switch your application from one set of tables to another for different environments (development, staging, production).\n\nTo connect to the DynamoDB Local, specify the region_name='localhost':\n\n.. code-block:: python\n\n from dynamo_objects import DynamoDatabase\n DynamoDatabase().connect(\n region_name='localhost',\n table_prefix='dev_'\n )\n\n================================\nObject Mapper\n================================\n\nTo use the object mapper, define record and table objects for each DynamoDB table:\n\n.. code-block:: python\n\n from boto.dynamodb2.fields import HashKey, RangeKey\n from dynamo_objects import DynamoRecord, DynamoTable\n\n class Store(DynamoRecord):\n\n def __init__(self, **data):\n # define table fields in the __init__ method\n self.store_id = ''\n self.name = ''\n self.tags = []\n super(Store, self).__init__(**data)\n\n\n class StoreTable(DynamoTable):\n\n def __init__(self):\n super(self.__class__, self).__init__(\n 'store',\n schema=[HashKey('store_id')],\n throughput={'read': 3, 'write': 3},\n record_class=Store)\n\nHere the :code:`StoreTable` class describes the table: table name, schema (hash and optionally range keys), throughput and record class.\n\nAnd the :code:`Store` class describes the table row, \nin the :code:`__init__` method we put all the table fields.\n\nSee more examples of table/record objects in the `tests/schema.py `_ file.\n\nNow the record object can be created and used like this:\n\n.. code-block:: python\n\n store = StoreTable()\n store = Store()\n store.name = 'My Store'\n table.save(store)\n\n # or initialize the fields using the constructor\n store2 = Store(name='My Store 2')\n # change the name\n store2.name = 'Another Store'\n StoreTable().save(store)\n\nCompare this to the pure boto code where you have a dictionary-like interface:\n\n.. code-block:: python\n\n store = Item(stores, data={\n name='My Store'\n })\n # ....\n store['nmae'] = 'Another Store'\n\nIf you mistype the field name like in :code:`store['nmae']` there will be no error - you will just create a new field in the database.\nThe main purpose of the object mapper is to prevent this. \n\nThe :code:`DynamoRecord` object will raise an exception if you mistype the field name.\nTo actually go schema-less, it is possible to override the :code:`_freeze_schema` method with :code:`pass` in the method body.\n\nYou can also override the :code:`_check_data` method to do additional transformations before saving to the database (like convert data types or normalize/unify data format).\n\nFind a record, update it and save:\n\n.. code-block:: python\n\n table = MyTable()\n # will raise ItemNotFound exception if record does not exist\n record = table.get('my_hash', 'my_range')\n record.some_field = 100\n table.save(record)\n\n # to handle the case when there is no record int the database use\n # try/except\n from boto.dynamodb2.exceptions import ItemNotFound\n try:\n record = table.get('my_hash', 'my_range')\n except ItemNotFound:\n # handle the record not found case\n # ...\n\n # sometimes it is more convenient to get None for non-existing record\n # `find` method will return None if record does not exist\n record = table.find('my_hash', 'my_range')\n if record is not None:\n record.some_field = 100\n table.save(record)\n\n # get a record or create new one if record does not exist\n record = table.get('my_hash', 'my_range', create=True)\n record.some_field = 200\n table.save(record)\n\n # delete the existing record\n # `delete` method will return the deleted record, so the record data can be\n # used for some additional actions like logging\n record = table.delete('hash', 'range')\n\nThe :code:`create=True` option for the :code:`table.get()` method is useful when you want to read the data from the database or get the Null object if data is not found.\nFor example:\n\n.. code-block:: python\n\n class User(DynamoRecord):\n\n def __init__(self, *data):\n self.name = 'guest'\n self.password = ''\n super(User, self).__init__(**data)\n\n # Find the user in the database, if not found - the `user` object \n # will represent guest user\n user = table.get(user_id, create=True)\n # print user name or 'guest' (default)\n print user.name\n\nQuery and scan methods have the same interface as boto's `query_2 `_ and `scan `_, but will convert the resulting record set into :code:`DynamoRecord` objects.\n\n.. code-block:: python\n\n # parameters are the same as for boto's query_2\n # returns array of records\n # don't use when you expect a lot of data, because it will\n # fetch all the data from the database and convert to DynamoRecord\n # before returning\n records = table.query(hash__eq='value', range__gte=50)\n ...\n records = table.scan(some_field__gte=10)\n ...\n # get count\n count = table.query_count(hash__eq='value', range__gte=50)\n\nTable object also supports the atomic counter update: \n\n.. code-block:: python\n\n # increment the `counter_name` field by 2 for the \n # item with hash key = `myhashkey`\n table.update_counter('myhashkey', counter_name=2)\n\n # decrement the `counter_name` field by 2 for the \n # item with hash key = `myhashkey` and rangekey = 'myrange'\n table.update_counter('myhashkey', 'myrange', counter_name=-2)\n\nAnd it is possible to use boto's objects directly:\n\n.. code-block:: python\n\n table = MyTable()\n # the boto Table object\n boto_table = table.table\n # ... \n\n record = table.get('my_hash', 'my_range')\n # the boto Item object\n boto_item = record._item\n # ... \n\n\n================================\nMemory tables\n================================\n\nMemory tables can be used to cache DynamoDB access in-memory.\nEvery record is only read once and no data is written until you call the :code:`save_data` or :code:`save_data_batch` method.\n\n.. code-block:: python\n\n # StoreTable is a regular table definition, DynamoTable subclass\n from myschema import StoreTable\n from dynamo_objects.memorydb import MemoryTable\n\n class StoreMemoryTable(MemoryTable):\n\n def __init__(self):\n super(StoreMemoryTable, self).__init__(StoreTable())\n\nHere we define a :code:`StoreMemoryTable` class for in-memory table which wraps the :code:`StoreTable` (a regular table definition).\nNow we can do this:\n\n\n.. code-block:: python\n\n table = StoreMemoryTable()\n # read records with store_id = 1 and store_id = 2\n record = table.get(1)\n record2 = table.get(2)\n # data is not actually saved yet, no write db operations\n table.save(record)\n table.save(record2)\n # ...\n # read same records again - will fetch from memory, no db read operations\n record = table.get(1)\n record2 = table.get(2)\n # ...\n # data is not actually saved yet, no write db operations\n table.save(record)\n table.save(record2)\n # Now we flush all the data back to DynamoDB\n # the `save_data_batch` will use the `batch write` DynamoDB operation\n table.save_data_batch()\n\nThis can be very useful if you do some computational operations and need to read / write a lot of small objects to the database.\nDepending on the data structure the used read / write throughput and the whole processing time can be noticeably reduced.\n\n================================\nTesting and DynamoDB Mock\n================================\n\nIt is possible to run unit tests using the real DynamoDB connection using the table prefixes feature: you can choose some special table prefix like :code:`xx_unit_tests_`. \nThis way you'll have a set of tables for your unit tests.\n\nBut this approach is not practical - tests will be slow and will consume the read/write operations (and this will cost money).\n\nAmazon provides a `DynamoDB emulator in java `_ but it is problematic to use it during development, because it is slow and consumes a lot of memory.\n\nThe solution is a simple in-memory `DynamoDB mock module `_. \nIt is a fast, but very approximate dynamo emulation without permanent data storage.\n\nTo enable the mock, just import the :code:`dynamock` module:\n\n.. code-block:: python\n\n from dynamo_objects import database\n # once imported, the `dynamock` module will mock real DynamoDB\n # operations and forward them to the simple implementation which \n # keeps all the data in memory\n from dynamo_objects import dynamock\n\nThere is an example of the mock usage in the `tests/base.py `_ module.\n\nThis base test module can be used for any project to test parts of code which work with DynamoDB.\nYou can find examples of unit tests under the `tests `_ folder. The database schema is described in the `tests/schema.py `_.\n\nTo run all tests use :code:`nosetests` (install with :code:`pip install nose`):\n\n.. code-block:: bash\n\n nosetests\n\nBy default it will use the in-memory `DynamoDB mock `_. \nTo run tests against the DynamoDB Local use following commands:\n\n.. code-block:: bash\n\n # in the first terminal window launch the local dynamodb\n # script will download it if necessary\n ./tool/dynamodb-local.sh\n\n # in another terminal window run the tests\n DYNAMODB_MOCK= nosetests\n\nI use fast in-memory mock to run tests locally, during the development.\n\nOn the CI server tests a launched two times - first against the in-memory mock and then one more time against the DynamoDB local.\n\nHere is an example of the shell script to do this:\n\n.. code-block:: bash\n\n # Run fast tests with in-memory mock\n nosetests\n RESULT_MOCK=$?\n \n # Run slow tests with DynamoDB local\n pushd path/to/folder/with/dynamodb-local\n java -Djava.library.path=./DynamoDBLocal_lib -jar ./DynamoDBLocal.jar -inMemory -sharedDb &\n PID=$!\n popd\n echo \"Started local dynamodb: $PID\"\n DYNAMODB_MOCK= nosetests\n RESULT_LOCALDB=$?\n kill -9 $PID\n exit $(($RESULT_MOCK+$RESULT_LOCALDB))\n\n\n================================\nAdditional Tools\n================================\n\nThe `database `_ module contains few additional useful tools.\n\nThe :code:`copy_item` and :code:`copy_table_data` methods allow to copy data from table to table (for example, you may want to copy some data from staging to production):\n\n.. code-block:: python\n\n db = database.Database()\n # note: table_prefix is empty, so we can explicitly set table names\n database.connect(\n region_name='...', ...\n table_prefix='')\n num_copied = db.copy_table_data('table_name', 'staging_table_name')\n\n # copy and transform data\n def transform_fn(record):\n record.name = 'staging_' + record.name\n db.copy_table_data('table_name', 'staging_table_name', transform=transform_fn)\n\nThere are also some other useful methods to create the table, wait until the new table becomes active, delete the table, etc.\n\nThe :code:`TableThroughput` class is a context manager to update (usually set higher) throughput limits and put them back after some operation.\nIt is useful when you need to do something what requires a high read/write throughput. \n\nUsing the :code:`TableThroughput` it is possible to set high limits just before the operation and set them back just after it:\n\n.. code-block:: python\n\n high_throughputs = {\n 'table_one': { 'table': { 'read': 100, 'write': 50, }, },\n 'table_two': {\n 'table': { 'read': 60, 'write': 120, },\n 'SomeGlobalIndex': { 'read': 1, 'write': 120 }\n }\n }\n\n with database.TablesThroughput(high_throughputs):\n # now throughputs are high\n some_comutational_operation()\n # now throughputs are low again (same as before the operation)\n\n\n================================\nRelated projects\n================================\n\n* `flywheel `_ - Object mapper for Amazon's DynamoDB\n* `PynamoDB `_ - A pythonic interface to Amazon's DynamoDB\n* `Dynamodb-mapper `_ Dynamodb-mapper - a DynamoDB object mapper, based on boto", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/serebrov/dynamo_objects", "keywords": "python dynamodb orm odm", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "dynamo-objects", "package_url": "https://pypi.org/project/dynamo-objects/", "platform": "", "project_url": "https://pypi.org/project/dynamo-objects/", "project_urls": { "Homepage": "https://github.com/serebrov/dynamo_objects" }, "release_url": "https://pypi.org/project/dynamo-objects/1.0.18/", "requires_dist": null, "requires_python": "", "summary": "Simple DynamoDB object mapper and utils", "version": "1.0.18" }, "last_serial": 3990320, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "40137b3d62f7f0f2d3d2b385775a673f", "sha256": "5897ae7f7a093e2fca3483a42ff78579a85b3cb2c5471015f56274e2df236185" }, "downloads": -1, "filename": "dynamo_objects-1.0.0.tar.gz", "has_sig": false, "md5_digest": "40137b3d62f7f0f2d3d2b385775a673f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19369, "upload_time": "2015-09-18T15:37:11", "url": "https://files.pythonhosted.org/packages/d6/86/1ce35634967ac6c024a1ad9a1aa0b3abd9fe1260b47363a2fb1299a4e1ad/dynamo_objects-1.0.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "8e1e9e186f7f5863d430ee3ebddf6ad1", "sha256": "7629e932e67874bfa1a0636986e7d31bc757c01de8f3c79de1778d26f9602e0a" }, "downloads": -1, "filename": "dynamo_objects-1.0.1.tar.gz", "has_sig": false, "md5_digest": "8e1e9e186f7f5863d430ee3ebddf6ad1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19421, "upload_time": "2015-09-18T16:12:59", "url": "https://files.pythonhosted.org/packages/65/4b/276f81e234844097ff6aa86838035e00743c6d986d17c4e30953a21ea0bd/dynamo_objects-1.0.1.tar.gz" } ], "1.0.10": [ { "comment_text": "", "digests": { "md5": "f325124d5fb48a458c7aaf59c1382dd8", "sha256": "435d96fa1396ad7aac9650a16f217b6d98a20cc343e9557c450d2b19537df5a5" }, "downloads": -1, "filename": "dynamo_objects-1.0.10.tar.gz", "has_sig": false, "md5_digest": "f325124d5fb48a458c7aaf59c1382dd8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20600, "upload_time": "2016-03-30T09:25:57", "url": "https://files.pythonhosted.org/packages/77/94/f2eec6d016c4fe02017effb71582aa7e8e749ce306b9a0b03f9fe6d1c152/dynamo_objects-1.0.10.tar.gz" } ], "1.0.11": [ { "comment_text": "", "digests": { "md5": "cb44fa10f7e02daf3efea646422ddfa1", "sha256": "0ca9b29a92863a603b68d918a4e3bc3b60312abdb64dad156a23c4f2f2b9daf9" }, "downloads": -1, "filename": "dynamo_objects-1.0.11.tar.gz", "has_sig": false, "md5_digest": "cb44fa10f7e02daf3efea646422ddfa1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20625, "upload_time": "2016-04-20T21:46:13", "url": "https://files.pythonhosted.org/packages/eb/73/e2425e629db5bf5780159ecacddb2d6a333a8aec66085a5221071e51f655/dynamo_objects-1.0.11.tar.gz" } ], "1.0.12": [ { "comment_text": "", "digests": { "md5": "6c72d31800b375e1705689b3ed82cea4", "sha256": "066c740a575b83a8e4e210045cf298397302a2d8ebcb34e915d839daa80fef19" }, "downloads": -1, "filename": "dynamo_objects-1.0.12.tar.gz", "has_sig": false, "md5_digest": "6c72d31800b375e1705689b3ed82cea4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20732, "upload_time": "2016-04-20T22:55:10", "url": "https://files.pythonhosted.org/packages/b6/59/d2a495d482322e67209ebfa983d1498aacf9f732f36d971c6b3c74d63cea/dynamo_objects-1.0.12.tar.gz" } ], "1.0.15": [ { "comment_text": "", "digests": { "md5": "86d460075a5e0c0dd06de838a4daaf78", "sha256": "b20bd082106a16b7eaa17ad1177cde43f989345afa4ea02ae0eda31617180645" }, "downloads": -1, "filename": "dynamo_objects-1.0.15.tar.gz", "has_sig": false, "md5_digest": "86d460075a5e0c0dd06de838a4daaf78", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20773, "upload_time": "2016-04-26T10:19:18", "url": "https://files.pythonhosted.org/packages/f2/86/1ec6646dfd5c7e48f40310c361820cabb150bc422f2752c0f4e8439e6cd9/dynamo_objects-1.0.15.tar.gz" } ], "1.0.16": [ { "comment_text": "", "digests": { "md5": "59095e4d1ebfdb5f5069f5f8c1ebf2fd", "sha256": "e275998f8163ef389c1b5c0ec689e3f4c5789930d36f344e616ae73fbeccd26a" }, "downloads": -1, "filename": "dynamo_objects-1.0.16.tar.gz", "has_sig": false, "md5_digest": "59095e4d1ebfdb5f5069f5f8c1ebf2fd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20775, "upload_time": "2016-04-26T10:48:44", "url": "https://files.pythonhosted.org/packages/73/1f/14fc50304c794c9a3c691256ea0e3f421341a9be2f869b85e3e41df8eb4f/dynamo_objects-1.0.16.tar.gz" } ], "1.0.17": [ { "comment_text": "", "digests": { "md5": "c438dffb8c93e950fec7963365051c88", "sha256": "415054d6cb6afe007c2e42cee25c25fde2d468e230bc35cccf03e631819099b0" }, "downloads": -1, "filename": "dynamo_objects-1.0.17.tar.gz", "has_sig": false, "md5_digest": "c438dffb8c93e950fec7963365051c88", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20772, "upload_time": "2016-04-26T10:58:33", "url": "https://files.pythonhosted.org/packages/8f/61/48eb708eba9116bfb5d49ef574c5127c69dfbd33612a253f8d262b307c28/dynamo_objects-1.0.17.tar.gz" } ], "1.0.18": [ { "comment_text": "", "digests": { "md5": "b3fa9777310b9d64f6cda24c328bc121", "sha256": "f8f7b39d77e15f960869e76166b526edb32475b71683274c19d7e99aac335106" }, "downloads": -1, "filename": "dynamo_objects-1.0.18.tar.gz", "has_sig": false, "md5_digest": "b3fa9777310b9d64f6cda24c328bc121", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20988, "upload_time": "2018-06-22T19:33:32", "url": "https://files.pythonhosted.org/packages/23/f0/9e92559e3a5be2eb9e432be3812e086d495c1779b78bc57a4b666b496228/dynamo_objects-1.0.18.tar.gz" } ], "1.0.2": [ { "comment_text": "", "digests": { "md5": "55ab81056adfbb7fe0b2267cba9cd112", "sha256": "8719a7592ec6194351de225980755ebff1fe94a79c2d894f89abd51c3f4fc3d5" }, "downloads": -1, "filename": "dynamo_objects-1.0.2.tar.gz", "has_sig": false, "md5_digest": "55ab81056adfbb7fe0b2267cba9cd112", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19460, "upload_time": "2015-09-18T16:23:03", "url": "https://files.pythonhosted.org/packages/6d/a1/0cad7c8cf9cbb44c03d7a092be36ee501f031fc8433c56efa546b7ba9084/dynamo_objects-1.0.2.tar.gz" } ], "1.0.3": [ { "comment_text": "", "digests": { "md5": "f1861006d4cfa80b352fe816ffa2aac5", "sha256": "5474545ab59e53f5d2c1c8aa5759a77c95bf8787d30265161f3ac347dd80e809" }, "downloads": -1, "filename": "dynamo_objects-1.0.3.tar.gz", "has_sig": false, "md5_digest": "f1861006d4cfa80b352fe816ffa2aac5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19568, "upload_time": "2015-09-18T16:30:25", "url": "https://files.pythonhosted.org/packages/74/1d/b3dcd4ac2544bfb0e8cd46bfb8d11d25fa1e83bf2683fd85d8e60c4ac027/dynamo_objects-1.0.3.tar.gz" } ], "1.0.4": [ { "comment_text": "", "digests": { "md5": "fde3b13f6681de8008dd67576b48961e", "sha256": "4184e9a6b54b926681a0f0d77ccc753634ee28f9c9441386d82ec7b21fc1004b" }, "downloads": -1, "filename": "dynamo_objects-1.0.4.tar.gz", "has_sig": false, "md5_digest": "fde3b13f6681de8008dd67576b48961e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19705, "upload_time": "2015-09-18T18:57:53", "url": "https://files.pythonhosted.org/packages/ba/20/d3ecec9ff4cbe7930295e21379a71b78b7d233592dac7bbcc5f8f6b00bd5/dynamo_objects-1.0.4.tar.gz" } ], "1.0.5": [ { "comment_text": "", "digests": { "md5": "f769714dc72ee99731b3c41b4c1b3b5a", "sha256": "5e57b2815cd1e43434f05354bafbf4a4f429e09b311fdd056158a6b51fff0c12" }, "downloads": -1, "filename": "dynamo_objects-1.0.5.tar.gz", "has_sig": false, "md5_digest": "f769714dc72ee99731b3c41b4c1b3b5a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19700, "upload_time": "2015-11-30T12:40:30", "url": "https://files.pythonhosted.org/packages/d2/49/1c8abebc994ccfa14bf24d7c1995b9d4b038278eba70892dc759a3060b2e/dynamo_objects-1.0.5.tar.gz" } ], "1.0.6": [ { "comment_text": "", "digests": { "md5": "f1abcf4f041909b5e82abd6a58e7b35a", "sha256": "d619b298526e4fa0aad6e4283ba1518ad4070f1e415735cdaed200e1a62eecbe" }, "downloads": -1, "filename": "dynamo_objects-1.0.6.tar.gz", "has_sig": false, "md5_digest": "f1abcf4f041909b5e82abd6a58e7b35a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20202, "upload_time": "2016-02-25T19:35:22", "url": "https://files.pythonhosted.org/packages/ca/9c/3ae158ad3fc6550255bd9c2cb4614a670afe15b7964820044304a6aed161/dynamo_objects-1.0.6.tar.gz" } ], "1.0.7": [ { "comment_text": "", "digests": { "md5": "02b9fb05fa70731d7b088e68e6d425d7", "sha256": "e6db242b349505c7b445186e6746e60b79cb1bd9bc9153c0d460c894131cb639" }, "downloads": -1, "filename": "dynamo_objects-1.0.7.tar.gz", "has_sig": false, "md5_digest": "02b9fb05fa70731d7b088e68e6d425d7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20231, "upload_time": "2016-02-25T19:51:58", "url": "https://files.pythonhosted.org/packages/e2/39/3cfce5e78cb9b006c5d22965a33a4af3dc3c1ec75e75209a19a822065a44/dynamo_objects-1.0.7.tar.gz" } ], "1.0.8": [ { "comment_text": "", "digests": { "md5": "69f8b8869193b9fa59bf8edf070eb772", "sha256": "4e0ddebbf64eabda8a740ab66c371a2b456cfe688a16db5992b8a60a6ecbeec4" }, "downloads": -1, "filename": "dynamo_objects-1.0.8.tar.gz", "has_sig": false, "md5_digest": "69f8b8869193b9fa59bf8edf070eb772", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20505, "upload_time": "2016-02-25T19:58:13", "url": "https://files.pythonhosted.org/packages/d5/d0/c21a3ec2caab336d52a4834a7c4e2b576a66fe58e91a145482d3a7a5a2f9/dynamo_objects-1.0.8.tar.gz" } ], "1.0.9": [ { "comment_text": "", "digests": { "md5": "ae4cd2eddf24490d439e24a5af114f5f", "sha256": "145e0c55e93c3201607dcecd9aeb689e8591ab8cd6779019ecda9af5da5e84d5" }, "downloads": -1, "filename": "dynamo_objects-1.0.9.tar.gz", "has_sig": false, "md5_digest": "ae4cd2eddf24490d439e24a5af114f5f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20587, "upload_time": "2016-02-26T00:54:13", "url": "https://files.pythonhosted.org/packages/16/8e/c3d5afd89d3ec9c913e1369e9101d3a06673c9c83bc1fab1e6faf168763a/dynamo_objects-1.0.9.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b3fa9777310b9d64f6cda24c328bc121", "sha256": "f8f7b39d77e15f960869e76166b526edb32475b71683274c19d7e99aac335106" }, "downloads": -1, "filename": "dynamo_objects-1.0.18.tar.gz", "has_sig": false, "md5_digest": "b3fa9777310b9d64f6cda24c328bc121", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20988, "upload_time": "2018-06-22T19:33:32", "url": "https://files.pythonhosted.org/packages/23/f0/9e92559e3a5be2eb9e432be3812e086d495c1779b78bc57a4b666b496228/dynamo_objects-1.0.18.tar.gz" } ] }