Skip to content
This repository has been archived by the owner on Oct 3, 2019. It is now read-only.

Commit

Permalink
Merge 6f0df40 into 827c8b5
Browse files Browse the repository at this point in the history
  • Loading branch information
jacebrowning committed Apr 2, 2017
2 parents 827c8b5 + 6f0df40 commit fb8f35d
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 36 deletions.
3 changes: 2 additions & 1 deletion .pycodestyle.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# E401 multiple imports on one line (checked by PyLint)
# E402 module level import not at top of file (checked by PyLint)
# E501: line too long (checked by PyLint)
# E701: multiple statements on one line (used to shorten test syntax)
# E711: comparison to None (used to improve test style)
# E712: comparison to True (used to improve test style)
ignore = E401,E402,E501,E711,E712
ignore = E401,E402,E501,E701,E711,E712
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Revision History

## 2.0 (unreleased)

- Removed warnings about calling save/load unnecessarily.
- Allow keyword arguments to be passed to class construction via `new` and `find` utilities.
- Fixed missing attributes added in `__init__` on `AttributeDictionary`.
- **BREAKING**: Disallowed positional arguments to construct dictionary-like converters.
- **BREAKING**: Renamed `ModelMixin.new` to `ModelMixin.create`.

## 1.3 (2017/01/24)

- Optimized the formatting of empty lists to create consistent diffs.
Expand Down
2 changes: 1 addition & 1 deletion yorm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
from .mixins import ModelMixin

__project__ = 'YORM'
__version__ = '1.3'
__version__ = '2.0b1'
2 changes: 1 addition & 1 deletion yorm/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class ModelMixin:
"""Adds ORM methods to a mapped class."""

@classmethod
def new(cls, *args, **kwargs):
def create(cls, *args, **kwargs):
return utilities.create(cls, *args, **kwargs)

@classmethod
Expand Down
4 changes: 2 additions & 2 deletions yorm/tests/test_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ def mixed_instance(mixed_class):
return mixed_class()

@patch('yorm.mixins.utilities')
def it_adds_a_new_method(utilities, mixed_class):
mixed_class.new('foobar', overwrite=True)
def it_adds_a_create_method(utilities, mixed_class):
mixed_class.create('foobar', overwrite=True)

