Skip to content

Async Object-Orientated Models

Contained within this file are experimental interfaces for working with the Synapse Python Client. Unless otherwise noted these interfaces are subject to change at any time. Use at your own risk.

These APIs also introduce AsyncIO to the client.

Sample Scripts:

See this page for sample scripts. The sample scripts are from a synchronous context, replace any of the method calls with the async counter-party and they will be functionally equivalent.

API reference

synapseclient.models.Project dataclass

Bases: ProjectSynchronousProtocol, AccessControllable, StorableContainer

A Project is a top-level container for organizing data in Synapse.

ATTRIBUTE DESCRIPTION
id

The unique immutable ID for this project. A new ID will be generated for new Projects. Once issued, this ID is guaranteed to never change or be re-issued

TYPE: Optional[str]

name

The name of this project. Must be 256 characters or less. Names may only contain: letters, numbers, spaces, underscores, hyphens, periods, plus signs, apostrophes, and parentheses

TYPE: Optional[str]

description

The description of this entity. Must be 1000 characters or less.

TYPE: Optional[str]

etag

Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle concurrent updates. Since the E-Tag changes every time an entity is updated it is used to detect when a client's current representation of an entity is out-of-date.

TYPE: Optional[str]

created_on

The date this entity was created.

TYPE: Optional[str]

modified_on

The date this entity was last modified.

TYPE: Optional[str]

created_by

The ID of the user that created this entity.

TYPE: Optional[str]

modified_by

The ID of the user that last modified this entity.

TYPE: Optional[str]

alias

The project alias for use in friendly project urls.

TYPE: Optional[str]

files

Any files that are at the root directory of the project.

TYPE: List[File]

folders

Any folders that are at the root directory of the project.

TYPE: List[Folder]

annotations

Additional metadata associated with the folder. The key is the name of your desired annotations. The value is an object containing a list of values (use empty list to represent no values for key) and the value type associated with all values in the list. To remove all annotations set this to an empty dict {} or None and store the entity.

TYPE: Optional[Dict[str, Union[List[str], List[bool], List[float], List[int], List[date], List[datetime]]]]

create_or_update

(Store only) Indicates whether the method should automatically perform an update if the resource conflicts with an existing Synapse object. When True this means that any changes to the resource will be non-destructive.

This boolean is ignored if you've already stored or retrieved the resource from Synapse for this instance at least once. Any changes to the resource will be destructive in this case. For example if you want to delete the content for a field you will need to call .get() and then modify the field.

TYPE: bool

parent_id

The parent ID of the project. In practice projects do not have a parent, but this is required for the inner workings of Synapse.

TYPE: Optional[str]

Creating a project

This example shows how to create a project

from synapseclient.models import Project, File
import synapseclient

synapseclient.login()

my_annotations = {
    "my_single_key_string": "a",
    "my_key_string": ["b", "a", "c"],
}
project = Project(
    name="My unique project name",
    annotations=my_annotations,
    description="This is a project with random data.",
)

project = project.store()

print(project)
Storing several files to a project

This example shows how to store several files to a project

file_1 = File(
    path=path_to_file_1,
    name=name_of_file_1,
)
file_2 = File(
    path=path_to_file_2,
    name=name_of_file_2,
)
project.files = [file_1, file_2]
project = project.store()
Source code in synapseclient/models/project.py
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
@dataclass()
@async_to_sync
class Project(ProjectSynchronousProtocol, AccessControllable, StorableContainer):
    """A Project is a top-level container for organizing data in Synapse.

    Attributes:
        id: The unique immutable ID for this project. A new ID will be generated for new
            Projects. Once issued, this ID is guaranteed to never change or be re-issued
        name: The name of this project. Must be 256 characters or less. Names may only
            contain: letters, numbers, spaces, underscores, hyphens, periods, plus
            signs, apostrophes, and parentheses
        description: The description of this entity. Must be 1000 characters or less.
        etag: Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle
            concurrent updates. Since the E-Tag changes every time an entity is updated
            it is used to detect when a client's current representation of an entity
            is out-of-date.
        created_on: The date this entity was created.
        modified_on: The date this entity was last modified.
        created_by: The ID of the user that created this entity.
        modified_by: The ID of the user that last modified this entity.
        alias: The project alias for use in friendly project urls.
        files: Any files that are at the root directory of the project.
        folders: Any folders that are at the root directory of the project.
        annotations: Additional metadata associated with the folder. The key is the name
            of your desired annotations. The value is an object containing a list of
            values (use empty list to represent no values for key) and the value type
            associated with all values in the list. To remove all annotations set this
            to an empty dict `{}` or None and store the entity.
        create_or_update: (Store only) Indicates whether the method should
            automatically perform an update if the resource conflicts with an existing
            Synapse object. When True this means that any changes to the resource will
            be non-destructive.

            This boolean is ignored if you've already stored or retrieved the resource
            from Synapse for this instance at least once. Any changes to the resource
            will be destructive in this case. For example if you want to delete the
            content for a field you will need to call `.get()` and then modify the
            field.
        parent_id: The parent ID of the project. In practice projects do not have a
            parent, but this is required for the inner workings of Synapse.

    Example: Creating a project
        This example shows how to create a project

            from synapseclient.models import Project, File
            import synapseclient

            synapseclient.login()

            my_annotations = {
                "my_single_key_string": "a",
                "my_key_string": ["b", "a", "c"],
            }
            project = Project(
                name="My unique project name",
                annotations=my_annotations,
                description="This is a project with random data.",
            )

            project = project.store()

            print(project)

    Example: Storing several files to a project
        This example shows how to store several files to a project

            file_1 = File(
                path=path_to_file_1,
                name=name_of_file_1,
            )
            file_2 = File(
                path=path_to_file_2,
                name=name_of_file_2,
            )
            project.files = [file_1, file_2]
            project = project.store()

    """

    id: Optional[str] = None
    """The unique immutable ID for this project. A new ID will be generated for new
    Projects. Once issued, this ID is guaranteed to never change or be re-issued"""

    name: Optional[str] = None
    """The name of this project. Must be 256 characters or less. Names may only contain:
    letters, numbers, spaces, underscores, hyphens, periods, plus signs, apostrophes,
    and parentheses"""

    description: Optional[str] = None
    """The description of this entity. Must be 1000 characters or less."""

    etag: Optional[str] = None
    """Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle
    concurrent updates. Since the E-Tag changes every time an entity is updated it
    is used to detect when a client's current representation of an entity is out-of-date."""

    created_on: Optional[str] = field(default=None, compare=False)
    """(Read Only) The date this entity was created."""

    modified_on: Optional[str] = field(default=None, compare=False)
    """(Read Only) The date this entity was last modified."""

    created_by: Optional[str] = field(default=None, compare=False)
    """(Read Only) The ID of the user that created this entity."""

    modified_by: Optional[str] = field(default=None, compare=False)
    """(Read Only) The ID of the user that last modified this entity."""

    alias: Optional[str] = None
    """The project alias for use in friendly project urls."""

    files: List["File"] = field(default_factory=list, compare=False)
    """Any files that are at the root directory of the project."""

    folders: List["Folder"] = field(default_factory=list, compare=False)
    """Any folders that are at the root directory of the project."""

    annotations: Optional[
        Dict[
            str,
            Union[
                List[str],
                List[bool],
                List[float],
                List[int],
                List[date],
                List[datetime],
            ],
        ]
    ] = field(default_factory=dict, compare=False)
    """Additional metadata associated with the folder. The key is the name of your
    desired annotations. The value is an object containing a list of values
    (use empty list to represent no values for key) and the value type associated with
    all values in the list. To remove all annotations set this to an empty dict `{}`
    or None and store the entity."""

    create_or_update: bool = field(default=True, repr=False)
    """
    (Store only)

    Indicates whether the method should automatically perform an update if the resource
    conflicts with an existing Synapse object. When True this means that any changes
    to the resource will be non-destructive.

    This boolean is ignored if you've already stored or retrieved the resource from
    Synapse for this instance at least once. Any changes to the resource will be
    destructive in this case. For example if you want to delete the content for a field
    you will need to call `.get()` and then modify the field.
    """

    parent_id: Optional[str] = None
    """The parent ID of the project. In practice projects do not have a parent, but this
    is required for the inner workings of Synapse."""

    _last_persistent_instance: Optional["Project"] = field(
        default=None, repr=False, compare=False
    )
    """The last persistent instance of this object. This is used to determine if the
    object has been changed and needs to be updated in Synapse."""

    @property
    def has_changed(self) -> bool:
        """Determines if the object has been changed and needs to be updated in Synapse."""
        return (
            not self._last_persistent_instance or self._last_persistent_instance != self
        )

    def _set_last_persistent_instance(self) -> None:
        """Stash the last time this object interacted with Synapse. This is used to
        determine if the object has been changed and needs to be updated in Synapse."""
        del self._last_persistent_instance
        self._last_persistent_instance = replace(self)
        self._last_persistent_instance.annotations = (
            deepcopy(self.annotations) if self.annotations else {}
        )

    def fill_from_dict(
        self,
        synapse_project: Union[Synapse_Project, Dict],
        set_annotations: bool = True,
    ) -> "Project":
        """
        Converts a response from the REST API into this dataclass.

        Arguments:
            synapse_project: The response from the REST API.

        Returns:
            The Project object.
        """
        self.id = synapse_project.get("id", None)
        self.name = synapse_project.get("name", None)
        self.description = synapse_project.get("description", None)
        self.etag = synapse_project.get("etag", None)
        self.created_on = synapse_project.get("createdOn", None)
        self.modified_on = synapse_project.get("modifiedOn", None)
        self.created_by = synapse_project.get("createdBy", None)
        self.modified_by = synapse_project.get("modifiedBy", None)
        self.alias = synapse_project.get("alias", None)
        self.parent_id = synapse_project.get("parentId", None)
        if set_annotations:
            self.annotations = Annotations.from_dict(
                synapse_project.get("annotations", {})
            )
        return self

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"Project_Store: ID: {self.id}, Name: {self.name}"
    )
    async def store_async(
        self,
        failure_strategy: FailureStrategy = FailureStrategy.LOG_EXCEPTION,
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> "Project":
        """
        Store project, files, and folders to synapse. If you have any files or folders
        attached to this project they will be stored as well. You may attach files
        and folders to this project by setting the `files` and `folders` attributes.

        By default the store operation will non-destructively update the project if
        you have not already retrieved the project from Synapse. If you have already
        retrieved the project from Synapse then the store operation will be destructive
        and will overwrite the project with the current state of this object. See the
        `create_or_update` attribute for more information.

        Arguments:
            failure_strategy: Determines how to handle failures when storing attached
                Files and Folders under this Project and an exception occurs.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The project object.

        Example: Using this method to update the description
            Store the project to Synapse using ID

                project = await Project(id="syn123", description="new").store_async()

            Store the project to Synapse using Name

                project = await Project(name="my_project", description="new").store_async()

        Raises:
            ValueError: If the project name is not set.
        """
        if not self.name and not self.id:
            raise ValueError("Project ID or Name is required")

        if (
            self.create_or_update
            and not self._last_persistent_instance
            and (
                existing_project_id := await get_id(
                    entity=self, synapse_client=synapse_client, failure_strategy=None
                )
            )
            and (
                existing_project := await Project(id=existing_project_id).get_async(
                    synapse_client=synapse_client
                )
            )
        ):
            merge_dataclass_entities(source=existing_project, destination=self)
        trace.get_current_span().set_attributes(
            {
                "synapse.name": self.name or "",
                "synapse.id": self.id or "",
            }
        )
        if self.has_changed:
            loop = asyncio.get_event_loop()
            synapse_project = Synapse_Project(
                id=self.id,
                etag=self.etag,
                name=self.name,
                description=self.description,
                alias=self.alias,
                parentId=self.parent_id,
            )
            delete_none_keys(synapse_project)
            entity = await loop.run_in_executor(
                None,
                lambda: Synapse.get_client(synapse_client=synapse_client).store(
                    obj=synapse_project,
                    set_annotations=False,
                    createOrUpdate=False,
                ),
            )
            self.fill_from_dict(synapse_project=entity, set_annotations=False)

        await store_entity_components(
            root_resource=self,
            failure_strategy=failure_strategy,
            synapse_client=synapse_client,
        )

        self._set_last_persistent_instance()
        Synapse.get_client(synapse_client=synapse_client).logger.debug(
            f"Saved Project {self.name}, id: {self.id}"
        )

        return self

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"Project_Get: ID: {self.id}, Name: {self.name}"
    )
    async def get_async(
        self,
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> "Project":
        """Get the project metadata from Synapse.

        Arguments:
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The project object.

        Example: Using this method
            Retrieve the project from Synapse using ID

                project = await Project(id="syn123").get_async()

            Retrieve the project from Synapse using Name

                project = await Project(name="my_project").get_async()

        Raises:
            ValueError: If the project ID or Name is not set.
            SynapseNotFoundError: If the project is not found in Synapse.
        """
        entity_id = await get_id(entity=self, synapse_client=synapse_client)

        await get_from_entity_factory(
            entity_to_update=self,
            synapse_id_or_path=entity_id,
            synapse_client=synapse_client,
        )

        self._set_last_persistent_instance()
        return self

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"Project_Delete: {self.id}, Name: {self.name}"
    )
    async def delete_async(self, *, synapse_client: Optional[Synapse] = None) -> None:
        """Delete the project from Synapse.

        Arguments:
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            None

        Example: Using this method
            Delete the project from Synapse using ID

                await Project(id="syn123").delete_async()

            Delete the project from Synapse using Name

                await Project(name="my_project").delete_async()

        Raises:
            ValueError: If the project ID or Name is not set.
            SynapseNotFoundError: If the project is not found in Synapse.
        """
        entity_id = await get_id(entity=self, synapse_client=synapse_client)

        loop = asyncio.get_event_loop()
        await loop.run_in_executor(
            None,
            lambda: Synapse.get_client(synapse_client=synapse_client).delete(
                obj=entity_id,
            ),
        )

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"Project_Copy: {self.id}"
    )
    async def copy_async(
        self,
        destination_id: str,
        copy_annotations: bool = True,
        copy_wiki: bool = True,
        exclude_types: Optional[List[str]] = None,
        file_update_existing: bool = False,
        file_copy_activity: Union[str, None] = "traceback",
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> "Project":
        """
        You must have already created the Project you will be copying to. It will have
        it's own Synapse ID and unique name that you will use as the destination_id.


        Copy the project to another Synapse project. This will recursively copy all
        Tables, Links, Files, and Folders within the project.

        Arguments:
            destination_id: Synapse ID of a project to copy to.
            copy_annotations: True to copy the annotations.
            copy_wiki: True to copy the wiki pages.
            exclude_types: A list of entity types ['file', 'table', 'link'] which
                determines which entity types to not copy. Defaults to an empty list.
            file_update_existing: When the destination has a file that has the same
                name, users can choose to update that file.
            file_copy_activity: Has three options to set the activity of the copied file:

                    - traceback: Creates a copy of the source files Activity.
                    - existing: Link to the source file's original Activity (if it exists)
                    - None: No activity is set
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The copied project object.

        Example: Using this function
            Assuming you have a project with the ID "syn123" and you want to copy it to a
            project with the ID "syn456":

                new_instance = await Project(id="syn123").copy_async(destination_id="syn456")

            Copy the project but do not persist annotations:

                new_instance = await Project(id="syn123").copy_async(destination_id="syn456", copy_annotations=False)

        Raises:
            ValueError: If the project does not have an ID and destination_id to copy.
        """
        if not self.id or not destination_id:
            raise ValueError("The project must have an ID and destination_id to copy.")

        loop = asyncio.get_event_loop()

        syn = Synapse.get_client(synapse_client=synapse_client)
        source_and_destination = await loop.run_in_executor(
            None,
            lambda: copy(
                syn=syn,
                entity=self.id,
                destinationId=destination_id,
                excludeTypes=exclude_types or [],
                skipCopyAnnotations=not copy_annotations,
                skipCopyWikiPage=not copy_wiki,
                updateExisting=file_update_existing,
                setProvenance=file_copy_activity,
            ),
        )

        new_project_id = source_and_destination.get(self.id, None)
        if not new_project_id:
            raise SynapseError("Failed to copy project.")
        project_copy = await (
            await Project(id=new_project_id).get_async(synapse_client=syn)
        ).sync_from_synapse_async(
            download_file=False,
            synapse_client=synapse_client,
        )
        syn.logger.debug(f"Copied from project {self.id} to {destination_id}")
        return project_copy

Functions

get_async(*, synapse_client=None) async

Get the project metadata from Synapse.

PARAMETER DESCRIPTION
synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Project

The project object.

Using this method

Retrieve the project from Synapse using ID

project = await Project(id="syn123").get_async()

Retrieve the project from Synapse using Name

project = await Project(name="my_project").get_async()
RAISES DESCRIPTION
ValueError

If the project ID or Name is not set.

SynapseNotFoundError

If the project is not found in Synapse.

Source code in synapseclient/models/project.py
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"Project_Get: ID: {self.id}, Name: {self.name}"
)
async def get_async(
    self,
    *,
    synapse_client: Optional[Synapse] = None,
) -> "Project":
    """Get the project metadata from Synapse.

    Arguments:
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The project object.

    Example: Using this method
        Retrieve the project from Synapse using ID

            project = await Project(id="syn123").get_async()

        Retrieve the project from Synapse using Name

            project = await Project(name="my_project").get_async()

    Raises:
        ValueError: If the project ID or Name is not set.
        SynapseNotFoundError: If the project is not found in Synapse.
    """
    entity_id = await get_id(entity=self, synapse_client=synapse_client)

    await get_from_entity_factory(
        entity_to_update=self,
        synapse_id_or_path=entity_id,
        synapse_client=synapse_client,
    )

    self._set_last_persistent_instance()
    return self

store_async(failure_strategy=FailureStrategy.LOG_EXCEPTION, *, synapse_client=None) async

Store project, files, and folders to synapse. If you have any files or folders attached to this project they will be stored as well. You may attach files and folders to this project by setting the files and folders attributes.

By default the store operation will non-destructively update the project if you have not already retrieved the project from Synapse. If you have already retrieved the project from Synapse then the store operation will be destructive and will overwrite the project with the current state of this object. See the create_or_update attribute for more information.

PARAMETER DESCRIPTION
failure_strategy

Determines how to handle failures when storing attached Files and Folders under this Project and an exception occurs.

TYPE: FailureStrategy DEFAULT: LOG_EXCEPTION

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Project

The project object.

Using this method to update the description

Store the project to Synapse using ID

project = await Project(id="syn123", description="new").store_async()

Store the project to Synapse using Name

project = await Project(name="my_project", description="new").store_async()
RAISES DESCRIPTION
ValueError

If the project name is not set.

Source code in synapseclient/models/project.py
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"Project_Store: ID: {self.id}, Name: {self.name}"
)
async def store_async(
    self,
    failure_strategy: FailureStrategy = FailureStrategy.LOG_EXCEPTION,
    *,
    synapse_client: Optional[Synapse] = None,
) -> "Project":
    """
    Store project, files, and folders to synapse. If you have any files or folders
    attached to this project they will be stored as well. You may attach files
    and folders to this project by setting the `files` and `folders` attributes.

    By default the store operation will non-destructively update the project if
    you have not already retrieved the project from Synapse. If you have already
    retrieved the project from Synapse then the store operation will be destructive
    and will overwrite the project with the current state of this object. See the
    `create_or_update` attribute for more information.

    Arguments:
        failure_strategy: Determines how to handle failures when storing attached
            Files and Folders under this Project and an exception occurs.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The project object.

    Example: Using this method to update the description
        Store the project to Synapse using ID

            project = await Project(id="syn123", description="new").store_async()

        Store the project to Synapse using Name

            project = await Project(name="my_project", description="new").store_async()

    Raises:
        ValueError: If the project name is not set.
    """
    if not self.name and not self.id:
        raise ValueError("Project ID or Name is required")

    if (
        self.create_or_update
        and not self._last_persistent_instance
        and (
            existing_project_id := await get_id(
                entity=self, synapse_client=synapse_client, failure_strategy=None
            )
        )
        and (
            existing_project := await Project(id=existing_project_id).get_async(
                synapse_client=synapse_client
            )
        )
    ):
        merge_dataclass_entities(source=existing_project, destination=self)
    trace.get_current_span().set_attributes(
        {
            "synapse.name": self.name or "",
            "synapse.id": self.id or "",
        }
    )
    if self.has_changed:
        loop = asyncio.get_event_loop()
        synapse_project = Synapse_Project(
            id=self.id,
            etag=self.etag,
            name=self.name,
            description=self.description,
            alias=self.alias,
            parentId=self.parent_id,
        )
        delete_none_keys(synapse_project)
        entity = await loop.run_in_executor(
            None,
            lambda: Synapse.get_client(synapse_client=synapse_client).store(
                obj=synapse_project,
                set_annotations=False,
                createOrUpdate=False,
            ),
        )
        self.fill_from_dict(synapse_project=entity, set_annotations=False)

    await store_entity_components(
        root_resource=self,
        failure_strategy=failure_strategy,
        synapse_client=synapse_client,
    )

    self._set_last_persistent_instance()
    Synapse.get_client(synapse_client=synapse_client).logger.debug(
        f"Saved Project {self.name}, id: {self.id}"
    )

    return self

delete_async(*, synapse_client=None) async

Delete the project from Synapse.

PARAMETER DESCRIPTION
synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
None

None

Using this method

Delete the project from Synapse using ID

await Project(id="syn123").delete_async()

Delete the project from Synapse using Name

await Project(name="my_project").delete_async()
RAISES DESCRIPTION
ValueError

If the project ID or Name is not set.

SynapseNotFoundError

If the project is not found in Synapse.

Source code in synapseclient/models/project.py
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"Project_Delete: {self.id}, Name: {self.name}"
)
async def delete_async(self, *, synapse_client: Optional[Synapse] = None) -> None:
    """Delete the project from Synapse.

    Arguments:
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        None

    Example: Using this method
        Delete the project from Synapse using ID

            await Project(id="syn123").delete_async()

        Delete the project from Synapse using Name

            await Project(name="my_project").delete_async()

    Raises:
        ValueError: If the project ID or Name is not set.
        SynapseNotFoundError: If the project is not found in Synapse.
    """
    entity_id = await get_id(entity=self, synapse_client=synapse_client)

    loop = asyncio.get_event_loop()
    await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).delete(
            obj=entity_id,
        ),
    )

sync_from_synapse_async(path=None, recursive=True, download_file=True, if_collision=COLLISION_OVERWRITE_LOCAL, failure_strategy=FailureStrategy.LOG_EXCEPTION, include_activity=True, follow_link=False, link_hops=1, *, synapse_client=None) async

Sync this container and all possible sub-folders from Synapse. By default this will download the files that are found and it will populate the files and folders attributes with the found files and folders. If you only want to retrieve the full tree of metadata about your container specify download_file as False.

This works similar to synapseutils.syncFromSynapse, however, this does not currently support the writing of data to a manifest TSV file. This will be a future enhancement.

Only Files and Folders are supported at this time to be synced from synapse.

PARAMETER DESCRIPTION
path

An optional path where the file hierarchy will be reproduced. If not specified the files will by default be placed in the synapseCache.

