liminal-orm 1.1.3__py3-none-any.whl → 2.0.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.
Files changed (33) hide show
  1. liminal/base/base_operation.py +4 -3
  2. liminal/base/base_validation_filters.py +15 -0
  3. liminal/base/name_template_parts.py +96 -0
  4. liminal/base/properties/base_field_properties.py +2 -2
  5. liminal/base/properties/base_name_template.py +83 -0
  6. liminal/base/properties/base_schema_properties.py +13 -1
  7. liminal/dropdowns/compare.py +8 -0
  8. liminal/dropdowns/operations.py +1 -1
  9. liminal/entity_schemas/api.py +18 -0
  10. liminal/entity_schemas/compare.py +62 -8
  11. liminal/entity_schemas/entity_schema_models.py +43 -0
  12. liminal/entity_schemas/generate_files.py +13 -11
  13. liminal/entity_schemas/operations.py +43 -18
  14. liminal/entity_schemas/tag_schema_models.py +146 -3
  15. liminal/entity_schemas/utils.py +15 -2
  16. liminal/enums/__init__.py +0 -1
  17. liminal/enums/benchling_entity_type.py +8 -0
  18. liminal/enums/name_template_part_type.py +12 -0
  19. liminal/external/__init__.py +11 -1
  20. liminal/migrate/revisions_timeline.py +2 -1
  21. liminal/orm/base_model.py +90 -29
  22. liminal/orm/name_template.py +39 -0
  23. liminal/orm/schema_properties.py +27 -1
  24. liminal/tests/conftest.py +18 -9
  25. liminal/tests/test_entity_schema_compare.py +61 -12
  26. liminal/utils.py +9 -0
  27. liminal/validation/__init__.py +84 -108
  28. liminal/{enums/benchling_report_level.py → validation/validation_severity.py} +2 -2
  29. {liminal_orm-1.1.3.dist-info → liminal_orm-2.0.0.dist-info}/METADATA +17 -20
  30. {liminal_orm-1.1.3.dist-info → liminal_orm-2.0.0.dist-info}/RECORD +33 -29
  31. {liminal_orm-1.1.3.dist-info → liminal_orm-2.0.0.dist-info}/LICENSE.md +0 -0
  32. {liminal_orm-1.1.3.dist-info → liminal_orm-2.0.0.dist-info}/WHEEL +0 -0
  33. {liminal_orm-1.1.3.dist-info → liminal_orm-2.0.0.dist-info}/entry_points.txt +0 -0
@@ -1,13 +1,15 @@
1
- from abc import ABC, abstractmethod
1
+ import inspect
2
2
  from datetime import datetime
3
- from typing import TYPE_CHECKING, Any
3
+ from functools import wraps
4
+ from typing import TYPE_CHECKING, Callable
4
5
 
5
6
  from pydantic import BaseModel, ConfigDict
6
7
 
7
- from liminal.enums import BenchlingReportLevel
8
+ from liminal.utils import pascalize
9
+ from liminal.validation.validation_severity import ValidationSeverity
8
10
 
9
11
  if TYPE_CHECKING:
10
- from liminal.orm.base_model import BaseModel as BaseModelBenchling
12
+ from liminal.orm.base_model import BaseModel as BenchlingBaseModel
11
13
 
12
14
 
13
15
  class BenchlingValidatorReport(BaseModel):
@@ -20,7 +22,7 @@ class BenchlingValidatorReport(BaseModel):
20
22
  Indicates whether the validation passed or failed.
21
23
  model : str
22
24
  The name of the model being validated. (eg: NGSSample)
23
- level : BenchlingReportLevel
25
+ level : ValidationSeverity
24
26
  The severity level of the validation report.
25
27
  validator_name : str | None
26
28
  The name of the validator that generated this report. (eg: BioContextValidator)
@@ -44,7 +46,7 @@ class BenchlingValidatorReport(BaseModel):
44
46
 
45
47
  valid: bool
46
48
  model: str
47
- level: BenchlingReportLevel
49
+ level: ValidationSeverity
48
50
  validator_name: str | None = None
