├── .bumpversion.cfg ├── .coveragerc ├── .gitignore ├── .pyup.yml ├── .travis.yml ├── LICENSE ├── README.cn.md ├── README.md ├── dev-requirements.txt ├── forgive ├── __init__.py └── db.py ├── pics ├── forgive.jpg └── logo.svg ├── release.py ├── setup.py └── tests ├── __init__.py ├── test_json_file_db.py └── test_memory_db.py /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 1.0.0 3 | commit = True 4 | tag = True 5 | tag_name = {new_version} 6 | 7 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | include = forgive/* 4 | omit = tests/* 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | .coverage 3 | README.rst 4 | coverage.xml 5 | 6 | *.pyc 7 | 8 | /.idea/ 9 | /build/ 10 | /dist/ 11 | /env/ 12 | /env2/ 13 | /env3/ 14 | 15 | *.egg-info/ 16 | -------------------------------------------------------------------------------- /.pyup.yml: -------------------------------------------------------------------------------- 1 | # autogenerated pyup.io config file 2 | # see https://pyup.io/docs/configuration/ for all available options 3 | 4 | schedule: every month 5 | update: insecure 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "2.7" 5 | - "3.5" 6 | - "3.6" 7 | 8 | script: 9 | - python -m unittest discover 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Lirian Su 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.cn.md: -------------------------------------------------------------------------------- 1 | ![logo](https://rawgit.com/hui-z/ForgiveDB/master/pics/logo.svg) 2 | 3 | [![Package](https://img.shields.io/pypi/v/forgive.svg)](https://pypi.python.org/pypi/forgive) 4 | [![Updates](https://pyup.io/repos/github/hui-z/ForgiveDB/shield.svg)](https://pyup.io/repos/github/hui-z/ForgiveDB/) 5 | [![Travis](https://img.shields.io/travis/hui-z/ForgiveDB.svg)](https://travis-ci.org/hui-z/ForgiveDB) 6 | [![Codecov](https://img.shields.io/codecov/c/github/hui-z/ForgiveDB.svg)](http://codecov.io/github/hui-z/ForgiveDB?branch=master) 7 | [![CodeHealth](https://landscape.io/github/hui-z/ForgiveDB/master/landscape.svg?style=flat)](https://landscape.io/github/hui-z/ForgiveDB/master) 8 | [![License](https://img.shields.io/github/license/hui-z/ForgiveDB.svg)](https://github.com/hui-z/ForgiveDB/blob/master/LICENSE) 9 | [![README](https://img.shields.io/badge/Intro-En-brightgreen.svg)](https://github.com/hui-z/ForgiveDB/blob/master/README.md) 10 | 11 | * 没人帮你合 Pull Request 怎么办 ? 12 | * 第三方库老是要升依赖版本怎么办 ? 13 | * 老是有新的需求怎么办 ? 14 | 15 | ![forgive](https://rawgit.com/hui-z/ForgiveDB/master/pics/forgive.jpg) 16 | 17 | [lowdb](https://github.com/typicode/lowdb) 是 JS 写的, 18 | [tinydb](http://tinydb.readthedocs.io/en/latest/intro.html) 也不够小。 19 | [ForgiveDB](https://github.com/hui-z/ForgiveDB) 才是你的归宿。 20 | 21 | ForgiveDB 是一个无依赖的微型数据库, 22 | 支持内存存储和 JSON 文件格式存储。 23 | 24 | 25 | ## 安装 26 | 27 | 使用 [pip](https://pip.pypa.io/) 一键安装,这可是个好东西。 28 | 29 | ``` python 30 | pip install forgive 31 | ``` 32 | 33 | 34 | ## 用法 35 | 36 | 代码就是最好的文档 37 | (其实是因为我们文档写的烂) 38 | 39 | ``` python 40 | from forgive import ForgiveDB 41 | 42 | file_db = ForgiveDB('/some/path') 43 | # file_db = ForgiveDB(r'C:\\some\\path') # windows 44 | file_db.set('key', 'value') 45 | value = file_db.get('key') 46 | default_value = file_db.get('no-such-key', 'default-value') 47 | 48 | # Or in memory 49 | memory_db = ForgiveDB() 50 | memory_db.set(ForgiveDB, 'simple and interesting') 51 | memory_db.get(ForgiveDB) # simple and interesting 52 | ``` 53 | 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](https://rawgit.com/hui-z/ForgiveDB/master/pics/logo.svg) 2 | 3 | [![Package](https://img.shields.io/pypi/v/forgive.svg)](https://pypi.python.org/pypi/forgive) 4 | [![Updates](https://pyup.io/repos/github/hui-z/ForgiveDB/shield.svg)](https://pyup.io/repos/github/hui-z/ForgiveDB/) 5 | [![Travis](https://img.shields.io/travis/hui-z/ForgiveDB.svg)](https://travis-ci.org/hui-z/ForgiveDB) 6 | [![Codecov](https://img.shields.io/codecov/c/github/hui-z/ForgiveDB.svg)](http://codecov.io/github/hui-z/ForgiveDB?branch=master) 7 | [![CodeHealth](https://landscape.io/github/hui-z/ForgiveDB/master/landscape.svg?style=flat)](https://landscape.io/github/hui-z/ForgiveDB/master) 8 | [![License](https://img.shields.io/github/license/hui-z/ForgiveDB.svg)](https://github.com/hui-z/ForgiveDB/blob/master/LICENSE) 9 | [![README](https://img.shields.io/badge/简介-中文-brightgreen.svg)](https://github.com/hui-z/ForgiveDB/blob/master/README.cn.md) 10 | 11 | * What if no one accepts your PR ? 12 | * What if a lib upgrades their dependencies ? 13 | * What if you get a brand new requirement ? 14 | 15 | ![forgive](https://rawgit.com/hui-z/ForgiveDB/master/pics/forgive.jpg) 16 | 17 | [lowdb](https://github.com/typicode/lowdb) is written in JavaScript, 18 | [tinydb](http://tinydb.readthedocs.io/en/latest/intro.html) is not that tiny. 19 | [ForgiveDB](https://github.com/hui-z/ForgiveDB) is your destiny. 20 | 21 | ForgiveDB is a tiny database with no depencencies, 22 | supports in-memory or json-file storage. 23 | 24 | 25 | ## Installation 26 | 27 | Use [pip](https://pip.pypa.io/). Oh you should learn it. 28 | 29 | ``` python 30 | pip install forgive 31 | ``` 32 | 33 | 34 | ## Usage 35 | 36 | Code is the best document. 37 | (Because we are not good at writing documents.) 38 | 39 | ``` python 40 | from forgive import ForgiveDB 41 | 42 | file_db = ForgiveDB('/some/path') 43 | # file_db = ForgiveDB(r'C:\\some\\path') # windows 44 | file_db.set('key', 'value') 45 | value = file_db.get('key') 46 | default_value = file_db.get('no-such-key', 'default-value') 47 | 48 | # Or in memory 49 | memory_db = ForgiveDB() 50 | memory_db.set(ForgiveDB, 'simple and interesting') 51 | memory_db.get(ForgiveDB) # simple and interesting 52 | ``` 53 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | bumpversion==0.5.3 2 | codecov==2.0.22 3 | pypandoc==1.4 4 | pytest==3.3.1 5 | twine==3.1.1 6 | 7 | -------------------------------------------------------------------------------- /forgive/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | from forgive.db import ForgiveDB # NOQA 5 | 6 | __version__ = '1.0.0' 7 | 8 | __all__ = [ 9 | '__version__' 10 | 'ForgiveDB', 11 | ] 12 | -------------------------------------------------------------------------------- /forgive/db.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | import json 5 | import os 6 | 7 | 8 | class ForgiveDB(object): 9 | _memory_db = {} 10 | 11 | def __init__(self, path=None): 12 | """ 13 | Initialize a ForgiveDB instance using path as a json file storage. 14 | If path is None, ForgiveDB will be in memory. 15 | 16 | :param path: the json file path 17 | :type path: str | unicode 18 | """ 19 | self.db_path = path 20 | 21 | @property 22 | def in_memory(self): 23 | """ 24 | indicator for if ForgiveDB is in memory 25 | :rtype: bool 26 | """ 27 | return self.db_path is None 28 | 29 | def get(self, key, default=None): 30 | """ Get key value, return default if key doesn't exist """ 31 | if self.in_memory: 32 | return self._memory_db.get(key, default) 33 | else: 34 | db = self._read_file() 35 | return db.get(key, default) 36 | 37 | def set(self, key, value): 38 | """ Set key value """ 39 | if self.in_memory: 40 | self._memory_db[key] = value 41 | else: 42 | db = self._read_file() 43 | db[key] = value 44 | with open(self.db_path, 'w') as f: 45 | f.write(json.dumps(db, ensure_ascii=False, indent=2)) 46 | 47 | def _read_file(self): 48 | """ 49 | read the db file content 50 | :rtype: dict 51 | """ 52 | if not os.path.exists(self.db_path): 53 | return {} 54 | with open(self.db_path, 'r') as f: 55 | content = f.read() 56 | return json.loads(content) 57 | -------------------------------------------------------------------------------- /pics/forgive.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hui-z/ForgiveDB/be032eb325566be02081257ee42853754b14514b/pics/forgive.jpg -------------------------------------------------------------------------------- /pics/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /release.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from __future__ import absolute_import, unicode_literals 4 | 5 | import os 6 | import sys 7 | 8 | # codecov.io project token 9 | import pypandoc 10 | 11 | codecov_token = '' or os.environ.get('FORGIVE_DB_CODECOV_TOKEN') 12 | 13 | base_dir = os.path.dirname(os.path.abspath(__file__)) 14 | 15 | sub_commands = {} 16 | 17 | 18 | def run(*commands): 19 | os.system('cd {} && {}'.format(base_dir, ' && '.join(commands))) 20 | 21 | 22 | def cmd(name): 23 | def decorator(func): 24 | sub_commands[name] = func 25 | 26 | def wrapper(*args, **kwargs): 27 | return func(*args, **kwargs) 28 | 29 | return wrapper 30 | 31 | return decorator 32 | 33 | 34 | def usage(): 35 | print('Usage: {} '.format(sys.argv[0])) 36 | print('Sub command: [{}]'.format(', '.join(sub_commands))) 37 | exit(1) 38 | 39 | 40 | @cmd('test') 41 | def test(): 42 | run('pytest --cov=./', 'codecov --token={}'.format(codecov_token)) 43 | 44 | 45 | @cmd('release') 46 | def release(*setup_commands): 47 | markdown_file = os.path.join(base_dir, 'README.md') 48 | rst_file = os.path.join(base_dir, 'README.rst') 49 | rst_content = pypandoc.convert(markdown_file, 'rst') 50 | with open(rst_file, 'wb') as f: 51 | f.write(rst_content.encode('utf-8')) 52 | run('python setup.py {}'.format(' '.join(setup_commands))) 53 | os.unlink(rst_file) 54 | 55 | 56 | if __name__ == '__main__': 57 | if len(sys.argv) < 2: 58 | usage() 59 | sub_command = sys.argv[1] 60 | if sub_command not in sub_commands: 61 | usage() 62 | func = sub_commands[sub_command] 63 | func(*sys.argv[2:]) 64 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | import os 5 | 6 | from setuptools import setup 7 | 8 | from forgive import __version__ 9 | 10 | 11 | def get_long_description(): 12 | readme_file = 'README.rst' 13 | if not os.path.exists(readme_file): 14 | readme_file = 'README.md' 15 | with open(readme_file, 'rb') as f: 16 | long_description = f.read().decode('utf-8') 17 | return long_description 18 | 19 | 20 | setup( 21 | name='forgive', 22 | version=__version__, 23 | description='Coding should be simple and fun. Certainly I will choose ForgiveDB.', 24 | long_description=get_long_description(), 25 | author='Lirian Su', 26 | author_email='liriansu@gmail.com', 27 | url='https://github.com/hui-z/ForgiveDB', 28 | license='WTFPL', 29 | packages=['forgive'], 30 | ) 31 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | -------------------------------------------------------------------------------- /tests/test_json_file_db.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | import os 5 | import tempfile 6 | import unittest 7 | 8 | from forgive import ForgiveDB 9 | 10 | 11 | class MemoryDBTests(unittest.TestCase): 12 | def setUp(self): 13 | with tempfile.NamedTemporaryFile(prefix='forgive', suffix='.db') as f: 14 | self.db_path = f.name 15 | self.db = ForgiveDB(self.db_path) 16 | 17 | def tearDown(self): 18 | if os.path.exists(self.db_path): 19 | os.unlink(self.db_path) 20 | 21 | def test_in_memory_flag(self): 22 | self.assertFalse(self.db.in_memory) 23 | 24 | def test_set(self): 25 | self.db.set('key', 'value') 26 | self.db.set('', '') 27 | self.db.set('list', 'str') 28 | self.db.set('you', 'naive') 29 | 30 | def test_get(self): 31 | self.test_set() 32 | self.assertEqual('value', self.db.get('key')) 33 | self.assertEqual('', self.db.get('')) 34 | self.assertEqual('str', self.db.get('list')) 35 | self.assertEqual('naive', self.db.get('you')) 36 | -------------------------------------------------------------------------------- /tests/test_memory_db.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | import unittest 5 | 6 | from forgive import ForgiveDB 7 | 8 | 9 | class MemoryDBTests(unittest.TestCase): 10 | def setUp(self): 11 | self.db = ForgiveDB() 12 | 13 | def test_in_memory_flag(self): 14 | self.assertTrue(self.db.in_memory) 15 | 16 | def test_set(self): 17 | self.db.set('key', 'value') 18 | self.db.set('', '') 19 | self.db.set(list, str) 20 | self.db.set(ForgiveDB, 'naive') 21 | 22 | def test_get(self): 23 | self.test_set() 24 | self.assertEqual('value', self.db.get('key')) 25 | self.assertEqual('', self.db.get('')) 26 | self.assertEqual(str, self.db.get(list)) 27 | self.assertEqual('naive', self.db.get(ForgiveDB)) 28 | --------------------------------------------------------------------------------