expect(utilities.mock_calls) == [
call.create(mixed_class, 'foobar', overwrite=True)
Expand Down
33 changes: 26 additions & 7 deletions yorm/tests/test_types_extended.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,42 @@ def it_converts_correctly(value, data):

def describe_attribute_dictionary():

@attr(var1=Integer)
@attr(var2=String)
class SampleAttributeDictionary(AttributeDictionary):
"""Sample attribute dictionary."""
@pytest.fixture
def cls():
@attr(var1=Integer)
@attr(var2=String)
class SampleAttributeDictionary(AttributeDictionary): pass
return SampleAttributeDictionary

@pytest.fixture
def converter():
return SampleAttributeDictionary()
def cls_with_init():
@attr(var1=Integer)
class SampleAttributeDictionaryWithInit(AttributeDictionary):
def __init__(self, *args, var2="2", **kwargs):
super().__init__(*args, **kwargs)
self.var2 = var2
return SampleAttributeDictionaryWithInit

def it_cannot_be_used_directly():
with expect.raises(NotImplementedError):
AttributeDictionary.to_value(None)
with expect.raises(NotImplementedError):
AttributeDictionary.to_data(None)

def it_has_keys_available_as_attributes(converter):
def it_has_keys_available_as_attributes(cls):
converter = cls()

value = converter.to_value({'var1': 1, 'var2': "2"})

expect(value.var1) == 1
expect(value.var2) == "2"

def it_adds_extra_attributes_from_init(cls_with_init):
converter = cls_with_init()

value = converter.to_value({'var1': 1})
print(value.__dict__)

expect(value.var1) == 1
expect(value.var2) == "2"

Expand Down
11 changes: 11 additions & 0 deletions yorm/tests/test_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def it_creates_files(model_class):
instance = utilities.create(model_class, 'foo', 'bar')

expect(instance.__mapper__.exists) == True
expect(instance.__mapper__.modified) == False

def it_requires_files_to_not_yet_exist(model_class, instance):
instance.__mapper__.create()
Expand All @@ -53,6 +54,11 @@ def it_can_overwrite_files(model_class, instance):

utilities.create(model_class, 'foo', 'bar', overwrite=True)

def it_supports_keyword_arguments(model_class):
instance = utilities.create(model_class, 'foo', key='bar')

expect(instance.__mapper__.exists) == True

def it_can_also_be_called_with_an_instance(instance):
expect(yorm.create(instance)) == instance

Expand All @@ -75,6 +81,11 @@ def it_allows_objects_to_be_created(model_class):
expect(utilities.find(model_class, 'new', 'one', create=True)) == \
model_class('new', 'one')

def it_supports_keyword_arguments(model_class, instance):
instance.__mapper__.create()

expect(utilities.find(model_class, 'foo', key='bar')) == instance

def it_can_also_be_called_with_an_instance(instance):
expect(yorm.find(instance, create=True)) == instance

Expand Down
4 changes: 1 addition & 3 deletions yorm/types/extended.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,7 @@ def create_default(cls):
msg = "AttributeDictionary class must be subclassed to use"
raise NotImplementedError(msg)

obj = cls.__new__(cls)
obj.__dict__ = obj
return obj
return cls()


class SortedList(List):
Expand Down
44 changes: 23 additions & 21 deletions yorm/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,31 @@

import inspect
import logging
import warnings

from . import common, exceptions

log = logging.getLogger(__name__)


def create(class_or_instance, *args, overwrite=False):
"""Create a new mapped object."""
instance = _instantiate(class_or_instance, *args)
mapper = common.get_mapper(instance, expected=True)
def create(class_or_instance, *args, overwrite=False, **kwargs):
"""Create a new mapped object.
NOTE: Calling this function is unnecessary with 'auto_create' enabled.
if mapper.auto_create:
msg = "'create' is called automatically with 'auto_create' enabled"
warnings.warn(msg)
"""
instance = _instantiate(class_or_instance, *args, **kwargs)
mapper = common.get_mapper(instance, expected=True)

if mapper.exists and not overwrite:
msg = "{!r} already exists".format(mapper.path)
raise exceptions.DuplicateMappingError(msg)

return save(instance)
return load(save(instance))


def find(class_or_instance, *args, create=False): # pylint: disable=redefined-outer-name
def find(class_or_instance, *args, create=False, **kwargs): # pylint: disable=redefined-outer-name
"""Find a matching mapped object or return None."""
instance = _instantiate(class_or_instance, *args)
instance = _instantiate(class_or_instance, *args, **kwargs)
mapper = common.get_mapper(instance, expected=True)

if mapper.exists:
Expand All @@ -45,23 +44,26 @@ def match(cls, **kwargs):


def load(instance):
"""Force the loading of a mapped object's file."""
mapper = common.get_mapper(instance, expected=True)
"""Force the loading of a mapped object's file.
warnings.warn("'load' is called automatically")
NOTE: Calling this function is unnecessary. It exists for the
aesthetic purpose of having symmetry between save and load.
"""
mapper = common.get_mapper(instance, expected=True)

mapper.load()

return instance


def save(instance):
"""Save a mapped object to file."""
mapper = common.get_mapper(instance, expected=True)
"""Save a mapped object to file.
if mapper.auto_save:
msg = "'save' is called automatically with 'auto_save' enabled"
warnings.warn(msg)
NOTE: Calling this function is unnecessary with 'auto_save' enabled.
"""
mapper = common.get_mapper(instance, expected=True)

if mapper.deleted:
msg = "{!r} was deleted".format(mapper.path)
Expand All @@ -84,9 +86,9 @@ def delete(instance):
return None


def _instantiate(class_or_instance, *args):
def _instantiate(class_or_instance, *args, **kwargs):
if inspect.isclass(class_or_instance):
instance = class_or_instance(*args)
instance = class_or_instance(*args, **kwargs)
else:
assert not args
instance = class_or_instance
Expand Down

0 comments on commit fb8f35d

Please sign in to comment.