Skip to content

Commit 4634302

Browse files
committed
Extracted aws implementation from datastore.
0 parents  commit 4634302

9 files changed

+331
-0
lines changed

.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
*.pyc
2+
build/
3+
*.egg-info
4+
dist
5+
.tm_properties
6+
pypi/
7+
.tox
8+
venv/

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
language: python

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License
2+
3+
Copyright (c) 2013 Juan Batiz-Benet
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# datastore-aws
2+
3+
## datastore implementation for aws
4+
5+
See [datastore](https://github.com/datastore/datastore).
6+
7+
8+
### Install
9+
10+
From pypi (using pip):
11+
12+
sudo pip install datastore.aws
13+
14+
From pypi (using setuptools):
15+
16+
sudo easy_install datastore.aws
17+
18+
From source:
19+
20+
git clone https://github.com/datastore/datastore.aws/
21+
cd datastore.aws
22+
sudo python setup.py install
23+
24+
25+
### License
26+
27+
datastore.aws is under the MIT License.
28+
29+
### Contact
30+
31+
datastore.aws is written by [Juan Batiz-Benet](https://github.com/jbenet).
32+
It was extracted from [datastore](https://github.com/datastore/datastore)
33+
in Feb 2013.
34+
35+
Project Homepage:
36+
[https://github.com/datastore/datastore.aws](https://github.com/datastore/datastore.aws)
37+
38+
Feel free to contact me. But please file issues in github first. Cheers!
39+
40+
41+
### Hello World
42+
43+
>>> import datastore.aws
44+
>>> from boto.s3.connection import S3Connection
45+
>>>
46+
>>> s3conn = S3Connection('<aws access key>', '<aws secret key>')
47+
>>> s3bucket = s3conn.get_bucket('<bucket name>')
48+
>>> ds = datastore.aws.S3BucketDatastore(s3bucket)
49+
>>>
50+
>>> hello = datastore.Key('hello')
51+
>>> ds.put(hello, 'world')
52+
>>> ds.contains(hello)
53+
True
54+
>>> ds.get(hello)
55+
'world'
56+
>>> ds.delete(hello)
57+
>>> ds.get(hello)
58+
None

datastore/__init__.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from pkgutil import extend_path
2+
__path__ = extend_path(__path__, __name__)
3+
4+
from datastore.core import *
5+
from datastore.core import __version__

datastore/aws/__init__.py

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
2+
__version__ = '0.1.0'
3+
__author__ = 'Juan Batiz-Benet'
4+
__email__ = '[email protected]'
5+
__doc__ = '''
6+
aws datastore implementation.
7+
8+
Tested with:
9+
* boto 2.5.2
10+
11+
'''
12+
13+
#TODO: Implement queries using a key index.
14+
#TODO: Implement TTL (and key configurations)
15+
16+
17+
from boto.s3.key import Key as S3Key
18+
from boto.exception import S3ResponseError
19+
20+
import datastore
21+
22+
23+
class S3BucketDatastore(datastore.Datastore):
24+
'''Simple aws s3 datastore. Does not support queries.
25+
26+
The s3 interface is very similar to datastore's. The only differences are:
27+
- values must be strings (SerializerShimDatastore)
28+
- keys must be converted into strings
29+
'''
30+
31+
def __init__(self, s3bucket):
32+
'''Initialize the datastore with given s3 bucket `s3bucket`.
33+
34+
Args:
35+
s3bucket: An s3 bucket to use.
36+
37+
Example::
38+
39+
from boto.s3.connection import S3Connection
40+
s3conn = S3Connection('<aws access key>', '<aws secret key>')
41+
s3bucket = s3conn.get_bucket('<bucket name>')
42+
s3ds = S3BucketDatastore(s3bucket)
43+
'''
44+
self._s3bucket = s3bucket
45+
46+
def _s3key(self, key):
47+
'''Return an s3 key for given datastore key.'''
48+
k = S3Key(self._s3bucket)
49+
k.key = str(key)
50+
return k
51+
52+
@classmethod
53+
def _s3keys_get_contents_as_string_gen(cls, s3keys):
54+
'''s3 content retriever generator.'''
55+
for s3key in s3keys:
56+
yield s3key.get_contents_as_string()
57+
58+
59+
def get(self, key):
60+
'''Return the object named by key or None if it does not exist.
61+
62+
Args:
63+
key: Key naming the object to retrieve
64+
65+
Returns:
66+
object or None
67+
'''
68+
try:
69+
return self._s3key(key).get_contents_as_string()
70+
except S3ResponseError, e:
71+
return None
72+
73+
def put(self, key, value):
74+
'''Stores the object `value` named by `key`.
75+
76+
Args:
77+
key: Key naming `value`
78+
value: the object to store.
79+
'''
80+
self._s3key(key).set_contents_from_string(value)
81+
82+
def delete(self, key):
83+
'''Removes the object named by `key`.
84+
85+
Args:
86+
key: Key naming the object to remove.
87+
'''
88+
self._s3key(key).delete()
89+
90+
def query(self, query):
91+
'''Returns an iterable of objects matching criteria expressed in `query`
92+
93+
Implementations of query will be the largest differentiating factor
94+
amongst datastores. All datastores **must** implement query, even using
95+
query's worst case scenario, see :ref:class:`Query` for details.
96+
97+
Args:
98+
query: Query object describing the objects to return.
99+
100+
Raturns:
101+
iterable cursor with all objects matching criteria
102+
'''
103+
allkeys = self._s3bucket.list(prefix=str(query.key).strip('/'))
104+
iterable = self._s3keys_get_contents_as_string_gen(allkeys)
105+
return query(iterable) # must apply filters, order, etc naively.
106+
107+
108+
def contains(self, key):
109+
'''Returns whether the object named by `key` exists.
110+
111+
Args:
112+
key: Key naming the object to check.
113+
114+
Returns:
115+
boalean whether the object exists
116+
'''
117+
return self._s3key(key).exists()
118+
119+
120+
'''
121+
Hello World:
122+
123+
>>> import datastore.aws
124+
>>> from boto.s3.connection import S3Connection
125+
>>>
126+
>>> s3conn = S3Connection('<aws access key>', '<aws secret key>')
127+
>>> s3bucket = s3conn.get_bucket('<bucket name>')
128+
>>> ds = datastore.aws.S3BucketDatastore(s3bucket)
129+
>>>
130+
>>> hello = datastore.Key('hello')
131+
>>> ds.put(hello, 'world')
132+
>>> ds.contains(hello)
133+
True
134+
>>> ds.get(hello)
135+
'world'
136+
>>> ds.delete(hello)
137+
>>> ds.get(hello)
138+
None
139+
140+
'''

datastore/aws/test.py

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# NOTE: make sure you set aws s3 information
2+
3+
import unittest
4+
import logging
5+
6+
from datastore import SerializerShimDatastore
7+
from datastore.core.test.test_basic import TestDatastore
8+
9+
from . import S3BucketDatastore
10+
from boto.s3.connection import S3Connection
11+
12+
13+
aws_access_key = '<aws access key>'
14+
aws_secret_key = '<aws secret key>'
15+
16+
class TestS3BucketDatastore(TestDatastore):
17+
18+
s3bucketname = '<aws bucket name>'
19+
20+
def _deletekeys(self):
21+
keys = list(self.s3bucket.list())
22+
for key in keys:
23+
print 'deleting', key
24+
self.s3bucket.delete_key(key)
25+
26+
def setUp(self):
27+
logging.getLogger('boto').setLevel(logging.CRITICAL)
28+
29+
err = 'Use a real S3 %s. Edit datastore/test/test_aws.py.'
30+
assert self.s3bucketname != '<aws bucket name>', err % 'bucket'
31+
assert aws_access_key != '<aws access key>', err % 'access key.'
32+
assert aws_secret_key != '<aws secret key>', err % 'secret key.'
33+
34+
self.s3conn = S3Connection(aws_access_key, aws_secret_key)
35+
self.s3bucket = self.s3conn.get_bucket(self.s3bucketname)
36+
self._deletekeys() # make sure we're clean :)
37+
38+
def tearDown(self):
39+
self._deletekeys() # clean up after ourselves :]
40+
del self.s3bucket
41+
del self.s3conn
42+
43+
def test_s3(self):
44+
ds = S3BucketDatastore(self.s3bucket)
45+
ser = SerializerShimDatastore(ds)
46+
self.subtest_simple([ser], numelems=20)
47+
48+
49+
if __name__ == '__main__':
50+
unittest.main()

requirements.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
datastore==0.3.0
2+
nose==1.2.1
3+
boto>=2.5.2

setup.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env python
2+
3+
import re
4+
from setuptools import setup, find_packages
5+
6+
pkgname = 'datastore.aws'
7+
8+
# gather the package information
9+
main_py = open('datastore/aws/__init__.py').read()
10+
metadata = dict(re.findall("__([a-z]+)__ = '([^']+)'", main_py))
11+
packages = filter(lambda p: p.startswith(pkgname), find_packages())
12+
13+
# convert the readme to pypi compatible rst
14+
try:
15+
try:
16+
import pypandoc
17+
readme = pypandoc.convert('README.md', 'rst')
18+
except ImportError:
19+
readme = open('README.md').read()
20+
except:
21+
print 'something went wrong reading the README.md file.'
22+
readme = ''
23+
24+
setup(
25+
name=pkgname,
26+
version=metadata['version'],
27+
description='aws datastore implementation',
28+
long_description=readme,
29+
author=metadata['author'],
30+
author_email=metadata['email'],
31+
url='http://github.com/datastore/datastore.aws',
32+
keywords=[
33+
'datastore',
34+
'aws',
35+
's3',
36+
],
37+
packages=packages,
38+
install_requires=['datastore>=0.3.0', 'boto>=2.5.2'],
39+
test_suite='datastore.aws.test',
40+
license='MIT License',
41+
classifiers=[
42+
'Topic :: Database',
43+
'Topic :: Database :: Front-Ends',
44+
]
45+
)

0 commit comments

Comments
 (0)