49
51
  entity_id: str | None = None
50
52
  registry_id: str | None = None
@@ -57,89 +59,35 @@ class BenchlingValidatorReport(BaseModel):
57
59
 
58
60
  model_config = ConfigDict(extra="allow")
59
61
 
60
-
61
- class BenchlingValidator(ABC):
62
- """Base class for benchling validators."""
63
-
64
- def __str__(self) -> str:
65
- return self.__class__.__name__ + "()"
66
-
67
- def _prefix(self) -> str:
68
- """Creates a prefix for the formatted error message which includes the class name and any instance variables.
69
- Ex: "BenchlingValidator(field_name=sample_code, field_value=123):"
70
- """
71
- prefix = f"{self.__class__.__name__}"
72
- if vars(self):
73
- prefix += "("
74
- for key, val in vars(self).items():
75
- prefix += f"{key}={self.truncate_msg(val, max_len=50)}, "
76
- prefix = prefix[:-2] + "):"
77
- else:
78
- prefix += ":"
79
- return prefix
80
-
81
- @abstractmethod
82
- def validate(self, entity: type["BaseModelBenchling"]) -> BenchlingValidatorReport:
83
- """Abstract method that all validator subclass must implement. Each subclass will have a differently defined validation
84
- function that runs on the given benchling entity.
85
-
86
- Parameters
87
- ----------
88
- entity : type["BaseModelBenchling"]
89
- The Benchling entity to validate.
90
-
91
- Returns
92
- -------
93
- BenchlingValidatorReport
94
- A report indicating whether the validation passed or failed, and any additional metadata.
95
- """
96
- raise NotImplementedError
97
-
98
- def __getattribute__(self, name: str) -> Any:
99
- attr = super().__getattribute__(name)
100
- if name == "validate":
101
- # Wrap the validate method in a try-except block to catch any unexpected errors that occur during validation.
102
- # If an unexpected error occurs, return a BenchlingValidatorReport with the unexpected error message.
103
- def try_except_wrapped_func(
104
- *args: Any, **kwargs: dict
105
- ) -> BenchlingValidatorReport:
106
- try:
107
- return attr(*args, **kwargs)
108
- except Exception as e:
109
- entity: type[BaseModelBenchling] = args[0]
110
- return BenchlingValidatorReport(
111
- valid=False,
112
- model=entity.__class__.__name__,
113
- validator_name=self.__class__.__name__,
114
- level=BenchlingReportLevel.UNEXPECTED,
115
- entity_id=entity.id,
116
- registry_id=entity.file_registry_id,
117
- entity_name=entity.name,
118
- web_url=entity.url if entity.url else None,
119
- creator_name=entity.creator.name if entity.creator else None,
120
- creator_email=entity.creator.email if entity.creator else None,
121
- updated_date=entity.modified_at,
122
- message=f"Unexpected exception: {e}",
123
- )
124
-
125
- return try_except_wrapped_func
126
- return attr
127
-
128
62
  @classmethod
