Skip to content

Commit

Permalink
Merge pull request #16 from johnomotani/fix-dill-pickling
Browse files Browse the repository at this point in the history
Fix pickling with dill
  • Loading branch information
johnomotani authored Jun 14, 2022
2 parents f77fec5 + 2286efa commit 7461bfd
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 28 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest
pip install pytest dill
- name: Test with pytest
run: |
pytest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pythonpublish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest
pip install pytest dill
- name: Test with pytest
run: |
pytest
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,9 @@ with the documentation for each option:

### `value_type`

The value_type argument can be used to give a type or sequence of types that the option
is allowed to have. Trying to set an option with a non-allowed type raises a
The `value_type` argument can be used to give a type or sequence of types that
the option is allowed to have. Trying to set an option with a non-allowed type
raises a
`ValueError`:
```python
>>> d2 = D(d=-2)
Expand Down Expand Up @@ -387,6 +388,12 @@ or all values including defaults, by passing `True` to the `with_defaults` argum
```
### Pickling (with dill)
`Options` objects can be pickled using `dill`. This is tested. Pickling of
`MutableOptions` objects is not currently supported.
Examples
--------
Expand Down
30 changes: 14 additions & 16 deletions optionsfactory/optionsfactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@


class OptionsFactory:
"""Factory to create Options instances
"""
"""Factory to create Options instances"""

def __init__(self, *args, **kwargs):
"""Define the members of Options instances that this factory will create
Expand Down Expand Up @@ -81,16 +79,12 @@ class A:

@property
def defaults(self):
"""Get the default values defined for this OptionsFactory
"""
"""Get the default values defined for this OptionsFactory"""
return deepcopy(self.__defaults)

@property
def doc(self):
"""Get the documentation for the options defined for this OptionsFactory
"""
"""Get the documentation for the options defined for this OptionsFactory"""
return {key: value.doc for key, value in self.__defaults.items()}

def add(self, **kwargs):
Expand Down Expand Up @@ -246,8 +240,7 @@ def parent(self):
return self.__parent

def as_table(self):
"""Return a string with a formatted table of the settings
"""
"""Return a string with a formatted table of the settings"""
return _options_table_string(self)

def to_dict(self, with_defaults=True):
Expand Down Expand Up @@ -475,8 +468,7 @@ def doc(self):
return deepcopy(self.__doc)

def as_table(self):
"""Return a string with a formatted table of the settings
"""
"""Return a string with a formatted table of the settings"""
return _options_table_string(self)

def to_dict(self, with_defaults=True):
Expand Down Expand Up @@ -554,6 +546,14 @@ def __setattr__(self, key, value):
raise TypeError("Options does not allow assigning to attributes")
super(OptionsFactory.Options, self).__setattr__(key, value)

def __getstate__(self):
# Need to define this so that pickling with dill works
return vars(self)

def __setstate__(self, state):
# Need to define this so that pickling with dill works
vars(self).update(state)

def is_default(self, key):
try:
return self.__is_default[key]
Expand Down Expand Up @@ -598,9 +598,7 @@ def __str__(self):


class MutableOptionsFactory(OptionsFactory):
"""Factory to create MutableOptions or Options instances
"""
"""Factory to create MutableOptions or Options instances"""

def create(self, values=None):
"""Create a MutableOptions instance
Expand Down
6 changes: 4 additions & 2 deletions optionsfactory/tests/test_mutableoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,8 @@ def test_values_nested(self):

def test_circular(self):
factory = MutableOptionsFactory(
a=lambda options: options.b, b=lambda options: options.a,
a=lambda options: options.b,
b=lambda options: options.a,
)
opts = factory.create()
with pytest.raises(ValueError, match="Circular definition"):
Expand Down Expand Up @@ -1136,7 +1137,8 @@ def test_values_nested(self):

def test_circular(self):
factory = MutableOptionsFactory(
a=lambda options: options.b, b=lambda options: options.a,
a=lambda options: options.b,
b=lambda options: options.a,
)
with pytest.raises(ValueError, match="Circular definition"):
opts = factory.create_immutable()
Expand Down
17 changes: 15 additions & 2 deletions optionsfactory/tests/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,8 @@ def test_values_nested(self):

def test_circular(self):
factory = OptionsFactory(
a=lambda options: options.b, b=lambda options: options.a,
a=lambda options: options.b,
b=lambda options: options.a,
)
with pytest.raises(ValueError, match="Circular definition"):
opts = factory.create()
Expand Down Expand Up @@ -483,7 +484,9 @@ def test_str(self):

def test_str_nested(self):
factory = OptionsFactory(
a=1, subsection=OptionsFactory(c=3, subsubsection=OptionsFactory(d=4)), b=2,
a=1,
subsection=OptionsFactory(c=3, subsubsection=OptionsFactory(d=4)),
b=2,
)
opts = factory.create({"b": 5, "subsection": {"subsubsection": {"d": 6}}})
assert (
Expand Down Expand Up @@ -705,3 +708,13 @@ def test_nested_with_arg_initialise(self):
assert opts.doc["subsection2"]["e"] is None
assert opts.subsection2.subsubsection.doc["f"] == "option f"
assert opts.subsection3.doc["z"] == "option z"

def test_dill_pickling(self):
import dill

factory = OptionsFactory(foo="bar")
options = factory.create()
pickled = dill.dumps(options)
unpickled = dill.loads(pickled, ignore=True)

assert unpickled.foo == "bar"
4 changes: 3 additions & 1 deletion optionsfactory/tests/test_withmeta.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ def test_expression_check_any(self):

def test_combined_check_all_any(self):
x = WithMeta(
5.0, check_all=is_positive, check_any=[lambda x: x < 10.0, is_None],
5.0,
check_all=is_positive,
check_any=[lambda x: x < 10.0, is_None],
)
assert x.evaluate_expression({}) == 5.0
x.value = -2.0
Expand Down
4 changes: 1 addition & 3 deletions optionsfactory/withmeta.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@


class WithMeta:
"""Type for passing metadata with options value or expression into OptionsFactory
"""
"""Type for passing metadata with options value or expression into OptionsFactory"""

def __init__(
self,
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"checking"
),
extras_require={"yaml": "PyYAML>=5.1"},
tests_require=["dill>=0.3.0"],
license="OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
long_description=long_description,
long_description_content_type="text/markdown",
Expand Down

0 comments on commit 7461bfd

Please sign in to comment.