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

Commit

Permalink
to_data now calls to_value
Browse files Browse the repository at this point in the history
  • Loading branch information
jacebrowning committed Sep 19, 2014
1 parent eac0400 commit 5c872df
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 120 deletions.
36 changes: 14 additions & 22 deletions yorm/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,16 @@ class Converter(metaclass=abc.ABCMeta): # pylint:disable=R0921

"""Base class for attribute converters."""

type = None # set in subclasses, used in dynamic converter inference
TYPE = None # type for inferred converters (set in subclasses)
DEFAULT = None # default value for conversion (set in subclasses)

@staticmethod
@abc.abstractmethod
def to_value(data): # pylint: disable=E0213
@abc.abstractclassmethod
def to_value(cls, obj): # pylint: disable=E0213
"""Convert the loaded value back to its original attribute type."""
raise NotImplementedError("method must be implemented in subclasses")

@staticmethod
@abc.abstractmethod
def to_data(value): # pylint: disable=E0213
@abc.abstractclassmethod
def to_data(cls, obj): # pylint: disable=E0213
"""Convert the attribute's value for optimal dumping to YAML."""
raise NotImplementedError("method must be implemented in subclasses")

Expand All @@ -83,7 +82,7 @@ class Dictionary(metaclass=ContainerMeta):
"""Base class for a dictionary of attribute converters."""

@classmethod
def to_value(cls, data): # pylint: disable=E0213
def to_value(cls, obj): # pylint: disable=E0213
"""Convert all loaded values back to its original attribute types."""
# TODO: determine if plain dictionaries should be allowed, remove pragma
if cls is Dictionary: # pragma: no cover
Expand All @@ -94,7 +93,7 @@ def to_value(cls, data): # pylint: disable=E0213

yorm_attrs = cls.yorm_attrs.copy()

for name, data in cls.to_dict(data).items():
for name, data in cls.to_dict(obj).items():
try:
converter = yorm_attrs.pop(name)
except KeyError:
Expand All @@ -111,12 +110,9 @@ def to_value(cls, data): # pylint: disable=E0213
return value

@classmethod
def to_data(cls, value): # pylint: disable=E0213
def to_data(cls, obj): # pylint: disable=E0213
"""Convert all attribute values for optimal dumping to YAML."""
# TODO: determine if plain dictionaries should be allowed, remove pragma
if cls is Dictionary: # pragma: no cover
msg = "Dictionary class must be subclassed to use"
raise NotImplementedError(msg)
value = cls.to_value(obj)

data = {}

Expand Down Expand Up @@ -167,7 +163,7 @@ def item_type(cls): # pylint: disable=E0213
return cls.yorm_attrs.get(cls.ALL)

@classmethod
def to_value(cls, data): # pylint: disable=E0213
def to_value(cls, obj): # pylint: disable=E0213
"""Convert all loaded values back to the original attribute type."""
# TODO: determine if plain lists should be allowed, remove pragma
if cls is List: # pragma: no cover
Expand All @@ -177,19 +173,15 @@ def to_value(cls, data): # pylint: disable=E0213

value = []

for item in cls.to_list(data):
for item in cls.to_list(obj):
value.append(cls.item_type.to_value(item))

return value

@classmethod
def to_data(cls, value): # pylint: disable=E0213
def to_data(cls, obj): # pylint: disable=E0213
"""Convert all attribute values for optimal dumping to YAML."""
# TODO: determine if plain lists should be allowed, remove pragma
if cls is List: # pragma: no cover
raise NotImplementedError("List class must be subclassed to use")
if not cls.item_type: # pragma: no cover
raise NotImplementedError("List subclass must specify item type")
value = cls.to_value(obj)

data = []

Expand Down
11 changes: 6 additions & 5 deletions yorm/extended.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ class Markdown(String):
""", re.VERBOSE)

@staticmethod
def to_value(data):
@classmethod
def to_value(cls, obj):
"""Join non-meaningful line breaks."""
value = String.to_value(data)
value = String.to_value(obj)
return Markdown.join(value)

@staticmethod
Expand All @@ -83,9 +83,10 @@ def join(text):
"""
return Markdown.REGEX_MARKDOWN_SPACES.sub(r'\1 \3', text).strip()

@staticmethod
def to_data(value):
@classmethod
def to_data(cls, obj):
"""Break a string at sentences and dump as a literal string."""
value = String.to_value(obj)
data = String.to_data(value)
split = Markdown.split(data)
return _Literal(split)
Expand Down
117 changes: 34 additions & 83 deletions yorm/standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,148 +10,99 @@ class Object(Converter): # pylint: disable=W0223

"""Base class for standard types (mapped directly to YAML)."""

