create directory path of file path if it doesn't exist

This commit is contained in:
Kevin Froman 2019-09-08 03:47:59 -05:00
parent e9e4999973
commit a33a56e6ad
5 changed files with 44 additions and 22 deletions

View File

@ -2,6 +2,10 @@
This project uses Semantic Versioning This project uses Semantic Versioning
## 0.2.0
Added automatic creation of path for file writing
## 0.1.1 ## 0.1.1
Added: Added:

View File

@ -1,4 +1,4 @@
''' """
DeadSimpleKv. An extremely simple key-value storage system with cache DeadSimpleKv. An extremely simple key-value storage system with cache
Copyright (C) 2019 Kevin Froman Copyright (C) 2019 Kevin Froman
@ -14,13 +14,13 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
''' """
import json, time, math, os, atexit import json, time, math, os, atexit
def _is_serializable(data): def _is_serializable(data):
''' """
Test if something is able to be in JSON format Test if something is able to be in JSON format
''' """
try: try:
json.dumps(data) json.dumps(data)
return True return True
@ -29,12 +29,12 @@ def _is_serializable(data):
class DeadSimpleKV: class DeadSimpleKV:
def __init__(self, file_path=None, refresh_seconds=0, flush_seconds=0, flush_on_exit=True): def __init__(self, file_path=None, refresh_seconds=0, flush_seconds=0, flush_on_exit=True):
''' """
Accepts a file path and refresh. Accepts a file path and refresh.
If file_path is not set, no data will be written to disk. If file_path is not set, no data will be written to disk.
Refresh is an integer specifying how many seconds should pass before data is re-read from disk. Refresh is an integer specifying how many seconds should pass before data is re-read from disk.
If refresh_seconds or flush_seconds are set to None they will not be done automatically (except where flush_on_exit applies) If refresh_seconds or flush_seconds are set to None they will not be done automatically (except where flush_on_exit applies)
''' """
temp_time = DeadSimpleKV._get_epoch() # Initialization time temp_time = DeadSimpleKV._get_epoch() # Initialization time
self.file_path = file_path # The file path where we write our data in JSON format self.file_path = file_path # The file path where we write our data in JSON format
self.refresh_seconds = refresh_seconds # How often to refresh the self.refresh_seconds = refresh_seconds # How often to refresh the
@ -43,6 +43,10 @@ class DeadSimpleKV:
self._last_refresh = temp_time # time last refreshed from disk self._last_refresh = temp_time # time last refreshed from disk
self._last_flush = temp_time # time last flushed to disk self._last_flush = temp_time # time last flushed to disk
if not file_path is None:
abs_path = os.path.dirname(os.path.abspath(file_path))
if not os.path.exists(abs_path): os.makedirs(abs_path)
try: try:
if os.path.exists(file_path): if os.path.exists(file_path):
self.refresh() self.refresh()
@ -53,9 +57,9 @@ class DeadSimpleKV:
atexit.register(self.flush) atexit.register(self.flush)
def get(self, key): def get(self, key):
''' """
Accepts key, which must be json serializable Accepts key, which must be json serializable
''' """
self._do_auto_refresh() self._do_auto_refresh()
try: try:
return self._data[key] return self._data[key]
@ -63,10 +67,10 @@ class DeadSimpleKV:
return None return None
def put(self, key, value): def put(self, key, value):
''' """
Value setter. Will automatically flush if auto_flush is True and file_path is not None Value setter. Will automatically flush if auto_flush is True and file_path is not None
Will return value error if either key or value are not JSON serializable Will return value error if either key or value are not JSON serializable
''' """
self._data[key] = value # Set the key self._data[key] = value # Set the key
# Check if key and value can be put in JSON # Check if key and value can be put in JSON
@ -78,16 +82,16 @@ class DeadSimpleKV:
return (key, value) return (key, value)
def delete(self, key): def delete(self, key):
''' """
Deletes value. Will automatically flush if auto_flush is True and file_path is not None Deletes value. Will automatically flush if auto_flush is True and file_path is not None
''' """
del self._data[key] del self._data[key]
self._do_auto_flush() self._do_auto_flush()
def refresh(self): def refresh(self):
''' """
Refresh data and then mark time read. Can be manually called Refresh data and then mark time read. Can be manually called
''' """
try: try:
self._data = json.loads(DeadSimpleKV._read_in(self.file_path)) self._data = json.loads(DeadSimpleKV._read_in(self.file_path))
except FileNotFoundError: except FileNotFoundError:
@ -95,9 +99,9 @@ class DeadSimpleKV:
self._last_refresh = DeadSimpleKV._get_epoch() self._last_refresh = DeadSimpleKV._get_epoch()
def flush(self): def flush(self):
''' """
Write out then mark time flushed. Can be manually called Write out to file then mark time flushed. Can be manually called
''' """
DeadSimpleKV._write_out(self.file_path, json.dumps(self._data)) DeadSimpleKV._write_out(self.file_path, json.dumps(self._data))
self._last_flush = DeadSimpleKV._get_epoch() self._last_flush = DeadSimpleKV._get_epoch()
@ -119,12 +123,12 @@ class DeadSimpleKV:
@staticmethod @staticmethod
def _write_out(file_path, data): def _write_out(file_path, data):
'''Write out the raw data''' """Write out the raw data"""
with open(file_path, 'w') as write_file: with open(file_path, 'w') as write_file:
write_file.write(data) write_file.write(data)
@staticmethod @staticmethod
def _read_in(file_path): def _read_in(file_path):
'''Read in raw data''' """Read in raw data"""
with open(file_path, 'r') as read_file: with open(file_path, 'r') as read_file:
return read_file.read() return read_file.read()

View File

@ -10,4 +10,5 @@ for f in tests/*.py; do
let "ran++" let "ran++"
done done
echo "ran $ran test files successfully in $SECONDS seconds" echo "ran $ran test files successfully in $SECONDS seconds"
rm *.dat rm *.dat
rm -rf my_test_dir

View File

@ -1,7 +1,7 @@
from distutils.core import setup from distutils.core import setup
setup(name='deadsimplekv', setup(name='deadsimplekv',
version='0.1.1', version='0.2.0',
description='Very simple key-value store for Python', description='Very simple key-value store for Python',
author='Kevin Froman', author='Kevin Froman',
author_email='beardog@mailbox.org', author_email='beardog@mailbox.org',

View File

@ -70,7 +70,7 @@ class TestInit(unittest.TestCase):
self.assertIsNone(kv.get('my_key')) self.assertIsNone(kv.get('my_key'))
def test_invalid(self): def test_invalid(self):
kv = deadsimplekv.DeadSimpleKV(get_test_id()) kv = deadsimplekv.DeadSimpleKV(get_test_id(), flush_on_exit=False)
try: try:
kv.put('rekt', b'bits') kv.put('rekt', b'bits')
except ValueError: except ValueError:
@ -96,6 +96,19 @@ class TestInit(unittest.TestCase):
self.assertEqual(test_data['my_key'], 'test') self.assertEqual(test_data['my_key'], 'test')
def test_flush_no_path(self):
# assert data gets flushed when the path is not made yet
test_id = "my_test_dir/my_second_dir/" + get_test_id()
kv = deadsimplekv.DeadSimpleKV(test_id)
kv.put('my_key', 'test2')
with open(test_id, 'r') as test_file:
test_data = json.loads(test_file.read())
self.assertEqual(test_data['my_key'], 'test2')
def test_time(self): def test_time(self):
self.assertEqual(deadsimplekv.DeadSimpleKV._get_epoch(), math.floor(time.time())) self.assertEqual(deadsimplekv.DeadSimpleKV._get_epoch(), math.floor(time.time()))