diff --git a/pybis/src/python/pybis/attribute.py b/pybis/src/python/pybis/attribute.py
index fe796bf76cacf2b77eaeadc2cff3c1e47e0f3d20..e034dffe105c5b2b2be1f359426b636a5c982492 100644
--- a/pybis/src/python/pybis/attribute.py
+++ b/pybis/src/python/pybis/attribute.py
@@ -1,15 +1,30 @@
 from pandas import DataFrame, Series
 from tabulate import tabulate
-from .definitions import openbis_definitions, fetch_option, get_method_for_entity, get_type_for_entity
-from .utils import parse_jackson, check_datatype, split_identifier, format_timestamp, is_identifier, is_permid, nvl, extract_person
+from .definitions import (
+    openbis_definitions,
+    fetch_option,
+    get_method_for_entity,
+    get_type_for_entity,
+)
+from .utils import (
+    parse_jackson,
+    check_datatype,
+    split_identifier,
+    format_timestamp,
+    is_identifier,
+    is_permid,
+    nvl,
+    extract_person,
+)
 from .attachment import Attachment
 
 import copy
 import base64
 import os
 
-class AttrHolder():
-    """ General class for both samples and experiments that hold all common attributes, such as:
+
+class AttrHolder:
+    """General class for both samples and experiments that hold all common attributes, such as:
     - space
     - project
     - experiment (sample)
@@ -19,19 +34,19 @@ class AttrHolder():
     """
 
     def __init__(self, openbis_obj, entity, type=None):
-        self.__dict__['_openbis'] = openbis_obj
-        self.__dict__['_entity'] = entity
+        self.__dict__["_openbis"] = openbis_obj
+        self.__dict__["_entity"] = entity
 
         if type is not None:
-            self.__dict__['_type'] = type.data
+            self.__dict__["_type"] = type.data
 
-        self.__dict__['_defs'] = openbis_definitions(entity)
-        #self.__dict__['_allowed_attrs'] = openbis_definitions(entity)['attrs']
-        #self.__dict__['_allowed_attrs_new'] = openbis_definitions(entity)['attrs_new']
-        #self.__dict__['_allowed_attrs_up'] = openbis_definitions(entity)['attrs_up']
-        self.__dict__['_identifier'] = None
-        self.__dict__['_is_new'] = True
-        self.__dict__['_tags'] = []
+        self.__dict__["_defs"] = openbis_definitions(entity)
+        # self.__dict__['_allowed_attrs'] = openbis_definitions(entity)['attrs']
+        # self.__dict__['_allowed_attrs_new'] = openbis_definitions(entity)['attrs_new']
+        # self.__dict__['_allowed_attrs_up'] = openbis_definitions(entity)['attrs_up']
+        self.__dict__["_identifier"] = None
+        self.__dict__["_is_new"] = True
+        self.__dict__["_tags"] = []
 
     def __call__(self, data):
         """This internal method is invoked when an existing object is loaded.
@@ -41,54 +56,54 @@ class AttrHolder():
         Since the data comes from openBIS, we do not have to check it (hence the
         self.__dict__ statements to prevent invoking the __setattr__ method)
         Internally data is stored with an underscore, e.g.
