Repository: ResearchObject/ro-crate-py

Issue:
Entity id should not be modifiable
It's used to index the entity in the crate's `__entity_map`, so changing it leads to inconsistencies:

```pycon
>>> from rocrate.rocrate import ROCrate
>>> crate = ROCrate()
>>> d = crate.add_dataset("FOO")
>>> crate._ROCrate__entity_map
{..., 'arcp://uuid,2f145cc1-20be-4cd7-ac86-d6d4a08cdcf9/FOO': <FOO/ Dataset>}
>>> d.id = "foo"
>>> crate._ROCrate__entity_map
{..., 'arcp://uuid,2f145cc1-20be-4cd7-ac86-d6d4a08cdcf9/FOO': <foo Dataset>}
>>> crate.dereference("foo")
>>> crate.dereference("FOO")
<foo Dataset>

```

Discussion:

Patch:
diff --git a/rocrate/model/entity.py b/rocrate/model/entity.py
index 41bf09b..fa26218 100644
--- a/rocrate/model/entity.py
+++ b/rocrate/model/entity.py
@@ -30,9 +30,9 @@ class Entity(MutableMapping):
     def __init__(self, crate, identifier=None, properties=None):
         self.crate = crate
         if identifier:
-            self.id = self.format_id(identifier)
+            self.__id = self.format_id(identifier)
         else:
-            self.id = f"#{uuid.uuid4()}"
+            self.__id = f"#{uuid.uuid4()}"
         if properties:
             empty = self._empty()
             empty.update(properties)
@@ -40,6 +40,10 @@ class Entity(MutableMapping):
         else:
             self._jsonld = self._empty()
 
+    @property
+    def id(self):
+        return self.__id
+
     # Format the given ID with rules appropriate for this type.
     # For example, Dataset (directory) data entities SHOULD end with /
     def format_id(self, identifier):


Test patch:
diff --git a/test/test_model.py b/test/test_model.py
index e7b5343..9f00a48 100644
--- a/test/test_model.py
+++ b/test/test_model.py
@@ -505,3 +505,10 @@ def test_add_no_duplicates(test_data_dir, tmpdir):
     assert ret["name"] == "Jim"
     assert ret in crate.get_entities()
     assert crate.contextual_entities == [jim]
+
+
+def test_immutable_id():
+    crate = ROCrate()
+    p = crate.add(Person(crate, "#foo"))
+    with pytest.raises(AttributeError):
+        p.id = "#bar"