@staticmethod
def to_value(data):
return data
@classmethod
def to_value(cls, obj):
return obj

@staticmethod
def to_data(value):
return value
@classmethod
def to_data(cls, obj):
return cls.to_value(obj)


class String(Object):

"""Converter for the `str` type."""

type = str
TYPE = str
DEFAULT = ""

@staticmethod
def to_value(data):
"""Convert data back to a string."""
return String.to_str(data)

@staticmethod
def to_data(value):
"""Convert a string into data."""
return String.to_str(value)

@staticmethod
def to_str(obj):
if isinstance(obj, String.type):
@classmethod
def to_value(cls, obj):
if isinstance(obj, cls.TYPE):
return obj
elif obj:
try:
return ', '.join(str(item) for item in obj)
except TypeError:
return str(obj)
else:
return ""
return cls.DEFAULT


class Integer(Object):

"""Converter for the `int` type."""

type = int

@staticmethod
def to_value(data):
"""Convert data back to an integer."""
return Integer.to_int(data)

@staticmethod
def to_data(value):
"""Convert an integer into data."""
return Integer.to_int(value)
TYPE = int
DEFAULT = 0

@staticmethod
def to_int(obj):
if isinstance(obj, Integer.type):
@classmethod
def to_value(cls, obj):
if isinstance(obj, cls.TYPE):
return obj
elif obj:
try:
return int(obj)
except ValueError:
return int(float(obj))
else:
return 0
return cls.DEFAULT


class Float(Object):

"""Converter for the `float` type."""

type = float
TYPE = float
DEFAULT = 0.0

@staticmethod
def to_value(data):
"""Convert data back to a float."""
return Float.to_float(data)

@staticmethod
def to_data(value):
"""Convert a float into data."""
return Float.to_float(value)

@staticmethod
def to_float(obj):
if isinstance(obj, Float.type):
@classmethod
def to_value(cls, obj):
if isinstance(obj, cls.TYPE):
return obj
elif obj:
return float(obj)
else:
return 0.0
return cls.DEFAULT


class Boolean(Object):

"""Converter for the `bool` type."""

type = bool
TYPE = bool
DEFAULT = False

FALSY = ('false', 'f', 'no', 'n', 'disabled', 'off', '0')

@staticmethod
def to_value(data):
"""Convert data back to a boolean."""
return Boolean.to_bool(data)

@staticmethod
def to_data(value):
"""Convert a boolean into data."""
return Boolean.to_bool(value)

@staticmethod
def to_bool(obj):
"""Convert a boolean-like object to a boolean.
>>> Boolean.to_bool(1)
True
>>> Boolean.to_bool(0)
False
>>> Boolean.to_bool(' True ')
True
>>> Boolean.to_bool('F')
False
"""
if isinstance(obj, str) and obj.lower().strip() in Boolean.FALSY:
@classmethod
def to_value(cls, obj):
if isinstance(obj, str) and obj.lower().strip() in cls.FALSY:
return False
else:
elif obj is not None:
return bool(obj)
else:
return cls.DEFAULT


def match(data):
"""Determine the appropriate convert for new data."""
"""Determine the appropriate converter for new data."""
log.trace("determining converter for: {}".format(repr(data)))
converters = Object.__subclasses__()

log.trace("converter options: {}".format(converters))
for converter in converters:
if converter.type and isinstance(data, converter.type):
if converter.TYPE and isinstance(data, converter.TYPE):
log.trace("matched: {}".format(converter))
return converter

Expand Down
17 changes: 9 additions & 8 deletions yorm/test/test_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ class Level(Converter):

"""Sample custom attribute."""

@staticmethod
def to_value(obj):
@classmethod
def to_value(cls, obj):
if obj:
if isinstance(obj, str):
return obj
Expand All @@ -30,15 +30,16 @@ def to_value(obj):
else:
return ""

@staticmethod
def to_data(obj):
count = obj.split('.')
@classmethod
def to_data(cls, obj):
value = cls.to_value(obj)
count = value.split('.')
if count == 0:
return int(obj)
return int(value)
elif count == 1:
return float(obj)
return float(value)
else:
return obj
return value


@map_attr(key2=String)
Expand Down
4 changes: 2 additions & 2 deletions yorm/test/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,9 @@ class TestConverter:
def test_not_implemented(self):
"""Verify `Converter` cannot be used directly."""
with pytest.raises(NotImplementedError):
Converter.to_value(None)
Converter.to_value(None) # pylint: disable=E1120
with pytest.raises(NotImplementedError):
Converter.to_data(None)
Converter.to_data(None) # pylint: disable=E1120


class TestDictionary:
Expand Down

0 comments on commit 5c872df

Please sign in to comment.