TYPE: Optional[str] DEFAULT: None

recursive

Whether or not to recursively get the entire hierarchy of the folder and sub-folders.

TYPE: bool DEFAULT: True

download_file

Whether to download the files found or not.

TYPE: bool DEFAULT: True

if_collision

Determines how to handle file collisions. May be

  • overwrite.local
  • keep.local
  • keep.both

TYPE: str DEFAULT: COLLISION_OVERWRITE_LOCAL

failure_strategy

Determines how to handle failures when retrieving children under this Folder and an exception occurs.

TYPE: FailureStrategy DEFAULT: LOG_EXCEPTION

include_activity

Whether to include the activity of the files.

TYPE: bool DEFAULT: True

follow_link

Whether to follow a link entity or not. Links can be used to point at other Synapse entities.

TYPE: bool DEFAULT: False

link_hops

The number of hops to follow the link. A number of 1 is used to prevent circular references. There is nothing in place to prevent infinite loops. Be careful if setting this above 1.

TYPE: int DEFAULT: 1

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Self

The object that was called on. This will be the same object that was called on to start the sync.

Using this function

Suppose I want to walk the immediate children of a folder without downloading the files:

from synapseclient import Synapse
from synapseclient.models import Folder

syn = Synapse()
syn.login()

my_folder = Folder(id="syn12345")
await my_folder.sync_from_synapse_async(download_file=False, recursive=False)

for folder in my_folder.folders:
    print(folder.name)

for file in my_folder.files:
    print(file.name)

Suppose I want to download the immediate children of a folder:

from synapseclient import Synapse
from synapseclient.models import Folder

syn = Synapse()
syn.login()

my_folder = Folder(id="syn12345")
await my_folder.sync_from_synapse_async(path="/path/to/folder", recursive=False)

for folder in my_folder.folders:
    print(folder.name)

for file in my_folder.files:
    print(file.name)

Suppose I want to download the immediate all children of a Project and all sub-folders and files:

from synapseclient import Synapse
from synapseclient.models import Project

syn = Synapse()
syn.login()

my_project = Project(id="syn12345")
await my_project.sync_from_synapse_async(path="/path/to/folder")
RAISES DESCRIPTION
ValueError

If the folder does not have an id set.

A sequence diagram for this method is as follows:

sequenceDiagram
    autonumber
    participant project_or_folder
    activate project_or_folder
    project_or_folder->>sync_from_synapse: Recursive search and download files
    activate sync_from_synapse
        opt Current instance not retrieved from Synapse
            sync_from_synapse->>project_or_folder: Call `.get()` method
            project_or_folder-->>sync_from_synapse: .
        end

        loop For each return of the generator
            sync_from_synapse->>client: call `.getChildren()` method
            client-->>sync_from_synapse: .
            note over sync_from_synapse: Append to a running list
        end

        loop For each child
            note over sync_from_synapse: Create all `pending_tasks` at current depth

            alt Child is File
                note over sync_from_synapse: Append `file.get()` method
            else Child is Folder
                note over sync_from_synapse: Append `folder.get()` method
                alt Recursive is True
                    note over sync_from_synapse: Append `folder.sync_from_synapse()` method
                end
            else Child is Link and hops > 0
                note over sync_from_synapse: Append task to follow link
            end
        end

        loop For each task in pending_tasks
            par `file.get()`
                sync_from_synapse->>File: Retrieve File metadata and Optionally download
                File->>client: `.get()`
                client-->>File: .
                File-->>sync_from_synapse: .
            and `folder.get()`
                sync_from_synapse->>Folder: Retrieve Folder metadataa
                Folder->>client: `.get()`
                client-->>Folder: .
                Folder-->>sync_from_synapse: .
            and `folder.sync_from_synapse_async()`
                note over sync_from_synapse: This is a recursive call to `sync_from_synapse`
                sync_from_synapse->>sync_from_synapse: Recursive call to `.sync_from_synapse_async()`
            and `_follow_link`
                sync_from_synapse ->>client: call `get_entity_id_bundle2` function
                client-->sync_from_synapse: .
                note over sync_from_synapse: Do nothing if not link
                note over sync_from_synapse: call `_create_task_for_child` and execute
            end
        end

    deactivate sync_from_synapse
    deactivate project_or_folder
Source code in synapseclient/models/mixins/storable_container.py
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"{self.__class__.__name__}_sync_from_synapse: {self.id}"
)
async def sync_from_synapse_async(
    self: Self,
    path: Optional[str] = None,
    recursive: bool = True,
    download_file: bool = True,
    if_collision: str = COLLISION_OVERWRITE_LOCAL,
    failure_strategy: FailureStrategy = FailureStrategy.LOG_EXCEPTION,
    include_activity: bool = True,
    follow_link: bool = False,
    link_hops: int = 1,
    *,
    synapse_client: Optional[Synapse] = None,
) -> Self:
    """
    Sync this container and all possible sub-folders from Synapse. By default this
    will download the files that are found and it will populate the
    `files` and `folders` attributes with the found files and folders. If you only
    want to retrieve the full tree of metadata about your container specify
    `download_file` as False.

    This works similar to [synapseutils.syncFromSynapse][], however, this does not
    currently support the writing of data to a manifest TSV file. This will be a
    future enhancement.

    Only Files and Folders are supported at this time to be synced from synapse.

    Arguments:
        path: An optional path where the file hierarchy will be reproduced. If not
            specified the files will by default be placed in the synapseCache.
        recursive: Whether or not to recursively get the entire hierarchy of the
            folder and sub-folders.
        download_file: Whether to download the files found or not.
        if_collision: Determines how to handle file collisions. May be

            - `overwrite.local`
            - `keep.local`
            - `keep.both`
        failure_strategy: Determines how to handle failures when retrieving children
            under this Folder and an exception occurs.
        include_activity: Whether to include the activity of the files.
        follow_link: Whether to follow a link entity or not. Links can be used to
            point at other Synapse entities.
        link_hops: The number of hops to follow the link. A number of 1 is used to
            prevent circular references. There is nothing in place to prevent
            infinite loops. Be careful if setting this above 1.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The object that was called on. This will be the same object that was called on
            to start the sync.

    Example: Using this function
        Suppose I want to walk the immediate children of a folder without downloading the files:

            from synapseclient import Synapse
            from synapseclient.models import Folder

            syn = Synapse()
            syn.login()

            my_folder = Folder(id="syn12345")
            await my_folder.sync_from_synapse_async(download_file=False, recursive=False)

            for folder in my_folder.folders:
                print(folder.name)

            for file in my_folder.files:
                print(file.name)

        Suppose I want to download the immediate children of a folder:

            from synapseclient import Synapse
            from synapseclient.models import Folder

            syn = Synapse()
            syn.login()

            my_folder = Folder(id="syn12345")
            await my_folder.sync_from_synapse_async(path="/path/to/folder", recursive=False)

            for folder in my_folder.folders:
                print(folder.name)

            for file in my_folder.files:
                print(file.name)


        Suppose I want to download the immediate all children of a Project and all sub-folders and files:

            from synapseclient import Synapse
            from synapseclient.models import Project

            syn = Synapse()
            syn.login()

            my_project = Project(id="syn12345")
            await my_project.sync_from_synapse_async(path="/path/to/folder")


    Raises:
        ValueError: If the folder does not have an id set.


    A sequence diagram for this method is as follows:

    ```mermaid
    sequenceDiagram
        autonumber
        participant project_or_folder
        activate project_or_folder
        project_or_folder->>sync_from_synapse: Recursive search and download files
        activate sync_from_synapse
            opt Current instance not retrieved from Synapse
                sync_from_synapse->>project_or_folder: Call `.get()` method
                project_or_folder-->>sync_from_synapse: .
            end

            loop For each return of the generator
                sync_from_synapse->>client: call `.getChildren()` method
                client-->>sync_from_synapse: .
                note over sync_from_synapse: Append to a running list
            end

            loop For each child
                note over sync_from_synapse: Create all `pending_tasks` at current depth

                alt Child is File
                    note over sync_from_synapse: Append `file.get()` method
                else Child is Folder
                    note over sync_from_synapse: Append `folder.get()` method
                    alt Recursive is True
                        note over sync_from_synapse: Append `folder.sync_from_synapse()` method
                    end
                else Child is Link and hops > 0
                    note over sync_from_synapse: Append task to follow link
                end
            end

            loop For each task in pending_tasks
                par `file.get()`
                    sync_from_synapse->>File: Retrieve File metadata and Optionally download
                    File->>client: `.get()`
                    client-->>File: .
                    File-->>sync_from_synapse: .
                and `folder.get()`
                    sync_from_synapse->>Folder: Retrieve Folder metadataa
                    Folder->>client: `.get()`
                    client-->>Folder: .
                    Folder-->>sync_from_synapse: .
                and `folder.sync_from_synapse_async()`
                    note over sync_from_synapse: This is a recursive call to `sync_from_synapse`
                    sync_from_synapse->>sync_from_synapse: Recursive call to `.sync_from_synapse_async()`
                and `_follow_link`
                    sync_from_synapse ->>client: call `get_entity_id_bundle2` function
                    client-->sync_from_synapse: .
                    note over sync_from_synapse: Do nothing if not link
                    note over sync_from_synapse: call `_create_task_for_child` and execute
                end
            end

        deactivate sync_from_synapse
        deactivate project_or_folder
    ```

    """
    if not self._last_persistent_instance:
        await self.get_async(synapse_client=synapse_client)
    Synapse.get_client(synapse_client=synapse_client).logger.info(
        f"Syncing {self.__class__.__name__} ({self.id}:{self.name}) from Synapse."
    )
    path = os.path.expanduser(path) if path else None

    loop = asyncio.get_event_loop()
    children = await loop.run_in_executor(
        None,
        lambda: self._retrieve_children(
            follow_link=follow_link,
            synapse_client=synapse_client,
        ),
    )

    pending_tasks = []
    self.folders = []
    self.files = []

    for child in children:
        pending_tasks.extend(
            self._create_task_for_child(
                child=child,
                recursive=recursive,
                path=path,
                download_file=download_file,
                if_collision=if_collision,
                failure_strategy=failure_strategy,
                synapse_client=synapse_client,
                include_activity=include_activity,
                follow_link=follow_link,
                link_hops=link_hops,
            )
        )

    for task in asyncio.as_completed(pending_tasks):
        result = await task
        self._resolve_sync_from_synapse_result(
            result=result,
            failure_strategy=failure_strategy,
            synapse_client=synapse_client,
        )
    return self

get_permissions_async(*, synapse_client=None) async

Get the permissions that the caller has on an Entity.

PARAMETER DESCRIPTION
synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Permissions

A Permissions object

Using this function:

Getting permissions for a Synapse Entity

permissions = await File(id="syn123").get_permissions_async()

Getting access types list from the Permissions object

permissions.access_types
Source code in synapseclient/models/mixins/access_control.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
async def get_permissions_async(
    self,
    *,
    synapse_client: Optional[Synapse] = None,
) -> "Permissions":
    """
    Get the [permissions][synapseclient.core.models.permission.Permissions]
    that the caller has on an Entity.

    Arguments:
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        A Permissions object


    Example: Using this function:
        Getting permissions for a Synapse Entity

            permissions = await File(id="syn123").get_permissions_async()

        Getting access types list from the Permissions object

            permissions.access_types
    """
    loop = asyncio.get_event_loop()

    return await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).get_permissions(
            entity=self.id
        ),
    )

get_acl_async(principal_id=None, *, synapse_client=None) async

Get the ACL that a user or group has on an Entity.

PARAMETER DESCRIPTION
principal_id

Identifier of a user or group (defaults to PUBLIC users)

TYPE: int DEFAULT: None

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
List[str]

An array containing some combination of ['READ', 'UPDATE', 'CREATE', 'DELETE', 'DOWNLOAD', 'MODERATE', 'CHANGE_PERMISSIONS', 'CHANGE_SETTINGS'] or an empty array

Source code in synapseclient/models/mixins/access_control.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
async def get_acl_async(
    self, principal_id: int = None, *, synapse_client: Optional[Synapse] = None
) -> List[str]:
    """
    Get the [ACL][synapseclient.core.models.permission.Permissions.access_types]
    that a user or group has on an Entity.

    Arguments:
        principal_id: Identifier of a user or group (defaults to PUBLIC users)
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        An array containing some combination of
            ['READ', 'UPDATE', 'CREATE', 'DELETE', 'DOWNLOAD', 'MODERATE',
            'CHANGE_PERMISSIONS', 'CHANGE_SETTINGS']
            or an empty array
    """
    loop = asyncio.get_event_loop()

    return await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).get_acl(
            entity=self.id, principal_id=principal_id
        ),
    )

set_permissions_async(principal_id=None, access_type=None, modify_benefactor=False, warn_if_inherits=True, overwrite=True, *, synapse_client=None) async

Sets permission that a user or group has on an Entity. An Entity may have its own ACL or inherit its ACL from a benefactor.

PARAMETER DESCRIPTION
principal_id

Identifier of a user or group. 273948 is for all registered Synapse users and 273949 is for public access. None implies public access.

TYPE: int DEFAULT: None

access_type

Type of permission to be granted. One or more of CREATE, READ, DOWNLOAD, UPDATE, DELETE, CHANGE_PERMISSIONS.

Defaults to ['READ', 'DOWNLOAD']

TYPE: List[str] DEFAULT: None

modify_benefactor

Set as True when modifying a benefactor's ACL

TYPE: bool DEFAULT: False

warn_if_inherits

Set as False, when creating a new ACL. Trying to modify the ACL of an Entity that inherits its ACL will result in a warning

TYPE: bool DEFAULT: True

overwrite

By default this function overwrites existing permissions for the specified user. Set this flag to False to add new permissions non-destructively.

TYPE: bool DEFAULT: True

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Dict[str, Union[str, list]]

An Access Control List object

Setting permissions

Grant all registered users download access

await File(id="syn123").set_permissions_async(principal_id=273948, access_type=['READ','DOWNLOAD'])

Grant the public view access

await File(id="syn123").set_permissions_async(principal_id=273949, access_type=['READ'])
Source code in synapseclient/models/mixins/access_control.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
async def set_permissions_async(
    self,
    principal_id: int = None,
    access_type: List[str] = None,
    modify_benefactor: bool = False,
    warn_if_inherits: bool = True,
    overwrite: bool = True,
    *,
    synapse_client: Optional[Synapse] = None,
) -> Dict[str, Union[str, list]]:
    """
    Sets permission that a user or group has on an Entity.
    An Entity may have its own ACL or inherit its ACL from a benefactor.

    Arguments:
        principal_id: Identifier of a user or group. `273948` is for all
            registered Synapse users and `273949` is for public access.
            None implies public access.
        access_type: Type of permission to be granted. One or more of CREATE,
            READ, DOWNLOAD, UPDATE, DELETE, CHANGE_PERMISSIONS.

            **Defaults to ['READ', 'DOWNLOAD']**
        modify_benefactor: Set as True when modifying a benefactor's ACL
        warn_if_inherits: Set as False, when creating a new ACL. Trying to modify
            the ACL of an Entity that inherits its ACL will result in a warning
        overwrite: By default this function overwrites existing permissions for
            the specified user. Set this flag to False to add new permissions
            non-destructively.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        An Access Control List object

    Example: Setting permissions
        Grant all registered users download access

            await File(id="syn123").set_permissions_async(principal_id=273948, access_type=['READ','DOWNLOAD'])

        Grant the public view access

            await File(id="syn123").set_permissions_async(principal_id=273949, access_type=['READ'])
    """
    if access_type is None:
        access_type = ["READ", "DOWNLOAD"]
    loop = asyncio.get_event_loop()

    return await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).setPermissions(
            entity=self.id,
            principalId=principal_id,
            accessType=access_type,
            modify_benefactor=modify_benefactor,
            warn_if_inherits=warn_if_inherits,
            overwrite=overwrite,
        ),
    )

synapseclient.models.Folder dataclass

Bases: FolderSynchronousProtocol, AccessControllable, StorableContainer

Folder is a hierarchical container for organizing data in Synapse.

ATTRIBUTE DESCRIPTION
id

The unique immutable ID for this folder. A new ID will be generated for new Folders. Once issued, this ID is guaranteed to never change or be re-issued.

TYPE: Optional[str]

name

The name of this folder. Must be 256 characters or less. Names may only contain: letters, numbers, spaces, underscores, hyphens, periods, plus signs, apostrophes, and parentheses.

TYPE: Optional[str]

parent_id

The ID of the Project or Folder that is the parent of this Folder.

TYPE: Optional[str]

description

The description of this entity. Must be 1000 characters or less.

TYPE: Optional[str]

etag

(Read Only) Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle concurrent updates. Since the E-Tag changes every time an entity is updated it is used to detect when a client's current representation of an entity is out-of-date.

TYPE: Optional[str]

created_on

(Read Only) The date this entity was created.

TYPE: Optional[str]

modified_on

(Read Only) The date this entity was last modified.

TYPE: Optional[str]

created_by

(Read Only) The ID of the user that created this entity.

TYPE: Optional[str]

modified_by

(Read Only) The ID of the user that last modified this entity.

TYPE: Optional[str]

files

Files that exist within this folder.

TYPE: List[File]

folders

Folders that exist within this folder.

TYPE: List[Folder]

annotations

Additional metadata associated with the folder. The key is the name of your desired annotations. The value is an object containing a list of values (use empty list to represent no values for key) and the value type associated with all values in the list. To remove all annotations set this to an empty dict {} or None and store the entity.

TYPE: Optional[Dict[str, Union[List[str], List[bool], List[float], List[int], List[date], List[datetime]]]]

create_or_update

(Store only) Indicates whether the method should automatically perform an update if the resource conflicts with an existing Synapse object. When True this means that any changes to the resource will be non-destructive.

This boolean is ignored if you've already stored or retrieved the resource from Synapse for this instance at least once. Any changes to the resource will be destructive in this case. For example if you want to delete the content for a field you will need to call .get() and then modify the field.

TYPE: bool

