dcicutils 8.5.0.1b6__py3-none-any.whl → 8.5.0.1b7__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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,,