Skip to main content

mgtx-benchling-wrapper

A wrapper of the Benchling API SDK with common functions and workflows used at MGTX DSC.

Installation 🚀

You can install this package using:

pip install mgtx-benchling-wrapper

Quickstart 🚩

After creating an app on your Benchling tenant, create a config.yaml file in your repo. The following is an example of the contents of a config.yaml

Note: You might want to keep the app client secret separated from your main code.

BenchlingCredentials:
  benchling_url: 'https://mytenant.benchling.com'
  benchling_access_token: 'https://mytenant.benchling.com/api/v2/token'
  app_client_id: 'your-app-client-id'
  app_client_secret: 'your-app-client-secret'
AssaySchema:
  schema_id: "schema_api_id"
Project:
  project_id: "project_api_id"

The following is an example of the use of the assay_results_ingestion workflow.

import yaml
from mgtx_benchling_wrapper import (
    BenchlingContext,
    BenchlingWrapperFacade,
    AssayResultIngestionWorkflow
)
from mgtx_benchling_wrapper.utils.logger import get_logger

logger = get_logger(__name__,
                        file_log_level='DEBUG',
                        console_log_level='INFO', )

def config():
    with open("tests/config/test_config.yml") as f:
        return yaml.safe_load(f)

#create the benchling context        
ctx = BenchlingContext(
        base_url=config()['BenchlingCredentials']['benchling_url'],
        client_id=config()['BenchlingCredentials']['app_client_id'],
        client_secret=config()['BenchlingCredentials']['app_client_secret'],
        token_url=config()['BenchlingCredentials']['benchling_access_token'],
    )    

#initialize the wrapper
wrapper = BenchlingWrapperFacade(ctx.benchling())

#retrieve assay_schema_id
schema_id = config()['AssaySchema']['schema_id']

#retrieve project_id
project_id = config()['Project']['project_id']

#initiate results ingestion workflow
results_ingestion = AssayResultIngestionWorkflow(wrapper)

#ingest results on benchling
list_missing_entities = results_ingestion.assay_results_ingestion(
        [dataframe_to_ingest],
        schema_id,
        project_id,
        unique_identifiers =['assay_run_id']
        )       

The assay_results_ingestion function from the AssayResultIngestionWorkflow class above has two main ways of operating. The first part of the function focuses on how the results are ingested and the second part focuses on where the results are ingested.

Behavior 1: How the results are ingested 🔎

Let's first have a look at three possibilities on how the results are ingested. This behavior is ruled by the function inputs unique_identifiers, compare_on, and archive. The unique_identifiers list is used to call all assay results containing entities in this list. The compare_on list is used to build a dataframe of pre-existing results on Benchling that is used to subset rows not yet on Benchling and therefore need to be ingested.

We will consider a dummy results schema exists on Benchling with the following data:

assay_run_id sample_id replicate average_result result_sd valid?
Assay 1 Sample 1 1 25 2 Yes
Assay 1 Sample 1 2 34 6 Yes
Assay 2 Sample 1 1 15 4 Yes
Assay 2 Sample 2 1 31 5 Yes

We consider that we have a dataframe which results we want to ingest as follows:

assay_run_id sample_id replicate average_result result_sd valid?
Assay 2 Sample 1 1 31 5 Yes
Assay 2 Sample 2 1 50 8 Yes

Example 1 🚦

You want to archive pre-existing assay results and ingest results. In this specific case, data had already been ingested but was erroneously processed, so old values need to be archived and new values are to be ingested. To achieve this you could use the following piece of code

list_missing_entities = results_ingestion.assay_results_ingestion(
        [dataframe_to_ingest],
        schema_id,
        project_id,
        unique_identifiers =['assay_run_id'],
        archive = True
        compare_on = None
        )  

In this example, the following results would be archived:

assay_run_id sample_id replicate average_result result_sd valid?
Assay 2 Sample 1 1 15 4 Yes
Assay 2 Sample 2 1 31 5 Yes

and the following results would be ingested:

assay_run_id sample_id replicate average_result result_sd valid?
Assay 2 Sample 1 1 31 5 Yes
Assay 2 Sample 2 1 50 8 Yes

NOTE: You could keep the unique_identifiers = ['assay_run_id', 'sample_id'] in the code above to achieve the same result.

Example 2 🚦

You want to ingest values not yet on Benchling. You want to use a specific columns to make the comparison.

list_missing_entities = results_ingestion.assay_results_ingestion(
        [dataframe_to_ingest],
        schema_id,
        project_id,
        unique_identifiers =['assay_run_id'],
        archive = False
        compare_on = ['sample_id', 'replicate', 'average_result', 'result_sd']
        )  

In this example, no results would be archived and the following results would be ingested:

assay_run_id sample_id replicate average_result result_sd valid?
Assay 2 Sample 1 1 31 5 Yes
Assay 2 Sample 2 1 50 8 Yes

NOTE: You could keep the unique_identifiers = ['assay_run_id', 'sample_id'] in the code above to achieve the same result.

Example 3 🚦

You want to ingest values for entities that are not yet present on the result schema. This method is quite useful when ingesting high volumes of data in the background such as bioreactor online traces, where you don't want to lose time calling a big number of assay results.