Source code in synapseclient/models/folder.py
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
@dataclass()
@async_to_sync
class Folder(FolderSynchronousProtocol, AccessControllable, StorableContainer):
    """Folder is a hierarchical container for organizing data in Synapse.

    Attributes:
        id: The unique immutable ID for this folder. A new ID will be generated for new
            Folders. Once issued, this ID is guaranteed to never change or be re-issued.
        name: The name of this folder. Must be 256 characters or less. Names may only
            contain: letters, numbers, spaces, underscores, hyphens, periods, plus
            signs, apostrophes, and parentheses.
        parent_id: The ID of the Project or Folder that is the parent of this Folder.
        description: The description of this entity. Must be 1000 characters or less.
        etag: (Read Only)
            Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle
            concurrent updates. Since the E-Tag changes every time an entity is updated
            it is used to detect when a client's current representation of an entity is
            out-of-date.
        created_on: (Read Only) The date this entity was created.
        modified_on: (Read Only) The date this entity was last modified.
        created_by: (Read Only) The ID of the user that created this entity.
        modified_by: (Read Only) The ID of the user that last modified this entity.
        files: Files that exist within this folder.
        folders: Folders that exist within this folder.
        annotations: Additional metadata associated with the folder. The key is the name
            of your desired annotations. The value is an object containing a list of
            values (use empty list to represent no values for key) and the value type
            associated with all values in the list. To remove all annotations set this
            to an empty dict `{}` or None and store the entity.
        create_or_update: (Store only) Indicates whether the method should
            automatically perform an update if the resource conflicts with an existing
            Synapse object. When True this means that any changes to the resource will
            be non-destructive.

            This boolean is ignored if you've already stored or retrieved the resource
            from Synapse for this instance at least once. Any changes to the resource
            will be destructive in this case. For example if you want to delete the
            content for a field you will need to call `.get()` and then modify the
            field.
    """

    id: Optional[str] = None
    """The unique immutable ID for this folder. A new ID will be generated for new
    Folders. Once issued, this ID is guaranteed to never change or be re-issued"""

    name: Optional[str] = None
    """The name of this folder. Must be 256 characters or less. Names may only contain:
    letters, numbers, spaces, underscores, hyphens, periods, plus signs, apostrophes,
    and parentheses"""

    parent_id: Optional[str] = None
    """The ID of the Project or Folder that is the parent of this Folder."""

    description: Optional[str] = None
    """The description of this entity. Must be 1000 characters or less."""

    etag: Optional[str] = None
    """(Read Only)
    Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle
    concurrent updates. Since the E-Tag changes every time an entity is updated it
    is used to detect when a client's current representation of an entity
    is out-of-date."""

    created_on: Optional[str] = None
    """(Read Only) The date this entity was created."""

    modified_on: Optional[str] = None
    """(Read Only) The date this entity was last modified."""

    created_by: Optional[str] = None
    """(Read Only) The ID of the user that created this entity."""

    modified_by: Optional[str] = None
    """(Read Only) The ID of the user that last modified this entity."""

    files: List["File"] = field(default_factory=list, compare=False)
    """Files that exist within this folder."""

    folders: List["Folder"] = field(default_factory=list, compare=False)
    """Folders that exist within this folder."""

    annotations: Optional[
        Dict[
            str,
            Union[
                List[str],
                List[bool],
                List[float],
                List[int],
                List[date],
                List[datetime],
            ],
        ]
    ] = field(default_factory=dict, compare=False)
    """Additional metadata associated with the folder. The key is the name of your
    desired annotations. The value is an object containing a list of values
    (use empty list to represent no values for key) and the value type associated with
    all values in the list. To remove all annotations set this to an empty dict `{}`
    or None and store the entity."""

    is_restricted: bool = field(default=False, repr=False)
    """
    (Store only)

    If set to true, an email will be sent to the Synapse access control team to start
    the process of adding terms-of-use or review board approval for this entity.
    You will be contacted with regards to the specific data being restricted and the
    requirements of access.
    """

    create_or_update: bool = field(default=True, repr=False)
    """
    (Store only)

    Indicates whether the method should automatically perform an update if the resource
    conflicts with an existing Synapse object. When True this means that any changes
    to the resource will be non-destructive.

    This boolean is ignored if you've already stored or retrieved the resource from
    Synapse for this instance at least once. Any changes to the resource will be
    destructive in this case. For example if you want to delete the content for a field
    you will need to call `.get()` and then modify the field.
    """

    _last_persistent_instance: Optional["Folder"] = field(
        default=None, repr=False, compare=False
    )
    """The last persistent instance of this object. This is used to determine if the
    object has been changed and needs to be updated in Synapse."""

    @property
    def has_changed(self) -> bool:
        """Determines if the object has been changed and needs to be updated in Synapse."""
        return (
            not self._last_persistent_instance or self._last_persistent_instance != self
        )

    def _set_last_persistent_instance(self) -> None:
        """Stash the last time this object interacted with Synapse. This is used to
        determine if the object has been changed and needs to be updated in Synapse."""
        del self._last_persistent_instance
        self._last_persistent_instance = replace(self)
        self._last_persistent_instance.annotations = (
            deepcopy(self.annotations) if self.annotations else {}
        )

    def fill_from_dict(
        self, synapse_folder: Synapse_Folder, set_annotations: bool = True
    ) -> "Folder":
        """
        Converts a response from the REST API into this dataclass.

        Arguments:
            synapse_file: The response from the REST API.
            set_annotations: Whether to set the annotations from the response.

        Returns:
            The Folder object.
        """
        self.id = synapse_folder.get("id", None)
        self.name = synapse_folder.get("name", None)
        self.parent_id = synapse_folder.get("parentId", None)
        self.description = synapse_folder.get("description", None)
        self.etag = synapse_folder.get("etag", None)
        self.created_on = synapse_folder.get("createdOn", None)
        self.modified_on = synapse_folder.get("modifiedOn", None)
        self.created_by = synapse_folder.get("createdBy", None)
        self.modified_by = synapse_folder.get("modifiedBy", None)
        if set_annotations:
            self.annotations = Annotations.from_dict(
                synapse_folder.get("annotations", None)
            )
        return self

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"Folder_Store: {self.name}"
    )
    async def store_async(
        self,
        parent: Optional[Union["Folder", "Project"]] = None,
        failure_strategy: FailureStrategy = FailureStrategy.LOG_EXCEPTION,
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> "Folder":
        """Store folders and files to synapse. If you have any files or folders attached
        to this folder they will be stored as well. You may attach files and folders
        to this folder by setting the `files` and `folders` attributes.

        By default the store operation will non-destructively update the folder if
        you have not already retrieved the folder from Synapse. If you have already
        retrieved the folder from Synapse then the store operation will be destructive
        and will overwrite the folder with the current state of this object. See the
        `create_or_update` attribute for more information.

        Arguments:
            parent: The parent folder or project to store the folder in.
            failure_strategy: Determines how to handle failures when storing attached
                Files and Folders under this Folder and an exception occurs.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The folder object.

        Raises:
            ValueError: If the folder does not have an id or a
                (name and (`parent_id` or parent with an id)) set.
        """
        parent_id = parent.id if parent else self.parent_id
        if not (self.id or (self.name and parent_id)):
            raise ValueError(
                "The folder must have an id or a "
                "(name and (`parent_id` or parent with an id)) set."
            )
        self.parent_id = parent_id

        if (
            self.create_or_update
            and not self._last_persistent_instance
            and (
                existing_folder_id := await get_id(
                    entity=self, failure_strategy=None, synapse_client=synapse_client
                )
            )
            and (
                existing_folder := await Folder(id=existing_folder_id).get_async(
                    synapse_client=synapse_client
                )
            )
        ):
            merge_dataclass_entities(source=existing_folder, destination=self)
        trace.get_current_span().set_attributes(
            {
                "synapse.name": self.name or "",
                "synapse.id": self.id or "",
            }
        )
        if self.has_changed:
            loop = asyncio.get_event_loop()
            synapse_folder = Synapse_Folder(
                id=self.id,
                name=self.name,
                parent=parent_id,
                etag=self.etag,
                description=self.description,
            )
            delete_none_keys(synapse_folder)
            entity = await loop.run_in_executor(
                None,
                lambda: Synapse.get_client(synapse_client=synapse_client).store(
                    obj=synapse_folder,
                    set_annotations=False,
                    isRestricted=self.is_restricted,
                    createOrUpdate=False,
                ),
            )

            self.fill_from_dict(synapse_folder=entity, set_annotations=False)

        await store_entity_components(
            root_resource=self,
            failure_strategy=failure_strategy,
            synapse_client=synapse_client,
        )
        self._set_last_persistent_instance()
        Synapse.get_client(synapse_client=synapse_client).logger.debug(
            f"Saved Folder {self.name}, id: {self.id}: parent: {self.parent_id}"
        )

        return self

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"Folder_Get: {self.id}"
    )
    async def get_async(
        self,
        parent: Optional[Union["Folder", "Project"]] = None,
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> "Folder":
        """Get the folder metadata from Synapse. You are able to find a folder by
        either the id or the name and parent_id.

        Arguments:
            parent: The parent folder or project this folder exists under.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The folder object.

        Raises:
            ValueError: If the folder does not have an id or a
                (name and (`parent_id` or parent with an id)) set.
        """
        parent_id = parent.id if parent else self.parent_id
        if not (self.id or (self.name and parent_id)):
            raise ValueError(
                "The folder must have an id or a "
                "(name and (`parent_id` or parent with an id)) set."
            )
        self.parent_id = parent_id

        entity_id = await get_id(entity=self, synapse_client=synapse_client)

        await get_from_entity_factory(
            entity_to_update=self,
            synapse_id_or_path=entity_id,
            synapse_client=synapse_client,
        )

        self._set_last_persistent_instance()
        return self

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"Folder_Delete: {self.id}"
    )
    async def delete_async(self, *, synapse_client: Optional[Synapse] = None) -> None:
        """Delete the folder from Synapse by its id.

        Arguments:
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            None

        Raises:
            ValueError: If the folder does not have an id set.
        """
        if not self.id:
            raise ValueError("The folder must have an id set.")
        loop = asyncio.get_event_loop()
        await loop.run_in_executor(
            None,
            lambda: Synapse.get_client(synapse_client=synapse_client).delete(
                obj=self.id,
            ),
        )

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"Folder_Copy: {self.id}"
    )
    async def copy_async(
        self,
        parent_id: str,
        copy_annotations: bool = True,
        exclude_types: Optional[List[str]] = None,
        file_update_existing: bool = False,
        file_copy_activity: Union[str, None] = "traceback",
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> "Folder":
        """
        Copy the folder to another Synapse location. This will recursively copy all
        Tables, Links, Files, and Folders within the folder.

        Arguments:
            parent_id: Synapse ID of a folder/project that the copied entity is
                being copied to
            copy_annotations: True to copy the annotations.
            exclude_types: A list of entity types ['file', 'table', 'link'] which
                determines which entity types to not copy. Defaults to an empty list.
            file_update_existing: When the destination has a file that has the same name,
                users can choose to update that file.
            file_copy_activity: Has three options to set the activity of the copied file:

                    - traceback: Creates a copy of the source files Activity.
                    - existing: Link to the source file's original Activity (if it exists)
                    - None: No activity is set
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The copied folder object.

        Example: Using this function
            Assuming you have a folder with the ID "syn123" and you want to copy it to a
            project with the ID "syn456":

                new_folder_instance = await Folder(id="syn123").copy_async(parent_id="syn456")

            Copy the folder but do not persist annotations:

                new_folder_instance = await Folder(id="syn123").copy_async(parent_id="syn456", copy_annotations=False)

        Raises:
            ValueError: If the folder does not have an ID and parent_id to copy.
        """
        if not self.id or not parent_id:
            raise ValueError("The folder must have an ID and parent_id to copy.")

        loop = asyncio.get_event_loop()

        syn = Synapse.get_client(synapse_client=synapse_client)
        source_and_destination = await loop.run_in_executor(
            None,
            lambda: copy(
                syn=syn,
                entity=self.id,
                destinationId=parent_id,
                excludeTypes=exclude_types or [],
                skipCopyAnnotations=not copy_annotations,
                updateExisting=file_update_existing,
                setProvenance=file_copy_activity,
            ),
        )

        new_folder_id = source_and_destination.get(self.id, None)
        if not new_folder_id:
            raise SynapseError("Failed to copy folder.")
        folder_copy = await (
            await Folder(id=new_folder_id).get_async(synapse_client=synapse_client)
        ).sync_from_synapse_async(
            download_file=False,
            synapse_client=synapse_client,
        )
        syn.logger.debug(
            f"Copied from folder {self.id} to {parent_id} with new id of {folder_copy.id}"
        )
        return folder_copy

Functions

get_async(parent=None, *, synapse_client=None) async

Get the folder metadata from Synapse. You are able to find a folder by either the id or the name and parent_id.

PARAMETER DESCRIPTION
parent

The parent folder or project this folder exists under.

TYPE: Optional[Union[Folder, Project]] DEFAULT: None

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Folder

The folder object.

RAISES DESCRIPTION
ValueError

If the folder does not have an id or a (name and (parent_id or parent with an id)) set.

Source code in synapseclient/models/folder.py
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"Folder_Get: {self.id}"
)
async def get_async(
    self,
    parent: Optional[Union["Folder", "Project"]] = None,
    *,
    synapse_client: Optional[Synapse] = None,
) -> "Folder":
    """Get the folder metadata from Synapse. You are able to find a folder by
    either the id or the name and parent_id.

    Arguments:
        parent: The parent folder or project this folder exists under.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The folder object.

    Raises:
        ValueError: If the folder does not have an id or a
            (name and (`parent_id` or parent with an id)) set.
    """
    parent_id = parent.id if parent else self.parent_id
    if not (self.id or (self.name and parent_id)):
        raise ValueError(
            "The folder must have an id or a "
            "(name and (`parent_id` or parent with an id)) set."
        )
    self.parent_id = parent_id

    entity_id = await get_id(entity=self, synapse_client=synapse_client)

    await get_from_entity_factory(
        entity_to_update=self,
        synapse_id_or_path=entity_id,
        synapse_client=synapse_client,
    )

    self._set_last_persistent_instance()
    return self

store_async(parent=None, failure_strategy=FailureStrategy.LOG_EXCEPTION, *, synapse_client=None) async

Store folders and files to synapse. If you have any files or folders attached to this folder they will be stored as well. You may attach files and folders to this folder by setting the files and folders attributes.

By default the store operation will non-destructively update the folder if you have not already retrieved the folder from Synapse. If you have already retrieved the folder from Synapse then the store operation will be destructive and will overwrite the folder with the current state of this object. See the create_or_update attribute for more information.

PARAMETER DESCRIPTION
parent

The parent folder or project to store the folder in.

TYPE: Optional[Union[Folder, Project]] DEFAULT: None

failure_strategy

Determines how to handle failures when storing attached Files and Folders under this Folder and an exception occurs.

TYPE: FailureStrategy DEFAULT: LOG_EXCEPTION

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Folder

The folder object.

RAISES DESCRIPTION
ValueError

If the folder does not have an id or a (name and (parent_id or parent with an id)) set.

Source code in synapseclient/models/folder.py
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"Folder_Store: {self.name}"
)
async def store_async(
    self,
    parent: Optional[Union["Folder", "Project"]] = None,
    failure_strategy: FailureStrategy = FailureStrategy.LOG_EXCEPTION,
    *,
    synapse_client: Optional[Synapse] = None,
) -> "Folder":
    """Store folders and files to synapse. If you have any files or folders attached
    to this folder they will be stored as well. You may attach files and folders
    to this folder by setting the `files` and `folders` attributes.

    By default the store operation will non-destructively update the folder if
    you have not already retrieved the folder from Synapse. If you have already
    retrieved the folder from Synapse then the store operation will be destructive
    and will overwrite the folder with the current state of this object. See the
    `create_or_update` attribute for more information.

    Arguments:
        parent: The parent folder or project to store the folder in.
        failure_strategy: Determines how to handle failures when storing attached
            Files and Folders under this Folder and an exception occurs.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The folder object.

    Raises:
        ValueError: If the folder does not have an id or a
            (name and (`parent_id` or parent with an id)) set.
    """
    parent_id = parent.id if parent else self.parent_id
    if not (self.id or (self.name and parent_id)):
        raise ValueError(
            "The folder must have an id or a "
            "(name and (`parent_id` or parent with an id)) set."
        )
    self.parent_id = parent_id

    if (
        self.create_or_update
        and not self._last_persistent_instance
        and (
            existing_folder_id := await get_id(
                entity=self, failure_strategy=None, synapse_client=synapse_client
            )
        )
        and (
            existing_folder := await Folder(id=existing_folder_id).get_async(
                synapse_client=synapse_client
            )
        )
    ):
        merge_dataclass_entities(source=existing_folder, destination=self)
    trace.get_current_span().set_attributes(
        {
            "synapse.name": self.name or "",
            "synapse.id": self.id or "",
        }
    )
    if self.has_changed:
        loop = asyncio.get_event_loop()
        synapse_folder = Synapse_Folder(
            id=self.id,
            name=self.name,
            parent=parent_id,
            etag=self.etag,
            description=self.description,
        )
        delete_none_keys(synapse_folder)
        entity = await loop.run_in_executor(
            None,
            lambda: Synapse.get_client(synapse_client=synapse_client).store(
                obj=synapse_folder,
                set_annotations=False,
                isRestricted=self.is_restricted,
                createOrUpdate=False,
            ),
        )

        self.fill_from_dict(synapse_folder=entity, set_annotations=False)

    await store_entity_components(
        root_resource=self,
        failure_strategy=failure_strategy,
        synapse_client=synapse_client,
    )
    self._set_last_persistent_instance()
    Synapse.get_client(synapse_client=synapse_client).logger.debug(
        f"Saved Folder {self.name}, id: {self.id}: parent: {self.parent_id}"
    )

    return self

delete_async(*, synapse_client=None) async

Delete the folder from Synapse by its id.

PARAMETER DESCRIPTION
synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
None

None

RAISES DESCRIPTION
ValueError

If the folder does not have an id set.

Source code in synapseclient/models/folder.py
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"Folder_Delete: {self.id}"
)
async def delete_async(self, *, synapse_client: Optional[Synapse] = None) -> None:
    """Delete the folder from Synapse by its id.

    Arguments:
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        None

    Raises:
        ValueError: If the folder does not have an id set.
    """
    if not self.id:
        raise ValueError("The folder must have an id set.")
    loop = asyncio.get_event_loop()
    await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).delete(
            obj=self.id,
        ),
    )

copy_async(parent_id, copy_annotations=True, exclude_types=None, file_update_existing=False, file_copy_activity='traceback', *, synapse_client=None) async

Copy the folder to another Synapse location. This will recursively copy all Tables, Links, Files, and Folders within the folder.

PARAMETER DESCRIPTION
parent_id

Synapse ID of a folder/project that the copied entity is being copied to

TYPE: str

copy_annotations

True to copy the annotations.

TYPE: bool DEFAULT: True

exclude_types

A list of entity types ['file', 'table', 'link'] which determines which entity types to not copy. Defaults to an empty list.

TYPE: Optional[List[str]] DEFAULT: None

file_update_existing

When the destination has a file that has the same name, users can choose to update that file.

TYPE: bool DEFAULT: False

file_copy_activity

Has three options to set the activity of the copied file:

- traceback: Creates a copy of the source files Activity.
- existing: Link to the source file's original Activity (if it exists)
- None: No activity is set

TYPE: Union[str, None] DEFAULT: 'traceback'

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Folder

The copied folder object.

Using this function

Assuming you have a folder with the ID "syn123" and you want to copy it to a project with the ID "syn456":

new_folder_instance = await Folder(id="syn123").copy_async(parent_id="syn456")

Copy the folder but do not persist annotations:

new_folder_instance = await Folder(id="syn123").copy_async(parent_id="syn456", copy_annotations=False)
RAISES DESCRIPTION
ValueError

If the folder does not have an ID and parent_id to copy.

Source code in synapseclient/models/folder.py
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"Folder_Copy: {self.id}"
)
async def copy_async(
    self,
    parent_id: str,
    copy_annotations: bool = True,
    exclude_types: Optional[List[str]] = None,
    file_update_existing: bool = False,
    file_copy_activity: Union[str, None] = "traceback",
    *,
    synapse_client: Optional[Synapse] = None,
) -> "Folder":
    """
    Copy the folder to another Synapse location. This will recursively copy all
    Tables, Links, Files, and Folders within the folder.

    Arguments:
        parent_id: Synapse ID of a folder/project that the copied entity is
            being copied to
        copy_annotations: True to copy the annotations.
        exclude_types: A list of entity types ['file', 'table', 'link'] which
            determines which entity types to not copy. Defaults to an empty list.
        file_update_existing: When the destination has a file that has the same name,
            users can choose to update that file.
        file_copy_activity: Has three options to set the activity of the copied file:

                - traceback: Creates a copy of the source files Activity.
                - existing: Link to the source file's original Activity (if it exists)
                - None: No activity is set
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The copied folder object.

    Example: Using this function
        Assuming you have a folder with the ID "syn123" and you want to copy it to a
        project with the ID "syn456":

            new_folder_instance = await Folder(id="syn123").copy_async(parent_id="syn456")

        Copy the folder but do not persist annotations:

            new_folder_instance = await Folder(id="syn123").copy_async(parent_id="syn456", copy_annotations=False)

    Raises:
        ValueError: If the folder does not have an ID and parent_id to copy.
    """
    if not self.id or not parent_id:
        raise ValueError("The folder must have an ID and parent_id to copy.")

    loop = asyncio.get_event_loop()

    syn = Synapse.get_client(synapse_client=synapse_client)
    source_and_destination = await loop.run_in_executor(
        None,
        lambda: copy(
            syn=syn,
            entity=self.id,
            destinationId=parent_id,
            excludeTypes=exclude_types or [],
            skipCopyAnnotations=not copy_annotations,
            updateExisting=file_update_existing,
            setProvenance=file_copy_activity,
        ),
    )

    new_folder_id = source_and_destination.get(self.id, None)
    if not new_folder_id:
        raise SynapseError("Failed to copy folder.")
    folder_copy = await (
        await Folder(id=new_folder_id).get_async(synapse_client=synapse_client)
    ).sync_from_synapse_async(
        download_file=False,
        synapse_client=synapse_client,
    )
    syn.logger.debug(
        f"Copied from folder {self.id} to {parent_id} with new id of {folder_copy.id}"
    )
    return folder_copy

sync_from_synapse_async(path=None, recursive=True, download_file=True, if_collision=COLLISION_OVERWRITE_LOCAL, failure_strategy=FailureStrategy.LOG_EXCEPTION, include_activity=True, follow_link=False, link_hops=1, *, synapse_client=None) async

Sync this container and all possible sub-folders from Synapse. By default this will download the files that are found and it will populate the files and folders attributes with the found files and folders. If you only want to retrieve the full tree of metadata about your container specify download_file as False.

This works similar to synapseutils.syncFromSynapse, however, this does not currently support the writing of data to a manifest TSV file. This will be a future enhancement.

Only Files and Folders are supported at this time to be synced from synapse.

PARAMETER DESCRIPTION
path

An optional path where the file hierarchy will be reproduced. If not specified the files will by default be placed in the synapseCache.

TYPE: Optional[str] DEFAULT: None

recursive

Whether or not to recursively get the entire hierarchy of the folder and sub-folders.

TYPE: bool DEFAULT: True

download_file

Whether to download the files found or not.

TYPE: bool DEFAULT: True

if_collision

Determines how to handle file collisions. May be

  • overwrite.local
  • keep.local
  • keep.both

TYPE: str DEFAULT: COLLISION_OVERWRITE_LOCAL

failure_strategy

Determines how to handle failures when retrieving children under this Folder and an exception occurs.

TYPE: FailureStrategy DEFAULT: LOG_EXCEPTION

include_activity

Whether to include the activity of the files.

TYPE: bool DEFAULT: True

follow_link

Whether to follow a link entity or not. Links can be used to point at other Synapse entities.

TYPE: bool DEFAULT: False

link_hops

The number of hops to follow the link. A number of 1 is used to prevent circular references. There is nothing in place to prevent infinite loops. Be careful if setting this above 1.

TYPE: int DEFAULT: 1

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Self

The object that was called on. This will be the same object that was called on to start the sync.

Using this function

Suppose I want to walk the immediate children of a folder without downloading the files:

from synapseclient import Synapse
from synapseclient.models import Folder

syn = Synapse()
syn.login()

my_folder = Folder(id="syn12345")
await my_folder.sync_from_synapse_async(download_file=False, recursive=False)

for folder in my_folder.folders:
    print(folder.name)

for file in my_folder.files:
    print(file.name)

Suppose I want to download the immediate children of a folder:

from synapseclient import Synapse
from synapseclient.models import Folder

syn = Synapse()
syn.login()

my_folder = Folder(id="syn12345")
await my_folder.sync_from_synapse_async(path="/path/to/folder", recursive=False)

for folder in my_folder.folders:
    print(folder.name)

for file in my_folder.files:
    print(file.name)

Suppose I want to download the immediate all children of a Project and all sub-folders and files:

from synapseclient import Synapse
from synapseclient.models import Project

syn = Synapse()
syn.login()

my_project = Project(id="syn12345")
await my_project.sync_from_synapse_async(path="/path/to/folder")
RAISES DESCRIPTION
ValueError

If the folder does not have an id set.

A sequence diagram for this method is as follows:

sequenceDiagram
    autonumber
    participant project_or_folder
    activate project_or_folder
    project_or_folder->>sync_from_synapse: Recursive search and download files
    activate sync_from_synapse
        opt Current instance not retrieved from Synapse
            sync_from_synapse->>project_or_folder: Call `.get()` method
            project_or_folder-->>sync_from_synapse: .
        end

        loop For each return of the generator
            sync_from_synapse->>client: call `.getChildren()` method
            client-->>sync_from_synapse: .
            note over sync_from_synapse: Append to a running list
        end

        loop For each child
            note over sync_from_synapse: Create all `pending_tasks` at current depth

            alt Child is File
                note over sync_from_synapse: Append `file.get()` method
            else Child is Folder
                note over sync_from_synapse: Append `folder.get()` method
                alt Recursive is True
                    note over sync_from_synapse: Append `folder.sync_from_synapse()` method
                end
            else Child is Link and hops > 0
                note over sync_from_synapse: Append task to follow link
            end
        end

        loop For each task in pending_tasks
            par `file.get()`
                sync_from_synapse->>File: Retrieve File metadata and Optionally download
                File->>client: `.get()`
                client-->>File: .
                File-->>sync_from_synapse: .
            and `folder.get()`
                sync_from_synapse->>Folder: Retrieve Folder metadataa
                Folder->>client: `.get()`
                client-->>Folder: .
                Folder-->>sync_from_synapse: .
            and `folder.sync_from_synapse_async()`
                note over sync_from_synapse: This is a recursive call to `sync_from_synapse`
                sync_from_synapse->>sync_from_synapse: Recursive call to `.sync_from_synapse_async()`
            and `_follow_link`
                sync_from_synapse ->>client: call `get_entity_id_bundle2` function
                client-->sync_from_synapse: .
                note over sync_from_synapse: Do nothing if not link
                note over sync_from_synapse: call `_create_task_for_child` and execute
            end
        end

    deactivate sync_from_synapse
    deactivate project_or_folder
