irods-http 0.1.0__py3-none-any.whl

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.
irods_http/__init__.py ADDED
@@ -0,0 +1,31 @@
1
+ """iRODS HTTP client library for Python."""
2
+
3
+ from . import (
4
+ collections,
5
+ data_objects,
6
+ queries,
7
+ resources,
8
+ rules,
9
+ tickets,
10
+ users_groups,
11
+ zones,
12
+ )
13
+ from .irods_http import (
14
+ IRODSHTTPSession,
15
+ authenticate,
16
+ get_server_info,
17
+ )
18
+
19
+ __all__ = [
20
+ "IRODSHTTPSession",
21
+ "authenticate",
22
+ "collections",
23
+ "data_objects",
24
+ "get_server_info",
25
+ "queries",
26
+ "resources",
27
+ "rules",
28
+ "tickets",
29
+ "users_groups",
30
+ "zones",
31
+ ]
@@ -0,0 +1,307 @@
1
+ """Collection operations for iRODS HTTP API."""
2
+
3
+ import builtins
4
+ import json
5
+
6
+ import requests
7
+
8
+ from . import common
9
+ from .irods_http import IRODSHTTPSession # noqa: TC001
10
+
11
+
12
+ def create(session: IRODSHTTPSession, lpath: str, create_intermediates: int = 0) -> dict:
13
+ """
14
+ Create a new collection.
15
+
16
+ Args:
17
+ session: IRODSHTTPSession object containing the base URL and authentication token.
18
+ lpath: The absolute logical path of the collection to be created.
19
+ create_intermediates: Set to 1 to create intermediates, otherwise set to 0. Defaults to 0.
20
+
21
+ Returns:
22
+ A dict containing the HTTP status code and iRODS response.
23
+ The iRODS response is only valid if no error occurred during HTTP communication.
24
+ """
25
+ common.validate_not_none(session.token)
26
+ common.validate_instance(lpath, str)
27
+ common.validate_0_or_1(create_intermediates)
28
+
29
+ data = {
30
+ "op": "create",
31
+ "lpath": lpath,
32
+ "create-intermediates": create_intermediates,
33
+ }
34
+
35
+ r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
36
+ return common.process_response(r)
37
+
38
+
39
+ def remove(session: IRODSHTTPSession, lpath: str, recurse: int = 0, no_trash: int = 0) -> dict:
40
+ """
41
+ Remove an existing collection.
42
+
43
+ Args:
44
+ session: IRODSHTTPSession object containing the base URL and authentication token.
45
+ lpath: The absolute logical path of the collection to be removed.
46
+ recurse: Set to 1 to remove contents of the collection, otherwise set to 0. Defaults to 0.
47
+ no_trash: Set to 1 to permanently remove, 0 to move to trash. Defaults to 0.
48
+
49
+ Returns:
50
+ A dict containing the HTTP status code and iRODS response.
51
+ The iRODS response is only valid if no error occurred during HTTP communication.
52
+ """
53
+ common.validate_not_none(session.token)
54
+ common.validate_instance(lpath, str)
55
+ common.validate_0_or_1(recurse)
56
+ common.validate_0_or_1(no_trash)
57
+
58
+ data = {
59
+ "op": "remove",
60
+ "lpath": lpath,
61
+ "recurse": recurse,
62
+ "no-trash": no_trash,
63
+ }
64
+
65
+ r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
66
+ return common.process_response(r)
67
+
68
+
69
+ def stat(session: IRODSHTTPSession, lpath: str, ticket: str = "") -> dict:
70
+ """
71
+ Give information about a collection.
72
+
73
+ Args:
74
+ session: IRODSHTTPSession object containing the base URL and authentication token.
75
+ lpath: The absolute logical path of the collection being accessed.
76
+ ticket: Ticket to be enabled before the operation. Defaults to an empty string.
77
+
78
+ Returns:
79
+ A dict containing the HTTP status code and iRODS response.
80
+ The iRODS response is only valid if no error occurred during HTTP communication.
81
+ """
82
+ common.validate_not_none(session.token)
83
+ common.validate_instance(lpath, str)
84
+ common.validate_instance(ticket, str)
85
+
86
+ params = {"op": "stat", "lpath": lpath, "ticket": ticket}
87
+
88
+ r = requests.get(session.url_base + "/collections", params=params, headers=session.get_headers) # noqa: S113
89
+ return common.process_response(r)
90
+
91
+
92
+ def list(session: IRODSHTTPSession, lpath: str, recurse: int = 0, ticket: str = "") -> dict: # noqa: A001
93
+ """
94
+ Show the contents of a collection.
95
+
96
+ Args:
97
+ session: IRODSHTTPSession object containing the base URL and authentication token.
98
+ lpath: The absolute logical path of the collection to have its contents listed.
99
+ recurse: Set to 1 to list the contents of objects in the collection,
100
+ otherwise set to 0. Defaults to 0.
101
+ ticket: Ticket to be enabled before the operation. Defaults to an empty string.
102
+
103
+ Returns:
104
+ A dict containing the HTTP status code and iRODS response.
105
+ The iRODS response is only valid if no error occurred during HTTP communication.
106
+ """
107
+ common.validate_not_none(session.token)
108
+ common.validate_instance(lpath, str)
109
+ common.validate_0_or_1(recurse)
110
+ common.validate_instance(ticket, str)
111
+
112
+ params = {"op": "list", "lpath": lpath, "recurse": recurse, "ticket": ticket}
113
+
114
+ r = requests.get(session.url_base + "/collections", params=params, headers=session.get_headers) # noqa: S113
115
+ return common.process_response(r)
116
+
117
+
118
+ def set_permission(
119
+ session: IRODSHTTPSession,
120
+ lpath: str,
121
+ entity_name: str,
122
+ permission: str,
123
+ admin: int = 0,
124
+ ) -> dict:
125
+ """
126
+ Set the permission of a user for a given collection.
127
+
128
+ Args:
129
+ session: IRODSHTTPSession object containing the base URL and authentication token.
130
+ lpath: The absolute logical path of the collection to have a permission set.
131
+ entity_name: The name of the user or group having its permission set.
132
+ permission: The permission level being set. Either 'null', 'read', 'write', or 'own'.
133
+ admin: Set to 1 to run this operation as an admin, otherwise set to 0. Defaults to 0.
134
+
135
+ Returns:
136
+ A dict containing the HTTP status code and iRODS response.
137
+ The iRODS response is only valid if no error occurred during HTTP communication.
138
+
139
+ Raises:
140
+ ValueError: If permission is not 'null', 'read', 'write', or 'own'.
141
+ """
142
+ common.validate_not_none(session.token)
143
+ common.validate_instance(lpath, str)
144
+ common.validate_instance(entity_name, str)
145
+ common.validate_instance(permission, str)
146
+ if permission not in ["null", "read", "write", "own"]:
147
+ raise ValueError("permission must be either 'null', 'read', 'write', or 'own'")
148
+ common.validate_0_or_1(admin)
149
+
150
+ data = {
151
+ "op": "set_permission",
152
+ "lpath": lpath,
153
+ "entity-name": entity_name,
154
+ "permission": permission,
155
+ "admin": admin,
156
+ }
157
+
158
+ r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
159
+ return common.process_response(r)
160
+
161
+
162
+ def set_inheritance(session: IRODSHTTPSession, lpath: str, enable: int, admin: int = 0) -> dict:
163
+ """
164
+ Set the inheritance for a collection.
165
+
166
+ Args:
167
+ session: IRODSHTTPSession object containing the base URL and authentication token.
168
+ lpath: The absolute logical path of the collection to have its inheritance set.
169
+ enable: Set to 1 to enable inheritance, or 0 to disable.
170
+ admin: Set to 1 to run this operation as an admin, otherwise set to 0. Defaults to 0.
171
+
172
+ Returns:
173
+ A dict containing the HTTP status code and iRODS response.
174
+ The iRODS response is only valid if no error occurred during HTTP communication.
175
+ """
176
+ common.validate_not_none(session.token)
177
+ common.validate_instance(lpath, str)
178
+ common.validate_0_or_1(enable)
179
+ common.validate_0_or_1(admin)
180
+
181
+ data = {
182
+ "op": "set_inheritance",
183
+ "lpath": lpath,
184
+ "enable": enable,
185
+ "admin": admin,
186
+ }
187
+
188
+ r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
189
+ return common.process_response(r)
190
+
191
+
192
+ def modify_permissions(session: IRODSHTTPSession, lpath: str, operations: dict, admin: int = 0) -> dict:
193
+ """
194
+ Modify permissions for multiple users or groups for a collection.
195
+
196
+ Args:
197
+ session: IRODSHTTPSession object containing the base URL and authentication token.
198
+ lpath: The absolute logical path of the collection to have its permissions modified.
199
+ operations: Dictionary containing the operations to carry out. Should contain names
200
+ and permissions for all operations.
201
+ admin: Set to 1 to run this operation as an admin, otherwise set to 0. Defaults to 0.
202
+
203
+ Returns:
204
+ A dict containing the HTTP status code and iRODS response.
205
+ The iRODS response is only valid if no error occurred during HTTP communication.
206
+ """
207
+ common.validate_not_none(session.token)
208
+ common.validate_instance(lpath, str)
209
+ common.validate_instance(operations, builtins.list)
210
+ common.validate_instance(operations[0], dict)
211
+ common.validate_0_or_1(admin)
212
+
213
+ data = {
214
+ "op": "modify_permissions",
215
+ "lpath": lpath,
216
+ "operations": json.dumps(operations),
217
+ "admin": admin,
218
+ }
219
+
220
+ r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
221
+ return common.process_response(r)
222
+
223
+
224
+ def modify_metadata(session: IRODSHTTPSession, lpath: str, operations: dict, admin: int = 0) -> dict:
225
+ """
226
+ Modify the metadata for a collection.
227
+
228
+ Args:
229
+ session: IRODSHTTPSession object containing the base URL and authentication token.
230
+ lpath: The absolute logical path of the collection to have its metadata modified.
231
+ operations: Dictionary containing the operations to carry out. Should contain the
232
+ operation, attribute, value, and optionally units.
233
+ admin: Set to 1 to run this operation as an admin, otherwise set to 0. Defaults to 0.
234
+
235
+ Returns:
236
+ A dict containing the HTTP status code and iRODS response.
237
+ The iRODS response is only valid if no error occurred during HTTP communication.
238
+ """
239
+ common.validate_not_none(session.token)
240
+ common.validate_instance(lpath, str)
241
+ common.validate_instance(operations, builtins.list)
242
+ common.validate_instance(operations[0], dict)
243
+ common.validate_0_or_1(admin)
244
+
245
+ data = {
246
+ "op": "modify_metadata",
247
+ "lpath": lpath,
248
+ "operations": json.dumps(operations),
249
+ "admin": admin,
250
+ }
251
+
252
+ r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
253
+ return common.process_response(r)
254
+
255
+
256
+ def rename(session: IRODSHTTPSession, old_lpath: str, new_lpath: str) -> dict:
257
+ """
258
+ Rename or move a collection.
259
+
260
+ Args:
261
+ session: IRODSHTTPSession object containing the base URL and authentication token.
262
+ old_lpath: The current absolute logical path of the collection.
263
+ new_lpath: The absolute logical path of the destination for the collection.
264
+
265
+ Returns:
266
+ A dict containing the HTTP status code and iRODS response.
267
+ The iRODS response is only valid if no error occurred during HTTP communication.
268
+ """
269
+ common.validate_not_none(session.token)
270
+ common.validate_instance(old_lpath, str)
271
+ common.validate_instance(new_lpath, str)
272
+
273
+ data = {"op": "rename", "old-lpath": old_lpath, "new-lpath": new_lpath}
274
+
275
+ r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
276
+ return common.process_response(r)
277
+
278
+
279
+ def touch(session: IRODSHTTPSession, lpath: str, seconds_since_epoch: int = -1, reference: str = "") -> dict:
280
+ """
281
+ Update mtime for a collection.
282
+
283
+ Args:
284
+ session: IRODSHTTPSession object containing the base URL and authentication token.
285
+ lpath: The absolute logical path of the collection being touched.
286
+ seconds_since_epoch: The value to set mtime to, defaults to -1 as a flag.
287
+ reference: The absolute logical path of the collection to use as a reference for mtime.
288
+
289
+ Returns:
290
+ A dict containing the HTTP status code and iRODS response.
291
+ The iRODS response is only valid if no error occurred during HTTP communication.
292
+ """
293
+ common.validate_not_none(session.token)
294
+ common.validate_instance(lpath, str)
295
+ common.validate_gte_minus1(seconds_since_epoch)
296
+ common.validate_instance(reference, str)
297
+
298
+ data = {"op": "touch", "lpath": lpath}
299
+
300
+ if seconds_since_epoch != -1:
301
+ data["seconds-since-epoch"] = seconds_since_epoch
302
+
303
+ if reference != "":
304
+ data["reference"] = reference
305
+
306
+ r = requests.post(session.url_base + "/collections", headers=session.post_headers, data=data) # noqa: S113
307
+ return common.process_response(r)
irods_http/common.py ADDED
@@ -0,0 +1,107 @@
1
+ """Common utility functions for iRODS HTTP client operations."""
2
+
3
+
4
+ def process_response(r):
5
+ """
6
+ Process an HTTP response and return standardized response dict.
7
+
8
+ Args:
9
+ r: The HTTP response object.
10
+
11
+ Returns:
12
+ A dict with 'status_code' and 'data' keys containing the HTTP status code
13
+ and parsed JSON response body (or None if response is empty).
14
+ """
15
+ rdict = r.json() if r.text != "" else None
16
+ return {"status_code": r.status_code, "data": rdict}
17
+
18
+
19
+ def validate_not_none(x):
20
+ """
21
+ Validate that a value is not None.
22
+
23
+ Args:
24
+ x: The value to validate.
25
+
26
+ Raises:
27
+ ValueError: If x is None
28
+ """
29
+ if x is None:
30
+ raise ValueError
31
+
32
+
33
+ def validate_instance(x, expected_type):
34
+ """
35
+ Validate that a value is an instance of the expected type.
36
+
37
+ Args:
38
+ x: The value to validate.
39
+ expected_type: The expected type.
40
+
41
+ Raises:
42
+ TypeError: If x is not an instance of expected_type.
43
+ """
44
+ if not isinstance(x, expected_type):
45
+ raise TypeError
46
+
47
+
48
+ def validate_0_or_1(x):
49
+ """
50
+ Validate that a value is either 0 or 1.
51
+
52
+ Args:
53
+ x: The value to validate (must be an int).
54
+
55
+ Raises:
56
+ TypeError: If x is not an integer.
57
+ ValueError: If x is not 0 or 1.
58
+ """
59
+ validate_instance(x, int)
60
+ if x not in [0, 1]:
61
+ raise ValueError(f"{x} must be 0 or 1")
62
+
63
+
64
+ def validate_gte_zero(x):
65
+ """
66
+ Validate that a value is greater than or equal to zero.
67
+
68
+ Args:
69
+ x: The value to validate (must be an int).
70
+
71
+ Raises:
72
+ TypeError: If x is not an integer.
73
+ ValueError: If x is less than 0.
74
+ """
75
+ validate_instance(x, int)
76
+ if not x >= 0:
77
+ raise ValueError(f"{x} must be >= 0")
78
+
79
+
80
+ def validate_gte_minus1(x):
81
+ """
82
+ Validate that a value is greater than or equal to -1.
83
+
84
+ Args:
85
+ x: The value to validate (must be an int).
86
+
87
+ Raises:
88
+ TypeError: If x is not an integer.
89
+ ValueError: If x is less than -1.
90
+ """
91
+ validate_instance(x, int)
92
+ if not x >= -1:
93
+ raise ValueError(f"{x} must be >= 0, or flag value of -1")
94
+
95
+
96
+ def assert_success(cls, response):
97
+ """
98
+ Validate HTTP and iRODS status codes are successes.
99
+
100
+ Args:
101
+ cls: The unittest.TestCase class
102
+ response: The response from the iRODS HTTP API request
103
+ """
104
+ # HTTP status code
105
+ cls.assertEqual(response["status_code"], 200)
106
+ # iRODS status code
107
+ cls.assertEqual(response["data"]["irods_response"]["status_code"], 0)