Skip to content

Commit

Permalink
Added dirty/clean functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanfeather committed Dec 21, 2014
1 parent 97399f9 commit 8a9b2ed
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/doc/API.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Functions
---------
.. currentmodule:: parsimony
.. autofunction:: generate

.. autofunction:: mark_dirty

:mod:`parsimony.configuration`: Sets the implementations used by parsimony.
===========================================================================
Expand Down
2 changes: 1 addition & 1 deletion src/doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
# built documents.
#
# The short X.Y version.
version = '0.3'
version = '0.4'
# The full version, including alpha/beta/rc tags.
import parsimony
release = parsimony.__version__
Expand Down
2 changes: 1 addition & 1 deletion src/parsimony/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .release import __version__
from .generate import generate
from .generate import generate, mark_dirty, dirty, clean
from .exceptions import ParsimonyException

from . import generators
Expand Down
18 changes: 9 additions & 9 deletions src/parsimony/configuration/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@
import os


__store = {}
__stores = {}


def store(key):
"""Gets the configured default store. Currently, a default of PickledStore is chosen.
:return store: Store object
"""
global __store
if __store is None:
__store = {}
global __stores
if __stores is None:
__stores = {}

if key not in __store:
__store[key] = parsimony.persistence.PickleStore(key)
if key not in __stores:
__stores[key] = parsimony.persistence.PickleStore(key)

return __store[key]
return __stores[key]


__cache = None
Expand Down Expand Up @@ -94,10 +94,10 @@ def callable_wrapper(key, function, **parameters):
def reset():
"""Set the configuration back to the default state. Useful for testing."""
global __context_name
global __store
global __stores
global __cache
global __obfuscator
__context_name = "Default"
__store = None
__stores = None
__cache = None
__obfuscator = None
37 changes: 36 additions & 1 deletion src/parsimony/generate.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import parsimony

__dirty_list = set()


def generate(key, function, **parameters):
"""Generates the key value using the function and supplied parameters.
Expand All @@ -10,4 +12,37 @@ def generate(key, function, **parameters):
:return: value generated by the function
"""
wrapper = parsimony.configuration.callable_wrapper(key, function, **parameters)
return wrapper.generate()
return wrapper.generate()


def mark_dirty(key):
"""Mark the key for mandatory regeneration.
This can be done at any time before or after a generator is executed. The value for the generator will be removed
from any underlying, attached, stores and caches if needed then regenerated upon a generate() call
:param key:
:return:
"""
global __dirty_list
__dirty_list.add(key)


def clean(key):
""" Tell parsimony that the value of the key is clean.
:param key: key of a clean generator
"""
global __dirty_list
__dirty_list.discard(key)


def dirty(key):
"""Tell if the generator key is dirty or not.
:param key:
:return: True or False
"""
global __dirty_list
return key in __dirty_list

29 changes: 19 additions & 10 deletions src/parsimony/generators/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ def generate(self):
version of the object first and returns it. If not in memory, the generated object is retrieved from a
ParameterStore and cached in memory, then returned.
If the object has been generated, but the parameters have changed, the object is regenerated and the ParameterStore
is overwritten.
If the object has been generated, but the parameters have changed, the object is regenerated and the
ParameterStore is overwritten.
:return generated_value: The value for this generator's key.
"""
Expand All @@ -69,13 +69,19 @@ def generate(self):
# did not exist in keys
# generator parameters needed updated

if (self._key in self._cache) and self._parameters_up_to_date() and self.up_to_date():
if self._generated:
return self._generated_value
else:
dirty = parsimony.dirty(self._key)
if dirty:
self._generated = False
self._generated_value = None
try:
del self._cache[self._key]
except KeyError:
pass # throw all other errors, but a key error just means we weren't cached yet

if (self._key in self._cache) and self._parameters_up_to_date() and self.up_to_date() and not dirty:
if not self._generated:
self._generated_value = self.load()
self._generated = True
return self._generated_value
else:
for parameter, value in self._current_parameters.items():
if not isinstance(value, parsimony.generators.Generator):
Expand All @@ -84,8 +90,11 @@ def generate(self):
self._generated_value = self.rebuild()
self.dump(self._generated_value)
self._generated = True
self._cache.update(self._key, self._obfuscator.obfuscate(GENERATOR_DEFAULT_STORE_VALUE), list(self._cache_keys.values()))
return self._generated_value
self._cache.update(self._key, self._obfuscator.obfuscate(GENERATOR_DEFAULT_STORE_VALUE),
list(self._cache_keys.values()))
parsimony.clean(self._key)

return self._generated_value

def _mangled_parameter_key(self, parameter_key):
"""
Expand Down Expand Up @@ -196,7 +205,7 @@ def _parameters_up_to_date(self):
if not needs_update:
for parameter in self._current_parameters.values():
if isinstance(parameter, parsimony.generators.Generator):
needs_update |= not parameter.up_to_date()
needs_update |= not parameter.up_to_date() or parsimony.dirty(parameter.key())