Source code in synapseclient/models/mixins/storable_container.py
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"{self.__class__.__name__}_sync_from_synapse: {self.id}"
)
async def sync_from_synapse_async(
    self: Self,
    path: Optional[str] = None,
    recursive: bool = True,
    download_file: bool = True,
    if_collision: str = COLLISION_OVERWRITE_LOCAL,
    failure_strategy: FailureStrategy = FailureStrategy.LOG_EXCEPTION,
    include_activity: bool = True,
    follow_link: bool = False,
    link_hops: int = 1,
    *,
    synapse_client: Optional[Synapse] = None,
) -> Self:
    """
    Sync this container and all possible sub-folders from Synapse. By default this
    will download the files that are found and it will populate the
    `files` and `folders` attributes with the found files and folders. If you only
    want to retrieve the full tree of metadata about your container specify
    `download_file` as False.

    This works similar to [synapseutils.syncFromSynapse][], however, this does not
    currently support the writing of data to a manifest TSV file. This will be a
    future enhancement.

    Only Files and Folders are supported at this time to be synced from synapse.

    Arguments:
        path: An optional path where the file hierarchy will be reproduced. If not
            specified the files will by default be placed in the synapseCache.
        recursive: Whether or not to recursively get the entire hierarchy of the
            folder and sub-folders.
        download_file: Whether to download the files found or not.
        if_collision: Determines how to handle file collisions. May be

            - `overwrite.local`
            - `keep.local`
            - `keep.both`
        failure_strategy: Determines how to handle failures when retrieving children
            under this Folder and an exception occurs.
        include_activity: Whether to include the activity of the files.
        follow_link: Whether to follow a link entity or not. Links can be used to
            point at other Synapse entities.
        link_hops: The number of hops to follow the link. A number of 1 is used to
            prevent circular references. There is nothing in place to prevent
            infinite loops. Be careful if setting this above 1.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The object that was called on. This will be the same object that was called on
            to start the sync.

    Example: Using this function
        Suppose I want to walk the immediate children of a folder without downloading the files:

            from synapseclient import Synapse
            from synapseclient.models import Folder

            syn = Synapse()
            syn.login()

            my_folder = Folder(id="syn12345")
            await my_folder.sync_from_synapse_async(download_file=False, recursive=False)

            for folder in my_folder.folders:
                print(folder.name)

            for file in my_folder.files:
                print(file.name)

        Suppose I want to download the immediate children of a folder:

            from synapseclient import Synapse
            from synapseclient.models import Folder

            syn = Synapse()
            syn.login()

            my_folder = Folder(id="syn12345")
            await my_folder.sync_from_synapse_async(path="/path/to/folder", recursive=False)

            for folder in my_folder.folders:
                print(folder.name)

            for file in my_folder.files:
                print(file.name)


        Suppose I want to download the immediate all children of a Project and all sub-folders and files:

            from synapseclient import Synapse
            from synapseclient.models import Project

            syn = Synapse()
            syn.login()

            my_project = Project(id="syn12345")
            await my_project.sync_from_synapse_async(path="/path/to/folder")


    Raises:
        ValueError: If the folder does not have an id set.


    A sequence diagram for this method is as follows:

    ```mermaid
    sequenceDiagram
        autonumber
        participant project_or_folder
        activate project_or_folder
        project_or_folder->>sync_from_synapse: Recursive search and download files
        activate sync_from_synapse
            opt Current instance not retrieved from Synapse
                sync_from_synapse->>project_or_folder: Call `.get()` method
                project_or_folder-->>sync_from_synapse: .
            end

            loop For each return of the generator
                sync_from_synapse->>client: call `.getChildren()` method
                client-->>sync_from_synapse: .
                note over sync_from_synapse: Append to a running list
            end

            loop For each child
                note over sync_from_synapse: Create all `pending_tasks` at current depth

                alt Child is File
                    note over sync_from_synapse: Append `file.get()` method
                else Child is Folder
                    note over sync_from_synapse: Append `folder.get()` method
                    alt Recursive is True
                        note over sync_from_synapse: Append `folder.sync_from_synapse()` method
                    end
                else Child is Link and hops > 0
                    note over sync_from_synapse: Append task to follow link
                end
            end

            loop For each task in pending_tasks
                par `file.get()`
                    sync_from_synapse->>File: Retrieve File metadata and Optionally download
                    File->>client: `.get()`
                    client-->>File: .
                    File-->>sync_from_synapse: .
                and `folder.get()`
                    sync_from_synapse->>Folder: Retrieve Folder metadataa
                    Folder->>client: `.get()`
                    client-->>Folder: .
                    Folder-->>sync_from_synapse: .
                and `folder.sync_from_synapse_async()`
                    note over sync_from_synapse: This is a recursive call to `sync_from_synapse`
                    sync_from_synapse->>sync_from_synapse: Recursive call to `.sync_from_synapse_async()`
                and `_follow_link`
                    sync_from_synapse ->>client: call `get_entity_id_bundle2` function
                    client-->sync_from_synapse: .
                    note over sync_from_synapse: Do nothing if not link
                    note over sync_from_synapse: call `_create_task_for_child` and execute
                end
            end

        deactivate sync_from_synapse
        deactivate project_or_folder
    ```

    """
    if not self._last_persistent_instance:
        await self.get_async(synapse_client=synapse_client)
    Synapse.get_client(synapse_client=synapse_client).logger.info(
        f"Syncing {self.__class__.__name__} ({self.id}:{self.name}) from Synapse."
    )
    path = os.path.expanduser(path) if path else None

    loop = asyncio.get_event_loop()
    children = await loop.run_in_executor(
        None,
        lambda: self._retrieve_children(
            follow_link=follow_link,
            synapse_client=synapse_client,
        ),
    )

    pending_tasks = []
    self.folders = []
    self.files = []

    for child in children:
        pending_tasks.extend(
            self._create_task_for_child(
                child=child,
                recursive=recursive,
                path=path,
                download_file=download_file,
                if_collision=if_collision,
                failure_strategy=failure_strategy,
                synapse_client=synapse_client,
                include_activity=include_activity,
                follow_link=follow_link,
                link_hops=link_hops,
            )
        )

    for task in asyncio.as_completed(pending_tasks):
        result = await task
        self._resolve_sync_from_synapse_result(
            result=result,
            failure_strategy=failure_strategy,
            synapse_client=synapse_client,
        )
    return self

get_permissions_async(*, synapse_client=None) async

Get the permissions that the caller has on an Entity.

PARAMETER DESCRIPTION
synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Permissions

A Permissions object

Using this function:

Getting permissions for a Synapse Entity

permissions = await File(id="syn123").get_permissions_async()

Getting access types list from the Permissions object

permissions.access_types
Source code in synapseclient/models/mixins/access_control.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
async def get_permissions_async(
    self,
    *,
    synapse_client: Optional[Synapse] = None,
) -> "Permissions":
    """
    Get the [permissions][synapseclient.core.models.permission.Permissions]
    that the caller has on an Entity.

    Arguments:
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        A Permissions object


    Example: Using this function:
        Getting permissions for a Synapse Entity

            permissions = await File(id="syn123").get_permissions_async()

        Getting access types list from the Permissions object

            permissions.access_types
    """
    loop = asyncio.get_event_loop()

    return await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).get_permissions(
            entity=self.id
        ),
    )

get_acl_async(principal_id=None, *, synapse_client=None) async

Get the ACL that a user or group has on an Entity.

PARAMETER DESCRIPTION
principal_id

Identifier of a user or group (defaults to PUBLIC users)

TYPE: int DEFAULT: None

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
List[str]

An array containing some combination of ['READ', 'UPDATE', 'CREATE', 'DELETE', 'DOWNLOAD', 'MODERATE', 'CHANGE_PERMISSIONS', 'CHANGE_SETTINGS'] or an empty array

Source code in synapseclient/models/mixins/access_control.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
async def get_acl_async(
    self, principal_id: int = None, *, synapse_client: Optional[Synapse] = None
) -> List[str]:
    """
    Get the [ACL][synapseclient.core.models.permission.Permissions.access_types]
    that a user or group has on an Entity.

    Arguments:
        principal_id: Identifier of a user or group (defaults to PUBLIC users)
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        An array containing some combination of
            ['READ', 'UPDATE', 'CREATE', 'DELETE', 'DOWNLOAD', 'MODERATE',
            'CHANGE_PERMISSIONS', 'CHANGE_SETTINGS']
            or an empty array
    """
    loop = asyncio.get_event_loop()

    return await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).get_acl(
            entity=self.id, principal_id=principal_id
        ),
    )

set_permissions_async(principal_id=None, access_type=None, modify_benefactor=False, warn_if_inherits=True, overwrite=True, *, synapse_client=None) async

Sets permission that a user or group has on an Entity. An Entity may have its own ACL or inherit its ACL from a benefactor.

PARAMETER DESCRIPTION
principal_id

Identifier of a user or group. 273948 is for all registered Synapse users and 273949 is for public access. None implies public access.

TYPE: int DEFAULT: None

access_type

Type of permission to be granted. One or more of CREATE, READ, DOWNLOAD, UPDATE, DELETE, CHANGE_PERMISSIONS.

Defaults to ['READ', 'DOWNLOAD']

TYPE: List[str] DEFAULT: None

modify_benefactor

Set as True when modifying a benefactor's ACL

TYPE: bool DEFAULT: False

warn_if_inherits

Set as False, when creating a new ACL. Trying to modify the ACL of an Entity that inherits its ACL will result in a warning

TYPE: bool DEFAULT: True

overwrite

By default this function overwrites existing permissions for the specified user. Set this flag to False to add new permissions non-destructively.

TYPE: bool DEFAULT: True

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Dict[str, Union[str, list]]

An Access Control List object

Setting permissions

Grant all registered users download access

await File(id="syn123").set_permissions_async(principal_id=273948, access_type=['READ','DOWNLOAD'])

Grant the public view access

await File(id="syn123").set_permissions_async(principal_id=273949, access_type=['READ'])
Source code in synapseclient/models/mixins/access_control.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
async def set_permissions_async(
    self,
    principal_id: int = None,
    access_type: List[str] = None,
    modify_benefactor: bool = False,
    warn_if_inherits: bool = True,
    overwrite: bool = True,
    *,
    synapse_client: Optional[Synapse] = None,
) -> Dict[str, Union[str, list]]:
    """
    Sets permission that a user or group has on an Entity.
    An Entity may have its own ACL or inherit its ACL from a benefactor.

    Arguments:
        principal_id: Identifier of a user or group. `273948` is for all
            registered Synapse users and `273949` is for public access.
            None implies public access.
        access_type: Type of permission to be granted. One or more of CREATE,
            READ, DOWNLOAD, UPDATE, DELETE, CHANGE_PERMISSIONS.

            **Defaults to ['READ', 'DOWNLOAD']**
        modify_benefactor: Set as True when modifying a benefactor's ACL
        warn_if_inherits: Set as False, when creating a new ACL. Trying to modify
            the ACL of an Entity that inherits its ACL will result in a warning
        overwrite: By default this function overwrites existing permissions for
            the specified user. Set this flag to False to add new permissions
            non-destructively.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        An Access Control List object

    Example: Setting permissions
        Grant all registered users download access

            await File(id="syn123").set_permissions_async(principal_id=273948, access_type=['READ','DOWNLOAD'])

        Grant the public view access

            await File(id="syn123").set_permissions_async(principal_id=273949, access_type=['READ'])
    """
    if access_type is None:
        access_type = ["READ", "DOWNLOAD"]
    loop = asyncio.get_event_loop()

    return await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).setPermissions(
            entity=self.id,
            principalId=principal_id,
            accessType=access_type,
            modify_benefactor=modify_benefactor,
            warn_if_inherits=warn_if_inherits,
            overwrite=overwrite,
        ),
    )

synapseclient.models.File dataclass

Bases: FileSynchronousProtocol, AccessControllable

A file within Synapse.

ATTRIBUTE DESCRIPTION
id

The unique immutable ID for this file. A new ID will be generated for new Files. Once issued, this ID is guaranteed to never change or be re-issued.

TYPE: Optional[str]

name

The name of this entity. Must be 256 characters or less. Names may only contain: letters, numbers, spaces, underscores, hyphens, periods, plus signs, apostrophes, and parentheses. If not specified, the name will be derived from the file name.

TYPE: Optional[str]

path

The path to the file on disk. Using shorthand ~ will be expanded to the user's home directory.

This is used during a get operation to specify where to download the file to. It should be pointing to a directory.

This is also used during a store operation to specify the file to upload. It should be pointing to a file.

TYPE: Optional[str]

description

The description of this file. Must be 1000 characters or less.

TYPE: Optional[str]

parent_id

The ID of the Entity that is the parent of this Entity. Setting this to a new value and storing it will move this File under the new parent.

TYPE: Optional[str]

version_label

The version label for this entity. Updates to the entity will increment the version number.

TYPE: Optional[str]

version_comment

The version comment for this entity

TYPE: Optional[str]

data_file_handle_id

ID of the file associated with this entity. You may define an existing data_file_handle_id to use the existing data_file_handle_id. The creator of the file must also be the owner of the data_file_handle_id to have permission to store the file.

TYPE: Optional[str]

external_url

The external URL of this file. If this is set AND synapse_store is False, only a reference to this URL and the file metadata will be stored in Synapse. The file itself will not be uploaded. If this attribute is set it will override the path.

TYPE: Optional[str]

activity

The Activity model represents the main record of Provenance in Synapse. It is analygous to the Activity defined in the W3C Specification on Provenance. Activity cannot be removed during a store operation by setting it to None. You must use: synapseclient.models.Activity.delete_async or [synapseclient.models.Activity.disassociate_from_entity_async][].

TYPE: Optional[Activity]

annotations

Additional metadata associated with the folder. The key is the name of your desired annotations. The value is an object containing a list of values (use empty list to represent no values for key) and the value type associated with all values in the list. To remove all annotations set this to an empty dict {} or None and store the entity.

TYPE: Optional[Dict[str, Union[List[str], List[bool], List[float], List[int], List[date], List[datetime]]]]

ATTRIBUTE DESCRIPTION
content_type

(New Upload Only) Used to manually specify Content-type header, for example 'application/png' or 'application/json; charset=UTF-8'. If not specified, the content type will be derived from the file extension.

This can be specified only during the initial store of this file or any time there is a new file to upload. In order to change this after the File has been created use synapseclient.models.File.change_metadata.

TYPE: Optional[str]

content_size

(New Upload Only) The size of the file in bytes. This can be specified only during the initial creation of the File. This is also only applicable to files not uploaded to Synapse. ie: synapse_store is False.

TYPE: Optional[int]

ATTRIBUTE DESCRIPTION
content_md5

(Store only) The MD5 of the file is known. If not supplied this will be computed in the client is possible. If supplied for a file entity already stored in Synapse it will be calculated again to check if a new upload needs to occur. This will not be filled in during a read for data. It is only used during a store operation. To retrieve the md5 of the file after read from synapse use the .file_handle.content_md5 attribute.

TYPE: Optional[str]

create_or_update

(Store only) Indicates whether the method should automatically perform an update if the file conflicts with an existing Synapse object.

TYPE: bool

force_version

(Store only) Indicates whether the method should increment the version of the object if something within the entity has changed. For example updating the description or name. You may set this to False and an update to the entity will not increment the version.

Updating the version_label attribute will also cause a version update regardless of this flag.

An update to the MD5 of the file will force a version update regardless of this flag.

TYPE: bool

is_restricted

(Store only) If set to true, an email will be sent to the Synapse access control team to start the process of adding terms-of-use or review board approval for this entity. You will be contacted with regards to the specific data being restricted and the requirements of access.

This may be used only by an administrator of the specified file.

TYPE: bool

merge_existing_annotations

(Store only) Works in conjunction with create_or_update in that this is only evaluated if create_or_update is True. If this entity exists in Synapse that has annotations that are not present in a store operation, these annotations will be added to the entity. If this is False any annotations that are not present within a store operation will be removed from this entity. This allows one to complete a destructive update of annotations on an entity.

TYPE: bool

associate_activity_to_new_version

(Store only) Works in conjunction with create_or_update in that this is only evaluated if create_or_update is True. When true an activity already attached to the current version of this entity will be associated the new version during a store operation if the version was updated. This is useful if you are updating the entity and want to ensure that the activity is persisted onto the new version the entity.

TYPE: bool

synapse_store

(Store only) Whether the File should be uploaded or if false: only the path should be stored when synapseclient.models.File.store is called.

TYPE: bool

ATTRIBUTE DESCRIPTION
download_file

(Get only) If True the file will be downloaded.

TYPE: bool

if_collision

(Get only) Determines how to handle file collisions. Defaults to "keep.both". May be:

  • overwrite.local
  • keep.local
  • keep.both

TYPE: str

synapse_container_limit

(Get only) A Synanpse ID used to limit the search in Synapse if file is specified as a local file. That is, if the file is stored in multiple locations in Synapse only the ones in the specified folder/project will be returned.

TYPE: Optional[str]

ATTRIBUTE DESCRIPTION
etag

(Read Only) Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle concurrent updates. Since the E-Tag changes every time an entity is updated it is used to detect when a client's current representation of an entity is out-of-date.

TYPE: Optional[str]

created_on

(Read Only) The date this entity was created.

TYPE: Optional[str]

modified_on

(Read Only) The date this entity was last modified.

TYPE: Optional[str]

created_by

(Read Only) The ID of the user that created this entity.

TYPE: Optional[str]

modified_by

(Read Only) The ID of the user that last modified this entity.

TYPE: Optional[str]

version_number

(Read Only) The version number issued to this version on the object.

TYPE: Optional[int]

is_latest_version

(Read Only) If this is the latest version of the object.

TYPE: Optional[bool]

file_handle

(Read Only) The file handle associated with this entity.

TYPE: Optional[FileHandle]

