Skip to content

Commit c29f46e

Browse files
committed
Released v1.1.0.
1 parent 4aaedd5 commit c29f46e

14 files changed

Lines changed: 335 additions & 0 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ __pycache__/
33
*.py[cod]
44
*$py.class
55

6+
# IDE files
7+
.idea/
8+
69
# C extensions
710
*.so
811

README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# HellPy
2+
3+
HellPy is a connector for HellDB - a key value store database.
4+
5+
## Installation
6+
7+
Install it using pip.
8+
9+
```shell script
10+
$ pip install hellpy
11+
```
12+
13+
## Documentation
14+
15+
### Initialize
16+
17+
```python
18+
from hellpy import Store
19+
20+
store = Store()
21+
22+
# To connect to different service.
23+
remote = Store(port=5555, host='222.31.43.43')
24+
```
25+
26+
### PUT
27+
28+
Writes using a PUT call to the HellDB instance store connected to.
29+
30+
```python
31+
store.put('age', 18)
32+
store.put('name', 'Manan')
33+
store.put('posts', [1, 2, 'not found'])
34+
store.put('bitmap', [
35+
[False, False, True],
36+
[False, True, False],
37+
[True, False, False],
38+
])
39+
```
40+
41+
### GET
42+
43+
Reads using a GET call.
44+
45+
```python
46+
store.get('name')
47+
# returns ["Manan"]
48+
store.get('name', 'age')
49+
# returns ["Manan", 18]
50+
store.get('invalid_key')
51+
# returns [None]
52+
```
53+
54+
### DEL
55+
56+
Deletes pair using a DEL call
57+
58+
```python
59+
store.delete('name', 'age')
60+
# returns [{'bool': True}, {'bool': True}]
61+
store.delete('invalid_key')
62+
# returns [{'bool': False}]
63+
```

hellpy/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from hellpy.store import Store

hellpy/exceptions.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class Error(Exception):
2+
pass
3+
4+
5+
class InvalidTypeError(Error):
6+
pass
7+
8+
9+
class InvalidArgumentError(Error):
10+
pass

hellpy/requests.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from typing import List
2+
3+
import logging
4+
import urllib.parse
5+
import urllib.request
6+
7+
from .tokens import ILLEGAL
8+
from hellpy.structures import Statement
9+
10+
11+
def get(url: str) -> str:
12+
try:
13+
with urllib.request.urlopen(url) as r:
14+
return r.read().decode('utf-8')
15+
except Exception as e:
16+
logging.error(e)
17+
return ILLEGAL
18+
19+
20+
def post(url: str, queries: List[Statement]) -> str:
21+
values = {'query': '\n'.join(map(str, queries))}
22+
data = urllib.parse.urlencode(values)
23+
data = data.encode('ascii')
24+
req = urllib.request.Request(url, data)
25+
try:
26+
with urllib.request.urlopen(req) as r:
27+
return r.read().decode('utf-8')
28+
except Exception as e:
29+
logging.error(e)
30+
return ILLEGAL