-            sample._space = { 
+            sample._space = {
                 '@type': 'as.dto.space.id.SpacePermId',
-                'permId': 'MATERIALS' 
+                'permId': 'MATERIALS'
             }
         but when fetching the attribute without the underscore, we only return
         the relevant data for the user:
             sample.space   # MATERIALS
         """
         # entity is read from openBIS, so it is not new anymore
-        self.__dict__['_is_new'] = False
+        self.__dict__["_is_new"] = False
 
-        for attr in self._defs['attrs']:
+        for attr in self._defs["attrs"]:
             if attr in ["code", "permId", "identifier", "type"]:
-                self.__dict__['_' + attr] = data.get(attr, None)
+                self.__dict__["_" + attr] = data.get(attr, None)
                 # remove the @id attribute
-                if isinstance(self.__dict__['_' + attr], dict):
-                    self.__dict__['_' + attr].pop('@id', None)
+                if isinstance(self.__dict__["_" + attr], dict):
+                    self.__dict__["_" + attr].pop("@id", None)
 
-            elif attr in ['vocabularyCode']:
-                self.__dict__['_'+attr] = data.get('permId', {}).get(attr, None)
+            elif attr in ["vocabularyCode"]:
+                self.__dict__["_" + attr] = data.get("permId", {}).get(attr, None)
 
-            elif attr in ['validationPlugin']:
+            elif attr in ["validationPlugin"]:
                 d = data.get(attr, None)
                 if d is not None:
-                    d = d['permId']
-                self.__dict__['_' + attr] = d
+                    d = d["permId"]
+                self.__dict__["_" + attr] = d
 
             elif attr in ["space"]:
                 d = data.get(attr, None)
                 if d is not None:
-                    d = d['permId']
-                self.__dict__['_' + attr] = d
+                    d = d["permId"]
+                self.__dict__["_" + attr] = d
 
             elif attr in ["sample", "experiment", "project", "container"]:
                 d = data.get(attr, None)
                 if d is not None:
-                    d = d['identifier']
-                self.__dict__['_' + attr] = d
+                    d = d["identifier"]
+                self.__dict__["_" + attr] = d
 
             elif attr in ["parents", "children", "samples", "components", "containers"]:
-                self.__dict__['_' + attr] = []
+                self.__dict__["_" + attr] = []
                 if data[attr] is not None:
                     for item in data[attr]:
                         try:
-                            if 'identifier' in item:
-                                self.__dict__['_' + attr].append(item['identifier'])
-                            elif 'permId' in item:
-                                self.__dict__['_' + attr].append(item['permId'])
+                            if "identifier" in item:
+                                self.__dict__["_" + attr].append(item["identifier"])
+                            elif "permId" in item:
+                                self.__dict__["_" + attr].append(item["permId"])
                         except Exception:
                             # TODO: under certain circumstances, openBIS only delivers an integer
                             pass
@@ -96,55 +111,53 @@ class AttrHolder():
             elif attr in ["tags"]:
                 self.add_tags(data[attr])
 
-            elif attr.endswith('Date'):
-                self.__dict__['_'+attr] = format_timestamp(data.get(attr))
+            elif attr.endswith("Date"):
+                self.__dict__["_" + attr] = format_timestamp(data.get(attr))
 
-            elif attr in ['registrator', 'modifier', 'dataProducer']:
-                self.__dict__['_'+attr] = extract_person(data.get(attr))
+            elif attr in ["registrator", "modifier", "dataProducer"]:
+                self.__dict__["_" + attr] = extract_person(data.get(attr))
 
             else:
-                self.__dict__['_' + attr] = data.get(attr, None)
-
+                self.__dict__["_" + attr] = data.get(attr, None)
 
     def _new_attrs(self, method_name=None):
         """Returns the Python-equivalent JSON request when a new object is created.
         It is used internally by the save() method of a newly created object.
         """
-        attr2ids = openbis_definitions('attr2ids')
-        new_obj = get_type_for_entity(self.entity, 'create')
+        attr2ids = openbis_definitions("attr2ids")
+        new_obj = get_type_for_entity(self.entity, "create")
 
-        for attr in self._defs['attrs_new']:
+        for attr in self._defs["attrs_new"]:
             items = None
 
-            if attr == 'type':
-                new_obj['typeId'] = self._type['permId']
+            if attr == "type":
+                new_obj["typeId"] = self._type["permId"]
                 continue
 
-            elif attr == 'kind':
+            elif attr == "kind":
                 # when creating a new dataset, the attribute 芦kind禄 is called 芦dataSetKind禄
-                new_obj['dataSetKind'] = self._kind
+                new_obj["dataSetKind"] = self._kind
 
-            elif attr == 'attachments':
-                attachments = getattr(self, '_new_attachments')
+            elif attr == "attachments":
+                attachments = getattr(self, "_new_attachments")
                 if attachments is None:
                     continue
                 atts_data = [attachment.get_data() for attachment in attachments]
                 items = atts_data
 
-            elif attr == 'userIds':
-                if '_changed_users' not in self.__dict__:
+            elif attr == "userIds":
+                if "_changed_users" not in self.__dict__:
                     continue
 
-                new_obj[attr]=[]
-                for userId in self.__dict__['_changed_users']:
-                    if self.__dict__['_changed_users'][userId]['action'] == 'Add':
-                        new_obj[attr].append({
-                            "permId": userId,
-                            "@type": "as.dto.person.id.PersonPermId"
-                        })
+                new_obj[attr] = []
+                for userId in self.__dict__["_changed_users"]:
+                    if self.__dict__["_changed_users"][userId]["action"] == "Add":
+                        new_obj[attr].append(
+                            {"permId": userId, "@type": "as.dto.person.id.PersonPermId"}
+                        )
 
             else:
-                items = getattr(self, '_' + attr)
+                items = getattr(self, "_" + attr)
 
                 key = None
                 if attr in attr2ids:
@@ -155,27 +168,20 @@ class AttrHolder():
 
                 new_obj[key] = items
 
-        # if method_name is not defined: guess the method name for creating a new entity 
+        # if method_name is not defined: guess the method name for creating a new entity
         if method_name is None:
-            method_name = get_method_for_entity(self.entity, 'create')
-        request = {
-            "method": method_name,
-            "params": [
-                self.openbis.token,
-                [new_obj]
-            ]
-        }
+            method_name = get_method_for_entity(self.entity, "create")
+        request = {"method": method_name, "params": [self.openbis.token, [new_obj]]}
         return request
 
-
     def _up_attrs(self, method_name=None, permId=None):
         """Returns the Python-equivalent JSON request when a new object is updated.
         It is used internally by the save() method of an object to be updated.
         """
-        #defs = openbis_definitions(self._entity)
-        attr2ids = openbis_definitions('attr2ids')
+        # defs = openbis_definitions(self._entity)
+        attr2ids = openbis_definitions("attr2ids")
 
-        up_obj = get_type_for_entity(self.entity, 'update')
+        up_obj = get_type_for_entity(self.entity, "update")
 
         # for some weird reasons, the permId is called differently
         # for every openBIS entity, but only when updating...
@@ -187,79 +193,105 @@ class AttrHolder():
 
         # look at all attributes available for that entity
         # that can be updated
-        for attr in self._defs['attrs_up']:
+        for attr in self._defs["attrs_up"]:
             items = None
 
-            if attr == 'attachments':
+            if attr == "attachments":
                 # v3 API currently only supports adding attachments
-                attachments = self.__dict__.get('_new_attachments', None)
+                attachments = self.__dict__.get("_new_attachments", None)
                 if attachments is None:
                     continue
                 atts_data = [attachment.get_data() for attachment in attachments]
 
-                up_obj['attachments'] = {
-                    "actions": [{
-                        "items": atts_data,
-                        "@type": "as.dto.common.update.ListUpdateActionAdd"
-                    }],
-                    "@type": "as.dto.attachment.update.AttachmentListUpdateValue"
+                up_obj["attachments"] = {
+                    "actions": [
+                        {
+                            "items": atts_data,
+                            "@type": "as.dto.common.update.ListUpdateActionAdd",
+                        }
+                    ],
+                    "@type": "as.dto.attachment.update.AttachmentListUpdateValue",
                 }
 
-            elif attr == 'tags':
+            elif attr == "tags":
                 items = []
-                for tag in self.__dict__['_tags']:
-                    items.append({
-                        "permId": tag['permId'],
-                        "@type" : "as.dto.tag.id.TagPermId"
-                    })
+                for tag in self.__dict__["_tags"]:
+                    items.append(
+                        {"permId": tag["permId"], "@type": "as.dto.tag.id.TagPermId"}
+                    )
 
-                up_obj['tagIds'] = {
+                up_obj["tagIds"] = {
                     "actions": [
                         {
                             "items": items,
                             "@type": "as.dto.common.update.ListUpdateActionSet",
                         }
                     ],
-                    "@type": "as.dto.common.update.IdListUpdateValue"
+                    "@type": "as.dto.common.update.IdListUpdateValue",
                 }
-
-            elif attr == 'userIds':
+            elif attr in "metaData".split():
+                # ListUpdateMapValues
+                metaData = self.__dict__["_" + attr]
+                if metaData:
+                    items = [metaData]
+                    data_type = "as.dto.common.update.ListUpdateActionSet"
+                elif metaData is not None and len(metaData) == 0:
+                    # metaData needs to be set to {} in order to remove it.
+                    items = ["custom_widget"]
+                    data_type = "as.dto.common.update.ListUpdateActionRemove"
+                up_obj[attr] = {
+                    "actions": [
+                        {
+                            "items": items,
+                            "@type": data_type
+                            # "@type": "as.dto.common.update.ListUpdateActionSet",
+                            # "@type": "as.dto.common.update.ListUpdateActionRemove",
+                        }
+                    ],
+                    "@type": "as.dto.common.update.ListUpdateMapValues",
+                }
+            elif attr == "userIds":
                 actions = []
-                if '_changed_users' not in self.__dict__:
+                if "_changed_users" not in self.__dict__:
                     continue
-                for userId in self.__dict__['_changed_users']:
-                    actions.append({
-		        "items": [
-                            {
-                                "permId": userId,
-                                "@type": "as.dto.person.id.PersonPermId"
-			    }
-                        ],
-		        "@type": "as.dto.common.update.ListUpdateAction{}".format(
-                            self.__dict__['_changed_users'][userId]['action']
-                        )
-		    })
+                for userId in self.__dict__["_changed_users"]:
+                    actions.append(
+                        {
+                            "items": [
+                                {
+                                    "permId": userId,
+                                    "@type": "as.dto.person.id.PersonPermId",
+                                }
+                            ],
+                            "@type": "as.dto.common.update.ListUpdateAction{}".format(
+                                self.__dict__["_changed_users"][userId]["action"]
+                            ),
+                        }
+                    )
 
-                up_obj['userIds'] = {
+                up_obj["userIds"] = {
                     "actions": actions,
-                    "@type": "as.dto.common.update.IdListUpdateValue" 
+                    "@type": "as.dto.common.update.IdListUpdateValue",
                 }
 
-            elif attr in 'description label official ordinal autoGeneratedCode subcodeUnique listable showContainer showParents showParentMetadata disallowDeletion validationPlugin'.split():
+            elif (
+                attr
+                in "description label official ordinal autoGeneratedCode subcodeUnique listable showContainer showParents showParentMetadata disallowDeletion validationPlugin".split()
+            ):
                 # alway update common fields
                 key = attr2ids.get(attr, attr)
                 up_obj[key] = {
-                    "value": self.__dict__['_'+attr],
+                    "value": self.__dict__["_" + attr],
                     "isModified": True,
-                    "@type": "as.dto.common.update.FieldUpdateValue"
+                    "@type": "as.dto.common.update.FieldUpdateValue",
                 }
 
-            elif '_' + attr in self.__dict__:
+            elif "_" + attr in self.__dict__:
                 # handle multivalue attributes (parents, children, tags etc.)
-                # we only cover the Set mechanism, which means we always update 
+                # we only cover the Set mechanism, which means we always update
                 # all items in a list
-                if 'multi' in self._defs and attr in self._defs['multi']:
-                    items = self.__dict__.get('_' + attr, [])
+                if "multi" in self._defs and attr in self._defs["multi"]:
+                    items = self.__dict__.get("_" + attr, [])
                     if items == None:
                         items = []
                     up_obj[attr2ids[attr]] = {
@@ -269,11 +301,11 @@ class AttrHolder():
                                 "@type": "as.dto.common.update.ListUpdateActionSet",
                             }
                         ],
-                        "@type": "as.dto.common.update.IdListUpdateValue"
+                        "@type": "as.dto.common.update.IdListUpdateValue",
                     }
                 else:
                     # handle single attributes (space, experiment, project, container, etc.)
-                    value = self.__dict__.get('_' + attr, {})
+                    value = self.__dict__.get("_" + attr, {})
                     if value is None:
                         pass
                     elif isinstance(value, bool):
@@ -286,33 +318,26 @@ class AttrHolder():
                             "@type": "as.dto.common.update.FieldUpdateValue",
                             "isModified": True,
                         }
-                    elif 'isModified' in value and value['isModified'] == True:
+                    elif "isModified" in value and value["isModified"] == True:
                         val = {}
-                        for x in ['identifier','permId','@type']:
+                        for x in ["identifier", "permId", "@type"]:
                             if x in value:
                                 val[x] = value[x]
 
                         up_obj[attr2ids[attr]] = {
                             "@type": "as.dto.common.update.FieldUpdateValue",
                             "isModified": True,
-                            "value": val
+                            "value": val,
                         }
 
         # update an existing entity
         if method_name is None:
-            method_name = get_method_for_entity(self.entity, 'update')
-        request = {
-            "method": method_name,
-            "params": [
-                self.openbis.token,
-                [up_obj]
-            ]
-        }
+            method_name = get_method_for_entity(self.entity, "update")
+        request = {"method": method_name, "params": [self.openbis.token, [up_obj]]}
         return request
 
-
     def __getattr__(self, name):
-        """ handles all attribute requests dynamically.
+        """handles all attribute requests dynamically.
         Values are returned in a sensible way, for example:
             the identifiers of parents, children and components are returned as an
             array of values, whereas attachments, users (of groups) and
@@ -320,67 +345,74 @@ class AttrHolder():
         """
 
         name_map = {
-            'group'      : 'authorizationGroup',
-            'roles'      : 'roleAssignments',
-            'permid'     : 'permId',
-            'collection' : 'experiment',
-            'object'     : 'sample'
+            "group": "authorizationGroup",
+            "roles": "roleAssignments",
+            "permid": "permId",
+            "collection": "experiment",
+            "object": "sample",
         }
         if name in name_map:
             name = name_map[name]
 
-        int_name = '_' + name
+        int_name = "_" + name
         if int_name in self.__dict__:
-            if int_name == '_attachments':
+            if int_name == "_attachments":
                 attachments = []
                 for att in self._attachments:
-                    attachments.append({
-                        "fileName":    att.get('fileName'),
-                        "title":       att.get('title'),
-                        "description": att.get('description'),
-                        "version":     att.get('version'),
-                    })
+                    attachments.append(
+                        {
+                            "fileName": att.get("fileName"),
+                            "title": att.get("title"),
+                            "description": att.get("description"),
+                            "version": att.get("version"),
+                        }
+                    )
                 return attachments
 
-            elif int_name == '_users':
+            elif int_name == "_users":
                 users = []
                 for user in self._users:
-                    users.append({
-                        "firstName": user.get('firstName'),
-                        "lastName" : user.get('lastName'),
-                        "email"    : user.get('email'),
-                        "userId"   : user.get('userId'),
-                        "space"    : user.get('space').get('code') if user.get('space') is not None else None,
-                    })
+                    users.append(
+                        {
+                            "firstName": user.get("firstName"),
+                            "lastName": user.get("lastName"),
+                            "email": user.get("email"),
+                            "userId": user.get("userId"),
+                            "space": user.get("space").get("code")
+                            if user.get("space") is not None
+                            else None,
+                        }
+                    )
                 return users
 
-            elif int_name == '_roleAssignments':
+            elif int_name == "_roleAssignments":
                 ras = []
                 for ra in self._roleAssignments:
-                    ras.append({
-                        "techId":        ra.get('id').get('techId'),
-                        "role":      ra.get('role'),
-                        "roleLevel": ra.get('roleLevel'),
-                        "space":     ra.get('space').get('code'),
-                        "project":   ra.get('role'),
-                    })
+                    ras.append(
+                        {
+                            "techId": ra.get("id").get("techId"),
+                            "role": ra.get("role"),
+                            "roleLevel": ra.get("roleLevel"),
+                            "space": ra.get("space").get("code"),
+                            "project": ra.get("role"),
+                        }
+                    )
                 return ras
 
-
-            # if the attribute contains a list, 
+            # if the attribute contains a list,
             # return a list of either identifiers, codes or
             # permIds (whatever is available first)
             elif isinstance(self.__dict__[int_name], list):
                 values = []
                 for item in self.__dict__[int_name]:
                     if "identifier" in item:
-                        values.append(item['identifier'])
+                        values.append(item["identifier"])
                     elif "code" in item:
-                        values.append(item['code'])
+                        values.append(item["code"])
                     elif "userId" in item:
-                        values.append(item['userId'])
+                        values.append(item["userId"])
                     elif "permId" in item:
-                        values.append(item['permId'])
+                        values.append(item["permId"])
                     else:
                         values.append(item)
                 return values
@@ -388,17 +420,17 @@ class AttrHolder():
             # attribute contains a dictionary: same procedure as above.
             elif isinstance(self.__dict__[int_name], dict):
                 if "identifier" in self.__dict__[int_name]:
-                    return self.__dict__[int_name]['identifier']
+                    return self.__dict__[int_name]["identifier"]
                 elif "code" in self.__dict__[int_name]:
-                    return self.__dict__[int_name]['code']
+                    return self.__dict__[int_name]["code"]
                 elif "name" in self.__dict__[int_name]:
-                    return self.__dict__[int_name]['name']
+                    return self.__dict__[int_name]["name"]
                 elif "userId" in self.__dict__[int_name]:
-                    return self.__dict__[int_name]['userId']
+                    return self.__dict__[int_name]["userId"]
                 elif "permId" in self.__dict__[int_name]:
-                    return self.__dict__[int_name]['permId']
+                    return self.__dict__[int_name]["permId"]
                 elif "id" in self.__dict__[int_name]:
-                    return self.__dict__[int_name]['id']
+                    return self.__dict__[int_name]["id"]
 
             else:
                 return self.__dict__[int_name]
@@ -414,25 +446,27 @@ class AttrHolder():
 
         # experiment aka collection, sample aka object
         name_map = {
-            'collection' : 'experiment',
-            'object'     : 'sample',
-            'parent'     : 'parents',
-            'child'      : 'children',
+            "collection": "experiment",
+            "object": "sample",
+            "parent": "parents",
+            "child": "children",
         }
         if name in name_map:
             name = name_map[name]
 
         if self._is_new:
-            if name not in self._defs['attrs_new']:
+            if name not in self._defs["attrs_new"]:
                 raise ValueError(
-                    "No such attribute: 芦{}禄 for entity: {}. Allowed attributes are: {}"
-                    .format(name, self.entity, self._defs['attrs_new'])
+                    "No such attribute: 芦{}禄 for entity: {}. Allowed attributes are: {}".format(
+                        name, self.entity, self._defs["attrs_new"]
+                    )
                 )
         else:
-            if name not in self._defs['attrs_up']:
+            if name not in self._defs["attrs_up"]:
                 raise ValueError(
-                    "No such attribute: 芦{}禄 for entity: {}. Allowed attributes are: {}"
-                    .format(name, self.entity, self._defs['attrs_up'])
+                    "No such attribute: 芦{}禄 for entity: {}. Allowed attributes are: {}".format(
+                        name, self.entity, self._defs["attrs_up"]
+                    )
                 )
 
         if name in ["parents", "children", "components"]:
@@ -443,27 +477,27 @@ class AttrHolder():
             for val in value:
                 if isinstance(val, str):
                     # fetch objects in openBIS, make sure they actually exists
-                    obj = getattr(self._openbis, 'get_' + self._entity.lower())(val)
+                    obj = getattr(self._openbis, "get_" + self._entity.lower())(val)
                     objs.append(obj)
-                elif getattr(val, '_permId'):
+                elif getattr(val, "_permId"):
                     # we got an existing object
                     objs.append(val)
 
             permids = []
             for item in objs:
-                if getattr(item, '_identifier') is not None:
+                if getattr(item, "_identifier") is not None:
                     id = item._identifier
-                elif getattr(item, '_permId') is not None:
+                elif getattr(item, "_permId") is not None:
                     id = item._permId
                 else:
                     return
-                    
+
                 # remove any existing @id keys to prevent jackson parser errors
-                id.pop('@id', None)
-                    
+                id.pop("@id", None)
+
                 permids.append(id)
 
-            self.__dict__['_' + name] = permids
+            self.__dict__["_" + name] = permids
         elif name in ["tags"]:
             self.set_tags(value)
 
@@ -471,31 +505,31 @@ class AttrHolder():
             self.set_users(value)
 
         elif name in ["vocabulary"]:
-            if value is None or value == '':
-                self.__dict__['_vocabulary'] = None
+            if value is None or value == "":
+                self.__dict__["_vocabulary"] = None
             else:
-                self.__dict__['_vocabulary'] = {
+                self.__dict__["_vocabulary"] = {
                     "@type": "as.dto.vocabulary.id.VocabularyPermId",
-                    "permId": value.upper()
+                    "permId": value.upper(),
                 }
 
         elif name in ["validationPlugin"]:
-            if value is None or value == '':
-                self.__dict__['_validationPlugin'] = None
+            if value is None or value == "":
+                self.__dict__["_validationPlugin"] = None
             else:
-                self.__dict__['_validationPlugin'] = {
+                self.__dict__["_validationPlugin"] = {
                     "@type": "as.dto.plugin.id.PluginPermId",
-                    "permId": value
+                    "permId": value,
                 }
 
         elif name in ["materialType"]:
-            if value is None or value == '':
-                self.__dict__['_materialType'] = None
+            if value is None or value == "":
+                self.__dict__["_materialType"] = None
             else:
-                self.__dict__['_materialType'] = {
+                self.__dict__["_materialType"] = {
                     "@type": "as.dto.entitytype.id.EntityTypePermId",
                     "permId": value.upper(),
-                    "entityKind": "MATERIAL"
+                    "entityKind": "MATERIAL",
                 }
 
         elif name in ["attachments"]:
@@ -512,7 +546,7 @@ class AttrHolder():
         elif name in ["space"]:
             obj = None
             if value is None:
-                self.__dict__['_'+name] = None
+                self.__dict__["_" + name] = None
                 return
 
             if isinstance(value, str):
@@ -521,13 +555,13 @@ class AttrHolder():
             else:
                 obj = value
 
-            self.__dict__['_' + name] = obj.data['permId']
+            self.__dict__["_" + name] = obj.data["permId"]
 
             # mark attribute as modified, if it's an existing entity
             if self.is_new:
                 pass
             else:
-                self.__dict__['_' + name]['isModified'] = True
+                self.__dict__["_" + name]["isModified"] = True
 
         elif name in ["sample", "experiment", "project"]:
             obj = None
@@ -535,35 +569,39 @@ class AttrHolder():
                 # fetch object in openBIS, make sure it actually exists
                 obj = getattr(self._openbis, "get_" + name)(value)
             elif value is None:
-                self.__dict__['_'+name] = {}
+                self.__dict__["_" + name] = {}
                 return
             else:
                 obj = value
 
-            self.__dict__['_' + name] = obj.data['identifier']
+            self.__dict__["_" + name] = obj.data["identifier"]
 
             # mark attribute as modified, if it's an existing entity
             if self.is_new:
                 pass
             else:
-                self.__dict__['_' + name]['isModified'] = True
+                self.__dict__["_" + name]["isModified"] = True
 
         elif name in ["identifier"]:
             raise KeyError("you can not modify the {}".format(name))
         elif name == "code":
             try:
-                if self._type['autoGeneratedCode']:
-                    raise KeyError("This {}Type has auto-generated code. You cannot set a code".format(self.entity))
+                if self._type["autoGeneratedCode"]:
+                    raise KeyError(
+                        "This {}Type has auto-generated code. You cannot set a code".format(
+                            self.entity
+                        )
+                    )
             except KeyError:
                 pass
             except TypeError:
                 pass
 
-            self.__dict__['_code'] = value
+            self.__dict__["_code"] = value
 
         elif name in ["userId", "description"]:
             # values that are directly assigned
-            self.__dict__['_' + name] = value
+            self.__dict__["_" + name] = value
 
         elif name in ["userIds"]:
             self.add_users(value)
@@ -572,14 +610,12 @@ class AttrHolder():
             # enum: check whether value is a valid constant
             if value and value not in self._defs[name]:
                 raise ValueError(
-                    "Allowed values for enum {} are: {}"
-                    .format(name, self._defs[name])
+                    "Allowed values for enum {} are: {}".format(name, self._defs[name])
                 )
             else:
-                self.__dict__['_'+name] = value
+                self.__dict__["_" + name] = value
         else:
-            self.__dict__['_'+name] = value
-
+            self.__dict__["_" + name] = value
 
     def get_type(self):
         return self._type
@@ -587,200 +623,229 @@ class AttrHolder():
     def _ident_for_whatever(self, whatever):
         if isinstance(whatever, str):
             # fetch parent in openBIS, we are given an identifier
-            obj = getattr(self._openbis, 'get_'+self._entity.lower())(whatever)
+            obj = getattr(self._openbis, "get_" + self._entity.lower())(whatever)
         else:
             # we assume we got an object
             obj = whatever
 
         ident = None
-        if getattr(obj, '_identifier'):
+        if getattr(obj, "_identifier"):
             ident = obj._identifier
-        elif getattr(obj, '_permId'):
+        elif getattr(obj, "_permId"):
             ident = obj._permId
 
-        ident.pop('@id', None)
+        ident.pop("@id", None)
         return ident
 
     def get_container(self, **kwargs):
-        return getattr(self._openbis, 'get_'+self._entity.lower())( self.container, **kwargs )
+        return getattr(self._openbis, "get_" + self._entity.lower())(
+            self.container, **kwargs
+        )
 
     def get_containers(self, **kwargs):
-        '''get the containers and return them as a list (Things/DataFrame)
+        """get the containers and return them as a list (Things/DataFrame)
         or return empty list
-        '''
-        return getattr(self._openbis, 'get_'+self._entity.lower())( self.containers, **kwargs )
+        """
+        return getattr(self._openbis, "get_" + self._entity.lower())(
+            self.containers, **kwargs
+        )
 
     def set_containers(self, containers_to_set):
-        '''set the new _containers list
-        '''
-        self.__dict__['_containers'] = []
+        """set the new _containers list"""
+        self.__dict__["_containers"] = []
         self.add_containers(containers_to_set)
 
     def add_containers(self, containers_to_add):
-        '''add component to _containers list
-        '''
+        """add component to _containers list"""
         if not isinstance(containers_to_add, list):
             containers_to_add = [containers_to_add]
         for component in containers_to_add:
             ident = self._ident_for_whatever(component)
-            if ident not in self.__dict__['_containers']:
-                self.__dict__['_containers'].append(ident)
+            if ident not in self.__dict__["_containers"]:
+                self.__dict__["_containers"].append(ident)
 
     def del_containers(self, containers_to_remove):
-        '''remove component from _containers list
-        '''
+        """remove component from _containers list"""
         if not isinstance(containers_to_remove, list):
             containers_to_remove = [containers_to_remove]
         for component in containers_to_remove:
             ident = self._ident_for_whatever(component)
-            for i, item in enumerate(self.__dict__['_containers']):
-                if 'identifier' in ident and 'identifier' in item and ident['identifier'] == item['identifier']:
-                    self.__dict__['_containers'].pop(i, None)
-                elif 'permId' in ident and 'permId' in item and ident['permId'] == item['permId']:
-                    self.__dict__['_containers'].pop(i, None)
+            for i, item in enumerate(self.__dict__["_containers"]):
+                if (
+                    "identifier" in ident
+                    and "identifier" in item
+                    and ident["identifier"] == item["identifier"]
+                ):
+                    self.__dict__["_containers"].pop(i, None)
+                elif (
+                    "permId" in ident
+                    and "permId" in item
+                    and ident["permId"] == item["permId"]
+                ):
+                    self.__dict__["_containers"].pop(i, None)
 
     def get_components(self, **kwargs):
-        '''Samples and DataSets may contain other DataSets and Samples. This function returns the
+        """Samples and DataSets may contain other DataSets and Samples. This function returns the
         contained Samples/DataSets (a.k.a. components) as a list (Things/DataFrame)
-        '''
-        return getattr(self._openbis, 'get_'+self._entity.lower())( self.components, **kwargs )
+        """
+        return getattr(self._openbis, "get_" + self._entity.lower())(
+            self.components, **kwargs
+        )
 
     get_contained = get_components  # Alias
 
     def set_components(self, components_to_set):
-        '''Samples and DataSets may contain other DataSets and Samples. This function sets the
+        """Samples and DataSets may contain other DataSets and Samples. This function sets the
         contained Samples/DataSets (a.k.a. components)
-        '''
-        self.__dict__['_components'] = []
+        """
+        self.__dict__["_components"] = []
         self.add_components(components_to_set)
 
     set_contained = set_components  # Alias
 
     def add_components(self, components_to_add):
-        '''Samples and DataSets may contain other DataSets and Samples. This function adds
+        """Samples and DataSets may contain other DataSets and Samples. This function adds
         additional Samples/DataSets to the current object.
-        '''
+        """
         if not isinstance(components_to_add, list):
             components_to_add = [components_to_add]
         for component in components_to_add:
             ident = self._ident_for_whatever(component)
-            if ident not in self.__dict__['_components']:
-                self.__dict__['_components'].append(ident)
+            if ident not in self.__dict__["_components"]:
+                self.__dict__["_components"].append(ident)
 
     add_contained = add_components  # Alias
 
     def del_components(self, components_to_remove):
-        '''Samples and DataSets may contain other DataSets and Samples. This function removes
+        """Samples and DataSets may contain other DataSets and Samples. This function removes
         additional Samples/DataSets from the current object.
-        '''
+        """
         if not isinstance(components_to_remove, list):
             components_to_remove = [components_to_remove]
         for component in components_to_remove:
             ident = self._ident_for_whatever(component)
-            for i, item in enumerate(self.__dict__['_components']):
-                if 'identifier' in ident and 'identifier' in item and ident['identifier'] == item['identifier']:
-                    self.__dict__['_components'].pop(i, None)
-                elif 'permId' in ident and 'permId' in item and ident['permId'] == item['permId']:
-                    self.__dict__['_components'].pop(i, None)
+            for i, item in enumerate(self.__dict__["_components"]):
+                if (
+                    "identifier" in ident
+                    and "identifier" in item
+                    and ident["identifier"] == item["identifier"]
+                ):
+                    self.__dict__["_components"].pop(i, None)
+                elif (
+                    "permId" in ident
+                    and "permId" in item
+                    and ident["permId"] == item["permId"]
+                ):
+                    self.__dict__["_components"].pop(i, None)
 
     del_contained = del_components  # Alias
 
     def get_parents(self, **kwargs):
-        '''get the current parents and return them as a list (Things/DataFrame)
+        """get the current parents and return them as a list (Things/DataFrame)
         or return empty list
-        '''
-        return getattr(self._openbis, 'get_'+self._entity.lower())( self.parents, **kwargs )
+        """
+        return getattr(self._openbis, "get_" + self._entity.lower())(
+            self.parents, **kwargs
+        )
 
     def set_parents(self, parents_to_set):
-        '''set the new _parents list
-        '''
-        self.__dict__['_parents'] = []
+        """set the new _parents list"""
+        self.__dict__["_parents"] = []
         self.add_parents(parents_to_set)
 
     def add_parents(self, parents_to_add):
-        '''add parent to _parents list
-        '''
+        """add parent to _parents list"""
         if not isinstance(parents_to_add, list):
             parents_to_add = [parents_to_add]
         for parent in parents_to_add:
             ident = self._ident_for_whatever(parent)
-            if ident not in self.__dict__['_parents']:
-                self.__dict__['_parents'].append(ident)
+            if ident not in self.__dict__["_parents"]:
+                self.__dict__["_parents"].append(ident)
 
     def del_parents(self, parents_to_remove):
-        '''remove parent from _parents list
-        '''
+        """remove parent from _parents list"""
         if not isinstance(parents_to_remove, list):
             parents_to_remove = [parents_to_remove]
         for parent in parents_to_remove:
             ident = self._ident_for_whatever(parent)
-            for i, item in enumerate(self.__dict__['_parents']):
-                if 'identifier' in ident and 'identifier' in item and ident['identifier'] == item['identifier']:
-                    self.__dict__['_parents'].pop(i)
-                elif 'permId' in ident and 'permId' in item and ident['permId'] == item['permId']:
-                    self.__dict__['_parents'].pop(i)
-
+            for i, item in enumerate(self.__dict__["_parents"]):
+                if (
+                    "identifier" in ident
+                    and "identifier" in item
+                    and ident["identifier"] == item["identifier"]
+                ):
+                    self.__dict__["_parents"].pop(i)
+                elif (
+                    "permId" in ident
+                    and "permId" in item
+                    and ident["permId"] == item["permId"]
+                ):
+                    self.__dict__["_parents"].pop(i)
 
     def get_children(self, **kwargs):
-        '''get the current children and return them as a list (Things/DataFrame)
+        """get the current children and return them as a list (Things/DataFrame)
         or return empty list
-        '''
-        return getattr(self._openbis, 'get_'+self._entity.lower())( self.children, **kwargs )
+        """
+        return getattr(self._openbis, "get_" + self._entity.lower())(
+            self.children, **kwargs
+        )
 
     def set_children(self, children_to_set):
-        '''set the new _children list
-        '''
-        self.__dict__['_children'] = []
+        """set the new _children list"""
+        self.__dict__["_children"] = []
         self.add_children(children_to_set)
 
     def add_children(self, children):
-        '''add children to _children list
-        '''
-        if getattr(self, '_children') is None:
-            self.__dict__['_children'] = []
+        """add children to _children list"""
+        if getattr(self, "_children") is None:
+            self.__dict__["_children"] = []
         if not isinstance(children, list):
             children = [children]
         for child in children:
-            self.__dict__['_children'].append(self._ident_for_whatever(child))
+            self.__dict__["_children"].append(self._ident_for_whatever(child))
 
     def del_children(self, children):
-        '''remove children from _children list
-        '''
-        if getattr(self, '_children') is None:
+        """remove children from _children list"""
+        if getattr(self, "_children") is None:
             return
         if not isinstance(children, list):
             children = [children]
         for child in children:
             ident = self._ident_for_whatever(child)
-            for i, item in enumerate(self.__dict__['_children']):
-                if 'identifier' in ident and 'identifier' in item and ident['identifier'] == item['identifier']:
-                    self.__dict__['_children'].pop(i, None)
-                elif 'permId' in ident and 'permId' in item and ident['permId'] == item['permId']:
-                    self.__dict__['_children'].pop(i, None)
+            for i, item in enumerate(self.__dict__["_children"]):
+                if (
+                    "identifier" in ident
+                    and "identifier" in item
+                    and ident["identifier"] == item["identifier"]
+                ):
+                    self.__dict__["_children"].pop(i, None)
+                elif (
+                    "permId" in ident
+                    and "permId" in item
+                    and ident["permId"] == item["permId"]
+                ):
+                    self.__dict__["_children"].pop(i, None)
 
     @property
     def tags(self):
-        if getattr(self, '_tags') is not None:
-            return [x['code'] for x in self._tags]
+        if getattr(self, "_tags") is not None:
+            return [x["code"] for x in self._tags]
 
     def get_tags(self):
-        if getattr(self, '_tags') is not None:
-            return self._openbis.get_tag([x['permId'] for x in self._tags])
+        if getattr(self, "_tags") is not None:
+            return self._openbis.get_tag([x["permId"] for x in self._tags])
 
     def set_tags(self, tags):
-        '''set _tags list
-        '''
+        """set _tags list"""
 
-        self.__dict__['_tags'] = []
+        self.__dict__["_tags"] = []
         self.add_tags(tags)
 
-
     def add_tags(self, tags):
-        '''add tags to _tags list
-        '''
+        """add tags to _tags list"""
         if not isinstance(tags, list):
             tags = [tags]
-        
+
         for tag in tags:
             if isinstance(tag, str):
                 tag_obj = self._openbis.get_tag(tag)
@@ -790,85 +855,81 @@ class AttrHolder():
                 }
             else:
                 tag_dict = {
-                    "code": tag['code'],
-                    "permId": tag['permId']['permId'],
+                    "code": tag["code"],
+                    "permId": tag["permId"]["permId"],
                 }
 
-            if tag_dict not in self.__dict__['_tags']:
-                self.__dict__['_tags'].append(tag_dict)
+            if tag_dict not in self.__dict__["_tags"]:
+                self.__dict__["_tags"].append(tag_dict)
 
     def del_tags(self, tags):
-        '''remove tags from _tags list
-        '''
+        """remove tags from _tags list"""
         if not isinstance(tags, list):
             tags = [tags]
 
         for tag in tags:
-            for i, tag_dict in enumerate(self.__dict__['_tags']):
-                if tag in tag_dict[i]['code'] or \
-                   tag in tag_dict[i]['permId']:
+            for i, tag_dict in enumerate(self.__dict__["_tags"]):
+                if tag in tag_dict[i]["code"] or tag in tag_dict[i]["permId"]:
                     tag_dict.pop(i, None)
 
     def set_users(self, userIds):
         if userIds is None:
             return
-        if getattr(self, '_userIds') is None:
-            self.__dict__['_userIds'] = []
+        if getattr(self, "_userIds") is None:
+            self.__dict__["_userIds"] = []
         if not isinstance(userIds, list):
             userIds = [userIds]
         for userId in userIds:
-            self.__dict__['_userIds'].append({
-                "permId": userId,
-                "@type": "as.dto.person.id.PersonPermId"
-            })
-        
+            self.__dict__["_userIds"].append(
+                {"permId": userId, "@type": "as.dto.person.id.PersonPermId"}
+            )
+
     def add_users(self, userIds):
         if userIds is None:
             return
-        if getattr(self, '_changed_users') is None:
-            self.__dict__['_changed_users'] = {}
+        if getattr(self, "_changed_users") is None:
+            self.__dict__["_changed_users"] = {}
 
         if not isinstance(userIds, list):
             userIds = [userIds]
         for userId in userIds:
-            self.__dict__['_changed_users'][userId] = {
-                "action": "Add"
-            }
-    add_members = add_users # Alias
+            self.__dict__["_changed_users"][userId] = {"action": "Add"}
 
+    add_members = add_users  # Alias
 
     def del_users(self, userIds):
         if userIds is None:
             return
-        if getattr(self, '_changed_users') is None:
-            self.__dict__['_changed_users'] = {}
+        if getattr(self, "_changed_users") is None:
+            self.__dict__["_changed_users"] = {}
 
         if not isinstance(userIds, list):
             userIds = [userIds]
         for userId in userIds:
-            self.__dict__['_changed_users'][userId] = {
-                "action": "Remove"
-            }
+            self.__dict__["_changed_users"][userId] = {"action": "Remove"}
+
     del_members = del_users  # Alias
 
     def get_attachments(self):
-        if getattr(self, '_attachments') is None:
+        if getattr(self, "_attachments") is None:
             return None
         else:
-            return DataFrame(self._attachments)[['fileName', 'title', 'description', 'version']]
+            return DataFrame(self._attachments)[
+                ["fileName", "title", "description", "version"]
+            ]
 
     def add_attachment(self, fileName, title=None, description=None):
         att = Attachment(filename=fileName, title=title, description=description)
-        if getattr(self, '_attachments') is None:
-            self.__dict__['_attachments'] = []
+        if getattr(self, "_attachments") is None:
+            self.__dict__["_attachments"] = []
         self._attachments.append(att.get_data_short())
 
-        if getattr(self, '_new_attachments') is None:
-            self.__dict__['_new_attachments'] = []
+        if getattr(self, "_new_attachments") is None:
+            self.__dict__["_new_attachments"] = []
         self._new_attachments.append(att)
 
     def download_attachments(self):
-        method = 'get' + self.entity + 's'
+        method = "get" + self.entity + "s"
         entity = self.entity.lower()
         request = {
             "method": method,
@@ -876,40 +937,36 @@ class AttrHolder():
                 self._openbis.token,
                 [self._permId],
                 dict(
-                    attachments=fetch_option['attachmentsWithContent'],
+                    attachments=fetch_option["attachmentsWithContent"],
                     **fetch_option[entity]
-                )
-            ]
+                ),
+            ],
         }
         resp = self._openbis._post_request(self._openbis.as_v3, request)
-        attachments = resp[self.permId]['attachments']
+        attachments = resp[self.permId]["attachments"]
         file_list = []
         for attachment in attachments:
             filename = os.path.join(
-                self._openbis.hostname,
-                self.permId,
-                attachment['fileName']
+                self._openbis.hostname, self.permId, attachment["fileName"]
             )
             os.makedirs(os.path.dirname(filename), exist_ok=True)
-            with open(filename, 'wb') as att:
-                content = base64.b64decode(attachment['content'])
+            with open(filename, "wb") as att:
+                content = base64.b64decode(attachment["content"])
                 att.write(content)
             file_list.append(filename)
         return file_list
 
-
     def all(self):
-        """Return all attributes of an entity in a dict
-        """
+        """Return all attributes of an entity in a dict"""
         attrs = {}
-        for attr in self._defs['attrs']:
-            if attr == 'attachments':
+        for attr in self._defs["attrs"]:
+            if attr == "attachments":
                 continue
             attrs[attr] = getattr(self, attr)
         return attrs
 
     def _repr_html_(self):
-        def nvl(val, string=''):
+        def nvl(val, string=""):
             if val is None:
                 return string
             return val
@@ -925,16 +982,16 @@ class AttrHolder():
             <tbody>
         """
 
-        attrs = self._defs['attrs_new'] if self.is_new else self._defs['attrs']
+        attrs = self._defs["attrs_new"] if self.is_new else self._defs["attrs"]
         for attr in attrs:
-            if attr == 'attachments':
+            if attr == "attachments":
                 continue
             html += "<tr> <td>{}</td> <td>{}</td> </tr>".format(
-                attr, nvl(getattr(self, attr, ''), '')
+                attr, nvl(getattr(self, attr, ""), "")
             )
-        if getattr(self, '_attachments') is not None:
+        if getattr(self, "_attachments") is not None:
             html += "<tr><td>attachments</td><td>"
-            html += "<br/>".join(att['fileName'] for att in self._attachments)
+            html += "<br/>".join(att["fileName"] for att in self._attachments)
             html += "</td></tr>"
 
         html += """
@@ -944,40 +1001,32 @@ class AttrHolder():
         return html
 
     def __repr__(self):
-        """ When using IPython, this method displays a nice table
+        """When using IPython, this method displays a nice table
         of all attributes and their values when the object is printed.
         """
 
-        headers = ['attribute', 'value']
+        headers = ["attribute", "value"]
         lines = []
-        attrs = self._defs['attrs_new'] if self.is_new else self._defs['attrs']
+        attrs = self._defs["attrs_new"] if self.is_new else self._defs["attrs"]
         for attr in attrs:
-            if attr == 'attachments':
+            if attr == "attachments":
                 continue
-            elif attr == 'users' and '_users' in self.__dict__:
-                lines.append([
-                    attr,
-                    ", ".join(att['userId'] for att in self._users)
-                ])
-            elif attr == 'roleAssignments' and '_roleAssignments' in self.__dict__:
+            elif attr == "users" and "_users" in self.__dict__:
+                lines.append([attr, ", ".join(att["userId"] for att in self._users)])
+            elif attr == "roleAssignments" and "_roleAssignments" in self.__dict__:
                 roles = []
                 for role in self._roleAssignments:
-                    if role.get('space') is not None:
-                        roles.append("{} ({})".format(
-                            role.get('role'),
-                            role.get('space').get('code')
-                        ))
+                    if role.get("space") is not None:
+                        roles.append(
+                            "{} ({})".format(
+                                role.get("role"), role.get("space").get("code")
+                            )
+                        )
                     else:
-                        roles.append(role.get('role'))
+                        roles.append(role.get("role"))
+
+                lines.append([attr, ", ".join(roles)])
 
-                lines.append([
-                    attr,
-                    ", ".join(roles)
-                ])
-                
             else:
-                lines.append([
-                    attr,
-                    nvl(getattr(self, attr, ''))
-                ])
+                lines.append([attr, nvl(getattr(self, attr, ""))])
         return tabulate(lines, headers=headers)
diff --git a/pybis/src/python/pybis/definitions.py b/pybis/src/python/pybis/definitions.py
index a83646fb23061fe0a1effb610e980dda55c3f601..c52ee7a3a091403148fbffe01ef5a2c5aa841e93 100644
--- a/pybis/src/python/pybis/definitions.py
+++ b/pybis/src/python/pybis/definitions.py
@@ -14,10 +14,10 @@ def openbis_definitions(entity):
             "attrs": "code permId description frozen frozenForProjects frozenForSamples registrator registrationDate modificationDate".split(),
             "multi": "".split(),
             "identifier": "spaceId",
-            "create": { "@type": "as.dto.space.create.SpaceCreation"},
-            "update": { "@type": "as.dto.space.update.SpaceUpdate"},
-            "delete": { "@type": "as.dto.space.delete.SpaceDeletionOptions"},
-            "fetch":  { "@type": "as.dto.space.fetchoptions.SpaceFetchOptions"},
+            "create": {"@type": "as.dto.space.create.SpaceCreation"},
+            "update": {"@type": "as.dto.space.update.SpaceUpdate"},
+            "delete": {"@type": "as.dto.space.delete.SpaceDeletionOptions"},
+            "fetch": {"@type": "as.dto.space.fetchoptions.SpaceFetchOptions"},
         },
         "project": {
             "attrs_new": "code description space attachments".split(),
@@ -25,8 +25,8 @@ def openbis_definitions(entity):
             "attrs": "code description permId identifier space leader registrator registrationDate modifier modificationDate attachments frozen frozenForExperiments frozenForSamples".split(),
             "multi": "".split(),
             "identifier": "projectId",
-            "create": { "@type": "as.dto.project.create.ProjectCreation"},
-            "update": { "@type": "as.dto.project.update.ProjectUpdate"},
+            "create": {"@type": "as.dto.project.create.ProjectCreation"},
+            "update": {"@type": "as.dto.project.update.ProjectUpdate"},
         },
         "experiment": {
             "attrs_new": "code type project tags attachments".split(),
@@ -34,30 +34,30 @@ def openbis_definitions(entity):
             "attrs": "code permId identifier type project tags registrator registrationDate modifier modificationDate attachments frozen frozenForDataSets frozenForSamples".split(),
             "multi": "tags attachments".split(),
             "identifier": "experimentId",
-            "create": { "@type": "as.dto.experiment.create.ExperimentCreation"},
-            "update": { "@type": "as.dto.experiment.update.ExperimentUpdate"},
+            "create": {"@type": "as.dto.experiment.create.ExperimentCreation"},
+            "update": {"@type": "as.dto.experiment.update.ExperimentUpdate"},
         },
         "externalDms": {
             "attrs_new": "code label address addressType creationId".split(),
             "attrs_up": "label address".split(),
             "attrs": "code permId label address addressType urlTemplate".split(),
             "identifier": "externalDmsId",
-            "create": { "@type": "as.dto.externaldms.create.ExternalDmsCreation"},
-            "update": { "@type": "as.dto.externaldms.update.ExternalDmsUpdate"},
+            "create": {"@type": "as.dto.externaldms.create.ExternalDmsCreation"},
+            "update": {"@type": "as.dto.externaldms.update.ExternalDmsUpdate"},
         },
         "sample": {
             "attrs_new": "code type project parents children container components space experiment tags attachments".split(),
             "attrs_up": "project parents children container components space experiment tags attachments freeze freezeForComponents freezeForChildren freezeForParents freezeForDataSets".split(),
             "attrs": "code permId identifier type project parents children components space experiment tags registrator registrationDate modifier modificationDate attachments container frozen frozenForComponents frozenForChildren frozenForParents frozenForDataSets".split(),
             "ids2type": {
-                'parentIds': {'permId': {'@type': 'as.dto.sample.id.SamplePermId'}},
-                'childIds': {'permId': {'@type': 'as.dto.sample.id.SamplePermId'}},
-                'componentIds': {'permId': {'@type': 'as.dto.sample.id.SamplePermId'}},
+                "parentIds": {"permId": {"@type": "as.dto.sample.id.SamplePermId"}},
+                "childIds": {"permId": {"@type": "as.dto.sample.id.SamplePermId"}},
+                "componentIds": {"permId": {"@type": "as.dto.sample.id.SamplePermId"}},
             },
             "identifier": "sampleId",
-            "create": { "@type": "as.dto.sample.create.SampleCreation"},
-            "update": { "@type": "as.dto.sample.update.SampleUpdate"},
-            "delete": { "@type": "as.dto.sample.delete.SampleDeletionOptions"},
+            "create": {"@type": "as.dto.sample.create.SampleCreation"},
+            "update": {"@type": "as.dto.sample.update.SampleUpdate"},
+            "delete": {"@type": "as.dto.sample.delete.SampleDeletionOptions"},
             "cre_type": "as.dto.sample.create.SampleCreation",
             "multi": "parents children components tags attachments".split(),
         },
@@ -66,112 +66,79 @@ def openbis_definitions(entity):
             "attrs_up": "description autoGeneratedCode generatedCodePrefix subcodeUnique listable showContainer showParents showParentMetadata, validationPlugin".split(),
             "attrs": "permId code description autoGeneratedCode generatedCodePrefix subcodeUnique listable showContainer showParents showParentMetadata modificationDate validationPlugin".split(),
             "default_attribute_values": {
-                "autoGeneratedCode"   : False,
-                "generatedCodePrefix" : "S",
-                "subcodeUnique"       : False,
-                "description"         : "",
-                "listable"            : True,
-                "showContainer"       : False,
-                "showParents"         : True,
-                "showParentMetadata"  : False
-            },
-            "search": {
-                "@type": "as.dto.sample.search.SampleTypeSearchCriteria"
-            },
-            "create": {
-                "@type": "as.dto.sample.create.SampleTypeCreation"
-            },
-            "update": {
-                "@type": "as.dto.sample.update.SampleTypeUpdate"
-            },
-            "delete": {
-                "@type": "as.dto.sample.delete.SampleTypeDeletionOptions"
+                "autoGeneratedCode": False,
+                "generatedCodePrefix": "S",
+                "subcodeUnique": False,
+                "description": "",
+                "listable": True,
+                "showContainer": False,
+                "showParents": True,
+                "showParentMetadata": False,
             },
+            "search": {"@type": "as.dto.sample.search.SampleTypeSearchCriteria"},
+            "create": {"@type": "as.dto.sample.create.SampleTypeCreation"},
+            "update": {"@type": "as.dto.sample.update.SampleTypeUpdate"},
+            "delete": {"@type": "as.dto.sample.delete.SampleTypeDeletionOptions"},
             "identifier": "typeId",
         },
         "materialType": {
-            "attrs_new":    "code description validationPlugin".split(),
-            "attrs_up":          "description validationPlugin".split(),
+            "attrs_new": "code description validationPlugin".split(),
+            "attrs_up": "description validationPlugin".split(),
             "attrs": "permId code description validationPlugin".split(),
-            "search": {
-                "@type": "as.dto.material.search.MaterialTypeSearchCriteria"
-            },
-            "fetch": {
-                "@type": "as.dto.material.fetchoptions.MaterialTypeFetchOptions"
-            },
-            "create": {
-                "@type": "as.dto.material.create.MaterialTypeCreation"
-            },
-            "update": {
-                "@type": "as.dto.material.update.MaterialTypeUpdate"
-            },
-            "delete": {
-                "@type": "as.dto.material.delete.MaterialTypeDeletionOptions"
-            },
+            "search": {"@type": "as.dto.material.search.MaterialTypeSearchCriteria"},
+            "fetch": {"@type": "as.dto.material.fetchoptions.MaterialTypeFetchOptions"},
+            "create": {"@type": "as.dto.material.create.MaterialTypeCreation"},
+            "update": {"@type": "as.dto.material.update.MaterialTypeUpdate"},
+            "delete": {"@type": "as.dto.material.delete.MaterialTypeDeletionOptions"},
             "identifier": "typeId",
         },
         "dataSetType": {
-            "attrs_new":    "code description mainDataSetPattern mainDataSetPath disallowDeletion validationPlugin".split(),
-            "attrs_up":          "description mainDataSetPattern mainDataSetPath disallowDeletion validationPlugin".split(),
+            "attrs_new": "code description mainDataSetPattern mainDataSetPath disallowDeletion validationPlugin".split(),
+            "attrs_up": "description mainDataSetPattern mainDataSetPath disallowDeletion validationPlugin".split(),
             "attrs": "permId code description mainDataSetPattern mainDataSetPath disallowDeletion modificationDate validationPlugin".split(),
-            "search": {
-                "@type": "as.dto.dataset.search.DataSetTypeSearchCriteria"
-            },
-            "fetch": {
-                "@type": "as.dto.dataset.fetchoptions.DataSetTypeFetchOptions"
-            },
-            "create": {
-                "@type": "as.dto.dataset.create.DataSetTypeCreation"
-            },
-            "update": {
-                "@type": "as.dto.dataset.update.DataSetTypeUpdate"
-            },
-            "delete": {
-                "@type": "as.dto.dataset.delete.DataSetTypeDeletionOptions"
-            },
+            "search": {"@type": "as.dto.dataset.search.DataSetTypeSearchCriteria"},
+            "fetch": {"@type": "as.dto.dataset.fetchoptions.DataSetTypeFetchOptions"},
+            "create": {"@type": "as.dto.dataset.create.DataSetTypeCreation"},
+            "update": {"@type": "as.dto.dataset.update.DataSetTypeUpdate"},
+            "delete": {"@type": "as.dto.dataset.delete.DataSetTypeDeletionOptions"},
             "identifier": "typeId",
         },
         "experimentType": {
             "attrs_new": "code description validationPlugin".split(),
-            "attrs_up":  "description modificationDate validationPlugin".split(),
-            "attrs":     "permId code description modificationDate validationPlugin".split(),
+            "attrs_up": "description modificationDate validationPlugin".split(),
+            "attrs": "permId code description modificationDate validationPlugin".split(),
             "search": {
                 "@type": "as.dto.experiment.search.ExperimentTypeSearchCriteria"
             },
             "fetch": {
                 "@type": "as.dto.experiment.fetchoptions.ExperimentTypeFetchOptions"
             },
-            "create": {
-                "@type": "as.dto.experiment.create.ExperimentTypeCreation"
-            },
-            "update": {
-                "@type": "as.dto.experiment.update.ExperimentTypeUpdate"
-            },
+            "create": {"@type": "as.dto.experiment.create.ExperimentTypeCreation"},
+            "update": {"@type": "as.dto.experiment.update.ExperimentTypeUpdate"},
             "delete": {
                 "@type": "as.dto.experiment.delete.ExperimentTypeDeletionOptions"
             },
             "identifier": "typeId",
         },
         "propertyType": {
-            "attrs": "code label description managedInternally dataType vocabulary materialType schema transformation semanticAnnotations registrator registrationDate".split(),
+            "attrs": "code label description managedInternally dataType vocabulary materialType schema transformation semanticAnnotations registrator registrationDate metaData".split(),
             "attrs_new": "code label description managedInternally dataType vocabulary materialType schema transformation metaData".split(),
             "attrs_up": "label description schema transformation metaData".split(),
-            "search": {
-                "@type": "as.dto.property.search.PropertyTypeSearchCriteria"
-            },
-            "create": { 
-                "@type": "as.dto.property.create.PropertyTypeCreation"
-            },
-            "update": { 
-                "@type": "as.dto.property.update.PropertyTypeUpdate"
-            },
-            "delete": {
-                "@type": "as.dto.property.delete.PropertyTypeDeletionOptions"
-            },
+            "search": {"@type": "as.dto.property.search.PropertyTypeSearchCriteria"},
+            "create": {"@type": "as.dto.property.create.PropertyTypeCreation"},
+            "update": {"@type": "as.dto.property.update.PropertyTypeUpdate"},
+            "delete": {"@type": "as.dto.property.delete.PropertyTypeDeletionOptions"},
             "dataType": [
-                "INTEGER", "VARCHAR", "MULTILINE_VARCHAR", 
-                "REAL", "TIMESTAMP", "BOOLEAN", "CONTROLLEDVOCABULARY",
-                "MATERIAL", "HYPERLINK", "XML"
+                "INTEGER",
+                "VARCHAR",
+                "MULTILINE_VARCHAR",
+                "REAL",
+                "TIMESTAMP",
+                "BOOLEAN",
+                "CONTROLLEDVOCABULARY",
+                "MATERIAL",
+                "HYPERLINK",
+                "XML",
             ],
             "identifier": "typeId",
         },
@@ -180,12 +147,8 @@ def openbis_definitions(entity):
             "attrs_up": "entityType propertyType predicateOntologyId predicateOntologyVersion predicateAccessionId descriptorOntologyId descriptorOntologyVersion descriptorAccessionId ".split(),
             "attrs": "permId entityType propertyType predicateOntologyId predicateOntologyVersion predicateAccessionId descriptorOntologyId descriptorOntologyVersion descriptorAccessionId creationDate".split(),
             "ids2type": {
-                "propertyTypeId": { 
-                    "permId": "as.dto.property.id.PropertyTypePermId"
-                },
-                "entityTypeId": { 
-                    "permId": "as.dto.entity.id.EntityTypePermId"
-                },
+                "propertyTypeId": {"permId": "as.dto.property.id.PropertyTypePermId"},
+                "entityTypeId": {"permId": "as.dto.entity.id.EntityTypePermId"},
             },
             "identifier": "permId",
             "cre_type": "as.dto.sample.create.SampleCreation",
@@ -195,12 +158,15 @@ def openbis_definitions(entity):
             "attrs_new": "type code kind experiment sample parents children components containers tags".split(),
             "attrs_up": "parents children experiment sample components containers tags freeze freezeForChildren freezeForParents freezeForComponents freezeForContainers".split(),
             "attrs": "code permId type kind experiment sample parents children components containers tags accessDate dataProducer dataProductionDate registrator registrationDate modifier modificationDate dataStore measured postRegistered frozen frozenForChildren frozenForParents frozenForComponents frozenForContainers".split(),
-
             "ids2type": {
-                'parentIds': {'permId': {'@type': 'as.dto.dataset.id.DataSetPermId'}},
-                'childIds': {'permId': {'@type': 'as.dto.dataset.id.DataSetPermId'}},
-                'componentIds': {'permId': {'@type': 'as.dto.dataset.id.DataSetPermId'}},
-                'containerIds': {'permId': {'@type': 'as.dto.dataset.id.DataSetPermId'}},
+                "parentIds": {"permId": {"@type": "as.dto.dataset.id.DataSetPermId"}},
+                "childIds": {"permId": {"@type": "as.dto.dataset.id.DataSetPermId"}},
+                "componentIds": {
+                    "permId": {"@type": "as.dto.dataset.id.DataSetPermId"}
+                },
+                "containerIds": {
+                    "permId": {"@type": "as.dto.dataset.id.DataSetPermId"}
+                },
             },
             "multi": "parents children containers components".split(),
             "identifier": "dataSetId",
@@ -225,11 +191,11 @@ def openbis_definitions(entity):
             "attrs": "code description managedInternally chosenFromList urlTemplate registrator registrationDate modifier modificationDate".split(),
             "multi": "".split(),
             "identifier": "vocabularyId",
-            "search": { "@type": "as.dto.vocabulary.search.VocabularySearchCriteria" },
-            "create": { "@type": "as.dto.vocabulary.create.VocabularyCreation"}, 
-            "update": { "@type": "as.dto.vocabulary.update.VocabularyUpdate"},
-            "delete": { "@type": "as.dto.vocabulary.delete.VocabularyDeletionOptions"},
-            "fetch":  { "@type": "as.dto.vocabulary.fetchoptions.VocabularyFetchOptions"},
+            "search": {"@type": "as.dto.vocabulary.search.VocabularySearchCriteria"},
+            "create": {"@type": "as.dto.vocabulary.create.VocabularyCreation"},
+            "update": {"@type": "as.dto.vocabulary.update.VocabularyUpdate"},
+            "delete": {"@type": "as.dto.vocabulary.delete.VocabularyDeletionOptions"},
+            "fetch": {"@type": "as.dto.vocabulary.fetchoptions.VocabularyFetchOptions"},
         },
         "vocabularyTerm": {
             "attrs_new": "code vocabularyCode label description official ordinal".split(),
@@ -237,10 +203,14 @@ def openbis_definitions(entity):
             "attrs": "code vocabularyCode label description official ordinal registrator registrationDate modifier modificationDate".split(),
             "multi": "".split(),
             "identifier": "vocabularyTermId",
-            "create": { "@type": "as.dto.vocabulary.create.VocabularyTermCreation"},
-            "update": { "@type": "as.dto.vocabulary.update.VocabularyTermUpdate"},
-            "delete": { "@type": "as.dto.vocabulary.delete.VocabularyTermDeletionOptions"},
-            "fetch":  { "@type": "as.dto.vocabulary.fetchoptions.VocabularyTermFetchOptions"},
+            "create": {"@type": "as.dto.vocabulary.create.VocabularyTermCreation"},
+            "update": {"@type": "as.dto.vocabulary.update.VocabularyTermUpdate"},
+            "delete": {
+                "@type": "as.dto.vocabulary.delete.VocabularyTermDeletionOptions"
+            },
+            "fetch": {
+                "@type": "as.dto.vocabulary.fetchoptions.VocabularyTermFetchOptions"
+            },
         },
         "plugin": {
             "attrs_new": "name description pluginType script available entityKind".split(),
@@ -248,8 +218,8 @@ def openbis_definitions(entity):
             "attrs": "permId name description registrator registrationDate pluginKind entityKinds pluginType script available".split(),
             "multi": "".split(),
             "identifier": "pluginId",
-            "pluginType": ['DYNAMIC_PROPERTY', 'MANAGED_PROPERTY', 'ENTITY_VALIDATION'],
-            "entityKind": ['MATERIAL', 'EXPERIMENT', 'SAMPLE', 'DATA_SET'],
+            "pluginType": ["DYNAMIC_PROPERTY", "MANAGED_PROPERTY", "ENTITY_VALIDATION"],
+            "entityKind": ["MATERIAL", "EXPERIMENT", "SAMPLE", "DATA_SET"],
         },
         "person": {
             "attrs_new": "userId space".split(),
@@ -258,18 +228,25 @@ def openbis_definitions(entity):
             "multi": "".split(),
             "identifier": "userId",
         },
-        "authorizationGroup" : {
+        "authorizationGroup": {
             "attrs_new": "code description userIds".split(),
             "attrs_up": "code description userIds".split(),
             "attrs": "permId code description registrator registrationDate modificationDate users".split(),
             "multi": "users".split(),
             "identifier": "groupId",
         },
-        "roleAssignment" : {
+        "roleAssignment": {
             "attrs": "id user authorizationGroup role roleLevel space project registrator registrationDate".split(),
             "attrs_new": "role roleLevel user authorizationGroup role space project".split(),
             "attrs_up": "role roleLevel user authorizationGroup role space project".split(),
-            "role": ['POWER_USER', 'OBSERVER', 'USER', 'DISABLED', 'ADMIN', 'ETL_SERVER']
+            "role": [
+                "POWER_USER",
+                "OBSERVER",
+                "USER",
+                "DISABLED",
+                "ADMIN",
+                "ETL_SERVER",
+            ],
         },
         "attr2ids": {
             "space": "spaceId",
@@ -297,18 +274,21 @@ def openbis_definitions(entity):
             "validationPlugin": "validationPluginId",
         },
         "ids2type": {
-            'spaceId': {'permId': {'@type': 'as.dto.space.id.SpacePermId'}},
-            'projectId': {'permId': {'@type': 'as.dto.project.id.ProjectPermId'}},
-            'experimentId': {'permId': {'@type': 'as.dto.experiment.id.ExperimentPermId'}},
-            'tagIds': {'code': {'@type': 'as.dto.tag.id.TagCode'}},
+            "spaceId": {"permId": {"@type": "as.dto.space.id.SpacePermId"}},
+            "projectId": {"permId": {"@type": "as.dto.project.id.ProjectPermId"}},
+            "experimentId": {
+                "permId": {"@type": "as.dto.experiment.id.ExperimentPermId"}
+            },
+            "tagIds": {"code": {"@type": "as.dto.tag.id.TagCode"}},
         },
         "dataSetFile": {
-            "search": { "@type": "dss.dto.datasetfile.search.DataSetFileSearchCriteria" }
+            "search": {"@type": "dss.dto.datasetfile.search.DataSetFileSearchCriteria"}
         },
     }
     return entities[entity]
 
-get_definition_for_entity = openbis_definitions   # Alias
+
+get_definition_for_entity = openbis_definitions  # Alias
 
 
 fetch_option = {
@@ -318,20 +298,22 @@ fetch_option = {
         "space": {"@type": "as.dto.space.fetchoptions.SpaceFetchOptions"},
     },
     "person": {"@type": "as.dto.person.fetchoptions.PersonFetchOptions"},
-    "users": {"@type": "as.dto.person.fetchoptions.PersonFetchOptions" },
-    "user": {"@type": "as.dto.person.fetchoptions.PersonFetchOptions" },
-    "owner": {"@type": "as.dto.person.fetchoptions.PersonFetchOptions" },
+    "users": {"@type": "as.dto.person.fetchoptions.PersonFetchOptions"},
+    "user": {"@type": "as.dto.person.fetchoptions.PersonFetchOptions"},
+    "owner": {"@type": "as.dto.person.fetchoptions.PersonFetchOptions"},
     "registrator": {"@type": "as.dto.person.fetchoptions.PersonFetchOptions"},
     "modifier": {"@type": "as.dto.person.fetchoptions.PersonFetchOptions"},
     "leader": {"@type": "as.dto.person.fetchoptions.PersonFetchOptions"},
-    "authorizationGroup": {"@type": "as.dto.authorizationgroup.fetchoptions.AuthorizationGroupFetchOptions"},
+    "authorizationGroup": {
+        "@type": "as.dto.authorizationgroup.fetchoptions.AuthorizationGroupFetchOptions"
+    },
     "experiment": {
         "@type": "as.dto.experiment.fetchoptions.ExperimentFetchOptions",
-        "type": {"@type": "as.dto.experiment.fetchoptions.ExperimentTypeFetchOptions"}
+        "type": {"@type": "as.dto.experiment.fetchoptions.ExperimentTypeFetchOptions"},
     },
     "sample": {
         "@type": "as.dto.sample.fetchoptions.SampleFetchOptions",
-        "type": {"@type": "as.dto.sample.fetchoptions.SampleTypeFetchOptions"}
+        "type": {"@type": "as.dto.sample.fetchoptions.SampleTypeFetchOptions"},
     },
     "samples": {"@type": "as.dto.sample.fetchoptions.SampleFetchOptions"},
     "sampleType": {
@@ -341,13 +323,11 @@ fetch_option = {
             "propertyType": {
                 "@type": "as.dto.property.fetchoptions.PropertyTypeFetchOptions"
             },
-            "plugin": {
-                "@type": "as.dto.plugin.fetchoptions.PluginFetchOptions"
-            }
+            "plugin": {"@type": "as.dto.plugin.fetchoptions.PluginFetchOptions"},
         },
         "validationPlugin": {
             "@type": "as.dto.plugin.fetchoptions.PluginFetchOptions",
-        }
+        },
     },
     "materialType": {
         "@type": "as.dto.material.fetchoptions.MaterialTypeFetchOptions",
@@ -356,13 +336,11 @@ fetch_option = {
             "propertyType": {
                 "@type": "as.dto.property.fetchoptions.PropertyTypeFetchOptions",
             },
-            "plugin": {
-                "@type": "as.dto.plugin.fetchoptions.PluginFetchOptions"
-            }
+            "plugin": {"@type": "as.dto.plugin.fetchoptions.PluginFetchOptions"},
         },
         "validationPlugin": {
             "@type": "as.dto.plugin.fetchoptions.PluginFetchOptions",
-        }
+        },
     },
     "dataSetType": {
         "@type": "as.dto.dataset.fetchoptions.DataSetTypeFetchOptions",
@@ -371,13 +349,11 @@ fetch_option = {
             "propertyType": {
                 "@type": "as.dto.property.fetchoptions.PropertyTypeFetchOptions",
             },
-            "plugin": {
-                "@type": "as.dto.plugin.fetchoptions.PluginFetchOptions"
-            }
+            "plugin": {"@type": "as.dto.plugin.fetchoptions.PluginFetchOptions"},
         },
         "validationPlugin": {
             "@type": "as.dto.plugin.fetchoptions.PluginFetchOptions",
-        }
+        },
     },
     "experimentType": {
         "@type": "as.dto.experiment.fetchoptions.ExperimentTypeFetchOptions",
@@ -386,19 +362,17 @@ fetch_option = {
             "propertyType": {
                 "@type": "as.dto.property.fetchoptions.PropertyTypeFetchOptions",
             },
-            "plugin": {
-                "@type": "as.dto.plugin.fetchoptions.PluginFetchOptions"
-            }
-        }, 
+            "plugin": {"@type": "as.dto.plugin.fetchoptions.PluginFetchOptions"},
+        },
         "validationPlugin": {
             "@type": "as.dto.plugin.fetchoptions.PluginFetchOptions",
-        }
+        },
     },
     "dataSet": {
         "@type": "as.dto.dataset.fetchoptions.DataSetFetchOptions",
-        "type":       {"@type": "as.dto.dataset.fetchoptions.DataSetTypeFetchOptions"},
-        "parents":    {"@type": "as.dto.dataset.fetchoptions.DataSetFetchOptions"},
-        "children":   {"@type": "as.dto.dataset.fetchoptions.DataSetFetchOptions"},
+        "type": {"@type": "as.dto.dataset.fetchoptions.DataSetTypeFetchOptions"},
+        "parents": {"@type": "as.dto.dataset.fetchoptions.DataSetFetchOptions"},
+        "children": {"@type": "as.dto.dataset.fetchoptions.DataSetFetchOptions"},
         "containers": {"@type": "as.dto.dataset.fetchoptions.DataSetFetchOptions"},
         "components": {"@type": "as.dto.dataset.fetchoptions.DataSetFetchOptions"},
     },
