Skip to content

Commit 880ed1e

Browse files
author
twhalen
committed
changed access to talk to a config directory instead of a single json
added a MongoAnyKeyStore
1 parent dca40e4 commit 880ed1e

File tree

5 files changed

+139
-23
lines changed

5 files changed

+139
-23
lines changed

py2store/__init__.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import os
2+
from py2store.util import ModuleNotFoundIgnore
3+
4+
file_sep = os.path.sep
25

36
# Imports to be able to easily get started...
4-
from py2store.stores.local_store import LocalStore, LocalBinaryStore, LocalTextStore
5-
from py2store.stores.local_store import QuickStore, QuickBinaryStore, QuickTextStore
7+
from py2store.base import KvCollection, KvReader, KvPersister, Reader, Persister
8+
from py2store.stores.local_store import LocalStore, LocalBinaryStore, LocalTextStore, PickleStore
9+
from py2store.stores.local_store import QuickStore, QuickBinaryStore, QuickTextStore, QuickJsonStore, QuickPickleStore
610
from py2store.base import Store
711
from py2store.trans import wrap_kvs
812
from py2store.access import user_configs_dict, user_configs, user_defaults_dict, user_defaults
913

10-
file_sep = os.path.sep
14+
with ModuleNotFoundIgnore():
15+
from py2store.stores.s3_store import S3BinaryStore, S3TextStore, S3PickleStore
16+
with ModuleNotFoundIgnore():
17+
from py2store.stores.mongo_store import MongoStore, MongoTupleKeyStore, MongoAnyKeyStore

py2store/access.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
respectively.
1616
"""
1717
import os
18+
from warnings import warn
1819
from py2store.util import DictAttr, str_to_var_str
1920

2021

@@ -35,17 +36,35 @@ def getenv(name, default=None):
3536
try:
3637
import json
3738

38-
user_configs_filepath = os.path.expanduser(getenv('PY2STORE_CONFIGS_JSON_FILEPATH', '~/.py2store_configs.json'))
39-
if os.path.isfile(user_configs_filepath):
40-
user_configs_dict = json.load(open(user_configs_filepath))
41-
user_configs = DictAttr(**{str_to_var_str(k): v for k, v in user_configs_dict.items()})
39+
user_configs_dirpath = os.path.expanduser(getenv('PY2STORE_CONFIGS_DIR', '~/.py2store_configs'))
40+
if os.path.isdir(user_configs_dirpath):
41+
def directory_json_items():
42+
for f in filter(lambda x: x.endswith('.json'), os.listdir(user_configs_dirpath)):
43+
filepath = os.path.join(user_configs_dirpath, f)
44+
name, _ = os.path.splitext(f)
45+
try:
46+
d = json.load(open(filepath))
47+
yield str_to_var_str(name), d
48+
except json.JSONDecodeError:
49+
warn(f"This json file couldn't be json-decoded: {filepath}")
50+
except Exception:
51+
warn(f"Unknown error when trying to json.load this file: {filepath}")
52+
53+
54+
user_configs = DictAttr(**{k: v for k, v in directory_json_items()})
55+
56+
else:
57+
warn(f"The configs directory wasn't found (please make it): {user_configs_dirpath}")
58+
warn("Configs in a single json is being deprecated")
59+
user_configs_filepath = os.path.expanduser(getenv('PY2STORE_CONFIGS_JSON_FILEPATH', '~/.py2store_configs.json'))
60+
if os.path.isfile(user_configs_filepath):
61+
user_configs_dict = json.load(open(user_configs_filepath))
62+
user_configs = DictAttr(**{str_to_var_str(k): v for k, v in user_configs_dict.items()})
4263

4364
user_defaults_filepath = os.path.expanduser(getenv('PY2STORE_DEFAULTS_JSON_FILEPATH', '~/.py2store_defaults.json'))
4465
if os.path.isfile(user_defaults_filepath):
4566
user_defaults_dict = json.load(open(user_defaults_filepath))
4667
user_defaults = DictAttr(**{str_to_var_str(k): v for k, v in user_defaults_dict.items()})
4768

4869
except Exception as e:
49-
from warnings import warn
50-
5170
warn(f"There was an exception when trying to get configs and defaults: {e}")

py2store/stores/mongo_store.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ class MongoTupleKeyStore(MongoStore):
1717
"""
1818
MongoStore using tuple keys.
1919
20-
>>> s = MongoTupleKeyStore(collection_name='tmp', key_fields=('_id', 'user'))
20+
>>> s = MongoTupleKeyStore(db_name='py2store_tests', collection_name='tmp', key_fields=('_id', 'user'))
21+
>>> for k in s: del s[k]
2122
>>> k = (1234, 'user')
2223
>>> v = {'name': 'bob', 'age': 42}
2324
>>> if k in s: # deleting all docs in tmp
@@ -46,6 +47,44 @@ def _key_of_id(self, _id):
4647
return tuple(_id[x] for x in self._key_fields)
4748

