synapseclient 4.2.0__tar.gz → 4.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. {synapseclient-4.2.0/synapseclient.egg-info → synapseclient-4.3.1}/PKG-INFO +27 -11
  2. {synapseclient-4.2.0 → synapseclient-4.3.1}/README.md +20 -5
  3. {synapseclient-4.2.0 → synapseclient-4.3.1}/setup.cfg +9 -7
  4. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/__init__.py +5 -0
  5. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/__main__.py +1 -3
  6. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/api/__init__.py +2 -0
  7. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/api/entity_services.py +20 -0
  8. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/client.py +40 -79
  9. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/cache.py +1 -1
  10. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/credentials/credential_provider.py +3 -3
  11. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/logging_setup.py +10 -0
  12. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/retry.py +4 -0
  13. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/upload/multipart_upload_async.py +27 -15
  14. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/upload/upload_functions_async.py +3 -27
  15. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/utils.py +8 -36
  16. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/activity.py +30 -0
  17. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/file.py +100 -18
  18. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/mixins/storable_container.py +5 -1
  19. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/protocols/activity_protocol.py +21 -0
  20. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/services/storable_entity_components.py +47 -4
  21. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/table.py +11 -1
  22. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/synapsePythonClient +1 -1
  23. {synapseclient-4.2.0 → synapseclient-4.3.1/synapseclient.egg-info}/PKG-INFO +27 -11
  24. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient.egg-info/requires.txt +6 -5
  25. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseutils/__init__.py +2 -1
  26. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseutils/monitor.py +87 -3
  27. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseutils/sync.py +348 -282
  28. {synapseclient-4.2.0 → synapseclient-4.3.1}/MANIFEST.in +0 -0
  29. {synapseclient-4.2.0 → synapseclient-4.3.1}/pyproject.toml +0 -0
  30. {synapseclient-4.2.0 → synapseclient-4.3.1}/setup.py +0 -0
  31. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/.synapseConfig +0 -0
  32. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/activity.py +0 -0
  33. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/annotations.py +0 -0
  34. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/api/annotations.py +0 -0
  35. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/api/entity_bundle_services_v2.py +0 -0
  36. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/api/file_services.py +0 -0
  37. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/__init__.py +0 -0
  38. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/async_utils.py +0 -0
  39. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/config.py +0 -0
  40. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/constants/__init__.py +0 -0
  41. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/constants/concrete_types.py +0 -0
  42. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/constants/config_file_constants.py +0 -0
  43. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/constants/limits.py +0 -0
  44. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/constants/method_flags.py +0 -0
  45. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/credentials/__init__.py +0 -0
  46. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/credentials/cred_data.py +0 -0
  47. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/cumulative_transfer_progress.py +0 -0
  48. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/dozer.py +0 -0
  49. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/exceptions.py +0 -0
  50. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/lock.py +0 -0
  51. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/models/__init__.py +0 -0
  52. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/models/custom_json.py +0 -0
  53. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/models/dict_object.py +0 -0
  54. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/models/permission.py +0 -0
  55. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/multithread_download/__init__.py +0 -0
  56. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/multithread_download/download_threads.py +0 -0
  57. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/pool_provider.py +0 -0
  58. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/remote_file_storage_wrappers.py +0 -0
  59. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/sts_transfer.py +0 -0
  60. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/upload/__init__.py +0 -0
  61. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/upload/multipart_upload.py +0 -0
  62. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/upload/upload_functions.py +0 -0
  63. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/upload/upload_utils.py +0 -0
  64. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/core/version_check.py +0 -0
  65. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/entity.py +0 -0
  66. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/evaluation.py +0 -0
  67. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/__init__.py +0 -0
  68. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/annotations.py +0 -0
  69. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/evaluation.py +0 -0
  70. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/folder.py +0 -0
  71. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/mixins/__init__.py +0 -0
  72. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/mixins/access_control.py +0 -0
  73. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/project.py +0 -0
  74. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/protocols/__init__.py +0 -0
  75. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/protocols/access_control_protocol.py +0 -0
  76. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/protocols/annotations_protocol.py +0 -0
  77. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/protocols/file_protocol.py +0 -0
  78. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/protocols/folder_protocol.py +0 -0
  79. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/protocols/project_protocol.py +0 -0
  80. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/protocols/storable_container_protocol.py +0 -0
  81. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/protocols/table_protocol.py +0 -0
  82. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/protocols/team_protocol.py +0 -0
  83. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/protocols/user_protocol.py +0 -0
  84. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/services/__init__.py +0 -0
  85. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/services/search.py +0 -0
  86. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/services/storable_entity.py +0 -0
  87. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/team.py +0 -0
  88. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/user.py +0 -0
  89. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/models/wiki.py +0 -0
  90. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/services/__init__.py +0 -0
  91. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/services/json_schema.py +0 -0
  92. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/table.py +0 -0
  93. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/team.py +0 -0
  94. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient/wiki.py +0 -0
  95. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient.egg-info/SOURCES.txt +0 -0
  96. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient.egg-info/dependency_links.txt +0 -0
  97. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient.egg-info/entry_points.txt +0 -0
  98. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient.egg-info/not-zip-safe +0 -0
  99. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseclient.egg-info/top_level.txt +0 -0
  100. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseutils/copy_functions.py +0 -0
  101. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseutils/describe_functions.py +0 -0
  102. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseutils/migrate_functions.py +0 -0
  103. {synapseclient-4.2.0 → synapseclient-4.3.1}/synapseutils/walk_functions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: synapseclient
