Skip to content

Commit

Permalink
Merge pull request #23 from fiaas/time-fields
Browse files Browse the repository at this point in the history
Support Time fields, using datetime.datetime
  • Loading branch information
mortenlj authored Nov 7, 2017
2 parents f65ceee + 072717e commit a44be36
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 96 deletions.
2 changes: 1 addition & 1 deletion docs/source/developer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Use this class... ...when
The :py:class:`~k8s.fields.Field` class takes three parameters:

type
The type of value this field contains. Can be simple types (int, bool etc), or subclasses of :py:class:`~k8s.base.Model`.
The type of value this field contains. Can be simple types (int, bool etc), :py:class:`datetime.datetime` or subclasses of :py:class:`~k8s.base.Model`.

default_value
The field is set to this value when an instance of the class is created. The default default is ``None``.
Expand Down
7 changes: 7 additions & 0 deletions docs/source/modules/k8s.models.configmap.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
k8s\.models\.configmap module
=============================

.. automodule:: k8s.models.configmap
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions docs/source/modules/k8s.models.resourcequota.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
k8s\.models\.resourcequota module
=================================

.. automodule:: k8s.models.resourcequota
:members:
:undoc-members:
:show-inheritance:
2 changes: 2 additions & 0 deletions docs/source/modules/k8s.models.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ Submodules

k8s.models.autoscaler
k8s.models.common
k8s.models.configmap
k8s.models.deployment
k8s.models.ingress
k8s.models.pod
k8s.models.replication_controller
k8s.models.resourcequota
k8s.models.service
k8s.models.third_party_resource

11 changes: 9 additions & 2 deletions k8s/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
# -*- coding: utf-8
from __future__ import absolute_import

from datetime import datetime

import pyrfc3339


class Field(object):
"""Generic field on a k8s model"""
Expand Down Expand Up @@ -56,8 +60,7 @@ def default_value(self):
return self.type(new=False)
return self._default_value

@staticmethod
def _as_dict(value):
def _as_dict(self, value):
try:
return value.as_dict()
except AttributeError:
Expand All @@ -67,6 +70,8 @@ def _as_dict(value):
if isinstance(value, dict):
d = {k: v for k, v in value.items() if v is not None}
return d if d else None
elif datetime in (self.type, self.alt_type) and isinstance(value, datetime):
return pyrfc3339.generate(value, accept_naive=True)
else:
return value

Expand All @@ -78,6 +83,8 @@ def _from_dict(self, value):
except AttributeError:
if isinstance(value, self.type) or (self.alt_type and isinstance(value, self.alt_type)):
return value
if self.type is datetime:
return pyrfc3339.parse(value)
return self.type(value)

def __repr__(self):
Expand Down
7 changes: 4 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

GENERIC_REQ = [
"six == 1.10.0",
"requests == 2.13.0"
"requests == 2.13.0",
"pyrfc3339 == 1.0",
]

CODE_QUALITY_REQ = [
'prospector'
'prospector',
]

TESTS_REQ = [
Expand All @@ -22,7 +23,7 @@
'pytest-cov',
'pytest-helpers-namespace',
'pytest >= 3.0',
'gitpython'
'gitpython',
]


Expand Down
126 changes: 126 additions & 0 deletions tests/k8s/test_fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# -*- coding: utf-8
from datetime import datetime

import pytest
import pytz
import six

from k8s import config
from k8s.base import Model
from k8s.fields import Field, ListField, OnceField, ReadOnlyField, RequiredField
from k8s.models.common import ObjectMeta


class ModelTest(Model):
class Meta:
pass

metadata = Field(ObjectMeta)
field = Field(int)
list_field = ListField(int)
once_field = OnceField(int)
read_only_field = ReadOnlyField(int)
alt_type_field = Field(int, alt_type=six.text_type)
dict_field = Field(dict)
_exec = Field(int)
time_field = Field(datetime)


class TestFields(object):
@pytest.fixture(autouse=True)
def set_config_debug(self, monkeypatch):
monkeypatch.setattr(config, "debug", True)

@pytest.mark.parametrize("field_name,initial_value,other_value", (
("field", 1, 2),
("list_field", [1], [1, 2]),
("once_field", 1, 2),
("_exec", 1, 2)
))
def test_field_new(self, field_name, initial_value, other_value):
kwargs = {"new": True, field_name: initial_value}
model = ModelTest(**kwargs)
assert getattr(model, field_name) == initial_value
setattr(model, field_name, other_value)
assert getattr(model, field_name) == other_value

@pytest.mark.parametrize("field_name,initial_value,other_value", (
("field", 1, 2),
("list_field", [1], [1, 2]),
))
def test_field_old(self, field_name, initial_value, other_value):
model = ModelTest.from_dict({field_name: initial_value})
assert getattr(model, field_name) == initial_value
setattr(model, field_name, other_value)
assert getattr(model, field_name) == other_value

def test_once_field_old(self):
model = ModelTest.from_dict({"once_field": 1})
assert model.once_field == 1
model.once_field = 2
assert model.once_field == 1

def test_exec_field_old(self):
model = ModelTest.from_dict({"exec": 1})
assert model._exec == 1
model._exec = 2
assert model._exec == 2
assert model.as_dict()[u"exec"] == 2

def test_read_only_field_new(self):
model = ModelTest(new=True, read_only_field=1)
assert model.read_only_field is None
model.read_only_field = 2
assert model.read_only_field is None

