liminal-orm 4.4.8__tar.gz → 4.5.0__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.
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/PKG-INFO +3 -2
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/cli/utils.py +1 -5
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/connection/benchling_connection.py +8 -2
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/connection/benchling_service.py +123 -26
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/pyproject.toml +3 -2
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/.gitignore +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/LICENSE.md +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/README.md +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/.gitignore +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/.lock +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/CACHEDIR.TAG +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/__init__.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/base/base_dropdown.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/base/base_operation.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/base/base_validation_filters.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/base/compare_operation.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/base/name_template_parts.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/base/properties/base_field_properties.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/base/properties/base_name_template.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/base/properties/base_schema_properties.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/base/str_enum.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/cli/cli.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/cli/controller.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/cli/live_test_dropdown_migration.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/cli/live_test_entity_schema_migration.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/connection/__init__.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/dropdowns/api.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/dropdowns/compare.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/dropdowns/generate_files.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/dropdowns/operations.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/dropdowns/utils.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/entity_schemas/api.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/entity_schemas/compare.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/entity_schemas/entity_schema_models.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/entity_schemas/generate_files.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/entity_schemas/operations.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/entity_schemas/tag_schema_models.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/entity_schemas/utils.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/enums/__init__.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/enums/benchling_api_field_type.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/enums/benchling_entity_type.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/enums/benchling_field_type.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/enums/benchling_folder_item_type.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/enums/benchling_naming_strategy.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/enums/benchling_sequence_type.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/enums/name_template_part_type.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/enums/sequence_constraint.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/external/__init__.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/mappers.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/migrate/components.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/migrate/revision.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/migrate/revisions_timeline.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/orm/base.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/orm/base_model.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/orm/base_results_model.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/orm/base_tables/registry_entity.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/orm/base_tables/schema.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/orm/base_tables/user.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/orm/column.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/orm/mixins.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/orm/name_template.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/orm/name_template_parts.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/orm/relationship.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/orm/results_schema_properties.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/orm/schema_properties.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/py.typed +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/pyvenv.cfg +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/results_schemas/generate_files.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/results_schemas/models/results_schema_model.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/results_schemas/utils.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/share/jupyter/kernels/python3/kernel.json +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/share/jupyter/kernels/python3/logo-32x32.png +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/share/jupyter/kernels/python3/logo-64x64.png +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/share/jupyter/kernels/python3/logo-svg.svg +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/share/man/man1/ipython.1 +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/tests/__init__.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/tests/conftest.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/tests/from benchling_sdk.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/tests/test_dropdown_compare.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/tests/test_entity_schema_compare.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/unit_dictionary/utils.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/utils.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/validation/__init__.py +0 -0
- {liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/validation/validation_severity.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: liminal-orm
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.5.0
|
|
4
4
|
Summary: An ORM and toolkit that builds on top of Benchling's platform to keep your schemas and downstream code dependencies in sync.
|
|
5
5
|
Project-URL: Homepage, https://github.com/dynotx/liminal-orm
|
|
6
6
|
Project-URL: Repository, https://github.com/dynotx/liminal-orm
|
|
@@ -15,8 +15,9 @@ Requires-Dist: click<8.2.0,>=8.0.0
|
|
|
15
15
|
Requires-Dist: lxml<6,>=5.3.0
|
|
16
16
|
Requires-Dist: numpy<2,>=1.23.5
|
|
17
17
|
Requires-Dist: pandas<3,>=1.5.3
|
|
18
|
+
Requires-Dist: playwright>=1.58.0
|
|
18
19
|
Requires-Dist: psycopg2-binary<3,>=2.9.10
|
|
19
|
-
Requires-Dist: pydantic<=2.
|
|
20
|
+
Requires-Dist: pydantic<=2.13,>=2
|
|
20
21
|
Requires-Dist: requests<3,>=2.32.3
|
|
21
22
|
Requires-Dist: rich<14,>=13.9.2
|
|
22
23
|
Requires-Dist: sqlalchemy<2
|
|
@@ -12,7 +12,7 @@ def _check_liminal_directory_initialized(liminal_dir_path: Path) -> None:
|
|
|
12
12
|
"""Raises an exception if the liminal directory does not exist at the given path."""
|
|
13
13
|
if not liminal_dir_path.exists() or not liminal_dir_path.is_dir():
|
|
14
14
|
raise Exception(
|
|
15
|
-
"
|
|
15
|
+
"/liminal directory not found at current working directory where `liminal` command was run. Run `liminal init` or ensure that your current working directory is where the /liminal environment is located."
|
|
16
16
|
)
|
|
17
17
|
else:
|
|
18
18
|
if not (liminal_dir_path / "env.py").exists():
|
|
@@ -67,10 +67,6 @@ def _read_local_env_file(
|
|
|
67
67
|
raise Exception(
|
|
68
68
|
"api_client_id and api_client_secret must be provided in BenchlingConnection in liminal/env.py. This is necessary for the migration service."
|
|
69
69
|
)
|
|
70
|
-
if not bc.internal_api_admin_email or not bc.internal_api_admin_password:
|
|
71
|
-
raise Exception(
|
|
72
|
-
"internal_api_admin_email and internal_api_admin_password must be provided in BenchlingConnection in liminal/env.py. This is necessary for the migration service."
|
|
73
|
-
)
|
|
74
70
|
return bc
|
|
75
71
|
raise Exception(
|
|
76
72
|
f"BenchlingConnection with tenant name or alias {benchling_tenant} not found in liminal/env.py. Please update the env.py file with a correctly defined BenchlingConnection."
|
|
@@ -37,9 +37,14 @@ class BenchlingConnection(BaseModel):
|
|
|
37
37
|
warehouse_connection_string: str | None = None
|
|
38
38
|
The connection string for the warehouse.
|
|
39
39
|
internal_api_admin_email: str | None = None
|
|
40
|
-
The email of the internal API admin.
|
|
40
|
+
The email of the internal API admin. If SSO is not enabled or optional on your Benchling tenant, this email is used to log in to Benchling, and give Liminal the authenticated internal API session cookie.
|
|
41
41
|
internal_api_admin_password: str | None = None
|
|
42
|
-
The password of the internal API admin.
|
|
42
|
+
The password of the internal API admin. If SSO is not enabled or optional on your Benchling tenant, this password is used to log in to Benchling, and give Liminal the authenticated internal API session cookie.
|
|
43
|
+
playwright_data_dir: str | None = "~/.liminal/chrome_data/"
|
|
44
|
+
The directory to store the playwright browser user data. If SSO is enabled and required on your Benchling tenant,
|
|
45
|
+
Liminal uses playwright so the user can log into Benchling in order to give Liminal the authenticated internal API session cookie.
|
|
46
|
+
This directory is used to store playwright's persistent context, allowing the user to set up a persistent chrome profile.
|
|
47
|
+
Set this to None in order to disable playwright's persistent context which enables automatic login.
|
|
43
48
|
fieldsets: bool = False
|
|
44
49
|
Whether your Benchling tenant has access to fieldsets.
|
|
45
50
|
config_flags: TenantConfigFlags = TenantConfigFlags()
|
|
@@ -54,6 +59,7 @@ class BenchlingConnection(BaseModel):
|
|
|
54
59
|
warehouse_connection_string: str | None = None
|
|
55
60
|
internal_api_admin_email: str | None = None
|
|
56
61
|
internal_api_admin_password: str | None = None
|
|
62
|
+
playwright_data_dir: str | None = "~/.liminal/playwright_chrome_data/"
|
|
57
63
|
fieldsets: bool = False
|
|
58
64
|
config_flags: TenantConfigFlags = TenantConfigFlags()
|
|
59
65
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import logging
|
|
3
|
+
import os
|
|
2
4
|
from typing import Any
|
|
3
5
|
|
|
6
|
+
from playwright.async_api import async_playwright
|
|
4
7
|
import requests
|
|
5
8
|
from benchling_sdk.auth.client_credentials_oauth2 import ClientCredentialsOAuth2
|
|
6
9
|
from benchling_sdk.benchling import Benchling
|
|
@@ -32,6 +35,10 @@ REMOTE_LIMINAL_SCHEMA_NAME = "liminal_remote"
|
|
|
32
35
|
REMOTE_REVISION_ID_FIELD_WH_NAME = "revision_id"
|
|
33
36
|
|
|
34
37
|
|
|
38
|
+
class SSODisabledError(ValueError):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
35
42
|
class BenchlingService(Benchling):
|
|
36
43
|
"""
|
|
37
44
|
Class that creates a connection object that can be used to connect to Benchling's API, database, or internal API.
|
|
@@ -88,30 +95,33 @@ class BenchlingService(Benchling):
|
|
|
88
95
|
)
|
|
89
96
|
self.use_internal_api = use_internal_api
|
|
90
97
|
if use_internal_api:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
try:
|
|
99
|
+
authenticated_session = asyncio.get_event_loop().run_until_complete(
|
|
100
|
+
self.autogenerate_auth(
|
|
101
|
+
connection.tenant_name,
|
|
102
|
+
connection.internal_api_admin_email,
|
|
103
|
+
connection.internal_api_admin_password,
|
|
104
|
+
connection.playwright_data_dir,
|
|
105
|
+
)
|
|
99
106
|
)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
self.custom_post_headers = {
|
|
104
|
-
"X-Csrftoken": csrf_token,
|
|
105
|
-
"Referer": f"https://{connection.tenant_name}.benchling.com/",
|
|
106
|
-
"Content-Type": "application/json",
|
|
107
|
-
}
|
|
108
|
-
LOGGER.info(
|
|
109
|
-
f"Tenant {connection.tenant_name}: Connected to Benchling internal API."
|
|
107
|
+
except SSODisabledError as e:
|
|
108
|
+
raise SSODisabledError(
|
|
109
|
+
f"{e} Please provide `internal_api_admin_email` and `internal_api_admin_password` in your BenchlingConnection."
|
|
110
110
|
)
|
|
111
|
-
|
|
112
|
-
raise
|
|
113
|
-
"
|
|
111
|
+
except RuntimeError as e:
|
|
112
|
+
raise RuntimeError(
|
|
113
|
+
f"{e}. If you are running this in a Jupyter notebook, use `nest_asyncio.apply()` to allow the async playwright login to run."
|
|
114
114
|
)
|
|
115
|
+
self.custom_post_cookies = {
|
|
116
|
+
"session": authenticated_session,
|
|
117
|
+
}
|
|
118
|
+
self.custom_post_headers = {
|
|
119
|
+
"Referer": f"https://{connection.tenant_name}.benchling.com/",
|
|
120
|
+
"Content-Type": "application/json",
|
|
121
|
+
}
|
|
122
|
+
LOGGER.info(
|
|
123
|
+
f"Tenant {connection.tenant_name}: Connected to Benchling internal API."
|
|
124
|
+
)
|
|
115
125
|
|
|
116
126
|
@property
|
|
117
127
|
def session(self) -> Session:
|
|
@@ -249,6 +259,89 @@ class BenchlingService(Benchling):
|
|
|
249
259
|
f"Error finding field on {REMOTE_LIMINAL_SCHEMA_NAME} schema with warehouse_name {REMOTE_REVISION_ID_FIELD_WH_NAME}. Check schema fields to ensure this field exists and is defined according to documentation."
|
|
250
260
|
)
|
|
251
261
|
|
|
262
|
+
@classmethod
|
|
263
|
+
async def autogenerate_auth(
|
|
264
|
+
cls,
|
|
265
|
+
benchling_tenant: str,
|
|
266
|
+
email: str | None = None,
|
|
267
|
+
password: str | None = None,
|
|
268
|
+
playwright_data_dir: str | None = None,
|
|
269
|
+
) -> str:
|
|
270
|
+
"""Logs in to Benchling using the admin email and password or playwright and returns the session cookie.
|
|
271
|
+
If email and password are not passed in or if SSO is set to required on the Benchling tenant, playwright is used to log in.
|
|
272
|
+
Otherwise, the admin email and password are used to log in."""
|
|
273
|
+
with requests.Session() as session:
|
|
274
|
+
if email and password:
|
|
275
|
+
signin_page = session.get(
|
|
276
|
+
f"https://{benchling_tenant}.benchling.com/signin",
|
|
277
|
+
allow_redirects=False,
|
|
278
|
+
)
|
|
279
|
+
if signin_page.status_code == 200:
|
|
280
|
+
return cls.get_authenticated_session_benchling_admin_login(
|
|
281
|
+
benchling_tenant, email, password
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
else:
|
|
285
|
+
signin_page = session.get(
|
|
286
|
+
f"https://{benchling_tenant}.benchling.com/ext/saml/signin:begin",
|
|
287
|
+
allow_redirects=False,
|
|
288
|
+
)
|
|
289
|
+
if signin_page.status_code == 403 or signin_page.status_code == 400:
|
|
290
|
+
raise SSODisabledError(
|
|
291
|
+
f"admin_email and admin_password not provided when sso is turned off for Benchling tenant {benchling_tenant}."
|
|
292
|
+
)
|
|
293
|
+
if signin_page.status_code == 302:
|
|
294
|
+
return await cls.get_authenticated_session_sso_login_playwright(
|
|
295
|
+
benchling_tenant, playwright_data_dir
|
|
296
|
+
)
|
|
297
|
+
else:
|
|
298
|
+
raise ValueError(
|
|
299
|
+
f"Unexpected response: Status code {signin_page.status_code}: {signin_page.text}"
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
@classmethod
|
|
303
|
+
async def get_authenticated_session_sso_login_playwright(
|
|
304
|
+
cls, benchling_tenant: str, playwright_data_dir: str | None = None
|
|
305
|
+
) -> str:
|
|
306
|
+
"""Logs in to Benchling using playwright and returns the session cookie.
|
|
307
|
+
This can be used when SSO is enabled and required on the Benchling tenant."""
|
|
308
|
+
LOGGER.info(f"Log into your {benchling_tenant} Benchling tenant...")
|
|
309
|
+
async with async_playwright() as playwright:
|
|
310
|
+
if playwright_data_dir:
|
|
311
|
+
context = await playwright.chromium.launch_persistent_context(
|
|
312
|
+
channel="chrome",
|
|
313
|
+
headless=False,
|
|
314
|
+
user_data_dir=os.path.expanduser(playwright_data_dir),
|
|
315
|
+
)
|
|
316
|
+
else:
|
|
317
|
+
browser = await playwright.chromium.launch(
|
|
318
|
+
channel="chrome", headless=False
|
|
319
|
+
)
|
|
320
|
+
context = await browser.new_context()
|
|
321
|
+
page = await context.new_page()
|
|
322
|
+
try:
|
|
323
|
+
await page.goto(f"https://{benchling_tenant}.benchling.com")
|
|
324
|
+
except Exception:
|
|
325
|
+
raise ValueError(
|
|
326
|
+
f"Error navigating to https://{benchling_tenant}.benchling.com"
|
|
327
|
+
)
|
|
328
|
+
try:
|
|
329
|
+
await page.wait_for_url(
|
|
330
|
+
f"**/{benchling_tenant}.benchling.com/**", timeout=600_000
|
|
331
|
+
)
|
|
332
|
+
except Exception:
|
|
333
|
+
raise TimeoutError(
|
|
334
|
+
f"Log in cancelled or timed out (2 min timeout). Did not detect SSO log in for https://{benchling_tenant}.benchling.com."
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
cookies = await context.cookies()
|
|
338
|
+
session_cookie = next(
|
|
339
|
+
(c["value"] for c in cookies if c["name"] == "session"), None
|
|
340
|
+
)
|
|
341
|
+
if not session_cookie:
|
|
342
|
+
raise ValueError("No session cookie found.")
|
|
343
|
+
return session_cookie
|
|
344
|
+
|
|
252
345
|
@classmethod
|
|
253
346
|
@retry(
|
|
254
347
|
stop=stop_after_attempt(3),
|
|
@@ -256,9 +349,11 @@ class BenchlingService(Benchling):
|
|
|
256
349
|
wait=wait_exponential(multiplier=1, min=1, max=8),
|
|
257
350
|
reraise=True,
|
|
258
351
|
)
|
|
259
|
-
def
|
|
352
|
+
def get_authenticated_session_benchling_admin_login(
|
|
260
353
|
cls, benchling_tenant: str, email: str, password: str
|
|
261
|
-
) ->
|
|
354
|
+
) -> str:
|
|
355
|
+
"""Logs in to Benchling using the admin email and password and returns the session cookie.
|
|
356
|
+
This can be used when SSO is disabled or optional on the Benchling tenant."""
|
|
262
357
|
with requests.Session() as session:
|
|
263
358
|
homepage = session.get(f"https://{benchling_tenant}.benchling.com/signin")
|
|
264
359
|
soup = BeautifulSoup(homepage.content, features="lxml")
|
|
@@ -286,6 +381,8 @@ class BenchlingService(Benchling):
|
|
|
286
381
|
raise ValueError(
|
|
287
382
|
f"Failed to sign in to Benchling: {signin_response.text}"
|
|
288
383
|
)
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
384
|
+
return (
|
|
385
|
+
signin_response.headers["Set-Cookie"]
|
|
386
|
+
.split("; Secure")[0]
|
|
387
|
+
.removeprefix("session=")
|
|
388
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "liminal-orm"
|
|
3
|
-
version = "4.
|
|
3
|
+
version = "4.5.0"
|
|
4
4
|
description = "An ORM and toolkit that builds on top of Benchling's platform to keep your schemas and downstream code dependencies in sync."
|
|
5
5
|
authors = [{ name = "DynoTx Open Source", email = "opensource@dynotx.com" }]
|
|
6
6
|
requires-python = ">=3.9,<4"
|
|
@@ -10,7 +10,7 @@ dependencies = [
|
|
|
10
10
|
"bs4>=0.0.2,<0.0.3",
|
|
11
11
|
"numpy>=1.23.5,<2",
|
|
12
12
|
"pandas>=1.5.3,<3",
|
|
13
|
-
"pydantic>=2,<=2.
|
|
13
|
+
"pydantic>=2,<=2.13",
|
|
14
14
|
"requests>=2.32.3,<3",
|
|
15
15
|
"rich>=13.9.2,<14",
|
|
16
16
|
"sqlalchemy<2",
|
|
@@ -20,6 +20,7 @@ dependencies = [
|
|
|
20
20
|
"psycopg2-binary>=2.9.10,<3",
|
|
21
21
|
"tornado==6.5.0",
|
|
22
22
|
"click>=8.0.0,<8.2.0",
|
|
23
|
+
"playwright>=1.58.0",
|
|
23
24
|
]
|
|
24
25
|
|
|
25
26
|
[virtualenvs]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/results_schemas/models/results_schema_model.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/share/jupyter/kernels/python3/logo-32x32.png
RENAMED
|
File without changes
|
{liminal_orm-4.4.8 → liminal_orm-4.5.0}/liminal/share/jupyter/kernels/python3/logo-64x64.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|