├── .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 | 
2 |
3 | [](https://pypi.python.org/pypi/forgive)
4 | [](https://pyup.io/repos/github/hui-z/ForgiveDB/)
5 | [](https://travis-ci.org/hui-z/ForgiveDB)
6 | [](http://codecov.io/github/hui-z/ForgiveDB?branch=master)
7 | [](https://landscape.io/github/hui-z/ForgiveDB/master)
8 | [](https://github.com/hui-z/ForgiveDB/blob/master/LICENSE)
9 | [](https://github.com/hui-z/ForgiveDB/blob/master/README.md)
10 |
11 | * 没人帮你合 Pull Request 怎么办 ?
12 | * 第三方库老是要升依赖版本怎么办 ?
13 | * 老是有新的需求怎么办 ?
14 |
15 | 
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 | 
2 |
3 | [](https://pypi.python.org/pypi/forgive)
4 | [](https://pyup.io/repos/github/hui-z/ForgiveDB/)
5 | [](https://travis-ci.org/hui-z/ForgiveDB)
6 | [](http://codecov.io/github/hui-z/ForgiveDB?branch=master)
7 | [](https://landscape.io/github/hui-z/ForgiveDB/master)
8 | [](https://github.com/hui-z/ForgiveDB/blob/master/LICENSE)
9 | [](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 | 
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 |
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 |
--------------------------------------------------------------------------------