def test_read_only_field_old(self):
model = ModelTest.from_dict({"read_only_field": 1})
assert model.read_only_field == 1
model.read_only_field = 2
assert model.read_only_field == 1

@pytest.mark.parametrize("value,modifier", [
(1, lambda x: x + 1),
(u"string", lambda x: x.upper())
])
def test_alt_type_field(self, value, modifier):
model = ModelTest.from_dict({"alt_type_field": value})
assert model.alt_type_field == value
assert model.as_dict()[u"alt_type_field"] == value
model.alt_type_field = modifier(value)
assert model.alt_type_field == modifier(value)

@pytest.mark.parametrize("input,dt", (
("2009-01-01T17:59:59Z", datetime(2009, 1, 1, 17, 59, 59, tzinfo=pytz.UTC)),
("2009-01-01T17:59:59+01:00", datetime(2009, 1, 1, 16, 59, 59, tzinfo=pytz.UTC)),
))
def test_time_field_from_dict(self, input, dt):
model = ModelTest.from_dict({"time_field": input})
assert isinstance(model.time_field, datetime)
assert model.time_field == dt

def test_time_field_as_dict(self):
model = ModelTest(time_field=datetime(2009, 1, 1, 17, 59, 59, tzinfo=pytz.UTC))
d = model.as_dict()
assert d["time_field"] == "2009-01-01T17:59:59Z"


class RequiredFieldTest(Model):
required_field = RequiredField(int)
field = Field(int, 100)


@pytest.mark.usefixtures("logger")
class TestRequiredField(object):
@pytest.mark.parametrize("kwargs", [
{"required_field": 1, "field": 2},
{"required_field": 1},
])
def test_create_with_fields(self, kwargs):
instance = RequiredFieldTest(new=True, **kwargs)
for key, value in kwargs.items():
assert getattr(instance, key) == value

def test_create_fails_when_field_missing(self):
with pytest.raises(TypeError):
RequiredFieldTest(new=True, field=1)
94 changes: 4 additions & 90 deletions tests/k8s/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
# -*- coding: utf-8
import pytest
import six
from k8s import config
from mock import create_autospec
from requests import Response

from k8s.base import Model
from k8s.client import Client
from k8s.fields import Field, ReadOnlyField, OnceField, ListField, RequiredField
from k8s.fields import Field, ListField, OnceField, ReadOnlyField
from k8s.models.common import ObjectMeta
from mock import create_autospec
from requests import Response


class ModelTest(Model):
Expand All @@ -25,92 +25,6 @@ class Meta:
_exec = Field(int)


class TestFields(object):
@pytest.fixture(autouse=True)
def set_config_debug(self, monkeypatch):
monkeypatch.setattr(config, "debug", True)

@pytest.mark.parametrize("field_name,initial_value,other_value", (
("field", 1, 2),
("list_field", [1], [1, 2]),
("once_field", 1, 2),
("_exec", 1, 2)
))
def test_field_new(self, field_name, initial_value, other_value):
kwargs = {"new": True, field_name: initial_value}
model = ModelTest(**kwargs)
assert getattr(model, field_name) == initial_value
setattr(model, field_name, other_value)
assert getattr(model, field_name) == other_value

@pytest.mark.parametrize("field_name,initial_value,other_value", (
("field", 1, 2),
("list_field", [1], [1, 2]),
))
def test_field_old(self, field_name, initial_value, other_value):
model = ModelTest.from_dict({field_name: initial_value})
assert getattr(model, field_name) == initial_value
setattr(model, field_name, other_value)
assert getattr(model, field_name) == other_value

def test_once_field_old(self):
model = ModelTest.from_dict({"once_field": 1})
assert model.once_field == 1
model.once_field = 2
assert model.once_field == 1

def test_exec_field_old(self):
model = ModelTest.from_dict({"exec": 1})
assert model._exec == 1
model._exec = 2
assert model._exec == 2
assert model.as_dict()[u"exec"] == 2

def test_read_only_field_new(self):
model = ModelTest(new=True, read_only_field=1)
assert model.read_only_field is None
model.read_only_field = 2
assert model.read_only_field is None

def test_read_only_field_old(self):
model = ModelTest.from_dict({"read_only_field": 1})
assert model.read_only_field == 1
model.read_only_field = 2
assert model.read_only_field == 1

@pytest.mark.parametrize("value,modifier", [
(1, lambda x: x + 1),
(u"string", lambda x: x.upper())
])
def test_alt_type_field(self, value, modifier):
model = ModelTest.from_dict({"alt_type_field": value})
assert model.alt_type_field == value
assert model.as_dict()[u"alt_type_field"] == value
model.alt_type_field = modifier(value)
assert model.alt_type_field == modifier(value)


class RequiredFieldTest(Model):
required_field = RequiredField(int)
field = Field(int, 100)


@pytest.mark.usefixtures("logger")
class TestRequiredField(object):
@pytest.mark.parametrize("kwargs", [
{"required_field": 1, "field": 2},
{"required_field": 1},
])
def test_create_with_fields(self, kwargs):
instance = RequiredFieldTest(new=True, **kwargs)
for key, value in kwargs.items():
assert getattr(instance, key) == value

def test_create_fails_when_field_missing(self):
with pytest.raises(TypeError):
RequiredFieldTest(new=True, field=1)


@pytest.mark.usefixtures("logger")
class TestModel(object):
@pytest.fixture()
Expand Down

0 comments on commit a44be36

Please sign in to comment.