return not needs_update

Expand Down
15 changes: 13 additions & 2 deletions src/parsimony/persistence/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def compare(self, value, parameter_key):

@abstractmethod
def update(self, key, value, parameter_keys=None):
""" Store new value for the paramater.
""" Store new value for the parameter.
Must be overridden by subclasses.
Expand All @@ -49,4 +49,15 @@ def __contains__(self, key):
:param key:
:return: if parameter is in this store
"""
pass
pass

@abstractmethod
def __delitem__(self, key):
""" Used to implement a del index operator.
Must be overriden by subclasses.
Example: del mycache['gen_key']
This removes the item from all levels of the cache.
:param key: key to clear from the cache
"""
4 changes: 4 additions & 0 deletions src/parsimony/persistence/in_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ def __init__(self, store):
def __contains__(self, key):
return key in self._store_data

def __delitem__(self, key):
del self._store_data[key]
self._store.write(self._store_data)

def parameter_keys(self, key):
if key in list(self._store_data.keys()):
return self._store_data[key]['parameters']
Expand Down
2 changes: 1 addition & 1 deletion src/parsimony/persistence/object_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def __init__(self, key, base_directory=None):
super().__init__(key)

def read(self):
"""Pickle load the stored value"
"""Pickle load the stored value
"""
with open(self._store_location, 'rb') as result_file:
result = pickle.load(result_file)
Expand Down
2 changes: 1 addition & 1 deletion src/parsimony/release.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.3.1-git'
__version__ = '0.4.0-git'
34 changes: 34 additions & 0 deletions test/test_cleaning.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
""" Tests for the dirty/cleaning functionality
"""
import shutil
from test import TestEvaluationUtils
import parsimony

_mock = None


def setup_function(function):
global _mock
_mock = TestEvaluationUtils.MockGenerationProcess()
parsimony.configuration.reset()


def teardown_function(function):
TestEvaluationUtils.reset()


def test_generate_dirty():
global _mock
result = parsimony.generate('test_result', _mock, key_param=TestEvaluationUtils.STRING1)
assert TestEvaluationUtils.RESULT1 == result
assert 1 == _mock.get_call_count()

parsimony.mark_dirty('test_result')
result = parsimony.generate('test_result', _mock, key_param=TestEvaluationUtils.STRING1)
assert TestEvaluationUtils.RESULT1 == result
assert 2 == _mock.get_call_count()

result = parsimony.generate('test_result', _mock, key_param=TestEvaluationUtils.STRING1)
assert TestEvaluationUtils.RESULT1 == result
assert 2 == _mock.get_call_count()
1 change: 1 addition & 0 deletions test/test_simple_use_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import shutil
import importlib


class SimpleUseCasesTest(unittest.TestCase):
def setUp(self):
self.mock = TestEvaluationUtils.MockGenerationProcess()
Expand Down

0 comments on commit 8a9b2ed

Please sign in to comment.