Repository: glotzerlab/signac
Pull request #112 (2019-02-04 21:53:21)

Enable deleting keys with attr deletion for AttrDict.
<!-- Provide a general summary of your changes in the Title above -->

## Description
<!-- Describe your changes in detail -->
Enable deleting of document key-values by attribute, deletion.
For example:
```python
del job.doc.foo
```
## Motivation and Context
<!-- Why is this change required? What problem does it solve? -->
<!-- If it fixes an open issue, please link to the issue here. -->
We can already "get" values by attribute API, why not delete them as well?

## Types of Changes
<!-- Please select all items that apply either now or after creating the pull request: -->
- [ ] Documentation update
- [ ] Bug fix
- [x] New feature
- [ ] Breaking change<sup>1</sup>

This pull request is based on
- [ ] *master*, because it is a bug fix or an update to the documentation.
- [x] *develop*, because it introduces a new feature.

<sup>1</sup>The change breaks (or has the potential to break) existing functionality.

## Checklist:
<!-- Please select all items that apply either now or after creating the pull request. -->
<!-- If you are unsure about any of these items, do not hesitate to ask! -->
- [x] I am familiar with the [**Contributing Guidelines**](https://github.com/glotzerlab/signac/blob/master/CONTRIBUTING.md).
- [x] I agree with the terms of the [**Contributor Agreement**](https://github.com/glotzerlab/signac/blob/master/ContributorAgreement.md).
- [x] My name is on the [list of contributors](https://github.com/glotzerlab/signac/blob/master/contributors.txt).
- [x] My code follows the [code style guideline](https://github.com/glotzerlab/signac/blob/misc/contributor-flow/CONTRIBUTING.md#code-style) of this project.
- [x] The changes introduced by this pull request are covered by existing or newly introduced tests.

If necessary:
- [ ] I have updated the API documentation as part of the package doc-strings.
- [ ] I have created a separate pull request to update the [framework documentation](https://docs.signac.io/) on [signac-docs](https://github.com/glotzerlab/signac-docs) and linked it here.
- [x] I have updated the [changelog](https://github.com/glotzerlab/signac/blob/master/changelog.txt).



Patch:
diff --git a/changelog.txt b/changelog.txt
index 09d86863..3d171bac 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -27,6 +27,7 @@ Added
  - Adds an ``H5Store`` class, useful for storing array-like data with an HDF5 backend. Accessible via ``with job.data:`` or ``with project.data:``.
  - Add automatic cast of numpy arrays to lists when storing them within a `JSONDict`, e.g., a `job.statepoint` or `job.document`.
  - Enable `Collection` class to manage collections stored in gzip files.
+ - Enable deleting of `JSONDict` keys through the attribute interface, e.g., `del job.doc.foo`.
 
 Fixed
 +++++
diff --git a/signac/core/attrdict.py b/signac/core/attrdict.py
index fa24f958..1623ce05 100644
--- a/signac/core/attrdict.py
+++ b/signac/core/attrdict.py
@@ -38,3 +38,9 @@ class SyncedAttrDict(_SyncedDict):
                 super(SyncedAttrDict, self).__setattr__(key, value)
             else:
                 self.__setitem__(key, value)
+
+    def __delattr__(self, key):
+        if key.startswith('__') or key in self._PROTECTED_KEYS:
+            super(SyncedAttrDict, self).__delattr__(key)
+        else:
+            self.__delitem__(key)


Test patch:
diff --git a/tests/test_job.py b/tests/test_job.py
index f1103fa2..0f2fc2ff 100644
--- a/tests/test_job.py
+++ b/tests/test_job.py
@@ -273,6 +273,13 @@ class JobSPInterfaceTest(BaseJobTest):
         self.assertNotIn('b', job.sp)
         with self.assertRaises(AttributeError):
             job.sp.b
+        job.sp.b = 0
+        self.assertIn('b', job.sp)
+        self.assertEqual(job.sp.b, 0)
+        del job.sp.b
+        self.assertNotIn('b', job.sp)
+        with self.assertRaises(AttributeError):
+            job.sp.b
 
     def test_interface_destination_conflict(self):
         job_a = self.open_job(dict(a=0))
diff --git a/tests/test_jsondict.py b/tests/test_jsondict.py
index a87a5907..acf31e0d 100644
--- a/tests/test_jsondict.py
+++ b/tests/test_jsondict.py
@@ -124,6 +124,13 @@ class JSONDictTest(BaseJSONDictTest):
         self.assertEqual(len(jsd), 0)
         with self.assertRaises(KeyError):
             jsd[key]
+        jsd[key] = d
+        self.assertEqual(len(jsd), 1)
+        self.assertEqual(jsd[key], d)
+        del jsd.delete
+        self.assertEqual(len(jsd), 0)
+        with self.assertRaises(KeyError):
+            jsd[key]
 
     def test_update(self):
         jsd = self.get_json_dict()