From 688dfde57aa15ab2b0853e126fbb18b32d252c32 Mon Sep 17 00:00:00 2001 From: Jace Browning Date: Wed, 13 Apr 2016 07:15:19 -0400 Subject: [PATCH 1/2] Use the term 'parse' for text-to-dict --- tests/test_examples.py | 10 +++++----- yorm/bases/converter.py | 4 ++-- yorm/decorators.py | 4 ++-- yorm/diskutils.py | 14 +++++++------- yorm/mapper.py | 8 ++++---- yorm/types/containers.py | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/test_examples.py b/tests/test_examples.py index 6668157..14ab2ed 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -159,7 +159,7 @@ class StatusDictionary(Dictionary): pass def test_decorator(self, tmpdir): - """Verify standard attribute types dump/load correctly (decorator).""" + """Verify standard attribute types dump/parse correctly (decorator).""" tmpdir.chdir() sample = SampleStandardDecorated('sample') assert "path/to/default/sample.yml" == sample.__mapper__.path @@ -219,7 +219,7 @@ def test_decorator(self, tmpdir): assert False is sample.falsey def test_function(self, tmpdir): - """Verify standard attribute types dump/load correctly (function).""" + """Verify standard attribute types dump/parse correctly (function).""" tmpdir.chdir() _sample = SampleStandard() attrs = {'object': self.StatusDictionary, @@ -267,7 +267,7 @@ def test_function(self, tmpdir): """) == sample.__mapper__.text def test_function_to_json(self, tmpdir): - """Verify standard attribute types dump/load correctly (function).""" + """Verify standard attribute types dump/parse correctly (function).""" tmpdir.chdir() _sample = SampleStandard() attrs = {'object': self.StatusDictionary, @@ -420,7 +420,7 @@ class TestExtended: """Integration tests for extended attribute types.""" def test_function(self, tmpdir): - """Verify extended attribute types dump/load correctly.""" + """Verify extended attribute types dump/parse correctly.""" tmpdir.chdir() _sample = SampleExtended() attrs = {'text': Markdown} @@ -460,7 +460,7 @@ class TestCustom: """Integration tests for custom attribute types.""" def test_decorator(self, tmpdir): - """Verify custom attribute types dump/load correctly.""" + """Verify custom attribute types dump/parse correctly.""" tmpdir.chdir() sample = SampleCustomDecorated('sample') diff --git a/yorm/bases/converter.py b/yorm/bases/converter.py index fbf9579..02cad54 100644 --- a/yorm/bases/converter.py +++ b/yorm/bases/converter.py @@ -19,7 +19,7 @@ def create_default(cls): @abstractclassmethod def to_value(cls, data): - """Convert loaded data to an attribute's value.""" + """Convert parsed data to an attribute's value.""" raise NotImplementedError(common.OVERRIDE_MESSAGE) @abstractclassmethod @@ -43,7 +43,7 @@ def to_value(cls, data): @abstractmethod def update_value(self, data, *, auto_attr): # pragma: no cover (abstract method) - """Update the attribute's value from loaded data.""" + """Update the attribute's value from parsed data.""" raise NotImplementedError(common.OVERRIDE_MESSAGE) def format_data(self): diff --git a/yorm/decorators.py b/yorm/decorators.py index 12603dd..66d7fd9 100644 --- a/yorm/decorators.py +++ b/yorm/decorators.py @@ -32,7 +32,7 @@ def sync_object(instance, path, attrs=None, **kwargs): """Enable YAML mapping on an object. :param instance: object to patch with YAML mapping behavior - :param path: file path for dump/load + :param path: file path for dump/parse :param attrs: dictionary of attribute names mapped to converter classes :param auto_create: automatically create the file to save attributes @@ -68,7 +68,7 @@ def sync_object(instance, path, attrs=None, **kwargs): def sync_instances(path_format, format_spec=None, attrs=None, **kwargs): """Class decorator to enable YAML mapping after instantiation. - :param path_format: formatting string to create file paths for dump/load + :param path_format: formatting string to create file paths for dump/parse :param format_spec: dictionary to use for string formatting :param attrs: dictionary of attribute names mapped to converter classes diff --git a/yorm/diskutils.py b/yorm/diskutils.py index 7b719ae..2776602 100644 --- a/yorm/diskutils.py +++ b/yorm/diskutils.py @@ -84,8 +84,8 @@ def delete(path): os.remove(path) -def load(text, path): - """Parse a dictionary a data from formatted text. +def parse(text, path): + """Parse a dictionary of data from formatted text. :param text: string containing dumped data :param path: file path to specify formatting @@ -95,12 +95,12 @@ def load(text, path): """ ext = _get_ext(path) if ext in ['json']: - data = _load_json(text, path) + data = _parse_json(text, path) elif ext in ['yml', 'yaml']: - data = _load_yaml(text, path) + data = _parse_yaml(text, path) else: log.warning("Unrecognized file extension: %s", ext) - data = _load_yaml(text, path) + data = _parse_yaml(text, path) if not isinstance(data, dict): msg = "Invalid file contents: {}".format(path) @@ -109,7 +109,7 @@ def load(text, path): return data -def _load_json(text, path): +def _parse_json(text, path): try: return json.loads(text) or {} except json.JSONDecodeError as exc: @@ -117,7 +117,7 @@ def _load_json(text, path): raise exceptions.FileContentError(msg) from None -def _load_yaml(text, path): +def _parse_yaml(text, path): try: return yaml.load(text) or {} except yaml.error.YAMLError as exc: diff --git a/yorm/mapper.py b/yorm/mapper.py index 0cea5c3..567e829 100644 --- a/yorm/mapper.py +++ b/yorm/mapper.py @@ -58,7 +58,7 @@ class Mapper: When getting an attribute: - FILE -> read -> [text] -> load -> [dict] -> fetch -> ATTRIBUTES + FILE -> read -> [text] -> parse -> [dict] -> fetch -> ATTRIBUTES When setting an attribute: @@ -163,8 +163,8 @@ def fetch(self): # Parse data from file text = self._read() - data = diskutils.load(text=text, path=self.path) - log.trace("Loaded data: \n%s", pformat(data)) + data = diskutils.parse(text=text, path=self.path) + log.trace("Parsed data: \n%s", pformat(data)) # Update all attributes attrs2 = self.attrs.copy() @@ -183,7 +183,7 @@ def fetch(self): log.warning(msg, name, data) continue - # Convert the loaded attribute + # Convert the parsed value to the attribute's final type attr = getattr(self._obj, name, None) if all((isinstance(attr, converter), issubclass(converter, Container))): diff --git a/yorm/types/containers.py b/yorm/types/containers.py index 7ff3051..b6276bc 100644 --- a/yorm/types/containers.py +++ b/yorm/types/containers.py @@ -124,7 +124,7 @@ def update_value(self, data, *, auto_attr=True): # Get the converter for all items converter = cls.item_type - # Convert the loaded data + # Convert the parsed data for item in to_list(data): try: From 797a0139c60103d6e01477f4a737c9cadb88de3b Mon Sep 17 00:00:00 2001 From: Jace Browning Date: Wed, 13 Apr 2016 10:02:39 -0400 Subject: [PATCH 2/2] Rename fetch/store to load/save --- CHANGES.md | 2 +- tests/test_examples.py | 8 ++--- tests/test_files.py | 6 ++-- tests/test_imports.py | 3 +- tests/test_nested_attributes.py | 20 ++++++------ yorm/__init__.py | 4 +-- yorm/bases/converter.py | 4 +-- yorm/bases/mappable.py | 44 ++++++++++++------------- yorm/decorators.py | 15 ++++----- yorm/mapper.py | 32 +++++++++--------- yorm/test/test_bases_container.py | 2 +- yorm/test/test_bases_mappable.py | 52 +++++++++++++++--------------- yorm/test/test_decorators.py | 4 +-- yorm/test/test_mapper.py | 28 ++++++++-------- yorm/test/test_types_containers.py | 2 +- yorm/test/test_utilities.py | 25 +++++++++++--- yorm/types/containers.py | 14 ++++---- yorm/utilities.py | 26 ++++++++++++--- 18 files changed, 162 insertions(+), 129 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 9c8aff5..c10caa4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,7 +6,7 @@ - Removed the ability to check for existing files in `sync()`. - Renamed and consolidated custom exceptions. - Renamed sync parameter `auto=True` to `auto_save=True`. -- Renamed sync parameter `strict=True` to `auto_attr=False`. +- Renamed sync parameter `strict=True` to `auto_track=False`. - Added sync parameter `auto_create` to defer file creation to ORM functions. ## 0.7.2 (2016/03/30) diff --git a/tests/test_examples.py b/tests/test_examples.py index 14ab2ed..b1425f1 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -120,7 +120,7 @@ def __repr__(self): return "".format(id(self)) -@yorm.sync("sample.yml", auto_attr=True) +@yorm.sync("sample.yml", auto_track=True) class SampleEmptyDecorated: """Sample class using standard attribute types.""" @@ -326,7 +326,7 @@ def test_auto_off(self, tmpdir): sample.string = "hello" assert "" == sample.__mapper__.text - sample.__mapper__.auto_store = True + sample.__mapper__.auto_save = True sample.string = "world" assert strip(""" @@ -343,7 +343,7 @@ def test_nesting(self, tmpdir): _sample = SampleNested() attrs = {'count': Integer, 'results': StatusDictionaryList} - sample = yorm.sync(_sample, "sample.yml", attrs, auto_attr=True) + sample = yorm.sync(_sample, "sample.yml", attrs, auto_track=True) # check defaults assert 0 == sample.count @@ -405,7 +405,7 @@ def test_objects(self, tmpdir): """) # (a mapped attribute must be read first to trigger retrieving) - sample.__mapper__.fetch() + sample.__mapper__.load() # check object values assert {'key': 'value'} == sample.object diff --git a/tests/test_files.py b/tests/test_files.py index ab60dc4..73dc523 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -58,7 +58,7 @@ def __repr__(self): class TestCreate: """Integration tests for creating mapped classes.""" - def test_fetch_from_existing(self, tmpdir): + def test_load_from_existing(self, tmpdir): """Verify attributes are updated from an existing file.""" tmpdir.chdir() sample = SampleStandardDecorated('sample') @@ -119,7 +119,7 @@ def test_multiple(self, tmpdir): class TestUpdate: """Integration tests for updating files/object.""" - def test_automatic_store_after_first_modification(self, tmpdir): + def test_automatic_save_after_first_modification(self, tmpdir): tmpdir.chdir() sample = SampleStandardDecorated('sample') assert "number_int: 0\n" in sample.__mapper__.text @@ -131,7 +131,7 @@ def test_automatic_store_after_first_modification(self, tmpdir): assert 1 is sample.number_int assert "number_int: 1\n" in sample.__mapper__.text - def test_automatic_store_after_first_modification_on_list(self, tmpdir): + def test_automatic_save_after_first_modification_on_list(self, tmpdir): tmpdir.chdir() sample = SampleStandardDecorated('sample') assert "array: []\n" in sample.__mapper__.text diff --git a/tests/test_imports.py b/tests/test_imports.py index e55a552..aa33dae 100644 --- a/tests/test_imports.py +++ b/tests/test_imports.py @@ -26,8 +26,9 @@ def test_from_top_decorators(): def test_from_top_utilities(): - from yorm import new + from yorm import create from yorm import find + from yorm import find_all from yorm import load from yorm import save from yorm import delete diff --git a/tests/test_nested_attributes.py b/tests/test_nested_attributes.py index 0debda4..a2749c9 100644 --- a/tests/test_nested_attributes.py +++ b/tests/test_nested_attributes.py @@ -93,7 +93,7 @@ def __repr__(self): @patch('yorm.settings.fake', True) class TestNestedOnce: - def test_append_triggers_store(self): + def test_append_triggers_save(self): top = Top() logging.info("Appending dictionary to list...") top.nested_list.append({'number': 1}) @@ -108,7 +108,7 @@ def test_append_triggers_store(self): number: 1.0 """) == top.__mapper__.text - def test_set_by_index_triggers_store(self): + def test_set_by_index_triggers_save(self): top = Top() top.nested_list = [{'number': 1.5}] assert strip(""" @@ -133,7 +133,7 @@ def test_set_by_index_triggers_store(self): number: 1.6 """) == top.__mapper__.text - def test_get_by_index_triggers_fetch(self): + def test_get_by_index_triggers_load(self): top = Top() top.__mapper__.text = strip(""" nested_list: @@ -141,7 +141,7 @@ def test_get_by_index_triggers_fetch(self): """) assert 1.7 == top.nested_list[0].number - def test_delete_index_triggers_store(self): + def test_delete_index_triggers_save(self): top = Top() top.nested_list = [{'number': 1.8}, {'number': 1.9}] assert strip(""" @@ -170,7 +170,7 @@ def test_delete_index_triggers_store(self): number: 1.9 """) == top.__mapper__.text - def test_set_dict_as_attribute_triggers_store(self): + def test_set_dict_as_attribute_triggers_save(self): top = Top() top.nested_dict.number = 2 assert strip(""" @@ -184,7 +184,7 @@ def test_set_dict_as_attribute_triggers_store(self): @patch('yorm.settings.fake', True) class TestNestedTwice: - def test_nested_list_item_value_change_triggers_store(self): + def test_nested_list_item_value_change_triggers_save(self): top = Top() top.nested_list = [{'number': 3}] assert strip(""" @@ -209,7 +209,7 @@ def test_nested_list_item_value_change_triggers_store(self): number: 4.0 """) == top.__mapper__.text - def test_nested_dict_item_value_change_triggers_store(self): + def test_nested_dict_item_value_change_triggers_save(self): top = Top() top.nested_dict = {'nested_list_2': [5]} assert strip(""" @@ -229,7 +229,7 @@ def test_nested_dict_item_value_change_triggers_store(self): nested_list: [] """) == top.__mapper__.text - def test_dict_in_list_value_change_triggers_store(self): + def test_dict_in_list_value_change_triggers_save(self): top = Top() top.nested_list.append(None) top.nested_list[0].nested_dict_3.number = 8 @@ -244,7 +244,7 @@ def test_dict_in_list_value_change_triggers_store(self): number: 0.0 """) == top.__mapper__.text - def test_list_in_dict_append_triggers_store(self): + def test_list_in_dict_append_triggers_save(self): top = Top() top.nested_list.append(None) top.nested_list.append(None) @@ -335,7 +335,7 @@ def test_alias_list_in_dict(self): top = Top() logging.info("Updating nested attribute...") top.nested_dict.number = 1 - logging.info("Storing refs...") + logging.info("Grabbing refs...") ref1 = top.nested_dict ref2 = top.nested_dict.nested_list_2 assert id(ref1) == id(top.nested_dict) diff --git a/yorm/__init__.py b/yorm/__init__.py index 7d075ac..a438b39 100644 --- a/yorm/__init__.py +++ b/yorm/__init__.py @@ -3,7 +3,7 @@ import sys __project__ = 'YORM' -__version__ = '0.8.dev2' +__version__ = '0.8.dev3' VERSION = __project__ + '-' + __version__ @@ -17,7 +17,7 @@ from . import bases, types from .common import UUID from .decorators import sync, sync_object, sync_instances, attr - from .utilities import new, find, load, save, delete + from .utilities import create, find, find_all, load, save, delete from .bases import Container, Converter, Mappable, Convertible except ImportError: # pragma: no cover (manual test) pass diff --git a/yorm/bases/converter.py b/yorm/bases/converter.py index 02cad54..d355867 100644 --- a/yorm/bases/converter.py +++ b/yorm/bases/converter.py @@ -38,11 +38,11 @@ def create_default(cls): @classmethod def to_value(cls, data): value = cls.create_default() - value.update_value(data, auto_attr=True) + value.update_value(data, auto_track=True) return value @abstractmethod - def update_value(self, data, *, auto_attr): # pragma: no cover (abstract method) + def update_value(self, data, *, auto_track): # pragma: no cover (abstract method) """Update the attribute's value from parsed data.""" raise NotImplementedError(common.OVERRIDE_MESSAGE) diff --git a/yorm/bases/mappable.py b/yorm/bases/mappable.py index 722f082..6933eee 100644 --- a/yorm/bases/mappable.py +++ b/yorm/bases/mappable.py @@ -11,8 +11,8 @@ TAG = '_modified_by_yorm' -def fetch_before(method): - """Decorator for methods that should fetch before call.""" +def load_before(method): + """Decorator for methods that should load before call.""" if getattr(method, TAG, False): return method @@ -23,10 +23,10 @@ def wrapped(self, *args, **kwargs): if not _private_call(method, args): mapper = common.get_mapper(self) if mapper and mapper.modified: - log.debug("Fetching before call: %s", method.__name__) - mapper.fetch() - if mapper.auto_store_after_fetch: - mapper.store() + log.debug("Loading before call: %s", method.__name__) + mapper.load() + if mapper.auto_save_after_load: + mapper.save() mapper.modified = False return method(self, *args, **kwargs) @@ -36,8 +36,8 @@ def wrapped(self, *args, **kwargs): return wrapped -def store_after(method): - """Decorator for methods that should store after call.""" +def save_after(method): + """Decorator for methods that should save after call.""" if getattr(method, TAG, False): return method @@ -49,9 +49,9 @@ def wrapped(self, *args, **kwargs): if not _private_call(method, args): mapper = common.get_mapper(self) - if mapper and mapper.auto_store: - log.debug("Storing after call: %s", method.__name__) - mapper.store() + if mapper and mapper.auto_save: + log.debug("Saving after call: %s", method.__name__) + mapper.save() return result @@ -75,37 +75,37 @@ class Mappable(metaclass=abc.ABCMeta): # pylint: disable=no-member - @fetch_before + @load_before def __getattribute__(self, name): """Trigger object update when reading attributes.""" return object.__getattribute__(self, name) - @store_after + @save_after def __setattr__(self, name, value): """Trigger file update when setting attributes.""" super().__setattr__(name, value) - @fetch_before + @load_before def __iter__(self): """Trigger object update when iterating.""" return super().__iter__() - @fetch_before + @load_before def __getitem__(self, key): """Trigger object update when reading an index.""" return super().__getitem__(key) - @store_after + @save_after def __setitem__(self, key, value): """Trigger file update when setting an index.""" super().__setitem__(key, value) - @store_after + @save_after def __delitem__(self, key): """Trigger file update when deleting an index.""" super().__delitem__(key) - @store_after + @save_after def append(self, value): """Trigger file update when appending items.""" super().append(value) @@ -122,9 +122,9 @@ def patch_methods(instance): except AttributeError: log.trace("No method: %s", name) else: - modified_method = fetch_before(method) + modified_method = load_before(method) setattr(cls, name, modified_method) - log.trace("Patched to fetch before call: %s", name) + log.trace("Patched to load before call: %s", name) for name in ['__setattr__', '__setitem__', '__delitem__', 'append']: try: @@ -132,6 +132,6 @@ def patch_methods(instance): except AttributeError: log.trace("No method: %s", name) else: - modified_method = store_after(method) + modified_method = save_after(method) setattr(cls, name, modified_method) - log.trace("Patched to store after call: %s", name) + log.trace("Patched to save after call: %s", name) diff --git a/yorm/decorators.py b/yorm/decorators.py index 66d7fd9..679946a 100644 --- a/yorm/decorators.py +++ b/yorm/decorators.py @@ -37,7 +37,7 @@ def sync_object(instance, path, attrs=None, **kwargs): :param auto_create: automatically create the file to save attributes :param auto_save: automatically save attribute changes to the file - :param auto_attr: automatically add new attributes from the file + :param auto_track: automatically add new attributes from the file """ log.info("Mapping %r to %s...", instance, path) @@ -46,18 +46,17 @@ def sync_object(instance, path, attrs=None, **kwargs): patch_methods(instance) attrs = _ordered(attrs) or common.attrs[instance.__class__] - kwargs['auto_store'] = kwargs.pop('auto_save', True) mapper = Mapper(instance, path, attrs, **kwargs) if mapper.missing: if mapper.auto_create: mapper.create() - if mapper.auto_store: - mapper.store() - mapper.fetch() + if mapper.auto_save: + mapper.save() + mapper.load() else: - if mapper.auto_store: - mapper.fetch() + if mapper.auto_save: + mapper.load() common.set_mapper(instance, mapper) log.info("Mapped %r to %s", instance, path) @@ -74,7 +73,7 @@ def sync_instances(path_format, format_spec=None, attrs=None, **kwargs): :param auto_create: automatically create the file to save attributes :param auto_save: automatically save attribute changes to the file - :param auto_attr: automatically add new attributes from the file + :param auto_track: automatically add new attributes from the file """ format_spec = format_spec or {} diff --git a/yorm/mapper.py b/yorm/mapper.py index 567e829..44829b0 100644 --- a/yorm/mapper.py +++ b/yorm/mapper.py @@ -58,11 +58,11 @@ class Mapper: When getting an attribute: - FILE -> read -> [text] -> parse -> [dict] -> fetch -> ATTRIBUTES + FILE -> read -> [text] -> parse -> [dict] -> load -> ATTRIBUTES When setting an attribute: - ATTRIBUTES -> store -> [dict] -> dump -> [text] -> write -> FILE + ATTRIBUTES -> save -> [dict] -> dump -> [text] -> write -> FILE After the mapped file is no longer needed: @@ -71,17 +71,17 @@ class Mapper: """ def __init__(self, obj, path, attrs, *, - auto_create=True, auto_store=True, auto_attr=False): + auto_create=True, auto_save=True, auto_track=False): self._obj = obj self.path = path self.attrs = attrs self.auto_create = auto_create - self.auto_store = auto_store - self.auto_attr = auto_attr + self.auto_save = auto_save + self.auto_track = auto_track self.exists = diskutils.exists(self.path) self.deleted = False - self.auto_store_after_fetch = False + self.auto_save_after_load = False self._activity = False self._timestamp = 0 @@ -157,9 +157,9 @@ def create(self): @file_required @prevent_recursion - def fetch(self): + def load(self): """Update the object's mapped attributes from its file.""" - log.info("Fetching %r from %s...", self._obj, prefix(self)) + log.info("Loading %r from %s...", self._obj, prefix(self)) # Parse data from file text = self._read() @@ -175,7 +175,7 @@ def fetch(self): try: converter = self.attrs[name] except KeyError: - if self.auto_attr: + if self.auto_track: converter = types.match(name, data) self.attrs[name] = converter else: @@ -187,12 +187,12 @@ def fetch(self): attr = getattr(self._obj, name, None) if all((isinstance(attr, converter), issubclass(converter, Container))): - attr.update_value(data, auto_attr=self.auto_attr) + attr.update_value(data, auto_track=self.auto_track) else: attr = converter.to_value(data) setattr(self._obj, name, attr) self._remap(attr, self) - log.trace("Value fetched: %s = %r", name, attr) + log.trace("Value loaded: %s = %r", name, attr) # Add missing attributes for name, converter in attrs2.items(): @@ -206,7 +206,7 @@ def fetch(self): self.modified = False def _remap(self, obj, root): - """Restore mapping on nested attributes.""" + """Resave mapping on nested attributes.""" if isinstance(obj, Container): common.set_mapper(obj, root) @@ -220,9 +220,9 @@ def _remap(self, obj, root): @file_required @prevent_recursion - def store(self): + def save(self): """Format and save the object's mapped attributes to its file.""" - log.info("Storing %r to %s...", self._obj, prefix(self)) + log.info("Saving %r to %s...", self._obj, prefix(self)) # Format the data items data = self.attrs.__class__() @@ -236,7 +236,7 @@ def store(self): else: data2 = converter.to_data(value) - log.trace("Data to store: %s = %r", name, data2) + log.trace("Data to save: %s = %r", name, data2) data[name] = data2 # Dump data to file @@ -245,7 +245,7 @@ def store(self): # Set meta attributes self.modified = True - self.auto_store_after_fetch = self.auto_store + self.auto_save_after_load = self.auto_save def delete(self): """Delete the object's file from the file system.""" diff --git a/yorm/test/test_bases_container.py b/yorm/test/test_bases_container.py index dc7016f..b472982 100644 --- a/yorm/test/test_bases_container.py +++ b/yorm/test/test_bases_container.py @@ -23,7 +23,7 @@ def create_default(cls): def to_data(cls, value): return str(value.value) - def update_value(self, data, *, auto_attr=None): # pylint: disable=unused-variable + def update_value(self, data, *, auto_track=None): # pylint: disable=unused-variable self.value += int(data) def test_container_class_cannot_be_instantiated(self): diff --git a/yorm/test/test_bases_mappable.py b/yorm/test/test_bases_mappable.py index aa8121e..addce55 100644 --- a/yorm/test/test_bases_mappable.py +++ b/yorm/test/test_bases_mappable.py @@ -74,7 +74,7 @@ def __init__(self): 'var4': IntegerList, 'var5': StatusDictionary} self.__mapper__ = MockMapper(self, path, attrs) - self.__mapper__.store() + self.__mapper__.save() def __repr__(self): return "".format(id(self)) @@ -174,7 +174,7 @@ def test_error_unexpected_yaml(self): def test_new(self): """Verify new attributes are added to the object.""" - self.sample.__mapper__.auto_attr = True + self.sample.__mapper__.auto_track = True text = strip(""" new: 42 """) @@ -183,7 +183,7 @@ def test_new(self): def test_new_unknown(self): """Verify an exception is raised on new attributes w/ unknown types""" - self.sample.__mapper__.auto_attr = True + self.sample.__mapper__.auto_track = True text = strip(""" new: !!timestamp 2001-12-15T02:59:43.1Z """) @@ -206,64 +206,64 @@ class Sample(MockDict, MockList): __mapper__ = Mock() __mapper__.attrs = {} - __mapper__.fetch = Mock() - __mapper__.store = Mock() + __mapper__.load = Mock() + __mapper__.save = Mock() def setup_method(self, _): """Create an mappable instance for tests.""" self.sample = self.Sample() - self.sample.__mapper__.fetch.reset_mock() - self.sample.__mapper__.store.reset_mock() - self.sample.__mapper__.auto_store_after_fetch = False + self.sample.__mapper__.load.reset_mock() + self.sample.__mapper__.save.reset_mock() + self.sample.__mapper__.auto_save_after_load = False def test_getattribute(self): with pytest.raises(AttributeError): getattr(self.sample, 'foo') - assert 1 == self.sample.__mapper__.fetch.call_count - assert 0 == self.sample.__mapper__.store.call_count + assert 1 == self.sample.__mapper__.load.call_count + assert 0 == self.sample.__mapper__.save.call_count def test_setattr(self): self.sample.__mapper__.attrs['foo'] = Mock() setattr(self.sample, 'foo', 'bar') - assert 0 == self.sample.__mapper__.fetch.call_count - assert 1 == self.sample.__mapper__.store.call_count + assert 0 == self.sample.__mapper__.load.call_count + assert 1 == self.sample.__mapper__.save.call_count def test_getitem(self): with pytest.raises(KeyError): print(self.sample['foo']) - assert 1 == self.sample.__mapper__.fetch.call_count - assert 0 == self.sample.__mapper__.store.call_count + assert 1 == self.sample.__mapper__.load.call_count + assert 0 == self.sample.__mapper__.save.call_count def test_setitem(self): self.sample['foo'] = 'bar' - assert 0 == self.sample.__mapper__.fetch.call_count - assert 1 == self.sample.__mapper__.store.call_count + assert 0 == self.sample.__mapper__.load.call_count + assert 1 == self.sample.__mapper__.save.call_count def test_delitem(self): self.sample['foo'] = 'bar' - self.sample.__mapper__.store.reset_mock() + self.sample.__mapper__.save.reset_mock() del self.sample['foo'] - assert 0 == self.sample.__mapper__.fetch.call_count - assert 1 == self.sample.__mapper__.store.call_count + assert 0 == self.sample.__mapper__.load.call_count + assert 1 == self.sample.__mapper__.save.call_count def test_append(self): self.sample.append('foo') - assert 1 == self.sample.__mapper__.fetch.call_count - assert 1 == self.sample.__mapper__.store.call_count + assert 1 == self.sample.__mapper__.load.call_count + assert 1 == self.sample.__mapper__.save.call_count def test_iter(self): self.sample.append('foo') self.sample.append('bar') - self.sample.__mapper__.fetch.reset_mock() - self.sample.__mapper__.store.reset_mock() - self.sample.__mapper__.auto_store_after_fetch = False + self.sample.__mapper__.load.reset_mock() + self.sample.__mapper__.save.reset_mock() + self.sample.__mapper__.auto_save_after_load = False self.sample.__mapper__.modified = True for item in self.sample: print(item) - assert 1 == self.sample.__mapper__.fetch.call_count - assert 0 == self.sample.__mapper__.store.call_count + assert 1 == self.sample.__mapper__.load.call_count + assert 0 == self.sample.__mapper__.save.call_count def test_handle_missing_mapper(self): sample = self.MockDict() diff --git a/yorm/test/test_decorators.py b/yorm/test/test_decorators.py index 10ac2c9..cdb50e7 100644 --- a/yorm/test/test_decorators.py +++ b/yorm/test/test_decorators.py @@ -61,7 +61,7 @@ def test_multiple(self): def test_init_existing(self): """Verify an existing file is read.""" with patch('yorm.diskutils.read', Mock(return_value="abc: 123")): - sample = decorators.sync(self.Sample(), "s.yml", auto_attr=True) + sample = decorators.sync(self.Sample(), "s.yml", auto_track=True) assert 123 == sample.abc @@ -71,7 +71,7 @@ def test_init_existing(self): class TestSyncInstances: """Unit tests for the `sync_instances` decorator.""" - @decorators.sync("sample.yml", auto_attr=True) + @decorators.sync("sample.yml", auto_track=True) class SampleDecorated: """Sample decorated class using a single path.""" diff --git a/yorm/test/test_mapper.py b/yorm/test/test_mapper.py index bec4d44..265b27c 100644 --- a/yorm/test/test_mapper.py +++ b/yorm/test/test_mapper.py @@ -31,7 +31,7 @@ def mapper(tmpdir, obj, attrs, request): yorm.settings.fake = True elif "real" in path: tmpdir.chdir() - yield Mapper(obj, path, attrs, auto_attr=False) + yield Mapper(obj, path, attrs, auto_track=False) yorm.settings.fake = backup @@ -89,11 +89,11 @@ def it_can_be_called_twice(mapper): expect(mapper.path).missing() - def describe_fetch(): + def describe_load(): def it_adds_missing_attributes(obj, mapper): mapper.create() - mapper.fetch() + mapper.load() expect(obj.var1) == 1 expect(obj.var2) == 0 @@ -103,29 +103,29 @@ def it_ignores_new_attributes(obj, mapper): mapper.create() mapper.text = "var4: foo" - mapper.fetch() + mapper.load() with expect.raises(AttributeError): print(obj.var4) - def it_infers_types_on_new_attributes_with_auto_attr(obj, mapper): - mapper.auto_attr = True + def it_infers_types_on_new_attributes_with_auto_track(obj, mapper): + mapper.auto_track = True mapper.create() mapper.text = "var4: foo" - mapper.fetch() + mapper.load() expect(obj.var4) == "foo" obj.var4 = 42 - mapper.store() + mapper.save() - mapper.fetch() + mapper.load() expect(obj.var4) == "42" def it_raises_an_exception_after_delete(mapper): mapper.delete() with expect.raises(exceptions.DeletedFileError): - mapper.fetch() + mapper.load() def describe_modified(): @@ -142,9 +142,9 @@ def is_true_after_delete(mapper): expect(mapper.modified).is_true() - def is_false_after_fetch(mapper): + def is_false_after_load(mapper): mapper.create() - mapper.fetch() + mapper.load() expect(mapper.modified).is_false() @@ -165,13 +165,13 @@ def describe_text(): def can_get_the_file_contents(obj, mapper): mapper.create() obj.var3 = 42 - mapper.store() + mapper.save() expect(mapper.text) == "var2: 0\nvar3: 42\n" def can_set_the_file_contents(obj, mapper): mapper.create() mapper.text = "var2: 42\n" - mapper.fetch() + mapper.load() expect(obj.var2) == 42 diff --git a/yorm/test/test_types_containers.py b/yorm/test/test_types_containers.py index 0163b80..6dc9c9b 100644 --- a/yorm/test/test_types_containers.py +++ b/yorm/test/test_types_containers.py @@ -111,7 +111,7 @@ def test_dict_as_object(self): def test_unknown_attrributes_are_ignored(self): obj = SampleDictionary.create_default() - obj.update_value({'key': "value", 'abc': 7}, auto_attr=False) + obj.update_value({'key': "value", 'abc': 7}, auto_track=False) assert {'abc': 7} == obj diff --git a/yorm/test/test_utilities.py b/yorm/test/test_utilities.py index 531b206..fb80dbf 100644 --- a/yorm/test/test_utilities.py +++ b/yorm/test/test_utilities.py @@ -35,10 +35,10 @@ def instance(model_class): return model_class('foo', 'bar') -def describe_new(): +def describe_create(): def it_creates_files(model_class): - instance = utilities.new(model_class, 'foo', 'bar') + instance = utilities.create(model_class, 'foo', 'bar') expect(instance.__mapper__.exists) == True @@ -46,11 +46,11 @@ def it_requires_files_to_not_yet_exist(model_class, instance): instance.__mapper__.create() with expect.raises(exceptions.DuplicateMappingError): - utilities.new(model_class, 'foo', 'bar') + utilities.create(model_class, 'foo', 'bar') def it_requires_a_mapped_object(): with expect.raises(TypeError): - utilities.new(Mock) + utilities.create(Mock) def describe_find(): @@ -72,10 +72,25 @@ def it_requires_a_mapped_object(): utilities.find(Mock) -def describe_load(): +def describe_find_all(): def it_is_not_yet_implemented(): with expect.raises(NotImplementedError): + utilities.find_all(Mock) + + +def describe_load(): + + def it_marks_files_as_unmodified(instance): + instance.__mapper__.create() + instance.__mapper__.modified = True + + utilities.load(instance) + + expect(instance.__mapper__.modified) == False + + def it_requires_a_mapped_object(): + with expect.raises(TypeError): utilities.load(Mock) diff --git a/yorm/types/containers.py b/yorm/types/containers.py index b6276bc..8392ad4 100644 --- a/yorm/types/containers.py +++ b/yorm/types/containers.py @@ -19,7 +19,7 @@ def __new__(cls, *args, **kwargs): @classmethod def to_data(cls, value): value2 = cls.create_default() - value2.update_value(value, auto_attr=False) + value2.update_value(value, auto_track=False) data = common.attrs[cls].__class__() for name, converter in common.attrs[cls].items(): @@ -27,7 +27,7 @@ def to_data(cls, value): return data - def update_value(self, data, *, auto_attr=True): + def update_value(self, data, *, auto_track=True): cls = self.__class__ value = cls.create_default() @@ -50,7 +50,7 @@ def update_value(self, data, *, auto_attr=True): try: converter = attrs.pop(name) except KeyError: - if auto_attr: + if auto_track: converter = standard.match(name, data2, nested=True) common.attrs[cls][name] = converter else: @@ -65,7 +65,7 @@ def update_value(self, data, *, auto_attr=True): if all((isinstance(attr, converter), issubclass(converter, Container))): - attr.update_value(data2, auto_attr=auto_attr) + attr.update_value(data2, auto_track=auto_track) else: attr = converter.to_value(data2) @@ -107,7 +107,7 @@ def item_type(cls): # pylint: disable=no-self-argument @classmethod def to_data(cls, value): value2 = cls.create_default() - value2.update_value(value, auto_attr=False) + value2.update_value(value, auto_track=False) data = [] @@ -117,7 +117,7 @@ def to_data(cls, value): return data - def update_value(self, data, *, auto_attr=True): + def update_value(self, data, *, auto_track=True): cls = self.__class__ value = cls.create_default() @@ -134,7 +134,7 @@ def update_value(self, data, *, auto_attr=True): if all((isinstance(attr, converter), issubclass(converter, Container))): - attr.update_value(item, auto_attr=auto_attr) + attr.update_value(item, auto_track=auto_track) else: attr = converter.to_value(item) # pylint: disable=no-member diff --git a/yorm/utilities.py b/yorm/utilities.py index 2b9da86..1e83c62 100644 --- a/yorm/utilities.py +++ b/yorm/utilities.py @@ -5,11 +5,15 @@ log = common.logger(__name__) -def new(cls, *args): +def create(cls, *args): """Create a new mapped object.""" instance = cls(*args) mapper = _ensure_mapped(instance) + if mapper.auto_create: + msg = "'create' is called automatically with 'auto_create' enabled" + log.warning(msg) + if mapper.exists: msg = "{!r} already exists".format(mapper.path) raise exceptions.DuplicateMappingError(msg) @@ -17,7 +21,7 @@ def new(cls, *args): return save(instance) -def find(cls, *args, create=False): +def find(cls, *args, create=False): # pylint: disable=redefined-outer-name """Find a matching mapped object or return None.""" instance = cls(*args) mapper = _ensure_mapped(instance) @@ -30,16 +34,30 @@ def find(cls, *args, create=False): return None -def load(cls, **kwargs): +def find_all(cls, **kwargs): """Return a list of all matching mapped objects.""" log.debug((cls, kwargs)) raise NotImplementedError +def load(instance): + """Force the loading of a mapped object's file.""" + mapper = _ensure_mapped(instance) + + log.warning("'load' is called automatically") + + mapper.load() + + return instance + + def save(instance): """Save a mapped object to file.""" mapper = _ensure_mapped(instance) + if mapper.auto_save: + log.warning("'save' is called automatically with 'auto_save' enabled") + if mapper.deleted: msg = "{!r} was deleted".format(mapper.path) raise exceptions.DeletedFileError(msg) @@ -47,7 +65,7 @@ def save(instance): if not mapper.exists: mapper.create() - mapper.store() + mapper.save() return instance