dcicutils 8.5.0.1b6__py3-none-any.whl → 8.5.0.1b7__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.
dcicutils/portal_utils.py CHANGED
@@ -1,13 +1,17 @@
1
1
  from collections import deque
2
2
  import io
3
3
  import json
4
- from pyramid.paster import get_app
5
- from pyramid.router import Router
4
+ from pyramid.config import Configurator as PyramidConfigurator
5
+ from pyramid.paster import get_app as pyramid_get_app
6
+ from pyramid.response import Response as PyramidResponse
7
+ from pyramid.router import Router as PyramidRouter
6
8
  import os
7
9
  import re
8
10
  import requests
9
11
  from requests.models import Response as RequestResponse
10
- from typing import Optional, Type, Union
12
+ from typing import Callable, Dict, List, Optional, Type, Union
13
+ from uuid import uuid4 as uuid
14
+ # from waitress import serve
11
15
  from webtest.app import TestApp, TestResponse
12
16
  from dcicutils.common import OrchestratedApp, ORCHESTRATED_APPS
13
17
  from dcicutils.ff_utils import get_metadata, get_schema, patch_metadata, post_metadata
@@ -37,7 +41,7 @@ class Portal:
37
41
  KEYS_FILE_DIRECTORY = os.path.expanduser(f"~")
38
42
 
39
43
  def __init__(self,
40
- arg: Optional[Union[Portal, TestApp, VirtualApp, Router, dict, tuple, str]] = None,
44
+ arg: Optional[Union[Portal, TestApp, VirtualApp, PyramidRouter, dict, tuple, str]] = None,
41
45
  env: Optional[str] = None, server: Optional[str] = None,
42
46
  app: Optional[OrchestratedApp] = None) -> None:
43
47
 
@@ -69,14 +73,14 @@ class Portal:
69
73
  self._app = portal._app
70
74
  self._vapp = portal._vapp
71
75
 
72
- def init_from_vapp(vapp: Union[TestApp, VirtualApp, Router], unspecified: Optional[list] = []) -> None:
76
+ def init_from_vapp(vapp: Union[TestApp, VirtualApp, PyramidRouter], unspecified: Optional[list] = []) -> None:
73
77
  init(unspecified)
74
- self._vapp = Portal._create_testapp(vapp)
78
+ self._vapp = Portal._create_vapp(vapp)
75
79
 
76
80
  def init_from_ini_file(ini_file: str, unspecified: Optional[list] = []) -> None:
77
81
  init(unspecified)
78
82
  self._ini_file = ini_file
79
- self._vapp = Portal._create_testapp(ini_file)
83
+ self._vapp = Portal._create_vapp(ini_file)
80
84
 
81
85
  def init_from_key(key: dict, server: Optional[str], unspecified: Optional[list] = []) -> None:
82
86
  init(unspecified)
@@ -138,7 +142,7 @@ class Portal:
138
142
 
139
143
  if isinstance(arg, Portal):
140
144
  init_from_portal(arg, unspecified=[env, server, app])
141
- elif isinstance(arg, (TestApp, VirtualApp, Router)):
145
+ elif isinstance(arg, (TestApp, VirtualApp, PyramidRouter)):
142
146
  init_from_vapp(arg, unspecified=[env, server, app])
143
147
  elif isinstance(arg, str) and arg.endswith(".ini"):
144
148
  init_from_ini_file(arg, unspecified=[env, server, app])
@@ -201,35 +205,36 @@ class Portal:
201
205
  def patch_metadata(self, object_id: str, data: str) -> Optional[dict]:
202
206
  if self._key:
203
207
  return patch_metadata(obj_id=object_id, patch_item=data, key=self._key)
204
- return self.patch(f"/{object_id}", data)
208
+ return self.patch(f"/{object_id}", data).json()
205
209
 
206
210
  def post_metadata(self, object_type: str, data: str) -> Optional[dict]:
207
211
  if self._key:
208
212
  return post_metadata(schema_name=object_type, post_item=data, key=self._key)
209
- return self.post(f"/{object_type}", data)
213
+ return self.post(f"/{object_type}", data).json()
210
214
 
