diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6cb3d906..7e35eefd 100755
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -30,6 +30,7 @@ repos:
rev: 1.16.0
hooks:
- id: blacken-docs
+ exclude: docs/tutorial.md
- repo: https://github.com/crate-ci/typos
rev: typos-dict-v0.11.2
diff --git a/README.md b/README.md
index 8f2f7cff..2ed247b5 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,8 @@ _An Efficient and Ergonomic Python Binding Library for BLAT_
[![c](https://img.shields.io/badge/C-A8B9CC.svg?style=for-the-badge&logo=C&logoColor=black)](https://www.gnu.org/software/gnu-c-manual/)
[![pypi](https://img.shields.io/pypi/v/pxblat.svg?style=for-the-badge)][pypi]
[![conda](https://img.shields.io/conda/vn/bioconda/pxblat?style=for-the-badge)][conda]
-![platform](https://img.shields.io/static/v1?style=for-the-badge&label=platform&message=macos%20%7C%20linux&color=brightgreen)
+![Linux](https://img.shields.io/badge/-Linux-grey?logo=linux&style=for-the-badge)
+![macOS](https://img.shields.io/badge/-OSX-black?logo=apple&style=for-the-badge)
[![pyversion](https://img.shields.io/pypi/pyversions/pxblat?style=for-the-badge)][pypi]
[![tests](https://img.shields.io/github/actions/workflow/status/cauliyang/pxblat/tests.yml?style=for-the-badge&logo=github&label=Tests)](https://github.com/cauliyang/pxblat/actions/workflows/tests.yml)
[![Codecov](https://img.shields.io/codecov/c/github/cauliyang/pxblat/main?style=for-the-badge)](https://app.codecov.io/gh/cauliyang/pxblat)
diff --git a/docs/_templates/base.html b/docs/_templates/base.html
index 5884beaf..8a40e363 100644
--- a/docs/_templates/base.html
+++ b/docs/_templates/base.html
@@ -1,6 +1,5 @@
{% extends "!base.html" %} {% block announce %}
- Now
Please Cite Me
v0.3.10
diff --git a/docs/conf.py b/docs/conf.py
index d16a780e..6af9b600 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -82,6 +82,7 @@
# so a file named 'default.css' will overwrite the builtin 'default.css'.
html_static_path = ["_static"]
+
myst_heading_anchors = 3
myst_enable_extensions = [
"dollarmath",
diff --git a/docs/installation.md b/docs/installation.md
index 02a0116e..ee814a27 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -1,95 +1,74 @@
-# Install
+# **Installation Guide**
-## **Prerequisites**
+Welcome to the installation guide for **PxBLAT**!
+Below, you'll find straightforward instructions on how to install **PxBLAT** on your system.
+You can choose between `CONDA` and `PyPI` for installation, depending on your preference.
-```{tip}
-I highly recommend to install `PxBLAT` via `CONDA` such that you do not need to
-install dependencies [goto ➡️](#get-started).
-```
+## Get started
-Before you begin with the installation and use of **PxBLAT**, there are certain prerequisites that need to be met.
-The following sections detail the necessary software, hardware, and knowledge prerequisites that you should have before you start.
-**PxBLAT** has been tested and runs on the following operating systems **Linux** and **MacOS**.
-Choosing appropriate dependencies manager according to the operating systems.
-You should have an active and stable internet connection for downloading and installing the software:
+Installing **PxBLAT** is a breeze, whether you prefer using `CONDA` or `PyPI`.
+Simply follow the tabs below to choose your preferred installation method.
`````{md-tab-set}
-````{md-tab-item} Brew
+````{md-tab-item} Pip
+
+To install using `pip`, run the following command in your terminal:
+
```{code-block} bash
-brew install htslib openssl
+pip install pxblat
```
+
+This will fetch the latest version of **PxBLAT** and install it on your system.
````
````{md-tab-item} Conda
+
+To install using `conda`, execute the following command:
+
```{code-block} bash
-conda install htslib openssl
-```
-````
-````{md-tab-item} Apt-get
-```{code-block} bash
-apt-get update && apt-get install libhts-dev libssl-dev
+conda install pxblat
```
+This command will install **PxBLAT** from the [conda] repository.
````
`````
-## **Get Started**
+## Compatibility and Support
-Installing **PxBLAT** via `CONDA` **do not requires** the prerequisites.
-Installing **PxBLAT** via `PyPI` requires the prerequisites.
-Once you have confirmed the prerequisites, you can proceed with the software installation:
+Below is a compatibility matrix that shows the support status of **PxBLAT** across different platforms and Python versions.
-`````{md-tab-set}
+### [Conda Support][conda]
-````{md-tab-item} Conda
-```{code-block} bash
-conda install pxblat
-```
-````
-
-````{md-tab-item} Pip
-```{code-block} bash
-pip install pxblat
-```
-````
-
-`````
+| Python Version | Linux x86_64 | macOS Intel |
+| :------------: | :----------: | :---------: |
+| 3.9 | ✅ | ✅ |
+| 3.10 | ✅ | ✅ |
+| 3.11 | ✅ | ✅ |
+| 3.12 | ⚫ | ⚫ |
-``````{warning}
-You meet the issue _*.h cannot found_ or _undefined symbol **_ If you install `pxblat` by `pip`.
-If you have installed the prerequisites, the problem is caused by incorrect environment variable {envvar}`CFLAGS`, `CXXFLAGS`,
-and `LDFLAGS`, which direct compiler and linker find right location of
-dependencies so as to compile and link code properly.
-**The easy solution** is to install **PxBLAT** via `conda`.
-You can also set proper environment variable if `conda` is not accessible.
+### [PyPI Support][pypi]
+| Python Version | Linux x86_64 | macOS Intel | macOS Apple Silicon |
+| :------------: | :----------: | :---------: | :-----------------: |
+| 3.9 | ✅ | ✅ | ✅ |
+| 3.10 | ✅ | ✅ | ✅ |
+| 3.11 | ✅ | ✅ | ✅ |
+| 3.12 | ⚫ | ⚫ | ⚫ |
-`````{md-tab-set}
+## Frequently Asked Questions (FAQ)
-````{md-tab-item} Bash/Zsh
-```{code-block} bash
-export CFLAGS="-Idependencies"
-export CXXFLAGS="-Idependencies"
-...
-```
-````
+- **Q1**: I'm unable to install `PxBLAT` via `Conda` on my macOS device with an Arm-based processor (M1 or M2). What should I do?
-````{md-tab-item} Fish
-```{code-block} fish
-set -x CFLAGS="-Idependencies"
-set -x CXXLAGS="-Idependencies"
-...
-```
-````
-`````
+ > **A**: Currently, `Conda` provides **PxBLAT** built for x86 architectures. For Arm-based macOS devices, we recommend installing **PxBLAT** via `PyPI` instead:
-``````
+ ```bash
+ pip install pxblat
+ ```
-## **FAQ (Frequently Asked Questions)**
+You've successfully installed **PxBLAT**! Feel free to dive into a real usage case ({doc}`tutorial`) to start exploring its functionalities.
-1. I cannot download `PxBLAT` via `Conda` in MacOS with Arm (M1 or M2).
+
-A: So far, conda build `PxBLAT` in x86 instead of arrch64.
-Hence we can install [prerequisites](#prerequisites) manually via `brew` or `Conda`.
-Then, we can use `pip install pxblat` to install `PxBLAT`.
+[conda]: https://anaconda.org/bioconda/pxblat
+[pypi]: https://pypi.org/project/pxblat/
diff --git a/docs/tutorial.md b/docs/tutorial.md
index 7a920985..bbcb0e3b 100644
--- a/docs/tutorial.md
+++ b/docs/tutorial.md
@@ -1,82 +1,171 @@
# **Tutorial**
-`PxBLAT` binds the codebase of [BLAT(v.37x1)][BLAT(v.37x1)], and aims to provide efficient and
-ergonomic APIs. Let's take the journey to show features `PxBLAT` provides.
+```{warning}
+Make sure you have installed PxBLAT, otherwise please go-to ({doc}`installation`).
+```
-## APIs Compared to `BLAT`
+```{important}
+We do not assume you already know common formats and BLAT, which is a standout within the bioinformatics landscape and is recognized for its capability to conduct genome sequence alignments.
+BLAT can help us know where one or several sequences can be mapped to the reference for nucleotide or peptide sequences.
+Assume we have multiple sequences, and want to know where these sequences can be mapped in reference sequence.
+After reading the tutorial, you are supported to know how to use PxBLAT to align your sequences.
+```
-So far, `PxBLAT` provides four main APIs, including {class}`.Client`, {class}`.Server`, {func}`.two_bit_to_fa` and {func}`.fa_to_two_bit`,
-as well as other useful functions ({doc}`reference`).
-`PxBLAT` is able to finish the most significant features of `BLAT`.
-Here is a table in which the features are compared.
+**PxBLAT** binds the codebase of [BLAT(v.37x1)][BLAT(v.37x1)], and aims to provide efficient and
+ergonomic APIs. Let's take the journey to show features **PxBLAT** provides.
-| PxBLAT | BLAT |
-| :--------------------- | :----------------------- |
-| {class}`.Client` | [gfClient][gfClient] |
-| {class}`.Server` | [gfServer][gfServer] |
-| {func}`.two_bit_to_fa` | [twoBitToFa][twoBitToFa] |
-| {func}`.fa_to_two_bit` | [faToTwoBit][faToTwoBit] |
+## 1. Understanding the FASTA Format
-## Options Design
+In bioinformatics, the FASTA format is a widely used text-based format for representing nucleotide sequences or peptide sequences and their associated information.
+Below, we will introduce the FASTA format, its structure, and how it is utilized in bioinformatics applications.
-`PxBLAT` uses a extra class to hold and change parameters for {class}`.Server`
-and {class}`.TwoBitToFaOption`.
-The design is a trick to mimic named parameter, and is used in Cpp and Rust.
-Python may not need to the design but it still benefit if the parameters are too
-long.
-For example, `PxBLAT` can create and change parameters of {class}`.Server` via {meth}`.Server.create_option`.
-The chain methods of options is builder pattern which is used in Cpp and Rust.
+The FASTA format is a simple, text-based format for representing biological sequences.
+Each entry in a FASTA file begins with a single-line description, followed by the sequence data.
+The description line is distinguished from the sequence data by a greater-than (`>`) symbol at the beginning.
-```{tip}
-Click the blinking circle cross, and you will be blessed and get more information.
+### Structure of a FASTA File
+
+Here is an example to illustrate the structure of a FASTA file:
+
+```
+>sequence1
+ATGCTAGCTAGCTAGCTAGCTAGCTA
+GCTAGCTAGCTAGCTAGCTAGCTAGC
+TAGCTAGCTAGCTAGCTAGCTAGCTA
```
-```{eval-rst}
-.. code-block:: python
- :linenos:
+In this example:
- from pxblat import Server
+- `>sequence1` are description lines for two different sequences.
+- The sequences themselves are represented in the lines following the description lines.
+- Sequences can span multiple lines for readability, and there are no line length restrictions.
- server_option = Server.create_option().withStepSize(3).withTileSize(10).build() # (1)!
- server = Server("localhost", port, two_bit, server_option)
+In bioinformatics, the FASTA format is used to represent sequences for various applications, such as:
-.. code-annotations::
- #. we change step size and tile size
+- Sequence alignment: Comparing sequences to find similarities and differences.
+- Database search: Searching for sequences in large databases.
+- Phylogenetics: Studying the evolutionary relationships between sequences.
+
+The FASTA format is a fundamental part of bioinformatics, providing a simple and efficient way to represent biological sequences.
+Understanding this format is crucial for anyone looking to work in the field or use bioinformatics tools, including **PxBLAT**.
+
+## 2. Prepare Example Data
+
+### Download sequences and reference examples
+
+- Let's create a new directory first.
+
+```bash
+mkdir tutorial
+cd tutorial
+```
+
+- Download reference data {download}`⬇️ test_ref.fa `, which is fasta format.
+
+````{example} Download via wget
+:collapsible: close
+
+```bash
+wget https://raw.githubusercontent.com/ylab-hi/pxblat/main/tests/data/test_ref.fa
+```
+
+````
+
+Let's check the reference data
+
+```console
+$ head test_ref.fa
+>chr1
+NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
+taaccctaaccctaaccctaaccctaaccctaaccctaaccctaacccta
+accctaaccctaaccctaaccctaaccctaaccctaaccctaaccctaac
+cctaacccaaccctaaccctaaccctaaccctaaccctaaccctaacccc
+taaccctaaccctaaccctaaccctaacctaaccctaaccctaaccctaa
+ccctaaccctaaccctaaccctaaccctaacccctaaccctaaccctaaa
+ccctaaaccctaaccctaaccctaaccctaaccctaaccccaaccccaac
+cccaaccccaaccccaaccccaaccctaacccctaaccctaaccctaacc
+ctaccctaaccctaaccctaaccctaaccctaaccctaacccctaacccc
+
+$ wc -l test_ref.fa
+301 test_ref.fa
```
-The options have same parameter as its command line version of `BLAT`.
-{doc}`reference` includes all possible parameters the option will accept.
+- Download test sequences {download}`⬇️ test_case1.fa `, which is fasta format.
+
+````{example} Download via wget
+:collapsible: close
-## From FASTA to 2bit
+```bash
+wget https://raw.githubusercontent.com/ylab-hi/pxblat/main/tests/data/test_case1.fa
+```
+````
+
+Let's check test reference
+
+```bash
+$ head test_case1.fa
+>case1
+TGAGAGGCATCTGGCCCTCCCTGCGCTGTGCCAGCAGCTTGGAGAACCCA
+CACTCAATGAACGCAGCACTCCACTACCCAGGAAATGCCTTCCTGCCCTC
+TCCTCATCCCATCCCTGGGCAGGGGACATGCAACTGTCTACAAGGTGCCA
+A
+```
+
+Now we already have `test_case1.fa` and `test_ref.fa` for following analysis.
+
+```bash
+$ ls
+test_case1.fa test_ref.fa
+```
-Before we query certain sequence to a reference, we need to generate [.2bit][.2bit] file from [fasta][fasta] format.
-`PxBLAT` provides a free function {func}`.fa_to_two_bit`.
-Also, `PxBLAT` support to convert the `.2bit` file back to fasta format via {func}`.two_bit_to_fa`, for example,
+## 3. Convert FASTA to 2bit
+
+Before we query certain sequence to a reference `test_ref.fa`, we need to convert [fasta][fasta] format to [.2bit][.2bit] file for reference sequence `test_ref.fa`.
+**PxBLAT** provides a function {func}`.fa_to_two_bit`.
+Also, **PxBLAT** supports to convert the `.2bit` file back to fasta format via {func}`.two_bit_to_fa`, for example,
```{tip}
-The source code includes [chr20.fa] and [chr20.2bit] as well, making it easy for users to give it a try.
+Click the blinking circle cross, and you will be blessed and get more information.
```
```{eval-rst}
.. code-block:: python
+ :name: fa_to_two_bit_block
:linenos:
from pxblat import fa_to_two_bit
fa_to_two_bit(
- ["fasta1.fa"], # (1)!
- "out.2bit", # (2)!
+ ["test_ref.fa"], # (1)!
+ "test_ref.2bit", # (2)!
noMask=False,
stripVersion=False,
ignoreDups=False,
useLong=False,
)
+ print("Done")
+
.. code-annotations::
#. Same as `BLAT`, :func:`.fa_to_two_bit` can accept multilple inputs
#. Output file path
```
+Let's create a Python file named `2bit.py`, and copy and paste [code above](#fa_to_two_bit_block) to `2bit.py`.
+Then, execute the `2bit.py`
+
+```bash
+python 2bit.py
+```
+
+After, we will get a new file named `test_ref.2bit`, which is the 2bit file we
+need to align sequences to reference.
+
+```bash
+$ ls
+2bit.py test_case1.fa test_ref.2bit test_ref.fa
+```
+
The code equals `faToTwoBit fasta1.fa out.2bit` by `BLAT(v. 37x1)`.
```bash
@@ -91,282 +180,379 @@ options:
-stripVersion Strip off version number after '.' for GenBank accessions.
-ignoreDups Convert first sequence only if there are duplicate sequence
names. Use 'twoBitDup' to find duplicate sequences.
-$ faToTwoBit fasta1.fa out.2bit
+$ faToTwoBit test_ref.fa test_ref.2bit
$ ls
-out.2bit fasta1.fa
+test_ref.2bit test_ref.fa
```
-Moreover, `PxBLAT` provides flexible options to allow conducting the conversion in {doc}`cli`.
+Moreover, **PxBLAT** provides flexible options to allow conducting the conversion in {doc}`cli`.
-## Query Sequences
+## 4. Query Sequences
-Most simple method to query sequence is to open {class}`pxblat.Server` in context mode
+**PxBLAT** contains {class}`pxblat.Server` and {class}`pxblat.Client`.
+We use them to align our sequences in two steps.
+
+1. Start {class}`pxblat.Server`
+2. {class}`pxblat.Client` send our sequence to {class}`pxblat.Server` for
+ alignment
+
+Generally, {class}`pxblat.Server` has three status including `preparing`, `ready`, and `stop`.
+It only accepts sequence alignment task in `ready` status.
+Hence, in real life we need to make sure the {class}`pxblat.Server` is in `ready` status before {class}`pxblat.Client`send sequences.
+**PxBLAT** allow this process more smooth without bothering intermediate file.
+
+**PxBLAT** provide several ways to start the {class}`pxblat.Server`.
+
+### 4.1 Start {class}`pxblat.Server` in context mode
```{eval-rst}
.. code-block:: python
+ :name: query_context_block
:linenos:
- from pxblat import Server, Client
+ from pxblat import Client, Server
- client = Client(
- host="localhost",
- port=65000, # (1)!
- seq_dir=two_bit, # (2)!
- min_score=20,
- min_identity=90,
- )
- server_option = Server.create_option().withCanStop(True).withStepSize(5).build() # (3)!
- with Server("localhost", port, two_bit, server_option) as server:
- work() # (4)!
- server.wait_for_ready() # (5)!
- result1 = client.query("ATCG") # (6)!
- result2 = client.query("AtcG") # (7)!
- result3 = client.query(["ATCG", "ATCG"]) # (8)!
- result4 = client.query(["fasta1.fa", "fasta2.fa"]) # (9)!
- result5 = client.query(["cgTA", "fasta.fa"]) # (10)!
+ def query_context():
+ host = "localhost" # (1)!
+ port = 65000 # (2)!
+ seq_dir = "." # (3)!
+ two_bit = "./test_ref.2bit" # (4)!
+
+ client = Client(
+ host=host,
+ port=port,
+ seq_dir=seq_dir,
+ min_score=20, # (5)!
+ min_identity=90, # (6)!
+ )
+
+ with Server(host, port, two_bit, can_stop=True, step_size=5) as server:
+ # work() assume work() is your own function that takes time to prepare something
+ server.wait_ready() # (7)!
+ result1 = client.query("ATCG") # (8)!
+ result2 = client.query("AtcG") # (9)!
+ result3 = client.query("test_case1.fa") # (10)!
+ result4 = client.query(["ATCG", "ATCG"]) # (11)!
+ result5 = client.query(["test_case1.fa"]) # (12)!
+ result6 = client.query(["cgTA", "test_case1.fa"]) # (13)!
+ print(result3[0]) # print result
+
+
+ if __name__ == "__main__":
+ query_context()
.. code-annotations::
- #. The port number of current running :class:`.Server`
- #. The two bit files from reference, and we can get it via :func:`.fa_to_two_bit` or via :doc:`cli`
- #. Create :class:`.ServerOption` with specific parameters for :class:`.Server`
- #. We can do some other stuffs that consuming time
- #. Block current thread to wait server to be ready
- #. :meth:`.Client.query` accepts a :class:`str` consisting of DNA or Protein Sequences, e.g. `"ATCG"`
- #. :meth:`.Client.query` accepts a path of Fasta file, e.g. `"data/fasta1.fa"`
- #. :meth:`.Client.query` accepts a :class:`list` of :class:`str` consisting of DNA or Protein Sequences, e.g. `["ATCG","CTGAG"]`
- #. :meth:`.Client.query` accepts a :class:`list` of path of Fasta files, e.g. `["data/fasta1.fa", "data/fasta2.fa"]`
- #. :meth:`.Client.query` accepts a :class:`list` of :class:`str` and path, e.g. `["ATCG", "data/fasta1.fa"]`
+ #. :attr:`.Client.host` is the hostname or IP address of the current running :class:`.Server`.
+ #. :attr:`.Client.post` is the port number of the current running :class:`.Server`.
+ #. :attr:`.Client.seq_dir` is the directory including `test_ref.fa` and `test_ref.2bit`
+ #. `two_bit` is the 2bit file that :ref:`we already create `
+ #. :attr:`.Client.min_score` is the minimum score for the alignment.
+ #. :attr:`.Client.min_identity` is the minimum identity for the alignment.
+ #. block current thread to wait server to be ready
+ #. :meth:`.Client.query` accepts a :class:`str` consisting of nucleotide or peptide sequences, e.g. `"ATCG"`
+ #. :meth:`.Client.query` accepts nucleotide or peptide sequences that are case-insensitive, e.g. `"AtcG"`
+ #. :meth:`.Client.query` accepts a path of fasta file, e.g. `"./test_case1.fa"`
+ #. :meth:`.Client.query` accepts a :class:`list` of :class:`str` consisting of nucleotide or peptide sequences, e.g. `["ATCG","CTGAG"]`
+ #. :meth:`.Client.query` accepts a :class:`list` of path of fasta files, e.g. `["test_case1.fa"]`
+ #. :meth:`.Client.query` accepts a :class:`list` of :class:`str` and path, e.g. `["ATCG", "test_case1.fa"]`
```
{meth}`.Client.query` accepts parameters of several types:
-1. Path of Fasta file e.g. `data/fasta1.fa`
-2. {class}`list` of {class}`str` consisting of DNA or Protein Sequences, e.g. `["ATCG","CTGAG"]`
-3. {class}`list` of path of Fasta files, e.g. `["data/fasta1.fa", "data/fasta2.fa"]`
-4. {class}`list` of `str` and path, e.g. `["ATCG", "data/fasta1.fa"]`
-5. {meth}`.Client.query` accepts a {class}`list` of {class}`str` and path, e.g. `["ATCG", "data/fasta1.fa"]`
+- Path of fasta file e.g. `./test_case1.fa`
+- {class}`str` consisting of nucleotide or peptide sequences that are case-insensitive, e.g. `ATCG`, or `ATcg`
+- {class}`list` of {class}`str` consisting of nucleotide or peptide sequences that are case-insensitive, e.g. `["AtcG","CTGAG"]`
+- {class}`list` of path of fasta files, e.g. `["data/fasta1.fa", "./test_case1.fa"]`
+- {class}`list` of `str` and path, e.g. `["ATCG", "data/fasta1.fa"]`
-{meth}`.Client.query` return [Bio.SearchIO.QueryResult](#query-result).
+Let's Create a new Python script named `query_context.py`, and copy and paste [code above](#query_context_block) to the script.
+Then execute the Python script.
+
+```bash
+$ python query_context.py
+Program: blat (v.37x1)
+ Query: case1 (151)
+
+ Target:
+ Hits: ---- ----- ----------------------------------------------------------
+ # # HSP ID + description
+ ---- ----- ----------------------------------------------------------
+ 0 1 chr1
+```
-We may need to query sequences in more general way, for example,
+{meth}`.Client.query` return [`QueryResult`](#query-result), which is introduced later.
+
+### 4.2 Start {class}`pxblat.Server` in general mode
```{eval-rst}
.. code-block:: python
+ :name: query_general_block
:linenos:
- from pxblat import Server, Client
+ from pxblat import Client, Server
- client = Client(
- host="localhost",
- port=port,
- seq_dir=two_bit,
- min_score=20,
- min_identity=90,
- wait_ready=True, # (1)!
- )
+ def query_general():
+ host = "localhost"
+ port = 65000
+ seq_dir = "."
+ two_bit = "./test_ref.2bit"
+
+ client = Client(
+ host=host,
+ port=port,
+ seq_dir=seq_dir,
+ min_score=20,
+ min_identity=90,
+ )
+
+ server = Server(host, port, two_bit, can_stop=True, step_size=5)
+ server.start() # (1)!
+ # work() assume work() is your own function that takes time to prepare something
+ server.wait_ready() # (2)!
+ result1 = client.query(["actg", "test_case1.fa"])
+ # another_work() assume the func is your own function that takes time
+ result2 = client.query("test_case1.fa")
+ server.stop() # (3)!
+
+ print(f"{result1=}")
+ print(f"{result2=}")
- server_option = Server.create_option().withCanStop(True).withStepSize(5).build()
- server = Server("localhost", port, two_bit, server_option)
- server.start()
- work() # (2)!
- # server.wait_ready()
- # (3)!
- result1 = client.query(["actg", "fasta.fa"])
- other_work()
- result2 = client.query("fasta.fa")
- server.stop() # (4)!
+
+ if __name__ == "__main__":
+ query_general()
.. code-annotations::
- #. The parameter `wait_ready` is used to control if the client need to check and wait current server to be ready or query directly.
- #. May a task consuming long time
- #. No need to wait current running server to be ready, and the job will be done by :class:`.Client`
- #. Stop the current running server
+ #. start server
+ #. block until the server is ready
+ #. stop the current running server
+```
+
+```{note}
+the explanation of parameters including `two_bit` and `seq_dir` etc. is same as
+[previous code](#query_context_block)
```
-Although {class}`.Server` and {class}`.Client` already consider most contexts, `PxBLAT` provides {class}`.ClientThread` that can launch a thread to
+Let's Create a new Python script named `query_general.py`, and copy and paste [code above](#query_general_block) to the script.
+Then execute the Python script.
+
+```bash
+$ python query_general.py
+result1=[None, QueryResult(id='case1', 1 hits)]
+result2=[QueryResult(id='case1', 1 hits)]
+```
+
+```{note}
+`None` means the sequence cannot be mapped to the reference.
+```
+
+Although {class}`.Server` and {class}`.Client` already consider most contexts, **PxBLAT** provides {class}`.ClientThread` that can launch a thread to
query sequence.
-However, {class}`.ClientThread` only query one sequence at one time.
-Its APIs are still not stable so far.
Free feel to check that if you have interests.
-## Query Result
+## 5. Query Result
+
+Right now we know how to query certain sequence to the reference, and let's dive into the query result and manipulate that together.
+
+Here we use contexts mode to align sequence, and modify a little bit based on [previous code](#query_context_block)
+
+````{example} query_context (hint: convenient to copy)
+:collapsible: close
-`PxBLAT` aims to introduce as few concepts as possible so that users do not need to take time to learn.
-The query result is the same as `QueryResult` of [Bio][Bio].
-Hence, we can manipulate the query result as shown below.
+```python
+from pxblat import Client, Server
+
+def query_context():
+ host = "localhost"
+ port = 65000
+ seq_dir = "."
+ two_bit = "./test_ref.2bit"
+ client = Client(
+ host=host,
+ port=port,
+ seq_dir=seq_dir,
+ min_score=20,
+ min_identity=90,
+ )
+ with Server(host, port, two_bit, can_stop=True, step_size=5) as server:
+ # work() assume work() is your own function that takes time to prepare something
+ server.wait_ready()
+ result1 = client.query("ATCG")
+ result2 = client.query("AtcG")
+ result3 = client.query("test_case1.fa")
+ result4 = client.query(["ATCG", "ATCG"])
+ result5 = client.query(["test_case1.fa"])
+ result6 = client.query(["cgTA", "test_case1.fa"])
+ return [result1, result2, result3, result4, result5, result6]
+```
+````
```{eval-rst}
-.. py:class:: QueryResult
-
- Class representing search results from a single query.
-
- QueryResult is the container object that stores all search hits from a
- single search query. It is the top-level object returned by SearchIO's two
- main functions, ``read`` and ``parse``. Depending on the search results and
- search output format, a QueryResult object will contain zero or more Hit
- objects (see Hit).
-
- You can take a quick look at a QueryResult's contents and attributes by
- invoking ``print`` on it::
-
- >>> from Bio import SearchIO
- >>> qresult = next(SearchIO.parse('Blast/mirna.xml', 'blast-xml'))
- >>> print(qresult)
- Program: blastn (2.2.27+)
- Query: 33211 (61)
- mir_1
- Target: refseq_rna
- Hits: ---- ----- ----------------------------------------------------------
- # # HSP ID + description
- ---- ----- ----------------------------------------------------------
- 0 1 gi|262205317|ref|NR_030195.1| Homo sapiens microRNA 52...
- 1 1 gi|301171311|ref|NR_035856.1| Pan troglodytes microRNA...
- 2 1 gi|270133242|ref|NR_032573.1| Macaca mulatta microRNA ...
- 3 2 gi|301171322|ref|NR_035857.1| Pan troglodytes microRNA...
- 4 1 gi|301171267|ref|NR_035851.1| Pan troglodytes microRNA...
- 5 2 gi|262205330|ref|NR_030198.1| Homo sapiens microRNA 52...
- 6 1 gi|262205302|ref|NR_030191.1| Homo sapiens microRNA 51...
- 7 1 gi|301171259|ref|NR_035850.1| Pan troglodytes microRNA...
- 8 1 gi|262205451|ref|NR_030222.1| Homo sapiens microRNA 51...
- 9 2 gi|301171447|ref|NR_035871.1| Pan troglodytes microRNA...
- 10 1 gi|301171276|ref|NR_035852.1| Pan troglodytes microRNA...
- 11 1 gi|262205290|ref|NR_030188.1| Homo sapiens microRNA 51...
- ...
-
- If you just want to know how many hits a QueryResult has, you can invoke
- ``len`` on it. Alternatively, you can simply type its name in the interpreter::
-
- >>> len(qresult)
- 100
- >>> qresult
- QueryResult(id='33211', 100 hits)
-
- QueryResult behaves like a hybrid of Python's built-in list and dictionary.
- You can retrieve its items (Hit objects) using the integer index of the
- item, just like regular Python lists::
-
- >>> first_hit = qresult[0]
- >>> first_hit
- Hit(id='gi|262205317|ref|NR_030195.1|', query_id='33211', 1 hsps)
-
- You can slice QueryResult objects as well. Slicing will return a new
- QueryResult object containing only the sliced hits::
-
- >>> sliced_qresult = qresult[:3] # slice the first three hits
- >>> len(qresult)
- 100
- >>> len(sliced_qresult)
- 3
- >>> print(sliced_qresult)
- Program: blastn (2.2.27+)
- Query: 33211 (61)
- mir_1
- Target: refseq_rna
- Hits: ---- ----- ----------------------------------------------------------
- # # HSP ID + description
- ---- ----- ----------------------------------------------------------
- 0 1 gi|262205317|ref|NR_030195.1| Homo sapiens microRNA 52...
- 1 1 gi|301171311|ref|NR_035856.1| Pan troglodytes microRNA...
- 2 1 gi|270133242|ref|NR_032573.1| Macaca mulatta microRNA ...
-
- Like Python dictionaries, you can also retrieve hits using the hit's ID.
- This is useful for retrieving hits that you know should exist in a given
- search::
-
- >>> hit = qresult['gi|262205317|ref|NR_030195.1|']
- >>> hit
- Hit(id='gi|262205317|ref|NR_030195.1|', query_id='33211', 1 hsps)
-
- You can also replace a Hit in QueryResult with another Hit using either the
- integer index or hit key string. Note that the replacing object must be a
- Hit that has the same ``query_id`` property as the QueryResult object.
-
- If you're not sure whether a QueryResult contains a particular hit, you can
- use the hit ID to check for membership first::
-
- >>> 'gi|262205317|ref|NR_030195.1|' in qresult
- True
- >>> 'gi|262380031|ref|NR_023426.1|' in qresult
- False
-
- Or, if you just want to know the rank / position of a given hit, you can
- use the hit ID as an argument for the ``index`` method. Note that the values
- returned will be zero-based. So zero (0) means the hit is the first in the
- QueryResult, three (3) means the hit is the fourth item, and so on. If the
- hit does not exist in the QueryResult, a ``ValueError`` will be raised.
-
- >>> qresult.index('gi|262205317|ref|NR_030195.1|')
- 0
- >>> qresult.index('gi|262205330|ref|NR_030198.1|')
- 5
- >>> qresult.index('gi|262380031|ref|NR_023426.1|')
- Traceback (most recent call last):
- ...
- ValueError: ...
-
- To ease working with a large number of hits, QueryResult has several
- ``filter`` and ``map`` methods, analogous to Python's built-in functions with
- the same names. There are ``filter`` and ``map`` methods available for
- operations over both Hit objects or HSP objects. As an example, here we are
- using the ``hit_map`` method to rename all hit IDs within a QueryResult::
-
- >>> def renamer(hit):
- ... hit.id = hit.id.split('|')[3]
- ... return hit
- >>> mapped_qresult = qresult.hit_map(renamer)
- >>> print(mapped_qresult)
- Program: blastn (2.2.27+)
- Query: 33211 (61)
- mir_1
- Target: refseq_rna
- Hits: ---- ----- ----------------------------------------------------------
- # # HSP ID + description
- ---- ----- ----------------------------------------------------------
- 0 1 NR_030195.1 Homo sapiens microRNA 520b (MIR520B), micr...
- 1 1 NR_035856.1 Pan troglodytes microRNA mir-520b (MIR520B...
- 2 1 NR_032573.1 Macaca mulatta microRNA mir-519a (MIR519A)...
- ...
-
- The principle for other ``map`` and ``filter`` methods are similar: they accept
- a function, applies it, and returns a new QueryResult object.
-
- There are also other methods useful for working with list-like objects:
- ``append``, ``pop``, and ``sort``. More details and examples are available in
- their respective documentations.
-
- Finally, just like Python lists and dictionaries, QueryResult objects are
- iterable. Iteration over QueryResults will yield Hit objects::
-
- >>> for hit in qresult[:4]: # iterate over the first four items
- ... hit
- ...
- Hit(id='gi|262205317|ref|NR_030195.1|', query_id='33211', 1 hsps)
- Hit(id='gi|301171311|ref|NR_035856.1|', query_id='33211', 1 hsps)
- Hit(id='gi|270133242|ref|NR_032573.1|', query_id='33211', 1 hsps)
- Hit(id='gi|301171322|ref|NR_035857.1|', query_id='33211', 2 hsps)
-
- If you need access to all the hits in a QueryResult object, you can get
- them in a list using the ``hits`` property. Similarly, access to all hit IDs is
- available through the ``hit_keys`` property.
-
- >>> qresult.hits
- [Hit(id='gi|262205317|ref|NR_030195.1|', query_id='33211', 1 hsps), ...]
- >>> qresult.hit_keys
- ['gi|262205317|ref|NR_030195.1|', 'gi|301171311|ref|NR_035856.1|', ...]
+
+.. code-block:: pycon
+ :name: query_result_block
+
+ >>> from pxblat import Client, Server
+ >>> def query_context():
+ ... host = "localhost"
+ ... port = 65000
+ ... seq_dir = "."
+ ... two_bit = "./test_ref.2bit"
+ ... client = Client(
+ ... host=host,
+ ... port=port,
+ ... seq_dir=seq_dir,
+ ... min_score=20,
+ ... min_identity=90,
+ ... )
+ ... with Server(host, port, two_bit, can_stop=True, step_size=5) as server:
+ ... # work() assume work() is your own function that takes time to prepare something
+ ... server.wait_ready()
+ ... result1 = client.query("ATCG")
+ ... result2 = client.query("AtcG")
+ ... result3 = client.query("test_case1.fa")
+ ... result4 = client.query(["ATCG", "ATCG"])
+ ... result5 = client.query(["test_case1.fa"])
+ ... result6 = client.query(["cgTA", "test_case1.fa"])
+ ... return [result1, result2, result3, result4, result5, result6]
+ >>> results = query_context() # get all alignment results
+ >>> results
+ [[None], [None], [QueryResult(id='case1', 1 hits)], [None, None], [QueryResult(id='case1', 1 hits)], [None, QueryResult(id='case1', 1 hits)]]
+ >>> results[2] # let's pick up the result of test_case1.fa
+ [QueryResult(id='case1', 1 hits)]
+ >>> result3 = results[2]
+ >>> result3
+ [QueryResult(id='case1', 1 hits)]
+ >>> print(result3[0]) # let's show information of the result
+ Program: blat (v.37x1)
+ Query: case1 (151)
+
+ Target:
+ Hits: ---- ----- ----------------------------------------------------------
+ # # HSP ID + description
+ ---- ----- ----------------------------------------------------------
+ 0 1 chr1
+ >>> result = result3[0] # let's get the element in the result list
+ >>> result
+ QueryResult(id='case1', 1 hits)
+ >>> print(result)
+ Program: blat (v.37x1)
+ Query: case1 (151)
+
+ Target:
+ Hits: ---- ----- ----------------------------------------------------------
+ # # HSP ID + description
+ ---- ----- ----------------------------------------------------------
+ 0 1 chr1
+ >>> result.hsps # check all high-scoring pairs (HSPs)
+ [HSP(hit_id='chr1', query_id='case1', 1 fragments)]
+ >>> result[0] # check the top hsp
+ Hit(id='chr1', query_id='case1', 1 hsps)
+ >>> print(result[0]) # show more information about top hsp
+ Query: case1
+
+ Hit: chr1 (14999)
+
+ HSPs: ---- -------- --------- ------ --------------- ---------------------
+ # E-value Bit score Span Query range Hit range
+ ---- -------- --------- ------ --------------- ---------------------
+ 0 ? ? ? [0:151] [12699:12850]
+ >>> top_hsp = result.hsps[0]
+ >>> top_hsp
+ HSP(hit_id='chr1', query_id='case1', 1 fragments)
+ >>> print(top_hsp)
+ Query: case1
+ Hit: chr1
+ Query range: [0:151] (1)
+ Hit range: [12699:12850] (1)
+ Quick stats: evalue ?; bitscore ?
+ Fragments: 1 (? columns)
+ >>> top_hsp.query_id # test_case1's id in top_hsp
+ 'case1'
+ >>> top_hsp.query_range # test_case1's query_range in top_hsp
+ (0, 151)
+ >>> top_hsp.query_span # test_case1's query_span in top_hsp
+ 151
+ >>> top_hsp.query_start # test_case1's query_start in top_hsp
+ 0
+ >>> top_hsp.query_strand # test_case1's query_strand in top_hsp
+ 1
+ >>> top_hsp.hit_id # in top_hsp, test_case1 hit `chr1` of the reference
+ 'chr1'
+ >>> top_hsp.hit_range # in top_hsp, test_case1 hit (12699, 12850) of the reference
+ (12699, 12850)
+ >>> top_hsp.hit_start
+ 12699
+ >>> top_hsp.hit_strand # in top_hsp, test_case1 hit strand of the reference (1 means positive strand)
+ 1
+ >>> top_hsp.
+ top_hsp.aln top_hsp.hit_frame top_hsp.ident_pct top_hsp.query_frame_all
+ top_hsp.aln_all top_hsp.hit_frame_all top_hsp.is_fragmented top_hsp.query_gap_num
+ top_hsp.aln_annotation top_hsp.hit_gap_num top_hsp.match_num top_hsp.query_gapopen_num
+ top_hsp.aln_annotation_all top_hsp.hit_gapopen_num top_hsp.match_rep_num top_hsp.query_id
+ top_hsp.aln_span top_hsp.hit_id top_hsp.mismatch_num top_hsp.query_inter_ranges
+ top_hsp.fragment top_hsp.hit_inter_ranges top_hsp.molecule_type top_hsp.query_inter_spans
+ top_hsp.fragments top_hsp.hit_inter_spans top_hsp.n_num top_hsp.query_is_protein
+ top_hsp.gap_num top_hsp.hit_range top_hsp.output_index top_hsp.query_range
+ top_hsp.gapopen_num top_hsp.hit_range_all top_hsp.query top_hsp.query_range_all
+ top_hsp.hit top_hsp.hit_span top_hsp.query_all top_hsp.query_span
+ top_hsp.hit_all top_hsp.hit_span_all top_hsp.query_description top_hsp.query_span_all
+ top_hsp.hit_description top_hsp.hit_start top_hsp.query_end top_hsp.query_start
+ top_hsp.hit_end top_hsp.hit_start_all top_hsp.query_end_all top_hsp.query_start_all
+ top_hsp.hit_end_all top_hsp.hit_strand top_hsp.query_features top_hsp.query_strand
+ top_hsp.hit_features top_hsp.hit_strand_all top_hsp.query_features_all top_hsp.query_strand_all
+ top_hsp.hit_features_all top_hsp.ident_num top_hsp.query_frame top_hsp.score
+
+```
+
+````{example} query_result code
+:collapsible: close
+
+```{literalinclude} tutorial_data/query_result.py
+```
+
+````
+
+We can precisely determine the regions of our sequence that align with specific parts of the reference.
+We are able to know strand, start position, and end position for alignment part
+both for our sequence and the reference.
+The last part of [code example](#query_result_block) shows all methods of a high-scoring pairs (HSP).
+
+## 6. APIs Compared to `BLAT`
+
+So far, **PxBLAT** provides APIs, including {class}`.Client`, {class}`.Server`, {func}`.two_bit_to_fa` and {func}`.fa_to_two_bit`,
+as well as other useful functions ({doc}`reference`).
+**PxBLAT** is able to finish the most significant features of `BLAT`.
+Here is a table in which the features are compared.
+
+```{list-table} APIs Comparison
+ :header-rows: 1
+ :align: center
+
+ * - PxBLAT
+ - BLAT
+ * - {class}`.Client`
+ - [gfClient][gfClient]
+ * - {class}`.Server`
+ - [gfServer][gfServer]
+ * - {func}`.two_bit_to_fa`
+ - [twoBitToFa][twoBitToFa]
+ * - {func}`.fa_to_two_bit`
+ - [faToTwoBit][faToTwoBit]
```
-## Beyond APIs
+## 7. Beyond APIs
Even though `PxBLAT` is designed as library, it provides command-line tools
using its APIs.
That could provide more choices for user according to different situations.
{doc}`reference` contain more details, and do not hesitate to check.
-## Caveats
+```{bug}
+please feel free to [edit the tutorial](https://github.com/ylab-hi/pxblat/edit/main/docs/tutorial.md) or [open an issue](https://github.com/ylab-hi/pxblat/issues/new/choose), if you find some unclear or wrong statement.
-{class}`.ServerOption` hold most important parameters that are passed to {class}`.Server`.
+```
diff --git a/docs/tutorial_data/2bit.py b/docs/tutorial_data/2bit.py
new file mode 100644
index 00000000..0df0235a
--- /dev/null
+++ b/docs/tutorial_data/2bit.py
@@ -0,0 +1,11 @@
+from pxblat import fa_to_two_bit
+
+fa_to_two_bit(
+ ["test_ref.fa"],
+ "test_ref.2bit",
+ noMask=False,
+ stripVersion=False,
+ ignoreDups=False,
+ useLong=False,
+)
+print("Done")
diff --git a/docs/tutorial_data/query_context.py b/docs/tutorial_data/query_context.py
new file mode 100644
index 00000000..f1cecda7
--- /dev/null
+++ b/docs/tutorial_data/query_context.py
@@ -0,0 +1,40 @@
+from pxblat import Client, Server
+
+
+def query_context():
+ host = "localhost"
+ port = 65000
+ seq_dir = "."
+ two_bit = "./test_ref.2bit"
+
+ client = Client(
+ host=host,
+ port=port,
+ seq_dir=seq_dir,
+ min_score=20,
+ min_identity=90,
+ )
+
+ with Server(host, port, two_bit, can_stop=True, step_size=5) as server:
+ # work() assume work() is your own function that takes time to prepare something
+ server.wait_ready()
+ result1 = client.query("ATCG")
+ result2 = client.query("AtcG")
+ result3 = client.query("test_case1.fa")
+ result4 = client.query(["ATCG", "ATCG"])
+ result5 = client.query(["test_case1.fa"])
+ result6 = client.query(["cgTA", "test_case1.fa"])
+ # print results
+ print(f"{result1=}")
+ print(f"{result2=}")
+ print(f"{result3=}")
+ print(f"{result3[0]=}")
+ print(f"{result3[0]=!s}")
+ print(f"{result4=}")
+ print(f"{result4[0]=}")
+ print(f"{result5=}")
+ print(f"{result6=}")
+
+
+if __name__ == "__main__":
+ query_context()
diff --git a/docs/tutorial_data/query_general.py b/docs/tutorial_data/query_general.py
new file mode 100644
index 00000000..0a727797
--- /dev/null
+++ b/docs/tutorial_data/query_general.py
@@ -0,0 +1,32 @@
+from pxblat import Client, Server
+
+
+def query_general():
+ host = "localhost"
+ port = 65000
+ seq_dir = "."
+ two_bit = "./test_ref.2bit"
+
+ client = Client(
+ host=host,
+ port=port,
+ seq_dir=seq_dir,
+ min_score=20,
+ min_identity=90,
+ )
+
+ server = Server(host, port, two_bit, can_stop=True, step_size=5)
+ server.start()
+ # work() assume work() is your own function that takes time to prepare something
+ server.wait_ready()
+ result1 = client.query(["actg", "test_case1.fa"])
+ # another_work() assume the func is your own function that takes time
+ result2 = client.query("test_case1.fa")
+ server.stop()
+
+ print(f"{result1=}")
+ print(f"{result2=}")
+
+
+if __name__ == "__main__":
+ query_general()
diff --git a/docs/tutorial_data/query_result.py b/docs/tutorial_data/query_result.py
new file mode 100644
index 00000000..d4b83f84
--- /dev/null
+++ b/docs/tutorial_data/query_result.py
@@ -0,0 +1,72 @@
+from pxblat import Client, Server
+
+
+def query_context():
+ host = "localhost"
+ port = 65000
+ seq_dir = "."
+ two_bit = "./test_ref.2bit"
+ client = Client(
+ host=host,
+ port=port,
+ seq_dir=seq_dir,
+ min_score=20,
+ min_identity=90,
+ )
+ with Server(host, port, two_bit, can_stop=True, step_size=5) as server:
+ # work() assume work() is your own function that takes time to prepare something
+ server.wait_ready()
+ result1 = client.query("ATCG")
+ result2 = client.query("AtcG")
+ result3 = client.query("test_case1.fa")
+ result4 = client.query(["ATCG", "ATCG"])
+ result5 = client.query(["test_case1.fa"])
+ result6 = client.query(["cgTA", "test_case1.fa"])
+ return [result1, result2, result3, result4, result5, result6]
+
+
+def query_result():
+ results = query_context() # get all alignment results
+ results[2] # let's pick up the result of test_case1.fa
+ result3 = results[2]
+ print(result3)
+ print(result3[0]) # let's show information of the result
+ result = result3[0] # let's get the element in the result list
+ print(result)
+ print(result)
+ print(result.hsps) # check all high-scoring pairs (HSPs)
+ print(result[0]) # check the top hsp
+ print(result[0]) # show more information about top hsp
+ top_hsp = result.hsps[0]
+ print(top_hsp)
+ print(top_hsp)
+ print(top_hsp.query_id) # test_case1's id in top_hsp
+ print(top_hsp.query_range) # test_case1's query_range in top_hsp
+ print(top_hsp.query_span) # test_case1's query_span in top_hsp
+ print(top_hsp.query_start) # test_case1's query_start in top_hsp
+ print(top_hsp.query_strand) # test_case1's query_strand in top_hsp
+ print(top_hsp.hit_id) # in top_hsp, test_case1 hit `chr1` of the reference
+ print(top_hsp.hit_range) # in top_hsp, test_case1 hit (12699, 12850) of the reference
+ print(top_hsp.hit_start)
+ print(top_hsp.hit_strand) # in top_hsp, test_case1 hit strand of the reference (1 means positive strand)
+ # >>> top_hsp.
+ # top_hsp.aln top_hsp.hit_frame top_hsp.ident_pct top_hsp.query_frame_all
+ # top_hsp.aln_all top_hsp.hit_frame_all top_hsp.is_fragmented top_hsp.query_gap_num
+ # top_hsp.aln_annotation top_hsp.hit_gap_num top_hsp.match_num top_hsp.query_gapopen_num
+ # top_hsp.aln_annotation_all top_hsp.hit_gapopen_num top_hsp.match_rep_num top_hsp.query_id
+ # top_hsp.aln_span top_hsp.hit_id top_hsp.mismatch_num top_hsp.query_inter_ranges
+ # top_hsp.fragment top_hsp.hit_inter_ranges top_hsp.molecule_type top_hsp.query_inter_spans
+ # top_hsp.fragments top_hsp.hit_inter_spans top_hsp.n_num top_hsp.query_is_protein
+ # top_hsp.gap_num top_hsp.hit_range top_hsp.output_index top_hsp.query_range
+ # top_hsp.gapopen_num top_hsp.hit_range_all top_hsp.query top_hsp.query_range_all
+ # top_hsp.hit top_hsp.hit_span top_hsp.query_all top_hsp.query_span
+ # top_hsp.hit_all top_hsp.hit_span_all top_hsp.query_description top_hsp.query_span_all
+ # top_hsp.hit_description top_hsp.hit_start top_hsp.query_end top_hsp.query_start
+ # top_hsp.hit_end top_hsp.hit_start_all top_hsp.query_end_all top_hsp.query_start_all
+ # top_hsp.hit_end_all top_hsp.hit_strand top_hsp.query_features top_hsp.query_strand
+ # top_hsp.hit_features top_hsp.hit_strand_all top_hsp.query_features_all top_hsp.query_strand_all
+ # top_hsp.hit_features_all top_hsp.ident_num top_hsp.query_frame top_hsp.score
+
+
+if __name__ == "__main__":
+ query_result()
diff --git a/docs/tutorial_data/test_case1.fa b/docs/tutorial_data/test_case1.fa
new file mode 100644
index 00000000..7f77c66c
--- /dev/null
+++ b/docs/tutorial_data/test_case1.fa
@@ -0,0 +1,5 @@
+>case1
+TGAGAGGCATCTGGCCCTCCCTGCGCTGTGCCAGCAGCTTGGAGAACCCA
+CACTCAATGAACGCAGCACTCCACTACCCAGGAAATGCCTTCCTGCCCTC
+TCCTCATCCCATCCCTGGGCAGGGGACATGCAACTGTCTACAAGGTGCCA
+A
diff --git a/docs/tutorial_data/test_ref.2bit b/docs/tutorial_data/test_ref.2bit
new file mode 100644
index 00000000..f819b533
Binary files /dev/null and b/docs/tutorial_data/test_ref.2bit differ
diff --git a/docs/tutorial_data/test_ref.fa b/docs/tutorial_data/test_ref.fa
new file mode 100644
index 00000000..d221cbac
--- /dev/null
+++ b/docs/tutorial_data/test_ref.fa
@@ -0,0 +1,301 @@
+>chr1
+NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
+taaccctaaccctaaccctaaccctaaccctaaccctaaccctaacccta
+accctaaccctaaccctaaccctaaccctaaccctaaccctaaccctaac
+cctaacccaaccctaaccctaaccctaaccctaaccctaaccctaacccc
+taaccctaaccctaaccctaaccctaacctaaccctaaccctaaccctaa
+ccctaaccctaaccctaaccctaaccctaacccctaaccctaaccctaaa
+ccctaaaccctaaccctaaccctaaccctaaccctaaccccaaccccaac
+cccaaccccaaccccaaccccaaccctaacccctaaccctaaccctaacc
+ctaccctaaccctaaccctaaccctaaccctaaccctaacccctaacccc
+taaccctaaccctaaccctaaccctaaccctaaccctaacccctaaccct
+aaccctaaccctaaccctcgcggtaccctcagccggcccgcccgcccggg
+tctgacctgaggagaactgtgctccgccttcagagtaccaccgaaatctg
+tgcagaggacaacgcagctccgccctcgcggtgctctccgggtctgtgct
+gaggagaacgcaactccgccgttgcaaaggcgcgccgcgccggcgcaggc
+gcagagaggcgcgccgcgccggcgcaggcgcagagaggcgcgccgcgccg
+gcgcaggcgcagagaggcgcgccgcgccggcgcaggcgcagagaggcgcg
+ccgcgccggcgcaggcgcagagaggcgcgccgcgccggcgcaggcgcaga
+cacatgctagcgcgtcggggtggaggcgtggcgcaggcgcagagaggcgc
+gccgcgccggcgcaggcgcagagacacatgctaccgcgtccaggggtgga
+ggcgtggcgcaggcgcagagaggcgcaccgcgccggcgcaggcgcagaga
+cacatgctagcgcgtccaggggtggaggcgtggcgcaggcgcagagacgc
+aagcctacgggcgggggttgggggggcgtgtgttgcaggagcaaagtcgc
+acggcgccgggctggggcggggggagggtggcgccgtgcacgcgcagaaa
+ctcacgtcacggtggcgcggcgcagagacgggtagaacctcagtaatccg
+aaaagccgggatcgaccgccccttgcttgcagccgggcactacaggaccc
+gcttgctcacggtgctgtgccagggcgccccctgctggcgactagggcaa
+ctgcagggctctcttgcttagagtggtggccagcgccccctgctggcgcc
+ggggcactgcagggccctcttgcttactgtatagtggtggcacgccgcct
+gctggcagctagggacattgcagggtcctcttgctcaaggtgtagtggca
+gcacgcccacctgctggcagctggggacactgccgggccctcttgctCCA
+ACAGTACTGGCGGATTATAGGGAAACACCCGGAGCATATGCTGTTTGGTC
+TCAGtagactcctaaatatgggattcctgggtttaaaagtaaaaaataaa
+tatgtttaatttgtgaactgattaccatcagaattgtactgttctgtatc
+ccaccagcaatgtctaggaatgcctgtttctccacaaagtgtttactttt
+ggatttttgccagtctaacaggtgaAGccctggagattcttattagtgat
+ttgggctggggcctggccatgtgtatttttttaaatttccactgatgatt
+ttgctgcatggccggtgttgagaatgactgCGCAAATTTGCCGGATTTCC
+TTTGCTGTTCCTGCATGTAGTTTAAACGAGATTGCCAGCACCGGGTATCA
+TTCACCATTTTTCTTTTCGTTAACTTGCCGTCAGCCTTTTCTTTGACCTC
+TTCTTTCTGTTCATGTGTATTTGCTGTCTCTTAGCCCAGACTTCCCGTGT
+CCTTTCCACCGGGCCTTTGAGAGGTCACAGGGTCTTGATGCTGTGGTCTT
+CATCTGCAGGTGTCTGACTTCCAGCAACTGCTGGCCTGTGCCAGGGTGCA
+AGCTGAGCACTGGAGTGGAGTTTTCCTGTGGAGAGGAGCCATGCCTAGAG
+TGGGATGGGCCATTGTTCATCTTCTGGCCCCTGTTGTCTGCATGTAACTT
+AATACCACAACCAGGCATAGGGGAAAGATTGGAGGAAAGATGAGTGAGAG
+CATCAACTTCTCTCACAACCTAGGCCAGTAAGTAGTGCTTGTGCTCATCT
+CCTTGGCTGTGATACGTGGCCGGCCCTCGCTCCAGCAGCTGGACCCCTAC
+CTGCCGTCTGCTGCCATCGGAGCCCAAAGCCGGGCTGTGACTGCTCAGAC
+CAGCCGGCTGGAGGGAGGGGCTCAGCAGGTCTGGCTTTGGCCCTGGGAGA
+GCAGGTGGAAGATCAGGCAGGCCATCGCTGCCACAGAACCCAGTGGATTG
+GCCTAGGTGGGATCTCTGAGCTCAACAAGCCCTCTCTGGGTGGTAGGTGC
+AGAGACGGGAGGGGCAGAGCCGCAGGCACAGCCAAGAGGGCTGAAGAAAT
+GGTAGAACGGAGCAGCTGGTGATGTGTGGGCCCACCGGCCCCAGGCTCCT
+GTCTCCCCCCAGGTGTGTGGTGATGCCAGGCATGCCCTTCCCCAGCATCA
+GGTCTCCAGAGCTGCAGAAGACGACGGCCGACTTGGATCACACTCTTGTG
+AGTGTCCCCAGTGTTGCAGAGGTGAGAGGAGAGTAGACAGTGAGTGGGAG
+TGGCGTCGCCCCTAGGGCTCTACGGGGCCGGCGTCTCCTGTCTCCTGGAG
+AGGCTTCGATGCCCCTCCACACCCTCTTGATCTTCCCTGTGATGTCATCT
+GGAGCCCTGCTGCTTGCGGTGGCCTATAAAGCCTCCTAGTCTGGCTCCAA
+GGCCTGGCAGAGTCTTTCCCAGGGAAAGCTACAAGCAGCAAACAGTCTGC
+ATGGGTCATCCCCTTCACTCCCAGCTCAGAGCCCAGGCCAGGGGCCCCCA
+AGAAAGGCTCTGGTGGAGAACCTGTGCATGAAGGCTGTCAACCAGTCCAT
+AGGCAAGCCTGGCTGCCTCCAGCTGGGTCGACAGACAGGGGCTGGAGAAG
+GGGAGAAGAGGAAAGTGAGGTTGCCTGCCCTGTCTCCTACCTGAGGCTGA
+GGAAGGAGAAGGGGATGCACTGTTGGGGAGGCAGCTGTAACTCAAAGCCT
+TAGCCTCTGTTCCCACGAAGGCAGGGCCATCAGGCACCAAAGGGATTCTG
+CCAGCATAGTGCTCCTGGACCAGTGATACACCCGGCACCCTGTCCTGGAC
+ACGCTGTTGGCCTGGATCTGAGCCCTGGTGGAGGTCAAAGCCACCTTTGG
+TTCTGCCATTGCTGCTGTGTGGAAGTTCACTCCTGCCTTTTCCTTTCCCT
+AGAGCCTCCACCACCCCGAGATCACATTTCTCACTGCCTTTTGTCTGCCC
+AGTTTCACCAGAAGTAGGCCTCTTCCTGACAGGCAGCTGCACCACTGCCT
+GGCGCTGTGCCCTTCCTTTGCTCTGCCCGCTGGAGACGGTGTTTGTCATG
+GGCCTGGTCTGCAGGGATCCTGCTACAAAGGTGAAACCCAGGAGAGTGTG
+GAGTCCAGAGTGTTGCCAGGACCCAGGCACAGGCATTAGTGCCCGTTGGA
+GAAAACAGGGGAATCCCGAAGAAATGGTGGGTCCTGGCCATCCGTGAGAT
+CTTCCCAGGGCAGCTCCCCTCTGTGGAATCCAATCTGTCTTCCATCCTGC
+GTGGCCGAGGGCCAGGCTTCTCACTGGGCCTCTGCAGGAGGCTGCCATTT
+GTCCTGCCCACCTTCTTAGAAGCGAGACGGAGCAGACCCATCTGCTACTG
+CCCTTTCTATAATAACTAAAGTTAGCTGCCCTGGACTATTCACCCCCTAG
+TCTCAATTTAAGAAGATCCCCATGGCCACAGGGCCCCTGCCTGGGGGCTT
+GTCACCTCCCCCACCTTCTTCCTGAGTCATTCCTGCAGCCTTGCTCCCTA
+ACCTGCCCCACAGCCTTGCCTGGATTTCTATCTCCCTGGCTTGGTGCCAG
+TTCCTCCAAGTCGATGGCACCTCCCTCCCTCTCAACCACTTGAGCAAACT
+CCAAGACATCTTCTACCCCAACACCAGCAATTGTGCCAAGGGCCATTAGG
+CTCTCAGCATGACTATTTTTAGAGACCCCGTGTCTGTCACTGAAACCTTT
+TTTGTGGGAGACTATTCCTCCCATCTGCAACAGCTGCCCCTGCTGACTGC
+CCTTCTCTCCTCCCTCTCATCCCAGAGAAACAGGTCAGCTGGGAGCTTCT
+GCCCCCACTGCCTAGGGACCAACAGGGGCAGGAGGCAGTCACTGACCCCG
+AGACGTTTGCATCCTGCACAGCTAGAGATCCTTTATTAAAAGCACACTGT
+TGGTTTCTGCTCAGTTCTTTATTGATTGGTGTGCCGTTTTCTCTGGAAGC
+CTCTTAAGAACACAGTGGCGCAGGCTGGGTGGAGCCGTCCCCCCATGGAG
+CACAGGCAGACAGAAGTCCCCGCCCCAGCTGTGTGGCCTCAAGCCAGCCT
+TCCGCTCCTTGAAGCTGGTCTCCACACAGTGCTGGTTCCGTCACCCCCTC
+CCAAGGAAGTAGGTCTGAGCAGCTTGTCCTGGCTGTGTCCATGTCAGAGC
+AACGGCCCAAGTCTGGGTCTGGGGGGGAAGGTGTCATGGAGCCCCCTACG
+ATTCCCAGTCGTCCTCGTCCTCCTCTGCCTGTGGCTGCTGCGGTGGCGGC
+AGAGGAGGGATGGAGTCTGACACGCGGGCAAAGGCTCCTCCGGGCCCCTC
+ACCAGCCCCAGGTCCTTTCCCAGAGATGCCTGGAGGGAAAAGGCTGAGTG
+AGGGTGGTTGGTGGGAAACCCTGGTTCCCCCAGCCCCCGGAGACTTAAAT
+ACAGGAAGAAAAAGGCAGGACAGAATTACAAGGTGCTGGCCCAGGGCGGG
+CAGCGGCCCTGCCTCCTACCCTTGCGCCTCATGACCAGCTTGTTGAAGAG
+ATCCGACATCAAGTGCCCACCTTGGCTCGTGGCTCTCACTGCAACGGGAA
+AGCCACAGACTGGGGTGAAGAGTTCAGTCACATGCGACCGGTGACTCCCT
+GTCCCCACCCCCATGACACTCCCCAGCCCTCCAAGGCCACTGTGTTTCCC
+AGTTAGCTCAGAGCCTCAGTCGATCCCTGACCCAGCACCGGGCACTGATG
+AGACAGCGGCTGTTTGAGGAGCCACCTCCCAGCCACCTCGGGGCCAGGGC
+CAGGGTGTGCAGCAccactgtacaatggggaaactggcccagagaggtga
+ggcagcttgcctggggtcacagagcaaggcaaaagcagcgctgggtacaa
+gctcaAAACCATAGTGCCCAGGGCACTGCCGCTGCAGGCGCAGGCATCGC
+ATCACACCAGTGTCTGCGTTCACAGCAGGCATCATCAGTAGCCTCCAGAG
+GCCTCAGGTCCAGTCTCTAAAAATATCTCAGGAGGCTGCAGTGGCTGACC
+ATTGCCTTGGACCGCTCTTGGCAGTCGAAGAAGATTCTCCTGTCAGTTTG
+AGCTGGGTGAGCTTAGAGAGGAAAGCTCCACTATGGCTCCCAAACCAGGA
+AGGAGCCATAGCCCAGGCAGGAGGGCTGAGGACCTCTGGTGGCGGCCCAG
+GGCTTCCAGCATGTGCCCTAGGGGAAGCAGGGGCCAGCTGGCAAGAGCAG
+GGGGTGGGCAGAAAGCACCCGGTGGACTCAGGGCTGGAGGGGAGGAGGCG
+ATCTTGCCCAAGGCCCTCCGACTGCAAGCTCCAGGGCCCGCTCACCTtgc
+tcctgctccttctgctgctgcttctccagctttcgctccttcatgctgcG
+CAGCTTGGCCTTGCCGATGCCCCCAGCTTGGCGGATGGACTCTAGCAGAG
+TGGCCAGCCACCGGAGGGGTCAACCACTTCCCTGGGAGCTCCCTGGACTG
+GAGCCGGGAGGTGGGGAACAGGGCAAGGAGGAAAGGCTGCTCAGGCAGGG
+CTGGGGAAGCTTACTGTGTCCAAGAGCCTGCTGGGAGGGAAGTCACCTCC
+CCTCAAACGAGGAGCCCTGCGCTGGGGAGGCCGGACCTTTGGAGACTGTG
+TGTGGGGGCCTGGGCACTGACTTCTGCAACCACCTGAGCGCGGGCATCCT
+GTGTGCAGATACTCCCTGCTTCCTCTCTAGCCCCCACCCTGCAGAGCTGG
+ACCCCTGAGCTAGCCATGCTCTGACAGTCTCAGTTGCACACACGAGCCAG
+CAGAGGGGTTTTGTGCCACTTCTGGATGCTAGGGTTACACTGGGAGACAC
+AGCAGTGAAGCTGAAATGAAAAATGTGTTGCTGTAGTTTGTTATTAGACC
+CCTTCTTTCCATTGGTTTAATTAGGAATGGGGAACCCAGAGCCTCACTTG
+TTCAGGCTCCCTCTGCCCTAGAAGTGAGAAGTCCAGAGCTCTACAGTTTG
+AAAACCACTATTTTATGAACCAAGTAGAACAAGATATTTGAAATGGAAAC
+TATTCAAAAAATTGAGAATTTCTGACCACTTAACAAACCCACAGAAAATC
+CACCCGAGTGCACTGAGCACGCCAGAAATCAGGTGGCCTCAAAGAGCTGC
+TCCCACCTGAAGGAGACGCGCTGCTGCTGCTGTCGTCCTGCCTGGCGCCT
+TGGCCTACAGGGGCCGCGGTTGAGGGTGGGAGTGGGGGTGCACTGGCCAG
+CACCTCAGGAGCtgggggtggtggtgggggcggtgggggtggtgTTAGTA
+CCCCATCTTGTAGGTCTGAAACACAAAGTGTGGGGTGTCTAGGGAAGAAG
+GTGTGTGACCAGGGAGGTCCCCGGCCCAGCTCCCATCCCAGAACCCAGCT
+CACCTACCTTGAGAGGCTCGGCTACCTCAGTGTGGAAGGTGGGCAGTTCT
+GGAATGGTGCCAGGGGCAGAGGGGGCAATGCCGGGGCCCAGGTCGGCAAT
+GTACATGAGGTCGTTGGCAATGCCGGGCAGGTCAGGCAGGTAGGATGGAA
+CATCAATCTCAGGCACCTGGCCCAGGTCTGGCACATAGAAGTAGTTCTCT
+GGGACCTGCAAGATTAGGCAGGGACATGTGAGAGGTGACAGGGACCTGCA
+GGGGCAGCCAACAAGACCTTGTGTGCACCTCCCATGGGTGGAATAAGGGG
+CCCAACAGCCTTGACTGGAGAGGAGCTCTGGCAAGGCCCTGGGCCACTGC
+ACCTGTCTCCACCTCTGTCCCACCCCTCCCACCTGCTGTTCCAGCTGCTC
+TCTCTTGCTGATGGACAAGGGGGCATCAAACAGCTTCTCCTCTGTCTCTG
+CCCCCAGCATCACATGGGTCTTTGTTACAGCACCAGCCAGGGGGTCCAGG
+AAGACATACTTCTTCTACCTACAGAGGCGACATGGGGGTCAGGCAAGCTG
+ACACCCGCTGTCCTGAGCCCATGTTCCTCTCCCACATCATCAGGGGCACA
+GCGTGCACTGTGGGGTCCCAGGCCTCCCGAGCCGAGCCACCCGTCACCCC
+CTGGCTCCTGGCCTATGTGCTGTACCTGTGTCTGATGCCCTGGGTCCCCA
+CTAAGCCAGGCCGGGCCTCCCGCCCACACCCCTCGGCCCTGCCCTCTGGC
+CATACAGGTTCTCGGTGGTGTTGAAGAGCAGCAAGGAGCTGACAGAGCTG
+ATGTTGCTGGGAAGACCCCCAAGTCCCTCTTCTGCATCGTCCTCGGGCTC
+CGGCTTGGTGCTCACGCACACAGGAAAGTCCTTCAGCTTCTCCTGAGAGG
+GCCAGGATGGCCAAGGGATGGTGAATATTTGGTGCTGGGCCTAATCAGCT
+GCCATCCCATCCCAGTCAGCCTCCTCTGGGGGACAGAACCCTATGGTGGC
+CCCGGCTCCTCCCCAGTATCCAGTCCTCCTGGTGTGTGACAGGCTATATG
+CGCGGCCAGCAGACCTGCAGGGCCCGCTCGTCCAGGGGGCGGTGCTTGCT
+CTGGATCCTGTGGCGGGGGCGTCTCTGCAGGCCAGGGTCCTGGGCGCCCG
+TGAAGATGGAGCCATATTCCTGCAGGCGCCCTGGAGCAGGGTACTTGGCA
+CTGGAGAACACCTGTGGACACAGGGACAAGTCTGAGGGGGCCCCAAGAGG
+CTCAGAGGGCTAGGATTGCTTGGCAGGAGAGGGTGGAGTTGGAAGCCTGG
+GCGAGAAGAAAGCTCAAGGTACAGGTGGGCAGCAGGGCAGAGACTGGGCA
+GCCTCAGAGGCACGGGGAAATGGAGGGACTGCCCAGTAGCCTCAGGACAC
+AGGGGTATGGGGACTACCTTGATGGCCTTCTTGCTGCCCTTGATCTTCTC
+AATCTTGGCCTGGGCCAAGGAGACCTTCTCTCCAATGGCCTGCACCTGGC
+TCCGGCTCTGCTCTACCTGCTGGGAGATCCTGCCATGGAGAAGATCACAG
+AGGCTGGGCTGCTCCCCACCCTCTGCACACCTCCTGCTTCTAACAGCAGA
+GCTGCCAGGCCAGGCCCTCAGGCAAGGGCTCTGAAGTCAGGGTCACCTAC
+TTGCCAGGGCCGATCTTGGTGCCATCCAGGGGGCCTCTACAAGGATAATC
+TGACCTGCAGGGTCGAGGAGTTGACGGTGCTGAGTTCCCTGCACTCTCAG
+TAGGGACAGGCCCTATGCTGCCACCTGTACATGCTATCTGAAGGACAGCC
+TCCAGGGCACACAGAGGATGGTATTTACACATGCACACATGGCTACTGAT
+GGGGCAAGCACTTCACAACCCCTCATGATCACGTGCAGCAGACAATGTGG
+CCTCTGCAGAGGGGGAACGGAGACCGGAGGCTGAGACTGGCAAGGCTGGA
+CCTGAGTGTCGTCACCTAAATTCAGACGGGGAACTGCCCCTGCACATACT
+GAACGGCTCACTGAGCAAACCCCGAGTCCCGACCACCGCCTCAGTGTGGT
+CTAGCTcctcacctgcttccatcctccctggtgcggggtgggcccagtga
+tatcagctgcctgctgttccccagatgtgccaagtgcattcttgtgtgct
+tgcatctcatggaacgccatttccccagacatccctgtggctggctccTG
+ATGCCCGAGGCCCAAGTGTCTGATGCTTTAAGGCACATCACCCCACTCAT
+GCTTTTCCATGTTCTTTGGCCGCAGCAAGGCCGCTCTCACTGCAAAGTTA
+ACTCTGATGCGTGTGTAACACAACATCCTCCTCCCAGTCGCCCCTGTAGC
+TCCCCTACCTCCAAGAGCCCAGCCCTTGCCCACAGGGCCACACTCCACGT
+GCAGAGCAGCCTCAGCACTCACCGGGCACGAGCGAGCCTGTGTGGTGCGC
+AGGGATGAGAAGGCAGAGGCGCGACTGGGGTTCATGAGGAAGGGCAGGAG
+GAGGGTGTGGGATGGTGGAGGGGTTTGAGAAGGCAGAGGCGCGACTGGGG
+TTCATGAGGAAAGGGAGGGGGAGGATGTGGGATGGTGGAGGGGCTGCAGA
+CTCTGGGCTAGGGAAAGCTGGGATGTCTCTAAAGGTTGGAATGAATGGCC
+TAGAATCCGACCCAATAAGCCAAAGCCACTTCCACCAACGTTAGAAGGCC
+TTGGCCCCCAGAGAGCCAATTTCACAATCCAGAAGTCCCCGTGCCCTAAA
+GGGTCTGCCCTGATTACTCCTGGCTCCTTGTGTGCAGGGGGCTCAGGCAT
+GGCAGGGCTGGGAGTACCAGCAGGCACTCAAGCGGCTTAAGTGTTCCATG
+ACAGACTGGTATGAAGGTGGCCACAATTCAGAAAGAAAAAAGAAGAGCAC
+CATCTCCTTCCAGTGAGGAAGCGGGACCACCACCCAGCGTGTGCTCCATC
+TTTTCTGGCTGGGGAGAGGCCTTCATCTGCTGTAAAGGGTCCTCCAGCAC
+AAGCTGTCTTAATTGACCCTAGTTCCCAGGGCAGCCTCGTTCTGCCTTGG
+GTGCTGACACGACCTTCGGTAGGTGCATAAGCTCTGCATTCGAGGTCCAC
+AGGGGCAGTGGGAGGGAACTGagactggggagggacaaaggctgctctgt
+cctggtgctcccacaaaggagaagggctgatcactcaaagttgcgaacac
+caagctcaacaatgagccctggaaaatttctggaatggattattaaacag
+agagtctgtaagcacttagaaaaggccgcggtgagtcccaggggccagca
+ctgctcgaaatgtacagcatttctctttgtaacaggattattagcctgct
+gtgcccggggaaaacatgcagcacagtgcatctcgagtcagcaggatttt
+gacggcttctaacaaaatcttgtagacaagatggagctatgggggttgga
+ggagagaacatataggaaaaatcagagccaaatgaaccacagccccaaag
+ggcacagttgaacaatggactgattccagccttgcacggagggatctggc
+agagtCCATCCAGTTCATTCAACACCTGGTTAGAAAACTGGGGCCAGCAC
+ACAGGGGAAGGGTAAGCTGGTTTCATGATCGAATCAAGGCTCAGACAATT
+TTTAAAGGCCAGAGGGTAGACTGCAATCACcaagatgaaatttacaagga
+acaaatgtgaagcccaacatttaggttttaaaaatcaagcgtataaatac
+agaaggtggagggaacttgctttagacacagttcaggtgaagaaagacct
+ggaaacttctgttaactataagctcagtaGGGGCTAAAAGCATGTTAATC
+GGCATAAAAAGGCAATGAGATCTTAGGGCACACAGCTCCCCGCCCCTCTT
+CTGCCCTTCATCCTTCTTTCAATCAGCAGGGACCGTGCACTCTCTTGGAG
+CCACCACAGAAAACAGAGGTGCATCCAGCACCACAGAAAACAGAGCCACC
+ACAGAAAACAGAGGGTGACTGTCATCCCCTCCAGTCTCTGCACACTCCCA
+GCTGCAGCAGAGCAGGAGGAGAGAGCACAGCCTGCAATGCTAATTTGCCA
+GGAGCTCACCTGCCTGCGTCACTGGGCACAGACGCCAGTGAGGCCAGAGG
+CCGGGCTGTGCTGGGGCCTGAGCCGGGTGGTGGGGAGAGAGTCTCTCCCC
+TGCCCCTGTCTCTTCCGTGCAGGAGGAGCATGTTTAAGGGGACGGGTTCA
+AAGCTGGTCACATCCCCACCGAAAAAGCCCATGGACAACGAAAAGCCCAC
+TAGCTTGTCCAGTGCCACAGGAGGGGCAAGTGGAGGAGGAGAGGTGGCGG
+TGCTCCCCACTCCACTGCCAGTCGTCACTGGCTCTCCCTTCCCTTCATCC
+TCGTTCCCTATCTGTCACCATTTCCTGTCGTCGTTTCCTCTGAATGTCTC
+ACCCTGCCCTCCCTGCTTGCAAGTCCCCTGTCTGTAGCCTCACCCCTGTC
+GTATCCTGACTACAATAACAGCTTCTGGGTGTCCCTGGCATCCACTCTCT
+CTCCCTTCTTGTCCCTTCCGTGACGGATGCCTGAGGAACCTTCCCCAAAC
+TCTTCTGTCCCATCCCTGCCCTGCTCAAAATCCAATCACAGCTCCCTAAC
+ACGCCTGAATCAACTTGAAGTCCTGTCTTGAGTAATCCGTGGGCCCTAAC
+TCACTCATCCCAACTCTTCACTCACTGCCCTGCCCCACACCCTGCCAGGG
+AGCCTCCCGTGGCACCGTGGGGACACAAAGGAACCAGGGCAAAGCTCCCT
+CAGCCCCATTCAAAGAGGCCTGGCCCACAGGCTCACGGAAAGTCAGCCTC
+TCATGCCCCGAGAGCTGAGTGCAAGGGAGAGGCAGCGCTGTCTGTGCTTC
+CCATGCAGAAGCACCCCCCTCCCACCCCTGTGCAGGCCGGCCTTCGCGGC
+AGACCACCATACACCACGTTCCAAGCCACACTGAGGCCTCCCTCCAAGCC
+TGCAGCCCCCATTTCCAGACCCTGCCAGGGCAACCTGCATATCCACCTCC
+CTACCCTGCCCCCCTCTTCCAGGAGTCTGCCCTATGTGGAGTAAGCACgt
+ggttttcctcttcagcaactatttcctttttactcaagcaatggccccat
+ttcccttggggaatccatctctctcgcaggcttagtcccagagcttcagg
+tggggctgcccacagagctcctcagTCTAAGCCAAGTGGTGTGTCATAGT
+CCCCTGGCCCCATTAATGGATTCTGGGATAGACATGAGGACCAAGCCAGG
+TGGGATGAGTGAGTGTGGCTTCTGGAGGAAGTGGGGACACAGGACAGCAT
+TCTTTCCTGCTGGACCTGACCCTGTGTCATGTCACCTTGCTACCACGAGA
+GCATGGCCTGTCTGGGAATGCAGCCAGACCCAAAGAAGCAAACTGACATG
+GAAGGAAAGCAAAACCAGGCCCTGAGGACATCATTTTAGCCCTTACTCCG
+AAGGCTGCTCTACTGATTGGTTAATTTTTGCTTAGCTTGGTCTGGGGAGT
+TCTGACAGGCGTGCCACCAATTCTTACCGATTTCTCTCCACTCTAGACCC
+TGAGAAGCCCACGCGGTTCATGCTAGCAATTAACAATCAATCTCGCCCTA
+TGTGTTCCCATTCCAGCCTCTAGGACACAGTGGCAGCCACATAATTGGTA
+TCTCTTAAGGTCCAGCACGAGGTGGAGCACATGGTGGAGAGACAGATGCA
+GTGACCTGGAACCCAGGAGTGAGGGAGCCAGGACTCAGGCCCAAGGCTCC
+TGAGAGGCATCTGGCCCTCCCTGCGCTGTGCCAGCAGCTTGGAGAACCCA
+CACTCAATGAACGCAGCACTCCACTACCCAGGAAATGCCTTCCTGCCCTC
+TCCTCATCCCATCCCTGGGCAGGGGACATGCAACTGTCTACAAGGTGCCA
+AGTACCAGGACAGGAAAGGAAAGACGCCAAAAATCCAGCGCTGCCCTCAG
+AGAAGGGCAACCACGCAGTCCCCATCTTGGCAAGGAAACACAATTTCCGA
+GGGAATGGTTTTGGCCTCCATTCTAAGTGCTGGACATGGGGTGGCCATAA
+TCTGGAGCTGATGGCTCTTAAAGACCTGCATCCTCTTCCCTAGGTGTCCC
+TCGGGCACATTTAGCACAAAGATAAGCACAAAAGGTGCATCCAGCACTTT
+GTTACTATTGGTGGCAGGTTTATGAATGGCAACCAAAGGCAGTGTACGGG
+TCAAGATTATCAACAGGGAagagatagcatttcctgaaggcttcctaggt
+gccaggcactgttccattcctttgcatgttttgattaatttaatatttaa
+aataattctaccaggaagctaccattattaccacaacttcacaaatgaga
+acaccgaggcttagaggggttgggttgcccaaggttacagaggaagaaaa
+caggggagctggatctgagccaaggcatcaactccaaggtaacccctcag
+tcacttcactgtgtgtcccctGGTTACTGGGACATTCTTGACAAACTCGG
+GGCAAGCCGGTGAGTCAGTGGGGGAGGACTTTCAGGAAGAGGTGGGTTCC
+CAGTTGGTGACAGAAGAGGAGGCTGCAAAGTGAAGGAGCAGGGGCTCCAG
+GTCTGGCGACAACCAGGGAAGGGACAGGGCAGGGATGGCTTGGACCACGA
+GAGGCACCTGAGTCAGGCAGTCACATACTTCCCACTGGGGTCTACCATGT
+GAGGCATGGTGTGGGATCCTGGGAAGGAGACCAAGCCTCATTTCAGTTTG
+CTTATGGCCAAAGACAGGACCTGTGTACCCGACAACCCCTGGGACCTTTA
+CCAAAAAAAGAGCAAACACCATTCACTCACTCATGTTAGATAAACACTGA
+GTGAAGTCACTGGAGCCCAAGGACTGTGCGAGGTCAGCACTGCCAATACA
+AGAagctgcagccctccagctcgcctccctcaatggccactccgtgctcc
+agccatgctggcttccttttaggtcctccacctccaggctgtagttcatg
+tgcttctttctggaatgttcttcccaacctacccactcaaccctcagact
+ttaccataaatgtcatttcctcacgtctgccttccctgacctgagaccaa
+gccaggcttcccatgacgagcctcacagtaccccatctCCCCTGAACAGA
+TGCAGTAATAACCTACATAACCCGGGGCCATGATCTAtggctttgaatcc
+tggctctgtcactaggccaggtctctcagcccttctgtgcctcagtttcc
+tcatctataaaatgagatgacggcagtgcctgctcatgaagtgtgagtta
+atgcactcaaatcaatggttgtgcacggtttatatgaatattagtgatta
+CAAAATATTATCAATAGACCTTGTCACAACTGTTATTGAAGAACtaatca
+tctattgcttatttaggtctttctctcctgccagaatgtgcgctccaggt
+ggagaggtatgttgccttatccgtggctggatatatagagattcccacac
+tgccttgcacacgagcactgctgggtaaatatttgttggctgcaggaaAA
+CGTGAAGGAATAGGCCCTCCAATGGGAGGAAAAGCATGAGTTGTGAGAGC
+AGAGCCACCACAGGAAACCAGGAGGCTAAGTGGGGTGGAAGGGAGTGAGC
+TCTCGGACTCCCAGGAGTAAAAGCTTCCAAGTTGGGCTCTCACTTCAGCC
+CCTCCCACACAGGGAAGCCAGATGGGTTCCCCAGGACCGGGATTCCCCAA
+GGGGGCTGCTCCCAGAGGGTGTGTTGCTGGGATTGCCCAGGACAGGGATG
+GCCCTCTCATCAGGTGGGGGTGAGTGGCAGCACCCACCTGCTGAAGATGT
+CTCCAGAGACCTTCTGCAGGTACTGCAGGGCATCCGCCATCTGCTGGACG
+GCCTCCTCTCGCCGCAGGTCTGGCTGGATGAAGGGCACGGCATAGGTCTG
+ACCTGCCAGGGAGTGCTGCATCCTCACAGGAGTCATGGTGCCTGTGGGTC
+GGAGCCGGAGCGTCAGAGCCACCCACGACCACCGGCACGCCCCCACCACA
diff --git a/poetry.lock b/poetry.lock
index bc9fcfbd..3d162d35 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -906,73 +906,68 @@ test = ["backports.socketpair", "cffi (>=1.12.2)", "contextvars (==2.4)", "cover
[[package]]
name = "greenlet"
-version = "3.0.0"
+version = "3.0.1"
description = "Lightweight in-process concurrent programming"
optional = false
python-versions = ">=3.7"
files = [
- {file = "greenlet-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e09dea87cc91aea5500262993cbd484b41edf8af74f976719dd83fe724644cd6"},
- {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47932c434a3c8d3c86d865443fadc1fbf574e9b11d6650b656e602b1797908a"},
- {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bdfaeecf8cc705d35d8e6de324bf58427d7eafb55f67050d8f28053a3d57118c"},
- {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a68d670c8f89ff65c82b936275369e532772eebc027c3be68c6b87ad05ca695"},
- {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ad562a104cd41e9d4644f46ea37167b93190c6d5e4048fcc4b80d34ecb278f"},
- {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02a807b2a58d5cdebb07050efe3d7deaf915468d112dfcf5e426d0564aa3aa4a"},
- {file = "greenlet-3.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b1660a15a446206c8545edc292ab5c48b91ff732f91b3d3b30d9a915d5ec4779"},
- {file = "greenlet-3.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:813720bd57e193391dfe26f4871186cf460848b83df7e23e6bef698a7624b4c9"},
- {file = "greenlet-3.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:aa15a2ec737cb609ed48902b45c5e4ff6044feb5dcdfcf6fa8482379190330d7"},
- {file = "greenlet-3.0.0-cp310-universal2-macosx_11_0_x86_64.whl", hash = "sha256:7709fd7bb02b31908dc8fd35bfd0a29fc24681d5cc9ac1d64ad07f8d2b7db62f"},
- {file = "greenlet-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:211ef8d174601b80e01436f4e6905aca341b15a566f35a10dd8d1e93f5dbb3b7"},
- {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6512592cc49b2c6d9b19fbaa0312124cd4c4c8a90d28473f86f92685cc5fef8e"},
- {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:871b0a8835f9e9d461b7fdaa1b57e3492dd45398e87324c047469ce2fc9f516c"},
- {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b505fcfc26f4148551826a96f7317e02c400665fa0883fe505d4fcaab1dabfdd"},
- {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123910c58234a8d40eaab595bc56a5ae49bdd90122dde5bdc012c20595a94c14"},
- {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:96d9ea57292f636ec851a9bb961a5cc0f9976900e16e5d5647f19aa36ba6366b"},
- {file = "greenlet-3.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0b72b802496cccbd9b31acea72b6f87e7771ccfd7f7927437d592e5c92ed703c"},
- {file = "greenlet-3.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:527cd90ba3d8d7ae7dceb06fda619895768a46a1b4e423bdb24c1969823b8362"},
- {file = "greenlet-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:37f60b3a42d8b5499be910d1267b24355c495064f271cfe74bf28b17b099133c"},
- {file = "greenlet-3.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1482fba7fbed96ea7842b5a7fc11d61727e8be75a077e603e8ab49d24e234383"},
- {file = "greenlet-3.0.0-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:be557119bf467d37a8099d91fbf11b2de5eb1fd5fc5b91598407574848dc910f"},
- {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73b2f1922a39d5d59cc0e597987300df3396b148a9bd10b76a058a2f2772fc04"},
- {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1e22c22f7826096ad503e9bb681b05b8c1f5a8138469b255eb91f26a76634f2"},
- {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d363666acc21d2c204dd8705c0e0457d7b2ee7a76cb16ffc099d6799744ac99"},
- {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:334ef6ed8337bd0b58bb0ae4f7f2dcc84c9f116e474bb4ec250a8bb9bd797a66"},
- {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6672fdde0fd1a60b44fb1751a7779c6db487e42b0cc65e7caa6aa686874e79fb"},
- {file = "greenlet-3.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:952256c2bc5b4ee8df8dfc54fc4de330970bf5d79253c863fb5e6761f00dda35"},
- {file = "greenlet-3.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:269d06fa0f9624455ce08ae0179430eea61085e3cf6457f05982b37fd2cefe17"},
- {file = "greenlet-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:9adbd8ecf097e34ada8efde9b6fec4dd2a903b1e98037adf72d12993a1c80b51"},
- {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6b5ce7f40f0e2f8b88c28e6691ca6806814157ff05e794cdd161be928550f4c"},
- {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf94aa539e97a8411b5ea52fc6ccd8371be9550c4041011a091eb8b3ca1d810"},
- {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80dcd3c938cbcac986c5c92779db8e8ce51a89a849c135172c88ecbdc8c056b7"},
- {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e52a712c38e5fb4fd68e00dc3caf00b60cb65634d50e32281a9d6431b33b4af1"},
- {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5539f6da3418c3dc002739cb2bb8d169056aa66e0c83f6bacae0cd3ac26b423"},
- {file = "greenlet-3.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:343675e0da2f3c69d3fb1e894ba0a1acf58f481f3b9372ce1eb465ef93cf6fed"},
- {file = "greenlet-3.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:abe1ef3d780de56defd0c77c5ba95e152f4e4c4e12d7e11dd8447d338b85a625"},
- {file = "greenlet-3.0.0-cp37-cp37m-win32.whl", hash = "sha256:e693e759e172fa1c2c90d35dea4acbdd1d609b6936115d3739148d5e4cd11947"},
- {file = "greenlet-3.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bdd696947cd695924aecb3870660b7545a19851f93b9d327ef8236bfc49be705"},
- {file = "greenlet-3.0.0-cp37-universal2-macosx_11_0_x86_64.whl", hash = "sha256:cc3e2679ea13b4de79bdc44b25a0c4fcd5e94e21b8f290791744ac42d34a0353"},
- {file = "greenlet-3.0.0-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:63acdc34c9cde42a6534518e32ce55c30f932b473c62c235a466469a710bfbf9"},
- {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a1a6244ff96343e9994e37e5b4839f09a0207d35ef6134dce5c20d260d0302c"},
- {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b822fab253ac0f330ee807e7485769e3ac85d5eef827ca224feaaefa462dc0d0"},
- {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8060b32d8586e912a7b7dac2d15b28dbbd63a174ab32f5bc6d107a1c4143f40b"},
- {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:621fcb346141ae08cb95424ebfc5b014361621b8132c48e538e34c3c93ac7365"},
- {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bb36985f606a7c49916eff74ab99399cdfd09241c375d5a820bb855dfb4af9f"},
- {file = "greenlet-3.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10b5582744abd9858947d163843d323d0b67be9432db50f8bf83031032bc218d"},
- {file = "greenlet-3.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f351479a6914fd81a55c8e68963609f792d9b067fb8a60a042c585a621e0de4f"},
- {file = "greenlet-3.0.0-cp38-cp38-win32.whl", hash = "sha256:9de687479faec7db5b198cc365bc34addd256b0028956501f4d4d5e9ca2e240a"},
- {file = "greenlet-3.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:3fd2b18432e7298fcbec3d39e1a0aa91ae9ea1c93356ec089421fabc3651572b"},
- {file = "greenlet-3.0.0-cp38-universal2-macosx_11_0_x86_64.whl", hash = "sha256:3c0d36f5adc6e6100aedbc976d7428a9f7194ea79911aa4bf471f44ee13a9464"},
- {file = "greenlet-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4cd83fb8d8e17633ad534d9ac93719ef8937568d730ef07ac3a98cb520fd93e4"},
- {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a5b2d4cdaf1c71057ff823a19d850ed5c6c2d3686cb71f73ae4d6382aaa7a06"},
- {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e7dcdfad252f2ca83c685b0fa9fba00e4d8f243b73839229d56ee3d9d219314"},
- {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c94e4e924d09b5a3e37b853fe5924a95eac058cb6f6fb437ebb588b7eda79870"},
- {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad6fb737e46b8bd63156b8f59ba6cdef46fe2b7db0c5804388a2d0519b8ddb99"},
- {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d55db1db455c59b46f794346efce896e754b8942817f46a1bada2d29446e305a"},
- {file = "greenlet-3.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:56867a3b3cf26dc8a0beecdb4459c59f4c47cdd5424618c08515f682e1d46692"},
- {file = "greenlet-3.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a812224a5fb17a538207e8cf8e86f517df2080c8ee0f8c1ed2bdaccd18f38f4"},
- {file = "greenlet-3.0.0-cp39-cp39-win32.whl", hash = "sha256:0d3f83ffb18dc57243e0151331e3c383b05e5b6c5029ac29f754745c800f8ed9"},
- {file = "greenlet-3.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:831d6f35037cf18ca5e80a737a27d822d87cd922521d18ed3dbc8a6967be50ce"},
- {file = "greenlet-3.0.0-cp39-universal2-macosx_11_0_x86_64.whl", hash = "sha256:a048293392d4e058298710a54dfaefcefdf49d287cd33fb1f7d63d55426e4355"},
- {file = "greenlet-3.0.0.tar.gz", hash = "sha256:19834e3f91f485442adc1ee440171ec5d9a4840a1f7bd5ed97833544719ce10b"},
+ {file = "greenlet-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064"},
+ {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d"},
+ {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd"},
+ {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565"},
+ {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2"},
+ {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63"},
+ {file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e"},
+ {file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846"},
+ {file = "greenlet-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9"},
+ {file = "greenlet-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65"},
+ {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96"},
+ {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a"},
+ {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec"},
+ {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72"},
+ {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234"},
+ {file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884"},
+ {file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94"},
+ {file = "greenlet-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c"},
+ {file = "greenlet-3.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa"},
+ {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353"},
+ {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c"},
+ {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9"},
+ {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0"},
+ {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5"},
+ {file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d"},
+ {file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445"},
+ {file = "greenlet-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4"},
+ {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206"},
+ {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2"},
+ {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a"},
+ {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a"},
+ {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de"},
+ {file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166"},
+ {file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36"},
+ {file = "greenlet-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1"},
+ {file = "greenlet-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8"},
+ {file = "greenlet-3.0.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16"},
+ {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174"},
+ {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3"},
+ {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74"},
+ {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd"},
+ {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9"},
+ {file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e"},
+ {file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a"},
+ {file = "greenlet-3.0.1-cp38-cp38-win32.whl", hash = "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd"},
+ {file = "greenlet-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6"},
+ {file = "greenlet-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376"},
+ {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997"},
+ {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe"},
+ {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc"},
+ {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1"},
+ {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d"},
+ {file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8"},
+ {file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546"},
+ {file = "greenlet-3.0.1-cp39-cp39-win32.whl", hash = "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57"},
+ {file = "greenlet-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619"},
+ {file = "greenlet-3.0.1.tar.gz", hash = "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b"},
]
[package.extras]
@@ -3135,18 +3130,18 @@ files = [
[[package]]
name = "traitlets"
-version = "5.11.2"
+version = "5.12.0"
description = "Traitlets Python configuration system"
optional = false
python-versions = ">=3.8"
files = [
- {file = "traitlets-5.11.2-py3-none-any.whl", hash = "sha256:98277f247f18b2c5cabaf4af369187754f4fb0e85911d473f72329db8a7f4fae"},
- {file = "traitlets-5.11.2.tar.gz", hash = "sha256:7564b5bf8d38c40fa45498072bf4dc5e8346eb087bbf1e2ae2d8774f6a0f078e"},
+ {file = "traitlets-5.12.0-py3-none-any.whl", hash = "sha256:81539f07f7aebcde2e4b5ab76727f53eabf18ad155c6ed7979a681411602fa47"},
+ {file = "traitlets-5.12.0.tar.gz", hash = "sha256:833273bf645d8ce31dcb613c56999e2e055b1ffe6d09168a164bcd91c36d5d35"},
]
[package.extras]
docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
-test = ["argcomplete (>=3.0.3)", "mypy (>=1.5.1)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"]
+test = ["argcomplete (>=3.0.3)", "mypy (>=1.6.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"]
[[package]]
name = "typer"
diff --git a/pyproject.toml b/pyproject.toml
index 70e85ac5..3a310dd6 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -231,6 +231,10 @@ allow-star-arg-any = true
"src/pxblat/cli/cli.py" = ["DTZ005"]
"src/pxblat/toolkit/__init__.py" = ["PLR0913"]
"src/pxblat/parser.py" = ["A001", "A002", "ARG001"]
+"docs/tutorial_data/query_context.py" = ["T201", "D103"]
+"docs/tutorial_data/2bit.py" = ["T201", "D103"]
+"docs/tutorial_data/query_general.py" = ["T201", "D103"]
+"docs/tutorial_data/query_result.py" = ["T201", "D103"]
[tool.ruff.pydocstyle]
convention = 'google'