Source code in synapseclient/models/file.py
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
@dataclass()
@async_to_sync
class File(FileSynchronousProtocol, AccessControllable):
    """A file within Synapse.

    Attributes:
        id: The unique immutable ID for this file. A new ID will be generated for new
            Files. Once issued, this ID is guaranteed to never change or be re-issued.
        name: The name of this entity. Must be 256 characters or less. Names may only
            contain: letters, numbers, spaces, underscores, hyphens, periods, plus
            signs, apostrophes, and parentheses. If not specified, the name will be
            derived from the file name.
        path: The path to the file on disk. Using shorthand `~` will be expanded to the
            user's home directory.

            This is used during a `get` operation to specify where to download the file
            to. It should be pointing to a directory.

            This is also used during a `store` operation to specify the file to upload.
            It should be pointing to a file.

        description: The description of this file. Must be 1000 characters or less.
        parent_id: The ID of the Entity that is the parent of this Entity. Setting this
            to a new value and storing it will move this File under the new parent.
        version_label: The version label for this entity. Updates to the entity will
            increment the version number.
        version_comment: The version comment for this entity
        data_file_handle_id: ID of the file associated with this entity. You may define
            an existing data_file_handle_id to use the existing data_file_handle_id. The
            creator of the file must also be the owner of the data_file_handle_id to
            have permission to store the file.
        external_url: The external URL of this file. If this is set AND `synapse_store`
            is False, only a reference to this URL and the file metadata will be stored
            in Synapse. The file itself will not be uploaded. If this attribute is set
            it will override the `path`.
        activity: The Activity model represents the main record of Provenance in
            Synapse. It is analygous to the Activity defined in the
            [W3C Specification](https://www.w3.org/TR/prov-n/) on Provenance. Activity
            cannot be removed during a store operation by setting it to None. You must
            use: [synapseclient.models.Activity.delete_async][] or
            [synapseclient.models.Activity.disassociate_from_entity_async][].
        annotations: Additional metadata associated with the folder. The key is the name
            of your desired annotations. The value is an object containing a list of
            values (use empty list to represent no values for key) and the value type
            associated with all values in the list. To remove all annotations set this
            to an empty dict `{}` or None and store the entity.

    Attributes:
        content_type: (New Upload Only)
            Used to manually specify Content-type header, for example
            'application/png' or 'application/json; charset=UTF-8'. If not specified,
            the content type will be derived from the file extension.


            This can be specified only during the initial store of this file or any time
            there is a new file to upload.
            In order to change this after the File has been created use
            [synapseclient.models.File.change_metadata][].
        content_size: (New Upload Only)
            The size of the file in bytes. This can be specified only during the initial
            creation of the File. This is also only applicable to files not uploaded to
            Synapse. ie: `synapse_store` is False.

    Attributes:
        content_md5: (Store only) The MD5 of the file is known. If not supplied this
            will be computed in the client is possible. If supplied for a file entity
            already stored in Synapse it will be calculated again to check if a new
            upload needs to occur. This will not be filled in during a read for data. It
            is only used during a store operation. To retrieve the md5 of the file after
            read from synapse use the `.file_handle.content_md5` attribute.
        create_or_update: (Store only)
            Indicates whether the method should automatically perform an
            update if the `file` conflicts with an existing Synapse object.
        force_version: (Store only)
            Indicates whether the method should increment the version of the object if
            something within the entity has changed. For example updating the
            description or name. You may set this to False and an update to the
            entity will not increment the version.

            Updating the `version_label` attribute will also cause a version update
            regardless  of this flag.

            An update to the MD5 of the file will force a version update regardless of
            this  flag.
        is_restricted: (Store only)
            If set to true, an email will be sent to the Synapse access control
            team to start the process of adding terms-of-use or review board approval
            for this entity. You will be contacted with regards to the specific data
            being restricted and the requirements of access.

            This may be used only by an administrator of the specified file.
        merge_existing_annotations: (Store only)
            Works in conjunction with `create_or_update` in that this is only evaluated
            if `create_or_update` is True. If this entity exists in Synapse that has
            annotations that are not present in a store operation, these annotations
            will be added to the entity. If this is False any annotations that are not
            present within a store operation will be removed from this entity. This
            allows one to complete a destructive update of annotations on an entity.
        associate_activity_to_new_version: (Store only)
            Works in conjunction with `create_or_update` in that this is only evaluated
            if `create_or_update` is True. When true an activity already attached to the
            current version of this entity will be associated the new version during a
            store operation if the version was updated. This is useful if you are
            updating the entity and want to ensure that the activity is persisted onto
            the new version the entity.
        synapse_store: (Store only)
            Whether the File should be uploaded or if false: only the path should
            be stored when [synapseclient.models.File.store][] is called.

    Attributes:
        download_file: (Get only) If True the file will be downloaded.
        if_collision: (Get only)
            Determines how to handle file collisions. Defaults to "keep.both". May be:

            - `overwrite.local`
            - `keep.local`
            - `keep.both`
        synapse_container_limit: (Get only)
            A Synanpse ID used to limit the search in Synapse if
            file is specified as a local file. That is, if the file is stored in
            multiple locations in Synapse only the ones in the specified folder/project
            will be returned.

    Attributes:
        etag: (Read Only) Synapse employs an Optimistic Concurrency Control (OCC) scheme
            to handle concurrent updates. Since the E-Tag changes every time an entity
            is updated it is used to detect when a client's current representation of an
            entity is out-of-date.
        created_on: (Read Only) The date this entity was created.
        modified_on: (Read Only) The date this entity was last modified.
        created_by: (Read Only) The ID of the user that created this entity.
        modified_by: (Read Only) The ID of the user that last modified this entity.
        version_number: (Read Only) The version number issued to this version on the
            object.
        is_latest_version: (Read Only) If this is the latest version of the object.
        file_handle: (Read Only) The file handle associated with this entity.
    """

    id: Optional[str] = None
    """The unique immutable ID for this file. A new ID will be generated for new Files.
    Once issued, this ID is guaranteed to never change or be re-issued."""

    name: Optional[str] = None
    """
    The name of this entity. Must be 256 characters or less.
    Names may only contain: letters, numbers, spaces, underscores, hyphens, periods,
    plus signs, apostrophes, and parentheses. If not specified, the name will be
    derived from the file name.
    """

    path: Optional[str] = field(default=None, compare=False)
    """The path to the file on disk. Using shorthand `~` will be expanded to the user's
    home directory.

    This is used during a `get` operation to specify where to download the file to. It
    should be pointing to a directory.

    This is also used during a `store` operation to specify the file to upload. It
    should be pointing to a file."""

    description: Optional[str] = None
    """The description of this file. Must be 1000 characters or less."""

    parent_id: Optional[str] = None
    """The ID of the Entity that is the parent of this Entity. Setting this to a new
    value and storing it will move this File under the new parent."""

    version_label: Optional[str] = None
    """The version label for this entity. Updates to the entity will increment the
    version number."""

    version_comment: Optional[str] = None
    """The version comment for this entity."""

    data_file_handle_id: Optional[str] = None
    """
    ID of the file handle associated with this entity. You may define an existing
    data_file_handle_id to use the existing data_file_handle_id. The creator of the
    file must also be the owner of the data_file_handle_id to have permission to
    store the file.
    """

    external_url: Optional[str] = field(default=None, compare=False)
    """
    The external URL of this file. If this is set AND `synapse_store` is False, only
    a reference to this URL and the file metadata will be stored in Synapse. The file
    itself will not be uploaded. If this attribute is set it will override the `path`.
    """

    activity: Optional[Activity] = field(default=None, compare=False)
    """The Activity model represents the main record of Provenance in Synapse.  It is
    analygous to the Activity defined in the
    [W3C Specification](https://www.w3.org/TR/prov-n/) on Provenance. Activity cannot
    be removed during a store operation by setting it to None. You must use:
    [synapseclient.models.Activity.delete_async][] or
    [synapseclient.models.Activity.disassociate_from_entity_async][].
    """

    annotations: Optional[
        Dict[
            str,
            Union[
                List[str],
                List[bool],
                List[float],
                List[int],
                List[date],
                List[datetime],
            ],
        ]
    ] = field(default_factory=dict, compare=False)
    """Additional metadata associated with the folder. The key is the name of your
    desired annotations. The value is an object containing a list of values
    (use empty list to represent no values for key) and the value type associated with
    all values in the list. To remove all annotations set this to an empty dict `{}`."""

    content_type: Optional[str] = None
    """
    (New Upload Only)
    Used to manually specify Content-type header, for example 'application/png'
    or 'application/json; charset=UTF-8'. If not specified, the content type will be
    derived from the file extension.

    This can be specified only during the initial store of this file. In order to change
    this after the File has been created use
    [synapseclient.models.File.change_metadata][].
    """

    content_size: Optional[int] = None
    """
    (New Upload Only)
    The size of the file in bytes. This can be specified only during the initial
    creation of the File. This is also only applicable to files not uploaded to Synapse.
    ie: `synapse_store` is False.
    """

    content_md5: Optional[str] = field(default=None, compare=False)
    """
    (Store only)
    The MD5 of the file is known. If not supplied this will be computed in the client
    is possible. If supplied for a file entity already stored in Synapse it will be
    calculated again to check if a new upload needs to occur. This will not be filled
    in during a read for data. It is only used during a store operation. To retrieve
    the md5 of the file after read from synapse use the `.file_handle.content_md5`
    attribute.
    """

    create_or_update: bool = field(default=True, repr=False, compare=False)
    """
    (Store only)

    Indicates whether the method should automatically perform an update if the file
    conflicts with an existing Synapse object.
    """

    force_version: bool = field(default=True, repr=False, compare=False)
    """
    (Store only)

    Indicates whether the method should increment the version of the object if something
    within the entity has changed. For example updating the description or name.
    You may set this to False and an update to the entity will not increment the
    version.

    Updating the `version_label` attribute will also cause a version update regardless
    of this flag.

    An update to the MD5 of the file will force a version update regardless of this
    flag.
    """

    is_restricted: bool = field(default=False, repr=False)
    """
    (Store only)

    If set to true, an email will be sent to the Synapse access control team to start
    the process of adding terms-of-use or review board approval for this entity.
    You will be contacted with regards to the specific data being restricted and the
    requirements of access.

    This may be used only by an administrator of the specified file.
    """

    merge_existing_annotations: bool = field(default=True, repr=False, compare=False)
    """
    (Store only)

    Works in conjunction with `create_or_update` in that this is only evaluated if
    `create_or_update` is True. If this entity exists in Synapse that has annotations
    that are not present in a store operation, these annotations will be added to the
    entity. If this is False any annotations that are not present within a store
    operation will be removed from this entity. This allows one to complete a
    destructive update of annotations on an entity.
    """

    associate_activity_to_new_version: bool = field(
        default=False, repr=False, compare=False
    )
    """
    (Store only)

    Works in conjunction with `create_or_update` in that this is only evaluated if
    `create_or_update` is True. When true an activity already attached to the current
    version of this entity will be associated the new version during a store operation
    if the version was updated. This is useful if you are updating the entity and want
    to ensure that the activity is persisted onto the new version the entity.

    When this is False the activity will not be associated to the new version of the
    entity during a store operation.

    Regardless of this setting, if you have an Activity object on the entity it will be
    persisted onto the new version. This is only used when you don't have an Activity
    object on the entity.
    """

    _present_manifest_fields: List[str] = field(default=None, repr=False, compare=False)
    """Hidden attribute to pass along what columns were present in a manifest upload."""

    synapse_store: bool = field(default=True, repr=False)
    """
    (Store only)

    Whether the File should be uploaded or if false: only the path should be stored when
    [synapseclient.models.File.store][] is called.
    """

    download_file: bool = field(default=True, repr=False, compare=False)
    """
    (Get only)

    If True the file will be downloaded."""

    if_collision: str = field(default="keep.both", repr=False, compare=False)
    """
    (Get only)

    Determines how to handle file collisions. Defaults to "keep.both".
            May be

            - `overwrite.local`
            - `keep.local`
            - `keep.both`
    """

    synapse_container_limit: Optional[str] = field(
        default=None, repr=False, compare=False
    )
    """A Synanpse ID used to limit the search in Synapse if file is specified as a local
    file. That is, if the file is stored in multiple locations in Synapse only the
    ones in the specified folder/project will be returned."""

    etag: Optional[str] = field(default=None, compare=False)
    """
    (Read Only)
    Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle
    concurrent updates. Since the E-Tag changes every time an entity is updated it is
    used to detect when a client's current representation of an entity is out-of-date.
    """

    created_on: Optional[str] = field(default=None, compare=False)
    """(Read Only) The date this entity was created."""

    modified_on: Optional[str] = field(default=None, compare=False)
    """(Read Only) The date this entity was last modified."""

    created_by: Optional[str] = field(default=None, compare=False)
    """(Read Only) The ID of the user that created this entity."""

    modified_by: Optional[str] = field(default=None, compare=False)
    """(Read Only) The ID of the user that last modified this entity."""

    version_number: Optional[int] = field(default=None, compare=False)
    """(Read Only) The version number issued to this version on the object."""

    is_latest_version: Optional[bool] = field(default=None, compare=False)
    """(Read Only) If this is the latest version of the object."""

    file_handle: Optional[FileHandle] = field(default=None, compare=False)
    """(Read Only) The file handle associated with this entity."""

    _last_persistent_instance: Optional["File"] = field(
        default=None, repr=False, compare=False
    )
    """The last persistent instance of this object. This is used to determine if the
    object has been changed and needs to be updated in Synapse."""

    @property
    def has_changed(self) -> bool:
        """Determines if the object has been changed and needs to be updated in Synapse."""
        return (
            not self._last_persistent_instance or self._last_persistent_instance != self
        )

    def _set_last_persistent_instance(self) -> None:
        """Stash the last time this object interacted with Synapse. This is used to
        determine if the object has been changed and needs to be updated in Synapse."""
        del self._last_persistent_instance
        self._last_persistent_instance = dataclasses.replace(self)
        self._last_persistent_instance.activity = (
            dataclasses.replace(self.activity) if self.activity else None
        )
        self._last_persistent_instance.annotations = (
            deepcopy(self.annotations) if self.annotations else {}
        )

    def _fill_from_file_handle(self) -> None:
        """Fill the file object from the file handle."""
        if self.file_handle:
            self.data_file_handle_id = self.file_handle.id
            self.content_type = self.file_handle.content_type
            self.content_size = self.file_handle.content_size
            self.external_url = self.file_handle.external_url

    def fill_from_dict(
        self,
        synapse_file: Union[Synapse_File, Dict[str, Union[bool, str, int]]],
        set_annotations: bool = True,
    ) -> "File":
        """
        Converts a response from the REST API into this dataclass.

        Arguments:
            synapse_file: The response from the REST API.
            set_annotations: Whether to set the annotations from the response.

        Returns:
            The File object.
        """
        self.id = synapse_file.get("id", None)
        self.name = synapse_file.get("name", None)
        self.description = synapse_file.get("description", None)
        self.etag = synapse_file.get("etag", None)
        self.created_on = synapse_file.get("createdOn", None)
        self.modified_on = synapse_file.get("modifiedOn", None)
        self.created_by = synapse_file.get("createdBy", None)
        self.modified_by = synapse_file.get("modifiedBy", None)
        self.parent_id = synapse_file.get("parentId", None)
        self.version_number = synapse_file.get("versionNumber", None)
        self.version_label = synapse_file.get("versionLabel", None)
        self.version_comment = synapse_file.get("versionComment", None)
        self.is_latest_version = synapse_file.get("isLatestVersion", False)
        self.data_file_handle_id = synapse_file.get("dataFileHandleId", None)
        self.path = synapse_file.get("path", self.path)
        synapse_file_handle = synapse_file.get("_file_handle", None)
        if synapse_file_handle:
            file_handle = self.file_handle or FileHandle()
            self.file_handle = file_handle.fill_from_dict(
                synapse_instance=synapse_file_handle
            )
            self._fill_from_file_handle()

        if set_annotations:
            self.annotations = Annotations.from_dict(
                synapse_file.get("annotations", {})
            )
        return self

    def _cannot_store(self) -> bool:
        """Determines based on some guard conditions if we are unable to continue with
        a store operation."""
        return (
            not (
                self.id is not None
                and (self.path is not None or self.data_file_handle_id is not None)
            )
            and not (self.path is not None and self.parent_id is not None)
            and not (
                self.parent_id is not None and self.data_file_handle_id is not None
            )
        )

    async def _load_local_md5(self) -> None:
        """Load the MD5 of the file if it's a local file and we have not already loaded
        it."""
        if not self.content_md5 and self.path and os.path.isfile(self.path):
            self.content_md5 = utils.md5_for_file_hex(filename=self.path)

    async def _find_existing_file(
        self, *, synapse_client: Optional[Synapse] = None
    ) -> Union["File", None]:
        """Determines if the file already exists in Synapse. If it does it will return
        the file object, otherwise it will return None. This is used to determine if the
        file should be updated or created."""

        async def get_file(existing_id: str) -> "File":
            """Small wrapper to retrieve a file instance without raising an error if it
            does not exist.

            Arguments:
                existing_id: The ID of the file to retrieve.

            Returns:
                The file object if it exists, otherwise None.
            """
            try:
                file_copy = File(
                    id=existing_id,
                    download_file=False,
                    version_number=self.version_number,
                    synapse_container_limit=self.synapse_container_limit,
                    parent_id=self.parent_id,
                )
                return await file_copy.get_async(
                    synapse_client=synapse_client,
                    include_activity=self.activity is not None
                    or self.associate_activity_to_new_version,
                )
            except SynapseFileNotFoundError:
                return None

        if (
            self.create_or_update
            and not self._last_persistent_instance
            and (
                existing_file_id := await get_id(
                    entity=self,
                    failure_strategy=None,
                    synapse_client=synapse_client,
                )
            )
            and (existing_file := await get_file(existing_file_id))
        ):
            return existing_file
        return None

    def _determine_fields_to_ignore_in_merge(self) -> List[str]:
        """This is used to determine what fields should not be merged when merging two
        entities. This allows for a fine tuned destructive update of an entity.

        This also has special handling during a manifest upload of files. If a manifest
        is specifying fields we'll use those values rather than copying them from the
        existing entity. This is to allow for a destructive update of an entity.

        """
        fields_to_not_merge = []
        if not self.merge_existing_annotations:
            fields_to_not_merge.append("annotations")

        if not self.associate_activity_to_new_version:
            fields_to_not_merge.append("activity")

        if self._present_manifest_fields:
            if "name" in self._present_manifest_fields:
                fields_to_not_merge.append("name")

            if "contentType" in self._present_manifest_fields:
                fields_to_not_merge.append("content_type")

        return fields_to_not_merge

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"File_Store: {self.path if self.path else self.id}"
    )
    async def store_async(
        self,
        parent: Optional[Union["Folder", "Project"]] = None,
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> "File":
        """
        Store the file in Synapse. With this method you may:

        - Upload a file into Synapse
        - Update the metadata of a file in Synapse
        - Store a File object in Synapse without updating a file by setting
            `synapse_store` to False.
        - Change the name of a file in Synapse by setting the `name` attribute of the
            File object. Also see the [synapseclient.models.File.change_metadata][]
            method for changing the name of the downloaded file.
        - Moving a file to a new parent by setting the `parent_id` attribute of the
            File object.

        If no Name is specified this will be derived from the file name. This is the
        reccommended way to store a file in Synapse.

        Please note:
        The file, as it appears on disk, will be the file that is downloaded from
        Synapse. The name of the actual File is different from the name of the File
        Entity in Synapse. It is generally not reccommended to specify a different
        name for the Entity and the file as it will cause confusion and potential
        conflicts later on.

        Arguments:
            parent: The parent folder or project to store the file in. May also be
                specified in the File object. If both are provided the parent passed
                into `store` will take precedence.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The file object.

        Raises:
            ValueError: If the file does not have an ID and a path, or a path and a
                parent ID, or a data file handle ID and a parent ID.

        Example: Using this function
            File with the ID `syn123` at path `path/to/file.txt`:

                file_instance = await File(id="syn123", path="path/to/file.txt").store_async()

            File at the path `path/to/file.txt` and a parent folder with the ID `syn456`:

                file_instance = await File(path="path/to/file.txt", parent_id="syn456").store_async()

            File at the path `path/to/file.txt` and a parent folder with the ID `syn456`:

                file_instance = await File(path="path/to/file.txt").store_async(parent=Folder(id="syn456"))

            File with a parent and existing file handle (This allows multiple entities to reference the underlying file):

                file_instance = await File(data_file_handle_id="123", parent_id="syn456").store_async()

            Rename a file (Does not update the file on disk or the name of the downloaded file):

                file_instance = await File(id="syn123", download_file=False).get_async()
                print(file_instance.name)  ## prints, e.g., "my_file.txt"
                await file_instance.change_metadata_async(name="my_new_name_file.txt")

            Rename a file, and the name of the file as downloaded
                (Does not update the file on disk). Is is reccommended that `name` and
                `download_as` match to prevent confusion later on:

                file_instance = await File(id="syn123", download_file=False).get_async()
                print(file_instance.name)  ## prints, e.g., "my_file.txt"
                await file_instance.change_metadata_async(name="my_new_name_file.txt", download_as="my_new_name_file.txt")

        """
        self.parent_id = parent.id if parent else self.parent_id
        if self._cannot_store():
            raise ValueError(
                "The file must have an (ID with a (path or `data_file_handle_id`)), or a "
                "(path with a (`parent_id` or parent with an id)), or a "
                "(data_file_handle_id with a (`parent_id` or parent with an id)) to store."
            )
        self.name = self.name or (guess_file_name(self.path) if self.path else None)
        client = Synapse.get_client(synapse_client=synapse_client)

        if existing_file := await self._find_existing_file(synapse_client=client):
            merge_dataclass_entities(
                source=existing_file,
                destination=self,
                fields_to_ignore=self._determine_fields_to_ignore_in_merge(),
            )

        if self.path:
            self.path = os.path.expanduser(self.path)
            async with client._get_parallel_file_transfer_semaphore(
                asyncio_event_loop=asyncio.get_running_loop()
            ):
                await self._upload_file(synapse_client=client)
        elif self.data_file_handle_id:
            self.path = client.cache.get(file_handle_id=self.data_file_handle_id)

        if self.has_changed:
            synapse_file = Synapse_File(
                id=self.id,
                path=self.path,
                description=self.description,
                etag=self.etag,
                name=self.name,
                parent=parent.id if parent else self.parent_id,
                contentType=self.content_type,
                contentSize=self.content_size,
                dataFileHandleId=self.data_file_handle_id,
                synapseStore=self.synapse_store,
                modifiedOn=self.modified_on,
                versionLabel=self.version_label,
                versionNumber=self.version_number,
                versionComment=self.version_comment,
            )
            delete_none_keys(synapse_file)

            entity = await store_entity(
                resource=self, entity=synapse_file, synapse_client=client
            )

            self.fill_from_dict(synapse_file=entity, set_annotations=False)

        re_read_required = await store_entity_components(
            root_resource=self, synapse_client=client
        )
        if re_read_required:
            before_download_file = self.download_file
            self.download_file = False
            await self.get_async(
                synapse_client=client,
            )
            self.download_file = before_download_file

        self._set_last_persistent_instance()

        client.logger.debug(f"Stored File {self.name}, id: {self.id}: {self.path}")
        # Clear the content_md5 so that it is recalculated if the file is updated
        self.content_md5 = None
        return self

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"File_Change_Metadata: {self.id}"
    )
    async def change_metadata_async(
        self,
        name: Optional[str] = None,
        download_as: Optional[str] = None,
        content_type: Optional[str] = None,
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> "File":
        """
        Change File Entity metadata for properties that are immutable after creation
        through the store method.

        Arguments:
            name: Specify to change the filename of a file as seen on Synapse.
            download_as: Specify filename to change the filename of a filehandle.
            content_type: Specify content type to change the content type of a
                filehandle.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The file object.

        Example: Using this function
            Can be used to change the filename, the filename when the file is
            downloaded, or the file content-type without downloading:

                file_entity = await File(id="syn123", download_file=False).get_async()
                print(os.path.basename(file_entity.path))  ## prints, e.g., "my_file.txt"
                file_entity = await file_entity.change_metadata_async(name="my_new_name_file.txt", download_as="my_new_downloadAs_name_file.txt", content_type="text/plain")
                print(os.path.basename(file_entity.path))  ## prints, "my_new_downloadAs_name_file.txt"
                print(file_entity.name) ## prints, "my_new_name_file.txt"

        Raises:
            ValueError: If the file does not have an ID to change metadata.
        """
        if not self.id:
            raise ValueError("The file must have an ID to change metadata.")
        from synapseutils.copy_functions import changeFileMetaData

        loop = asyncio.get_event_loop()

        syn = Synapse.get_client(synapse_client=synapse_client)
        entity = await loop.run_in_executor(
            None,
            lambda: changeFileMetaData(
                syn=syn,
                entity=self.id,
                name=name,
                downloadAs=download_as,
                contentType=content_type,
                forceVersion=self.force_version,
            ),
        )

        self.fill_from_dict(synapse_file=entity, set_annotations=True)
        self._set_last_persistent_instance()
        Synapse.get_client(synapse_client=synapse_client).logger.debug(
            f"Change metadata for file {self.name}, id: {self.id}: {self.path}"
        )
        return self

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"File_Get: {self.id}, {self.path}"
    )
    async def get_async(
        self,
        include_activity: bool = False,
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> "File":
        """
        Get the file from Synapse. You may retrieve a File entity by either:

        - id
        - path


        If you specify both, the `id` will take precedence.


        If you specify the `path` and the file is stored in multiple locations in
        Synapse only the first one found will be returned. The other matching files
        will be printed to the console.


        You may also specify a `version_number` to get a specific version of the file.

        Arguments:
            include_activity: If True the activity will be included in the file
                if it exists.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The file object.

        Raises:
            ValueError: If the file does not have an ID or path to get.


        Example: Using this function
            Assuming you have a file with the ID "syn123":

                file_instance = await File(id="syn123").get_async()

            Assuming you want to download a file to this directory: "path/to/directory":

                file_instance = await File(path="path/to/directory").get_async()
        """
        if not self.id and not self.path:
            raise ValueError("The file must have an ID or path to get.")
        syn = Synapse.get_client(synapse_client=synapse_client)

        await self._load_local_md5()

        await get_from_entity_factory(
            entity_to_update=self,
            synapse_id_or_path=self.id or self.path,
            version=self.version_number,
            if_collision=self.if_collision,
            limit_search=self.synapse_container_limit or self.parent_id,
            download_file=self.download_file,
            download_location=os.path.dirname(self.path)
            if self.path and os.path.isfile(self.path)
            else self.path,
            md5=self.content_md5,
            synapse_client=syn,
        )

        if (
            self.data_file_handle_id
            and (not self.path or (self.path and not os.path.isfile(self.path)))
            and (cached_path := syn.cache.get(file_handle_id=self.data_file_handle_id))
        ):
            self.path = cached_path

        if include_activity:
            self.activity = await Activity.from_parent_async(
                parent=self, synapse_client=synapse_client
            )

        self._set_last_persistent_instance()
        Synapse.get_client(synapse_client=synapse_client).logger.debug(
            f"Got file {self.name}, id: {self.id}, path: {self.path}"
        )
        return self

    @classmethod
    async def from_id_async(
        cls,
        synapse_id: str,
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> "File":
        """Wrapper for [synapseclient.models.File.get][].

        Arguments:
            synapse_id: The ID of the file in Synapse.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The file object.

        Example: Using this function
            Assuming you have a file with the ID "syn123":

                file_instance = await File.from_id_async(synapse_id="syn123")
        """
        return await cls(id=synapse_id).get_async(
            synapse_client=synapse_client,
        )

    @classmethod
    async def from_path_async(
        cls,
        path: str,
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> "File":
        """Get the file from Synapse. If the path of the file matches multiple files
        within Synapse the first one found will be returned. The other matching
        files will be printed to the console.


        Wrapper for [synapseclient.models.File.get][].

        Arguments:
            path: The path to the file on disk.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The file object.

        Example: Using this function
            Assuming you have a file at the path "path/to/file.txt":

                file_instance = await File.from_path_async(path="path/to/file.txt")
        """
        return await cls(path=path).get_async(
            synapse_client=synapse_client,
        )

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"File_Delete: {self.id}"
    )
    async def delete_async(
        self,
        version_only: Optional[bool] = False,
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> None:
        """
        Delete the file from Synapse using the ID of the file.

        Arguments:
            version_only: If True only the version specified in the `version_number`
                attribute of the file will be deleted. If False the entire file will
                be deleted.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            None

        Raises:
            ValueError: If the file does not have an ID to delete.
            ValueError: If the file does not have a version number to delete a version,
                and `version_only` is True.

        Example: Using this function
            Assuming you have a file with the ID "syn123":

                await File(id="syn123").delete_async()
        """
        if not self.id:
            raise ValueError("The file must have an ID to delete.")
        if version_only and not self.version_number:
            raise ValueError("The file must have a version number to delete a version.")

        loop = asyncio.get_event_loop()
        await loop.run_in_executor(
            None,
            lambda: Synapse.get_client(synapse_client=synapse_client).delete(
                obj=self.id,
                version=self.version_number if version_only else None,
            ),
        )
        Synapse.get_client(synapse_client=synapse_client).logger.debug(
            f"Deleted file {self.id}"
        )

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"File_Copy: {self.id}"
    )
    async def copy_async(
        self,
        parent_id: str,
        update_existing: bool = False,
        copy_annotations: bool = True,
        copy_activity: Union[str, None] = "traceback",
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> "File":
        """
        Copy the file to another Synapse location. Defaults to the latest version of the
        file, or the version_number specified in the instance.

        Arguments:
            parent_id: Synapse ID of a folder/project that the copied entity is being
                copied to
            update_existing: When the destination has a file that has the same name,
                users can choose to update that file.
            copy_annotations: True to copy the annotations.
            copy_activity: Has three options to set the activity of the copied file:

                    - traceback: Creates a copy of the source files Activity.
                    - existing: Link to the source file's original Activity (if it exists)
                    - None: No activity is set
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The copied file object.

        Example: Using this function
            Assuming you have a file with the ID "syn123" and you want to copy it to a folder with the ID "syn456":

                new_file_instance = await File(id="syn123").copy_async(parent_id="syn456")

            Copy the file but do not persist annotations or activity:

                new_file_instance = await File(id="syn123").copy_async(parent_id="syn456", copy_annotations=False, copy_activity=None)

        Raises:
            ValueError: If the file does not have an ID and parent_id to copy.
        """
        if not self.id or not parent_id:
            raise ValueError("The file must have an ID and parent_id to copy.")
        from synapseutils.copy_functions import copy

        loop = asyncio.get_event_loop()

        syn = Synapse.get_client(synapse_client=synapse_client)
        source_and_destination = await loop.run_in_executor(
            None,
            lambda: copy(
                syn=syn,
                version=self.version_number,
                entity=self.id,
                destinationId=parent_id,
                skipCopyAnnotations=not copy_annotations,
                updateExisting=update_existing,
                setProvenance=copy_activity,
            ),
        )

        parent_id = source_and_destination.get(self.id, None)
        if not parent_id:
            raise SynapseError("Failed to copy file.")
        file_copy = await File(id=parent_id, download_file=False).get_async(
            synapse_client=synapse_client
        )
        file_copy.download_file = True
        Synapse.get_client(synapse_client=synapse_client).logger.debug(
            f"Copied from file {self.id} to {parent_id} with new id of {file_copy.id}"
        )
        return file_copy

    async def _needs_upload(self, syn: Synapse) -> bool:
        """
        Determines if a file needs to be uploaded to Synapse. The following conditions
        apply:

        - The file exists and is an ExternalFileHandle and the url has changed
        - The file exists and is a local file and the MD5 has changed
        - The file is not present in Synapse

        If the file is already specifying a data_file_handle_id then it is assumed that
        the file is already uploaded to Synapse. It does not need to be uploaded and
        the only thing that will occur is the File metadata will be added to Synapse
        outside of this upload process.

        Arguments:
            syn: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            True if the file needs to be uploaded, otherwise False.
        """
        needs_upload = False
        # Check if the file should be uploaded
        if self._last_persistent_instance is not None:
            if (
                self.file_handle
                and self.file_handle.concrete_type
                == "org.sagebionetworks.repo.model.file.ExternalFileHandle"
            ):
                # switching away from ExternalFileHandle or the url was updated
                needs_upload = self.synapse_store or (
                    self.file_handle.external_url != self.external_url
                )
            else:
                # Check if we need to upload a new version of an existing
                # file. If the file referred to by entity['path'] has been
                # modified, we want to upload the new version.
                # If synapeStore is false then we must upload a ExternalFileHandle
                needs_upload = (
                    not self.synapse_store
                    or not self.file_handle
                    or not (
                        exists_in_cache := syn.cache.contains(
                            self.file_handle.id, self.path
                        )
                    )
                )

                md5_stored_in_synapse = (
                    self.file_handle.content_md5 if self.file_handle else None
                )

                # Check if we got an MD5 checksum from Synapse and compare it to the local file
                if (
                    self.synapse_store
                    and needs_upload
                    and os.path.isfile(self.path)
                    and md5_stored_in_synapse
                ):
                    await self._load_local_md5()
                    if md5_stored_in_synapse == (
                        local_file_md5_hex := self.content_md5
                    ):
                        needs_upload = False

                    # If we had a cache miss, but already uploaded to Synapse we
                    # can add the file to the cache.
                    if (
                        not exists_in_cache
                        and self.file_handle
                        and self.file_handle.id
                        and local_file_md5_hex
                    ):
                        syn.cache.add(
                            file_handle_id=self.file_handle.id,
                            path=self.path,
                            md5=local_file_md5_hex,
                        )
        elif self.data_file_handle_id is not None:
            needs_upload = False
        else:
            needs_upload = True
        return needs_upload

    async def _upload_file(
        self,
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> "File":
        """The upload process for a file. This will upload the file to Synapse if it
        needs to be uploaded. If the file does not need to be uploaded the file
        metadata will be added to Synapse outside of this upload process.

        Arguments:
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The file object.
        """
        syn = Synapse.get_client(synapse_client=synapse_client)

        needs_upload = await self._needs_upload(syn=syn)

        if needs_upload:
            parent_id_for_upload = self.parent_id

            if not parent_id_for_upload:
                raise SynapseMalformedEntityError(
                    "Entities of type File must have a parentId."
                )

            updated_file_handle = await upload_file_handle(
                syn=syn,
                parent_entity_id=parent_id_for_upload,
                path=(
                    self.path
                    if (self.synapse_store or self.external_url is None)
                    else self.external_url
                ),
                synapse_store=self.synapse_store,
                md5=self.content_md5,
                file_size=self.content_size,
                mimetype=self.content_type,
            )

            self.file_handle = FileHandle().fill_from_dict(updated_file_handle)
            self._fill_from_file_handle()

        return self

    def _convert_into_legacy_file(self) -> SynapseFile:
        """Convert the file object into a SynapseFile object."""
        return_data = SynapseFile(
            id=self.id,
            name=self.name,
            description=self.description,
            etag=self.etag,
            createdOn=self.created_on,
            modifiedOn=self.modified_on,
            createdBy=self.created_by,
            modifiedBy=self.modified_by,
            parentId=self.parent_id,
            versionNumber=self.version_number,
            versionLabel=self.version_label,
            versionComment=self.version_comment,
            dataFileHandleId=self.data_file_handle_id,
            path=self.path,
            properties={
                "isLatestVersion": self.is_latest_version,
            },
            _file_handle=(
                self.file_handle._convert_into_legacy_file_handle()
                if self.file_handle
                else None
            ),
            annotations=self.annotations,
        )
        delete_none_keys(return_data)
        return return_data

Functions

get_async(include_activity=False, *, synapse_client=None) async

Get the file from Synapse. You may retrieve a File entity by either:

  • id
  • path

If you specify both, the id will take precedence.

If you specify the path and the file is stored in multiple locations in Synapse only the first one found will be returned. The other matching files will be printed to the console.

You may also specify a version_number to get a specific version of the file.

PARAMETER DESCRIPTION
include_activity

If True the activity will be included in the file if it exists.

TYPE: bool DEFAULT: False

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
File

The file object.

RAISES DESCRIPTION
ValueError

If the file does not have an ID or path to get.

Using this function

Assuming you have a file with the ID "syn123":

file_instance = await File(id="syn123").get_async()

Assuming you want to download a file to this directory: "path/to/directory":

file_instance = await File(path="path/to/directory").get_async()
Source code in synapseclient/models/file.py
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"File_Get: {self.id}, {self.path}"
)
async def get_async(
    self,
    include_activity: bool = False,
    *,
    synapse_client: Optional[Synapse] = None,
) -> "File":
    """
    Get the file from Synapse. You may retrieve a File entity by either:

    - id
    - path


    If you specify both, the `id` will take precedence.


    If you specify the `path` and the file is stored in multiple locations in
    Synapse only the first one found will be returned. The other matching files
    will be printed to the console.


    You may also specify a `version_number` to get a specific version of the file.

    Arguments:
        include_activity: If True the activity will be included in the file
            if it exists.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The file object.

    Raises:
        ValueError: If the file does not have an ID or path to get.


    Example: Using this function
        Assuming you have a file with the ID "syn123":

            file_instance = await File(id="syn123").get_async()

        Assuming you want to download a file to this directory: "path/to/directory":

            file_instance = await File(path="path/to/directory").get_async()
    """
    if not self.id and not self.path:
        raise ValueError("The file must have an ID or path to get.")
    syn = Synapse.get_client(synapse_client=synapse_client)

    await self._load_local_md5()

    await get_from_entity_factory(
        entity_to_update=self,
        synapse_id_or_path=self.id or self.path,
        version=self.version_number,
        if_collision=self.if_collision,
        limit_search=self.synapse_container_limit or self.parent_id,
        download_file=self.download_file,
        download_location=os.path.dirname(self.path)
        if self.path and os.path.isfile(self.path)
        else self.path,
        md5=self.content_md5,
        synapse_client=syn,
    )

    if (
        self.data_file_handle_id
        and (not self.path or (self.path and not os.path.isfile(self.path)))
        and (cached_path := syn.cache.get(file_handle_id=self.data_file_handle_id))
    ):
        self.path = cached_path

    if include_activity:
        self.activity = await Activity.from_parent_async(
            parent=self, synapse_client=synapse_client
        )

    self._set_last_persistent_instance()
    Synapse.get_client(synapse_client=synapse_client).logger.debug(
        f"Got file {self.name}, id: {self.id}, path: {self.path}"
    )
    return self

store_async(parent=None, *, synapse_client=None) async

Store the file in Synapse. With this method you may:

  • Upload a file into Synapse
  • Update the metadata of a file in Synapse
  • Store a File object in Synapse without updating a file by setting synapse_store to False.
  • Change the name of a file in Synapse by setting the name attribute of the File object. Also see the synapseclient.models.File.change_metadata method for changing the name of the downloaded file.
  • Moving a file to a new parent by setting the parent_id attribute of the File object.

If no Name is specified this will be derived from the file name. This is the reccommended way to store a file in Synapse.

Please note: The file, as it appears on disk, will be the file that is downloaded from Synapse. The name of the actual File is different from the name of the File Entity in Synapse. It is generally not reccommended to specify a different name for the Entity and the file as it will cause confusion and potential conflicts later on.

PARAMETER DESCRIPTION
parent

The parent folder or project to store the file in. May also be specified in the File object. If both are provided the parent passed into store will take precedence.

TYPE: Optional[Union[Folder, Project]] DEFAULT: None

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
File

The file object.

RAISES DESCRIPTION
ValueError

If the file does not have an ID and a path, or a path and a parent ID, or a data file handle ID and a parent ID.

Using this function

File with the ID syn123 at path path/to/file.txt:

file_instance = await File(id="syn123", path="path/to/file.txt").store_async()

File at the path path/to/file.txt and a parent folder with the ID syn456:

file_instance = await File(path="path/to/file.txt", parent_id="syn456").store_async()

File at the path path/to/file.txt and a parent folder with the ID syn456:

file_instance = await File(path="path/to/file.txt").store_async(parent=Folder(id="syn456"))

File with a parent and existing file handle (This allows multiple entities to reference the underlying file):

file_instance = await File(data_file_handle_id="123", parent_id="syn456").store_async()

Rename a file (Does not update the file on disk or the name of the downloaded file):

file_instance = await File(id="syn123", download_file=False).get_async()
print(file_instance.name)  ## prints, e.g., "my_file.txt"
await file_instance.change_metadata_async(name="my_new_name_file.txt")

Rename a file, and the name of the file as downloaded (Does not update the file on disk). Is is reccommended that name and download_as match to prevent confusion later on:

file_instance = await File(id="syn123", download_file=False).get_async()
print(file_instance.name)  ## prints, e.g., "my_file.txt"
await file_instance.change_metadata_async(name="my_new_name_file.txt", download_as="my_new_name_file.txt")
Source code in synapseclient/models/file.py
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"File_Store: {self.path if self.path else self.id}"
)
async def store_async(
    self,
    parent: Optional[Union["Folder", "Project"]] = None,
    *,
    synapse_client: Optional[Synapse] = None,
) -> "File":
    """
    Store the file in Synapse. With this method you may:

    - Upload a file into Synapse
    - Update the metadata of a file in Synapse
    - Store a File object in Synapse without updating a file by setting
        `synapse_store` to False.
    - Change the name of a file in Synapse by setting the `name` attribute of the
        File object. Also see the [synapseclient.models.File.change_metadata][]
        method for changing the name of the downloaded file.
    - Moving a file to a new parent by setting the `parent_id` attribute of the
        File object.

    If no Name is specified this will be derived from the file name. This is the
    reccommended way to store a file in Synapse.

    Please note:
    The file, as it appears on disk, will be the file that is downloaded from
    Synapse. The name of the actual File is different from the name of the File
    Entity in Synapse. It is generally not reccommended to specify a different
    name for the Entity and the file as it will cause confusion and potential
    conflicts later on.

    Arguments:
        parent: The parent folder or project to store the file in. May also be
            specified in the File object. If both are provided the parent passed
            into `store` will take precedence.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The file object.

    Raises:
        ValueError: If the file does not have an ID and a path, or a path and a
            parent ID, or a data file handle ID and a parent ID.

    Example: Using this function
        File with the ID `syn123` at path `path/to/file.txt`:

            file_instance = await File(id="syn123", path="path/to/file.txt").store_async()

        File at the path `path/to/file.txt` and a parent folder with the ID `syn456`:

            file_instance = await File(path="path/to/file.txt", parent_id="syn456").store_async()

        File at the path `path/to/file.txt` and a parent folder with the ID `syn456`:

            file_instance = await File(path="path/to/file.txt").store_async(parent=Folder(id="syn456"))

        File with a parent and existing file handle (This allows multiple entities to reference the underlying file):

            file_instance = await File(data_file_handle_id="123", parent_id="syn456").store_async()

        Rename a file (Does not update the file on disk or the name of the downloaded file):

            file_instance = await File(id="syn123", download_file=False).get_async()
            print(file_instance.name)  ## prints, e.g., "my_file.txt"
            await file_instance.change_metadata_async(name="my_new_name_file.txt")

        Rename a file, and the name of the file as downloaded
            (Does not update the file on disk). Is is reccommended that `name` and
            `download_as` match to prevent confusion later on:

            file_instance = await File(id="syn123", download_file=False).get_async()
            print(file_instance.name)  ## prints, e.g., "my_file.txt"
            await file_instance.change_metadata_async(name="my_new_name_file.txt", download_as="my_new_name_file.txt")

    """
    self.parent_id = parent.id if parent else self.parent_id
    if self._cannot_store():
        raise ValueError(
            "The file must have an (ID with a (path or `data_file_handle_id`)), or a "
            "(path with a (`parent_id` or parent with an id)), or a "
            "(data_file_handle_id with a (`parent_id` or parent with an id)) to store."
        )
    self.name = self.name or (guess_file_name(self.path) if self.path else None)
    client = Synapse.get_client(synapse_client=synapse_client)

    if existing_file := await self._find_existing_file(synapse_client=client):
        merge_dataclass_entities(
            source=existing_file,
            destination=self,
            fields_to_ignore=self._determine_fields_to_ignore_in_merge(),
        )

    if self.path:
        self.path = os.path.expanduser(self.path)
        async with client._get_parallel_file_transfer_semaphore(
            asyncio_event_loop=asyncio.get_running_loop()
        ):
            await self._upload_file(synapse_client=client)
    elif self.data_file_handle_id:
        self.path = client.cache.get(file_handle_id=self.data_file_handle_id)

    if self.has_changed:
        synapse_file = Synapse_File(
            id=self.id,
            path=self.path,
            description=self.description,
            etag=self.etag,
            name=self.name,
            parent=parent.id if parent else self.parent_id,
            contentType=self.content_type,
            contentSize=self.content_size,
            dataFileHandleId=self.data_file_handle_id,
            synapseStore=self.synapse_store,
            modifiedOn=self.modified_on,
            versionLabel=self.version_label,
            versionNumber=self.version_number,
            versionComment=self.version_comment,
        )
        delete_none_keys(synapse_file)

        entity = await store_entity(
            resource=self, entity=synapse_file, synapse_client=client
        )

        self.fill_from_dict(synapse_file=entity, set_annotations=False)

    re_read_required = await store_entity_components(
        root_resource=self, synapse_client=client
    )
    if re_read_required:
        before_download_file = self.download_file
        self.download_file = False
        await self.get_async(
            synapse_client=client,
        )
        self.download_file = before_download_file

    self._set_last_persistent_instance()

    client.logger.debug(f"Stored File {self.name}, id: {self.id}: {self.path}")
    # Clear the content_md5 so that it is recalculated if the file is updated
    self.content_md5 = None
    return self

copy_async(parent_id, update_existing=False, copy_annotations=True, copy_activity='traceback', *, synapse_client=None) async

Copy the file to another Synapse location. Defaults to the latest version of the file, or the version_number specified in the instance.

PARAMETER DESCRIPTION
parent_id

Synapse ID of a folder/project that the copied entity is being copied to

TYPE: str

update_existing

When the destination has a file that has the same name, users can choose to update that file.

TYPE: bool DEFAULT: False

copy_annotations

True to copy the annotations.

TYPE: bool DEFAULT: True

copy_activity

Has three options to set the activity of the copied file:

- traceback: Creates a copy of the source files Activity.
- existing: Link to the source file's original Activity (if it exists)
- None: No activity is set

TYPE: Union[str, None] DEFAULT: 'traceback'

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
File

The copied file object.

Using this function

Assuming you have a file with the ID "syn123" and you want to copy it to a folder with the ID "syn456":

new_file_instance = await File(id="syn123").copy_async(parent_id="syn456")

Copy the file but do not persist annotations or activity:

new_file_instance = await File(id="syn123").copy_async(parent_id="syn456", copy_annotations=False, copy_activity=None)
RAISES DESCRIPTION
ValueError

If the file does not have an ID and parent_id to copy.

Source code in synapseclient/models/file.py
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"File_Copy: {self.id}"
)
async def copy_async(
    self,
    parent_id: str,
    update_existing: bool = False,
    copy_annotations: bool = True,
    copy_activity: Union[str, None] = "traceback",
    *,
    synapse_client: Optional[Synapse] = None,
) -> "File":
    """
    Copy the file to another Synapse location. Defaults to the latest version of the
    file, or the version_number specified in the instance.

    Arguments:
        parent_id: Synapse ID of a folder/project that the copied entity is being
            copied to
        update_existing: When the destination has a file that has the same name,
            users can choose to update that file.
        copy_annotations: True to copy the annotations.
        copy_activity: Has three options to set the activity of the copied file:

                - traceback: Creates a copy of the source files Activity.
                - existing: Link to the source file's original Activity (if it exists)
                - None: No activity is set
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The copied file object.

    Example: Using this function
        Assuming you have a file with the ID "syn123" and you want to copy it to a folder with the ID "syn456":

            new_file_instance = await File(id="syn123").copy_async(parent_id="syn456")

        Copy the file but do not persist annotations or activity:

            new_file_instance = await File(id="syn123").copy_async(parent_id="syn456", copy_annotations=False, copy_activity=None)

    Raises:
        ValueError: If the file does not have an ID and parent_id to copy.
    """
    if not self.id or not parent_id:
        raise ValueError("The file must have an ID and parent_id to copy.")
    from synapseutils.copy_functions import copy

    loop = asyncio.get_event_loop()

    syn = Synapse.get_client(synapse_client=synapse_client)
    source_and_destination = await loop.run_in_executor(
        None,
        lambda: copy(
            syn=syn,
            version=self.version_number,
            entity=self.id,
            destinationId=parent_id,
            skipCopyAnnotations=not copy_annotations,
            updateExisting=update_existing,
            setProvenance=copy_activity,
        ),
    )

    parent_id = source_and_destination.get(self.id, None)
    if not parent_id:
        raise SynapseError("Failed to copy file.")
    file_copy = await File(id=parent_id, download_file=False).get_async(
        synapse_client=synapse_client
    )
    file_copy.download_file = True
    Synapse.get_client(synapse_client=synapse_client).logger.debug(
        f"Copied from file {self.id} to {parent_id} with new id of {file_copy.id}"
    )
    return file_copy

delete_async(version_only=False, *, synapse_client=None) async

Delete the file from Synapse using the ID of the file.

PARAMETER DESCRIPTION
version_only

If True only the version specified in the version_number attribute of the file will be deleted. If False the entire file will be deleted.

TYPE: Optional[bool] DEFAULT: False

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
None

None

RAISES DESCRIPTION
ValueError

If the file does not have an ID to delete.

ValueError

If the file does not have a version number to delete a version, and version_only is True.

Using this function

Assuming you have a file with the ID "syn123":

await File(id="syn123").delete_async()
Source code in synapseclient/models/file.py
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"File_Delete: {self.id}"
)
async def delete_async(
    self,
    version_only: Optional[bool] = False,
    *,
    synapse_client: Optional[Synapse] = None,
) -> None:
    """
    Delete the file from Synapse using the ID of the file.

    Arguments:
        version_only: If True only the version specified in the `version_number`
            attribute of the file will be deleted. If False the entire file will
            be deleted.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        None

    Raises:
        ValueError: If the file does not have an ID to delete.
        ValueError: If the file does not have a version number to delete a version,
            and `version_only` is True.

    Example: Using this function
        Assuming you have a file with the ID "syn123":

            await File(id="syn123").delete_async()
    """
    if not self.id:
        raise ValueError("The file must have an ID to delete.")
    if version_only and not self.version_number:
        raise ValueError("The file must have a version number to delete a version.")

    loop = asyncio.get_event_loop()
    await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).delete(
            obj=self.id,
            version=self.version_number if version_only else None,
        ),
    )
    Synapse.get_client(synapse_client=synapse_client).logger.debug(
        f"Deleted file {self.id}"
    )