129
- def create_report(
63
+ def create_validation_report(
130
64
  cls,
131
65
  valid: bool,
132
- level: BenchlingReportLevel,
133
- entity: type["BaseModelBenchling"],
66
+ level: ValidationSeverity,
67
+ entity: type["BenchlingBaseModel"],
68
+ validator_name: str,
134
69
  message: str | None = None,
135
- **kwargs: Any,
136
- ) -> BenchlingValidatorReport:
137
- """Creates a BenchlingValidatorReport with the given parameters."""
138
- return BenchlingValidatorReport(
70
+ ) -> "BenchlingValidatorReport":
71
+ """Creates a BenchlingValidatorReport with the given parameters.
72
+
73
+ Parameters
74
+ ----------
75
+ valid: bool
76
+ Indicates whether the validation passed or failed.
77
+ level: ValidationSeverity
78
+ The severity level of the validation report.
79
+ entity: type[BenchlingBaseModel]
80
+ The entity being validated.
81
+ validator_name: str
82
+ The name of the validator that generated this report.
83
+ message: str | None
84
+ A message describing the result of the validation.
85
+ """
86
+ return cls(
139
87
  valid=valid,
140
88
  level=level,
141
89
  model=entity.__class__.__name__,
142
- validator_name=cls.__name__,
90
+ validator_name=validator_name,
143
91
  entity_id=entity.id,
144
92
  registry_id=entity.file_registry_id,
145
93
  entity_name=entity.name,
@@ -148,31 +96,59 @@ class BenchlingValidator(ABC):
148
96
  creator_email=entity.creator.email if entity.creator else None,
149
97
  updated_date=entity.modified_at,
150
98
  message=message,
151
- **kwargs,
152
99
  )
153
100
 
154
- def format_err(self, *msgs: str | None) -> str:
155
- """Creates a formatted error message from the given messages. The first message is prefixed with the class name and any instance variables.
156
- Ex: "BenchlingValidator(field_name=sample_code, field_value=123): The field value is invalid | The field value is too long"
157
- """
158
- ret_val = ""
159
- for ind, msg in enumerate(msgs):
160
- if ind == 0:
161
- if (msg is None) or (msg == ""):
162
- continue
163
- elif not msg.startswith(self._prefix()):
164
- ret_val += f"{self._prefix()} {msg}"
165
- else:
166
- ret_val += f"{msg}"
167
- elif ((msgs[0] is None) or (msgs[0] == "")) and (ind == 1):
168
- ret_val += f"{self._prefix()} {msg}"
169
- else:
170
- ret_val += f" | {msg}"
171
- return ret_val
172
-
173
- def truncate_msg(self, msg: Any, max_len: int = 150) -> str:
174
- """Shortens the given message to the given max length. If the message is longer than the max length, it is truncated and an ellipsis is added to the end."""
175
- msg = str(msg)
176
- if len(msg) > max_len:
177
- return f"{msg[:max_len]}..."
178
- return msg
101
+
102
+ def liminal_validator(
103
+ validator_level: ValidationSeverity = ValidationSeverity.LOW,
104
+ validator_name: str | None = None,
105
+ ) -> Callable:
106
+ """A decorator that validates a function that takes a Benchling entity as an argument and returns None.
107
+
108
+ Parameters:
109
+ validator_level: ValidationSeverity
110
+ The level of the validator.
111
+ validator_name: str | None
112
+ The name of the validator. Defaults to the pascalized name of the function.
113
+ """
114
+
115
+ def decorator(func: Callable[[type["BenchlingBaseModel"]], None]) -> Callable:
116
+ """Decorator that validates a function that takes a Benchling entity as an argument and returns None."""
117
+ sig = inspect.signature(func)
118
+ params = list(sig.parameters.values())
119
+ if not params or params[0].name != "self" or len(params) > 1:
120
+ raise TypeError(
121
+ "Validator must defined in a schema class, where the only argument to this validator must be 'self'."
122
+ )
123
+
124
+ if sig.return_annotation is not None:
125
+ raise TypeError("The return type must be None.")
126
+
127
+ nonlocal validator_name
128
+ if validator_name is None:
129
+ validator_name = pascalize(func.__name__)
130
+
131
+ @wraps(func)
132
+ def wrapper(self: type["BenchlingBaseModel"]) -> BenchlingValidatorReport:
133
+ """Wrapper that runs the validator function and returns a BenchlingValidatorReport."""
134
+ try:
135
+ func(self)
136
+ except Exception as e:
137
+ return BenchlingValidatorReport.create_validation_report(
138
+ valid=False,
139
+ level=validator_level,
140
+ entity=self,
141
+ validator_name=validator_name,
142
+ message=str(e),
143
+ )
144
+ return BenchlingValidatorReport.create_validation_report(
145
+ valid=True,
146
+ level=validator_level,
147
+ entity=self,
148
+ validator_name=validator_name,
149
+ )
150
+
151
+ setattr(wrapper, "_is_liminal_validator", True)
152
+ return wrapper
153
+
154
+ return decorator
@@ -1,8 +1,8 @@
1
1
  from liminal.base.str_enum import StrEnum
2
2
 
3
3
 
4
- class BenchlingReportLevel(StrEnum):
5
- """This enum represents the different levels of validation that can be returned by Benchling."""
4
+ class ValidationSeverity(StrEnum):
5
+ """This enum represents the different levels of validation that can be returned by Liminal."""
6
6
 
7
7
  LOW = "LOW"
8
8
  MED = "MED"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: liminal-orm
3
- Version: 1.1.3
3
+ Version: 2.0.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
  Home-page: https://github.com/dynotx/liminal-orm
6
6
  Author: DynoTx Open Source
@@ -41,13 +41,12 @@ Liminal ORM<sup>1</sup> is an open-source Python package that builds on [Benchli
41
41
  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:
42
42
 
43
43
  - The ability to run migrations to your Benchling tenant(s) through an easy to use CLI.
44
- - One source of truth defined in code for your Benchling schema model that your many Benchling tenants can stay in sync with.
45
44
  - Easy to implement validation rules to reflect business logic for all of your Benchling entities.
46
45
  - Strongly typed queries for all your Benchling entities.
47
46
  - CI/CD integration with GitHub Actions to ensure that your Benchling schemas and code are always in sync.
48
47
  - And more based on community contributions/feedback :)
49
48
 
50
- 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 :)
49
+ 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 [Slack community](https://join.slack.com/t/liminalorm/shared_invite/zt-2ujrp07s3-bctook4e~cAjn1LgOLVY~Q) (preferred method) 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 (info below).
51
50
 
52
51
  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)!