3
- Version: 4.2.0
3
+ Version: 4.3.1
4
4
  Summary: A client for Synapse, a collaborative, open-source research platform that allows teams to share data, track analyses, and collaborate.
5
5
  Home-page: https://www.synapse.org
6
6
  Author: The Synapse Engineering Team
@@ -39,14 +39,13 @@ Requires-Dist: nest-asyncio~=1.6.0
39
39
  Requires-Dist: asyncio-atexit~=1.0.1
40
40
  Requires-Dist: httpx~=0.27.0
41
41
  Requires-Dist: tqdm<5.0,>=4.66.2
42
- Requires-Dist: loky~=3.0.0
43
42
  Requires-Dist: async-lru~=2.0.4
44
43
  Requires-Dist: psutil~=5.9.8
45
44
  Provides-Extra: dev
46
- Requires-Dist: pytest<7.0,>=6.0.0; extra == "dev"
45
+ Requires-Dist: pytest<8.0,>=7.0.0; extra == "dev"
47
46
  Requires-Dist: pytest-mock<4.0,>=3.0; extra == "dev"
48
47
  Requires-Dist: pytest-socket~=0.6.0; extra == "dev"
49
- Requires-Dist: pytest-asyncio~=0.19; extra == "dev"
48
+ Requires-Dist: pytest-asyncio<1.0,>=0.23.6; extra == "dev"
50
49
  Requires-Dist: flake8<4.0,>=3.7.0; extra == "dev"
51
50
  Requires-Dist: pytest-xdist[psutil]<3.0.0,>=2.2; extra == "dev"
52
51
  Requires-Dist: pytest-rerunfailures~=12.0; extra == "dev"
@@ -54,16 +53,18 @@ Requires-Dist: func-timeout~=4.3; extra == "dev"
54
53
  Requires-Dist: pytest-cov~=4.1.0; extra == "dev"
55
54
  Requires-Dist: black; extra == "dev"
56
55
  Requires-Dist: pre-commit; extra == "dev"
56
+ Requires-Dist: pandas<3.0,>=1.5; extra == "dev"
57
57
  Provides-Extra: tests
58
- Requires-Dist: pytest<7.0,>=6.0.0; extra == "tests"
58
+ Requires-Dist: pytest<8.0,>=7.0.0; extra == "tests"
59
59
  Requires-Dist: pytest-mock<4.0,>=3.0; extra == "tests"
60
60
  Requires-Dist: pytest-socket~=0.6.0; extra == "tests"
61
- Requires-Dist: pytest-asyncio~=0.19; extra == "tests"
61
+ Requires-Dist: pytest-asyncio<1.0,>=0.23.6; extra == "tests"
62
62
  Requires-Dist: flake8<4.0,>=3.7.0; extra == "tests"
63
63
  Requires-Dist: pytest-xdist[psutil]<3.0.0,>=2.2; extra == "tests"
64
64
  Requires-Dist: pytest-rerunfailures~=12.0; extra == "tests"
65
65
  Requires-Dist: func-timeout~=4.3; extra == "tests"
66
66
  Requires-Dist: pytest-cov~=4.1.0; extra == "tests"
67
+ Requires-Dist: pandas<3.0,>=1.5; extra == "tests"
67
68
  Provides-Extra: pandas
68
69
  Requires-Dist: pandas<3.0,>=1.5; extra == "pandas"
69
70
  Provides-Extra: pysftp
@@ -125,16 +126,24 @@ The Python Synapse client has been tested on 3.8, 3.9, 3.10 and 3.11 on Mac OS X
125
126
 
