From 299ce0e52ae97b839542853330a7594300af1904 Mon Sep 17 00:00:00 2001 From: Quique Porta Date: Mon, 19 Sep 2022 22:28:04 +0200 Subject: [PATCH 01/13] Update mamba version --- .github/workflows/python-package.yml | 4 +--- requirements-test.txt | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 727af92..4580342 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -20,8 +20,6 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements-test.txt - pip install coveralls - python setup.py -q develop - name: Test with mamba run: | - PYTHONPATH=$PYTHONPATH:. mamba --enable-coverage \ No newline at end of file + PYTHONPATH=$PYTHONPATH:. mamba \ No newline at end of file diff --git a/requirements-test.txt b/requirements-test.txt index 9669953..7a3a879 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,2 +1,2 @@ expects==0.9.0 -mamba==0.11.0 +mamba==0.11.2 From b34909adac21c447f376a7388ffd41fb7b395b41 Mon Sep 17 00:00:00 2001 From: Quique Porta Date: Mon, 19 Sep 2022 22:29:13 +0200 Subject: [PATCH 02/13] Add documentation to mamba tests --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 4580342..f4cb043 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -22,4 +22,4 @@ jobs: pip install -r requirements-test.txt - name: Test with mamba run: | - PYTHONPATH=$PYTHONPATH:. mamba \ No newline at end of file + PYTHONPATH=$PYTHONPATH:. mamba -f documentation \ No newline at end of file From 2851d05a98955f3147674d85251300e96714438e Mon Sep 17 00:00:00 2001 From: Quique Porta Date: Mon, 19 Sep 2022 22:38:51 +0200 Subject: [PATCH 03/13] Add dockerfile to run local tests --- .coveragerc | 4 ---- Dockerfile | 12 ++++++++++++ README.rst | 13 +++---------- docker/test | 4 ++++ 4 files changed, 19 insertions(+), 14 deletions(-) delete mode 100644 .coveragerc create mode 100644 Dockerfile create mode 100755 docker/test diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index a2c0517..0000000 --- a/.coveragerc +++ /dev/null @@ -1,4 +0,0 @@ -[run] -source = ./simple_value_object/ -omit = ./simple_value_object/__init__.py -branch = True diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8d25b5f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.10-alpine + +WORKDIR /app + +ENV PYTHONPATH="/app" + +COPY ./requirements-test.txt / + +RUN pip install --upgrade pip +RUN pip install -r /requirements-test.txt + +COPY . /app diff --git a/README.rst b/README.rst index 8cc074e..bfb09d6 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ Value Object ============ -|Version Number| |Build Status| |Coverage Status| |Python Version| |License MIT| +|Version Number| |Python Version| |License MIT| Based on Ruby Gem by NoFlopSquad (https://github.com/noflopsquad/value-object) @@ -165,18 +165,11 @@ Test .. code-block:: sh - > pip install -r requirements-test.txt - > PYTHONPATH=$PYTHONPATH:. mamba + > $ docker/test .. |Version Number| image:: https://img.shields.io/badge/version-1.5.0-blue.svg -.. |Build Status| image:: https://travis-ci.org/quiqueporta/simple-value-object.svg?branch=master - :target: https://travis-ci.org/quiqueporta/simple-value-object - -.. |Coverage Status| image:: https://coveralls.io/repos/quiqueporta/simple-value-object/badge.svg?branch=master&service=github - :target: https://coveralls.io/github/quiqueporta/simple-value-object?branch=master - .. |License MIT| image:: https://img.shields.io/github/license/quiqueporta/simple-value-object -.. |Python Version| image:: https://img.shields.io/badge/python-3.4,_3.5,_3.6,_3.7-blue.svg +.. |Python Version| image:: https://img.shields.io/badge/python-3.6,_3.7,_3.8,_3.9,_3.10,-blue.svg diff --git a/docker/test b/docker/test new file mode 100755 index 0000000..1e1f2a1 --- /dev/null +++ b/docker/test @@ -0,0 +1,4 @@ +#!/bin/sh + +docker build -t simple-value-object:latest . +docker run --rm -it simple-value-object:latest mamba --f documentation From 3cbd2d59c1a667276d34150b4f53b84ff3c82df4 Mon Sep 17 00:00:00 2001 From: Quique Porta Date: Mon, 19 Sep 2022 22:41:04 +0200 Subject: [PATCH 04/13] Update description in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9ff52e1..6a5774b 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ license='MIT/X11', author='Quique Porta', author_email='quiqueporta@gmail.com', - description='A simple mixin for create Value Objects', + description='A simple mixin to create Value Objects', long_description=open('README.rst').read(), url='https://github.com/quiqueporta/value-object', download_url='https://github.com/quiqueporta/simple-value-object/releases', From 82ac62473439aa2d49c2156eb7005d49a1ab23c7 Mon Sep 17 00:00:00 2001 From: Quique Porta Date: Mon, 19 Sep 2022 22:51:40 +0200 Subject: [PATCH 05/13] Changed exception from CannotBeChangeException to CannotBeChanged --- CHANGES.rst | 5 +++++ simple_value_object/exceptions.py | 4 ++-- simple_value_object/value_object.py | 6 +++--- specs/value_object_spec.py | 28 ++++++++++++++-------------- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 31c4e0c..c4c3258 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,11 @@ Changelog ========= +2.0.0 (2022-09-19) +------------------ + +- Changed exception from CannotBeChangeException to CannotBeChanged + 1.5.0 (2020-12-07) ------------------ diff --git a/simple_value_object/exceptions.py b/simple_value_object/exceptions.py index 91aba33..787ae38 100644 --- a/simple_value_object/exceptions.py +++ b/simple_value_object/exceptions.py @@ -5,9 +5,9 @@ def __init__(self, *args, **kwargs): ) -class CannotBeChangeException(Exception): +class CannotBeChanged(Exception): def __init__(self, *args, **kwargs): - super(CannotBeChangeException, self).__init__( + super().__init__( 'You cannot change values from a Value Object, create a new one' ) diff --git a/simple_value_object/value_object.py b/simple_value_object/value_object.py index be7c9cf..ad20d0f 100644 --- a/simple_value_object/value_object.py +++ b/simple_value_object/value_object.py @@ -2,7 +2,7 @@ import inspect from .exceptions import ( - CannotBeChangeException, + CannotBeChanged, InvariantReturnValueException, NotDeclaredArgsException, ViolatedInvariantException @@ -95,7 +95,7 @@ def obtain_invariants(): return self def __setattr__(self, name, value): - raise CannotBeChangeException() + raise CannotBeChanged() def __eq__(self, other): if other is None: @@ -158,7 +158,7 @@ def __hash__(self): return id(self) def _immutable(self, *args, **kwargs): - raise CannotBeChangeException() + raise CannotBeChanged() __setitem__ = _immutable __delitem__ = _immutable diff --git a/specs/value_object_spec.py b/specs/value_object_spec.py index 7bd2ff0..1a41407 100644 --- a/specs/value_object_spec.py +++ b/specs/value_object_spec.py @@ -9,7 +9,7 @@ invariant ) from simple_value_object.exceptions import ( - CannotBeChangeException, + CannotBeChanged, InvariantReturnValueException, NotDeclaredArgsException, ViolatedInvariantException @@ -65,7 +65,7 @@ def change_point(): a_point.x = 4 expect(lambda: change_point()).to( - raise_error(CannotBeChangeException, 'You cannot change values from a Value Object, create a new one') + raise_error(CannotBeChanged, 'You cannot change values from a Value Object, create a new one') ) with it('can set default values'): @@ -131,46 +131,46 @@ def __init__(self, a_dict): another_value_object = AValueObject({'key': 'value'}) expect(lambda: a_value_object.a_dict.update({'key': 'another_value'})).to( - raise_error(CannotBeChangeException) + raise_error(CannotBeChanged) ) expect(lambda: another_value_object.a_dict.update({'key': 'another_value'})).to( - raise_error(CannotBeChangeException) + raise_error(CannotBeChanged) ) expect(lambda: a_value_object.a_dict.clear()).to( - raise_error(CannotBeChangeException) + raise_error(CannotBeChanged) ) expect(lambda: another_value_object.a_dict.clear()).to( - raise_error(CannotBeChangeException) + raise_error(CannotBeChanged) ) def remove_key(): del(a_value_object.a_dict['key']) del(another_value_object.a_dict['key']) expect(lambda: remove_key()).to( - raise_error(CannotBeChangeException) + raise_error(CannotBeChanged) ) def change_key(): a_value_object.a_dict['key'] = 'new_value' another_value_object.a_dict['key'] = 'new_value' expect(lambda: change_key()).to( - raise_error(CannotBeChangeException) + raise_error(CannotBeChanged) ) expect(lambda: a_value_object.a_dict.pop()).to( - raise_error(CannotBeChangeException) + raise_error(CannotBeChanged) ) expect(lambda: another_value_object.a_dict.pop()).to( - raise_error(CannotBeChangeException) + raise_error(CannotBeChanged) ) expect(lambda: a_value_object.a_dict.popitem()).to( - raise_error(CannotBeChangeException) + raise_error(CannotBeChanged) ) expect(lambda: another_value_object.a_dict.popitem()).to( - raise_error(CannotBeChangeException) + raise_error(CannotBeChanged) ) expect(lambda: a_value_object.a_dict.setdefault('key')).to( - raise_error(CannotBeChangeException) + raise_error(CannotBeChanged) ) expect(lambda: another_value_object.a_dict.setdefault('key')).to( - raise_error(CannotBeChangeException) + raise_error(CannotBeChanged) ) with it('does not allow to change lists arguments'): From fba6a468cde8b97eec9e238dec6c12cb44c72127 Mon Sep 17 00:00:00 2001 From: Quique Porta Date: Mon, 19 Sep 2022 22:53:11 +0200 Subject: [PATCH 06/13] Rename InvariantReturnValueException to InvariantMustReturnBool --- CHANGES.rst | 4 +++- simple_value_object/exceptions.py | 4 ++-- simple_value_object/value_object.py | 4 ++-- specs/value_object_spec.py | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index c4c3258..31d65a8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,9 @@ Changelog 2.0.0 (2022-09-19) ------------------ -- Changed exception from CannotBeChangeException to CannotBeChanged +- Rename CannotBeChangeException to CannotBeChanged +- Rename InvariantReturnValueException to InvariantMustReturnBool + 1.5.0 (2020-12-07) ------------------ diff --git a/simple_value_object/exceptions.py b/simple_value_object/exceptions.py index 787ae38..928889b 100644 --- a/simple_value_object/exceptions.py +++ b/simple_value_object/exceptions.py @@ -16,8 +16,8 @@ class ViolatedInvariantException(Exception): pass -class InvariantReturnValueException(Exception): +class InvariantMustReturnBool(Exception): def __init__(self, *args, **kwargs): - super(InvariantReturnValueException, self).__init__( + super(InvariantMustReturnBool, self).__init__( 'Invariants must return a boolean value' ) diff --git a/simple_value_object/value_object.py b/simple_value_object/value_object.py index ad20d0f..9be1232 100644 --- a/simple_value_object/value_object.py +++ b/simple_value_object/value_object.py @@ -3,7 +3,7 @@ from .exceptions import ( CannotBeChanged, - InvariantReturnValueException, + InvariantMustReturnBool, NotDeclaredArgsException, ViolatedInvariantException ) @@ -72,7 +72,7 @@ def invariant_execute(invariant): return_value = invariant(self, self) if not isinstance(return_value, bool): - raise InvariantReturnValueException() + raise InvariantMustReturnBool() return return_value diff --git a/specs/value_object_spec.py b/specs/value_object_spec.py index 1a41407..8d7b35b 100644 --- a/specs/value_object_spec.py +++ b/specs/value_object_spec.py @@ -10,7 +10,7 @@ ) from simple_value_object.exceptions import ( CannotBeChanged, - InvariantReturnValueException, + InvariantMustReturnBool, NotDeclaredArgsException, ViolatedInvariantException ) @@ -240,4 +240,4 @@ def __init__(self, day, month, year): def first_year_quarter(cls, instance): return 0 - expect(lambda: Date(8, 6, 2002)).to(raise_error(InvariantReturnValueException)) + expect(lambda: Date(8, 6, 2002)).to(raise_error(InvariantMustReturnBool)) From e0cf6098f5a87b261a012fa772368333627af0f0 Mon Sep 17 00:00:00 2001 From: Quique Porta Date: Mon, 19 Sep 2022 23:02:01 +0200 Subject: [PATCH 07/13] Rename ViolatedInvariantException to InvariantViolation --- CHANGES.rst | 2 ++ simple_value_object/exceptions.py | 18 +++++++----------- simple_value_object/value_object.py | 23 ++++++----------------- specs/value_object_spec.py | 10 +++++----- 4 files changed, 20 insertions(+), 33 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 31d65a8..c6ccfa2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,8 @@ Changelog - Rename CannotBeChangeException to CannotBeChanged - Rename InvariantReturnValueException to InvariantMustReturnBool +- Rename NotDeclaredArgsException to ConstructorWithoutArguments +- Rename ViolatedInvariantException to InvariantViolation 1.5.0 (2020-12-07) diff --git a/simple_value_object/exceptions.py b/simple_value_object/exceptions.py index 928889b..6796555 100644 --- a/simple_value_object/exceptions.py +++ b/simple_value_object/exceptions.py @@ -1,23 +1,19 @@ -class NotDeclaredArgsException(Exception): - def __init__(self, *args, **kwargs): - super(NotDeclaredArgsException, self).__init__( - 'No arguments declared in __init__' - ) +class ConstructorWithoutArguments(Exception): + def __init__(self): + super().__init__('No arguments declared in __init__') class CannotBeChanged(Exception): - def __init__(self, *args, **kwargs): + def __init__(self): super().__init__( 'You cannot change values from a Value Object, create a new one' ) -class ViolatedInvariantException(Exception): +class InvariantViolation(Exception): pass class InvariantMustReturnBool(Exception): - def __init__(self, *args, **kwargs): - super(InvariantMustReturnBool, self).__init__( - 'Invariants must return a boolean value' - ) + def __init__(self): + super().__init__('Invariants must return a boolean value') diff --git a/simple_value_object/value_object.py b/simple_value_object/value_object.py index 9be1232..a6a9663 100644 --- a/simple_value_object/value_object.py +++ b/simple_value_object/value_object.py @@ -4,8 +4,8 @@ from .exceptions import ( CannotBeChanged, InvariantMustReturnBool, - NotDeclaredArgsException, - ViolatedInvariantException + ConstructorWithoutArguments, + InvariantViolation ) MIN_NUMBER_ARGS = 1 @@ -24,7 +24,7 @@ def check_class_initialization(): init_constructor_without_arguments = len(args_spec.args) <= MIN_NUMBER_ARGS if init_constructor_without_arguments: - raise NotDeclaredArgsException() + raise ConstructorWithoutArguments() def replace_mutable_kwargs_with_immutable_types(): for arg, value in kwargs.items(): @@ -62,11 +62,7 @@ def override_default_values_with_args(): def check_invariants(): for invariant in obtain_invariants(): if not invariant_execute(invariant[INVARIANT_METHOD]): - raise ViolatedInvariantException( - 'Args violates invariant: {}'.format( - invariant[INVARIANT_NAME] - ) - ) + raise InvariantViolation(f'Invariant violation: {invariant[INVARIANT_NAME]}') def invariant_execute(invariant): return_value = invariant(self, self) @@ -125,15 +121,8 @@ def hash(self): class ArgsSpec(object): def __init__(self, method): - try: - if sys.version_info.major == 2: - self.__argspec = inspect.getargspec(method) - self.__varkw = self.__argspec.keywords - else: - self.__argspec = inspect.getfullargspec(method) - self.__varkw = self.__argspec.varkw - except TypeError: - raise NotDeclaredArgsException() + self.__argspec = inspect.getfullargspec(method) + self.__varkw = self.__argspec.varkw @property def args(self): diff --git a/specs/value_object_spec.py b/specs/value_object_spec.py index 8d7b35b..e9500a0 100644 --- a/specs/value_object_spec.py +++ b/specs/value_object_spec.py @@ -11,8 +11,8 @@ from simple_value_object.exceptions import ( CannotBeChanged, InvariantMustReturnBool, - NotDeclaredArgsException, - ViolatedInvariantException + ConstructorWithoutArguments, + InvariantViolation ) @@ -114,7 +114,7 @@ class WithNoArgs(ValueObject): pass expect(lambda: WithNoArgs()).to( - raise_error(NotDeclaredArgsException, 'No arguments declared in __init__') + raise_error(ConstructorWithoutArguments, 'No arguments declared in __init__') ) with it('must have number of values equal to number of args'): @@ -223,11 +223,11 @@ def x_less_than_y(cls, instance): expect(lambda: AnotherPoint(-5, 3)).to( - raise_error(ViolatedInvariantException, 'Args violates invariant: inside_first_quadrant') + raise_error(InvariantViolation, 'Invariant violation: inside_first_quadrant') ) expect(lambda: AnotherPoint(6, 3)).to( - raise_error(ViolatedInvariantException, 'Args violates invariant: x_less_than_y') + raise_error(InvariantViolation, 'Invariant violation: x_less_than_y') ) with it('raises an exception when a declared invariant doesnt returns a boolean value'): From 80167966180edfc77d1d898df30ab3d7655d9d40 Mon Sep 17 00:00:00 2001 From: Quique Porta Date: Mon, 19 Sep 2022 23:04:16 +0200 Subject: [PATCH 08/13] Remove object inheritance from ValueObject --- simple_value_object/value_object.py | 10 +++++----- specs/value_object_spec.py | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/simple_value_object/value_object.py b/simple_value_object/value_object.py index a6a9663..82f8d9e 100644 --- a/simple_value_object/value_object.py +++ b/simple_value_object/value_object.py @@ -1,10 +1,10 @@ -import sys import inspect +import sys from .exceptions import ( CannotBeChanged, - InvariantMustReturnBool, ConstructorWithoutArguments, + InvariantMustReturnBool, InvariantViolation ) @@ -13,10 +13,10 @@ INVARIANT_METHOD = 1 -class ValueObject(object): +class ValueObject: def __new__(cls, *args, **kwargs): - self = super(ValueObject, cls).__new__(cls) + self = super().__new__(cls) args_spec = ArgsSpec(self.__init__) @@ -118,7 +118,7 @@ def hash(self): return hash(repr(self)) -class ArgsSpec(object): +class ArgsSpec: def __init__(self, method): self.__argspec = inspect.getfullargspec(method) diff --git a/specs/value_object_spec.py b/specs/value_object_spec.py index e9500a0..1de8264 100644 --- a/specs/value_object_spec.py +++ b/specs/value_object_spec.py @@ -4,14 +4,15 @@ description, it ) + from simple_value_object import ( ValueObject, invariant ) from simple_value_object.exceptions import ( CannotBeChanged, - InvariantMustReturnBool, ConstructorWithoutArguments, + InvariantMustReturnBool, InvariantViolation ) From 51063084b9829a9befd174b0c0819f0fbdcf240b Mon Sep 17 00:00:00 2001 From: Quique Porta Date: Mon, 19 Sep 2022 23:59:14 +0200 Subject: [PATCH 09/13] Simplify invariants --- CHANGES.rst | 1 + README.rst | 18 ++++++++++-------- simple_value_object/decorators.py | 5 +++-- simple_value_object/value_object.py | 29 +++++++++++++---------------- specs/value_object_spec.py | 10 +++++----- 5 files changed, 32 insertions(+), 31 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index c6ccfa2..9f523bc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,7 @@ Changelog - Rename InvariantReturnValueException to InvariantMustReturnBool - Rename NotDeclaredArgsException to ConstructorWithoutArguments - Rename ViolatedInvariantException to InvariantViolation +- Simplifly invariants now receives `self` attribute only 1.5.0 (2020-12-07) diff --git a/README.rst b/README.rst index bfb09d6..c59b930 100644 --- a/README.rst +++ b/README.rst @@ -43,7 +43,7 @@ Constructor and field readers # 2 point.x = 5 - # CannotBeChangeException: You cannot change values from a Value Object, create a new one + # CannotBeChanged: You cannot change values from a Value Object, create a new one class Date(ValueObject): def __init__(self, day, month, year): @@ -61,7 +61,7 @@ Constructor and field readers # 2015 date.month = 5 - # CannotBeChangeException: You cannot change values from a Value Object, create a new one + # CannotBeChanged: You cannot change values from a Value Object, create a new one Equality based on field values @@ -115,6 +115,8 @@ Hash code based on field values Invariants ~~~~~~~~~~ +Invariants **must** return a boolean value. + .. code-block:: python from simple_value_object import ValueObject, invariant @@ -125,18 +127,18 @@ Invariants pass @invariant - def inside_first_quadrant(cls, instance): - return instance.x > 0 and instance.y > 0 + def inside_first_quadrant(self): + return self.x > 0 and self.y > 0 @invariant - def x_less_than_y(cls, instance): - return instance.x < instance.y + def x_less_than_y(self): + return self.x < self.y Point(-5, 3) - #ViolatedInvariantException: Args violates invariant: inside_first_cuadrant + #InvariantViolation: inside_first_cuadrant Point(6, 3) - #ViolatedInvariantException: Args violates invariant: x_less_than_y + #InvariantViolation: x_less_than_y Point(1,3) #<__main__.Point at 0x7f2bd043c780> diff --git a/simple_value_object/decorators.py b/simple_value_object/decorators.py index f5816db..e4c798d 100644 --- a/simple_value_object/decorators.py +++ b/simple_value_object/decorators.py @@ -1,5 +1,6 @@ def invariant(func): - def invariant_func_wrapper(cls, instance): - return func(cls, instance) + def invariant_func_wrapper(instance): + return func(instance) + invariant_func_wrapper.name = func.__name__ return invariant_func_wrapper diff --git a/simple_value_object/value_object.py b/simple_value_object/value_object.py index 82f8d9e..582e3df 100644 --- a/simple_value_object/value_object.py +++ b/simple_value_object/value_object.py @@ -61,27 +61,24 @@ def override_default_values_with_args(): def check_invariants(): for invariant in obtain_invariants(): - if not invariant_execute(invariant[INVARIANT_METHOD]): - raise InvariantViolation(f'Invariant violation: {invariant[INVARIANT_NAME]}') + if is_invariant_violated(invariant): + raise InvariantViolation(f'Invariant violation: {invariant.name}') - def invariant_execute(invariant): - return_value = invariant(self, self) + def obtain_invariants(): + for invariant in filter(is_invariant, cls.__dict__.values()): + yield invariant - if not isinstance(return_value, bool): - raise InvariantMustReturnBool() + def is_invariant(method): + return hasattr(method, '__call__') and method.__name__ == 'invariant_func_wrapper' - return return_value + def is_invariant_violated(invariant): + invariant_result = invariant(self) - def is_invariant(method): - try: - return 'invariant_func_wrapper' in str(method) and '__init__' not in str(method) - except TypeError: - return False + if not isinstance(invariant_result, bool): + raise InvariantMustReturnBool() + + return invariant_result is False - def obtain_invariants(): - invariants = [(member[INVARIANT_NAME], member[INVARIANT_METHOD]) for member in inspect.getmembers(cls, is_invariant)] - for invariant in invariants: - yield invariant check_class_initialization() replace_mutable_kwargs_with_immutable_types() diff --git a/specs/value_object_spec.py b/specs/value_object_spec.py index 1de8264..553ff6c 100644 --- a/specs/value_object_spec.py +++ b/specs/value_object_spec.py @@ -215,12 +215,12 @@ def __init__(self, x, y): pass @invariant - def inside_first_quadrant(cls, instance): - return instance.x > 0 and instance.y > 0 + def inside_first_quadrant(self): + return self.x > 0 and self.y > 0 @invariant - def x_less_than_y(cls, instance): - return instance.x < instance.y + def x_less_than_y(self): + return self.x < self.y expect(lambda: AnotherPoint(-5, 3)).to( @@ -238,7 +238,7 @@ def __init__(self, day, month, year): pass @invariant - def first_year_quarter(cls, instance): + def first_year_quarter(self): return 0 expect(lambda: Date(8, 6, 2002)).to(raise_error(InvariantMustReturnBool)) From 76a3d0ecf5019641d0a0986962e226bc0eb310c6 Mon Sep 17 00:00:00 2001 From: Quique Porta Date: Tue, 20 Sep 2022 00:05:31 +0200 Subject: [PATCH 10/13] Increase version --- README.rst | 4 ++-- simple_value_object/__init__.py | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index c59b930..e9b6736 100644 --- a/README.rst +++ b/README.rst @@ -170,8 +170,8 @@ Test > $ docker/test -.. |Version Number| image:: https://img.shields.io/badge/version-1.5.0-blue.svg +.. |Version Number| image:: https://img.shields.io/badge/version-2.0.0-blue.svg .. |License MIT| image:: https://img.shields.io/github/license/quiqueporta/simple-value-object -.. |Python Version| image:: https://img.shields.io/badge/python-3.6,_3.7,_3.8,_3.9,_3.10,-blue.svg +.. |Python Version| image:: https://img.shields.io/badge/python-3.6,_3.7,_3.8,_3.9,_3.10-blue.svg diff --git a/simple_value_object/__init__.py b/simple_value_object/__init__.py index 01d262c..c606646 100644 --- a/simple_value_object/__init__.py +++ b/simple_value_object/__init__.py @@ -1,9 +1,7 @@ -# -*- coding: utf-8 -*- - from .value_object import ValueObject from .decorators import invariant -VERSION = (1, 5, 0, 'final') +VERSION = (2, 0, 0, 'final') __version__ = VERSION From 7f76703d3202b0ad010b5657ca30225b7cc5ecee Mon Sep 17 00:00:00 2001 From: Quique Porta Date: Tue, 20 Sep 2022 00:08:01 +0200 Subject: [PATCH 11/13] Update authors --- AUTHORS | 5 +++-- simple_value_object/value_object.py | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index cd6a657..de7c92a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,5 +1,5 @@ -Original Author ---------------- +Author +------ Quique Porta https://github.com/quiqueporta @@ -8,3 +8,4 @@ Contributors William Travis Jones https://github.com/wtjones David Arias http://davidarias.net Hugo Leonardo Costa e Silva https://github.com/hugoleodev +Jesse Heitler https://github.com/jesseh diff --git a/simple_value_object/value_object.py b/simple_value_object/value_object.py index 582e3df..6f4cbb6 100644 --- a/simple_value_object/value_object.py +++ b/simple_value_object/value_object.py @@ -69,7 +69,10 @@ def obtain_invariants(): yield invariant def is_invariant(method): - return hasattr(method, '__call__') and method.__name__ == 'invariant_func_wrapper' + try: + return method.__name__ == 'invariant_func_wrapper' + except AttributeError: + return False def is_invariant_violated(invariant): invariant_result = invariant(self) From 43b6c8761894354bd863c374640b7f3397a8c723 Mon Sep 17 00:00:00 2001 From: Quique Porta Date: Thu, 27 Oct 2022 19:25:35 +0200 Subject: [PATCH 12/13] Update CHANGE log --- CHANGES.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9f523bc..9535b57 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,7 @@ Changelog ========= -2.0.0 (2022-09-19) +2.0.0 (2022-10-27) ------------------ - Rename CannotBeChangeException to CannotBeChanged @@ -9,6 +9,7 @@ Changelog - Rename NotDeclaredArgsException to ConstructorWithoutArguments - Rename ViolatedInvariantException to InvariantViolation - Simplifly invariants now receives `self` attribute only +- Fix replace_mutable_kwargs_with_immutable_types for `set` kwargs 1.5.0 (2020-12-07) From d6fd61f921cf6c81335fcc2fd5f0ee27b766e6ca Mon Sep 17 00:00:00 2001 From: Quique Porta Date: Thu, 27 Oct 2022 19:28:55 +0200 Subject: [PATCH 13/13] Create a base exception to make capture errors easier --- simple_value_object/exceptions.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/simple_value_object/exceptions.py b/simple_value_object/exceptions.py index 6796555..4322493 100644 --- a/simple_value_object/exceptions.py +++ b/simple_value_object/exceptions.py @@ -1,19 +1,23 @@ -class ConstructorWithoutArguments(Exception): +class SimpleValueObjectException(Exception): + pass + + +class ConstructorWithoutArguments(SimpleValueObjectException): def __init__(self): super().__init__('No arguments declared in __init__') -class CannotBeChanged(Exception): +class CannotBeChanged(SimpleValueObjectException): def __init__(self): super().__init__( 'You cannot change values from a Value Object, create a new one' ) -class InvariantViolation(Exception): +class InvariantViolation(SimpleValueObjectException): pass -class InvariantMustReturnBool(Exception): +class InvariantMustReturnBool(SimpleValueObjectException): def __init__(self): super().__init__('Invariants must return a boolean value')