53
52
 
@@ -67,6 +66,7 @@ If you or your organization use Liminal, please consider adding yourself or your
67
66
  - [Community](#community)
68
67
  - [Contributing](#contributing)
69
68
  - [License](#license)
69
+ - [Direct Contact](#direct-contact)
70
70
  - [Acknowledgements](#acknowledgements)
71
71
  - [Footnotes](#footnotes)
72
72
 
@@ -83,22 +83,19 @@ With your schemas defined in code, you can now take advantage of the additional
83
83
  1. Entity validation: Easily create custom validation rules for your Benchling entities.
84
84
 
85
85
  ```python
86
- from liminal.validation import BenchlingValidator, BenchlingValidatorReport, BenchlingReportLevel
87
- from liminal.orm.base_model import BaseModel
88
-
89
- class CookTempValidator(BenchlingValidator):
90
- """Validates that a field value is a valid enum value for a Benchling entity"""
91
-
92
- def validate(self, entity: type[BaseModel]) -> BenchlingValidatorReport:
93
- valid = True
94
- message = None
95
- if entity.cook_time is not None and entity.cook_temp is None:
96
- valid = False
97
- message = "Cook temp is required if cook time is set"
98
- if entity.cook_time is None and entity.cook_temp is not None:
99
- valid = False
100
- message = "Cook time is required if cook temp is set"
101
- return self.create_report(valid, BenchlingReportLevel.MED, entity, message)
86
+ from liminal.validation import ValidationSeverity, liminal_validator
87
+
88
+ class Pizza(BaseModel, CustomEntityMixin):
89
+ ...
90
+
91
+ @liminal_validator(ValidationSeverity.MED)
92
+ def cook_time_and_temp_validator(self) -> None:
93
+ if self.cook_time is not None and self.cook_temp is None:
94
+ raise ValueError("Cook temp is required if cook time is set")
95
+ if self.cook_time is None and self.cook_temp is not None:
96
+ raise ValueError("Cook time is required if cook temp is set")
97
+
98
+ validation_reports = Pizza.validate(session)
102
99
  ```
103
100
 
104
101
  2. Strongly typed queries: Write type-safe queries using SQLAlchemy to access your Benchling entities.
@@ -106,7 +103,6 @@ With your schemas defined in code, you can now take advantage of the additional
106
103
  ```python
107
104
  with BenchlingSession(benchling_connection, with_db=True) as session:
108
105
  pizza = session.query(Pizza).filter(Pizza.name == "Margherita").first()
109
- print(pizza)
110
106
  ```
111
107
 
112
108
  3. CI/CD integration: Use Liminal to automatically generate and apply your revision files to your Benchling tenant(s) as part of your CI/CD pipeline.
@@ -136,6 +132,7 @@ Liminal ORM is distributed under the [Apache License, Version 2.0](./LICENSE.md)
136
132
 
137
133
  ## [Direct Contact](#direct-contact)
138
134
 
135
+ - Liminal Community Slack group: [Join here](https://join.slack.com/t/liminalorm/shared_invite/zt-2ujrp07s3-bctook4e~cAjn1LgOLVY~Q)
139
136
  - Email: <opensource@dynotx.com>
140
137
  - LinkedIn: [Nirmit Damania](https://www.linkedin.com/in/nirmit-damania/)
141
138
 
@@ -1,10 +1,12 @@
1
1
  liminal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  liminal/base/base_dropdown.py,sha256=Unk4l_5Y8rj_eSWYqzFi2BAFSQToQDWW2qdXwiCHTg8,2523
3
- liminal/base/base_operation.py,sha256=t16LqSPKQPkh1oJtJU1Wr8TPyflILfHqfrRa4Y8ohAM,3089
4
- liminal/base/base_validation_filters.py,sha256=2Q4QhqDSloZqc313p_fc8dnSw0uMhk_iMtsEtsC_5LQ,536
3
+ liminal/base/base_operation.py,sha256=RjGRaCTNQ5oVI4PAQ5D3svymw_HAcGvzBXWWrMRo29k,3128
4
+ liminal/base/base_validation_filters.py,sha256=kHG3G5gXkuNHQosMTrxRc57OTmczcaoSx0DmkrScIr4,1043
5
5
  liminal/base/compare_operation.py,sha256=hkpv4ewHhxy4dlTPKgJuzBjsAqO6Km7OrrKB44pRA_o,352
6
- liminal/base/properties/base_field_properties.py,sha256=wSEZI3YlQw2jEAOCE0yemcwYZJJhcfnBXhXZR9YAz6o,4891
7
- liminal/base/properties/base_schema_properties.py,sha256=IHfSEdghuLtRJvUDCl-Axgf0wgoCj71J134vVTxKWHQ,4425
6
+ liminal/base/name_template_parts.py,sha256=KCGXAcCuOqCjlgYn-mw1K7fwDI92D20l-FnlpEVrbM8,2771
7
+ liminal/base/properties/base_field_properties.py,sha256=NvuQjYZ5U_2363P2c8FXmmLfOtdzUqTuZTjDDfXnO-E,4922
8
+ liminal/base/properties/base_name_template.py,sha256=EHytur3jcPSRx-it_2elYSjdgx7OOr206qyf2bFcweg,3151
9
+ liminal/base/properties/base_schema_properties.py,sha256=pVeIVHea-3Wt0fde9CsOvluwVmYMbsMhGeSncRL2Ats,5351
8
10
  liminal/base/str_enum.py,sha256=jF3d-Lo8zsHUe6GsctX2L-TSj92Y3qCYDrTD-saeJoc,210
9
11
  liminal/cli/cli.py,sha256=JxWHLO9KMeMaOnOYwzdH0w71l0477ScFOkWNtTlc97Y,9045
10
12
  liminal/cli/controller.py,sha256=QNj3QO9TMb9hfc6U-VhLuFa0_aohOHZUmvY4XkATPhw,10118
@@ -14,50 +16,52 @@ liminal/connection/__init__.py,sha256=3z4pSANIOkc9mh1Xp763oYQuJZDEh4lauN901PU4vq
14
16
  liminal/connection/benchling_connection.py,sha256=nALLAA-hPIO2Eb_KhUL-nU3jOlMDSIrPMUgUyDKGRRw,2862
15
17
  liminal/connection/benchling_service.py,sha256=lEYCHF1U8nII8Rn3rMBPTffTFiVFjoFeNmX2Kq36-qE,7170
16
18
  liminal/dropdowns/api.py,sha256=n5oxi1EhkmpmPpNi1LOI4xcIQmk1C069XFaGP5XSBx8,6959
17
- liminal/dropdowns/compare.py,sha256=5cz8djtaStozUun_Cp8t_5PVjq-aovme2Qq5J8FXFg4,6829
19
+ liminal/dropdowns/compare.py,sha256=-UbCkeTKx3THwvjMTUubyYVXBkhmvyhEKzwrIzBkthY,7141
18
20
  liminal/dropdowns/generate_files.py,sha256=IqnBs-IyLsIZE0NUkdB99zd5EAF-1f9CPBeblz-GzJE,2041
19
- liminal/dropdowns/operations.py,sha256=RKXgwO6NQacao6dqzk6__T1B9s-B2YHLPPUyM-0wf0U,13516
21
+ liminal/dropdowns/operations.py,sha256=-TRIsxqnUtrIUjhrt5k_PdiBCDUXsXDzsOUmznJE-6Q,13516
20
22
  liminal/dropdowns/utils.py,sha256=1-H7bTszCUeqeRBpiYXjRjreDzhn1Fd1MFwIsrEI-o4,4109
21
- liminal/entity_schemas/api.py,sha256=Dkd44NGJ4JqRTJLJtPsZ8Qan2owbEYf446A6EuP8iL0,2786
22
- liminal/entity_schemas/compare.py,sha256=C5qr9amGCKvkV61pa29obN883vSdFzSoiebD7hKEHY4,14128
23
- liminal/entity_schemas/entity_schema_models.py,sha256=SNScXY3SeF0lhdAXwqKXrrgpCphpLg6s5tU9fO3juB4,5239
24
- liminal/entity_schemas/generate_files.py,sha256=SW0EccfODptNkZUVwJxa8-8ssd1RWdBqzrCGph4_vSI,8515
25
- liminal/entity_schemas/operations.py,sha256=E12U-F0PtViBApbkoXbEtOlIEcOSSYn_gBx-2Z-vGGM,23164
26
- liminal/entity_schemas/tag_schema_models.py,sha256=rRCAvpjx7iAiIxOh9MRBrpH603zHbFxKOY7sLgOutnE,16260
27
- liminal/entity_schemas/utils.py,sha256=X53KNfguWtu7z-tAliBHuCYpSwTqBGgRSGsdR_BU9Vc,4206
28
- liminal/enums/__init__.py,sha256=jz_c-B_fifatvrYoESlHZ9ljYdz-3rNl0sBazoESiHI,523
23
+ liminal/entity_schemas/api.py,sha256=Emn_Y95cAG9Wis6tpchw6QBVKQh4If86LOdgKk0Ndjw,3575
24
+ liminal/entity_schemas/compare.py,sha256=CIYglq1F-g9jGc1eRRD4LnNErrH2n__pPIJc4EB1hkM,16570
25
+ liminal/entity_schemas/entity_schema_models.py,sha256=YDpz1XkNc9e51zah8Z6qCk30gAuXP6xLEYv1Lb3ECpA,6838
26
+ liminal/entity_schemas/generate_files.py,sha256=ujzofsGlr76z5YadOar0_6ufkjBYZjz0aawBUupIvnU,8878
27
+ liminal/entity_schemas/operations.py,sha256=rs9EXmHDgnUad2SSfZ3tnDPFA6L-2wvllAGqBwBurMs,24020
28
+ liminal/entity_schemas/tag_schema_models.py,sha256=BGvfQwjfIcRl3yHmYUM6sQpkbFTUMaeT6ix62fjKp2A,22017
29
+ liminal/entity_schemas/utils.py,sha256=iZ1_M2r8zKOCFj9QSMdrv-_4XznDn_znAOfoP4Mh1jA,4943
30
+ liminal/enums/__init__.py,sha256=Ue_3QtElW-JMSWtu4vGsAOFQbYnzHHZUdkWpdkzkKA4,453
29
31
  liminal/enums/benchling_api_field_type.py,sha256=0QamSWEMnxZtedZXlh6zNhSRogS9ZqvWskdHHN19xJo,633
30
- liminal/enums/benchling_entity_type.py,sha256=CXCxJzboEbABLMwxrGIoc8hC73LxoSJXHwJWfPjrjvY,435
32
+ liminal/enums/benchling_entity_type.py,sha256=BS6U8qnRM3I3xTTqp9BbInV7yjPh9gC3ULvN6-zLaCM,624
31
33
  liminal/enums/benchling_field_type.py,sha256=uinDm5Mn_yGK1jlmlRH3NlAlXUzA1guNk8wF6lbUKD4,947
32
34
  liminal/enums/benchling_folder_item_type.py,sha256=Jb-YxCvB8O86_qTsfwtLQOkKGjTWGKHFwIKf24eemYk,248
33
35
  liminal/enums/benchling_naming_strategy.py,sha256=wG3AfnPOui5Qfc0Fihszm5uKWjuc7gdpI8jptNB5A-w,1201
34
- liminal/enums/benchling_report_level.py,sha256=HtnSW_yNuRpJ_iQHhzcZudb1Me4QubVg3n9sPSnJiNI,263
35
36
  liminal/enums/benchling_sequence_type.py,sha256=TBI4C5c1XKE4ZXqsz1ApDUzy2wR-04u-M3VO_zLikjM,202
36
- liminal/external/__init__.py,sha256=fXA-WM8eD4q8D0bhnGvRCV-eOzvppnd8d9w6xcH0iLA,991
37
+ liminal/enums/name_template_part_type.py,sha256=Kv0phZIO_dPN3tLHM0lT2tjUd3zBGqpJQGahEpGjNcU,365
38
+ liminal/external/__init__.py,sha256=rYg51-fI6FO7H2iZkbIPUzWpSW8MTBKfiTNwLZt3yvY,1265
37
39
  liminal/mappers.py,sha256=O9gc95b7JvfaR8xVrn0X1d0Tcs6Iwh-yhBHXhWSX8i0,9616
38
40
  liminal/migrate/components.py,sha256=2HuFp5KDNhofROMRI-BioUoA4CCjhQ_v_F0QmGJzUBU,3480
39
41
  liminal/migrate/revision.py,sha256=KppU0u-d0JsfPsXsmncxy9Q_XBJyf-o4e16wNZAJODM,7774
40
- liminal/migrate/revisions_timeline.py,sha256=06qf_7E1Hecucfczpm85rV3ATLDjpCf7y6TUfah5aLM,14450
42
+ liminal/migrate/revisions_timeline.py,sha256=G9VwxPrLhLqKOrIXyxrXyHpujc-72m7omsZjI5-0D0M,14520
41
43
  liminal/migrate/utils.py,sha256=HdSr3N2WN_1S-PLRGVWSMYl-4gIcP-Ph2wPycGi2cGg,3404
42
44
  liminal/orm/base.py,sha256=fFSpiNRYgK5UG7lbXdQGV8KgO8pwjMqt0pycM3rWJ2o,615
43
- liminal/orm/base_model.py,sha256=V6hX2q6n_5xWJaOdYUNOpw3dHcypy_wyloGxkl9_TDs,10975
45
+ liminal/orm/base_model.py,sha256=RYVQ2kFaxMDGYWL2Lv63KMUZljSyIkdkjoSHoHLU8UU,13892
44
46
  liminal/orm/base_tables/registry_entity.py,sha256=4ET1cepTGjZ3AMFI5q-iMYxMObzXwuUDBD0jNNqCipE,2126
45
47
  liminal/orm/base_tables/schema.py,sha256=7_btCVSUJxjVdGcKVRKL8sKcNw7-_gazTpfEh1jru3o,921
46
48
  liminal/orm/base_tables/user.py,sha256=elRAHj7HgO3iVLK_pNCIwf_9Rl_9k6vkBgaYazoJSQc,818
47
49
  liminal/orm/column.py,sha256=e4JWn97s_4EVJ1LOO5l6iucHQUd39Vl0stqMEj0uet8,5114
48
50
  liminal/orm/mixins.py,sha256=yEeUDF1qEBLP523q8bZra4KtNVK0gwZN9mXJSNe3GEE,4802
51
+ liminal/orm/name_template.py,sha256=qsppCDPCVBhVZs3Uz-x4QdJbElH6VPlwfRD1BX_pAwE,1739
49
52
  liminal/orm/relationship.py,sha256=Zl4bMHbtDSPx1psGHYnojGGJpA8B8hwcPJdgjB1lmW0,2490
50
- liminal/orm/schema_properties.py,sha256=UEIIayhiwHw7YexSGoKU9Z7gj57d7_C1CMUv51-HcGk,2158
53
+ liminal/orm/schema_properties.py,sha256=yv6MOsE_16OWJnGDh2K8wI_054PJwafYmHgY_Awr3XA,3420
51
54
  liminal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
55
  liminal/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
- liminal/tests/conftest.py,sha256=1RGiI_h8izLjT9txJ1IkRHYf8yrN_r6yjUn5SI2bxr8,17311
56
+ liminal/tests/conftest.py,sha256=B463eOfe1uCHDJsUNvG-6tY8Qx8FJMByGDOtuyM87lA,17669
54
57
  liminal/tests/from benchling_sdk.py,sha256=CjRUHFB3iaa4rUPLGOqDiBq5EPKldm-Fd8aQQr92zF4,147
55
58
  liminal/tests/test_dropdown_compare.py,sha256=yHB0ovQlBLRu8-qYkqIPd8VtYEOmOft_93FQM86g_z8,8198
56
- liminal/tests/test_entity_schema_compare.py,sha256=p-9inAZM4GOm4e1cadO191LNsnqceUGGyy0YVIXXxVw,16440
57
- liminal/utils.py,sha256=vMjSasDnEghwqULDo14joxxJ56G4-9cBsw719nQ8C7g,2798
58
- liminal/validation/__init__.py,sha256=SBd48xxBMJrBzI48G2RcK056EMlevt5YjmZMkfCWN1I,6924
59
- liminal_orm-1.1.3.dist-info/LICENSE.md,sha256=oVA877F_D1AV44dpjsv4f-4k690uNGApX1EtzOo3T8U,11353
60
- liminal_orm-1.1.3.dist-info/METADATA,sha256=B5aAUjYgRCbXTlzlpoQJSGln5Q8rJAYUOGO5Q5MMl7o,11313
61
- liminal_orm-1.1.3.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
62
- liminal_orm-1.1.3.dist-info/entry_points.txt,sha256=atIrU63rrzH81dWC2sjUbFLlc5FWMmYRdMxXEWexIZA,47
63
- liminal_orm-1.1.3.dist-info/RECORD,,
59
+ liminal/tests/test_entity_schema_compare.py,sha256=JfvrdRfnm77dchHL5mwTLXfrKYW47wANacFJlS5Iw80,18953
60
+ liminal/utils.py,sha256=radRtRsZmCiNblMvxOX1DH0rcO5TR09kFlp6OONIPBU,2951
61
+ liminal/validation/__init__.py,sha256=HOjBHCwxvQao6SN_Q5B-JbabG7Z6ff44JIDKLeK96l8,5458
62
+ liminal/validation/validation_severity.py,sha256=ib03PTZCQHcbBDc01v4gJF53YtA-ANY6QSFnhTV-FbU,259
63
+ liminal_orm-2.0.0.dist-info/LICENSE.md,sha256=oVA877F_D1AV44dpjsv4f-4k690uNGApX1EtzOo3T8U,11353
64
+ liminal_orm-2.0.0.dist-info/METADATA,sha256=_ioXetNNEIXfaO4Zmgis49Y86FfiUyGbmpCqy5SJI3E,11056
65
+ liminal_orm-2.0.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
66
+ liminal_orm-2.0.0.dist-info/entry_points.txt,sha256=atIrU63rrzH81dWC2sjUbFLlc5FWMmYRdMxXEWexIZA,47
67
+ liminal_orm-2.0.0.dist-info/RECORD,,