126
127
  The [Python Synapse Client is on PyPI](https://pypi.python.org/pypi/synapseclient) and can be installed with pip:
127
128
 
128
- (sudo) pip install synapseclient[pandas,pysftp]
129
+ # Here are a few ways to install the client. Choose the one that fits your use-case
130
+ # sudo may optionally be needed depending on your setup
131
+
132
+ pip install --upgrade synapseclient
133
+ pip install --upgrade "synapseclient[pandas]"
134
+ pip install --upgrade "synapseclient[pandas, pysftp, boto3]"
129
135
 
130
136
  ...or to upgrade an existing installation of the Synapse client:
131
137
 
132
- (sudo) pip install --upgrade synapseclient
138
+ # sudo may optionally be needed depending on your setup
139
+ pip install --upgrade synapseclient
133
140
 
134
- The dependencies on `pandas` and `pysftp` are optional. Synapse [Tables](https://python-docs.synapse.org/reference/tables/) integrate
141
+ The dependencies on `pandas`, `pysftp`, and `boto3` are optional. Synapse
142
+ [Tables](https://python-docs.synapse.org/reference/tables/) integrate
135
143
  with [Pandas](http://pandas.pydata.org/). The library `pysftp` is required for users of
136
- [SFTP](https://python-docs.synapse.org/guides/data_storage/#sftp) file storage. Both libraries require native code
137
- to be compiled or installed separately from prebuilt binaries.
144
+ [SFTP](https://python-docs.synapse.org/guides/data_storage/#sftp) file storage. All
145
+ libraries require native code to be compiled or installed separately from prebuilt
146
+ binaries.
138
147
 
139
148
  ### Install from source
140
149
 
@@ -144,6 +153,13 @@ Clone the [source code repository](https://github.com/Sage-Bionetworks/synapsePy
144
153
  cd synapsePythonClient
145
154
  pip install .
146
155
 
156
+ Alternatively, you can use pip to install a particular branch, commit, or other git reference:
157
+
158
+ pip install git+https://github.com/Sage-Bionetworks/synapsePythonClient@master
159
+
160
+ or
161
+
162
+ pip install git+https://github.com/Sage-Bionetworks/synapsePythonClient@my-commit-hash
147
163
 
148
164
  Command line usage
149
165
  ------------------
@@ -44,16 +44,24 @@ The Python Synapse client has been tested on 3.8, 3.9, 3.10 and 3.11 on Mac OS X
44
44
 
45
45
  The [Python Synapse Client is on PyPI](https://pypi.python.org/pypi/synapseclient) and can be installed with pip:
46
46
 
47
- (sudo) pip install synapseclient[pandas,pysftp]
47
+ # Here are a few ways to install the client. Choose the one that fits your use-case
48
+ # sudo may optionally be needed depending on your setup
49
+
50
+ pip install --upgrade synapseclient
51
+ pip install --upgrade "synapseclient[pandas]"
52
+ pip install --upgrade "synapseclient[pandas, pysftp, boto3]"
48
53
 
49
54
  ...or to upgrade an existing installation of the Synapse client:
50
55
 
51
- (sudo) pip install --upgrade synapseclient
56
+ # sudo may optionally be needed depending on your setup
57
+ pip install --upgrade synapseclient
52
58
 
53
- The dependencies on `pandas` and `pysftp` are optional. Synapse [Tables](https://python-docs.synapse.org/reference/tables/) integrate
59
+ The dependencies on `pandas`, `pysftp`, and `boto3` are optional. Synapse
60
+ [Tables](https://python-docs.synapse.org/reference/tables/) integrate
54
61
  with [Pandas](http://pandas.pydata.org/). The library `pysftp` is required for users of
55
- [SFTP](https://python-docs.synapse.org/guides/data_storage/#sftp) file storage. Both libraries require native code
56
- to be compiled or installed separately from prebuilt binaries.
62
+ [SFTP](https://python-docs.synapse.org/guides/data_storage/#sftp) file storage. All
63
+ libraries require native code to be compiled or installed separately from prebuilt
64
+ binaries.
57
65
 
58
66
  ### Install from source
59
67
 
@@ -63,6 +71,13 @@ Clone the [source code repository](https://github.com/Sage-Bionetworks/synapsePy
63
71
  cd synapsePythonClient
64
72
  pip install .
65
73
 
74
+ Alternatively, you can use pip to install a particular branch, commit, or other git reference:
75
+
76
+ pip install git+https://github.com/Sage-Bionetworks/synapsePythonClient@master
77
+
78
+ or
79
+
80
+ pip install git+https://github.com/Sage-Bionetworks/synapsePythonClient@my-commit-hash
66
81
 
67
82
  Command line usage
68
83
  ------------------
@@ -48,26 +48,26 @@ install_requires =
48
48
  asyncio-atexit~=1.0.1
49
49
  httpx~=0.27.0
50
50
  tqdm>=4.66.2,<5.0
51
- loky~=3.0.0
52
51
  async-lru~=2.0.4
53
52
  psutil~=5.9.8
54
53
  tests_require =
55
- pytest>=6.0.0,<7.0
54
+ pytest>=7.0.0,<8.0
56
55
  pytest-mock>=3.0,<4.0
57
56
  pytest-socket~=0.6.0
58
- pytest-asyncio~=0.19
57
+ pytest-asyncio>=0.23.6,<1.0
59
58
  flake8>=3.7.0,<4.0
60
59
  pytest-xdist[psutil]>=2.2,<3.0.0
61
60
  pytest-rerunfailures~=12.0
62
61
  func-timeout~=4.3
63
62
  pytest-cov~=4.1.0
63
+ pandas>=1.5,<3.0
64
64
 
65
65
  [options.extras_require]
66
66
  dev =
67
- pytest>=6.0.0,<7.0
67
+ pytest>=7.0.0,<8.0
68
68
  pytest-mock>=3.0,<4.0
69
69
  pytest-socket~=0.6.0
70
- pytest-asyncio~=0.19
70
+ pytest-asyncio>=0.23.6,<1.0
71
71
  flake8>=3.7.0,<4.0
72
72
  pytest-xdist[psutil]>=2.2,<3.0.0
73
73
  pytest-rerunfailures~=12.0
@@ -75,16 +75,18 @@ dev =
75
75
  pytest-cov~=4.1.0
76
76
  black
77
77
  pre-commit
78
+ pandas>=1.5,<3.0
78
79
  tests =
79
- pytest>=6.0.0,<7.0
80
+ pytest>=7.0.0,<8.0
80
81
  pytest-mock>=3.0,<4.0
81
82
  pytest-socket~=0.6.0
82
- pytest-asyncio~=0.19
83
+ pytest-asyncio>=0.23.6,<1.0
83
84
  flake8>=3.7.0,<4.0
84
85
  pytest-xdist[psutil]>=2.2,<3.0.0
85
86
  pytest-rerunfailures~=12.0
86
87
  func-timeout~=4.3
87
88
  pytest-cov~=4.1.0
89
+ pandas>=1.5,<3.0
88
90
  pandas =
89
91
  pandas>=1.5,<3.0
90
92
  pysftp =
@@ -88,6 +88,11 @@ USER_AGENT = {
88
88
  % (__version__, requests.utils.default_user_agent())
89
89
  }
90
90
 
91
+ USER_AGENT_COMMAND_LINE = {
92
+ "User-Agent": "synapsecommandlineclient/%s %s"
93
+ % (__version__, requests.utils.default_user_agent())
94
+ }
95
+
91
96
  # patch json
92
97
  from .core.models import custom_json # noqa
93
98
 
@@ -1871,9 +1871,7 @@ def _authenticate_login(
1871
1871
 
1872
1872
  def main():
1873
1873
  args = build_parser().parse_args()
1874
- synapseclient.USER_AGENT["User-Agent"] = (
1875
- "synapsecommandlineclient " + synapseclient.USER_AGENT["User-Agent"]
1876
- )
1874
+ synapseclient.USER_AGENT = synapseclient.USER_AGENT_COMMAND_LINE
1877
1875
  if args.otel:
1878
1876
  trace.set_tracer_provider(
1879
1877
  TracerProvider(
@@ -13,6 +13,7 @@ from .entity_services import (
13
13
  post_entity,
14
14
  put_entity,
15
15
  create_access_requirements_if_none,
16
+ delete_entity_generated_by,
16
17
  )
17
18
  from .file_services import (
18
19
  get_file_handle,
@@ -51,4 +52,5 @@ __all__ = [
51
52
  "get_upload_destination",
52
53
  "get_upload_destination_location",
53
54
  "create_access_requirements_if_none",
55
+ "delete_entity_generated_by",
54
56
  ]
@@ -175,3 +175,23 @@ async def create_access_requirements_if_none(
175
175
  "the Synapse access control team to start the process of adding "
176
176
  "terms-of-use or review board approval for this entity."
177
177
  )
178
+
179
+
180
+ async def delete_entity_generated_by(
181
+ entity_id: str,
182
+ synapse_client: Optional["Synapse"] = None,
183
+ ) -> None:
184
+ """
185
+ Arguments:
186
+ entity_id: The ID of the entity.
187
+ synapse_client: If not passed in or None this will use the last client from
188
+ the `.login()` method.
189
+
190
+ Returns: None
191
+ """
192
+ from synapseclient import Synapse
193
+
194
+ client = Synapse.get_client(synapse_client=synapse_client)
195
+ return await client.rest_delete_async(
196
+ uri=f"/entity/{entity_id}/generatedBy",
197
+ )
@@ -34,7 +34,6 @@ import httpx
34
34
  from deprecated import deprecated
35
35
 
36
36
  from concurrent.futures import ThreadPoolExecutor
37
- from loky import get_reusable_executor
38
37
 
39
38
  import synapseclient
40
39
  from .annotations import (
@@ -121,9 +120,6 @@ from synapseclient.core.upload.multipart_upload_async import (
121
120
  multipart_upload_string_async,
122
121
  )
123
122
  from synapseclient.core.remote_file_storage_wrappers import S3ClientWrapper, SFTPWrapper
124
- from synapseclient.core.upload.upload_functions import (
125
- upload_file_handle,
126
- )
127
123
  from synapseclient.core.upload.upload_functions_async import (
128
124
  upload_file_handle as upload_file_handle_async,
129
125
  upload_synapse_s3,
@@ -136,7 +132,6 @@ from synapseclient.core.models.permission import Permissions
136
132
  from opentelemetry import trace
137
133
  from opentelemetry.trace import SpanKind
138
134
 
139
-
140
135
  tracer = trace.get_tracer("synapseclient")
141
136
 
142
137
  PRODUCTION_ENDPOINTS = {
@@ -348,7 +343,7 @@ class Synapse(object):
348
343
  span.end()
349
344
 
350
345
  event_hooks = {"request": [log_request], "response": [log_response]}
351
- httpx_timeout = httpx.Timeout(70)
346
+ httpx_timeout = httpx.Timeout(70, pool=None)
352
347
  self._requests_session_storage = requests_session_storage or httpx.Client(
353
348
  timeout=httpx_timeout, event_hooks=event_hooks
354
349
  )
@@ -402,7 +397,7 @@ class Synapse(object):
402
397
  self.max_threads = transfer_config["max_threads"]
403
398
  self._thread_executor = {}
404
399
  self._process_executor = {}
405
- self._md5_semaphore = {}
400
+ self._parallel_file_transfer_semaphore = {}
406
401
  self.use_boto_sts_transfers = transfer_config["use_boto_sts"]
407
402
 
408
403
  def _get_requests_session_async_synapse(
@@ -435,7 +430,7 @@ class Synapse(object):
435
430
  await self._requests_session_async_synapse[asyncio_event_loop].aclose()
436
431
  del self._requests_session_async_synapse[asyncio_event_loop]
437
432
 
438
- httpx_timeout = httpx.Timeout(70)
433
+ httpx_timeout = httpx.Timeout(70, pool=None)
439
434
  span_dict: Dict[httpx.Request, trace.Span] = {}
440
435
 
441
436
  async def log_request(request: httpx.Request) -> None:
@@ -513,54 +508,44 @@ class Synapse(object):
513
508
  asyncio_atexit.register(close_pool)
514
509
  return self._thread_executor[asyncio_event_loop]
515
510
 
516
- def _get_process_pool_executor(self, asyncio_event_loop: asyncio.AbstractEventLoop):
511
+ def _get_parallel_file_transfer_semaphore(
512
+ self, asyncio_event_loop: asyncio.AbstractEventLoop
513
+ ) -> asyncio.Semaphore:
517
514
  """
518
- Retrieve the process pool executor for the Synapse client. Or create a new one
519
- if it does not exist. This executor is used for parallel processing of data.
515
+ Retrieve the semaphore for the Synapse client. Or create a new one if it does
516
+ not exist. This semaphore is used to limit the number of files that can actively
517
+ enter the uploading/downloading process.
520
518
 
521
519
  This is expected to be called from within an AsyncIO loop.
522
520
 
523
- Note: Within Windows a ProcessPoolExecutor requires that the initial entry point
524
- into the code be within a `if __name__ == "__main__":` block. This is not
525
- possible within the current codebase as it would require everyone using this
526
- library to have this as their entry point. As a result, the ProcessPoolExecutor
527
- will not work within Windows.
528
-
529
- To get around this Windows limitation this is using this package:
530
- https://github.com/joblib/loky
531
- """
532
- if (
533
- hasattr(self, "_process_executor")
534
- and asyncio_event_loop in self._process_executor
535
- and self._process_executor[asyncio_event_loop] is not None
536
- ):
537
- return self._process_executor[asyncio_event_loop]
538
-
539
- self._process_executor.update({asyncio_event_loop: get_reusable_executor(1)})
521
+ By default the number of files that can enter the "uploading" state will be
522
+ limited to 2 * max_threads. This is to ensure that the files that are entering
523
+ into the "uploading" state will have priority to finish. Additionally, it means
524
+ that there should be a good spread of files getting up to the "uploading"
525
+ state, entering the "uploading" state, and finishing the "uploading" state.
540
526
 
541
- return self._process_executor[asyncio_event_loop]
542
-
543
- def _get_md5_semaphore(
544
- self, asyncio_event_loop: asyncio.AbstractEventLoop
545
- ) -> asyncio.Semaphore:
546
- """
547
- Retrieve the semaphore for the Synapse client. Or create a new one if it does not
548
- exist. This semaphore is used to ensure that only one process is calculating the
549
- MD5 hash at a time. This is to prevent the custom process pool executor from
550
- thrashing or handling the waiting. We should let asyncio handle the waiting.
527
+ If we break these states down into large components they would look like:
528
+ - Before "uploading" state: HTTP rest calls to retrieve what data Synapse has
529
+ - Entering "uploading" state: MD5 calculation and HTTP rest calls to determine
530
+ how/where to upload a file to.
531
+ - During "uploading" state: Uploading the file to a storage provider.
532
+ - After "uploading" state: HTTP rest calls to finalize the upload.
551
533
 
552
- This is expected to be called from within an AsyncIO loop.
534
+ This has not yet been applied to parallel file downloads. That will take place
535
+ later on.
553
536
  """
554
537
  if (
555
- hasattr(self, "_md5_semaphore")
556
- and asyncio_event_loop in self._md5_semaphore
557
- and self._md5_semaphore[asyncio_event_loop] is not None
538
+ hasattr(self, "_parallel_file_transfer_semaphore")
539
+ and asyncio_event_loop in self._parallel_file_transfer_semaphore
540
+ and self._parallel_file_transfer_semaphore[asyncio_event_loop] is not None
558
541
  ):
559
- return self._md5_semaphore[asyncio_event_loop]
542
+ return self._parallel_file_transfer_semaphore[asyncio_event_loop]
560
543
 
561
- self._md5_semaphore.update({asyncio_event_loop: asyncio.Semaphore(1)})
544
+ self._parallel_file_transfer_semaphore.update(
545
+ {asyncio_event_loop: asyncio.Semaphore(max(self.max_threads * 2, 1))}
546
+ )
562
547
 
563
- return self._md5_semaphore[asyncio_event_loop]
548
+ return self._parallel_file_transfer_semaphore[asyncio_event_loop]
564
549
 
565
550
  # initialize logging
566
551
  def _init_logger(self):
@@ -716,10 +701,10 @@ class Synapse(object):
716
701
  If no login arguments are provided or only username is provided, login() will attempt to log in using
717
702
  information from these sources (in order of preference):
718
703
 
719
- 1. User defined arguments during a CLI session
720
- 2. User's Personal Access Token (aka: Synapse Auth Token)
704
+ 1. .synapseConfig file (in user home folder unless configured otherwise)
705
+ 2. User defined arguments during a CLI session
706
+ 3. User's Personal Access Token (aka: Synapse Auth Token)
721
707
  from the environment variable: SYNAPSE_AUTH_TOKEN
722
- 3. .synapseConfig file (in user home folder unless configured otherwise)
723
708
  4. Retrieves user's authentication token from AWS SSM Parameter store (if configured)
724
709
 
725
710
  Arguments:
@@ -1729,7 +1714,6 @@ class Synapse(object):
1729
1714
  activityName=None,
1730
1715
  activityDescription=None,
1731
1716
  set_annotations=True,
1732
- async_file_handle_upload: bool = True,
1733
1717
  ):
1734
1718
  """
1735
1719
  Creates a new Entity or updates an existing Entity, uploading any files in the process.
@@ -1752,11 +1736,6 @@ class Synapse(object):
1752
1736
  You will be contacted with regards to the specific data being restricted and the
1753
1737
  requirements of access.
1754
1738
  set_annotations: If True, set the annotations on the entity. If False, do not set the annotations.
1755
- async_file_handle_upload: Temporary feature flag that will be removed at an
1756
- unannounced later date. This is used during the Synapse Utils
1757
- syncToSynapse to disable the async file handle upload. The is because
1758
- the multi-threaded logic in the syncToSynapse is not compatible with
1759
- the async file handle upload.
1760
1739
 
1761
1740
  Returns:
1762
1741
  A Synapse Entity, Evaluation, or Wiki
@@ -1916,37 +1895,20 @@ class Synapse(object):
1916
1895
  "Entities of type File must have a parentId."
1917
1896
  )
1918
1897
 
1919
- if async_file_handle_upload:
1920
- fileHandle = wrap_async_to_sync(
1921
- upload_file_handle_async(
1922
- self,
1923
- parent_id_for_upload,
1924
- local_state["path"]
1925
- if (
1926
- synapseStore
1927
- or local_state_fh.get("externalURL") is None
1928
- )
1929
- else local_state_fh.get("externalURL"),
1930
- synapse_store=synapseStore,
1931
- md5=local_file_md5_hex or local_state_fh.get("contentMd5"),
1932
- file_size=local_state_fh.get("contentSize"),
1933
- mimetype=local_state_fh.get("contentType"),
1934
- ),
1935
- self,
1936
- )
1937
- else:
1938
- fileHandle = upload_file_handle(
1898
+ fileHandle = wrap_async_to_sync(
1899
+ upload_file_handle_async(
1939
1900
  self,
1940
1901
  parent_id_for_upload,
1941
1902
  local_state["path"]
1942
1903
  if (synapseStore or local_state_fh.get("externalURL") is None)
1943
1904
  else local_state_fh.get("externalURL"),
1944
- synapseStore=synapseStore,
1905
+ synapse_store=synapseStore,
1945
1906
  md5=local_file_md5_hex or local_state_fh.get("contentMd5"),
1946
1907
  file_size=local_state_fh.get("contentSize"),
1947
1908
  mimetype=local_state_fh.get("contentType"),
1948
- max_threads=self.max_threads,
1949
- )
1909
+ ),
1910
+ self,
1911
+ )
1950
1912
  properties["dataFileHandleId"] = fileHandle["id"]
1951
1913
  local_state["_file_handle"] = fileHandle
1952
1914
 
@@ -3521,7 +3483,7 @@ class Synapse(object):
3521
3483
  else:
3522
3484
  mode = "wb"
3523
3485
  previouslyTransferred = 0
3524
- sig = hashlib.new("md5", usedforsecurity=False)
3486
+ sig = hashlib.new("md5", usedforsecurity=False) # nosec
3525
3487
 
3526
3488
  try:
3527
3489
  with open(temp_destination, mode) as fd:
@@ -6027,7 +5989,6 @@ class Synapse(object):
6027
5989
  else:
6028
5990
  return
6029
5991
  data_dict = json.loads(data_to_parse)
6030
-
6031
5992
  if "parentId" in data_dict:
6032
5993
  current_span.set_attribute(
6033
5994
  "synapse.parent_id", data_dict["parentId"]
@@ -87,7 +87,7 @@ class Cache:
87
87
  value = os.path.expandvars(os.path.expanduser(value))
88
88
  # create the cache_root_dir if it does not already exist
89
89
  if not os.path.exists(value):
90
- os.makedirs(value)
90
+ os.makedirs(value, exist_ok=True)
91
91
  self.__dict__[key] = value
92
92
 
93
93
  def __init__(self, cache_root_dir=CACHE_ROOT_DIR, fanout=1000):
@@ -209,9 +209,9 @@ class SynapseCredentialsProviderChain(object):
209
209
 
210
210
  By default this class uses the following providers in this order:
211
211
 
212
- 1. [UserArgsCredentialsProvider][synapseclient.core.credentials.credential_provider.UserArgsCredentialsProvider]
213
- 2. [EnvironmentVariableCredentialsProvider][synapseclient.core.credentials.credential_provider.EnvironmentVariableCredentialsProvider]
214
- 3. [ConfigFileCredentialsProvider][synapseclient.core.credentials.credential_provider.ConfigFileCredentialsProvider]
212
+ 1. [ConfigFileCredentialsProvider][synapseclient.core.credentials.credential_provider.ConfigFileCredentialsProvider]
213
+ 2. [UserArgsCredentialsProvider][synapseclient.core.credentials.credential_provider.UserArgsCredentialsProvider]
214
+ 3. [EnvironmentVariableCredentialsProvider][synapseclient.core.credentials.credential_provider.EnvironmentVariableCredentialsProvider]
215
215
  4. [AWSParameterStoreCredentialsProvider][synapseclient.core.credentials.credential_provider.AWSParameterStoreCredentialsProvider]
216
216
 
217
217
  Attributes:
@@ -96,6 +96,16 @@ logging_config.dictConfig(
96
96
  "propagate": True,
97
97
  },
98
98
  SILENT_LOGGER_NAME: {"handlers": [], "level": "INFO", "propagate": False},
99
+ # "httpx": {
100
+ # "handlers": ["debug_stderr"],
101
+ # "level": "DEBUG",
102
+ # "propagate": True,
103
+ # },
104
+ # "httpcore": {
105
+ # "handlers": ["debug_stderr"],
106
+ # "level": "DEBUG",
107
+ # "propagate": True,
108
+ # },
99
109
  },
100
110
  }
101
111
  )
@@ -328,6 +328,8 @@ async def with_retry_time_based_async(
328
328
  caught_exception = None
329
329
  caught_exception_info = None
330
330
  response = None
331
+ current_span = trace.get_current_span()
332
+ current_span.set_attribute("synapse.retries", str(retries + 1))
331
333
 
332
334
  try:
333
335
  response = await function()
@@ -449,6 +451,8 @@ def with_retry_time_based(
449
451
  caught_exception = None
450
452
  caught_exception_info = None
451
453
  response = None
454
+ current_span = trace.get_current_span()
455
+ current_span.set_attribute("synapse.retries", str(retries + 1))
452
456
 
453
457
  try:
454
458
  response = function()
@@ -64,6 +64,7 @@ flowchart TD
64
64
 
65
65
  # pylint: disable=protected-access
66
66
  import asyncio
67
+ from contextlib import contextmanager
67
68
  import gc
68
69
  import mimetypes
69
70
  import os
@@ -113,7 +114,7 @@ from synapseclient.core.upload.upload_utils import (
113
114
  )
114
115
  from synapseclient.core.utils import MB
115
116
  from synapseclient.core.utils import md5_fn as md5_fn_util
116
- from synapseclient.core.utils import md5_for_file_multiprocessing
117
+ from synapseclient.core.utils import md5_for_file_hex
117
118
 
118
119
  if TYPE_CHECKING:
119
120
  from synapseclient import Synapse
@@ -126,6 +127,22 @@ MIN_PART_SIZE = 5 * MB
126
127
  DEFAULT_PART_SIZE = 8 * MB
127
128
  MAX_RETRIES = 7
128
129
 
130
+ _thread_local = threading.local()
131
+
132
+
133
+ @contextmanager
134
+ def shared_progress_bar(progress_bar):
135
+ """An outside process that will eventually trigger an upload through this module
136
+ can configure a shared Progress Bar by running its code within this context manager.
137
+ """
138
+ _thread_local.progress_bar = progress_bar
139
+ try:
140
+ yield
141
+ finally:
142
+ _thread_local.progress_bar.close()
143
+ _thread_local.progress_bar.refresh()
144
+ del _thread_local.progress_bar
145
+
129
146
 
130
147
  @dataclass
131
148
  class HandlePartResult:
@@ -173,6 +190,7 @@ class UploadAttemptAsync:
173
190
  self._aborted = False
174
191
  self._storage_str = storage_str
175
192
 
193
+ self._close_progress_bar = getattr(_thread_local, "progress_bar", None) is None
176
194
  # populated later
177
195
  self._upload_id: Optional[str] = None
178
196
  self._pre_signed_part_urls: Optional[Mapping[int, str]] = None
@@ -329,7 +347,9 @@ class UploadAttemptAsync:
329
347
  if self._is_copy():
330
348
  # we won't have bytes to measure during a copy so the byte oriented
331
349
  # progress bar is not useful
332
- self._progress_bar = tqdm(
350
+ self._progress_bar = getattr(
351
+ _thread_local, "progress_bar", None
352
+ ) or tqdm(
333
353
  total=part_count,
334
354
  desc=self._storage_str or "Copying",
335
355
  unit_scale=True,
@@ -343,7 +363,9 @@ class UploadAttemptAsync:
343
363
  file_size,
344
364
  )
345
365
 
346
- self._progress_bar = tqdm(
366
+ self._progress_bar = getattr(
367
+ _thread_local, "progress_bar", None
368
+ ) or tqdm(
347
369
  total=file_size,
348
370
  desc=self._storage_str or "Uploading",
349
371
  unit="B",
@@ -448,7 +470,7 @@ class UploadAttemptAsync:
448
470
  Returns:
449
471
  The response from the server for the completed upload.
450
472
  """
451
- if not self._syn.silent and self._progress_bar:
473
+ if not self._syn.silent and self._progress_bar and self._close_progress_bar:
452
474
  self._progress_bar.close()
453
475
  upload_status_response = await put_file_multipart_complete(
454
476
  upload_id=self._upload_id,
@@ -647,17 +669,7 @@ async def multipart_upload_file_async(
647
669
  mime_type, _ = mimetypes.guess_type(file_path, strict=False)
648
670
  content_type = mime_type or "application/octet-stream"
649
671
 
650
- md5_hex = md5 or (
651
- await md5_for_file_multiprocessing(
652
- filename=file_path,
653
- process_pool_executor=syn._get_process_pool_executor(
654
- asyncio_event_loop=asyncio.get_running_loop()
655
- ),
656
- md5_semaphore=syn._get_md5_semaphore(
657
- asyncio_event_loop=asyncio.get_running_loop()
658
- ),
659
- )
660
- )
672
+ md5_hex = md5 or md5_for_file_hex(filename=file_path)
661
673
 
662
674
  part_size = get_part_size(
663
675
  part_size or DEFAULT_PART_SIZE,