Skip to content

Commit

Permalink
Merge tag '1.2.0' into develop
Browse files Browse the repository at this point in the history
* Feature: Improved compatibility to PY27, PY32, PY33, PY34, and PYPY
* Feature: Supports multiple config files.
* Feature: Writes less, smarter logic on deciding if a write is necessary.
* Feature: Delegates writes to a background process.
* Testing: Renamed tests to be more descriptive of expectations.
* Testing: Added a bunch of tests describing different scenarios.
* Massive Refactoring
  • Loading branch information
manuphatak committed May 19, 2015
2 parents 086f0da + 2cc26bf commit 740279c
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 19 deletions.
11 changes: 10 additions & 1 deletion HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,17 @@ History

Next Release
++++++++++++
* Feature: Supports multiple config files
* Stay Tuned.

1.2.0 (2015-05-18)
++++++++++++++++++
* Feature: Improved compatibility to PY27, PY32, PY33, PY34, and PYPY
* Feature: Supports multiple config files.
* Feature: Writes less, smarter logic on deciding if a write is necessary.
* Feature: Delegates writes to a background process.
* Testing: Renamed tests to be more descriptive of expectations.
* Testing: Added a bunch of tests describing different scenarios.
* Massive Refactoring

1.1.0 (2015-04-15)
++++++++++++++++++
Expand Down
7 changes: 5 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ A convenience utility for working with JSON configuration files.
Features
--------

* Automatically syncs file on changes
* Automatically syncs file on changes.
* Automatically handles complicated nested data structures.
* Lightweight (<5KB) and Fast.
* Takes advantage of Python's native dictionary syntax.
* Tested against python 2.7, 3.4, and pypy. (2.6, 3.2, 3.3 are in the works)
* Tested against python 2.7, 3.2, 3.3, 3.4, and PYPY.
* Saves silently in the background.
* Unit Tested with high coverage.
* Fully documented at https://json-config.readthedocs.org

.. code-block:: python
Expand Down
2 changes: 1 addition & 1 deletion json_config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
"""
__author__ = 'Manu Phatak'
__email__ = 'bionikspoon@gmail.com'
__version__ = '1.1.0'
__version__ = '1.2.0'

from .configuration import connect
50 changes: 35 additions & 15 deletions json_config/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
"""
A convenience utility for working with JSON config files.
"""
from __future__ import unicode_literals
from functools import wraps, partial
from collections import defaultdict

import json
from threading import Timer
import threading
from functools import wraps, partial
from collections import defaultdict

pprint = True


class ConfigObject(defaultdict):
_container = None
"""Reference to the parent node"""

def __init__(self, parent=None, config_file=None):
self.config_file = config_file
Expand Down Expand Up @@ -47,55 +47,68 @@ def connect(cls, config_file):

@classmethod
def parent_node(cls, config_file):
"""
Create a parent node, with special parent properties.
:param string config_file: Path to JSON file.
:return: self.
:rtype : ConfigObject
"""
self = cls(config_file=config_file)
self._container = self
self.timer = None
return self

@classmethod
def child_node(cls, parent, *obj):
"""
Create a child node with a reference to the parent.
:param parent: Reference to the parent node.
:param obj: Values to be stored.
:return: self
:rtype : ConfigObject
"""
self = cls(parent=parent)
self.update(*obj)
return self

def save_config(*method_arguments):
"""
Decorator. Write the config file after execution if is parent node
and is changed.
Decorator. Start the delayed worker to save the file after a series
of calls.
:param method_arguments: a hack for method decorators.
:return: decorated method
"""
function = method_arguments[-1]
"""A hack for method decorators"""

@wraps(function)
def _wrapper(self, *args, **kwargs):
"""Save after changes."""
"""Cancel save attempts until series of requests is complete."""
try:
if self._container.timer:
self._container.timer.cancel()
# print('cancelling thread')
return function(self, *args, **kwargs)

finally:
save = self._container.write_file
self._container.timer = Timer(0.001, save)
self._container.timer = threading.Timer(0.001, save)
self._container.timer.name = self._container.config_file
# print('starting thread')
self._container.timer.start()

# self._container.write_file()

return _wrapper

def write_file(self): # extracted for testing
# print('executing thread')
def write_file(self):
"""
Write the JSON config file to disk.
"""
if not self.config_file:
raise RuntimeError('Missing Config File')

with open(self.config_file, 'w') as f:
f.write(repr(self._container))
f.close()

@save_config
def __setitem__(self, key, value):
Expand Down Expand Up @@ -132,6 +145,13 @@ def __hash__(self):
return hash(str(self))

def block(self):
"""
Block until writing threads are completed.
This is mostly for internal use and testing. If you plan to connect
to a file being managed to this tool, use this to make sure the file
safe to read and write on.
"""
save_config_threads = [thread for thread in threading.enumerate() if
thread.name == self._container.config_file]

Expand Down

0 comments on commit 740279c

Please sign in to comment.