labkey 2.6.1__py3-none-any.whl → 3.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.
- labkey/__init__.py +1 -1
- labkey/api_wrapper.py +2 -0
- labkey/exceptions.py +16 -1
- labkey/query.py +182 -4
- labkey/security.py +1 -1
- labkey/server_context.py +27 -4
- labkey/utils.py +20 -0
- {labkey-2.6.1.dist-info → labkey-3.1.0.dist-info}/METADATA +2 -4
- labkey-3.1.0.dist-info/RECORD +16 -0
- {labkey-2.6.1.dist-info → labkey-3.1.0.dist-info}/WHEEL +1 -1
- labkey-2.6.1.dist-info/RECORD +0 -16
- {labkey-2.6.1.dist-info → labkey-3.1.0.dist-info}/LICENSE.txt +0 -0
- {labkey-2.6.1.dist-info → labkey-3.1.0.dist-info}/top_level.txt +0 -0
labkey/__init__.py
CHANGED
labkey/api_wrapper.py
CHANGED
@@ -22,6 +22,7 @@ class APIWrapper:
|
|
22
22
|
verify_ssl=True,
|
23
23
|
api_key=None,
|
24
24
|
disable_csrf=False,
|
25
|
+
allow_redirects=False,
|
25
26
|
):
|
26
27
|
self.server_context = ServerContext(
|
27
28
|
domain=domain,
|
@@ -31,6 +32,7 @@ class APIWrapper:
|
|
31
32
|
verify_ssl=verify_ssl,
|
32
33
|
api_key=api_key,
|
33
34
|
disable_csrf=disable_csrf,
|
35
|
+
allow_redirects=allow_redirects,
|
34
36
|
)
|
35
37
|
self.container = ContainerWrapper(self.server_context)
|
36
38
|
self.domain = DomainWrapper(self.server_context)
|
labkey/exceptions.py
CHANGED
@@ -47,7 +47,22 @@ class RequestError(exceptions.RequestException):
|
|
47
47
|
self.message = "No response received"
|
48
48
|
|
49
49
|
def __str__(self):
|
50
|
-
return
|
50
|
+
return str(self.message)
|
51
|
+
|
52
|
+
|
53
|
+
class UnexpectedRedirectError(RequestError):
|
54
|
+
default_msg = "Unexpected redirect occurred"
|
55
|
+
|
56
|
+
def __init__(self, server_response, **kwargs):
|
57
|
+
super().__init__(server_response, **kwargs)
|
58
|
+
|
59
|
+
location = server_response.headers.get("Location", "")
|
60
|
+
|
61
|
+
# If the server is redirecting from http to https the user probably has a misconfigured ServerContext with use_ssl=False
|
62
|
+
if server_response.url.startswith("http://") and location.startswith("https://"):
|
63
|
+
self.message = "Redirected from http to https, set use_ssl=True in your APIWrapper or ServerContext"
|
64
|
+
elif location != "":
|
65
|
+
self.message = f"Unexpected redirect to: {location}"
|
51
66
|
|
52
67
|
|
53
68
|
class QueryNotFoundError(RequestError):
|
labkey/query.py
CHANGED
@@ -44,6 +44,7 @@ import functools
|
|
44
44
|
from typing import List
|
45
45
|
|
46
46
|
from .server_context import ServerContext
|
47
|
+
from .utils import waf_encode
|
47
48
|
|
48
49
|
_default_timeout = 60 * 5 # 5 minutes
|
49
50
|
|
@@ -164,12 +165,25 @@ class QueryFilter:
|
|
164
165
|
return "<QueryFilter [{} {} {}]>".format(self.column_name, self.filter_type, self.value)
|
165
166
|
|
166
167
|
|
168
|
+
class AuditBehavior:
|
169
|
+
"""
|
170
|
+
Enum of different auditing levels
|
171
|
+
"""
|
172
|
+
|
173
|
+
DETAILED = "DETAILED"
|
174
|
+
NONE = "NONE"
|
175
|
+
SUMMARY = "SUMMARY"
|
176
|
+
|
177
|
+
|
167
178
|
def delete_rows(
|
168
179
|
server_context: ServerContext,
|
169
180
|
schema_name: str,
|
170
181
|
query_name: str,
|
171
182
|
rows: any,
|
172
183
|
container_path: str = None,
|
184
|
+
transacted: bool = True,
|
185
|
+
audit_behavior: AuditBehavior = None,
|
186
|
+
audit_user_comment: str = None,
|
173
187
|
timeout: int = _default_timeout,
|
174
188
|
):
|
175
189
|
"""
|
@@ -179,12 +193,25 @@ def delete_rows(
|
|
179
193
|
:param query_name: table name to delete from
|
180
194
|
:param rows: Set of rows to delete
|
181
195
|
:param container_path: labkey container path if not already set in context
|
196
|
+
:param transacted: whether all of the updates should be done in a single transaction
|
197
|
+
:param audit_behavior: used to override the audit behavior for the update. See class query.AuditBehavior
|
198
|
+
:param audit_user_comment: used to provide a comment that will be attached to certain detailed audit log records
|
182
199
|
:param timeout: timeout of request in seconds (defaults to 30s)
|
183
200
|
:return:
|
184
201
|
"""
|
185
202
|
url = server_context.build_url("query", "deleteRows.api", container_path=container_path)
|
203
|
+
|
186
204
|
payload = {"schemaName": schema_name, "queryName": query_name, "rows": rows}
|
187
205
|
|
206
|
+
if transacted is False:
|
207
|
+
payload["transacted"] = transacted
|
208
|
+
|
209
|
+
if audit_behavior is not None:
|
210
|
+
payload["auditBehavior"] = audit_behavior
|
211
|
+
|
212
|
+
if audit_user_comment is not None:
|
213
|
+
payload["auditUserComment"] = audit_user_comment
|
214
|
+
|
188
215
|
return server_context.make_request(
|
189
216
|
url,
|
190
217
|
json=payload,
|
@@ -231,6 +258,7 @@ def execute_sql(
|
|
231
258
|
parameters: dict = None,
|
232
259
|
required_version: float = None,
|
233
260
|
timeout: int = _default_timeout,
|
261
|
+
waf_encode_sql: bool = True,
|
234
262
|
):
|
235
263
|
"""
|
236
264
|
Execute sql query against a LabKey server.
|
@@ -248,11 +276,12 @@ def execute_sql(
|
|
248
276
|
:param parameters: parameter values to pass through to a parameterized query
|
249
277
|
:param required_version: Api version of response
|
250
278
|
:param timeout: timeout of request in seconds (defaults to 30s)
|
279
|
+
:param waf_encode_sql: WAF encode sql in request (defaults to True)
|
251
280
|
:return:
|
252
281
|
"""
|
253
282
|
url = server_context.build_url("query", "executeSql.api", container_path=container_path)
|
254
283
|
|
255
|
-
payload = {"schemaName": schema_name, "sql": sql}
|
284
|
+
payload = {"schemaName": schema_name, "sql": waf_encode(sql) if waf_encode_sql else sql}
|
256
285
|
|
257
286
|
if container_filter is not None:
|
258
287
|
payload["containerFilter"] = container_filter
|
@@ -285,6 +314,10 @@ def insert_rows(
|
|
285
314
|
query_name: str,
|
286
315
|
rows: List[any],
|
287
316
|
container_path: str = None,
|
317
|
+
skip_reselect_rows: bool = False,
|
318
|
+
transacted: bool = True,
|
319
|
+
audit_behavior: AuditBehavior = None,
|
320
|
+
audit_user_comment: str = None,
|
288
321
|
timeout: int = _default_timeout,
|
289
322
|
):
|
290
323
|
"""
|
@@ -294,6 +327,10 @@ def insert_rows(
|
|
294
327
|
:param query_name: table name to insert into
|
295
328
|
:param rows: set of rows to insert
|
296
329
|
:param container_path: labkey container path if not already set in context
|
330
|
+
:param skip_reselect_rows: whether the full detailed response for the insert can be skipped
|
331
|
+
:param transacted: whether all of the updates should be done in a single transaction
|
332
|
+
:param audit_behavior: used to override the audit behavior for the update. See class query.AuditBehavior
|
333
|
+
:param audit_user_comment: used to provide a comment that will be attached to certain detailed audit log records
|
297
334
|
:param timeout: timeout of request in seconds (defaults to 30s)
|
298
335
|
:return:
|
299
336
|
"""
|
@@ -301,6 +338,18 @@ def insert_rows(
|
|
301
338
|
|
302
339
|
payload = {"schemaName": schema_name, "queryName": query_name, "rows": rows}
|
303
340
|
|
341
|
+
if skip_reselect_rows is True:
|
342
|
+
payload["skipReselectRows"] = skip_reselect_rows
|
343
|
+
|
344
|
+
if transacted is False:
|
345
|
+
payload["transacted"] = transacted
|
346
|
+
|
347
|
+
if audit_behavior is not None:
|
348
|
+
payload["auditBehavior"] = audit_behavior
|
349
|
+
|
350
|
+
if audit_user_comment is not None:
|
351
|
+
payload["auditUserComment"] = audit_user_comment
|
352
|
+
|
304
353
|
return server_context.make_request(
|
305
354
|
url,
|
306
355
|
json=payload,
|
@@ -419,6 +468,9 @@ def update_rows(
|
|
419
468
|
query_name: str,
|
420
469
|
rows: List[any],
|
421
470
|
container_path: str = None,
|
471
|
+
transacted: bool = True,
|
472
|
+
audit_behavior: AuditBehavior = None,
|
473
|
+
audit_user_comment: str = None,
|
422
474
|
timeout: int = _default_timeout,
|
423
475
|
):
|
424
476
|
"""
|
@@ -429,6 +481,9 @@ def update_rows(
|
|
429
481
|
:param query_name: table name to update
|
430
482
|
:param rows: Set of rows to update
|
431
483
|
:param container_path: labkey container path if not already set in context
|
484
|
+
:param transacted: whether all of the updates should be done in a single transaction
|
485
|
+
:param audit_behavior: used to override the audit behavior for the update. See class query.AuditBehavior
|
486
|
+
:param audit_user_comment: used to provide a comment that will be attached to certain detailed audit log records
|
432
487
|
:param timeout: timeout of request in seconds (defaults to 30s)
|
433
488
|
:return:
|
434
489
|
"""
|
@@ -436,6 +491,66 @@ def update_rows(
|
|
436
491
|
|
437
492
|
payload = {"schemaName": schema_name, "queryName": query_name, "rows": rows}
|
438
493
|
|
494
|
+
if transacted is False:
|
495
|
+
payload["transacted"] = transacted
|
496
|
+
|
497
|
+
if audit_behavior is not None:
|
498
|
+
payload["auditBehavior"] = audit_behavior
|
499
|
+
|
500
|
+
if audit_user_comment is not None:
|
501
|
+
payload["auditUserComment"] = audit_user_comment
|
502
|
+
|
503
|
+
return server_context.make_request(
|
504
|
+
url,
|
505
|
+
json=payload,
|
506
|
+
timeout=timeout,
|
507
|
+
)
|
508
|
+
|
509
|
+
|
510
|
+
def move_rows(
|
511
|
+
server_context: ServerContext,
|
512
|
+
target_container_path: str,
|
513
|
+
schema_name: str,
|
514
|
+
query_name: str,
|
515
|
+
rows: any,
|
516
|
+
container_path: str = None,
|
517
|
+
transacted: bool = True,
|
518
|
+
audit_behavior: AuditBehavior = None,
|
519
|
+
audit_user_comment: str = None,
|
520
|
+
timeout: int = _default_timeout,
|
521
|
+
):
|
522
|
+
"""
|
523
|
+
Move a set of rows from the schema.query
|
524
|
+
:param server_context: A LabKey server context. See utils.create_server_context.
|
525
|
+
:param target_container_path: target labkey container path for the move
|
526
|
+
:param schema_name: schema of table
|
527
|
+
:param query_name: table name to move from
|
528
|
+
:param rows: Set of rows to move
|
529
|
+
:param container_path: source labkey container path if not already set in context
|
530
|
+
:param transacted: whether all of the updates should be done in a single transaction
|
531
|
+
:param audit_behavior: used to override the audit behavior for the update. See class query.AuditBehavior
|
532
|
+
:param audit_user_comment: used to provide a comment that will be attached to certain detailed audit log records
|
533
|
+
:param timeout: timeout of request in seconds (defaults to 30s)
|
534
|
+
:return:
|
535
|
+
"""
|
536
|
+
url = server_context.build_url("query", "moveRows.api", container_path=container_path)
|
537
|
+
|
538
|
+
payload = {
|
539
|
+
"targetContainerPath": target_container_path,
|
540
|
+
"schemaName": schema_name,
|
541
|
+
"queryName": query_name,
|
542
|
+
"rows": rows,
|
543
|
+
}
|
544
|
+
|
545
|
+
if transacted is False:
|
546
|
+
payload["transacted"] = transacted
|
547
|
+
|
548
|
+
if audit_behavior is not None:
|
549
|
+
payload["auditBehavior"] = audit_behavior
|
550
|
+
|
551
|
+
if audit_user_comment is not None:
|
552
|
+
payload["auditUserComment"] = audit_user_comment
|
553
|
+
|
439
554
|
return server_context.make_request(
|
440
555
|
url,
|
441
556
|
json=payload,
|
@@ -458,10 +573,21 @@ class QueryWrapper:
|
|
458
573
|
query_name: str,
|
459
574
|
rows: any,
|
460
575
|
container_path: str = None,
|
576
|
+
transacted: bool = True,
|
577
|
+
audit_behavior: AuditBehavior = None,
|
578
|
+
audit_user_comment: str = None,
|
461
579
|
timeout: int = _default_timeout,
|
462
580
|
):
|
463
581
|
return delete_rows(
|
464
|
-
self.server_context,
|
582
|
+
self.server_context,
|
583
|
+
schema_name,
|
584
|
+
query_name,
|
585
|
+
rows,
|
586
|
+
container_path,
|
587
|
+
transacted,
|
588
|
+
audit_behavior,
|
589
|
+
audit_user_comment,
|
590
|
+
timeout,
|
465
591
|
)
|
466
592
|
|
467
593
|
@functools.wraps(truncate_table)
|
@@ -484,6 +610,7 @@ class QueryWrapper:
|
|
484
610
|
parameters: dict = None,
|
485
611
|
required_version: float = None,
|
486
612
|
timeout: int = _default_timeout,
|
613
|
+
waf_encode_sql: bool = True,
|
487
614
|
):
|
488
615
|
return execute_sql(
|
489
616
|
self.server_context,
|
@@ -498,6 +625,7 @@ class QueryWrapper:
|
|
498
625
|
parameters,
|
499
626
|
required_version,
|
500
627
|
timeout,
|
628
|
+
waf_encode_sql,
|
501
629
|
)
|
502
630
|
|
503
631
|
@functools.wraps(insert_rows)
|
@@ -507,10 +635,23 @@ class QueryWrapper:
|
|
507
635
|
query_name: str,
|
508
636
|
rows: List[any],
|
509
637
|
container_path: str = None,
|
638
|
+
skip_reselect_rows: bool = False,
|
639
|
+
transacted: bool = True,
|
640
|
+
audit_behavior: AuditBehavior = None,
|
641
|
+
audit_user_comment: str = None,
|
510
642
|
timeout: int = _default_timeout,
|
511
643
|
):
|
512
644
|
return insert_rows(
|
513
|
-
self.server_context,
|
645
|
+
self.server_context,
|
646
|
+
schema_name,
|
647
|
+
query_name,
|
648
|
+
rows,
|
649
|
+
container_path,
|
650
|
+
skip_reselect_rows,
|
651
|
+
transacted,
|
652
|
+
audit_behavior,
|
653
|
+
audit_user_comment,
|
654
|
+
timeout,
|
514
655
|
)
|
515
656
|
|
516
657
|
@functools.wraps(select_rows)
|
@@ -566,8 +707,45 @@ class QueryWrapper:
|
|
566
707
|
query_name: str,
|
567
708
|
rows: List[any],
|
568
709
|
container_path: str = None,
|
710
|
+
transacted: bool = True,
|
711
|
+
audit_behavior: AuditBehavior = None,
|
712
|
+
audit_user_comment: str = None,
|
569
713
|
timeout: int = _default_timeout,
|
570
714
|
):
|
571
715
|
return update_rows(
|
572
|
-
self.server_context,
|
716
|
+
self.server_context,
|
717
|
+
schema_name,
|
718
|
+
query_name,
|
719
|
+
rows,
|
720
|
+
container_path,
|
721
|
+
transacted,
|
722
|
+
audit_behavior,
|
723
|
+
audit_user_comment,
|
724
|
+
timeout,
|
725
|
+
)
|
726
|
+
|
727
|
+
@functools.wraps(move_rows)
|
728
|
+
def move_rows(
|
729
|
+
self,
|
730
|
+
target_container_path: str,
|
731
|
+
schema_name: str,
|
732
|
+
query_name: str,
|
733
|
+
rows: any,
|
734
|
+
container_path: str = None,
|
735
|
+
transacted: bool = True,
|
736
|
+
audit_behavior: AuditBehavior = None,
|
737
|
+
audit_user_comment: str = None,
|
738
|
+
timeout: int = _default_timeout,
|
739
|
+
):
|
740
|
+
return move_rows(
|
741
|
+
self.server_context,
|
742
|
+
target_container_path,
|
743
|
+
schema_name,
|
744
|
+
query_name,
|
745
|
+
rows,
|
746
|
+
container_path,
|
747
|
+
transacted,
|
748
|
+
audit_behavior,
|
749
|
+
audit_user_comment,
|
750
|
+
timeout,
|
573
751
|
)
|
labkey/security.py
CHANGED
@@ -279,7 +279,7 @@ def stop_impersonating(server_context: ServerContext):
|
|
279
279
|
Stop impersonating a user while keeping the original user logged in.
|
280
280
|
"""
|
281
281
|
url = server_context.build_url(LOGIN_CONTROLLER, "stopImpersonating.api")
|
282
|
-
return server_context.make_request(url)
|
282
|
+
return server_context.make_request(url, allow_redirects=True)
|
283
283
|
|
284
284
|
|
285
285
|
@dataclass
|
labkey/server_context.py
CHANGED
@@ -8,6 +8,7 @@ from labkey.exceptions import (
|
|
8
8
|
QueryNotFoundError,
|
9
9
|
ServerContextError,
|
10
10
|
ServerNotFoundError,
|
11
|
+
UnexpectedRedirectError,
|
11
12
|
)
|
12
13
|
|
13
14
|
API_KEY_TOKEN = "apikey"
|
@@ -29,7 +30,8 @@ def handle_response(response, non_json_response=False):
|
|
29
30
|
content=response.content,
|
30
31
|
)
|
31
32
|
return result
|
32
|
-
|
33
|
+
elif sc == 302:
|
34
|
+
raise UnexpectedRedirectError(response)
|
33
35
|
elif sc == 401:
|
34
36
|
raise RequestAuthorizationError(response)
|
35
37
|
elif sc == 404:
|
@@ -62,6 +64,7 @@ class ServerContext:
|
|
62
64
|
verify_ssl=True,
|
63
65
|
api_key=None,
|
64
66
|
disable_csrf=False,
|
67
|
+
allow_redirects=False,
|
65
68
|
):
|
66
69
|
self._container_path = container_path
|
67
70
|
self._context_path = context_path
|
@@ -70,6 +73,7 @@ class ServerContext:
|
|
70
73
|
self._verify_ssl = verify_ssl
|
71
74
|
self._api_key = api_key
|
72
75
|
self._disable_csrf = disable_csrf
|
76
|
+
self.allow_redirects = allow_redirects
|
73
77
|
self._session = requests.Session()
|
74
78
|
self._session.headers.update({"User-Agent": f"LabKey Python API/{__version__}"})
|
75
79
|
|
@@ -174,7 +178,9 @@ class ServerContext:
|
|
174
178
|
non_json_response: bool = False,
|
175
179
|
file_payload: any = None,
|
176
180
|
json: dict = None,
|
181
|
+
allow_redirects=False,
|
177
182
|
) -> any:
|
183
|
+
allow_redirects_ = allow_redirects or self.allow_redirects
|
178
184
|
if self._api_key is not None:
|
179
185
|
if self._session.headers.get(API_KEY_TOKEN) is not self._api_key:
|
180
186
|
self._session.headers.update({API_KEY_TOKEN: self._api_key})
|
@@ -189,7 +195,13 @@ class ServerContext:
|
|
189
195
|
|
190
196
|
try:
|
191
197
|
if method == "GET":
|
192
|
-
response = self._session.get(
|
198
|
+
response = self._session.get(
|
199
|
+
url,
|
200
|
+
params=payload,
|
201
|
+
headers=headers,
|
202
|
+
timeout=timeout,
|
203
|
+
allow_redirects=allow_redirects_,
|
204
|
+
)
|
193
205
|
else:
|
194
206
|
if file_payload is not None:
|
195
207
|
response = self._session.post(
|
@@ -198,6 +210,7 @@ class ServerContext:
|
|
198
210
|
files=file_payload,
|
199
211
|
headers=headers,
|
200
212
|
timeout=timeout,
|
213
|
+
allow_redirects=allow_redirects_,
|
201
214
|
)
|
202
215
|
elif json is not None:
|
203
216
|
if headers is None:
|
@@ -206,10 +219,20 @@ class ServerContext:
|
|
206
219
|
headers_ = {**headers, "Content-Type": "application/json"}
|
207
220
|
# sort_keys is a hack to make unit tests work
|
208
221
|
data = json_dumps(json, sort_keys=True)
|
209
|
-
response = self._session.post(
|
222
|
+
response = self._session.post(
|
223
|
+
url,
|
224
|
+
data=data,
|
225
|
+
headers=headers_,
|
226
|
+
timeout=timeout,
|
227
|
+
allow_redirects=allow_redirects_,
|
228
|
+
)
|
210
229
|
else:
|
211
230
|
response = self._session.post(
|
212
|
-
url,
|
231
|
+
url,
|
232
|
+
data=payload,
|
233
|
+
headers=headers,
|
234
|
+
timeout=timeout,
|
235
|
+
allow_redirects=allow_redirects_,
|
213
236
|
)
|
214
237
|
return handle_response(response, non_json_response)
|
215
238
|
except RequestException as e:
|
labkey/utils.py
CHANGED
@@ -16,6 +16,8 @@
|
|
16
16
|
import json
|
17
17
|
from functools import wraps
|
18
18
|
from datetime import date, datetime
|
19
|
+
from base64 import b64encode
|
20
|
+
from urllib import parse
|
19
21
|
|
20
22
|
|
21
23
|
# Issue #14: json.dumps on datetime throws TypeError
|
@@ -71,3 +73,21 @@ def transform_helper(user_transform_func, file_path_run_properties):
|
|
71
73
|
row = [str(el).strip() for el in row]
|
72
74
|
row = "\t".join(row)
|
73
75
|
file_out.write(row + "\n")
|
76
|
+
|
77
|
+
|
78
|
+
def btoa(value: str) -> str:
|
79
|
+
if not value:
|
80
|
+
return value
|
81
|
+
binary = value.encode("utf-8")
|
82
|
+
return b64encode(binary).decode()
|
83
|
+
|
84
|
+
|
85
|
+
def encode_uri_component(value: str) -> str:
|
86
|
+
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
|
87
|
+
return parse.quote(value, encoding="utf-8", safe="-_.!~*'()")
|
88
|
+
|
89
|
+
|
90
|
+
def waf_encode(value: str) -> str:
|
91
|
+
if value:
|
92
|
+
return "/*{{base64/x-www-form-urlencoded/wafText}}*/" + btoa(encode_uri_component(value))
|
93
|
+
return value
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: labkey
|
3
|
-
Version:
|
3
|
+
Version: 3.1.0
|
4
4
|
Summary: Python client API for LabKey Server
|
5
5
|
Home-page: https://github.com/LabKey/labkey-api-python
|
6
6
|
Author: LabKey
|
@@ -9,7 +9,6 @@ Maintainer: Alan Vezina
|
|
9
9
|
Maintainer-email: alanv@labkey.com
|
10
10
|
License: Apache License 2.0
|
11
11
|
Keywords: labkey api client
|
12
|
-
Platform: UNKNOWN
|
13
12
|
Classifier: Development Status :: 4 - Beta
|
14
13
|
Classifier: Environment :: Console
|
15
14
|
Classifier: Intended Audience :: Science/Research
|
@@ -20,6 +19,7 @@ Classifier: Operating System :: Microsoft
|
|
20
19
|
Classifier: Operating System :: POSIX
|
21
20
|
Classifier: Programming Language :: Python :: 3
|
22
21
|
Classifier: Topic :: Scientific/Engineering
|
22
|
+
License-File: LICENSE.txt
|
23
23
|
Requires-Dist: requests
|
24
24
|
Provides-Extra: test
|
25
25
|
Requires-Dist: pytest ; extra == 'test'
|
@@ -28,5 +28,3 @@ Requires-Dist: mock ; extra == 'test'
|
|
28
28
|
Requires-Dist: pytest-cov ; extra == 'test'
|
29
29
|
|
30
30
|
Python client API for LabKey Server. Supports query and experiment APIs.
|
31
|
-
|
32
|
-
|
@@ -0,0 +1,16 @@
|
|
1
|
+
labkey/__init__.py,sha256=SjBskVArzIGzH9aIlUwSK246XoguCK7iCvEm0fXQAhQ,695
|
2
|
+
labkey/api_wrapper.py,sha256=OxnV6_5jONWiwsi24TOXTKP5inXtAfABGqXAPua3eCM,1427
|
3
|
+
labkey/container.py,sha256=DXmLhGsNnN_QLXa_tMCM0Xf_Kkz7B8KjRFhJT5I4FtY,5497
|
4
|
+
labkey/domain.py,sha256=SABG7BfSqQGJr8DPjUsvhi8C7xP2MTpkq24sdtAu4J8,22486
|
5
|
+
labkey/exceptions.py,sha256=00x-4oP_2d0SfZKxsGIs8QGvXiNq2y7C5hAzuzT3Gkk,3619
|
6
|
+
labkey/experiment.py,sha256=Wtuz52bhuvDWk_o2AbTw7SJnwEji2iD6Q04ct0PWSxE,8804
|
7
|
+
labkey/query.py,sha256=w6vYDwiwnLalrDQ8pGY-c9DvoPQuMXaFXnZLRUiezuk,24041
|
8
|
+
labkey/security.py,sha256=eMa2b_22FWB2rVvVpxTeWrK0VgGTszhRchsApbDR6sA,15306
|
9
|
+
labkey/server_context.py,sha256=0YcA7BRijLMs172qTrTB12MlnIcqz4X9dkkCwSxxzaA,7741
|
10
|
+
labkey/storage.py,sha256=WpgpTWQzgig_qJXMGszFcFjtf8oMd2-6TBehSkpJPUg,6144
|
11
|
+
labkey/utils.py,sha256=oL6qmHkpzLTm_9c7gSEzumv_MVh5s8fj6bOrbuslrV0,3233
|
12
|
+
labkey-3.1.0.dist-info/LICENSE.txt,sha256=xllut76FgcGL5zbIRvuRc7aezPbvlMUTWJPsVr2Sugg,11358
|
13
|
+
labkey-3.1.0.dist-info/METADATA,sha256=8O6aEerFp6Qn5B18KBaa7GGKApoNCwDDWDpaB9atXwM,1076
|
14
|
+
labkey-3.1.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
15
|
+
labkey-3.1.0.dist-info/top_level.txt,sha256=DQIk1fQNg7NxxBsEt_K7k-aopiH87jmnU98zwDp0n04,7
|
16
|
+
labkey-3.1.0.dist-info/RECORD,,
|
labkey-2.6.1.dist-info/RECORD
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
labkey/__init__.py,sha256=kC19-fjUIjUmWaNbRLugYAwZn-jtYgZt-dd65OLQ6hw,695
|
2
|
-
labkey/api_wrapper.py,sha256=ks6q5qwcGkutg2m5sMdTvryhfK42yltfPCV5iLPi22k,1351
|
3
|
-
labkey/container.py,sha256=DXmLhGsNnN_QLXa_tMCM0Xf_Kkz7B8KjRFhJT5I4FtY,5497
|
4
|
-
labkey/domain.py,sha256=SABG7BfSqQGJr8DPjUsvhi8C7xP2MTpkq24sdtAu4J8,22486
|
5
|
-
labkey/exceptions.py,sha256=VtuKphEczSsfHAXgWRv-2HRzxSiObBnOEC_sgEPPR3c,2929
|
6
|
-
labkey/experiment.py,sha256=Wtuz52bhuvDWk_o2AbTw7SJnwEji2iD6Q04ct0PWSxE,8804
|
7
|
-
labkey/query.py,sha256=ZUQLdfZOFJ07om1L0ARPlcLMXGIskxgGMJwuo5Y_L6Q,18083
|
8
|
-
labkey/security.py,sha256=cVrlYbb7Yogjo9L1exGKgpscRFEljBpzFITQZWHW2X8,15284
|
9
|
-
labkey/server_context.py,sha256=UGf4fJ9Cw_YIM_DwdPVP8WgKORt5aO_4bFCz14rjsrg,6950
|
10
|
-
labkey/storage.py,sha256=WpgpTWQzgig_qJXMGszFcFjtf8oMd2-6TBehSkpJPUg,6144
|
11
|
-
labkey/utils.py,sha256=rfZM1LT5iPOgVbKcOLUuWh59LqXYV-g3zoQUKwKcz-I,2651
|
12
|
-
labkey-2.6.1.dist-info/LICENSE.txt,sha256=xllut76FgcGL5zbIRvuRc7aezPbvlMUTWJPsVr2Sugg,11358
|
13
|
-
labkey-2.6.1.dist-info/METADATA,sha256=YZi0E0SrIagngSWsUND3mTI4D7jJzz7YZiYI_b1Rmbc,1070
|
14
|
-
labkey-2.6.1.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
|
15
|
-
labkey-2.6.1.dist-info/top_level.txt,sha256=DQIk1fQNg7NxxBsEt_K7k-aopiH87jmnU98zwDp0n04,7
|
16
|
-
labkey-2.6.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|