liminal-orm 1.0.5__py3-none-any.whl → 1.0.6__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.
@@ -97,3 +97,12 @@ class BaseSchemaProperties(BaseModel):
97
97
  if not isinstance(other, BaseSchemaProperties):
98
98
  return False
99
99
  return self.model_dump() == other.model_dump()
100
+
101
+ def __str__(self) -> str:
102
+ return ", ".join(
103
+ [f"{k}={v}" for k, v in self.model_dump(exclude_unset=True).items()]
104
+ )
105
+
106
+ def __repr__(self) -> str:
107
+ """Generates a string representation of the class so that it can be executed."""
108
+ return f"{self.__class__.__name__}({', '.join([f'{k}={v.__repr__()}' for k, v in self.model_dump(exclude_defaults=True).items()])})"
liminal/cli/cli.py CHANGED
@@ -79,7 +79,10 @@ def generate_files(
79
79
  ..., help="Benchling tenant (or alias) to connect to."
80
80
  ),
81
81
  write_path: Path = typer.Option(
82
- Path("."), help="The path to write the generated files to."
82
+ Path("."),
83
+ "-p",
84
+ "--write-path",
85
+ help="The path to write the generated files to.",
83
86
  ),
84
87
  ) -> None:
85
88
  current_revision_id, benchling_connection = read_local_env_file(
@@ -9,7 +9,12 @@ from bs4 import BeautifulSoup
9
9
  from sqlalchemy import create_engine
10
10
  from sqlalchemy.engine import Engine
11
11
  from sqlalchemy.orm import Session, configure_mappers
12
- from tenacity import retry, retry_if_exception_type, stop_after_attempt
12
+ from tenacity import (
13
+ retry,
14
+ retry_if_exception_type,
15
+ stop_after_attempt,
16
+ wait_exponential,
17
+ )
13
18
 
14
19
  from liminal.connection.benchling_connection import BenchlingConnection
15
20
 
@@ -144,6 +149,7 @@ class BenchlingService(Benchling):
144
149
  @retry(
145
150
  stop=stop_after_attempt(3),
146
151
  retry=retry_if_exception_type(ValueError),
152
+ wait=wait_exponential(multiplier=1, min=1, max=8),
147
153
  reraise=True,
148
154
  )
149
155
  def autogenerate_auth(
@@ -106,14 +106,14 @@ def generate_all_entity_schema_files(
106
106
  ):
107
107
  if not col.is_multi:
108
108
  relationship_strings.append(
109
- f"""{tab}single_relationship("{wh_name_to_classname[col.entity_link]}", {col_name})"""
109
+ f"""{tab}{col_name}_entity = single_relationship("{wh_name_to_classname[col.entity_link]}", {col_name})"""
110
110
  )
111
111
  import_strings.append(
112
112
  "from liminal.orm.relationship import single_relationship"
113
113
  )
114
114
  else:
115
115
  relationship_strings.append(
116
- f"""{tab}multi_relationship("{wh_name_to_classname[col.entity_link]}", "{classname}", "{col_name}")"""
116
+ f"""{tab}{col_name}_entities = multi_relationship("{wh_name_to_classname[col.entity_link]}", "{classname}", "{col_name}")"""
117
117
  )
118
118
  import_strings.append(
119
119
  "from liminal.orm.relationship import multi_relationship"
@@ -195,10 +195,10 @@ class UpdateEntitySchema(BaseOperation):
195
195
  return update_tag_schema(benchling_service, tag_schema.id, update.model_dump())
196
196
 
197
197
  def describe_operation(self) -> str:
198
- return f"Updating properties for entity schema {self.wh_schema_name}: {repr(self.update_props)}."
198
+ return f"Updating properties for entity schema {self.wh_schema_name}: {str(self.update_props)}."
199
199
 
200
200
  def describe(self) -> str:
201
- return f"Schema properties for {self.wh_schema_name} are different in code versus Benchling: {repr(self.update_props)}."
201
+ return f"Schema properties for {self.wh_schema_name} are different in code versus Benchling: {str(self.update_props)}."
202
202
 
203
203
  def _validate(self, benchling_service: BenchlingService) -> TagSchemaModel:
204
204
  all_schemas = TagSchemaModel.get_all_json(benchling_service)
@@ -485,11 +485,7 @@ class UpdateEntitySchemaField(BaseOperation):
485
485
  # Only if changing name of field
486
486
  if self.update_props.name:
487
487
  existing_new_field = next(
488
- (
489
- f
490
- for f in tag_schema.allFields
491
- if f.systemName == self.update_props.name
492
- ),
488
+ (f for f in tag_schema.allFields if f.name == self.update_props.name),
493
489
  None,
494
490
  )
495
491
  if existing_new_field:
@@ -59,6 +59,7 @@ class CreateTagSchemaFieldModel(BaseModel):
59
59
  systemName: str
60
60
  name: str
61
61
  requiredLink: FieldRequiredLinkShortModel | None = None
62
+ tooltipText: str | None = None
62
63
 
63
64
  @classmethod
64
65
  def from_props(
@@ -121,6 +122,7 @@ class CreateTagSchemaFieldModel(BaseModel):
121
122
  folderItemType=folder_item_type,
122
123
  tagSchema=tagSchema,
123
124
  ),
125
+ tooltipText=new_props.tooltip,
124
126
  )
125
127
 
126
128
 
liminal/orm/column.py CHANGED
@@ -82,7 +82,7 @@ class Column(SqlColumn):
82
82
  super().__init__(
83
83
  self.sqlalchemy_type,
84
84
  foreign_key,
85
- nullable=not properties.required,
85
+ nullable=not required,
86
86
  info={"benchling_properties": properties},
87
87
  **kwargs,
88
88
  )
@@ -107,3 +107,7 @@ class Column(SqlColumn):
107
107
  f"Could not set benchling properties for column {column.name}. Please check that the column has a valid benchling properties set."
108
108
  )
109
109
  return Column(**properties.model_dump())
110
+
111
+ def _constructor(self, *args: Any, **kwargs: Any) -> SqlColumn:
112
+ """Returns a new instance of the SqlAlchemy Column class."""
113
+ return SqlColumn(*args, **kwargs)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: liminal-orm
3
- Version: 1.0.5
3
+ Version: 1.0.6
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
  Home-page: https://github.com/dynotx/liminal-orm
6
6
  Author: DynoTx Open Source
@@ -14,6 +14,7 @@ Requires-Dist: benchling-sdk (>=1.8.0)
14
14
  Requires-Dist: bs4 (>=0.0.2,<0.0.3)
15
15
  Requires-Dist: lxml (>=5.3.0,<6.0.0)
16
16
  Requires-Dist: pandas (>=1.5.3)
17
+ Requires-Dist: psycopg2-binary (>=2.9.10,<3.0.0)
17
18
  Requires-Dist: pydantic (>=2,<=2.7)
18
19
  Requires-Dist: requests (>=2.32.3,<3.0.0)
19
20
  Requires-Dist: rich (>=13.9.2,<14.0.0)
@@ -27,7 +28,12 @@ Description-Content-Type: text/markdown
27
28
 
28
29
  # [Liminal ORM](#liminal-orm)
29
30
 
30
- Liminal ORM<sup>1</sup> is an open-source Python package that builds on [Benchling's](https://www.benchling.com/) LIMS<sup>2</sup> platform and provides a simple, code-first approach for synchronizing and managing your Benchling schemas. Check out the [**full documentation here**](https://dynotx.github.io/liminal-orm/)!
31
+ [![PyPI version](https://img.shields.io/pypi/v/liminal-orm.svg)](https://pypi.org/project/liminal-orm/)
32
+ [![License](https://img.shields.io/github/license/dynotx/liminal-orm)](https://github.com/dynotx/liminal-orm/blob/main/LICENSE.md)
33
+ [![CI](https://github.com/dynotx/liminal-orm/actions/workflows/liminal.yml/badge.svg)](https://github.com/dynotx/liminal-orm/actions/workflows/liminal.yml)
34
+ [![Downloads](https://static.pepy.tech/personalized-badge/liminal-orm?period=total&units=international_system&left_color=grey&right_color=blue&left_text=Downloads)](https://pepy.tech/project/liminal-orm)
35
+
36
+ Liminal ORM<sup>1</sup> is an open-source Python package that builds on [Benchling's](https://www.benchling.com/) LIMS<sup>2</sup> platform and provides a simple, code-first approach for synchronizing and managing your Benchling schemas. Check out the [**full documentation here**](https://dynotx.github.io/liminal-orm/) and join our [**Slack community here**](https://join.slack.com/t/liminalorm/shared_invite/zt-2ujrp07s3-bctook4e~cAjn1LgOLVY~Q)!
31
37
 
32
38
  Liminal provides an ORM framework using [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy) along with a schema migration service inspired by [Alembic](https://alembic.sqlalchemy.org/en/latest/). This allows you to define your Benchling schemas in code and create a *single source of truth* that synchronizes between your upstream Benchling tenant(s) and downstream dependencies. By creating a standard interface and through using one-line CLI<sup>3</sup> commands, Liminal enables a code-first approach for managing Benchling tenants and accessing Benchling data. With the schemas defined in code, you can also take advantage of the additional capabilities that the Liminal toolkit provides. This includes:
33
39
 
@@ -38,10 +44,10 @@ Liminal provides an ORM framework using [SQLAlchemy](https://github.com/sqlalche
38
44
  - CI/CD integration with GitHub Actions to ensure that your Benchling schemas and code are always in sync.
39
45
  - And more based on community contributions/feedback :)
40
46
 
41
- Benchling is an industry standard cloud platform for life sciences R&D. Liminal builds on top of Benchling's platform and assumes that you already have a Benchling tenant set up and have (or have access to) an admin user account. If not, learn more about getting started with Benchling [here](https://www.benchling.com/explore-benchling)!
42
-
43
47
  If you are a Benchling user, try out Liminal by following the [**Quick Start Guide**](https://dynotx.github.io/liminal-orm/getting-started/prerequisites/)! Reach out in the [Discussions](https://github.com/dynotx/liminal-orm/discussions) forum with any questions or to simply introduce yourself! If there is something blocking you from using Liminal or you're having trouble setting Liminal up, please share in [Issues](https://github.com/dynotx/liminal-orm/issues) or reach out directly (contact information below). You can expect responses within 48 hours :)
44
48
 
49
+ Benchling is an industry standard cloud platform for life sciences R&D. Liminal builds on top of Benchling's platform and assumes that you already have a Benchling tenant set up and have (or have access to) an admin user account. If not, learn more about getting started with Benchling [here](https://www.benchling.com/explore-benchling)!
50
+
45
51
  Nirmit Damania is the creator and current maintainer of Liminal (I post Liminal updates to [Discussions](https://github.com/dynotx/liminal-orm/discussions) and my [LinkedIn](https://www.linkedin.com/in/nirmit-damania/)). Most importantly, **you** have the ability to influence the future of Liminal! Any feedback, positive or negative, is highly encouraged and will be used to steer the direction of Liminal. Refer to the [Contributing guide](https://github.com/dynotx/liminal-orm/blob/main/CONTRIBUTING.md) to learn more about how you can contribute to Liminal.
46
52
 
47
53
  ⭐️ Leave a star on the repo to spread the word!
@@ -4,15 +4,15 @@ liminal/base/base_operation.py,sha256=Yi0EOyLzXOFjiX4lGZW2mV_uo0lxfyRMs9-6HFVxf1
4
4
  liminal/base/base_validation_filters.py,sha256=2Q4QhqDSloZqc313p_fc8dnSw0uMhk_iMtsEtsC_5LQ,536
5
5
  liminal/base/compare_operation.py,sha256=hkpv4ewHhxy4dlTPKgJuzBjsAqO6Km7OrrKB44pRA_o,352
6
6
  liminal/base/properties/base_field_properties.py,sha256=V9UlY_geSoOhx_Iwiw2eZ7kDKghYy4Xa5bbGcCOFDk8,4511
7
- liminal/base/properties/base_schema_properties.py,sha256=XIgTZvWYjqpSXJ-_uSew0sp-wWRb62-McmJtJ5rdNg0,3925
7
+ liminal/base/properties/base_schema_properties.py,sha256=fM4wT60yUggT32xS_F8u31bidjBWOS9Xpn0I6FlXZaQ,4335
8
8
  liminal/base/str_enum.py,sha256=jF3d-Lo8zsHUe6GsctX2L-TSj92Y3qCYDrTD-saeJoc,210
9
- liminal/cli/cli.py,sha256=Bv0H_RWLpPxaCNmD4X9l_0wYQ8VCDbAHwALvCTUxB1g,8998
9
+ liminal/cli/cli.py,sha256=JxWHLO9KMeMaOnOYwzdH0w71l0477ScFOkWNtTlc97Y,9045
10
10
  liminal/cli/controller.py,sha256=QNj3QO9TMb9hfc6U-VhLuFa0_aohOHZUmvY4XkATPhw,10118
11
11
  liminal/cli/live_test_dropdown_migration.py,sha256=i20JkY5xmLffHrB73WOl3Tjyb4V2RPb3rt7-wh-BwME,2826
12
12
  liminal/cli/live_test_entity_schema_migration.py,sha256=X03tEYYHYDXeXHWgLc9tcwksKVvr0b7nlYQndDS4MvA,5683
13
13
  liminal/connection/__init__.py,sha256=3z4pSANIOkc9mh1Xp763oYQuJZDEh4lauN901PU4vqI,166
14
14
  liminal/connection/benchling_connection.py,sha256=ZcKmaQE6T_yjAaSPF69e2PnhoZz5yZ_0TywjVykr0TM,2358
15
- liminal/connection/benchling_service.py,sha256=DQDeRjgoUHeFisY5xZXJJ-Vb0xE8lu6hXj--j5kEvnU,7413
15
+ liminal/connection/benchling_service.py,sha256=HNL-5CpL9vA3XH-MybCJMCswNhYxZV3e8h0HJMV7eoA,7511
16
16
  liminal/dropdowns/api.py,sha256=n5oxi1EhkmpmPpNi1LOI4xcIQmk1C069XFaGP5XSBx8,6959
17
17
  liminal/dropdowns/compare.py,sha256=5cz8djtaStozUun_Cp8t_5PVjq-aovme2Qq5J8FXFg4,6829
18
18
  liminal/dropdowns/generate_files.py,sha256=IqnBs-IyLsIZE0NUkdB99zd5EAF-1f9CPBeblz-GzJE,2041
@@ -21,9 +21,9 @@ liminal/dropdowns/utils.py,sha256=1-H7bTszCUeqeRBpiYXjRjreDzhn1Fd1MFwIsrEI-o4,41
21
21
  liminal/entity_schemas/api.py,sha256=Dkd44NGJ4JqRTJLJtPsZ8Qan2owbEYf446A6EuP8iL0,2786
22
22
  liminal/entity_schemas/compare.py,sha256=EL5v82FEBxMIubhlQmsmQO7a-EZeMWXRU2pBt9HmzPI,13849
23
23
  liminal/entity_schemas/entity_schema_models.py,sha256=X2ouBUWIonrWeB3hMVj0bCqchbcztrp3joaftv8yHWo,5393
24
- liminal/entity_schemas/generate_files.py,sha256=ZiTk_6wqnW_5_uW6gfCP13qUGd8uqlIh-wi7ydSk8N0,8442
25
- liminal/entity_schemas/operations.py,sha256=sub6rigZjro72EMqqsayOYNxPL6eDYwCCIlzMr9sNfQ,23306
26
- liminal/entity_schemas/tag_schema_models.py,sha256=VVqI9iC7T_p1RZJSu09AsptK7Cr-YQvXjYP5cjhpeBo,15979
24
+ liminal/entity_schemas/generate_files.py,sha256=O8SPg4lQDYsfz7NPHH3AJdRj9naDy_13hGH9cQStXt8,8484
25
+ liminal/entity_schemas/operations.py,sha256=piimMjnEFnKpucb2mLvPAXmUKjTbTEI7ZZD5B7amydE,23220
26
+ liminal/entity_schemas/tag_schema_models.py,sha256=jl6J_3iB0mxE51FbRKdwScRF24fRAumKpQDhfHGcqy8,16057
27
27
  liminal/entity_schemas/utils.py,sha256=tJVEfpJ6CtpuYI1_LWOE5-pGzNe0bsrGluLbU8sc9EM,4192
28
28
  liminal/enums/__init__.py,sha256=jz_c-B_fifatvrYoESlHZ9ljYdz-3rNl0sBazoESiHI,523
29
29
  liminal/enums/benchling_api_field_type.py,sha256=DEMlkvKuc8kaslnKdWsdB8Z70OY3CGwOHfZNC3a1SOE,528
@@ -41,7 +41,7 @@ liminal/migrate/revisions_timeline.py,sha256=06qf_7E1Hecucfczpm85rV3ATLDjpCf7y6T
41
41
  liminal/migrate/utils.py,sha256=HdSr3N2WN_1S-PLRGVWSMYl-4gIcP-Ph2wPycGi2cGg,3404
42
42
  liminal/orm/base.py,sha256=fFSpiNRYgK5UG7lbXdQGV8KgO8pwjMqt0pycM3rWJ2o,615
43
43
  liminal/orm/base_model.py,sha256=dLbhAFaD2hPEbEmAQTkMBRumkthGWdSd0YjHcbzE4hU,10925
44
- liminal/orm/column.py,sha256=dSYtqjpPgF2ECvw7CJ1AiMLGF8TdYT7IdaebIA0U_t8,4509
44
+ liminal/orm/column.py,sha256=YjFORf_DF4COE80kX40OFuDMSeZ6M2je6b_iUaUOtKs,4678
45
45
  liminal/orm/mixins.py,sha256=wm5DvI_6th94bfsgsbwzFSvTRbjEZfdT6R85J_NjvOc,4356
46
46
  liminal/orm/relationship.py,sha256=Zl4bMHbtDSPx1psGHYnojGGJpA8B8hwcPJdgjB1lmW0,2490
47
47
  liminal/orm/schema_properties.py,sha256=vZBOwS2SFtI6CjlYsaGno08TpXi47PC832mPmCJM3KI,2049
@@ -54,8 +54,8 @@ liminal/tests/test_dropdown_compare.py,sha256=yHB0ovQlBLRu8-qYkqIPd8VtYEOmOft_93
54
54
  liminal/tests/test_entity_schema_compare.py,sha256=FBYxqIB9oeFArWYKnbGV8I7NPzv2syziaPjEQg-wZ1o,15529
55
55
  liminal/utils.py,sha256=UT07vILwm9fu8DLgwIxMm1DxEPFIIsW-0mgBU8oNrNY,2566
56
56
  liminal/validation/__init__.py,sha256=SBd48xxBMJrBzI48G2RcK056EMlevt5YjmZMkfCWN1I,6924
57
- liminal_orm-1.0.5.dist-info/LICENSE.md,sha256=oVA877F_D1AV44dpjsv4f-4k690uNGApX1EtzOo3T8U,11353
58
- liminal_orm-1.0.5.dist-info/METADATA,sha256=BX2olNamEBmkc06pSsycFlFNiCAO74y2ouzl-Zm6ez8,10393
59
- liminal_orm-1.0.5.dist-info/WHEEL,sha256=kLuE8m1WYU0Ig0_YEGrXyTtiJvKPpLpDEiChiNyei5Y,88
60
- liminal_orm-1.0.5.dist-info/entry_points.txt,sha256=atIrU63rrzH81dWC2sjUbFLlc5FWMmYRdMxXEWexIZA,47
61
- liminal_orm-1.0.5.dist-info/RECORD,,
57
+ liminal_orm-1.0.6.dist-info/LICENSE.md,sha256=oVA877F_D1AV44dpjsv4f-4k690uNGApX1EtzOo3T8U,11353
58
+ liminal_orm-1.0.6.dist-info/METADATA,sha256=Jwa6P74ZUhDNYtMvI3y4jvcn_wvJluQ1kHAZXOtuoqo,11165
59
+ liminal_orm-1.0.6.dist-info/WHEEL,sha256=kLuE8m1WYU0Ig0_YEGrXyTtiJvKPpLpDEiChiNyei5Y,88
60
+ liminal_orm-1.0.6.dist-info/entry_points.txt,sha256=atIrU63rrzH81dWC2sjUbFLlc5FWMmYRdMxXEWexIZA,47
61
+ liminal_orm-1.0.6.dist-info/RECORD,,