from_id_async(synapse_id, *, synapse_client=None) async classmethod

Wrapper for synapseclient.models.File.get.

PARAMETER DESCRIPTION
synapse_id

The ID of the file in Synapse.

TYPE: str

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
File

The file object.

Using this function

Assuming you have a file with the ID "syn123":

file_instance = await File.from_id_async(synapse_id="syn123")
Source code in synapseclient/models/file.py
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
@classmethod
async def from_id_async(
    cls,
    synapse_id: str,
    *,
    synapse_client: Optional[Synapse] = None,
) -> "File":
    """Wrapper for [synapseclient.models.File.get][].

    Arguments:
        synapse_id: The ID of the file in Synapse.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The file object.

    Example: Using this function
        Assuming you have a file with the ID "syn123":

            file_instance = await File.from_id_async(synapse_id="syn123")
    """
    return await cls(id=synapse_id).get_async(
        synapse_client=synapse_client,
    )

from_path_async(path, *, synapse_client=None) async classmethod

Get the file from Synapse. If the path of the file matches multiple files within Synapse the first one found will be returned. The other matching files will be printed to the console.

Wrapper for synapseclient.models.File.get.

PARAMETER DESCRIPTION
path

The path to the file on disk.

TYPE: str

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
File

The file object.

Using this function

Assuming you have a file at the path "path/to/file.txt":

file_instance = await File.from_path_async(path="path/to/file.txt")
Source code in synapseclient/models/file.py
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
@classmethod
async def from_path_async(
    cls,
    path: str,
    *,
    synapse_client: Optional[Synapse] = None,
) -> "File":
    """Get the file from Synapse. If the path of the file matches multiple files
    within Synapse the first one found will be returned. The other matching
    files will be printed to the console.


    Wrapper for [synapseclient.models.File.get][].

    Arguments:
        path: The path to the file on disk.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The file object.

    Example: Using this function
        Assuming you have a file at the path "path/to/file.txt":

            file_instance = await File.from_path_async(path="path/to/file.txt")
    """
    return await cls(path=path).get_async(
        synapse_client=synapse_client,
    )