@@ -409,8 +383,10 @@ fetch_option = {
     },
     "physicalData": {"@type": "as.dto.dataset.fetchoptions.PhysicalDataFetchOptions"},
     "linkedData": {
-        "externalDms": {"@type": "as.dto.externaldms.fetchoptions.ExternalDmsFetchOptions"},
-        "@type": "as.dto.dataset.fetchoptions.LinkedDataFetchOptions"
+        "externalDms": {
+            "@type": "as.dto.externaldms.fetchoptions.ExternalDmsFetchOptions"
+        },
+        "@type": "as.dto.dataset.fetchoptions.LinkedDataFetchOptions",
     },
     "roleAssignment": {
         "@type": "as.dto.roleassignment.fetchoptions.RoleAssignmentFetchOptions",
@@ -425,23 +401,21 @@ fetch_option = {
             "@type": "as.dto.property.fetchoptions.PropertyTypeFetchOptions",
             "vocabulary": {
                 "@type": "as.dto.vocabulary.fetchoptions.VocabularyFetchOptions",
-            }
-        }
+            },
+        },
     },
     "propertyType": {
         "@type": "as.dto.property.fetchoptions.PropertyTypeFetchOptions",
         "vocabulary": {
             "@type": "as.dto.vocabulary.fetchoptions.VocabularyFetchOptions"
         },
-        "materialType": { 
-            "@type": "as.dto.material.fetchoptions.MaterialTypeFetchOptions" 
+        "materialType": {
+            "@type": "as.dto.material.fetchoptions.MaterialTypeFetchOptions"
         },
         "semanticAnnotations": {
             "@type": "as.dto.semanticannotation.fetchoptions.SemanticAnnotationFetchOptions"
         },
-        "registrator": {
-            "@type": "as.dto.person.fetchoptions.PersonFetchOptions"
-        },
+        "registrator": {"@type": "as.dto.person.fetchoptions.PersonFetchOptions"},
     },
     "semanticAnnotations": {
         "@type": "as.dto.semanticannotation.fetchoptions.SemanticAnnotationFetchOptions"
@@ -451,9 +425,7 @@ fetch_option = {
     "attachments": {"@type": "as.dto.attachment.fetchoptions.AttachmentFetchOptions"},
     "attachmentsWithContent": {
         "@type": "as.dto.attachment.fetchoptions.AttachmentFetchOptions",
-        "content": {
-            "@type": "as.dto.common.fetchoptions.EmptyFetchOptions"
-        },
+        "content": {"@type": "as.dto.common.fetchoptions.EmptyFetchOptions"},
     },
     "script": {
         "@type": "as.dto.common.fetchoptions.EmptyFetchOptions",
@@ -464,23 +436,31 @@ fetch_option = {
     "vocabulary": {
         "@type": "as.dto.vocabulary.fetchoptions.VocabularyFetchOptions",
     },
-    "vocabularyTerm": {"@type": "as.dto.vocabulary.fetchoptions.VocabularyTermFetchOptions"},
-    "deletedObjects": { "@type": "as.dto.deletion.fetchoptions.DeletedObjectFetchOptions" },
-    "deletion": { "@type": "as.dto.deletion.fetchoptions.DeletionFetchOptions" },
-    "externalDms": { "@type": "as.dto.externaldms.fetchoptions.ExternalDmsFetchOptions"},
-    "dataSetFile": { "@type": "dss.dto.datasetfile.fetchoptions.DataSetFileFetchOptions"},
+    "vocabularyTerm": {
+        "@type": "as.dto.vocabulary.fetchoptions.VocabularyTermFetchOptions"
+    },
+    "deletedObjects": {
+        "@type": "as.dto.deletion.fetchoptions.DeletedObjectFetchOptions"
+    },
+    "deletion": {"@type": "as.dto.deletion.fetchoptions.DeletionFetchOptions"},
+    "externalDms": {"@type": "as.dto.externaldms.fetchoptions.ExternalDmsFetchOptions"},
+    "dataSetFile": {
+        "@type": "dss.dto.datasetfile.fetchoptions.DataSetFileFetchOptions"
+    },
 }
 
+
 def get_fetchoption_for_entity(entity):
-    entity = entity[0].lower() + entity[1:]   # make first character lowercase
+    entity = entity[0].lower() + entity[1:]  # make first character lowercase
     try:
         return fetch_option[entity]
     except KeyError as e:
         return {}
 
-def get_type_for_entity(entity, action, parents_or_children=''):
+
+def get_type_for_entity(entity, action, parents_or_children=""):
     if action not in "create update delete search".split():
-        raise ValueError('unknown action: {}'.format(action))
+        raise ValueError("unknown action: {}".format(action))
 
     definition = openbis_definitions(entity)
     if action in definition and not parents_or_children:
@@ -494,22 +474,30 @@ def get_type_for_entity(entity, action, parents_or_children=''):
             "delete": "DeletionOptions",
             "search": "SearchCriteria",
         }
-        
+
         if parents_or_children:
             return {
-                "@type": "as.dto.{}.{}.{}{}{}"
-                .format(entity.lower(), action, cap_entity, parents_or_children, noun[action])
+                "@type": "as.dto.{}.{}.{}{}{}".format(
+                    entity.lower(),
+                    action,
+                    cap_entity,
+                    parents_or_children,
+                    noun[action],
+                )
             }
         else:
             return {
-                "@type": "as.dto.{}.{}.{}{}"
-                .format(entity.lower(), action, cap_entity, noun[action])
+                "@type": "as.dto.{}.{}.{}{}".format(
+                    entity.lower(), action, cap_entity, noun[action]
+                )
             }
 
+
 def get_fetchoptions(entity, including=None):
-    if including is None: including = []
-    including += ['@type']
-    entity = entity[0].lower() + entity[1:]   # make first character lowercase
+    if including is None:
+        including = []
+    including += ["@type"]
+    entity = entity[0].lower() + entity[1:]  # make first character lowercase
     fo = {}
     for inc in including:
         try:
@@ -525,8 +513,7 @@ def get_method_for_entity(entity, action):
 
     if entity == "vocabulary":
         return "{}Vocabularies".format(action)
-    
+
     cap_entity = entity[:1].upper() + entity[1:]
 
     return "{}{}s".format(action, cap_entity)
-
diff --git a/pybis/src/python/pybis/openbis_object.py b/pybis/src/python/pybis/openbis_object.py
index 252a41dab37a850c61f0ae4488d49939bc938c06..7b59e015c7bebeb19c591e52dd2d8e126e1335c5 100644
--- a/pybis/src/python/pybis/openbis_object.py
+++ b/pybis/src/python/pybis/openbis_object.py
@@ -1,30 +1,30 @@
 from .property import PropertyHolder
 from .attribute import AttrHolder
 from .utils import VERBOSE
-from .definitions import get_definition_for_entity, get_type_for_entity, get_method_for_entity
+from .definitions import (
+    get_definition_for_entity,
+    get_type_for_entity,
+    get_method_for_entity,
+)
 from collections import defaultdict
 
-class OpenBisObject():
 
-    def __init_subclass__(
-        cls,
-        entity=None,
-        single_item_method_name=None
-    ):
+class OpenBisObject:
+    def __init_subclass__(cls, entity=None, single_item_method_name=None):
         """create a specialized parent class.
         The class that inherits from OpenBisObject does not need
         to implement its own __init__ method in order to provide the
         entity name. Instead, it can pass the entity name as a param:
         class XYZ(OpenBisObject, entity="myEntity")
         """
-        cls._entity=entity
-        cls._single_item_method_name=single_item_method_name
+        cls._entity = entity
+        cls._single_item_method_name = single_item_method_name
 
     def __init__(self, openbis_obj, type=None, data=None, props=None, **kwargs):
-        self.__dict__['openbis'] = openbis_obj
-        self.__dict__['type'] = type
-        self.__dict__['p'] = PropertyHolder(openbis_obj, type)
-        self.__dict__['a'] = AttrHolder(openbis_obj, self._entity, type)
+        self.__dict__["openbis"] = openbis_obj
+        self.__dict__["type"] = type
+        self.__dict__["p"] = PropertyHolder(openbis_obj, type)
+        self.__dict__["a"] = AttrHolder(openbis_obj, self._entity, type)
 
         # existing OpenBIS object
         if data is not None:
@@ -38,88 +38,87 @@ class OpenBisObject():
             for key in kwargs:
                 setattr(self, key, kwargs[key])
 
-
     def __dir__(self):
         defs = get_definition_for_entity(self.entity)
         if self.is_new:
-            return defs['attrs_new']
+            return defs["attrs_new"]
         else:
-            return list(set(defs['attrs'] + defs['attrs_up']))
-
+            return list(set(defs["attrs"] + defs["attrs_up"]))
 
     def _set_data(self, data):
         # assign the attribute data to self.a by calling it
         # (invoking the AttrHolder.__call__ function)
         self.a(data)
-        self.__dict__['data'] = data
+        self.__dict__["data"] = data
 
         # put the properties in the self.p namespace (without checking them)
-        if 'properties' in data:
-            for key, value in data['properties'].items():
+        if "properties" in data:
+            for key, value in data["properties"].items():
                 self.p.__dict__[key.lower()] = value
-            
+
         # object is already saved to openBIS, so it is not new anymore
-        self.a.__dict__['_is_new'] = False
+        self.a.__dict__["_is_new"] = False
 
     @property
     def attrs(self):
-        return self.__dict__['a']
+        return self.__dict__["a"]
 
     @property
     def space(self):
         try:
-            return self.openbis.get_space(self._space['permId'])
+            return self.openbis.get_space(self._space["permId"])
         except Exception:
             pass
 
     @property
     def project(self):
         try:
-            return self.openbis.get_project(self._project['identifier'])
+            return self.openbis.get_project(self._project["identifier"])
         except Exception:
             pass
 
     @property
     def experiment(self):
         try:
-            return self.openbis.get_experiment(self._experiment['identifier'])
+            return self.openbis.get_experiment(self._experiment["identifier"])
         except Exception:
             pass
+
     collection = experiment  # Alias
 
     @property
     def sample(self):
         try:
-            return self.openbis.get_sample(self._sample['identifier'])
+            return self.openbis.get_sample(self._sample["identifier"])
         except Exception:
             pass
-    object = sample # Alias
+
+    object = sample  # Alias
 
     @property
     def _permId(self):
         try:
-            return self.data['permId']
+            return self.data["permId"]
         except Exception:
             return ""
 
     @property
     def permId(self):
         try:
-            return self.data['permId']['permId']
+            return self.data["permId"]["permId"]
         except Exception:
             try:
-                return self.a.__dict__['_permId']['permId']
+                return self.a.__dict__["_permId"]["permId"]
             except Exception:
                 return ""
-        
 
     def __getattr__(self, name):
-        return getattr(self.__dict__['a'], name)
+        return getattr(self.__dict__["a"], name)
 
     def __setattr__(self, name, value):
-        if name in ['set_properties', 'set_tags', 'add_tags']:
+        if name in ["set_properties", "set_tags", "add_tags"]:
             raise ValueError("These are methods which should not be overwritten")
-        setattr(self.__dict__['a'], name, value)
+        setattr(self.__dict__["a"], name, value)
 
     def _repr_html_(self):
         """Print all the assigned attributes (identifier, tags, etc.) in a nicely formatted table. See
@@ -128,18 +127,17 @@ class OpenBisObject():
         return self.a._repr_html_()
 
     def __repr__(self):
-        """same thing as _repr_html_() but for IPython
-        """
+        """same thing as _repr_html_() but for IPython"""
         return self.a.__repr__()
 
     def mark_to_be_deleted(self):
-        self.__dict__['mark_to_be_deleted'] = True
+        self.__dict__["mark_to_be_deleted"] = True
 
     def unmark_to_be_deleted(self):
-        self.__dict__['mark_to_be_deleted'] = False
+        self.__dict__["mark_to_be_deleted"] = False
 
     def is_marked_to_be_deleted(self):
-        return self.__dict__.get('mark_to_be_deleted', False)
+        return self.__dict__.get("mark_to_be_deleted", False)
 
     def delete(self, reason):
         """Delete this openbis entity.
@@ -147,43 +145,38 @@ class OpenBisObject():
         """
         if not self.data:
             return
- 
+
         self.openbis.delete_openbis_entity(
-            entity=self._entity,
-            objectId=self.data['permId'],
-            reason=reason
-        )
-        if VERBOSE: print(
-            "{} {} successfully deleted.".format(
-                self._entity,
-                self.permId
-            )
+            entity=self._entity, objectId=self.data["permId"], reason=reason
         )
+        if VERBOSE:
+            print("{} {} successfully deleted.".format(self._entity, self.permId))
 
     def _get_single_item_method(self):
         single_item_method = None
         if self._single_item_method_name:
-            single_item_method = getattr(
-                self.openbis, self._single_item_method_name
-            )
+            single_item_method = getattr(self.openbis, self._single_item_method_name)
         else:
             # try to guess the method...
-            single_item_method = getattr(self.openbis, 'get_' + self.entity)
+            single_item_method = getattr(self.openbis, "get_" + self.entity)
 
         return single_item_method
 
-
     def save(self):
         get_single_item = self._get_single_item_method()
         # check for mandatory properties before saving the object
         props = None
         if self.props:
             for prop_name, prop in self.props._property_names.items():
-                if prop['mandatory']:
-                    if getattr(self.props, prop_name) is None \
-                    or getattr(self.props, prop_name) == "":
+                if prop["mandatory"]:
+                    if (
+                        getattr(self.props, prop_name) is None
+                        or getattr(self.props, prop_name) == ""
+                    ):
                         raise ValueError(
-                            "Property '{}' is mandatory and must not be None".format(prop_name)
+                            "Property '{}' is mandatory and must not be None".format(
+                                prop_name
+                            )
                         )
 
             props = self.p._all_props()
@@ -191,22 +184,27 @@ class OpenBisObject():
         # NEW
         if self.is_new:
             request = self._new_attrs()
-            if props: request["params"][1][0]["properties"] = props
+            if props:
+                request["params"][1][0]["properties"] = props
 
             resp = self.openbis._post_request(self.openbis.as_v3, request)
 
-            if VERBOSE: print("{} successfully created.".format(self.entity))
-            new_entity_data = get_single_item(resp[0]['permId'], only_data=True)
+            if VERBOSE:
+                print("{} successfully created.".format(self.entity))
+            new_entity_data = get_single_item(resp[0]["permId"], only_data=True)
             self._set_data(new_entity_data)
             return self
 
         # UPDATE
         else:
             request = self._up_attrs(method_name=None, permId=self._permId)
-            if props: request["params"][1][0]["properties"] = props
+
+            if props:
+                request["params"][1][0]["properties"] = props
 
             resp = self.openbis._post_request(self.openbis.as_v3, request)
-            if VERBOSE: print("{} successfully updated.".format(self.entity))
+            if VERBOSE:
+                print("{} successfully updated.".format(self.entity))
             new_entity_data = get_single_item(self.permId, only_data=True)
             self._set_data(new_entity_data)
             return self
@@ -216,7 +214,7 @@ class Transaction:
     def __init__(self, *entities):
 
         self.entities = {}
-        self.reason = 'no reason'
+        self.reason = "no reason"
 
         if not entities:
             return
@@ -224,7 +222,6 @@ class Transaction:
         for entity in entities:
             self.add(entity)
 
-
     def add(self, entity_obj):
         """Collect the creations or updates of entities.
         self.entities = {
@@ -245,17 +242,16 @@ class Transaction:
         if not entity_obj.entity in self.entities:
             self.entities[entity_obj.entity] = defaultdict(list)
 
-        mode = 'update'
+        mode = "update"
         if entity_obj.is_new:
-            mode = 'create'
+            mode = "create"
         elif entity_obj.is_marked_to_be_deleted():
-            mode = 'delete'
+            mode = "delete"
         else:
-            mode  = 'update'
+            mode = "update"
 
         self.entities[entity_obj.entity][mode].append(entity_obj)
 
-
     def commit(self):
         """Merge the individual requests to Make one single request.
         For each entity-type and mode (create, update) an individual request will be sent,
@@ -263,45 +259,52 @@ class Transaction:
         """
 
         import copy
+
         for entity_type in self.entities:
             for mode in self.entities[entity_type]:
 
                 request_coll = []
                 for entity in self.entities[entity_type][mode]:
                     if mode == "delete":
-                        delete_options = get_type_for_entity(entity_type, 'delete')
-                        delete_options['reason'] = self.reason
-                        method = get_method_for_entity(entity_type, 'delete')
+                        delete_options = get_type_for_entity(entity_type, "delete")
+                        delete_options["reason"] = self.reason
+                        method = get_method_for_entity(entity_type, "delete")
                         request = {
                             "method": method,
                             "params": [
                                 entity.openbis.token,
-                                [
-                                    entity.data['permId']
-                                ],
-                                delete_options
-                            ]
+                                [entity.data["permId"]],
+                                delete_options,
+                            ],
                         }
                         request_coll.append(request)
                         continue
                     props = None
                     if entity.props:
                         for prop_name, prop in entity.props._property_names.items():
-                            if prop['mandatory']:
-                                if getattr(entity.props, prop_name) is None \
-                                or getattr(entity.props, prop_name) == "":
+                            if prop["mandatory"]:
+                                if (
+                                    getattr(entity.props, prop_name) is None
+                                    or getattr(entity.props, prop_name) == ""
+                                ):
                                     raise ValueError(
-                                        "Property '{}' is mandatory and must not be None".format(prop_name)
+                                        "Property '{}' is mandatory and must not be None".format(
+                                            prop_name
+                                        )
                                     )
                     props = entity.p._all_props()
 
                     if mode == "create":
                         request = entity._new_attrs()
-                        if props: request["params"][1][0]["properties"] = props
+                        if props:
+                            request["params"][1][0]["properties"] = props
 
                     elif mode == "update":
-                        request = entity._up_attrs(method_name=None, permId=entity._permId)
-                        if props: request["params"][1][0]["properties"] = props
+                        request = entity._up_attrs(
+                            method_name=None, permId=entity._permId
+                        )
+                        if props:
+                            request["params"][1][0]["properties"] = props
 
                     else:
                         raise ValueError(f"Unkown mode: {mode}")
@@ -313,27 +316,32 @@ class Transaction:
                     batch_request = copy.deepcopy(request_coll[0])
                     # merge all requests into one
                     for i, request in enumerate(request_coll):
-                        if i == 0: continue
-                        batch_request['params'][1].append(
-                            request['params'][1][0]
-                        )
+                        if i == 0:
+                            continue
+                        batch_request["params"][1].append(request["params"][1][0])
 
                     try:
-                        resp = entity.openbis._post_request(entity.openbis.as_v3, batch_request)
-                        if VERBOSE: print(f"{i+1} {entity_type}(s) {mode}d.")
+                        resp = entity.openbis._post_request(
+                            entity.openbis.as_v3, batch_request
+                        )
+                        if VERBOSE:
+                            print(f"{i+1} {entity_type}(s) {mode}d.")
 
                         # mark every sample as not being new anymore
                         # and add the permId attribute received by the response
                         # we assume the response permIds are the same order as we sent them
                         if resp:
                             for i, resp_item in enumerate(resp):
-                                if mode == 'delete': continue
-                                self.entities[entity_type][mode][i].a.__dict__['_is_new'] = False
-                                self.entities[entity_type][mode][i].a.__dict__['_permId'] = resp_item
-
+                                if mode == "delete":
+                                    continue
+                                self.entities[entity_type][mode][i].a.__dict__[
+                                    "_is_new"
+                                ] = False
+                                self.entities[entity_type][mode][i].a.__dict__[
+                                    "_permId"
+                                ] = resp_item
 
                     except ValueError as err:
-                        if VERBOSE: 
+                        if VERBOSE:
                             print(f"ERROR: {mode} of {i+1} {entity_type}(s) FAILED")
                         raise ValueError(err)
-