hellpy/store.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
from typing import List
2+
3+
import json
4+
import logging
5+
6+
from hellpy import requests
7+
from hellpy.structures import (
8+
BaseType,
9+
UrlBuilder,
10+
GetStatement,
11+
PutStatement,
12+
DelStatement,
13+
)
14+
15+
16+
class Store(object):
17+
"""
18+
Store is the main connector for HellDB from where all the
19+
reads and writes take place using an API designed to replicate
20+
the syntax of Latin, the query language developed for HellDB.
21+
"""
22+
23+
def __init__(self, port: int = 8080, host: str = '127.0.0.1'):
24+
25+
""" Constructor for Store to build HellDB's url. """
26+
27+
self.url: str = f'http://{host}:{port}'
28+
self.url_store: UrlBuilder = UrlBuilder(self.url)
29+
30+
if not self.ok():
31+
logging.fatal(f"cannot connect to HellDB instance on {self.url}")
32+
33+
@staticmethod
34+
def extract(resp_text: str) -> List[BaseType]:
35+
36+
""" Helper method to return response's BaseType list generically. """
37+
38+
response = json.loads(resp_text)
39+
if len(response['errors']) != 0:
40+
logging.fatal('\n'.join(response['errors']))
41+
else:
42+
return response['results'][0]
43+
44+
def ok(self) -> bool:
45+
46+
""" Checks whether HellDB is running healthy. """
47+
48+
resp = requests.get(self.url_store.status_url)
49+
return resp == "ok"
50+
51+
def get(self, *keys: str) -> List[BaseType]:
52+
53+
""" GET api for reading keys. """
54+
55+
get_statement = GetStatement(*keys)
56+
return Store.extract(
57+
requests.post(
58+
self.url_store.query_url,
59+
[get_statement],
60+
),
61+
)
62+
63+
def delete(self, *keys: str) -> List[BaseType]:
64+
65+
""" DEL api for deleting key value pairs. """
66+
67+
del_statement = DelStatement(*keys)
68+
return Store.extract(
69+
requests.post(
70+
self.url_store.query_url,
71+
[del_statement],
72+
)
73+
)
74+
75+
def put(self, key: str, value: BaseType) -> List[BaseType]:
76+
77+
""" PUT api for writing value to a key. """
78+
79+
put_statement = PutStatement(key, value)
80+
return Store.extract(
81+
requests.post(
82+
self.url_store.query_url,
83+
[put_statement],
84+
)
85+
)
86+
87+
def __len__(self) -> int:
88+
89+
""" Gets number of key-value pairs in HellDB. """
90+
91+
return int(requests.get(self.url_store.length_url))

hellpy/structures/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .types import *
2+
from .builders import *
3+
from .response import *
4+
from .statements import *

hellpy/structures/builders.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import json
2+
3+
from hellpy.utils import valid_type
4+
from hellpy.structures import BaseType
5+
from hellpy.exceptions import InvalidTypeError
6+
7+
8+
class Builder(object):
9+
pass
10+
11+
12+
class UrlBuilder(Builder):
13+
def __init__(self, base_url: str) -> None:
14+
self.query_url = f'{base_url}/query'
15+
self.status_url = f'{base_url}/status'
16+
self.length_url = f'{base_url}/length'
17+
18+
19+
class KeyValueBuilder(Builder):
20+
@staticmethod
21+
def keys_string(*keys: str) -> str:
22+
return ' & '.join(keys)
23+
24+
@staticmethod
25+
def value_string(value: BaseType) -> str:
26+
if not valid_type(value):
27+
raise InvalidTypeError(f'invalid type "{type(value)}" provided')
28+
return json.dumps(value)

hellpy/structures/response.py

Whitespace-only changes.

hellpy/structures/statements.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import abc
2+
3+
from hellpy.tokens import *
4+
from hellpy.structures import BaseType
5+
from hellpy.structures import KeyValueBuilder
6+
from hellpy.exceptions import InvalidArgumentError
7+
8+
9+
class Statement(abc.ABC):
10+
11+
""" A generic Statement class modelled after HellDB's statement interface. """
12+
13+
@property
14+
@abc.abstractmethod
15+
def token(self) -> str:
16+
pass
17+
18+
@abc.abstractmethod
19+
def __str__(self) -> str:
20+
pass
21+
22+
23+
class GetStatement(Statement):
24+
def __init__(self, *keys: str):
25+
if len(keys) == 0:
26+
raise InvalidArgumentError('at least one key required to read')
27+
self.keys = keys
28+
29+
@property
30+
def token(self) -> str:
31+
return GET
32+
33+
def __str__(self) -> str:
34+
keys_str = " & ".join(self.keys)
35+
return f'{self.token} {keys_str};'
36+
37+
38+
class DelStatement(Statement):
39+
def __init__(self, *keys: str):
40+
if len(keys) == 0:
41+
raise InvalidArgumentError('at least one key required to delete')
42+
self.keys_string = KeyValueBuilder.keys_string(*keys)
43+
44+
@property
45+
def token(self) -> str:
46+
return DEL
47+
48+
def __str__(self) -> str:
49+
return f'{self.token} {self.keys_string};'
50+
51+
52+
class PutStatement(Statement):
53+
def __init__(self, key: str, value: BaseType):
54+
self.key = key
55+
self.value_string: str = KeyValueBuilder.value_string(value)
56+
57+
@property
58+
def token(self) -> str:
59+
return PUT
60+
61+
def __str__(self) -> str:
62+
return f'{self.token} {self.key} {self.value_string};'

0 commit comments

Comments
 (0)