change_metadata_async(name=None, download_as=None, content_type=None, *, synapse_client=None) async

Change File Entity metadata for properties that are immutable after creation through the store method.

PARAMETER DESCRIPTION
name

Specify to change the filename of a file as seen on Synapse.

TYPE: Optional[str] DEFAULT: None

download_as

Specify filename to change the filename of a filehandle.

TYPE: Optional[str] DEFAULT: None

content_type

Specify content type to change the content type of a filehandle.

TYPE: Optional[str] DEFAULT: None

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
File

The file object.

Using this function

Can be used to change the filename, the filename when the file is downloaded, or the file content-type without downloading:

file_entity = await File(id="syn123", download_file=False).get_async()
print(os.path.basename(file_entity.path))  ## prints, e.g., "my_file.txt"
file_entity = await file_entity.change_metadata_async(name="my_new_name_file.txt", download_as="my_new_downloadAs_name_file.txt", content_type="text/plain")
print(os.path.basename(file_entity.path))  ## prints, "my_new_downloadAs_name_file.txt"
print(file_entity.name) ## prints, "my_new_name_file.txt"
RAISES DESCRIPTION
ValueError

If the file does not have an ID to change metadata.

Source code in synapseclient/models/file.py
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"File_Change_Metadata: {self.id}"
)
async def change_metadata_async(
    self,
    name: Optional[str] = None,
    download_as: Optional[str] = None,
    content_type: Optional[str] = None,
    *,
    synapse_client: Optional[Synapse] = None,
) -> "File":
    """
    Change File Entity metadata for properties that are immutable after creation
    through the store method.

    Arguments:
        name: Specify to change the filename of a file as seen on Synapse.
        download_as: Specify filename to change the filename of a filehandle.
        content_type: Specify content type to change the content type of a
            filehandle.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The file object.

    Example: Using this function
        Can be used to change the filename, the filename when the file is
        downloaded, or the file content-type without downloading:

            file_entity = await File(id="syn123", download_file=False).get_async()
            print(os.path.basename(file_entity.path))  ## prints, e.g., "my_file.txt"
            file_entity = await file_entity.change_metadata_async(name="my_new_name_file.txt", download_as="my_new_downloadAs_name_file.txt", content_type="text/plain")
            print(os.path.basename(file_entity.path))  ## prints, "my_new_downloadAs_name_file.txt"
            print(file_entity.name) ## prints, "my_new_name_file.txt"

    Raises:
        ValueError: If the file does not have an ID to change metadata.
    """
    if not self.id:
        raise ValueError("The file must have an ID to change metadata.")
    from synapseutils.copy_functions import changeFileMetaData

    loop = asyncio.get_event_loop()

    syn = Synapse.get_client(synapse_client=synapse_client)
    entity = await loop.run_in_executor(
        None,
        lambda: changeFileMetaData(
            syn=syn,
            entity=self.id,
            name=name,
            downloadAs=download_as,
            contentType=content_type,
            forceVersion=self.force_version,
        ),
    )

    self.fill_from_dict(synapse_file=entity, set_annotations=True)
    self._set_last_persistent_instance()
    Synapse.get_client(synapse_client=synapse_client).logger.debug(
        f"Change metadata for file {self.name}, id: {self.id}: {self.path}"
    )
    return self

get_permissions_async(*, synapse_client=None) async

Get the permissions that the caller has on an Entity.

PARAMETER DESCRIPTION
synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Permissions

A Permissions object

Using this function:

Getting permissions for a Synapse Entity

permissions = await File(id="syn123").get_permissions_async()

Getting access types list from the Permissions object

permissions.access_types
Source code in synapseclient/models/mixins/access_control.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
async def get_permissions_async(
    self,
    *,
    synapse_client: Optional[Synapse] = None,
) -> "Permissions":
    """
    Get the [permissions][synapseclient.core.models.permission.Permissions]
    that the caller has on an Entity.

    Arguments:
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        A Permissions object


    Example: Using this function:
        Getting permissions for a Synapse Entity

            permissions = await File(id="syn123").get_permissions_async()

        Getting access types list from the Permissions object

            permissions.access_types
    """
    loop = asyncio.get_event_loop()

    return await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).get_permissions(
            entity=self.id
        ),
    )

get_acl_async(principal_id=None, *, synapse_client=None) async

Get the ACL that a user or group has on an Entity.

PARAMETER DESCRIPTION
principal_id

Identifier of a user or group (defaults to PUBLIC users)

TYPE: int DEFAULT: None

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
List[str]

An array containing some combination of ['READ', 'UPDATE', 'CREATE', 'DELETE', 'DOWNLOAD', 'MODERATE', 'CHANGE_PERMISSIONS', 'CHANGE_SETTINGS'] or an empty array

Source code in synapseclient/models/mixins/access_control.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
async def get_acl_async(
    self, principal_id: int = None, *, synapse_client: Optional[Synapse] = None
) -> List[str]:
    """
    Get the [ACL][synapseclient.core.models.permission.Permissions.access_types]
    that a user or group has on an Entity.

    Arguments:
        principal_id: Identifier of a user or group (defaults to PUBLIC users)
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        An array containing some combination of
            ['READ', 'UPDATE', 'CREATE', 'DELETE', 'DOWNLOAD', 'MODERATE',
            'CHANGE_PERMISSIONS', 'CHANGE_SETTINGS']
            or an empty array
    """
    loop = asyncio.get_event_loop()

    return await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).get_acl(
            entity=self.id, principal_id=principal_id
        ),
    )

set_permissions_async(principal_id=None, access_type=None, modify_benefactor=False, warn_if_inherits=True, overwrite=True, *, synapse_client=None) async

Sets permission that a user or group has on an Entity. An Entity may have its own ACL or inherit its ACL from a benefactor.

PARAMETER DESCRIPTION
principal_id

Identifier of a user or group. 273948 is for all registered Synapse users and 273949 is for public access. None implies public access.

TYPE: int DEFAULT: None

access_type

Type of permission to be granted. One or more of CREATE, READ, DOWNLOAD, UPDATE, DELETE, CHANGE_PERMISSIONS.

Defaults to ['READ', 'DOWNLOAD']

TYPE: List[str] DEFAULT: None

modify_benefactor

Set as True when modifying a benefactor's ACL

TYPE: bool DEFAULT: False

warn_if_inherits

Set as False, when creating a new ACL. Trying to modify the ACL of an Entity that inherits its ACL will result in a warning

TYPE: bool DEFAULT: True

overwrite

By default this function overwrites existing permissions for the specified user. Set this flag to False to add new permissions non-destructively.

TYPE: bool DEFAULT: True

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Dict[str, Union[str, list]]

An Access Control List object

Setting permissions

Grant all registered users download access

await File(id="syn123").set_permissions_async(principal_id=273948, access_type=['READ','DOWNLOAD'])

Grant the public view access

await File(id="syn123").set_permissions_async(principal_id=273949, access_type=['READ'])
Source code in synapseclient/models/mixins/access_control.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
async def set_permissions_async(
    self,
    principal_id: int = None,
    access_type: List[str] = None,
    modify_benefactor: bool = False,
    warn_if_inherits: bool = True,
    overwrite: bool = True,
    *,
    synapse_client: Optional[Synapse] = None,
) -> Dict[str, Union[str, list]]:
    """
    Sets permission that a user or group has on an Entity.
    An Entity may have its own ACL or inherit its ACL from a benefactor.

    Arguments:
        principal_id: Identifier of a user or group. `273948` is for all
            registered Synapse users and `273949` is for public access.
            None implies public access.
        access_type: Type of permission to be granted. One or more of CREATE,
            READ, DOWNLOAD, UPDATE, DELETE, CHANGE_PERMISSIONS.

            **Defaults to ['READ', 'DOWNLOAD']**
        modify_benefactor: Set as True when modifying a benefactor's ACL
        warn_if_inherits: Set as False, when creating a new ACL. Trying to modify
            the ACL of an Entity that inherits its ACL will result in a warning
        overwrite: By default this function overwrites existing permissions for
            the specified user. Set this flag to False to add new permissions
            non-destructively.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        An Access Control List object

    Example: Setting permissions
        Grant all registered users download access

            await File(id="syn123").set_permissions_async(principal_id=273948, access_type=['READ','DOWNLOAD'])

        Grant the public view access

            await File(id="syn123").set_permissions_async(principal_id=273949, access_type=['READ'])
    """
    if access_type is None:
        access_type = ["READ", "DOWNLOAD"]
    loop = asyncio.get_event_loop()

    return await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).setPermissions(
            entity=self.id,
            principalId=principal_id,
            accessType=access_type,
            modify_benefactor=modify_benefactor,
            warn_if_inherits=warn_if_inherits,
            overwrite=overwrite,
        ),
    )

synapseclient.models.Table dataclass

Bases: TableSynchronousProtocol, AccessControllable

A Table represents the metadata of a table.

ATTRIBUTE DESCRIPTION
id

The unique immutable ID for this table. A new ID will be generated for new Tables. Once issued, this ID is guaranteed to never change or be re-issued

TYPE: Optional[str]

name

The name of this table. Must be 256 characters or less. Names may only contain: letters, numbers, spaces, underscores, hyphens, periods, plus signs, apostrophes, and parentheses

TYPE: Optional[str]

parent_id

The ID of the Entity that is the parent of this table.

TYPE: Optional[str]

columns

The columns of this table.

TYPE: Optional[List[Column]]

description

The description of this entity. Must be 1000 characters or less.

TYPE: Optional[List[Column]]

etag

Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle concurrent updates. Since the E-Tag changes every time an entity is updated it is used to detect when a client's current representation of an entity is out-of-date.

TYPE: Optional[str]

created_on

The date this table was created.

TYPE: Optional[str]

created_by

The ID of the user that created this table.

TYPE: Optional[str]

modified_on

The date this table was last modified. In YYYY-MM-DD-Thh:mm:ss.sssZ format

TYPE: Optional[str]

modified_by

The ID of the user that last modified this table.

TYPE: Optional[str]

version_number

The version number issued to this version on the object.

TYPE: Optional[int]

version_label

The version label for this table

TYPE: Optional[str]

version_comment

The version comment for this table

TYPE: Optional[str]

is_latest_version

If this is the latest version of the object.

TYPE: Optional[bool]

is_search_enabled

When creating or updating a table or view specifies if full text search should be enabled. Note that enabling full text search might slow down the indexing of the table or view.

TYPE: Optional[bool]

activity

The Activity model represents the main record of Provenance in Synapse. It is analygous to the Activity defined in the W3C Specification on Provenance. Activity cannot be removed during a store operation by setting it to None. You must use: synapseclient.models.Activity.delete_async or [synapseclient.models.Activity.disassociate_from_entity_async][].

TYPE: Optional[Activity]

annotations

Additional metadata associated with the table. The key is the name of your desired annotations. The value is an object containing a list of values (use empty list to represent no values for key) and the value type associated with all values in the list. To remove all annotations set this to an empty dict {} or None and store the entity.

TYPE: Optional[Dict[str, Union[List[str], List[bool], List[float], List[int], List[date], List[datetime]]]]

Source code in synapseclient/models/table.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
@dataclass()
@async_to_sync
class Table(TableSynchronousProtocol, AccessControllable):
    """A Table represents the metadata of a table.

    Attributes:
        id: The unique immutable ID for this table. A new ID will be generated for new
            Tables. Once issued, this ID is guaranteed to never change or be re-issued
        name: The name of this table. Must be 256 characters or less. Names may only
            contain: letters, numbers, spaces, underscores, hyphens, periods, plus
            signs, apostrophes, and parentheses
        parent_id: The ID of the Entity that is the parent of this table.
        columns: The columns of this table.
        description: The description of this entity. Must be 1000 characters or less.
        etag: Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle
            concurrent updates. Since the E-Tag changes every time an entity is updated
            it is used to detect when a client's current representation of an entity is
            out-of-date.
        created_on: The date this table was created.
        created_by: The ID of the user that created this table.
        modified_on: The date this table was last modified.
            In YYYY-MM-DD-Thh:mm:ss.sssZ format
        modified_by: The ID of the user that last modified this table.
        version_number: The version number issued to this version on the object.
        version_label: The version label for this table
        version_comment: The version comment for this table
        is_latest_version: If this is the latest version of the object.
        is_search_enabled: When creating or updating a table or view specifies if full
            text search should be enabled. Note that enabling full text search might
            slow down the indexing of the table or view.
        activity: The Activity model represents the main record of Provenance in
            Synapse. It is analygous to the Activity defined in the
            [W3C Specification](https://www.w3.org/TR/prov-n/) on Provenance. Activity
            cannot be removed during a store operation by setting it to None. You must
            use: [synapseclient.models.Activity.delete_async][] or
            [synapseclient.models.Activity.disassociate_from_entity_async][].
        annotations: Additional metadata associated with the table. The key is the name
            of your desired annotations. The value is an object containing a list of
            values (use empty list to represent no values for key) and the value type
            associated with all values in the list. To remove all annotations set this
            to an empty dict `{}` or None and store the entity.

    """

    id: Optional[str] = None
    """The unique immutable ID for this table. A new ID will be generated for new
    Tables. Once issued, this ID is guaranteed to never change or be re-issued"""

    name: Optional[str] = None
    """The name of this table. Must be 256 characters or less. Names may only
    contain: letters, numbers, spaces, underscores, hyphens, periods, plus signs,
    apostrophes, and parentheses"""

    parent_id: Optional[str] = None
    """The ID of the Entity that is the parent of this table."""

    columns: Optional[List[Column]] = None

    # TODO: Description doesn't seem to be returned from the API. Look into why.
    # description: Optional[str] = None
    # """The description of this entity. Must be 1000 characters or less."""

    etag: Optional[str] = None
    """
    Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle
    concurrent updates. Since the E-Tag changes every time an entity is updated it is
    used to detect when a client's current representation of an entity is out-of-date.
    """

    created_on: Optional[str] = None
    """The date this table was created."""

    created_by: Optional[str] = None
    """The ID of the user that created this table."""

    modified_on: Optional[str] = None
    """The date this table was last modified. In YYYY-MM-DD-Thh:mm:ss.sssZ format"""

    modified_by: Optional[str] = None
    """The ID of the user that last modified this table."""

    version_number: Optional[int] = None
    """The version number issued to this version on the object."""

    version_label: Optional[str] = None
    """The version label for this table"""

    version_comment: Optional[str] = None
    """The version comment for this table"""

    is_latest_version: Optional[bool] = None
    """If this is the latest version of the object."""

    is_search_enabled: Optional[bool] = None
    """When creating or updating a table or view specifies if full text search
    should be enabled. Note that enabling full text search might slow down the
    indexing of the table or view."""

    activity: Optional[Activity] = None
    """The Activity model represents the main record of Provenance in Synapse.  It is
    analygous to the Activity defined in the
    [W3C Specification](https://www.w3.org/TR/prov-n/) on Provenance. Activity cannot
    be removed during a store operation by setting it to None. You must use:
    [synapseclient.models.Activity.delete_async][] or
    [synapseclient.models.Activity.disassociate_from_entity_async][].
    """

    annotations: Optional[
        Dict[
            str,
            Union[
                List[str],
                List[bool],
                List[float],
                List[int],
                List[date],
                List[datetime],
            ],
        ]
    ] = field(default_factory=dict)
    """Additional metadata associated with the table. The key is the name of your
    desired annotations. The value is an object containing a list of values
    (use empty list to represent no values for key) and the value type associated with
    all values in the list. To remove all annotations set this to an empty dict `{}`
    or None and store the entity."""

    def fill_from_dict(
        self, synapse_table: Synapse_Table, set_annotations: bool = True
    ) -> "Table":
        """Converts the data coming from the Synapse API into this datamodel.

        :param synapse_table: The data coming from the Synapse API
        """
        self.id = synapse_table.get("id", None)
        self.name = synapse_table.get("name", None)
        self.parent_id = synapse_table.get("parentId", None)
        # TODO: Description doesn't seem to be returned from the API. Look into why.
        # self.description = synapse_table.description
        self.etag = synapse_table.get("etag", None)
        self.created_on = synapse_table.get("createdOn", None)
        self.created_by = synapse_table.get("createdBy", None)
        self.modified_on = synapse_table.get("modifiedOn", None)
        self.modified_by = synapse_table.get("modifiedBy", None)
        self.version_number = synapse_table.get("versionNumber", None)
        self.version_label = synapse_table.get("versionLabel", None)
        self.version_comment = synapse_table.get("versionComment", None)
        self.is_latest_version = synapse_table.get("isLatestVersion", None)
        self.is_search_enabled = synapse_table.get("isSearchEnabled", False)
        self.columns = [
            Column(id=columnId, name=None, column_type=None)
            for columnId in synapse_table.get("columnIds", [])
        ]
        if set_annotations:
            self.annotations = Annotations.from_dict(
                synapse_table.get("annotations", {})
            )
        return self

    @otel_trace_method(
        method_to_trace_name=lambda _, **kwargs: f"Store_rows_by_csv: {kwargs.get('csv_path', None)}"
    )
    async def store_rows_from_csv_async(
        self, csv_path: str, *, synapse_client: Optional[Synapse] = None
    ) -> str:
        """Takes in a path to a CSV and stores the rows to Synapse.

        Arguments:
            csv_path: The path to the CSV to store.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The path to the CSV that was stored.
        """
        synapse_table = Synapse_Table(schema=self.id, values=csv_path)
        loop = asyncio.get_event_loop()
        entity = await loop.run_in_executor(
            None,
            lambda: Synapse.get_client(synapse_client=synapse_client).store(
                obj=synapse_table
            ),
        )
        print(entity)
        # TODO: What should this return?
        return csv_path

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"Delete_rows: {self.name}"
    )
    async def delete_rows_async(
        self, rows: List[Row], *, synapse_client: Optional[Synapse] = None
    ) -> None:
        """Delete rows from a table.

        Arguments:
            rows: The rows to delete.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            None
        """
        rows_to_delete = []
        for row in rows:
            rows_to_delete.append([row.row_id, row.version_number])
        loop = asyncio.get_event_loop()
        await loop.run_in_executor(
            None,
            lambda: delete_rows(
                syn=Synapse.get_client(synapse_client=synapse_client),
                table_id=self.id,
                row_id_vers_list=rows_to_delete,
            ),
        )

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"Table_Schema_Store: {self.name}"
    )
    async def store_schema_async(
        self, *, synapse_client: Optional[Synapse] = None
    ) -> "Table":
        """Store non-row information about a table including the columns and annotations.

        Arguments:
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The Table instance stored in synapse.
        """
        tasks = []
        if self.columns:
            # TODO: When a table is retrieved via `.get()` we create Column objects but
            # TODO: We only have the ID attribute. THis is causing this if check to eval
            # TODO: To True, however, we aren't actually modifying the column.
            # TODO: Perhaps we should have a `has_changed` boolean on all dataclasses
            # TODO: That we can check to see if we need to store the data.
            tasks.extend(
                column.store_async(synapse_client=synapse_client)
                for column in self.columns
            )
            try:
                results = await asyncio.gather(*tasks, return_exceptions=True)

                # TODO: Proper exception handling
                for result in results:
                    if isinstance(result, Column):
                        print(f"Stored {result.name}")
                    else:
                        if isinstance(result, BaseException):
                            raise result
                        raise ValueError(f"Unknown type: {type(result)}", result)
            except Exception as ex:
                Synapse.get_client(synapse_client=synapse_client).logger.exception(ex)
                print("I hit an exception")

        synapse_schema = Synapse_Schema(
            name=self.name,
            columns=self.columns,
            parent=self.parent_id,
        )
        trace.get_current_span().set_attributes(
            {
                "synapse.name": self.name or "",
                "synapse.id": self.id or "",
            }
        )
        loop = asyncio.get_event_loop()
        entity = await loop.run_in_executor(
            None,
            lambda: Synapse.get_client(synapse_client=synapse_client).store(
                obj=synapse_schema
            ),
        )

        self.fill_from_dict(synapse_table=entity, set_annotations=False)

        re_read_required = await store_entity_components(
            root_resource=self, synapse_client=synapse_client
        )
        if re_read_required:
            await self.get_async(
                synapse_client=synapse_client,
            )

        return self

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"Table_Get: {self.name}"
    )
    async def get_async(self, *, synapse_client: Optional[Synapse] = None) -> "Table":
        """Get the metadata about the table from synapse.

        Arguments:
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The Table instance stored in synapse.
        """
        # TODO: How do we want to support retriving the table? Do we want to support by name, and parent?
        loop = asyncio.get_event_loop()
        entity = await loop.run_in_executor(
            None,
            lambda: Synapse.get_client(synapse_client=synapse_client).get(
                entity=self.id
            ),
        )
        self.fill_from_dict(synapse_table=entity, set_annotations=True)
        return self

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"Table_Delete: {self.name}"
    )
    # TODO: Synapse allows immediate deletion of entities, but the Synapse Client does not
    # TODO: Should we support immediate deletion?
    async def delete_async(self, *, synapse_client: Optional[Synapse] = None) -> None:
        """Delete the table from synapse.

        Arguments:
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            None
        """
        loop = asyncio.get_event_loop()
        await loop.run_in_executor(
            None,
            lambda: Synapse.get_client(synapse_client=synapse_client).delete(
                obj=self.id
            ),
        )

    @classmethod
    async def query_async(
        cls,
        query: str,
        result_format: Union[CsvResultFormat, RowsetResultFormat] = CsvResultFormat(),
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> Union[Synapse_CsvFileTable, Synaspe_TableQueryResult]:
        """Query for data on a table stored in Synapse.

        Arguments:
            query: The query to run.
            result_format: The format of the results. Defaults to CsvResultFormat().
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The results of the query.
        """
        loop = asyncio.get_event_loop()

        # TODO: Future Idea - We stream back a CSV, and let those reading this to handle the CSV however they want
        results = await loop.run_in_executor(
            None,
            lambda: Synapse.get_client(synapse_client=synapse_client).tableQuery(
                query=query,
                **result_format.to_dict(),
            ),
        )
        print(results)
        return results

Functions

get_async(*, synapse_client=None) async

Get the metadata about the table from synapse.

PARAMETER DESCRIPTION
synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Table

The Table instance stored in synapse.

Source code in synapseclient/models/table.py
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"Table_Get: {self.name}"
)
async def get_async(self, *, synapse_client: Optional[Synapse] = None) -> "Table":
    """Get the metadata about the table from synapse.

    Arguments:
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The Table instance stored in synapse.
    """
    # TODO: How do we want to support retriving the table? Do we want to support by name, and parent?
    loop = asyncio.get_event_loop()
    entity = await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).get(
            entity=self.id
        ),
    )
    self.fill_from_dict(synapse_table=entity, set_annotations=True)
    return self

store_schema_async(*, synapse_client=None) async

Store non-row information about a table including the columns and annotations.

PARAMETER DESCRIPTION
synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Table

The Table instance stored in synapse.