list_missing_entities = results_ingestion.assay_results_ingestion(
        [dataframe_to_ingest],
        schema_id,
        project_id,
        unique_identifiers =['assay_run_id'],
        archive = False
        compare_on = None
        )  

In this example no results would be archived and no results would be ingested because all assay_run_id entities are already present in the schema.

NOTE: Best to use this method with setting the unique_identifiers to a list of one variable that represents the overarching batch run or assay run. In the case where unique_identifiers=['assay_run_id', 'sample_id'], if a new assay_run_id were to be ingested and a sample_id previously ingested was also mentioned in the dataframe, no results would be ingested.

Behavior 2: Where the results are ingested 🔎

There are two options: sending results to the warehouse only (backend) or sending results to a notebook entry (backend and frontend). Choose the appropriate method taking into account the volume of data you will be ingesting to minimize API calls and avoid hitting rate limits.

Let's use example 1 from section Behavior 1: How the results are ingested 🔎 as the basis.

Example 1: Sending results to warehouse only using transactions 🚦

To achieve this we would use the following code:

list_missing_entities = results_ingestion.assay_results_ingestion(
        [dataframe_to_ingest],
        schema_id,
        project_id,
        unique_identifiers =['assay_run_id'],
        archive = True,
        compare_on = None,
        entry_name = None,
        commit_in_transaction = True
        )  

This piece of code will ingest the results to the backend using transactions. Each transaction will contain requests with a maximum of 100 assay results. Transactions is the safest way to ingest data to backend given that when one ingestion in the transaction fails, all fail.

Example 2: Sending results to warehouse only using a bulk request 🚦

To achieve this we would use the following code:

list_missing_entities = results_ingestion.assay_results_ingestion(
        [dataframe_to_ingest],
        schema_id,
        project_id,
        unique_identifiers =['assay_run_id'],
        archive = True,
        compare_on = None,
        entry_name = None,
        commit_in_transaction = False
        )  

This piece of code will ingest the results to the backend using a bulk request. Up to 4000 assay runs can be ingested using the bulk request end-point. This is not as safe as a transaction but faster.

Example 3: Sending results to a notebook entry🚦

To achieve this we would use the following code:

list_missing_entities = results_ingestion.assay_results_ingestion(
        [dataframe_to_ingest],
        schema_id,
        project_id,
        unique_identifiers =['assay_run_id'],
        archive = True,
        compare_on = None,
        entry_name = 'Entry name containing a target table with of the schema_id type',
        commit_in_transaction = False
        )  

This piece of code will ingest the results to the entry with entry_name if a suited table is present. A table in a notebook entry is able to show up to 1000 values. Once results are ingested, navigate to the entry and click on the green flashing API button to see the data.

Any of the behaviors in examples on section Behavior 1: How the results are ingested 🔎 can be coupled with the behaviors in this section.

Creating a schema_definition

Another example is using the SchemaHandler to get a schema_definition the describes a schema on Benchling.

#run the following after initializing the wrapper

from mgtx_benchling_wrapper import SchemaHandler

schema_handler = SchemaHandler(wrapper)

schema_definition = schema_handler.build_schema_definition(schema_id, 'assay_results_schema')

Using a method directly from the wrapper

Another example is using a method from the wrapper directly to build your own workflow.

#run the following after initializing the wrapper

list_custom_entities = wrapper.custom_entities.get_by_names(['your-entity-1', 'your-entity-2'])

for custom_entity in list_custom_entities:
    name = wrapper.custom_entities.name()
    print(f"the name of the custom entity {custom_entity} is {name}.")

To release and publish run the following on gitBash to initialize the GitHub Action for publishing the package.

git tag v0.1.#
git push origin v0.1.#

Logs are by default enabled for developer use on the console. To save logs, an environment variable is needed. Create the environment variable on your repo in a .env file as such, and then make sure to load it.

APP_DEBUG_LOGS=1

Metadata

Release files for mgtx-benchling-wrapper 0.1.10

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mgtx-benchling-wrapper 0.1.10
File Size Uploaded
mgtx_benchling_wrapper-0.1.10.tar.gz 36.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mgtx-benchling-wrapper 0.1.10
File Interpreter ABI Platform
mgtx_benchling_wrapper-0.1.10-py3-none-any.whl Python 3 none any Details

Total release size: 90.8 kB

Release files / mgtx_benchling_wrapper-0.1.10.tar.gz

Download URL mgtx_benchling_wrapper-0.1.10.tar.gz
Size 36.8 kB
Tags Source
SHA-256 checksum
How to use checksums
62783725894427feee617b3f8abf51a9943987e0f11624ac081635de83f5c7a8
BLAKE2b-256 checksum
How to use checksums
f9e08a1ae2da40f8a534f3e12d77a617719f3c7192980dc7582f94908a25b4e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 25, 2026.

Transparency log

Release files / mgtx_benchling_wrapper-0.1.10-py3-none-any.whl

Download URL mgtx_benchling_wrapper-0.1.10-py3-none-any.whl
Size 53.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bd504e038ce965651828324b55cf9cf148ec26259c077ad92ac1a28153a5696a
BLAKE2b-256 checksum
How to use checksums
996a44860737d866112891aa68813e5a41b74213c4bc48679f079d3acf95f599
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.10 This release

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page