211
215
  def get(self, uri: str, follow: bool = True, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
212
- if self._vapp:
213
- response = self._vapp.get(self.url(uri), **self._kwargs(**kwargs))
214
- if response and response.status_code in [301, 302, 303, 307, 308] and follow:
215
- response = response.follow()
216
- return self._response(response)
217
- return requests.get(self.url(uri), allow_redirects=follow, **self._kwargs(**kwargs))
216
+ if not self._vapp:
217
+ return requests.get(self.url(uri), allow_redirects=follow, **self._kwargs(**kwargs))
218
+ response = self._vapp.get(self.url(uri), **self._kwargs(**kwargs))
219
+ if response and response.status_code in [301, 302, 303, 307, 308] and follow:
220
+ response = response.follow()
221
+ return self._response(response)
218
222
 
219
223
  def patch(self, uri: str, data: Optional[dict] = None,
220
224
  json: Optional[dict] = None, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
221
- if self._vapp:
222
- return self._vapp.patch_json(self.url(uri), json or data, **self._kwargs(**kwargs))
223
- return requests.patch(self.url(uri), data=data, json=json, **self._kwargs(**kwargs))
225
+ if not self._vapp:
226
+ return requests.patch(self.url(uri), data=data, json=json, **self._kwargs(**kwargs))
227
+ return self._response(self._vapp.patch_json(self.url(uri), json or data, **self._kwargs(**kwargs)))
224
228
 
225
229
  def post(self, uri: str, data: Optional[dict] = None, json: Optional[dict] = None,
226
230
  files: Optional[dict] = None, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
227
- if self._vapp:
228
- if files:
229
- return self._vapp.post(self.url(uri), json or data, upload_files=files, **self._kwargs(**kwargs))
230
- else:
231
- return self._vapp.post_json(self.url(uri), json or data, upload_files=files, **self._kwargs(**kwargs))
232
- return requests.post(self.url(uri), data=data, json=json, files=files, **self._kwargs(**kwargs))
231
+ if not self._vapp:
232
+ return requests.post(self.url(uri), data=data, json=json, files=files, **self._kwargs(**kwargs))
233
+ if files:
234
+ response = self._vapp.post(self.url(uri), json or data, upload_files=files, **self._kwargs(**kwargs))
235
+ else:
236
+ response = self._vapp.post_json(self.url(uri), json or data, upload_files=files, **self._kwargs(**kwargs))
237
+ return self._response(response)
233
238
 
234
239
  def get_schema(self, schema_name: str) -> Optional[dict]:
235
240
  return get_schema(self.schema_name(schema_name), portal_vapp=self._vapp, key=self._key)
@@ -311,7 +316,7 @@ class Portal:
311
316
  if is_valid_app(app) or (app := infer_app_from_env(env)):
312
317
  return os.path.join(Portal.KEYS_FILE_DIRECTORY, f".{app.lower()}-keys.json")
313
318
 
314
- def _response(self, response) -> Optional[RequestResponse]:
319
+ def _response(self, response: TestResponse) -> Optional[RequestResponse]:
315
320
  if response and isinstance(getattr(response.__class__, "json"), property):
316
321
  class RequestResponseWrapper: # For consistency change json property to method.
317
322
  def __init__(self, response, **kwargs):
@@ -325,51 +330,72 @@ class Portal:
325
330
  return response
326
331
 
327
332
  @staticmethod
328
- def create_for_testing(ini_file: Optional[str] = None) -> Portal:
329
- if isinstance(ini_file, str):
330
- return Portal(Portal._create_testapp(ini_file))
331
- minimal_ini_for_unit_testing = "[app:app]\nuse = egg:encoded\nsqlalchemy.url = postgresql://dummy\n"
332
- with temporary_file(content=minimal_ini_for_unit_testing, suffix=".ini") as ini_file:
333
- return Portal(Portal._create_testapp(ini_file))
334
-
335
- @staticmethod
336
- def create_for_testing_local(ini_file: Optional[str] = None) -> Portal:
337
- if isinstance(ini_file, str) and ini_file:
338
- return Portal(Portal._create_testapp(ini_file))
339
- minimal_ini_for_testing_local = "\n".join([
340
- "[app:app]\nuse = egg:encoded\nfile_upload_bucket = dummy",
341
- "sqlalchemy.url = postgresql://postgres@localhost:5441/postgres?host=/tmp/snovault/pgdata",
342
- "multiauth.groupfinder = encoded.authorization.smaht_groupfinder",
343
- "multiauth.policies = auth0 session remoteuser accesskey",
344
- "multiauth.policy.session.namespace = mailto",
345
- "multiauth.policy.session.use = encoded.authentication.NamespacedAuthenticationPolicy",
346
- "multiauth.policy.session.base = pyramid.authentication.SessionAuthenticationPolicy",
347
- "multiauth.policy.remoteuser.namespace = remoteuser",
348
- "multiauth.policy.remoteuser.use = encoded.authentication.NamespacedAuthenticationPolicy",
349
- "multiauth.policy.remoteuser.base = pyramid.authentication.RemoteUserAuthenticationPolicy",
350
- "multiauth.policy.accesskey.namespace = accesskey",
351
- "multiauth.policy.accesskey.use = encoded.authentication.NamespacedAuthenticationPolicy",
352
- "multiauth.policy.accesskey.base = encoded.authentication.BasicAuthAuthenticationPolicy",
353
- "multiauth.policy.accesskey.check = encoded.authentication.basic_auth_check",
354
- "multiauth.policy.auth0.use = encoded.authentication.NamespacedAuthenticationPolicy",
355
- "multiauth.policy.auth0.namespace = auth0",
356
- "multiauth.policy.auth0.base = encoded.authentication.Auth0AuthenticationPolicy"
357
- ])
358
- with temporary_file(content=minimal_ini_for_testing_local, suffix=".ini") as minimal_ini_file:
359
- return Portal(Portal._create_testapp(minimal_ini_file))
333
+ def create_for_testing(arg: Optional[Union[str, bool, List[dict], dict, Callable]] = None) -> Portal:
334
+ if isinstance(arg, list) or isinstance(arg, dict) or isinstance(arg, Callable):
335
+ return Portal(Portal._create_router_for_testing(arg))
336
+ if isinstance(arg, str) and arg.endswith(".ini"):
337
+ return Portal(Portal._create_vapp(arg))
338
+ if arg == "local" or arg is True:
339
+ minimal_ini_for_testing = "\n".join([
340
+ "[app:app]\nuse = egg:encoded\nfile_upload_bucket = dummy",
341
+ "sqlalchemy.url = postgresql://postgres@localhost:5441/postgres?host=/tmp/snovault/pgdata",
342
+ "multiauth.groupfinder = encoded.authorization.smaht_groupfinder",
343
+ "multiauth.policies = auth0 session remoteuser accesskey",
344
+ "multiauth.policy.session.namespace = mailto",
345
+ "multiauth.policy.session.use = encoded.authentication.NamespacedAuthenticationPolicy",
346
+ "multiauth.policy.session.base = pyramid.authentication.SessionAuthenticationPolicy",
347
+ "multiauth.policy.remoteuser.namespace = remoteuser",
348
+ "multiauth.policy.remoteuser.use = encoded.authentication.NamespacedAuthenticationPolicy",
349
+ "multiauth.policy.remoteuser.base = pyramid.authentication.RemoteUserAuthenticationPolicy",
350
+ "multiauth.policy.accesskey.namespace = accesskey",
351
+ "multiauth.policy.accesskey.use = encoded.authentication.NamespacedAuthenticationPolicy",
352
+ "multiauth.policy.accesskey.base = encoded.authentication.BasicAuthAuthenticationPolicy",
353
+ "multiauth.policy.accesskey.check = encoded.authentication.basic_auth_check",
354
+ "multiauth.policy.auth0.use = encoded.authentication.NamespacedAuthenticationPolicy",
355
+ "multiauth.policy.auth0.namespace = auth0",
356
+ "multiauth.policy.auth0.base = encoded.authentication.Auth0AuthenticationPolicy"
357
+ ])
358
+ else:
359
+ minimal_ini_for_testing = "[app:app]\nuse = egg:encoded\nsqlalchemy.url = postgresql://dummy\n"
360
+ with temporary_file(content=minimal_ini_for_testing, suffix=".ini") as ini_file:
361
+ return Portal(Portal._create_vapp(ini_file))
360
362
 
361
363
  @staticmethod
362
- def _create_testapp(arg: Union[TestApp, VirtualApp, Router, str] = None, app_name: Optional[str] = None) -> TestApp:
364
+ def _create_vapp(arg: Union[TestApp, VirtualApp, PyramidRouter, str] = None) -> TestApp:
363
365
  if isinstance(arg, TestApp):
364
366
  return arg
365
367
  elif isinstance(arg, VirtualApp):
366
368
  if not isinstance(arg.wrapped_app, TestApp):
367
- raise Exception("Portal._create_testapp VirtualApp argument error.")
369
+ raise Exception("Portal._create_vapp VirtualApp argument error.")
368
370
  return arg.wrapped_app
369
- if isinstance(arg, Router):
371
+ if isinstance(arg, PyramidRouter):
370
372
  router = arg
371
- elif isinstance(arg, str) or arg is None:
372
- router = get_app(arg or "development.ini", app_name or "app")
373
+ elif isinstance(arg, str) or not arg:
374
+ router = pyramid_get_app(arg or "development.ini", "app")
373
375
  else:
374
- raise Exception("Portal._create_testapp argument error.")
376
+ raise Exception("Portal._create_vapp argument error.")
375
377
  return TestApp(router, {"HTTP_ACCEPT": "application/json", "REMOTE_USER": "TEST"})
378
+
379
+ @staticmethod
380
+ def _create_router_for_testing(endpoints: Optional[List[Dict[str, Union[str, Callable]]]] = None):
381
+ if isinstance(endpoints, dict):
382
+ endpoints = [endpoints]
383
+ elif isinstance(endpoints, Callable):
384
+ endpoints = [{"path": "/", "method": "GET", "function": endpoints}]
385
+ if not isinstance(endpoints, list) or not endpoints:
386
+ endpoints = [{"path": "/", "method": "GET", "function": lambda request: {"status": "OK"}}]
387
+ with PyramidConfigurator() as config:
388
+ nendpoints = 0
389
+ for endpoint in endpoints:
390
+ if (endpoint_path := endpoint.get("path")) and (endpoint_function := endpoint.get("function")):
391
+ endpoint_method = endpoint.get("method", "GET")
392
+ def endpoint_wrapper(request): # noqa
393
+ response = endpoint_function(request)
394
+ return PyramidResponse(json.dumps(response), content_type="application/json; charset=utf-8")
395
+ endpoint_id = str(uuid())
396
+ config.add_route(endpoint_id, endpoint_path)
397
+ config.add_view(endpoint_wrapper, route_name=endpoint_id, request_method=endpoint_method)
398
+ nendpoints += 1
399
+ if nendpoints == 0:
400
+ return Portal._create_router_for_testing([])
401
+ return config.make_wsgi_app()
@@ -616,12 +616,9 @@ class Portal(PortalBase):
616
616
  return self.get_metadata(f"/{type_name}/{value}") is not None
617
617
 
618
618
  @staticmethod
619
- def create_for_testing(ini_file: Optional[str] = None, schemas: Optional[List[dict]] = None) -> Portal:
620
- return Portal(PortalBase.create_for_testing(ini_file), schemas=schemas)
621
-
622
- @staticmethod
623
- def create_for_testing_local(ini_file: Optional[str] = None, schemas: Optional[List[dict]] = None) -> Portal:
624
- return Portal(PortalBase.create_for_testing_local(ini_file), schemas=schemas)
619
+ def create_for_testing(arg: Optional[Union[str, bool, List[dict], dict, Callable]] = None,
620
+ schemas: Optional[List[dict]] = None) -> Portal:
621
+ return Portal(PortalBase.create_for_testing(arg), schemas=schemas)
625
622
 
626
623
 
627
624
  def _split_dotted_string(value: str):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dcicutils
3
- Version: 8.5.0.1b6
3
+ Version: 8.5.0.1b7
4
4
  Summary: Utility package for interacting with the 4DN Data Portal and other 4DN resources
5
5
  Home-page: https://github.com/4dn-dcic/utils
6
6
  License: MIT
@@ -43,7 +43,7 @@ dcicutils/log_utils.py,sha256=7pWMc6vyrorUZQf-V-M3YC6zrPgNhuV_fzm9xqTPph0,10883
43
43
  dcicutils/misc_utils.py,sha256=nRjLEORY35YmJwTjO0fnauBPznaI_bkVasIW8PccDYM,100179
44
44
  dcicutils/obfuscation_utils.py,sha256=fo2jOmDRC6xWpYX49u80bVNisqRRoPskFNX3ymFAmjw,5963
45
45
  dcicutils/opensearch_utils.py,sha256=V2exmFYW8Xl2_pGFixF4I2Cc549Opwe4PhFi5twC0M8,1017
46
- dcicutils/portal_utils.py,sha256=PLIKONo_BhVrf-r5fkXAaxP5IQZRjvPjf83YxvRx0ZE,18393
46
+ dcicutils/portal_utils.py,sha256=IZfwtbkirNiSSvpUzkQxHBedzaSgMiiVbkLLKzHEvVI,20043
47
47
  dcicutils/project_utils.py,sha256=qPdCaFmWUVBJw4rw342iUytwdQC0P-XKpK4mhyIulMM,31250
48
48
  dcicutils/qa_checkers.py,sha256=cdXjeL0jCDFDLT8VR8Px78aS10hwNISOO5G_Zv2TZ6M,20534
49
49
  dcicutils/qa_utils.py,sha256=TT0SiJWiuxYvbsIyhK9VO4uV_suxhB6CpuC4qPacCzQ,160208
@@ -56,14 +56,14 @@ dcicutils/secrets_utils.py,sha256=8dppXAsiHhJzI6NmOcvJV5ldvKkQZzh3Fl-cb8Wm7MI,19
56
56
  dcicutils/sheet_utils.py,sha256=VlmzteONW5VF_Q4vo0yA5vesz1ViUah1MZ_yA1rwZ0M,33629
57
57
  dcicutils/snapshot_utils.py,sha256=ymP7PXH6-yEiXAt75w0ldQFciGNqWBClNxC5gfX2FnY,22961
58
58
  dcicutils/ssl_certificate_utils.py,sha256=F0ifz_wnRRN9dfrfsz7aCp4UDLgHEY8LaK7PjnNvrAQ,9707
59
- dcicutils/structured_data.py,sha256=mWjH6h9ARUvYMHI4ZiwbaoKtoeM0CAOz1UxCHh4HIHE,32874
59
+ dcicutils/structured_data.py,sha256=ziugsIE_FlYRFQIl2GJqwduRa4yIBPbHBQRYPSeUzSk,32713
60
60
  dcicutils/task_utils.py,sha256=MF8ujmTD6-O2AC2gRGPHyGdUrVKgtr8epT5XU8WtNjk,8082
61
61
  dcicutils/trace_utils.py,sha256=g8kwV4ebEy5kXW6oOrEAUsurBcCROvwtZqz9fczsGRE,1769
62
62
  dcicutils/validation_utils.py,sha256=cMZIU2cY98FYtzK52z5WUYck7urH6JcqOuz9jkXpqzg,14797
63
63
  dcicutils/variant_utils.py,sha256=2H9azNx3xAj-MySg-uZ2SFqbWs4kZvf61JnK6b-h4Qw,4343
64
64
  dcicutils/zip_utils.py,sha256=0OXR0aLNwyLIZOzIFTM_5DOun7dxIv6TIZbFiithkO0,3276
65
- dcicutils-8.5.0.1b6.dist-info/LICENSE.txt,sha256=t0_-jIjqxNnymZoNJe-OltRIuuF8qfhN0ATlHyrUJPk,1102
66
- dcicutils-8.5.0.1b6.dist-info/METADATA,sha256=wWiZixknI6Pne33sGS-Lxeh6hSKSScj44tPcctlpHJw,3314
67
- dcicutils-8.5.0.1b6.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
68
- dcicutils-8.5.0.1b6.dist-info/entry_points.txt,sha256=8wbw5csMIgBXhkwfgsgJeuFcoUc0WsucUxmOyml2aoA,209
69
- dcicutils-8.5.0.1b6.dist-info/RECORD,,
65
+ dcicutils-8.5.0.1b7.dist-info/LICENSE.txt,sha256=t0_-jIjqxNnymZoNJe-OltRIuuF8qfhN0ATlHyrUJPk,1102
66
+ dcicutils-8.5.0.1b7.dist-info/METADATA,sha256=AZPMmFxRxXIpn3-mhRMhuS3mHK9YFWjpsoA3T2TXo-g,3314
67
+ dcicutils-8.5.0.1b7.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
68
+ dcicutils-8.5.0.1b7.dist-info/entry_points.txt,sha256=8wbw5csMIgBXhkwfgsgJeuFcoUc0WsucUxmOyml2aoA,209
69
+ dcicutils-8.5.0.1b7.dist-info/RECORD,,