Source code in synapseclient/models/table.py
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"Table_Schema_Store: {self.name}"
)
async def store_schema_async(
    self, *, synapse_client: Optional[Synapse] = None
) -> "Table":
    """Store non-row information about a table including the columns and annotations.

    Arguments:
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The Table instance stored in synapse.
    """
    tasks = []
    if self.columns:
        # TODO: When a table is retrieved via `.get()` we create Column objects but
        # TODO: We only have the ID attribute. THis is causing this if check to eval
        # TODO: To True, however, we aren't actually modifying the column.
        # TODO: Perhaps we should have a `has_changed` boolean on all dataclasses
        # TODO: That we can check to see if we need to store the data.
        tasks.extend(
            column.store_async(synapse_client=synapse_client)
            for column in self.columns
        )
        try:
            results = await asyncio.gather(*tasks, return_exceptions=True)

            # TODO: Proper exception handling
            for result in results:
                if isinstance(result, Column):
                    print(f"Stored {result.name}")
                else:
                    if isinstance(result, BaseException):
                        raise result
                    raise ValueError(f"Unknown type: {type(result)}", result)
        except Exception as ex:
            Synapse.get_client(synapse_client=synapse_client).logger.exception(ex)
            print("I hit an exception")

    synapse_schema = Synapse_Schema(
        name=self.name,
        columns=self.columns,
        parent=self.parent_id,
    )
    trace.get_current_span().set_attributes(
        {
            "synapse.name": self.name or "",
            "synapse.id": self.id or "",
        }
    )
    loop = asyncio.get_event_loop()
    entity = await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).store(
            obj=synapse_schema
        ),
    )

    self.fill_from_dict(synapse_table=entity, set_annotations=False)

    re_read_required = await store_entity_components(
        root_resource=self, synapse_client=synapse_client
    )
    if re_read_required:
        await self.get_async(
            synapse_client=synapse_client,
        )

    return self

store_rows_from_csv_async(csv_path, *, synapse_client=None) async

Takes in a path to a CSV and stores the rows to Synapse.

PARAMETER DESCRIPTION
csv_path

The path to the CSV to store.

TYPE: str

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
str

The path to the CSV that was stored.

Source code in synapseclient/models/table.py
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
@otel_trace_method(
    method_to_trace_name=lambda _, **kwargs: f"Store_rows_by_csv: {kwargs.get('csv_path', None)}"
)
async def store_rows_from_csv_async(
    self, csv_path: str, *, synapse_client: Optional[Synapse] = None
) -> str:
    """Takes in a path to a CSV and stores the rows to Synapse.

    Arguments:
        csv_path: The path to the CSV to store.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The path to the CSV that was stored.
    """
    synapse_table = Synapse_Table(schema=self.id, values=csv_path)
    loop = asyncio.get_event_loop()
    entity = await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).store(
            obj=synapse_table
        ),
    )
    print(entity)
    # TODO: What should this return?
    return csv_path

delete_rows_async(rows, *, synapse_client=None) async

Delete rows from a table.

PARAMETER DESCRIPTION
rows

The rows to delete.

TYPE: List[Row]

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
None

None

Source code in synapseclient/models/table.py
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"Delete_rows: {self.name}"
)
async def delete_rows_async(
    self, rows: List[Row], *, synapse_client: Optional[Synapse] = None
) -> None:
    """Delete rows from a table.

    Arguments:
        rows: The rows to delete.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        None
    """
    rows_to_delete = []
    for row in rows:
        rows_to_delete.append([row.row_id, row.version_number])
    loop = asyncio.get_event_loop()
    await loop.run_in_executor(
        None,
        lambda: delete_rows(
            syn=Synapse.get_client(synapse_client=synapse_client),
            table_id=self.id,
            row_id_vers_list=rows_to_delete,
        ),
    )

query_async(query, result_format=CsvResultFormat(), *, synapse_client=None) async classmethod

Query for data on a table stored in Synapse.

PARAMETER DESCRIPTION
query

The query to run.

TYPE: str

result_format

The format of the results. Defaults to CsvResultFormat().

TYPE: Union[CsvResultFormat, RowsetResultFormat] DEFAULT: CsvResultFormat()

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Union[CsvFileTable, TableQueryResult]

The results of the query.

Source code in synapseclient/models/table.py
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
@classmethod
async def query_async(
    cls,
    query: str,
    result_format: Union[CsvResultFormat, RowsetResultFormat] = CsvResultFormat(),
    *,
    synapse_client: Optional[Synapse] = None,
) -> Union[Synapse_CsvFileTable, Synaspe_TableQueryResult]:
    """Query for data on a table stored in Synapse.

    Arguments:
        query: The query to run.
        result_format: The format of the results. Defaults to CsvResultFormat().
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The results of the query.
    """
    loop = asyncio.get_event_loop()

    # TODO: Future Idea - We stream back a CSV, and let those reading this to handle the CSV however they want
    results = await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).tableQuery(
            query=query,
            **result_format.to_dict(),
        ),
    )
    print(results)
    return results

delete_async(*, synapse_client=None) async

Delete the table from synapse.

PARAMETER DESCRIPTION
synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
None

None

Source code in synapseclient/models/table.py
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
@otel_trace_method(
    method_to_trace_name=lambda self, **kwargs: f"Table_Delete: {self.name}"
)
# TODO: Synapse allows immediate deletion of entities, but the Synapse Client does not
# TODO: Should we support immediate deletion?
async def delete_async(self, *, synapse_client: Optional[Synapse] = None) -> None:
    """Delete the table from synapse.

    Arguments:
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        None
    """
    loop = asyncio.get_event_loop()
    await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).delete(
            obj=self.id
        ),
    )

get_permissions_async(*, synapse_client=None) async

Get the permissions that the caller has on an Entity.

PARAMETER DESCRIPTION
synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Permissions

A Permissions object

Using this function:

Getting permissions for a Synapse Entity

permissions = await File(id="syn123").get_permissions_async()

Getting access types list from the Permissions object

permissions.access_types
Source code in synapseclient/models/mixins/access_control.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
async def get_permissions_async(
    self,
    *,
    synapse_client: Optional[Synapse] = None,
) -> "Permissions":
    """
    Get the [permissions][synapseclient.core.models.permission.Permissions]
    that the caller has on an Entity.

    Arguments:
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        A Permissions object


    Example: Using this function:
        Getting permissions for a Synapse Entity

            permissions = await File(id="syn123").get_permissions_async()

        Getting access types list from the Permissions object

            permissions.access_types
    """
    loop = asyncio.get_event_loop()

    return await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).get_permissions(
            entity=self.id
        ),
    )

get_acl_async(principal_id=None, *, synapse_client=None) async

Get the ACL that a user or group has on an Entity.

PARAMETER DESCRIPTION
principal_id

Identifier of a user or group (defaults to PUBLIC users)

TYPE: int DEFAULT: None

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
List[str]

An array containing some combination of ['READ', 'UPDATE', 'CREATE', 'DELETE', 'DOWNLOAD', 'MODERATE', 'CHANGE_PERMISSIONS', 'CHANGE_SETTINGS'] or an empty array

Source code in synapseclient/models/mixins/access_control.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
async def get_acl_async(
    self, principal_id: int = None, *, synapse_client: Optional[Synapse] = None
) -> List[str]:
    """
    Get the [ACL][synapseclient.core.models.permission.Permissions.access_types]
    that a user or group has on an Entity.

    Arguments:
        principal_id: Identifier of a user or group (defaults to PUBLIC users)
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        An array containing some combination of
            ['READ', 'UPDATE', 'CREATE', 'DELETE', 'DOWNLOAD', 'MODERATE',
            'CHANGE_PERMISSIONS', 'CHANGE_SETTINGS']
            or an empty array
    """
    loop = asyncio.get_event_loop()

    return await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).get_acl(
            entity=self.id, principal_id=principal_id
        ),
    )

set_permissions_async(principal_id=None, access_type=None, modify_benefactor=False, warn_if_inherits=True, overwrite=True, *, synapse_client=None) async

Sets permission that a user or group has on an Entity. An Entity may have its own ACL or inherit its ACL from a benefactor.

PARAMETER DESCRIPTION
principal_id

Identifier of a user or group. 273948 is for all registered Synapse users and 273949 is for public access. None implies public access.

TYPE: int DEFAULT: None

access_type

Type of permission to be granted. One or more of CREATE, READ, DOWNLOAD, UPDATE, DELETE, CHANGE_PERMISSIONS.

Defaults to ['READ', 'DOWNLOAD']

TYPE: List[str] DEFAULT: None

modify_benefactor

Set as True when modifying a benefactor's ACL

TYPE: bool DEFAULT: False

warn_if_inherits

Set as False, when creating a new ACL. Trying to modify the ACL of an Entity that inherits its ACL will result in a warning

TYPE: bool DEFAULT: True

overwrite

By default this function overwrites existing permissions for the specified user. Set this flag to False to add new permissions non-destructively.

TYPE: bool DEFAULT: True

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Dict[str, Union[str, list]]

An Access Control List object

Setting permissions

Grant all registered users download access

await File(id="syn123").set_permissions_async(principal_id=273948, access_type=['READ','DOWNLOAD'])

Grant the public view access

await File(id="syn123").set_permissions_async(principal_id=273949, access_type=['READ'])
Source code in synapseclient/models/mixins/access_control.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
async def set_permissions_async(
    self,
    principal_id: int = None,
    access_type: List[str] = None,
    modify_benefactor: bool = False,
    warn_if_inherits: bool = True,
    overwrite: bool = True,
    *,
    synapse_client: Optional[Synapse] = None,
) -> Dict[str, Union[str, list]]:
    """
    Sets permission that a user or group has on an Entity.
    An Entity may have its own ACL or inherit its ACL from a benefactor.

    Arguments:
        principal_id: Identifier of a user or group. `273948` is for all
            registered Synapse users and `273949` is for public access.
            None implies public access.
        access_type: Type of permission to be granted. One or more of CREATE,
            READ, DOWNLOAD, UPDATE, DELETE, CHANGE_PERMISSIONS.

            **Defaults to ['READ', 'DOWNLOAD']**
        modify_benefactor: Set as True when modifying a benefactor's ACL
        warn_if_inherits: Set as False, when creating a new ACL. Trying to modify
            the ACL of an Entity that inherits its ACL will result in a warning
        overwrite: By default this function overwrites existing permissions for
            the specified user. Set this flag to False to add new permissions
            non-destructively.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        An Access Control List object

    Example: Setting permissions
        Grant all registered users download access

            await File(id="syn123").set_permissions_async(principal_id=273948, access_type=['READ','DOWNLOAD'])

        Grant the public view access

            await File(id="syn123").set_permissions_async(principal_id=273949, access_type=['READ'])
    """
    if access_type is None:
        access_type = ["READ", "DOWNLOAD"]
    loop = asyncio.get_event_loop()

    return await loop.run_in_executor(
        None,
        lambda: Synapse.get_client(synapse_client=synapse_client).setPermissions(
            entity=self.id,
            principalId=principal_id,
            accessType=access_type,
            modify_benefactor=modify_benefactor,
            warn_if_inherits=warn_if_inherits,
            overwrite=overwrite,
        ),
    )

synapseclient.models.Activity dataclass

Bases: ActivitySynchronousProtocol

An activity is a Synapse object that helps to keep track of what objects were used in an analysis step, as well as what objects were generated. Thus, all relationships between Synapse objects and an activity are governed by dependencies. That is, an activity needs to know what it used, and outputs need to know what activity they were generatedBy.

ATTRIBUTE DESCRIPTION
id

The unique immutable ID for this actvity.

TYPE: Optional[str]

name

A name for this Activity.

TYPE: Optional[str]

description

A description for this Activity.

TYPE: Optional[str]

etag

Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle concurrent updates. Since the E-Tag changes every time an entity is updated it is used to detect when a client's current representation of an entity is out-of-date.

TYPE: Optional[str]

created_on

The date this object was created.

TYPE: Optional[str]

modified_on

The date this object was last modified.

TYPE: Optional[str]

created_by

The user that created this object.

TYPE: Optional[str]

modified_by

The user that last modified this object.

TYPE: Optional[str]

used

The entities or URLs used by this Activity.

TYPE: List[Union[UsedEntity, UsedURL]]

executed

The entities or URLs executed by this Activity.

TYPE: List[Union[UsedEntity, UsedURL]]

Source code in synapseclient/models/activity.py
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
@dataclass
@async_to_sync
class Activity(ActivitySynchronousProtocol):
    """
    An activity is a Synapse object that helps to keep track of what objects were used
    in an analysis step, as well as what objects were generated. Thus, all relationships
    between Synapse objects and an activity are governed by dependencies. That is, an
    activity needs to know what it `used`, and outputs need to know what activity
    they were `generatedBy`.

    Attributes:
        id: The unique immutable ID for this actvity.
        name: A name for this Activity.
        description: A description for this Activity.
        etag: Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle
            concurrent updates. Since the E-Tag changes every time an entity is updated
            it is used to detect when a client's current representation of an entity is
            out-of-date.
        created_on: The date this object was created.
        modified_on: The date this object was last modified.
        created_by: The user that created this object.
        modified_by: The user that last modified this object.
        used: The entities or URLs used by this Activity.
        executed: The entities or URLs executed by this Activity.
    """

    id: Optional[str] = None
    """The unique immutable ID for this actvity."""

    name: Optional[str] = None
    """A name for this Activity."""

    description: Optional[str] = None
    """A description for this Activity."""

    etag: Optional[str] = None
    """
    Synapse employs an Optimistic Concurrency Control (OCC) scheme to handle
    concurrent updates. Since the E-Tag changes every time an entity is updated it is
    used to detect when a client's current representation of an entity is out-of-date.
    """

    created_on: Optional[str] = None
    """The date this object was created."""

    modified_on: Optional[str] = None
    """The date this object was last modified."""

    created_by: Optional[str] = None
    """The user that created this object."""

    modified_by: Optional[str] = None
    """The user that last modified this object."""

    used: List[Union[UsedEntity, UsedURL]] = field(default_factory=list)
    """The entities used by this Activity."""

    executed: List[Union[UsedEntity, UsedURL]] = field(default_factory=list)
    """The entities executed by this Activity."""

    def fill_from_dict(
        self, synapse_activity: Union[Synapse_Activity, Dict]
    ) -> "Activity":
        """
        Converts a response from the REST API into this dataclass.

        Arguments:
            synapse_activity: The response from the REST API.

        Returns:
            The Activity object.
        """
        if not synapse_activity:
            synapse_activity = {}
        self.id = synapse_activity.get("id", None)
        self.name = synapse_activity.get("name", None)
        self.description = synapse_activity.get("description", None)
        self.etag = synapse_activity.get("etag", None)
        self.created_on = synapse_activity.get("createdOn", None)
        self.modified_on = synapse_activity.get("modifiedOn", None)
        self.created_by = synapse_activity.get("createdBy", None)
        self.modified_by = synapse_activity.get("modifiedBy", None)
        self.executed = []
        self.used = []
        for used in synapse_activity.get("used", []):
            concrete_type = used.get("concreteType", None)
            if USED_URL == concrete_type:
                used_url = UsedURL(
                    name=used.get("name", None),
                    url=used.get("url", None),
                )

                if used.get("wasExecuted", False):
                    self.executed.append(used_url)
                else:
                    self.used.append(used_url)
            elif USED_ENTITY == concrete_type:
                reference = used.get("reference", {})
                used_entity = UsedEntity(
                    target_id=reference.get("targetId", None),
                    target_version_number=reference.get("targetVersionNumber", None),
                )

                if used.get("wasExecuted", False):
                    self.executed.append(used_entity)
                else:
                    self.used.append(used_entity)

        return self

    def _create_used_and_executed_synapse_activities(
        self,
    ) -> UsedAndExecutedSynapseActivities:
        """
        Helper function to create the used and executed activities for the
        Synapse Activity.

        Returns:
            A tuple of the used and executed activities.
        """
        synapse_activity_used = []
        synapse_activity_executed = []

        for used in self.used:
            if isinstance(used, UsedEntity):
                synapse_activity_used.append(
                    {
                        "reference": {
                            "targetId": used.target_id,
                            "targetVersionNumber": used.target_version_number,
                        }
                    }
                )
            elif isinstance(used, UsedURL):
                synapse_activity_used.append(
                    {
                        "name": used.name,
                        "url": used.url,
                    }
                )

        for executed in self.executed:
            if isinstance(executed, UsedEntity):
                synapse_activity_executed.append(
                    {
                        "reference": {
                            "targetId": executed.target_id,
                            "targetVersionNumber": executed.target_version_number,
                        },
                        "wasExecuted": True,
                    }
                )
            elif isinstance(executed, UsedURL):
                synapse_activity_executed.append(
                    {"name": executed.name, "url": executed.url, "wasExecuted": True}
                )
        return UsedAndExecutedSynapseActivities(
            used=synapse_activity_used, executed=synapse_activity_executed
        )

    @otel_trace_method(
        method_to_trace_name=lambda self, **kwargs: f"Activity_store: {self.name}"
    )
    async def store_async(
        self,
        parent: Optional[Union["Table", "File"]] = None,
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> "Activity":
        """
        Store the Activity in Synapse.

        Arguments:
            parent: The parent entity to associate this activity with.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The activity object.

        Raises:
            ValueError: Raised if both of the following are true:

                - If the parent does not have an ID.
                - If the Activity does not have an ID and ETag.
        """
        # TODO: Input validation: SYNPY-1400
        used_and_executed_activities = (
            self._create_used_and_executed_synapse_activities()
        )

        synapse_activity = Synapse_Activity(
            name=self.name,
            description=self.description,
            used=used_and_executed_activities.used,
            executed=used_and_executed_activities.executed,
        )

        loop = asyncio.get_event_loop()
        if self.id:
            # Despite init in `Synapse_Activity` not accepting an ID/ETAG the
            # `updateActivity` method expects that it exists on the dict
            # and `setProvenance` accepts it as well.
            synapse_activity["id"] = self.id
            synapse_activity["etag"] = self.etag
        if parent:
            saved_activity = await loop.run_in_executor(
                None,
                lambda: Synapse.get_client(synapse_client=synapse_client).setProvenance(
                    entity=parent.id,
                    activity=synapse_activity,
                ),
            )
        else:
            saved_activity = await loop.run_in_executor(
                None,
                lambda: Synapse.get_client(
                    synapse_client=synapse_client
                ).updateActivity(
                    activity=synapse_activity,
                ),
            )
        self.fill_from_dict(synapse_activity=saved_activity)
        Synapse.get_client(synapse_client=synapse_client).logger.debug(
            f"Stored activity {self.id}"
        )

        return self

    @classmethod
    async def from_parent_async(
        cls,
        parent: Union["Table", "File"],
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> Union["Activity", None]:
        """
        Get the Activity from Synapse based on the parent entity.

        Arguments:
            parent: The parent entity this activity is associated with. The parent may
                also have a version_number. Gets the most recent version if version is
                omitted.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Returns:
            The activity object or None if it does not exist.

        Raises:
            ValueError: If the parent does not have an ID.
        """
        # TODO: Input validation: SYNPY-1400
        with tracer.start_as_current_span(name=f"Activity_get: Parent_ID: {parent.id}"):
            loop = asyncio.get_event_loop()
            try:
                synapse_activity = await loop.run_in_executor(
                    None,
                    lambda: Synapse.get_client(
                        synapse_client=synapse_client
                    ).getProvenance(
                        entity=parent.id,
                        version=parent.version_number,
                    ),
                )
            except SynapseHTTPError as ex:
                if ex.response.status_code == 404:
                    return None
                else:
                    raise ex
            if synapse_activity:
                return cls().fill_from_dict(synapse_activity=synapse_activity)
            else:
                return None

    @classmethod
    async def delete_async(
        cls,
        parent: Union["Table", "File"],
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> None:
        """
        Delete the Activity from Synapse. The Activity must be disassociated from
        all entities before it can be deleted. The first step of this delete call
        is to disassociate the Activity from the parent entity. If you have other
        entities that are associated with this Activity you must disassociate them
        by calling this method on them as well. You'll receive an error for all entities
        until the last one which will delete the Activity.

        Arguments:
            parent: The parent entity this activity is associated with.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Raises:
            ValueError: If the parent does not have an ID.
        """
        # TODO: Input validation: SYNPY-1400
        with tracer.start_as_current_span(
            name=f"Activity_delete: Parent_ID: {parent.id}"
        ):
            loop = asyncio.get_event_loop()
            await loop.run_in_executor(
                None,
                lambda: Synapse.get_client(
                    synapse_client=synapse_client
                ).deleteProvenance(
                    entity=parent.id,
                ),
            )
            parent.activity = None

    @classmethod
    async def disassociate_from_entity_async(
        cls,
        parent: Union["Table", "File"],
        *,
        synapse_client: Optional[Synapse] = None,
    ) -> None:
        """
        Disassociate the Activity from the parent entity. This is the first step in
        deleting the Activity. If you have other entities that are associated with this
        Activity you must disassociate them by calling this method on them as well.

        Arguments:
            parent: The parent entity this activity is associated with.
            synapse_client: If not passed in and caching was not disabled by
                `Synapse.allow_client_caching(False)` this will use the last created
                instance from the Synapse class constructor.

        Raises:
            ValueError: If the parent does not have an ID.
        """
        # TODO: Input validation: SYNPY-1400
        with tracer.start_as_current_span(
            name=f"Activity_disassociate: Parent_ID: {parent.id}"
        ):
            await delete_entity_generated_by(
                entity_id=parent.id, synapse_client=synapse_client
            )
            parent.activity = None

Functions

from_parent_async(parent, *, synapse_client=None) async classmethod

Get the Activity from Synapse based on the parent entity.

PARAMETER DESCRIPTION
parent

The parent entity this activity is associated with. The parent may also have a version_number. Gets the most recent version if version is omitted.

TYPE: Union[Table, File]

synapse_client

If not passed in and caching was not disabled by Synapse.allow_client_caching(False) this will use the last created instance from the Synapse class constructor.

TYPE: Optional[Synapse] DEFAULT: None

RETURNS DESCRIPTION
Union[Activity, None]

The activity object or None if it does not exist.

RAISES DESCRIPTION
ValueError

If the parent does not have an ID.

Source code in synapseclient/models/activity.py
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
@classmethod
async def from_parent_async(
    cls,
    parent: Union["Table", "File"],
    *,
    synapse_client: Optional[Synapse] = None,
) -> Union["Activity", None]:
    """
    Get the Activity from Synapse based on the parent entity.

    Arguments:
        parent: The parent entity this activity is associated with. The parent may
            also have a version_number. Gets the most recent version if version is
            omitted.
        synapse_client: If not passed in and caching was not disabled by
            `Synapse.allow_client_caching(False)` this will use the last created
            instance from the Synapse class constructor.

    Returns:
        The activity object or None if it does not exist.

    Raises:
        ValueError: If the parent does not have an ID.
    """
    # TODO: Input validation: SYNPY-1400
    with tracer.start_as_current_span(name=f"Activity_get: Parent_ID: {parent.id}"):
        loop = asyncio.get_event_loop()
        try:
            synapse_activity = await loop.run_in_executor(
                None,
                lambda: Synapse.get_client(
                    synapse_client=synapse_client
                ).getProvenance(
                    entity=parent.id,
                    version=parent.version_number,
                ),
            )
        except SynapseHTTPError as ex:
            if ex.response.