4849

50+
# TODO: Finish
51+
class MongoAnyKeyStore(MongoStore):
52+
"""
53+
MongoStore using tuple keys.
54+
55+
>>> s = MongoAnyKeyStore(db_name='py2store_tests', collection_name='tmp', )
56+
>>> for k in s: del s[k]
57+
>>> s['foo'] = {'must': 'be', 'a': 'dict'}
58+
>>> assert s['foo']
59+
{'must': 'be', 'a': 'dict'}
60+
>>> ss = MongoStore()
61+
>>> ss[{'_id': 'foo'}] # see that 'foo' was actually put in the _id field
62+
>>>
63+
"""
64+
65+
@wraps(MongoStore.__init__)
66+
def __init__(self, *args, **kwargs):
67+
super().__init__(*args, **kwargs)
68+
assert isinstance(self._key_fields, tuple), "key_fields should be a tuple or a string"
69+
assert len(self._key_fields) == 1, "key_fields must have one and only one element (a string)"
70+
self._key_field = self._key_fields[0]
71+
72+
@lazyprop
73+
def _key_fields(self):
74+
return self.store._key_fields
75+
76+
def _id_of_key(self, k):
77+
return {self._key_field: k}
78+
79+
def _key_of_id(self, _id):
80+
return _id[self._key_field]
81+
82+
def __setitem__(self, k, v):
83+
if k in self:
84+
del self[k]
85+
super().__setitem__(k, v)
86+
87+
4988
def test_mongo_store(s=MongoStore(), k=None, v=None):
5089
if k is None:
5190
k = {'_id': 'foo'}

py2store/util.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -296,17 +296,27 @@ def __enter__(self):
296296
def __exit__(self, exc_type, exc_val, exc_tb):
297297
if exc_type is ModuleNotFoundError:
298298
warn(self.msg)
299-
# if exc_val is not None and getattr(exc_val, 'name', None) is not None:
300-
# warn(f"""
301-
# It seems you don't have required `{exc_val.name}` package for this Store.
302-
# This is just a warning: The process goes on...
303-
# (But, hey, if you really need that package, try installing it by running:
304-
#
305-
# pip install {exc_val.name}
306-
#
307-
# in your terminal.
308-
# For more information: https://pypi.org/project/{exc_val.name}, or google around...
309-
# """)
310-
# else:
311-
# print("It seems you don't have a required package")
299+
# if exc_val is not None and getattr(exc_val, 'name', None) is not None:
300+
# warn(f"""
301+
# It seems you don't have required `{exc_val.name}` package for this Store.
302+
# This is just a warning: The process goes on...
303+
# (But, hey, if you really need that package, try installing it by running:
304+
#
305+
# pip install {exc_val.name}
306+
#
307+
# in your terminal.
308+
# For more information: https://pypi.org/project/{exc_val.name}, or google around...
309+
# """)
310+
# else:
311+
# print("It seems you don't have a required package")
312312
return True
313+
314+
315+
class ModuleNotFoundIgnore:
316+
def __enter__(self):
317+
pass
318+
319+
def __exit__(self, exc_type, exc_val, exc_tb):
320+
if exc_type is ModuleNotFoundError:
321+
pass
322+
return True

py2store/utils/uri_parsing.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from urllib.parse import urlsplit
2+
3+
4+
def parse_uri(uri):
5+
"""
6+
Parses DB URI string into a dict of params.
7+
:param uri: string formatted as: "scheme://username:password@host:port/database"
8+
:return: a dict with these params parsed.
9+
"""
10+
splitted_uri = urlsplit(uri)
11+
12+
if splitted_uri.path.startswith('/'):
13+
path = splitted_uri.path[1:]
14+
else:
15+
path = ''
16+
17+
return {
18+
'scheme': splitted_uri.scheme,
19+
'database': path,
20+
'username': splitted_uri.username,
21+
'password': splitted_uri.password,
22+
'hostname': splitted_uri.hostname,
23+
'port': splitted_uri.port,
24+
}
25+
26+
27+
def build_uri(
28+
scheme,
29+
database='', # TODO: Change name: Not always a database
30+
username=None,
31+
password=None,
32+
host='localhost',
33+
port=None,
34+
):
35+
"""
36+
Reverse of `parse_uri` function.
37+
Builds a URI string from provided params.
38+
"""
39+
port_ = f':{port}' if port else ''
40+
uri = f'{scheme}://{username}:{password}@{host}{port_}/{database}'
41+
return uri

0 commit comments

Comments
 (0)