Repository: brocksam/pyproprop

Issue:
Error in post method
When calling a processed property with a post method, for example: 

```
squares = processed_property(
        "squares",
        type=int,
        iterable_allowed=True,
        method=square()
)
```
 The following is raised: `NameError: name 'optional' is not defined`.

This is comping from line `processed_property` line 537:
https://github.com/brocksam/pyproprop/blob/693950be09186100bc029535ddaef0e9f61eb4f9/pyproprop/processed_property.py#L524-L539

Optional is not is in the scope of `apply_method`.

Discussion:

Patch:
diff --git a/.restyled.yaml b/.restyled.yaml
index 877a3c5..3c5a7dc 100644
--- a/.restyled.yaml
+++ b/.restyled.yaml
@@ -1,3 +1,2 @@
 restylers:
-  - isort
-  - reorder-python-imports
\ No newline at end of file
+  - isort
\ No newline at end of file
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 14e1e13..48d6418 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -38,6 +38,14 @@ Unreleased
 
 - None
 
+[0.4.1] - 2020-09-22
+--------------------
+
+Fixed
+~~~~~
+
+- Fix bug which raises error when using the `method` kwarg with `processed_property`, as reported in issue #48. Was caused by variables being out of scope in the function `apply_method` with `pyproprop/processed_property.py`.
+
 [0.4.0] - 2020-09-22
 --------------------
 
diff --git a/pyproprop/processed_property.py b/pyproprop/processed_property.py
index 641dc4d..108ae46 100644
--- a/pyproprop/processed_property.py
+++ b/pyproprop/processed_property.py
@@ -7,17 +7,13 @@ reuse.
 
 """
 from numbers import Real
-from typing import Any
-from typing import Iterable
-from typing import Tuple
+from typing import Any, Iterable, Tuple
 
 import numpy as np
 
-from .format_str_case import format_str_case
-from .format_str_case import SUPPORTED_STR_FORMAT_OPTIONS
+from .format_str_case import SUPPORTED_STR_FORMAT_OPTIONS, format_str_case
 from .options import Options
-from .utils import format_for_output
-from .utils import generate_name_description_error_message
+from .utils import format_for_output, generate_name_description_error_message
 
 __all__ = ["processed_property"]
 
@@ -141,7 +137,8 @@ def processed_property(name, **kwargs):
             args = (name_str, )
             setter_dispatcher.update({process_optimisable: (args, {})})
         if post_method is not None:
-            setter_dispatcher.update({apply_method: no_args_kwargs})
+            args = (optional, post_method)
+            setter_dispatcher.update({apply_method: (args, {})})
         return setter_dispatcher
 
     storage_name = "_" + name
@@ -521,7 +518,7 @@ def check_len(value, len_sequence, name_str):
     return value
 
 
-def apply_method(value):
+def apply_method(value, optional, post_method):
     """Applies a specified method at the end of the property setter.
 
     Parameters
diff --git a/setup.py b/setup.py
index caceec9..3dc14a5 100644
--- a/setup.py
+++ b/setup.py
@@ -117,7 +117,7 @@ def get_contents_from_file(filepath, by_line=False, strip=""):
 
 
 PACKAGE_NAME = "pyproprop"
-VERSION = "0.4.0"
+VERSION = "0.4.1"
 AUTHOR = "Sam Brockie"
 AUTHOR_EMAIL = "sambrockie@icloud.com"
 DESCRIPTION = ("Package for aiding writing classes with lots of similar "


Test patch:
diff --git a/tests/unit/processed_property/test_post_method.py b/tests/unit/processed_property/test_post_method.py
index e69de29..2ad6b79 100644
--- a/tests/unit/processed_property/test_post_method.py
+++ b/tests/unit/processed_property/test_post_method.py
@@ -0,0 +1,46 @@
+"""Test processed properties with post-methods applied."""
+
+
+import hypothesis.strategies as st
+import numpy as np
+from hypothesis import given
+
+from pyproprop import processed_property
+
+
+def square(x):
+    """Dummy function for squaring two numbers."""
+    xx = x * x
+    return xx
+
+
+class ClassWithPostMethodProperty:
+    """Dummy class for testing processed properties with post methods."""
+
+    method_prop = processed_property("method_prop", type=int, method=square)
+    np_method_prop = processed_property("np_method_prop", type=float,
+                                        cast=True, iterable_allowed=True,
+                                        method=np.cos)
+
+
+@given(st.integers())
+def test_post_method(test_value):
+    """Applied python function correctly."""
+    test_fixture = ClassWithPostMethodProperty()
+    test_fixture.method_prop = test_value
+    assert test_fixture.method_prop == square(test_value)
+
+
+@given(st.one_of(st.integers(min_value=-9223372036854775808,
+                             max_value=9223372036854775807),
+                 st.floats(allow_infinity=False, allow_nan=False),
+                 st.lists(st.integers(min_value=-9223372036854775808,
+                                      max_value=9223372036854775807)),
+                 )
+       )
+def test_post_method_numpy(test_value):
+    """Applies numpy function correctly."""
+    test_fixture = ClassWithPostMethodProperty()
+    test_fixture.np_method_prop = test_value
+    expected_result = np.cos(np.array([test_value]))
+    assert (test_fixture.np_method_prop == expected_result).all()