Browse Source

added datetimeex.Duration class

Fabian Peter Hammerle 9 years ago
parent
commit
36c0a4db65
4 changed files with 151 additions and 2 deletions
  1. 49 0
      ioex/datetimeex.py
  2. 2 2
      setup.py
  3. 60 0
      tests/datetimeex/test_duration.py
  4. 40 0
      tests/datetimeex/test_duration_yaml.py

+ 49 - 0
ioex/datetimeex.py

@@ -30,6 +30,55 @@ def construct_yaml_timestamp(loader, node):
 def register_yaml_timestamp_constructor(loader, tag = u'tag:yaml.org,2002:timestamp'):
     loader.add_constructor(tag, construct_yaml_timestamp)
 
+class Duration(object):
+
+    yaml_tag = u'!duration'
+
+    def __init__(self, years = 0):
+        self.years = years
+
+    @property
+    def years(self):
+        return self._years
+
+    @years.setter
+    def years(self, years):
+        if not type(years) is int:
+            raise TypeError('expected int, %r given' % years)
+        elif years < 0:
+            raise ValueError('number of years must be >= 0, %r given' % years)
+        else:
+            self._years = years
+
+    @property
+    def isoformat(self):
+        return 'P%dY' % self.years
+
+    def __eq__(self, other):
+        return (type(self) == type(other)
+                and self.years == other.years)
+
+    @classmethod
+    def from_yaml(cls, loader, node):
+        return cls(**loader.construct_mapping(node))
+
+    @classmethod
+    def register_yaml_constructor(cls, loader, tag = yaml_tag):
+        loader.add_constructor(tag, cls.from_yaml)
+
+    @classmethod
+    def to_yaml(cls, dumper, duration, tag = yaml_tag):
+        return dumper.represent_mapping(
+            tag = tag,
+            mapping = {k: v for k, v in {
+                'years': duration.years,
+                }.items() if v != 0},
+            )
+
+    @classmethod
+    def register_yaml_representer(cls, dumper):
+        dumper.add_representer(cls, cls.to_yaml)
+
 class Period(object):
 
     yaml_tag = u'!period'

+ 2 - 2
setup.py

@@ -5,12 +5,12 @@ import glob
 setup(
     name = 'ioex',
     packages = ['ioex'],
-    version = '0.5',
+    version = '0.6',
     description = 'extension for python\'s build-in input / output interface',
     author = 'Fabian Peter Hammerle',
     author_email = 'fabian.hammerle@gmail.com',
     url = 'https://github.com/fphammerle/ioex',
-    download_url = 'https://github.com/fphammerle/ioex/tarball/0.5',
+    download_url = 'https://github.com/fphammerle/ioex/tarball/0.6',
     keywords = [],
     classifiers = [],
     scripts = glob.glob('scripts/*'),

+ 60 - 0
tests/datetimeex/test_duration.py

@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+import pytest
+
+from ioex.datetimeex import Duration
+
+@pytest.mark.parametrize(('years'), [
+    0,
+    13,
+    ])
+def test_init(years):
+    d = Duration(years = years)
+    assert d.years == years
+
+def test_init_default():
+    d = Duration()
+    assert d.years == 0
+    None,
+
+@pytest.mark.parametrize(('years', 'exception_type'), [
+    [-2, ValueError],
+    ['1', TypeError],
+    ])
+def test_init_fail(years, exception_type):
+    with pytest.raises(exception_type):
+        Duration(years = years)
+
+@pytest.mark.parametrize(('years'), [
+    0,
+    13,
+    ])
+def test_set_years(years):
+    d = Duration()
+    d.years = years
+    assert d.years == years
+
+@pytest.mark.parametrize(('years', 'exception_type'), [
+    [-2, ValueError],
+    ['1', TypeError],
+    ])
+def test_set_years_fail(years, exception_type):
+    d = Duration()
+    with pytest.raises(exception_type):
+        d.years = years
+
+@pytest.mark.parametrize(('init_params', 'iso'), [
+    [{'years': 0}, 'P0Y'],
+    [{'years': 3}, 'P3Y'],
+    ])
+def test_get_isoformat(init_params, iso):
+    d = Duration(**init_params)
+    assert d.isoformat == iso
+
+@pytest.mark.parametrize(('a', 'b'), [
+    [Duration(), Duration(years = 0)],
+    [Duration(years = 0), Duration(years = 0)],
+    [Duration(years = 3), Duration(years = 3)],
+    ])
+def test_eq(a, b):
+    assert a == b
+    assert b == a

+ 40 - 0
tests/datetimeex/test_duration_yaml.py

@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+import pytest
+
+import copy
+from ioex.datetimeex import Duration
+yaml = pytest.importorskip('yaml')
+
+@pytest.mark.parametrize(('loader'), [yaml.Loader, yaml.SafeLoader])
+@pytest.mark.parametrize(('expected_duration', 'yaml_string'), [
+    [Duration(years = 0),  '!duration\nyears: 0'],
+    [Duration(years = 32), '!duration\nyears: 32'],
+    [Duration(years = 0),  '!duration\n{}'],
+    [Duration(years = 0),  '!duration {}'],
+    ])
+def test_from_yaml(expected_duration, yaml_string, loader):
+    loader_copy = copy.deepcopy(loader)
+    Duration.register_yaml_constructor(loader_copy)
+    loaded_duration = yaml.load(yaml_string, Loader = loader_copy)
+    assert expected_duration == loaded_duration
+
+@pytest.mark.parametrize(('loader'), [yaml.Loader, yaml.SafeLoader])
+@pytest.mark.parametrize(('expected_duration', 'yaml_string', 'tag'), [
+    [Duration(years = 2),  '!dur\nyears: 2', '!dur'],
+    [Duration(years = 0), '!duration_tag {}', '!duration_tag'],
+    ])
+def test_from_yaml(expected_duration, yaml_string, tag, loader):
+    loader_copy = copy.deepcopy(loader)
+    Duration.register_yaml_constructor(loader_copy, tag = tag)
+    loaded_duration = yaml.load(yaml_string, Loader = loader_copy)
+    assert expected_duration == loaded_duration
+
+@pytest.mark.parametrize(('dumper'), [yaml.Dumper, yaml.SafeDumper])
+@pytest.mark.parametrize(('duration', 'yaml_string'), [
+    [Duration(years = 0),  '!duration {}\n'],
+    [Duration(years = 32), '!duration\nyears: 32\n'],
+    ])
+def test_to_yaml(duration, yaml_string, dumper):
+    dumper_copy = copy.deepcopy(dumper)
+    Duration.register_yaml_representer(dumper_copy)
+    assert yaml.dump(duration, Dumper = dumper_copy, default_flow_style = False) == yaml_string