<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>REBELSCIENCE</title><link>https://rebelscience.club/</link><description>Bioinformatics, Programming and Open-Source Science</description><item><title>Genome Toolkit. Part 4.7: Testing, Reliable Errors, and Final Polish</title><link>https://rebelscience.club/2026/09/genome-toolkit-part-4-7-testing-reliable-errors-examples-and-final-polish/</link><guid isPermaLink="true">https://rebelscience.club/2026/09/genome-toolkit-part-4-7-testing-reliable-errors-examples-and-final-polish/</guid><pubDate>Tue, 15 Sep 2026 12:51:09 GMT</pubDate><description>In Part 4.7, we complete the foundational Genome Toolkit refactor by adding clear custom domain exceptions and automated tests with pytest. We test our biological models, plain-text and FASTA loaders, and k-mer algorithms, then use those tests to uncover edge cases that our previous manual experiments did not catch.
</description><content:encoded><![CDATA[
<p class="wp-block-paragraph">Welcome back to the Genome Toolkit series!</p>



<p class="wp-block-paragraph">We have reached the final part of our foundational refactor.</p>



<p class="wp-block-paragraph">By the end of Part 4.6, the main Genome Toolkit flow already works:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">FASTA
  ↓
SequenceRecord
  ↓
DNA
  ↓
algorithm(Sequence)
  ↓
structured result
    ├── metadata
    ├── inputs
    ├── parameters
    └── output</pre></div>



<p class="wp-block-paragraph">We now have a modern <code>uv</code> project, validated biological models, plain-text and FASTA loading, generic k-mer algorithms, and structured scientific results.</p>



<p class="wp-block-paragraph">That is a big improvement over the small script we started with.</p>



<p class="wp-block-paragraph">But before we leave the refactoring work behind and return to adding more bioinformatics, there is one important question left:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">What happens when something goes wrong, and how do we make sure the behavior we already built keeps working as Genome Toolkit grows?</p>
</blockquote>



<p class="wp-block-paragraph">That is what Part 4.7 is about.</p>



<p class="wp-block-paragraph">We are going to add a small set of meaningful Genome Toolkit errors, introduce automated tests with <code>pytest</code>, use those tests to discover a few real edge cases, fix only what the tests show us needs fixing, replace our old README with a practical final version, and then run one last verification of the project.</p>



<p class="wp-block-paragraph">Our path looks like this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">working architecture
        ↓
clear error types
        ↓
automated tests
        ↓
tests expose hidden problems
        ↓
small focused fixes
        ↓
rerun tests
        ↓
README + documentation
        ↓
final verification
        ↓
PART 4 REFACTOR COMPLETE</pre></div>



<p class="wp-block-paragraph">The architecture is already doing its job. In this part, we are adding the reliability around it: clear errors, automated checks, and one final verification before we return to bioinformatics.</p>



<h2 class="wp-block-heading">Before We Start: Two Useful Background Videos</h2>



<p class="wp-block-paragraph">We only need a small part of Python exception handling and <code>pytest</code> for this article, but both topics are important enough that they are worth understanding properly.</p>



<p class="wp-block-paragraph">If exceptions or automated testing are completely new to you, I strongly recommend making a short detour before continuing. Spend some time learning what exceptions are, what tests do, how a simple test is written, and why developers run those tests again and again while software changes.</p>



<p class="wp-block-paragraph">These two videos are a good starting point.</p>



<p class="wp-block-paragraph"><strong>Python exceptions</strong></p>



<p class="wp-block-paragraph">Corey Schafer — <em>Python Tutorial: Using Try/Except Blocks for Error Handling</em></p>



<p class="wp-block-paragraph"><a href="https://youtu.be/NIWwJbo-9_8">https://youtu.be/NIWwJbo-9_8</a></p>



<p class="wp-block-paragraph"><strong>pytest</strong></p>



<p class="wp-block-paragraph">Anthony Explains — <em>getting started with pytest (beginner &#8211; intermediate)</em></p>



<p class="wp-block-paragraph"><a href="https://youtu.be/mzlH8lp4ISA">https://youtu.be/mzlH8lp4ISA</a></p>



<p class="wp-block-paragraph">You do not need to become a testing expert before continuing with us.</p>



<p class="wp-block-paragraph">We will explain every pytest feature we actually use. The reason for the detour is simply that testing is a normal part of building serious software, and it becomes especially important for scientific software. If somebody uses our toolkit for an experiment, we want them to know that the code was not only written and manually tried a few times, but that important behavior is also checked automatically.</p>



<p class="wp-block-paragraph">Tests will not prove that every scientific result is biologically correct. We will come back to that distinction later. What they give us is a repeatable way to check the software behavior we have deliberately defined.</p>



<h2 class="wp-block-heading">What Are We Changing?</h2>



<p class="wp-block-paragraph">Part 4.7 adds one small module to the package:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/exceptions.py</pre></div>



<p class="wp-block-paragraph">and one new top-level directory:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">tests/</pre></div>



<p class="wp-block-paragraph">We will also update:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/load/text.py
src/genome_toolkit/load/fasta.py
src/genome_toolkit/algorithms/kmer.py
README.md
pyproject.toml
uv.lock</pre></div>



<p class="wp-block-paragraph">Our biological models, structured-result design, and the experiment workflow from Part 4.6 stay in place.</p>



<p class="wp-block-paragraph">There is one useful design idea to keep in mind.</p>



<p class="wp-block-paragraph">Genome Toolkit should understand failures that belong to Genome Toolkit itself. For example:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">this FASTA file is malformed
this biological record does not exist
this algorithm parameter does not make sense</pre></div>



<p class="wp-block-paragraph">A future user interface can decide how to present those failures.</p>



<p class="wp-block-paragraph">That future interface could be:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">a Python script
a web application
an API
an AI agent
another scientific tool</pre></div>



<p class="wp-block-paragraph">Genome Toolkit should simply report the scientific or data-processing problem clearly. We do not need to put web-server logic, AI-agent logic, or user-interface behavior inside the scientific package.</p>



<h2 class="wp-block-heading">1. Add Clear Genome Toolkit Exception Types</h2>



<p class="wp-block-paragraph">Before we write the tests, let us make one small improvement to the errors Genome Toolkit can report.</p>



<p class="wp-block-paragraph">Python already gives us useful built-in exceptions such as:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">FileNotFoundError
PermissionError
ValueError</pre></div>



<p class="wp-block-paragraph">Pydantic gives us:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">ValidationError</pre></div>



<p class="wp-block-paragraph">We are keeping all of those where they already describe the problem clearly.</p>



<p class="wp-block-paragraph">The problem is that <code>ValueError</code> can mean many different things. For example:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">the caller used a function incorrectly
the FASTA source is malformed
the requested biological record does not exist
an algorithm parameter does not make sense</pre></div>



<p class="wp-block-paragraph">For a small script, reading the message may be enough.</p>



<p class="wp-block-paragraph">For a library that may later be used by another Python program, a web API, an MCP tool, or an AI agent, it is useful to distinguish these failures by their Python type as well.</p>



<p class="wp-block-paragraph">The professional name for what we are adding is <strong>custom domain exceptions</strong>.</p>



<p class="wp-block-paragraph">They are normal Python exception classes whose names describe failures that have specific meaning inside Genome Toolkit.</p>



<p class="wp-block-paragraph">You can also think of them as clearer labels for different kinds of problems:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">FormatError
→ the source data has the wrong structure

RecordNotFoundError
→ the source is valid, but the requested record is missing

AlgorithmInputError
→ an algorithm parameter is outside the supported domain</pre></div>



<p class="wp-block-paragraph">Create:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/exceptions.py    # &lt;-- NEW</pre></div>



<p class="wp-block-paragraph">with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;&quot;Genome Toolkit domain exceptions.&quot;&quot;&quot;


class GenomeToolkitError(Exception):
    &quot;&quot;&quot;Base exception for Genome Toolkit domain errors.&quot;&quot;&quot;


class FormatError(GenomeToolkitError):
    &quot;&quot;&quot;Input data does not follow the expected format.&quot;&quot;&quot;


class RecordNotFoundError(GenomeToolkitError):
    &quot;&quot;&quot;Requested biological record was not found.&quot;&quot;&quot;


class AlgorithmInputError(GenomeToolkitError):
    &quot;&quot;&quot;Algorithm parameter is outside its supported domain.&quot;&quot;&quot;</pre></div>



<p class="wp-block-paragraph">These classes deliberately contain no extra machinery.</p>



<p class="wp-block-paragraph"><code>GenomeToolkitError</code> gives us one common base class. The three subclasses give callers a more specific category when something goes wrong.</p>



<p class="wp-block-paragraph">We will use this simple rule:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">file does not exist
→ FileNotFoundError

permission denied
→ PermissionError

DNA model validation fails
→ Pydantic ValidationError

caller misuses a Python function
→ ValueError where appropriate

source data has the wrong format
→ FormatError

requested biological record does not exist
→ RecordNotFoundError

algorithm parameter is outside the supported domain
→ AlgorithmInputError</pre></div>



<p class="wp-block-paragraph">That is enough for the scientific package.</p>



<p class="wp-block-paragraph">A future API, web application, or AI agent can decide how it wants to present those errors to an end user.</p>



<h2 class="wp-block-heading">2. Add pytest as a Development Dependency</h2>



<p class="wp-block-paragraph">Now let us add the tool that will run our automated tests.</p>



<p class="wp-block-paragraph">From the Genome Toolkit project root, run:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv add --dev pytest</pre></div>



<p class="wp-block-paragraph">The important part is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">--dev</pre></div>



<p class="wp-block-paragraph">That tells <code>uv</code> that pytest is a <strong>development dependency</strong>.</p>



<p class="wp-block-paragraph">We need pytest while we are building and checking Genome Toolkit, but somebody using the scientific package does not need pytest just to run its algorithms.</p>



<p class="wp-block-paragraph">After the command, expect these two files to change:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">pyproject.toml    # &lt;-- UPDATED
uv.lock           # &lt;-- UPDATED</pre></div>



<p class="wp-block-paragraph">This also makes the development environment reproducible.</p>



<p class="wp-block-paragraph">Having pytest installed inside one existing <code>.venv</code> is not enough. Declaring it in the project means another machine can run:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv sync</pre></div>



<p class="wp-block-paragraph">and get the testing dependency too.</p>



<p class="wp-block-paragraph">We can quickly verify that pytest is available:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest --version</pre></div>



<p class="wp-block-paragraph">Now we are ready to write our first automated checks.</p>



<h2 class="wp-block-heading">3. Start With Two Simple DNA Tests</h2>



<p class="wp-block-paragraph">We will start with the easiest part of Genome Toolkit.</p>



<p class="wp-block-paragraph">Our <code>DNA</code> model already works, and these tests do not require any implementation changes.</p>



<p class="wp-block-paragraph">Create:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">tests/
└── test_sequence.py    # &lt;-- NEW</pre></div>



<h3 class="wp-block-heading">3.1 Check That DNA Keeps Its Identifier and Normalizes the Sequence</h3>



<p class="wp-block-paragraph">Start with the import we need:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.sequence import DNA</pre></div>



<p class="wp-block-paragraph">Then add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def test_dna_preserves_identifier_and_normalizes_sequence() -&gt; None:
    &quot;&quot;&quot;Check that DNA keeps the identifier we provide
    and converts lowercase sequence symbols to uppercase.
    &quot;&quot;&quot;
    dna = DNA(
        identifier=&quot;example&quot;,
        sequence=&quot;acgtn&quot;,
    )

    assert dna.identifier == &quot;example&quot;
    assert dna.sequence == &quot;ACGTN&quot;</pre></div>



<p class="wp-block-paragraph">This gives us one small permanent check for two pieces of normal DNA behavior:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">identifier
→ preserved

sequence
→ normalized to uppercase</pre></div>



<p class="wp-block-paragraph">Run:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest tests/test_sequence.py -v</pre></div>



<p class="wp-block-paragraph">The <code>-v</code> means <strong>verbose</strong>. It tells pytest to show the individual test names.</p>



<p class="wp-block-paragraph">We should see:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">test_dna_preserves_identifier_and_normalizes_sequence PASSED</pre></div>



<h3 class="wp-block-heading">3.2 Check That Invalid DNA Still Fails</h3>



<p class="wp-block-paragraph">Now we want to check the opposite case.</p>



<p class="wp-block-paragraph">This test needs pytest and Pydantic&#8217;s <code>ValidationError</code>, so this is the point where we add those imports:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">import pytest
from pydantic import ValidationError

from genome_toolkit.sequence import DNA</pre></div>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def test_dna_rejects_invalid_symbols() -&gt; None:
    &quot;&quot;&quot;Check that DNA refuses sequence symbols outside
    the alphabet supported by our DNA model.
    &quot;&quot;&quot;
    with pytest.raises(ValidationError):
        DNA(
            identifier=&quot;example&quot;,
            sequence=&quot;ACGTZ&quot;,
        )</pre></div>



<p class="wp-block-paragraph"><code>pytest.raises(ValidationError)</code> means that this failure is the expected behavior.</p>



<p class="wp-block-paragraph">If <code>ACGTZ</code> suddenly becomes valid DNA in our model, the test should fail.</p>



<p class="wp-block-paragraph">Run again:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest tests/test_sequence.py -v</pre></div>



<p class="wp-block-paragraph">Both tests should pass.</p>



<p class="wp-block-paragraph">We now have a working pytest setup and our first automated checks.</p>



<h2 class="wp-block-heading">4. Update the Loader Error Types Before Testing the Loaders</h2>



<p class="wp-block-paragraph">Before we write tests for <code>text.py</code> and <code>fasta.py</code>, we want those loaders to use the clearer exception types we just created.</p>



<p class="wp-block-paragraph">The important point is that we are <strong>not</strong> rewriting the loaders.</p>



<p class="wp-block-paragraph">We are changing only the places where an existing generic <code>ValueError</code> has a more useful Genome Toolkit meaning.</p>



<p class="wp-block-paragraph">This lets us finish each source file once and then stay inside the test file while we verify the behavior.</p>



<h3 class="wp-block-heading">4.1 Update <code>text.py</code></h3>



<p class="wp-block-paragraph">Open:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/load/text.py</pre></div>



<p class="wp-block-paragraph">This file now needs <code>FormatError</code>, so add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.exceptions import FormatError</pre></div>



<p class="wp-block-paragraph">The plain-text loader already checks whether the file contains usable sequence data.</p>



<p class="wp-block-paragraph">Change this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not sequence:
    raise ValueError(
        f&quot;'{path}' does not contain sequence data.&quot;
    )</pre></div>



<p class="wp-block-paragraph">to this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not sequence:
    raise FormatError(
        f&quot;'{path}' does not contain sequence data.&quot;
    )</pre></div>



<p class="wp-block-paragraph">The meaning is now more specific:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">file exists
but contains no usable sequence
→ FormatError</pre></div>



<p class="wp-block-paragraph">A missing path still naturally raises:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">FileNotFoundError</pre></div>



<p class="wp-block-paragraph">Python already describes that case correctly, so we do not catch or wrap it.</p>



<p class="wp-block-paragraph">Because this is a public function, make sure its <code>Raises:</code> documentation matches the new behavior:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Raises:
    FormatError: If the file does not contain usable sequence data.</pre></div>



<p class="wp-block-paragraph">That completes the Part 4.7 change in <code>text.py</code>.</p>



<h3 class="wp-block-heading">4.2 Update <code>fasta.py</code></h3>



<p class="wp-block-paragraph">Now open:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/load/fasta.py</pre></div>



<p class="wp-block-paragraph">The FASTA loader needs two of our new exception types:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.exceptions import (
    FormatError,
    RecordNotFoundError,
)</pre></div>



<p class="wp-block-paragraph">The rule is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">bad FASTA structure or unusable FASTA data
→ FormatError

valid FASTA, requested record does not exist
→ RecordNotFoundError

caller misused the Python function
→ ValueError</pre></div>



<p class="wp-block-paragraph">We only need to update the existing failure branches.</p>



<h4 class="wp-block-heading"><code>_parse_header()</code>: Blank Header</h4>



<p class="wp-block-paragraph">Change:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not header:
    raise ValueError(
        &quot;A FASTA header must contain a sequence identifier.&quot;
    )</pre></div>



<p class="wp-block-paragraph">to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not header:
    raise FormatError(
        &quot;A FASTA header must contain a sequence identifier.&quot;
    )</pre></div>



<p class="wp-block-paragraph">Here <code>header</code> is singular because <code>_parse_header()</code> is checking one FASTA header.</p>



<h4 class="wp-block-heading"><code>get_headers()</code>: Sequence Before the First Header</h4>



<p class="wp-block-paragraph">Change:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">elif not headers:
    raise ValueError(
        f&quot;'{path}' does not begin with a FASTA header.&quot;
    )</pre></div>



<p class="wp-block-paragraph">to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">elif not headers:
    raise FormatError(
        f&quot;'{path}' does not begin with a FASTA header.&quot;
    )</pre></div>



<h4 class="wp-block-heading"><code>get_headers()</code>: No FASTA Records</h4>



<p class="wp-block-paragraph">Change:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not headers:
    raise ValueError(
        f&quot;'{path}' does not contain FASTA records.&quot;
    )</pre></div>



<p class="wp-block-paragraph">to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not headers:
    raise FormatError(
        f&quot;'{path}' does not contain FASTA records.&quot;
    )</pre></div>



<p class="wp-block-paragraph">Here <code>headers</code> is plural because <code>get_headers()</code> owns the list of discovered records.</p>



<h4 class="wp-block-heading"><code>get_sequence()</code>: Sequence Before Any Header</h4>



<p class="wp-block-paragraph">Change:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not seen_header:
    raise ValueError(
        f&quot;'{path}' does not begin with a FASTA header.&quot;
    )</pre></div>



<p class="wp-block-paragraph">to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not seen_header:
    raise FormatError(
        f&quot;'{path}' does not begin with a FASTA header.&quot;
    )</pre></div>



<h4 class="wp-block-heading"><code>get_sequence()</code>: Requested Record Is Missing</h4>



<p class="wp-block-paragraph">Change the existing missing-record error from <code>ValueError</code> to <code>RecordNotFoundError</code>:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if selected_identifier is None:
    target = (
        identifier
        if identifier is not None
        else f&quot;index {index}&quot;
    )

    raise RecordNotFoundError(
        f&quot;No sequence found matching '{target}' in '{path}'.&quot;
    )</pre></div>



<p class="wp-block-paragraph">The FASTA source itself may be perfectly valid here. The requested record simply is not present.</p>



<h4 class="wp-block-heading"><code>get_sequence()</code>: Selected Record Has No Sequence</h4>



<p class="wp-block-paragraph">Keep the existing check, but report it as a format problem:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not sequence:
    raise FormatError(
        f&quot;Sequence '{selected_identifier}' in '{path}' &quot;
        &quot;does not contain sequence data.&quot;
    )</pre></div>



<p class="wp-block-paragraph">We still keep ordinary <code>ValueError</code> for direct Python API misuse:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">both identifier and index supplied
neither selector supplied
blank identifier
negative index</pre></div>



<p class="wp-block-paragraph">Finally, make sure the public <code>Raises:</code> documentation agrees with the implementation:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Raises:
    ValueError: If both selectors or neither selector is provided,
        `identifier` is empty, or `index` is negative.
    FormatError: If the FASTA structure is invalid or the selected
        record contains no sequence data.
    RecordNotFoundError: If the requested identifier or index does
        not exist.</pre></div>



<p class="wp-block-paragraph">That completes the Part 4.7 changes in <code>fasta.py</code>.</p>



<p class="wp-block-paragraph">We do not need to return to either loader again.</p>



<h2 class="wp-block-heading">5. Test the Loaders</h2>



<p class="wp-block-paragraph">Now we can stay inside one file:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">tests/test_load.py</pre></div>



<p class="wp-block-paragraph">We will add the tests one at a time.</p>



<p class="wp-block-paragraph">The short docstring inside each test explains exactly what behavior we are checking, so we do not need a paragraph of theory around every function.</p>



<h3 class="wp-block-heading">5.1 Plain-text Loading</h3>



<p class="wp-block-paragraph">Start with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from pathlib import Path

from genome_toolkit.load import text</pre></div>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def test_text_loader_reads_wrapped_sequence(
    tmp_path: Path,
) -&gt; None:
    &quot;&quot;&quot;Check that sequence text spread across multiple lines
    and spaces is joined into one clean sequence.
    &quot;&quot;&quot;
    path = tmp_path / &quot;sample.txt&quot;

    path.write_text(
        &quot;&quot;&quot;
         AATT
           TTAA
          AAC
        &quot;&quot;&quot;,
        encoding=&quot;utf-8&quot;,
    )

    record = text.get_sequence(path)

    assert record.sequence == &quot;AATTTTAAAAC&quot;</pre></div>



<p class="wp-block-paragraph">The only new pytest feature here is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">tmp_path</pre></div>



<p class="wp-block-paragraph"><code>tmp_path</code> is a pytest fixture that gives this test a temporary directory.</p>



<p class="wp-block-paragraph">We can create realistic little files there without adding disposable test files to the repository.</p>



<p class="wp-block-paragraph">Run:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest tests/test_load.py -v</pre></div>



<p class="wp-block-paragraph">Expected:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">test_text_loader_reads_wrapped_sequence PASSED</pre></div>



<h3 class="wp-block-heading">5.2 FASTA Header Discovery</h3>



<p class="wp-block-paragraph">We are using <code>fasta</code> for the first time in this file, so update the import:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.load import fasta, text</pre></div>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def test_fasta_discovers_headers(
    tmp_path: Path,
) -&gt; None:
    &quot;&quot;&quot;Check that get_headers() finds every FASTA record
    and returns each identifier and description in file order.
    &quot;&quot;&quot;
    path = tmp_path / &quot;sample.fasta&quot;

    path.write_text(
        &quot;&quot;&quot;
        &gt;first one
        AAAA
        &gt;second two
        CCCC
        &quot;&quot;&quot;,
        encoding=&quot;utf-8&quot;,
    )

    headers = fasta.get_headers(path)

    assert headers == [
        (&quot;first&quot;, &quot;one&quot;),
        (&quot;second&quot;, &quot;two&quot;),
    ]</pre></div>



<p class="wp-block-paragraph">Run again:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest tests/test_load.py -v</pre></div>



<p class="wp-block-paragraph">Both tests should pass.</p>



<h3 class="wp-block-heading">5.3 Select a FASTA Record by Identifier</h3>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def test_fasta_selects_record_by_identifier(
    tmp_path: Path,
) -&gt; None:
    &quot;&quot;&quot;Check that get_sequence() can find the requested FASTA record
    by identifier and return its identifier, description, and sequence.
    &quot;&quot;&quot;
    path = tmp_path / &quot;sample.fasta&quot;

    path.write_text(
        &quot;&quot;&quot;
        &gt;first one
        AAAA
        &gt;second two
        CCCC
        &quot;&quot;&quot;,
        encoding=&quot;utf-8&quot;,
    )

    record = fasta.get_sequence(
        path,
        identifier=&quot;second&quot;,
    )

    assert record.identifier == &quot;second&quot;
    assert record.description == &quot;two&quot;
    assert record.sequence == &quot;CCCC&quot;</pre></div>



<p class="wp-block-paragraph">Run again:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest tests/test_load.py -v</pre></div>



<p class="wp-block-paragraph">Expected: green.</p>



<h3 class="wp-block-heading">5.4 Select a FASTA Record by Index</h3>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def test_fasta_selects_record_by_index(
    tmp_path: Path,
) -&gt; None:
    &quot;&quot;&quot;Check that get_sequence() can select a FASTA record
    by its zero-based position in the file.
    &quot;&quot;&quot;
    path = tmp_path / &quot;sample.fasta&quot;

    path.write_text(
        &quot;&quot;&quot;
        &gt;first one
        AAAA
        &gt;second two
        CCCC
        &quot;&quot;&quot;,
        encoding=&quot;utf-8&quot;,
    )

    record = fasta.get_sequence(
        path,
        index=1,
    )

    assert record.identifier == &quot;second&quot;
    assert record.sequence == &quot;CCCC&quot;</pre></div>



<p class="wp-block-paragraph">Run again.</p>



<p class="wp-block-paragraph">Expected: green.</p>



<h3 class="wp-block-heading">5.5 Test the Loader Error Types</h3>



<p class="wp-block-paragraph">Now this file needs <code>pytest</code> and our two loader exceptions, so add them now:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">import pytest

from genome_toolkit.exceptions import (
    FormatError,
    RecordNotFoundError,
)</pre></div>



<p class="wp-block-paragraph">First, an empty plain-text file:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def test_text_loader_rejects_empty_file(
    tmp_path: Path,
) -&gt; None:
    &quot;&quot;&quot;Check that an existing text file with no sequence data
    is reported as a Genome Toolkit format problem.
    &quot;&quot;&quot;
    path = tmp_path / &quot;empty.txt&quot;
    path.write_text(&quot;&quot;, encoding=&quot;utf-8&quot;)

    with pytest.raises(FormatError):
        text.get_sequence(path)</pre></div>



<p class="wp-block-paragraph">Next, malformed FASTA:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def test_fasta_rejects_invalid_format(
    tmp_path: Path,
) -&gt; None:
    &quot;&quot;&quot;Check that sequence data appearing before the first FASTA header
    is rejected as malformed FASTA input.
    &quot;&quot;&quot;
    path = tmp_path / &quot;invalid.fasta&quot;

    path.write_text(
        &quot;&quot;&quot;
        ACGT
        &quot;&quot;&quot;,
        encoding=&quot;utf-8&quot;,
    )

    with pytest.raises(FormatError):
        fasta.get_headers(path)</pre></div>



<p class="wp-block-paragraph">Finally, a valid FASTA file with a missing requested record:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def test_fasta_reports_missing_record(
    tmp_path: Path,
) -&gt; None:
    &quot;&quot;&quot;Check that valid FASTA raises RecordNotFoundError
    when the requested sequence identifier does not exist.
    &quot;&quot;&quot;
    path = tmp_path / &quot;sample.fasta&quot;

    path.write_text(
        &quot;&quot;&quot;
        &gt;first
        AAAA
        &quot;&quot;&quot;,
        encoding=&quot;utf-8&quot;,
    )

    with pytest.raises(RecordNotFoundError):
        fasta.get_sequence(
            path,
            identifier=&quot;missing&quot;,
        )</pre></div>



<p class="wp-block-paragraph">Run:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest tests/test_load.py -v</pre></div>



<p class="wp-block-paragraph">All loader tests should now be green.</p>



<p class="wp-block-paragraph">We have checked:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">wrapped plain text
→ PASS

FASTA header discovery
→ PASS

FASTA selection by identifier
→ PASS

FASTA selection by index
→ PASS

empty plain text
→ FormatError

malformed FASTA
→ FormatError

missing FASTA record
→ RecordNotFoundError</pre></div>



<p class="wp-block-paragraph">That finishes the loading layer.</p>



<h2 class="wp-block-heading">6. Test the K-mer Algorithms</h2>



<p class="wp-block-paragraph">Now let us move to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">tests/test_kmer.py</pre></div>



<p class="wp-block-paragraph">There is one important difference from the loaders.</p>



<p class="wp-block-paragraph">The loaders already had explicit failure branches that we could simply give better exception types.</p>



<p class="wp-block-paragraph">The k-mer algorithms do <strong>not</strong> yet have equivalent explicit checks for all of the edge cases we want to test.</p>



<p class="wp-block-paragraph">If we changed <code>kmer.py</code> first, we would hide the useful part where pytest actually exposes the missing behavior.</p>



<p class="wp-block-paragraph">So we will stay in the test file, add the reasonable tests, run them, and only then return to <code>kmer.py</code> once.</p>



<h3 class="wp-block-heading">6.1 Make Sure Overlapping Counting Still Works</h3>



<p class="wp-block-paragraph">Start with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.algorithms import count_kmer
from genome_toolkit.sequence import DNA</pre></div>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def test_count_kmer_counts_overlaps() -&gt; None:
    &quot;&quot;&quot;Check that count_kmer() includes overlapping matches
    instead of skipping positions after the first match.
    &quot;&quot;&quot;
    dna = DNA(
        identifier=&quot;overlap&quot;,
        sequence=&quot;AAAAA&quot;,
    )

    result = count_kmer(
        dna,
        &quot;AAA&quot;,
    )

    assert result.output.count == 3</pre></div>



<p class="wp-block-paragraph">For:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">AAAAA</pre></div>



<p class="wp-block-paragraph">the k-mer:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">AAA</pre></div>



<p class="wp-block-paragraph">appears three times:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">AAA..
.AAA.
..AAA</pre></div>



<p class="wp-block-paragraph">Run:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest tests/test_kmer.py -v</pre></div>



<p class="wp-block-paragraph">Expected:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">test_count_kmer_counts_overlaps PASSED</pre></div>



<p class="wp-block-paragraph">This is a regression test for behavior that already works.</p>



<h3 class="wp-block-heading">6.2 Try a Lowercase k-mer</h3>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def test_count_kmer_normalizes_lowercase_kmer() -&gt; None:
    &quot;&quot;&quot;Check that lowercase input represents the same biological k-mer
    and that the result records the normalized uppercase parameter.
    &quot;&quot;&quot;
    dna = DNA(
        identifier=&quot;example&quot;,
        sequence=&quot;AAAAA&quot;,
    )

    result = count_kmer(
        dna,
        &quot;aaa&quot;,
    )

    assert result.output.count == 3
    assert result.parameters.kmer == &quot;AAA&quot;</pre></div>



<p class="wp-block-paragraph">Do not change the algorithm yet.</p>



<h3 class="wp-block-heading">6.3 Try an Empty k-mer</h3>



<p class="wp-block-paragraph">This is the first algorithm test that needs pytest and <code>AlgorithmInputError</code>, so add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">import pytest

from genome_toolkit.exceptions import AlgorithmInputError</pre></div>



<p class="wp-block-paragraph">Then add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def test_count_kmer_rejects_empty_kmer() -&gt; None:
    &quot;&quot;&quot;Check that an empty string is rejected because it cannot
    represent a meaningful k-mer for this algorithm.
    &quot;&quot;&quot;
    dna = DNA(
        identifier=&quot;example&quot;,
        sequence=&quot;ACGT&quot;,
    )

    with pytest.raises(AlgorithmInputError):
        count_kmer(
            dna,
            &quot;&quot;,
        )</pre></div>



<p class="wp-block-paragraph">Again, do not change <code>kmer.py</code> yet.</p>



<h3 class="wp-block-heading">6.4 Check the Normal Most-frequent-k-mer Result</h3>



<p class="wp-block-paragraph">We now use the second algorithm, so update the algorithm import:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.algorithms import (
    count_kmer,
    find_most_frequent_kmers,
)</pre></div>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def test_frequent_kmers_returns_kmers_and_frequency() -&gt; None:
    &quot;&quot;&quot;Check that the algorithm returns the most frequent k-mer
    together with the number of times it occurs.
    &quot;&quot;&quot;
    dna = DNA(
        identifier=&quot;example&quot;,
        sequence=&quot;AAATTTAAA&quot;,
    )

    result = find_most_frequent_kmers(
        dna,
        k_len=3,
    )

    assert result.output.kmers == [&quot;AAA&quot;]
    assert result.output.frequency == 2</pre></div>



<p class="wp-block-paragraph">The <code>frequency</code> field already exists from Part 4.6.</p>



<p class="wp-block-paragraph">This test is only protecting the result we already built.</p>



<h3 class="wp-block-heading">6.5 Try Invalid <code>k_len</code> Values</h3>



<p class="wp-block-paragraph">For a sequence with length <code>4</code>, these are useful boundary values:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">0
-1
5</pre></div>



<p class="wp-block-paragraph">Instead of writing three nearly identical test functions, we can use pytest parametrization.</p>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">@pytest.mark.parametrize(
    &quot;k_len&quot;,
    [
        0,
        -1,
        5,
    ],
)
def test_frequent_kmers_rejects_invalid_length(
    k_len: int,
) -&gt; None:
    &quot;&quot;&quot;Check that zero, negative, and too-large k-mer lengths
    are all rejected with the same algorithm input error.
    &quot;&quot;&quot;
    dna = DNA(
        identifier=&quot;example&quot;,
        sequence=&quot;ACGT&quot;,
    )

    with pytest.raises(AlgorithmInputError):
        find_most_frequent_kmers(
            dna,
            k_len,
        )</pre></div>



<p class="wp-block-paragraph"><code>@pytest.mark.parametrize()</code> tells pytest to run the same test several times with different values.</p>



<p class="wp-block-paragraph">Conceptually, this one function becomes:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">test with k_len = 0
test with k_len = -1
test with k_len = 5</pre></div>



<p class="wp-block-paragraph">Now run the file:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest tests/test_kmer.py -v</pre></div>



<p class="wp-block-paragraph">The normal behavior stays green, but the new edge cases expose three issue groups:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">overlapping count_kmer()
→ PASS

lowercase k-mer
→ FAIL

empty k-mer
→ FAIL

normal find_most_frequent_kmers()
→ PASS

invalid k_len values
→ FAIL</pre></div>



<p class="wp-block-paragraph">The lowercase case is a real correctness bug.</p>



<p class="wp-block-paragraph">Our validated sequence is uppercase:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">&quot;AAA&quot;</pre></div>



<p class="wp-block-paragraph">but the algorithm compares it directly with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">&quot;aaa&quot;</pre></div>



<p class="wp-block-paragraph">so the strings do not match.</p>



<p class="wp-block-paragraph">The empty k-mer is different. Python can operate on an empty string, but an empty string is not a meaningful k-mer for this algorithm.</p>



<p class="wp-block-paragraph">The invalid <code>k_len</code> values are another undefined boundary. We should reject them deliberately instead of relying on strange slicing behavior or a later generic Python error.</p>



<p class="wp-block-paragraph">Now we know exactly what to change.</p>



<h2 class="wp-block-heading">7. Fix the K-mer Edge Cases</h2>



<p class="wp-block-paragraph">Open:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/algorithms/kmer.py</pre></div>



<p class="wp-block-paragraph">This is the first time the implementation itself needs <code>AlgorithmInputError</code>, so add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.exceptions import AlgorithmInputError</pre></div>



<h3 class="wp-block-heading">7.1 Fix <code>count_kmer()</code></h3>



<p class="wp-block-paragraph">Before the existing calculation, add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not kmer:
    raise AlgorithmInputError(
        &quot;'kmer' must not be empty.&quot;
    )

kmer = kmer.upper()</pre></div>



<p class="wp-block-paragraph">The first check rejects an input that has no meaningful k-mer interpretation.</p>



<p class="wp-block-paragraph">The second line normalizes the parameter once so both the comparison and the structured result use the same uppercase form.</p>



<p class="wp-block-paragraph">Update the relevant public docstring lines so the behavior is visible to callers:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Args:
    sequence: Validated biological sequence to search.
    kmer: K-mer to count. The value is normalized to uppercase.

Raises:
    AlgorithmInputError: If `kmer` is empty.</pre></div>



<p class="wp-block-paragraph">A k-mer longer than the sequence can still naturally return:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">0</pre></div>



<p class="wp-block-paragraph">so we do not invent another error for that case.</p>



<h3 class="wp-block-heading">7.2 Fix <code>find_most_frequent_kmers()</code></h3>



<p class="wp-block-paragraph">Before the existing frequency calculation, add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if k_len &lt;= 0:
    raise AlgorithmInputError(
        &quot;'k_len' must be greater than zero.&quot;
    )

if k_len &gt; len(sequence):
    raise AlgorithmInputError(
        &quot;'k_len' must not be greater than the sequence length.&quot;
    )</pre></div>



<p class="wp-block-paragraph">These checks define the supported algorithm domain before the calculation starts.</p>



<p class="wp-block-paragraph">Update its <code>Raises:</code> documentation too:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Raises:
    AlgorithmInputError: If `k_len` is not greater than zero or is
        greater than the sequence length.</pre></div>



<p class="wp-block-paragraph">We do not add DNA-specific alphabet validation here.</p>



<p class="wp-block-paragraph">These algorithms accept the shared <code>Sequence</code> model and may later be useful with other biological sequence types.</p>



<p class="wp-block-paragraph">Now rerun:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest tests/test_kmer.py -v</pre></div>



<p class="wp-block-paragraph">Everything in <code>test_kmer.py</code> should be green.</p>



<h2 class="wp-block-heading">8. Run the Complete Test Suite</h2>



<p class="wp-block-paragraph">While working on one area, a focused command is convenient:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest tests/test_kmer.py -v</pre></div>



<p class="wp-block-paragraph">Before we finish, run everything:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest -v</pre></div>



<p class="wp-block-paragraph">The complete suite checks:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">sequence models
loaders
algorithms
expected errors</pre></div>



<p class="wp-block-paragraph">This gives us one repeatable command we can run whenever Genome Toolkit changes.</p>



<p class="wp-block-paragraph">Tests do <strong>not</strong> prove that an algorithm is scientifically correct.</p>



<p class="wp-block-paragraph">Scientific correctness may still require known reference results, published methods, benchmarking, comparisons with trusted implementations, and biological validation.</p>



<p class="wp-block-paragraph">What pytest gives us is a repeatable engineering check that the software still behaves the way we deliberately defined.</p>



<h2 class="wp-block-heading">9. Run the Same Tests From VS Code</h2>



<p class="wp-block-paragraph">Everything we have done so far works directly from the terminal:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest -v</pre></div>



<p class="wp-block-paragraph">That command remains our simplest universal way to run the complete test suite because it works regardless of which editor we use.</p>



<p class="wp-block-paragraph">If you use VS Code or VSCodium, we can also connect the editor to the same pytest tests. This gives us a visual list of the tests and lets us run the whole suite, one test file, or one individual test without typing the command each time.</p>



<p class="wp-block-paragraph">This is still the same pytest setup we have already built. VS Code is only giving us a graphical interface around it.</p>



<h3 class="wp-block-heading">9.1 Configure Python Tests and Choose pytest</h3>



<p class="wp-block-paragraph">Open the <strong>Testing</strong> panel from the activity bar on the left.</p>



<p class="wp-block-paragraph">If testing has not been configured for this workspace yet, VS Code will show:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">Configure Python Tests</pre></div>



<p class="wp-block-paragraph">Click it and choose:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">pytest</pre></div>



<p class="wp-block-paragraph">We already installed pytest with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv add --dev pytest</pre></div>



<p class="wp-block-paragraph">so we are not installing a second testing framework here. We are simply telling VS Code which framework our project already uses.</p>



<div class="wp-block-image">
<figure class="aligncenter size-full"><a href="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163307-1.png"><img decoding="async" width="2027" height="1254" src="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163307-1.png" alt="VS Code Testing panel showing Configure Python Tests with pytest selected" class="wp-image-2596" srcset="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163307-1.png 2027w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163307-1-768x475.png 768w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163307-1-1164x720.png 1164w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163307-1-1536x950.png 1536w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163307-1-24x15.png 24w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163307-1-36x22.png 36w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163307-1-48x30.png 48w" sizes="(max-width: 2027px) 100vw, 2027px" /></a></figure>
</div>



<h3 class="wp-block-heading">9.2 Tell VS Code Where Our Tests Live</h3>



<p class="wp-block-paragraph">Next, VS Code asks us to select the directory that contains the tests.</p>



<p class="wp-block-paragraph">Choose:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">tests</pre></div>



<p class="wp-block-paragraph">That is the directory we created in this article:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">tests/
├── test_kmer.py
├── test_load.py
└── test_sequence.py</pre></div>



<p class="wp-block-paragraph">After we select it, VS Code can discover the pytest tests inside those files.</p>



<div class="wp-block-image">
<figure class="aligncenter size-full"><a href="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163407.png"><img decoding="async" width="1029" height="222" src="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163407.png" alt="Selecting the tests directory in VS Code" class="wp-image-2586" srcset="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163407.png 1029w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163407-765x165.png 765w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163407-24x5.png 24w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163407-36x8.png 36w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163407-48x10.png 48w" sizes="(max-width: 1029px) 100vw, 1029px" /></a></figure>
</div>



<h3 class="wp-block-heading">9.3 Run the Complete Test Suite From the Testing Panel</h3>



<p class="wp-block-paragraph">Once discovery finishes, the Testing panel shows our test files and the individual tests inside them.</p>



<p class="wp-block-paragraph">We can run the complete suite by clicking the <strong>Run Test</strong> button at the top level of the project.</p>



<p class="wp-block-paragraph">In our current project, VS Code discovers all of the tests we created in this article. A green check beside a test means its latest run passed.</p>



<div class="wp-block-image">
<figure class="aligncenter size-full"><a href="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163501.png"><img decoding="async" width="602" height="902" src="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163501.png" alt="Discovered Genome Toolkit tests with green passing results" class="wp-image-2588" srcset="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163501.png 602w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163501-481x720.png 481w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163501-16x24.png 16w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163501-24x36.png 24w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163501-32x48.png 32w" sizes="(max-width: 602px) 100vw, 602px" /></a></figure>
</div>



<p class="wp-block-paragraph">This is the graphical equivalent of running:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest -v</pre></div>



<p class="wp-block-paragraph">The terminal command is still useful and remains the command we can rely on in any editor, CI system, or remote environment.</p>



<h3 class="wp-block-heading">9.4 Run One Test File or One Individual Test</h3>



<p class="wp-block-paragraph">We do not always need to run everything.</p>



<p class="wp-block-paragraph">From the Testing panel, we can run:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">the complete test suite
one test file
one individual test</pre></div>



<p class="wp-block-paragraph">We can also work directly inside a test file. VS Code shows a small test-status control beside discovered test functions. After a successful run it appears as a green check, and we can use that control to run that individual test again.</p>



<p class="wp-block-paragraph">For example, while working on:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">test_frequent_kmers_returns_kmers_and_frequency()</pre></div>



<p class="wp-block-paragraph">we can run only that test instead of running all sixteen tests.</p>



<div class="wp-block-image">
<figure class="aligncenter size-full"><a href="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163612.png"><img decoding="async" width="2022" height="1251" src="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163612.png" alt="Run Test control beside an individual test function" class="wp-image-2590" srcset="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163612.png 2022w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163612-768x475.png 768w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163612-1164x720.png 1164w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163612-1536x950.png 1536w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163612-24x15.png 24w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163612-36x22.png 36w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260917_163612-48x30.png 48w" sizes="(max-width: 2022px) 100vw, 2022px" /></a></figure>
</div>



<p class="wp-block-paragraph">This becomes especially useful as the project grows. While changing one algorithm, we can quickly run its focused test, then run the complete suite before we finish.</p>



<h3 class="wp-block-heading">9.5 Ignore Local VS Code Settings</h3>



<p class="wp-block-paragraph">Configuring Python tests in VS Code creates workspace-specific editor settings inside:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">.vscode/</pre></div>



<p class="wp-block-paragraph">Those settings are useful on our own machine, but they are editor-specific and are not part of Genome Toolkit itself.</p>



<p class="wp-block-paragraph">Open:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">.gitignore</pre></div>



<p class="wp-block-paragraph">and add <code>.vscode</code> under the local environment section.</p>



<p class="wp-block-paragraph">Our <code>.gitignore</code> now becomes:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}"># Python-generated files

__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info

# Virtual environments

.venv
.vscode</pre></div>



<p class="wp-block-paragraph">This keeps Python-generated files, the local virtual environment, and our local VS Code workspace settings out of the Git repository.</p>



<p class="wp-block-paragraph">The two ways of running our tests now look like this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">terminal
→ uv run pytest -v

VS Code / VSCodium
→ Testing panel
→ run all tests, one file, or one test</pre></div>



<p class="wp-block-paragraph">Both use the same pytest tests in the same <code>tests/</code> directory.</p>



<h2 class="wp-block-heading">10. Replace <code>README.md</code> With the Final Project README</h2>



<p class="wp-block-paragraph">We have changed Genome Toolkit quite a lot during Part 4.</p>



<p class="wp-block-paragraph">The README should now describe the project we actually have without turning into another tutorial.</p>



<p class="wp-block-paragraph">A new reader mainly needs to know:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">what Genome Toolkit is
what it can currently do
how to install uv
how to set up the project
how to run the application
how to run the tests</pre></div>



<p class="wp-block-paragraph">Open:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">README.md</pre></div>



<p class="wp-block-paragraph">and replace its contents with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}"># Genome Toolkit

Genome Toolkit is a small Python bioinformatics toolkit for working with DNA sequences and learning how common bioinformatics algorithms work.

## Current capabilities

- Represent and validate DNA sequences
- Load sequences from plain-text files
- Read FASTA files
- Select FASTA sequences by identifier or index
- Count occurrences of a k-mer
- Find the most frequent k-mers in a DNA sequence

## Requirements

The project uses [uv](https://docs.astral.sh/uv/) for Python, dependency, and virtual-environment management.

### Install uv

#### Linux

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```

#### macOS

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```

#### Windows

```powershell
powershell -ExecutionPolicy ByPass -c &quot;irm https://astral.sh/uv/install.ps1 | iex&quot;
```

Verify the installation:

```bash
uv --version
```

## Installation

Clone the repository:

```bash
git clone https://github.com/rebelC0der/Genome_Toolkit.git
cd Genome_Toolkit
```

Synchronize the environment:

```bash
uv sync
```

`uv` creates and manages the project's virtual environment automatically.

## Running Genome Toolkit

Run the development application:

```bash
uv run application.py
```

Run the complete tests:

```bash
uv run pytest -v
```

## Repository

Genome Toolkit is developed as an educational bioinformatics project focused on simple, readable implementations of biological data handling and algorithms.</pre></div>



<p class="wp-block-paragraph">The outer example uses four backticks because the README itself contains normal three-backtick code blocks.</p>



<p class="wp-block-paragraph">We are not adding an <code>examples/</code> directory in Part 4.7.</p>



<p class="wp-block-paragraph"><code>application.py</code> remains our development and experiment playground.</p>



<h2 class="wp-block-heading">11. Final Project Structure</h2>



<p class="wp-block-paragraph">After this final reliability pass, our project looks like this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">Genome_Toolkit/
├── .gitignore                         # &lt;-- UPDATED
├── README.md                         # &lt;-- UPDATED
├── application.py
├── pyproject.toml                    # &lt;-- UPDATED
├── uv.lock                           # &lt;-- UPDATED
├── samples/
│   ├── sample.txt
│   └── sample.fasta
├── tests/                            # &lt;-- NEW
│   ├── test_kmer.py                  # &lt;-- NEW
│   ├── test_load.py                  # &lt;-- NEW
│   └── test_sequence.py              # &lt;-- NEW
└── src/
    └── genome_toolkit/
        ├── __init__.py
        ├── exceptions.py             # &lt;-- NEW
        ├── py.typed
        ├── algorithms/
        │   ├── __init__.py
        │   ├── base.py
        │   └── kmer.py               # &lt;-- UPDATED
        ├── load/
        │   ├── __init__.py
        │   ├── fasta.py              # &lt;-- UPDATED
        │   ├── records.py
        │   └── text.py               # &lt;-- UPDATED
        └── sequence/
            ├── __init__.py
            ├── base.py
            └── dna.py</pre></div>



<p class="wp-block-paragraph">Every new file now has a clear reason to exist:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">exceptions.py
→ clear Genome Toolkit-specific failures

tests/
→ repeatable checks around the package</pre></div>



<p class="wp-block-paragraph">We have not added extra architecture just to make the project look more complicated.</p>



<h2 class="wp-block-heading">12. Final Verification</h2>



<p class="wp-block-paragraph">We are ready for one last check.</p>



<p class="wp-block-paragraph">First, synchronize the environment:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv sync</pre></div>



<p class="wp-block-paragraph">Then run every test:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run pytest -v</pre></div>



<p class="wp-block-paragraph">Finally, run the application again:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run application.py</pre></div>



<p class="wp-block-paragraph">We can consider the foundational refactor complete when:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">package syncs
tests pass
application runs
README explains setup</pre></div>



<p class="wp-block-paragraph">Why run <code>application.py</code> if we already have tests?</p>



<p class="wp-block-paragraph">Because they answer slightly different questions.</p>



<p class="wp-block-paragraph">The tests check specific pieces of behavior automatically.</p>



<p class="wp-block-paragraph"><code>application.py</code> still gives us a complete real workflow:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">FASTA
  ↓
SequenceRecord
  ↓
DNA
  ↓
algorithms
  ↓
structured scientific results</pre></div>



<p class="wp-block-paragraph">Running both gives us more confidence than relying on only one of them.</p>



<h2 class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">Part 4.7 finishes the reliability and usability work around our Genome Toolkit refactor.</p>



<p class="wp-block-paragraph">We added four small exception classes:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">GenomeToolkitError
FormatError
RecordNotFoundError
AlgorithmInputError</pre></div>



<p class="wp-block-paragraph">Together they give Genome Toolkit a simple error contract: a predictable way to tell other code what kind of package-level problem happened.</p>



<p class="wp-block-paragraph">We kept normal Python and Pydantic exceptions where they already made sense.</p>



<p class="wp-block-paragraph">Then we added pytest and wrote tests for the three main parts of our package:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">sequence models
loaders
k-mer algorithms</pre></div>



<p class="wp-block-paragraph">The interesting part was that testing did not only confirm what already worked.</p>



<p class="wp-block-paragraph">Our normal cases stayed green:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">overlapping count_kmer()
→ PASSED

normal find_most_frequent_kmers()
→ PASSED</pre></div>



<p class="wp-block-paragraph">But reasonable edge cases found things our manual experiments had missed:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">lowercase k-mer
→ FAILED
→ normalize it once

empty k-mer
→ FAILED
→ reject it clearly

invalid k_len
→ FAILED
→ define the valid range</pre></div>



<p class="wp-block-paragraph">That is exactly the kind of feedback we want from automated tests.</p>



<p class="wp-block-paragraph">We also connected the same pytest suite to VS Code / VSCodium, added <code>.vscode</code> to <code>.gitignore</code>, replaced the README with a much more useful project landing page, and ran one final project verification.</p>



<p class="wp-block-paragraph">Genome Toolkit now has:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">modern package structure
validated biological objects
FASTA and plain-text loaders
generic k-mer algorithms
structured scientific results
clear Genome Toolkit errors
automated tests
practical setup documentation</pre></div>



<p class="wp-block-paragraph">The foundation is finished.</p>



<h2 class="wp-block-heading">New Concepts We Learned</h2>



<ul class="wp-block-list">
<li><strong>Exception</strong> — The normal Python mechanism for reporting that something went wrong.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Custom exception</strong> — An exception class we create ourselves when a problem has specific meaning inside our own project.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Domain exception</strong> — Another way of saying a custom error that describes a problem from our problem area. For Genome Toolkit, examples include malformed biological data or a missing biological record.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Error contract</strong> — The small, predictable set of exception types our package intentionally uses so other code can understand what kind of failure happened.</li>
</ul>



<ul class="wp-block-list">
<li><strong>pytest</strong> — The testing framework we use to discover and run automated Python tests.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Development dependency</strong> — A package we need while developing or testing Genome Toolkit, but not simply to use its scientific functions.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Test</strong> — Code that runs part of our project and checks whether the result matches what we expect.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Arrange, Act, Assert</strong> — Prepare the input, run the code, and check the result.</li>
</ul>



<ul class="wp-block-list">
<li><strong><code>pytest.raises()</code></strong> — A pytest tool for checking that code raises the exception we expect.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Fixture</strong> — Something pytest prepares for a test automatically. We used <code>tmp_path</code> to get a temporary directory for test files.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Parametrization</strong> — Running one test several times with different input values.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Regression test</strong> — A test for something that already works, kept so a future change does not accidentally break that feature.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Edge case</strong> — An unusual but important input near the limits of what a function is expected to handle.</li>
</ul>



<p class="wp-block-paragraph">The main lesson is simple:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Tests do not prove that our biology is correct. They give us repeatable checks that our software behaves the way we have defined, and they help us catch accidental changes before those changes reach a real experiment.</p>
</blockquote>



<h2 class="wp-block-heading">What is Next?</h2>



<p class="wp-block-paragraph">Part 4 is complete.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">PART 4
FOUNDATIONAL REFACTOR COMPLETE

        ↓

PART 5+
EXPAND THE SCIENCE</pre></div>



<p class="wp-block-paragraph">That means our focus can now shift back toward bioinformatics.</p>



<p class="wp-block-paragraph">Instead of repeatedly restructuring the package, we can use the structure we already built to add more science.</p>



<p class="wp-block-paragraph">A future algorithm can follow a simple rhythm:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">BIOLOGICAL QUESTION

What are we trying to learn or calculate?
Why is it biologically useful?

        ↓

RESULT

What information should the algorithm return?

        ↓

IMPLEMENT

Build the algorithm inside Genome Toolkit.

        ↓

TEST

Test normal inputs.
Try important edge cases.
Check expected failures.

        ↓

EXPERIMENT

Run it on representative or real biological data.
Compare parameters and results.

        ↓

REPEAT</pre></div>



<p class="wp-block-paragraph">That is the workflow we want to carry into Part 5.</p>



<p class="wp-block-paragraph">When several algorithms eventually form a genuinely useful workflow, we can keep that workflow in a dedicated example. Until then, <code>application.py</code> remains our place for development and experiments.</p>



<p class="wp-block-paragraph">As Genome Toolkit grows, we may also open it more broadly so other developers or researchers can contribute algorithms and improvements. And later, the toolkit may be used through other interfaces such as an API or an AI agent.</p>



<p class="wp-block-paragraph">The important thing is that those future tools can now build on a package whose main features and errors are much clearer and much better tested.</p>



<p class="wp-block-paragraph">The full source code for Genome Toolkit is available here:</p>



<p class="wp-block-paragraph"><a href="https://github.com/rebelC0der/Genome_Toolkit">https://github.com/rebelC0der/Genome_Toolkit</a></p>



<p class="wp-block-paragraph">I hope adding clear errors, learning how automated tests can expose problems we missed manually, and completing the Genome Toolkit foundation was useful for your bioinformatics and programming journey! If you found this article valuable and want to help us continue building rebelScience, please consider supporting our project. You can explore various ways to contribute here:</p>



<p class="wp-block-paragraph"><a href="https://rebelscience.club/cryptocurrency-donations/">https://rebelscience.club/cryptocurrency-donations/</a></p>



<p class="wp-block-paragraph">Until next time, rebelCoder, signing out.</p>



<h2 class="wp-block-heading">References</h2>



<ul class="wp-block-list">
<li>Corey Schafer — Python Tutorial: Using Try/Except Blocks for Error Handling <a href="https://youtu.be/NIWwJbo-9_8">https://youtu.be/NIWwJbo-9_8</a></li>
</ul>



<ul class="wp-block-list">
<li>Anthony Explains — getting started with pytest (beginner &#8211; intermediate) <a href="https://youtu.be/mzlH8lp4ISA">https://youtu.be/mzlH8lp4ISA</a></li>
</ul>



<ul class="wp-block-list">
<li>pytest documentation <a href="https://docs.pytest.org/">https://docs.pytest.org/</a></li>
</ul>



<ul class="wp-block-list">
<li>pytest parametrization <a href="https://docs.pytest.org/en/stable/how-to/parametrize.html">https://docs.pytest.org/en/stable/how-to/parametrize.html</a></li>
</ul>



<ul class="wp-block-list">
<li>uv development dependencies <a href="https://docs.astral.sh/uv/concepts/projects/dependencies/">https://docs.astral.sh/uv/concepts/projects/dependencies/</a></li>
</ul>



<ul class="wp-block-list">
<li>uv installation <a href="https://docs.astral.sh/uv/getting-started/installation/">https://docs.astral.sh/uv/getting-started/installation/</a></li>
</ul>



<ul class="wp-block-list">
<li>VS Code Python testing <a href="https://code.visualstudio.com/docs/python/testing">https://code.visualstudio.com/docs/python/testing</a></li>
</ul>



<p class="wp-block-paragraph">Video version:</p>



<figure class="wp-block-embed is-provider-youtube wp-block-embed-youtube"><div class="wp-block-embed__wrapper">
https://youtu.be/5cvMr2OFFcc
</div></figure>
]]></content:encoded></item><item><title>Genome Toolkit. Part 4.6: Building Structured Scientific Results</title><link>https://rebelscience.club/2026/09/genome-toolkit-part-4-6-building-structured-scientific-results/</link><guid isPermaLink="true">https://rebelscience.club/2026/09/genome-toolkit-part-4-6-building-structured-scientific-results/</guid><pubDate>Mon, 07 Sep 2026 11:53:50 GMT</pubDate><description>In Part 4.6, we upgrade Genome Toolkit’s k-mer algorithms to accept validated biological Sequence objects and return structured scientific results with metadata, inputs, parameters, and outputs, while keeping the underlying calculations unchanged.
</description><content:encoded><![CDATA[
<p class="wp-block-paragraph">Welcome back to the Genome Toolkit series!</p>



<p class="wp-block-paragraph">In Part 4.5, we completed the basic input side of Genome Toolkit. We can now load a real multi-record FASTA file, select one record, convert the neutral <code>SequenceRecord</code> into a validated <code>DNA</code> model, and pass its sequence into our existing k-mer algorithms.</p>



<p class="wp-block-paragraph">Our current workflow is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">FASTA
  ↓
SequenceRecord
  ↓
DNA
  ↓
dna.sequence
  ↓
count_kmer()
find_most_frequent_kmers()</pre></div>



<p class="wp-block-paragraph">The calculations already work.</p>



<p class="wp-block-paragraph">In Part 4.6, we are not replacing those algorithms or making the mathematics more complicated. We are making the information going into and coming out of them more consistent, reliable, and useful for scientific work.</p>



<p class="wp-block-paragraph">We will see the complete before-and-after picture in a moment, then build that change one small piece at a time.</p>



<h2 class="wp-block-heading">What Are We Trying to Achieve?</h2>



<p class="wp-block-paragraph">Before Part 4.6, the two algorithms look conceptually like this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">count_kmer()

plain string
&quot;AATTTTAAAAC&quot;

+

plain string
&quot;AA&quot;

↓

algorithm

↓

integer
4</pre></div>



<p class="wp-block-paragraph">and:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">find_most_frequent_kmers()

plain string
&quot;AATTTTAAAAC&quot;

+

integer
3

↓

algorithm

↓

list[str]
[&quot;TTT&quot;, &quot;AAA&quot;]</pre></div>



<p class="wp-block-paragraph">There is nothing wrong with those calculated values.</p>



<p class="wp-block-paragraph">The problem is that the result itself does not know where it came from.</p>



<p class="wp-block-paragraph">If we save:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">4</pre></div>



<p class="wp-block-paragraph">or:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">[&quot;TTT&quot;, &quot;AAA&quot;]</pre></div>



<p class="wp-block-paragraph">we have lost most of the context that made those values meaningful.</p>



<p class="wp-block-paragraph">After Part 4.6, the flow becomes:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">FASTA file
    ↓
SequenceRecord
    ↓
validated DNA
    ↓
algorithm
    ↓
structured scientific result</pre></div>



<p class="wp-block-paragraph">The algorithm can still give us the simple value:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Read only the calculated count from the structured result.
count_result.output.count</pre></div>



<p class="wp-block-paragraph">or:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Read only the most frequent k-mer list from the structured result.
frequency_result.output.kmers</pre></div>



<p class="wp-block-paragraph">but the same result object also contains:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">metadata
inputs
parameters
output</pre></div>



<p class="wp-block-paragraph">That is the entire goal of this part.</p>



<p class="wp-block-paragraph">For Genome Toolkit today, that validated object is our <code>DNA</code> model.</p>



<p class="wp-block-paragraph">Later, when we add other biological models such as RNA or Protein, generic algorithms that only need sequence behavior will be able to work with those too.</p>



<p class="wp-block-paragraph">The FASTA file itself does not validate the biology. Our loader reads FASTA structure and returns a neutral <code>SequenceRecord</code>. The biological model then validates the sequence before the algorithm receives it:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">FASTA
  ↓
SequenceRecord
  ↓
DNA validation
  ↓
algorithm</pre></div>



<p class="wp-block-paragraph">So by the time <code>count_kmer()</code> receives our object, we are no longer handing it an arbitrary string with no biological context.</p>



<h2 class="wp-block-heading">The Result Shape</h2>



<p class="wp-block-paragraph">Before we build the models, let us define one word we will use throughout this part: <strong>metadata</strong>.</p>



<p class="wp-block-paragraph"><strong>Metadata simply means data that describes other data.</strong> Our calculated result might be <code>4</code>, but metadata gives us additional information that helps explain that result, such as which algorithm produced it, which Genome Toolkit version was used, and when the calculation happened. It does not change the scientific result itself. It gives that result useful context.</p>



<p class="wp-block-paragraph">In Genome Toolkit, the calculated value belongs under <code>output</code>, while <code>metadata</code> describes the execution that produced it.</p>



<p class="wp-block-paragraph">We want every successful Genome Toolkit algorithm result to use the same top-level vocabulary:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">metadata
inputs
parameters
output</pre></div>



<p class="wp-block-paragraph">For <code>count_kmer()</code>, that will look like:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;application/json&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;JSON&quot;,&quot;language&quot;:&quot;JSON&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;json&quot;}">{
  &quot;metadata&quot;: {
    &quot;toolkit_version&quot;: &quot;0.1.0&quot;,
    &quot;algorithm&quot;: &quot;count_kmer&quot;,
    &quot;timestamp&quot;: &quot;&lt;UTC timestamp&gt;&quot;
  },
  &quot;inputs&quot;: {
    &quot;sequence&quot;: {
      &quot;identifier&quot;: &quot;original_example&quot;,
      &quot;description&quot;: &quot;Original Genome Toolkit test sequence&quot;,
      &quot;length&quot;: 11
    }
  },
  &quot;parameters&quot;: {
    &quot;kmer&quot;: &quot;AA&quot;
  },
  &quot;output&quot;: {
    &quot;count&quot;: 4
  }
}</pre></div>



<p class="wp-block-paragraph">The result is larger than:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">4</pre></div>



<p class="wp-block-paragraph">but now we know what that <code>4</code> actually belongs to.</p>



<p class="wp-block-paragraph">And when we only want the number, we still write:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Access the simple calculated count when the extra context is not needed.
result.output.count</pre></div>



<p class="wp-block-paragraph">So we get both:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">simple access
+
full scientific context</pre></div>



<h2 class="wp-block-heading">Why Use Explicit Result Models?</h2>



<p class="wp-block-paragraph">At first, creating several small Pydantic models for one algorithm may look like unnecessary code.</p>



<p class="wp-block-paragraph">Inside the calculation, we still use ordinary Python values:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">int
str
list
dict</pre></div>



<p class="wp-block-paragraph">We do not create classes for local counters, dictionaries, loops, or intermediate values.</p>



<p class="wp-block-paragraph">The models appear only at the important algorithm boundary, where they give us:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">predictable fields
runtime validation
attribute access
serialization
JSON Schema
stable API / MCP contracts</pre></div>



<p class="wp-block-paragraph">Some models currently contain only one field. That is intentional. They define stable sections of a scientific result, not internal algorithm state.</p>



<p class="wp-block-paragraph">So the rule is simple:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">inside the calculation
→ plain Python

at important boundaries
→ explicit typed models</pre></div>



<p class="wp-block-paragraph">Yes, this creates a little more code. But the repetition is visible and predictable, and each class has one small job. We will only introduce more automation if future algorithms show us, with real evidence, that another abstraction would actually make the code easier to maintain.</p>



<h2 class="wp-block-heading">Why Keep <code>inputs</code>, <code>parameters</code>, and <code>output</code> Separate?</h2>



<p class="wp-block-paragraph">The distinction is practical.</p>



<p class="wp-block-paragraph">The biological sequence is the object being analyzed:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">inputs.sequence</pre></div>



<p class="wp-block-paragraph">The k-mer is a setting chosen for this calculation:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">parameters.kmer</pre></div>



<p class="wp-block-paragraph">And the count is what the algorithm calculated:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">output.count</pre></div>



<p class="wp-block-paragraph">For example:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">same DNA
  +
kmer = &quot;AA&quot;
  ↓
count = 4</pre></div>



<p class="wp-block-paragraph">and:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">same DNA
  +
kmer = &quot;TT&quot;
  ↓
different result</pre></div>



<p class="wp-block-paragraph">The biological input stayed the same.</p>



<p class="wp-block-paragraph">The parameter changed.</p>



<p class="wp-block-paragraph">A future algorithm may have two biological inputs, or completely different parameters, but it can still follow the same top-level result vocabulary.</p>



<h2 class="wp-block-heading">Why Will the Algorithms Accept <code>Sequence</code>?</h2>



<p class="wp-block-paragraph">Our k-mer functions currently accept:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Before Part 4.6, the algorithm accepted a plain sequence string.
sequence: str</pre></div>



<p class="wp-block-paragraph">That made sense when our project was still working with hardcoded strings.</p>



<p class="wp-block-paragraph">But now our application already has:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Our application already has a validated DNA object at this point.
dna</pre></div>



<p class="wp-block-paragraph">which contains:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">identifier
description
validated sequence</pre></div>



<p class="wp-block-paragraph">Before calling the algorithm, we currently throw that context away:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Old approach: discard the model context and pass only its string sequence.
seq = dna.sequence</pre></div>



<p class="wp-block-paragraph">and pass only the string.</p>



<p class="wp-block-paragraph">In Part 4.6, we stop doing that.</p>



<p class="wp-block-paragraph">The generic k-mer algorithms will accept:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># New approach: accept the shared validated Sequence model directly.
sequence: Sequence</pre></div>



<p class="wp-block-paragraph">instead.</p>



<p class="wp-block-paragraph">Our current <code>DNA</code> class inherits from <code>Sequence</code>, so this works directly:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># DNA inherits from Sequence, so the validated object can be passed directly.
count_kmer(dna, kmer)</pre></div>



<p class="wp-block-paragraph">The k-mer algorithm does not need any DNA-specific rule. It only needs to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">measure the sequence
index it
slice it</pre></div>



<p class="wp-block-paragraph">Those behaviors already belong to our shared <code>Sequence</code> model.</p>



<p class="wp-block-paragraph">That gives us a useful rule for future algorithms:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Use the least-specific validated biological type that provides everything the calculation needs.</p>
</blockquote>



<p class="wp-block-paragraph">For generic k-mer calculations:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">Sequence</pre></div>



<p class="wp-block-paragraph">is enough.</p>



<p class="wp-block-paragraph">For a DNA-specific algorithm such as reverse complement, the appropriate input would still be:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">DNA</pre></div>



<h2 class="wp-block-heading">Files Changed in This Part</h2>



<p class="wp-block-paragraph">Before writing any code, let us look at the exact files we will touch.</p>



<p class="wp-block-paragraph">We are adding one new file:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/algorithms/base.py        # &lt;-- NEW</pre></div>



<p class="wp-block-paragraph">and updating three existing files:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/algorithms/kmer.py        # &lt;-- UPDATED
src/genome_toolkit/algorithms/__init__.py    # &lt;-- UPDATED
application.py                               # &lt;-- UPDATED</pre></div>



<p class="wp-block-paragraph">Nothing else changes.</p>



<p class="wp-block-paragraph">Our relevant project structure will become:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">genome_toolkit/
├── application.py                          # &lt;-- UPDATED
├── pyproject.toml
├── uv.lock
├── samples/
│   ├── sample.txt
│   └── sample.fasta
└── src/
    └── genome_toolkit/
        ├── __init__.py
        ├── py.typed
        ├── algorithms/
        │   ├── __init__.py                  # &lt;-- UPDATED
        │   ├── base.py                      # &lt;-- NEW
        │   └── kmer.py                      # &lt;-- UPDATED
        ├── load/
        │   ├── __init__.py
        │   ├── fasta.py
        │   ├── records.py
        │   └── text.py
        └── sequence/
            ├── __init__.py
            ├── base.py
            └── dna.py</pre></div>



<p class="wp-block-paragraph">There are no new dependencies.</p>



<p class="wp-block-paragraph">Pydantic is already part of Genome Toolkit from Part 4.3, so:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">pyproject.toml
uv.lock</pre></div>



<p class="wp-block-paragraph">stay unchanged.</p>



<p class="wp-block-paragraph">Our work is focused entirely on the algorithm input and result layer.</p>



<h2 class="wp-block-heading">1. Create the Shared Result Foundation</h2>



<p class="wp-block-paragraph">We will start with the one new file in this part:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/algorithms/base.py</pre></div>



<p class="wp-block-paragraph">This file will contain only the result models that our algorithms can share.</p>



<p class="wp-block-paragraph">We will build it one small class at a time so the inheritance remains easy to follow.</p>



<p class="wp-block-paragraph">Start with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;&quot;Shared models for structured algorithm results.&quot;&quot;&quot;

# Import UTC-aware timestamps for execution metadata.
from datetime import UTC, datetime

# Import the Pydantic tools used by our result models.
from pydantic import BaseModel, ConfigDict, Field

# Record the installed toolkit version in every result.
from genome_toolkit import __version__

# Build compact input metadata from validated Sequence objects.
from genome_toolkit.sequence import Sequence</pre></div>



<p class="wp-block-paragraph">We already use Pydantic for our biological models, so Part 4.6 does not add a new dependency.</p>



<p class="wp-block-paragraph">The new standard-library import:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Import UTC-aware timestamps for result provenance.
from datetime import UTC, datetime</pre></div>



<p class="wp-block-paragraph">will let us record when a result was created.</p>



<p class="wp-block-paragraph">We also import:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Reuse the installed package version in every structured result.
from genome_toolkit import __version__</pre></div>



<p class="wp-block-paragraph">because the package version is useful provenance. If we return to a saved result later, we want to know which Genome Toolkit version produced it.</p>



<h3 class="wp-block-heading">1. <code>ResultModel</code></h3>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Give every result-related model the same strict configuration.
class ResultModel(BaseModel):
    &quot;&quot;&quot;Base configuration for algorithm result models.&quot;&quot;&quot;

    # Reject unexpected fields so the result shape stays predictable.
    model_config = ConfigDict(extra=&quot;forbid&quot;)</pre></div>



<p class="wp-block-paragraph">This is the common base for all of our result-related models.</p>



<p class="wp-block-paragraph">We already met:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Pydantic ConfigDict controls shared model configuration.
ConfigDict</pre></div>



<p class="wp-block-paragraph">when building our biological models.</p>



<p class="wp-block-paragraph">Here:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Reject unexpected fields so scientific result shapes stay predictable.
extra=&quot;forbid&quot;</pre></div>



<p class="wp-block-paragraph">means Pydantic rejects fields that we did not define.</p>



<p class="wp-block-paragraph">For example, if a result model expects:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">count</pre></div>



<p class="wp-block-paragraph">but some code accidentally supplies:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">counts</pre></div>



<p class="wp-block-paragraph">the unexpected field is rejected instead of silently changing the shape of our scientific result.</p>



<p class="wp-block-paragraph">At this point the inheritance is only:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">Pydantic BaseModel
        ↓
    ResultModel</pre></div>



<h3 class="wp-block-heading">2. <code>ToolkitMetadata</code></h3>



<p class="wp-block-paragraph">Next add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Store information about the toolkit execution itself.
class ToolkitMetadata(ResultModel):
    &quot;&quot;&quot;Toolkit execution information for an algorithm result.&quot;&quot;&quot;

    # Record the toolkit version that produced the result.
    toolkit_version: str = __version__

    # Record the exact algorithm name.
    algorithm: str

    # Create a fresh UTC timestamp for each new result.
    timestamp: datetime = Field(
        default_factory=lambda: datetime.now(UTC)
    )</pre></div>



<p class="wp-block-paragraph">This model stores:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">toolkit_version
algorithm
timestamp</pre></div>



<p class="wp-block-paragraph">The version comes from the same package version source we created earlier in the series.</p>



<p class="wp-block-paragraph">The algorithm name will be supplied when a result is created.</p>



<p class="wp-block-paragraph">The timestamp is generated automatically.</p>



<p class="wp-block-paragraph">Our inheritance is now:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">ResultModel
└── ToolkitMetadata</pre></div>



<h4 class="wp-block-heading">Why Use <code>default_factory</code>?</h4>



<p class="wp-block-paragraph">It may be tempting to write:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># This would calculate the timestamp too early, when the class is defined.
timestamp: datetime = datetime.now(UTC)</pre></div>



<p class="wp-block-paragraph">But that expression would be evaluated when Python creates the class.</p>



<p class="wp-block-paragraph">We need a fresh timestamp for every result.</p>



<p class="wp-block-paragraph">So we use:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Use a factory so every new result receives its own fresh timestamp.
timestamp: datetime = Field(
    default_factory=lambda: datetime.now(UTC)
)</pre></div>



<p class="wp-block-paragraph">The factory runs each time Pydantic creates a new <code>ToolkitMetadata</code> object:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">result 1 → timestamp 1
result 2 → timestamp 2
result 3 → timestamp 3</pre></div>



<p class="wp-block-paragraph">The small:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># This zero-argument function returns the current UTC time when called.
lambda: datetime.now(UTC)</pre></div>



<p class="wp-block-paragraph">is simply a function with no arguments that returns the current UTC time.</p>



<p class="wp-block-paragraph">We use UTC because scientific calculations may run on machines in different time zones.</p>



<h3 class="wp-block-heading">3. <code>SequenceMetadata</code></h3>



<p class="wp-block-paragraph">Next add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Keep compact information about the analyzed biological sequence.
class SequenceMetadata(ResultModel):
    &quot;&quot;&quot;Compact information about an analyzed biological sequence.&quot;&quot;&quot;

    # Preserve the sequence identifier.
    identifier: str

    # Preserve its optional description.
    description: str | None = None

    # Record its length without copying the full sequence.
    length: int</pre></div>



<p class="wp-block-paragraph">This model stores only:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">identifier
description
length</pre></div>



<p class="wp-block-paragraph">It deliberately does not copy the entire biological sequence.</p>



<p class="wp-block-paragraph">Real sequences can become very large. Duplicating a complete genome into every saved algorithm result would make even a small calculation produce a huge result.</p>



<p class="wp-block-paragraph">The real validated <code>Sequence</code> remains the algorithm input.</p>



<p class="wp-block-paragraph">The result keeps compact information describing what was analyzed.</p>



<p class="wp-block-paragraph"><code>SequenceMetadata</code> therefore stays a data-only schema. The small <code>sequence_metadata()</code> function will handle the transformation into that schema.</p>



<h3 class="wp-block-heading">4. <code>sequence_metadata()</code></h3>



<p class="wp-block-paragraph"><code>SequenceMetadata</code> now has one job:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">define the shape of compact sequence metadata</pre></div>



<p class="wp-block-paragraph">We still need a small reusable way to transform a validated <code>Sequence</code> object into that schema.</p>



<p class="wp-block-paragraph">Without a helper, every algorithm would have to repeat:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Manual version: build compact metadata from the validated sequence.
SequenceMetadata(
    identifier=sequence.identifier,
    description=sequence.description,
    length=len(sequence),
)</pre></div>



<p class="wp-block-paragraph">Instead, add one plain helper function below the model:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Convert a validated Sequence into our compact result schema.
def sequence_metadata(
    sequence: Sequence,
) -&gt; SequenceMetadata:
    &quot;&quot;&quot;Build compact metadata from a validated sequence object.

    Args:
        sequence: Validated biological sequence used by an algorithm.

    Returns:
        Compact metadata describing the analyzed sequence.
    &quot;&quot;&quot;
    # Copy only the small shared fields needed by algorithm results.
    return SequenceMetadata(
        identifier=sequence.identifier,
        description=sequence.description,
        length=len(sequence),
    )</pre></div>



<p class="wp-block-paragraph">Now our responsibilities stay very clear:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">SequenceMetadata
→ defines the data/schema

sequence_metadata()
→ performs the small Sequence → SequenceMetadata transformation</pre></div>



<p class="wp-block-paragraph">An algorithm can simply write:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Convert the validated sequence into compact input metadata.
sequence_metadata(sequence)</pre></div>



<p class="wp-block-paragraph">and keep the repeated metadata plumbing out of the scientific calculation.</p>



<h3 class="wp-block-heading">5. <code>SequenceInputs</code></h3>



<p class="wp-block-paragraph">Next add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Group one analyzed biological sequence under `inputs.sequence`.
class SequenceInputs(ResultModel):
    &quot;&quot;&quot;Biological inputs for an algorithm using one sequence.&quot;&quot;&quot;

    # Store compact sequence metadata rather than the entire sequence.
    sequence: SequenceMetadata</pre></div>



<p class="wp-block-paragraph">For our current algorithms, the input section becomes:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">inputs
└── sequence
    ├── identifier
    ├── description
    └── length</pre></div>



<p class="wp-block-paragraph">A future algorithm can define another input model if it needs something different.</p>



<h3 class="wp-block-heading">6. <code>AlgorithmResult</code></h3>



<p class="wp-block-paragraph">Finally add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Give every complete algorithm result the same metadata section.
class AlgorithmResult(ResultModel):
    &quot;&quot;&quot;Base class for structured Genome Toolkit results.&quot;&quot;&quot;

    # Record how and when the algorithm result was produced.
    metadata: ToolkitMetadata</pre></div>



<p class="wp-block-paragraph">Every complete algorithm result will inherit this shared metadata field.</p>



<p class="wp-block-paragraph">Later:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">KmerCountResult
FrequentKmersResult</pre></div>



<p class="wp-block-paragraph">will inherit from <code>AlgorithmResult</code>.</p>



<p class="wp-block-paragraph">We deliberately keep <code>AlgorithmResult</code> small. We are not trying to model jobs, workflow engines, users, server responses, storage systems, or complete input sequences.</p>



<p class="wp-block-paragraph">We only need a reliable base for successful scientific results.</p>



<h3 class="wp-block-heading">A Simple Mental Model</h3>



<p class="wp-block-paragraph">Before moving on, here is the whole relationship in one place:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">ResultModel
    shared Pydantic behavior

ToolkitMetadata
    toolkit version + algorithm + UTC timestamp

SequenceMetadata
    compact information about the analyzed sequence

sequence_metadata()
    Sequence → SequenceMetadata

SequenceInputs
    common input structure for one-sequence algorithms

AlgorithmResult
    shared result foundation</pre></div>



<p class="wp-block-paragraph">The k-mer-specific result classes we add next will build on that shared foundation.</p>



<p class="wp-block-paragraph">The important point is that these classes are not adding new scientific calculations. They are giving the inputs and outputs a predictable structure.</p>



<h3 class="wp-block-heading"><code>base.py</code> Is Finished</h3>



<p class="wp-block-paragraph">That completes the new shared result foundation.</p>



<p class="wp-block-paragraph">We now have:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">ResultModel
ToolkitMetadata
SequenceMetadata
sequence_metadata()
SequenceInputs
AlgorithmResult</pre></div>



<p class="wp-block-paragraph">Each piece has one small responsibility, and we only had to define the shared structure once.</p>



<p class="wp-block-paragraph">Now we can move into our existing <code>kmer.py</code> and use that foundation around the real scientific calculations.</p>



<h2 class="wp-block-heading">Updating <code>kmer.py</code></h2>



<p class="wp-block-paragraph">Open:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/algorithms/kmer.py</pre></div>



<p class="wp-block-paragraph">We will update one algorithm at a time.</p>



<p class="wp-block-paragraph">For each algorithm, we only need to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">1. define its parameters
2. define its output
3. define its full result
4. return that result from the existing calculation</pre></div>



<p class="wp-block-paragraph">The mathematical loops remain unchanged.</p>



<h2 class="wp-block-heading">2. Upgrade <code>count_kmer()</code> First</h2>



<h3 class="wp-block-heading">Adding the Result Models</h3>



<p class="wp-block-paragraph">Open:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/algorithms/kmer.py</pre></div>



<p class="wp-block-paragraph">Its imports now become:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;&quot;K-mer analysis algorithms and structured results.&quot;&quot;&quot;

# Generic k-mer algorithms need only the shared validated Sequence API.
from genome_toolkit.sequence import Sequence

# Import the shared models and metadata helper used to assemble results.
from .base import (
    AlgorithmResult,
    ResultModel,
    SequenceInputs,
    ToolkitMetadata,
    sequence_metadata,
)</pre></div>



<p class="wp-block-paragraph">The important new input type is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Generic k-mer algorithms now depend on the shared validated Sequence type.
Sequence</pre></div>



<p class="wp-block-paragraph">We also import:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Reuse the tiny transformation from Sequence to SequenceMetadata.
sequence_metadata</pre></div>



<p class="wp-block-paragraph">so each algorithm can build the common input metadata without repeating the same field-copying code.</p>



<p class="wp-block-paragraph">Then add the three small models used by <code>count_kmer()</code>:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Define the parameter section for count_kmer().
class KmerCountParameters(ResultModel):
    &quot;&quot;&quot;Parameters passed to `count_kmer()`.&quot;&quot;&quot;

    # K-mer whose overlapping occurrences we want to count.
    kmer: str


# Define the output section for count_kmer().
class KmerCountOutput(ResultModel):
    &quot;&quot;&quot;Values computed by `count_kmer()`.&quot;&quot;&quot;

    # Number of overlapping occurrences found.
    count: int


# Combine metadata, inputs, parameters, and output in one public result.
class KmerCountResult(AlgorithmResult):
    &quot;&quot;&quot;Structured result returned by `count_kmer()`.&quot;&quot;&quot;

    # Biological input metadata.
    inputs: SequenceInputs

    # Parameters chosen for this calculation.
    parameters: KmerCountParameters

    # Value calculated by the algorithm.
    output: KmerCountOutput</pre></div>



<p class="wp-block-paragraph">Their structure is easy to read:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">KmerCountResult
├── metadata
├── inputs
├── parameters
│   └── kmer
└── output
    └── count</pre></div>



<p class="wp-block-paragraph"><code>metadata</code> comes from:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># KmerCountResult inherits the shared metadata field from AlgorithmResult.
AlgorithmResult</pre></div>



<p class="wp-block-paragraph">The other three sections are defined by the specific result.</p>



<h3 class="wp-block-heading">Changing the <code>count_kmer()</code> Function Boundary</h3>



<p class="wp-block-paragraph">Before Part 4.6:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Before Part 4.6: plain string input and primitive integer output.
def count_kmer(
    sequence: str,
    kmer: str,
) -&gt; int:</pre></div>



<p class="wp-block-paragraph">Now change it to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># After Part 4.6: validated Sequence input and structured result output.
def count_kmer(
    sequence: Sequence,
    kmer: str,
) -&gt; KmerCountResult:</pre></div>



<p class="wp-block-paragraph">There are two changes:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">input
str → Sequence

output
int → KmerCountResult</pre></div>



<p class="wp-block-paragraph">The complete function becomes:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Accept a validated Sequence and return a structured result.
def count_kmer(
    sequence: Sequence,
    kmer: str,
) -&gt; KmerCountResult:
    &quot;&quot;&quot;Count overlapping occurrences of a k-mer.

    Args:
        sequence: Validated biological sequence to search.
        kmer: K-mer to count, including overlapping occurrences.

    Returns:
        Structured result containing input metadata, parameters, and count.
    &quot;&quot;&quot;
    # Start the same counter used by our original algorithm.
    kmer_count = 0

    # Visit every valid overlapping starting position.
    for position in range(len(sequence) - (len(kmer) - 1)):
        # Compare the current slice with the requested k-mer.
        if sequence[position : position + len(kmer)] == kmer:
            # Count this matching occurrence.
            kmer_count += 1

    # Package the unchanged count together with its scientific context.
    return KmerCountResult(
        # Record toolkit version, algorithm name, and execution time.
        metadata=ToolkitMetadata(
            algorithm=count_kmer.__name__,
        ),
        # Convert the validated sequence into compact input metadata.
        inputs=SequenceInputs(
            sequence=sequence_metadata(sequence),
        ),
        # Record the k-mer selected for this calculation.
        parameters=KmerCountParameters(
            kmer=kmer,
        ),
        # Store the calculated count.
        output=KmerCountOutput(
            count=kmer_count,
        ),
    )</pre></div>



<p class="wp-block-paragraph">Look carefully at the calculation:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Start the existing counter at zero.
kmer_count = 0

# Slide across every valid overlapping k-mer position.
for position in range(len(sequence) - (len(kmer) - 1)):
    # Count each slice matching the requested k-mer.
    if sequence[position : position + len(kmer)] == kmer:
        kmer_count += 1</pre></div>



<p class="wp-block-paragraph">That is still our existing algorithm.</p>



<p class="wp-block-paragraph">We did not change:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">the loop
the positions
the slice
the comparison
the counter</pre></div>



<p class="wp-block-paragraph">The difference is what we do after the calculation.</p>



<p class="wp-block-paragraph">Before:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Old return: expose only the primitive calculated count.
return kmer_count</pre></div>



<p class="wp-block-paragraph">Now:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># New return: package that same count with its scientific context.
return KmerCountResult(...)</pre></div>



<p class="wp-block-paragraph">The computed integer still exists:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># The underlying calculated integer still exists unchanged.
kmer_count</pre></div>



<p class="wp-block-paragraph">but we place it under:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">output.count</pre></div>



<p class="wp-block-paragraph">alongside the rest of the scientific context.</p>



<p class="wp-block-paragraph">So despite the extra result models around it, the actual k-mer counting algorithm is still the same few lines of Python. We are standardizing the successful result, not replacing the science.</p>



<h2 class="wp-block-heading">3. Apply the Same Pattern to <code>find_most_frequent_kmers()</code></h2>



<p class="wp-block-paragraph">Now we do the same for our second algorithm.</p>



<h3 class="wp-block-heading">Adding Its Result Models</h3>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Define the parameter section for find_most_frequent_kmers().
class FrequentKmersParameters(ResultModel):
    &quot;&quot;&quot;Parameters passed to `find_most_frequent_kmers()`.&quot;&quot;&quot;

    # Length of the k-mers we want to analyze.
    k_len: int


# Define the output section for the frequent-kmer calculation.
class FrequentKmersOutput(ResultModel):
    &quot;&quot;&quot;Values computed by `find_most_frequent_kmers()`.&quot;&quot;&quot;

    # All k-mers tied for the highest observed frequency.
    kmers: list[str]

    # Shared number of occurrences of those k-mers.
    frequency: int


# Combine metadata, inputs, parameters, and output in one public result.
class FrequentKmersResult(AlgorithmResult):
    &quot;&quot;&quot;Structured result returned by `find_most_frequent_kmers()`.&quot;&quot;&quot;

    # Biological input metadata.
    inputs: SequenceInputs

    # Parameters chosen for this calculation.
    parameters: FrequentKmersParameters

    # Values calculated by the algorithm.
    output: FrequentKmersOutput</pre></div>



<p class="wp-block-paragraph">Its shape is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">FrequentKmersResult
├── metadata
├── inputs
├── parameters
│   └── k_len
└── output
    ├── kmers
    └── frequency</pre></div>



<p class="wp-block-paragraph">There is one useful addition here:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Preserve the shared highest frequency instead of throwing it away.
frequency: int</pre></div>



<p class="wp-block-paragraph">Our original algorithm already calculates:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># The existing algorithm already calculates this value internally.
highest_frequency</pre></div>



<p class="wp-block-paragraph">but previously returned only the list of tied k-mers:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Previous output exposed only the tied k-mer strings.
[&quot;TTT&quot;, &quot;AAA&quot;]</pre></div>



<p class="wp-block-paragraph">That meant we calculated the frequency and then threw it away.</p>



<p class="wp-block-paragraph">The structured result keeps it.</p>



<h3 class="wp-block-heading">Changing the Function Boundary</h3>



<p class="wp-block-paragraph">Before:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Before Part 4.6: plain string input and primitive list output.
def find_most_frequent_kmers(
    sequence: str,
    k_len: int,
) -&gt; list[str]:</pre></div>



<p class="wp-block-paragraph">Now:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># After Part 4.6: validated Sequence input and structured result output.
def find_most_frequent_kmers(
    sequence: Sequence,
    k_len: int,
) -&gt; FrequentKmersResult:</pre></div>



<p class="wp-block-paragraph">The complete function is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Accept a validated Sequence and return a structured result.
def find_most_frequent_kmers(
    sequence: Sequence,
    k_len: int,
) -&gt; FrequentKmersResult:
    &quot;&quot;&quot;Find the most frequent k-mers of a requested length.

    Args:
        sequence: Validated biological sequence to analyze.
        k_len: Length of the k-mers to count.

    Returns:
        Structured result containing the most frequent k-mers and their
        shared frequency.
    &quot;&quot;&quot;
    # Count the occurrences of every observed k-mer.
    kmer_frequencies: dict[str, int] = {}

    # Visit every overlapping k-mer of the requested length.
    for i in range(len(sequence) - k_len + 1):
        # Extract the current k-mer.
        kmer = sequence[i : i + k_len]

        # Increase an existing count or create the first count.
        if kmer in kmer_frequencies:
            kmer_frequencies[kmer] += 1
        else:
            kmer_frequencies[kmer] = 1

    # Find the largest count in the frequency table.
    highest_frequency = max(kmer_frequencies.values())

    # Keep all k-mers tied for that highest frequency.
    frequent_kmers = [
        kmer
        for kmer, frequency in kmer_frequencies.items()
        if frequency == highest_frequency
    ]

    # Package the calculated values together with their scientific context.
    return FrequentKmersResult(
        # Record toolkit version, algorithm name, and execution time.
        metadata=ToolkitMetadata(
            algorithm=find_most_frequent_kmers.__name__,
        ),
        # Convert the validated sequence into compact input metadata.
        inputs=SequenceInputs(
            sequence=sequence_metadata(sequence),
        ),
        # Record the requested k-mer length.
        parameters=FrequentKmersParameters(
            k_len=k_len,
        ),
        # Preserve both the k-mers and their shared frequency.
        output=FrequentKmersOutput(
            kmers=frequent_kmers,
            frequency=highest_frequency,
        ),
    )</pre></div>



<p class="wp-block-paragraph">Again, the algorithm itself is still familiar.</p>



<p class="wp-block-paragraph">We still build:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Existing dictionary that stores the count for each observed k-mer.
kmer_frequencies</pre></div>



<p class="wp-block-paragraph">We still scan every overlapping k-mer:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Existing loop: visit every overlapping k-mer of the requested length.
for i in range(len(sequence) - k_len + 1):</pre></div>



<p class="wp-block-paragraph">We still find:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># The existing algorithm already calculates this value internally.
highest_frequency</pre></div>



<p class="wp-block-paragraph">And we still keep every k-mer tied at that frequency.</p>



<p class="wp-block-paragraph">The only difference is that the result now preserves both:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">kmers
frequency</pre></div>



<p class="wp-block-paragraph">together with the input, parameters, and execution metadata.</p>



<h2 class="wp-block-heading">4. Keep <code>kmer.py</code> Easy to Navigate</h2>



<p class="wp-block-paragraph">After updating both algorithms, the file should remain ordered like this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">KmerCountParameters
KmerCountOutput
KmerCountResult
count_kmer()

FrequentKmersParameters
FrequentKmersOutput
FrequentKmersResult
find_most_frequent_kmers()</pre></div>



<p class="wp-block-paragraph">This keeps each result structure beside the algorithm that uses it.</p>



<p class="wp-block-paragraph">We do not need another layer of algorithm classes, result factories, decorators, or orchestration. The shared repetitive pieces live in <code>base.py</code>; the algorithm-specific models and the scientific calculations remain explicit in <code>kmer.py</code>.</p>



<p class="wp-block-paragraph">If a future algorithm family becomes genuinely large, we can split it then. We do not need speculative subpackages now.</p>



<h2 class="wp-block-heading">5. Export the Public Result Types</h2>



<p class="wp-block-paragraph">Our public algorithm package currently exposes only the functions.</p>



<p class="wp-block-paragraph">Update:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/algorithms/__init__.py</pre></div>



<p class="wp-block-paragraph">to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;&quot;Bioinformatics algorithms and structured results.&quot;&quot;&quot;

# Re-export the two public algorithms and their public result types.
from .kmer import (
    FrequentKmersResult,
    KmerCountResult,
    count_kmer,
    find_most_frequent_kmers,
)

# Define the names that form the public algorithms package API.
__all__ = [
    &quot;count_kmer&quot;,
    &quot;find_most_frequent_kmers&quot;,
    &quot;KmerCountResult&quot;,
    &quot;FrequentKmersResult&quot;,
]</pre></div>



<p class="wp-block-paragraph">The normal function API stays simple:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Import the public k-mer functions through the package API.
from genome_toolkit.algorithms import (
    count_kmer,
    find_most_frequent_kmers,
)</pre></div>



<p class="wp-block-paragraph">But the two public result classes are now also available:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Import the public result types when we need them for annotations
# or future external interface declarations.
from genome_toolkit.algorithms import (
    FrequentKmersResult,
    KmerCountResult,
)</pre></div>



<p class="wp-block-paragraph">That will be useful for type annotations and for future external interfaces that need to declare exactly what an algorithm returns.</p>



<p class="wp-block-paragraph">We do not export every small nested model such as:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">KmerCountParameters
KmerCountOutput
FrequentKmersParameters
FrequentKmersOutput</pre></div>



<p class="wp-block-paragraph">Those models support the public result structure internally. They do not need to fill the main algorithm namespace.</p>



<h2 class="wp-block-heading">6. Turn <code>application.py</code> Into a Small Experiment Workflow</h2>



<p class="wp-block-paragraph">We now have all of the pieces we need for a much more useful final <code>application.py</code>.</p>



<p class="wp-block-paragraph">Instead of loading or rebuilding the same biological sequence for every calculation, we will do the expensive and scientifically important preparation once:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">FASTA
  ↓
select one record
  ↓
SequenceRecord
  ↓
validate once
  ↓
DNA</pre></div>



<p class="wp-block-paragraph">Then we reuse that same validated <code>DNA</code> object across every experiment:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">                     ┌→ count_kmer(&quot;CCG&quot;)
                     ├→ count_kmer(&quot;TTCC&quot;)
one validated DNA ───┼→ find_most_frequent_kmers(k_len=4)
                     ├→ find_most_frequent_kmers(k_len=5)
                     └→ k_len sweep from 1 to 8</pre></div>



<p class="wp-block-paragraph">This is an important consequence of the result design we built in this part.</p>



<p class="wp-block-paragraph">When we pass:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Reuse the same validated DNA object.
count_kmer(dna, &quot;CCG&quot;)</pre></div>



<p class="wp-block-paragraph">Python passes the existing object to the function. We are not loading the FASTA file again and we are not creating another full <code>DNA</code> sequence for every algorithm run.</p>



<p class="wp-block-paragraph">The structured result also does <strong>not</strong> copy the complete biological sequence into its metadata.</p>



<p class="wp-block-paragraph">Each result keeps only:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">identifier
description
length</pre></div>



<p class="wp-block-paragraph">through <code>SequenceMetadata</code>.</p>



<p class="wp-block-paragraph">So our experiment can keep one central validated sequence in memory while producing many independent result objects around it:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">one full DNA sequence
        ↓
reused by many calculations
        ↓
many compact result records</pre></div>



<p class="wp-block-paragraph">Each result still has enough provenance to tell us which biological sequence was analyzed, without storing the entire sequence again and again.</p>



<p class="wp-block-paragraph">That becomes increasingly important when we move from our small sample to sequences containing millions or billions of bases.</p>



<h3 class="wp-block-heading">Inspect the FASTA File First</h3>



<p class="wp-block-paragraph">Before selecting a sequence, we can inspect the available FASTA records:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Read only the FASTA headers so we can see which records are available.
fasta_headers = fasta.get_headers(FASTA_SAMPLE)

# Print each zero-based position and parsed header.
for position, header in enumerate(fasta_headers):
    print(f&quot;{position}: {header}&quot;)</pre></div>



<p class="wp-block-paragraph">This lets us choose a real biological record deliberately instead of hardcoding an unknown sequence string.</p>



<p class="wp-block-paragraph">For our final experiment, we will load:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">M57671.1</pre></div>



<p class="wp-block-paragraph">by identifier.</p>



<p class="wp-block-paragraph">This is one of the biological sequences in the <code>sample.fasta</code> file we added in Part 4.5. If you want to use exactly the same FASTA data while following this experiment, you can find the latest version in the Genome Toolkit repository:</p>



<p class="wp-block-paragraph"><a href="https://github.com/rebelC0der/Genome_Toolkit/tree/main/samples">https://github.com/rebelC0der/Genome_Toolkit/tree/main/samples</a></p>



<p class="wp-block-paragraph">The file we use here is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">sample.fasta</pre></div>



<h3 class="wp-block-heading">Load and Validate the Sequence Once</h3>



<p class="wp-block-paragraph">We then create our central biological object:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Load one FASTA record by its biological identifier.
fasta_record_1 = fasta.get_sequence(
    FASTA_SAMPLE,
    identifier=&quot;M57671.1&quot;,
)

# Validate the neutral loader record once as DNA.
dna = DNA.model_validate(
    fasta_record_1,
    from_attributes=True,
)</pre></div>



<p class="wp-block-paragraph">From this point onward, every experiment reuses:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># One shared validated biological object.
dna</pre></div>



<p class="wp-block-paragraph">We do not reload the FASTA file for every k-mer calculation.</p>



<h3 class="wp-block-heading">Run Independent <code>count_kmer()</code> Experiments</h3>



<p class="wp-block-paragraph">We can now run the same algorithm with different parameters:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Run two independent count experiments against the same DNA object.
count_kmers_run_1 = count_kmer(dna, &quot;CCG&quot;)
count_kmers_run_2 = count_kmer(dna, &quot;TTCC&quot;)</pre></div>



<p class="wp-block-paragraph">Each call produces its own structured scientific result.</p>



<p class="wp-block-paragraph">We can inspect the original sequence directly when we want:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># The full validated sequence still exists once on the DNA object.
print(f&quot;nSequence: {dna.sequence}&quot;)</pre></div>



<p class="wp-block-paragraph">and serialize each experiment independently:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Serialize each count experiment as its own complete scientific result.
print(
    f&quot;nExperiment #1:n&quot;
    f&quot;{count_kmers_run_1.model_dump_json(indent=2)}&quot;
)
print(
    f&quot;nExperiment #2:n&quot;
    f&quot;{count_kmers_run_2.model_dump_json(indent=2)}&quot;
)</pre></div>



<p class="wp-block-paragraph">The two results share the same compact sequence context but preserve different algorithm parameters and outputs.</p>



<h3 class="wp-block-heading">Run Independent Frequent K-mer Experiments</h3>



<p class="wp-block-paragraph">We can do exactly the same with our second algorithm:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Analyze the same DNA with two different k-mer lengths.
kmer_freq_run_1 = find_most_frequent_kmers(dna, k_len=4)
kmer_freq_run_2 = find_most_frequent_kmers(dna, k_len=5)</pre></div>



<p class="wp-block-paragraph">Then serialize both runs:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Print the complete structured results for both frequency experiments.
print(
    f&quot;nExperiment #3:n&quot;
    f&quot;{kmer_freq_run_1.model_dump_json(indent=2)}&quot;
)
print(
    f&quot;nExperiment #4:n&quot;
    f&quot;{kmer_freq_run_2.model_dump_json(indent=2)}&quot;
)</pre></div>



<p class="wp-block-paragraph">Again, we have not reloaded or revalidated the sequence.</p>



<p class="wp-block-paragraph">Only the experiment parameter changed.</p>



<h3 class="wp-block-heading">Sweep Through Several <code>k_len</code> Values</h3>



<p class="wp-block-paragraph">The final experiment shows why clean algorithm boundaries are useful.</p>



<p class="wp-block-paragraph">Instead of writing eight separate calls, we can sweep through several k-mer lengths:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Start one compact parameter-sweep experiment.
print(&quot;nExperiment #5:&quot;)

# Reuse the same DNA object for k-mer lengths from 1 through 8.
for klen in range(1, 9):
    kmers_found = find_most_frequent_kmers(dna, klen)

    # Pull only the values we want for this compact experiment summary.
    print(
        f&quot;k-len: {klen}, &quot;
        f&quot;kmers found: {len(kmers_found.output.kmers)}: &quot;
        f&quot;{kmers_found.output.kmers}&quot;
    )</pre></div>



<p class="wp-block-paragraph">This is already starting to look less like a single demonstration and more like a lightweight scientific experiment workflow.</p>



<p class="wp-block-paragraph">One validated input can drive many independent calculations.</p>



<h2 class="wp-block-heading">The Final <code>application.py</code></h2>



<p class="wp-block-paragraph">This is the final application checkpoint for Part 4.6:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from pathlib import Path

from genome_toolkit.algorithms import (
    count_kmer,
    find_most_frequent_kmers,
)
from genome_toolkit.load import fasta
from genome_toolkit.sequence import DNA

SAMPLES_DIR = Path(__file__).resolve().parent / &quot;samples&quot;
FASTA_SAMPLE = SAMPLES_DIR / &quot;sample.fasta&quot;

# Insepct the FASTA File headers:
fasta_headers = fasta.get_headers(FASTA_SAMPLE)

for position, header in enumerate(fasta_headers):
    print(f&quot;{position}: {header}&quot;)

# Load a sequence from FASTA by identifier
fasta_record_1 = fasta.get_sequence(
    FASTA_SAMPLE,
    identifier=&quot;M57671.1&quot;,
)

dna = DNA.model_validate(
    fasta_record_1,
    from_attributes=True,
)

count_kmers_run_1 = count_kmer(dna, &quot;CCG&quot;)
count_kmers_run_2 = count_kmer(dna, &quot;TTCC&quot;)

print(f&quot;\nSequence: {dna.sequence}&quot;)

print(f&quot;\nExperiment #1:\n{count_kmers_run_1.model_dump_json(indent=2)}&quot;)
print(f&quot;\nExperiment #2:\n{count_kmers_run_2.model_dump_json(indent=2)}&quot;)

kmer_freq_run_1 = find_most_frequent_kmers(dna, k_len=4)
kmer_freq_run_2 = find_most_frequent_kmers(dna, k_len=5)

print(f&quot;\nExperiment #3:{kmer_freq_run_1.model_dump_json(indent=2)}&quot;)
print(f&quot;\nExperiment #4:{kmer_freq_run_2.model_dump_json(indent=2)}&quot;)

print(&quot;\nExperiment #5:&quot;)

for klen in range(1, 9):
    kmers_found = find_most_frequent_kmers(dna, klen)
    print(f&quot;k-len: {klen}, kmers found: {kmers_found.output.kmers}&quot;)
</pre></div>



<p class="wp-block-paragraph">Run:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run python application.py</pre></div>



<p class="wp-block-paragraph">The recorded run shown in this article produces:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">0: ('M57671.1', 'Octodon degus insulin mRNA, complete cds')
1: ('ALPHA_GENE_4582', 'one of the alpha proteins')
2: ('BETA_REGULATOR_991', 'a regulatory sequence from the beta cluster')
3: ('GAMMA_OPERON_SEQ3', 'a short sequence from the gamma operon region')
4: ('original_example', 'Original Genome Toolkit test sequence')

Sequence: TGCGTTAGGCTAAACCTTGGGCCCGGCTTAGGCTAAACCTTGGGCCCGGCTTGGGCCCGGCTTAGGCTAAACCTTGGGCCCGGCTTAGGCTAAACCTTGGGCCCGGCTTAGGCTAAACCTTGGGCCGGTTAAGCTTCCGGTTAAGCTTCCGGTTAAGCTTCCGGTTAAGCTTCCGGTTAAGCTTCCGG

Experiment #1:
{
  &quot;metadata&quot;: {
    &quot;toolkit_version&quot;: &quot;0.1.0&quot;,
    &quot;algorithm&quot;: &quot;count_kmer&quot;,
    &quot;timestamp&quot;: &quot;2026-09-10T12:55:09.074227Z&quot;
  },
  &quot;inputs&quot;: {
    &quot;sequence&quot;: {
      &quot;identifier&quot;: &quot;M57671.1&quot;,
      &quot;description&quot;: &quot;Octodon degus insulin mRNA, complete cds&quot;,
      &quot;length&quot;: 188
    }
  },
  &quot;parameters&quot;: {
    &quot;kmer&quot;: &quot;CCG&quot;
  },
  &quot;output&quot;: {
    &quot;count&quot;: 11
  }
}

Experiment #2:
{
  &quot;metadata&quot;: {
    &quot;toolkit_version&quot;: &quot;0.1.0&quot;,
    &quot;algorithm&quot;: &quot;count_kmer&quot;,
    &quot;timestamp&quot;: &quot;2026-09-10T12:55:09.074287Z&quot;
  },
  &quot;inputs&quot;: {
    &quot;sequence&quot;: {
      &quot;identifier&quot;: &quot;M57671.1&quot;,
      &quot;description&quot;: &quot;Octodon degus insulin mRNA, complete cds&quot;,
      &quot;length&quot;: 188
    }
  },
  &quot;parameters&quot;: {
    &quot;kmer&quot;: &quot;TTCC&quot;
  },
  &quot;output&quot;: {
    &quot;count&quot;: 5
  }
}

Experiment #3:{
  &quot;metadata&quot;: {
    &quot;toolkit_version&quot;: &quot;0.1.0&quot;,
    &quot;algorithm&quot;: &quot;find_most_frequent_kmers&quot;,
    &quot;timestamp&quot;: &quot;2026-09-10T12:55:09.074464Z&quot;
  },
  &quot;inputs&quot;: {
    &quot;sequence&quot;: {
      &quot;identifier&quot;: &quot;M57671.1&quot;,
      &quot;description&quot;: &quot;Octodon degus insulin mRNA, complete cds&quot;,
      &quot;length&quot;: 188
    }
  },
  &quot;parameters&quot;: {
    &quot;k_len&quot;: 4
  },
  &quot;output&quot;: {
    &quot;kmers&quot;: [
      &quot;CCGG&quot;
    ],
    &quot;frequency&quot;: 11
  }
}

Experiment #4:{
  &quot;metadata&quot;: {
    &quot;toolkit_version&quot;: &quot;0.1.0&quot;,
    &quot;algorithm&quot;: &quot;find_most_frequent_kmers&quot;,
    &quot;timestamp&quot;: &quot;2026-09-10T12:55:09.074538Z&quot;
  },
  &quot;inputs&quot;: {
    &quot;sequence&quot;: {
      &quot;identifier&quot;: &quot;M57671.1&quot;,
      &quot;description&quot;: &quot;Octodon degus insulin mRNA, complete cds&quot;,
      &quot;length&quot;: 188
    }
  },
  &quot;parameters&quot;: {
    &quot;k_len&quot;: 5
  },
  &quot;output&quot;: {
    &quot;kmers&quot;: [
      &quot;CTTGG&quot;,
      &quot;TTGGG&quot;,
      &quot;TGGGC&quot;,
      &quot;GGGCC&quot;
    ],
    &quot;frequency&quot;: 6
  }
}

Experiment #5:
k-len: 1, kmers found: 1: ['G']
k-len: 2, kmers found: 1: ['GG']
k-len: 3, kmers found: 1: ['GGC']
k-len: 4, kmers found: 1: ['CCGG']
k-len: 5, kmers found: 4: ['CTTGG', 'TTGGG', 'TGGGC', 'GGGCC']
k-len: 6, kmers found: 3: ['CTTGGG', 'TTGGGC', 'TGGGCC']
k-len: 7, kmers found: 2: ['CTTGGGC', 'TTGGGCC']
k-len: 8, kmers found: 1: ['CTTGGGCC']</pre></div>



<p class="wp-block-paragraph">This final application brings together everything we built in Part 4.6.</p>



<p class="wp-block-paragraph">We load and validate the biological sequence once.</p>



<p class="wp-block-paragraph">We reuse that same <code>DNA</code> object across multiple independent experiments.</p>



<p class="wp-block-paragraph">Each algorithm run produces its own structured result.</p>



<p class="wp-block-paragraph">And each result stores compact provenance about the input without duplicating the complete biological sequence.</p>



<h2 class="wp-block-heading">7. Final Flow</h2>



<p class="wp-block-paragraph">Our Part 4.6 workflow now looks like this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">FASTA file
    ↓
inspect headers
    ↓
select one record by identifier
    ↓
SequenceRecord
    ↓
validate once
    ↓
DNA
    │
    ├── count_kmer(&quot;CCG&quot;)
    │      ↓
    │   structured result
    │
    ├── count_kmer(&quot;TTCC&quot;)
    │      ↓
    │   structured result
    │
    ├── find_most_frequent_kmers(k_len=4)
    │      ↓
    │   structured result
    │
    ├── find_most_frequent_kmers(k_len=5)
    │      ↓
    │   structured result
    │
    └── k_len sweep 1..8
           ↓
        structured results</pre></div>



<p class="wp-block-paragraph">The full biological sequence exists once on the validated <code>DNA</code> object.</p>



<p class="wp-block-paragraph">Each algorithm call receives that same object and creates only the result data needed for that run.</p>



<p class="wp-block-paragraph">Inside the result, <code>SequenceMetadata</code> stores:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">identifier
description
length</pre></div>



<p class="wp-block-paragraph">rather than another copy of:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">dna.sequence</pre></div>



<p class="wp-block-paragraph">So our architecture separates two jobs cleanly:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">DNA
→ owns the validated biological sequence

algorithm result
→ records compact provenance + parameters + output</pre></div>



<p class="wp-block-paragraph">That is a much better foundation for running many experiments against the same biological input.</p>



<h3 class="wp-block-heading">Before and After</h3>



<p class="wp-block-paragraph">The two diagrams below summarize what changed in Part 4.6.</p>



<p class="wp-block-paragraph">The first shows our original k-mer functions accepting raw strings and returning primitive Python values. The second shows the finished flow, where biological data comes from FASTA, becomes a validated object, passes through the same scientific algorithms, and returns structured scientific results.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><a href="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145502.png"><img decoding="async" width="1920" height="960" src="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145502.png" alt="" class="wp-image-2551" srcset="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145502.png 1920w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145502-766x383.png 766w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145502-1280x640.png 1280w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145502-1536x768.png 1536w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145502-24x12.png 24w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145502-36x18.png 36w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145502-48x24.png 48w" sizes="(max-width: 1920px) 100vw, 1920px" /></a></figure>
</div>

<div class="wp-block-image">
<figure class="aligncenter size-large"><a href="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145523-1.png"><img decoding="async" width="1280" height="640" src="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145523-1-1280x640.png" alt="" class="wp-image-2552" srcset="https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145523-1-1280x640.png 1280w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145523-1-766x383.png 766w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145523-1-1536x768.png 1536w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145523-1-24x12.png 24w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145523-1-36x18.png 36w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145523-1-48x24.png 48w, https://rebelscience.club/wp-content/uploads/2026/09/Screenshot_20260911_145523-1.png 1920w" sizes="(max-width: 1280px) 100vw, 1280px" /></a></figure>
</div>


<h2 class="wp-block-heading">Why This Helps Reproducibility and Provenance</h2>



<p class="wp-block-paragraph">We introduced <strong>reproducibility</strong> and <strong>provenance</strong> earlier in this series.</p>



<p class="wp-block-paragraph">Part 4.6 gives those ideas a concrete place in our code.</p>



<p class="wp-block-paragraph">A primitive result:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">4</pre></div>



<p class="wp-block-paragraph">contains the computed answer but almost no history.</p>



<p class="wp-block-paragraph">At the same time, a provenance record does not need to duplicate the complete biological input. Our application keeps the validated <code>DNA</code> object once, while each result records only enough compact sequence metadata to identify what was analyzed.</p>



<p class="wp-block-paragraph">Our structured result tells us:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">which toolkit version ran
which algorithm ran
when it ran
which biological sequence was analyzed
which parameters were used
what the algorithm calculated</pre></div>



<p class="wp-block-paragraph">That does not magically make every experiment reproducible by itself. A full experiment may also need information about data sources, software environments, preprocessing, and other steps.</p>



<p class="wp-block-paragraph">But this is a strong foundation.</p>



<p class="wp-block-paragraph">Instead of throwing away useful context at the moment a result is created, Genome Toolkit begins carrying that context forward.</p>



<h2 class="wp-block-heading">What We Are Not Solving Yet</h2>



<p class="wp-block-paragraph">Part 4.6 is about the successful result path.</p>



<p class="wp-block-paragraph">We are <strong>not</strong> going to mix that work with every algorithm edge case.</p>



<p class="wp-block-paragraph">For example:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">empty k-mer
k_len = 0
negative k_len
k_len greater than sequence length</pre></div>



<p class="wp-block-paragraph">still need deliberate behavior.</p>



<p class="wp-block-paragraph">Those cases belong in Part 4.7, where we will write tests first and then define the validation and exceptions those tests show we actually need.</p>



<p class="wp-block-paragraph">Keeping those concerns separate lets us answer two different questions clearly:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">Part 4.6
What does a successful scientific result look like?</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">Part 4.7
What should happen when an input or source is invalid?</pre></div>



<h2 class="wp-block-heading">Our Final Project Structure</h2>



<p class="wp-block-paragraph">Part 4.6 changes one application file and the algorithm package.</p>



<p class="wp-block-paragraph">The complete current structure is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">genome_toolkit/
├── .git/
├── .gitignore
├── .venv/
├── README.md
├── application.py                  # &lt;-- UPDATED
├── pyproject.toml
├── uv.lock
├── samples/
│   ├── sample.txt
│   └── sample.fasta
└── src/
    └── genome_toolkit/
        ├── __init__.py
        ├── py.typed
        ├── algorithms/
        │   ├── __init__.py          # &lt;-- UPDATED
        │   ├── base.py              # &lt;-- NEW
        │   └── kmer.py              # &lt;-- UPDATED
        ├── load/
        │   ├── __init__.py
        │   ├── fasta.py
        │   ├── records.py
        │   └── text.py
        └── sequence/
            ├── __init__.py
            ├── base.py
            └── dna.py</pre></div>



<p class="wp-block-paragraph">Notice what did <strong>not</strong> change:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">samples/
load/
sequence/
pyproject.toml
uv.lock</pre></div>



<p class="wp-block-paragraph">We did not add a dependency, redesign the FASTA loader, or change our DNA validation.</p>



<p class="wp-block-paragraph">Part 4.6 is focused on the algorithm boundary and the scientific result layer.</p>



<h2 class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">In Part 4.6, we changed both sides of our k-mer algorithms.</p>



<p class="wp-block-paragraph">Before:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">raw str
  ↓
algorithm
  ↓
int / list[str]</pre></div>



<p class="wp-block-paragraph">Now:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">validated Sequence
        ↓
same scientific calculation
        ↓
typed structured result</pre></div>



<p class="wp-block-paragraph">Every result follows the same predictable structure:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">metadata
inputs
parameters
output</pre></div>



<p class="wp-block-paragraph">We created <code>algorithms/base.py</code> for the small result components shared across algorithms, then upgraded both k-mer functions without changing their scientific calculations.</p>



<p class="wp-block-paragraph">Our final <code>application.py</code> also demonstrates an important scientific usage pattern:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">load once
validate once
reuse many times</pre></div>



<p class="wp-block-paragraph">We load <code>M57671.1</code> from FASTA once, create one validated <code>DNA</code> object, and reuse that same object across several independent k-mer experiments.</p>



<p class="wp-block-paragraph">The structured results do not store another full copy of the sequence. They keep only compact sequence metadata:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">identifier
description
length</pre></div>



<p class="wp-block-paragraph">alongside the algorithm metadata, parameters, and calculated output.</p>



<p class="wp-block-paragraph">That gives us a useful balance:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">one central validated biological object
+
many independent compact scientific results</pre></div>



<p class="wp-block-paragraph">The final parameter sweep also shows why this matters in practice. Once the input and output boundaries are clean, running a series of related experiments becomes simple Python rather than repeated data-loading code.</p>



<p class="wp-block-paragraph">Most importantly, the k-mer mathematics itself did not change. We improved how biological inputs are reused and how scientific results preserve their context.</p>



<h2 class="wp-block-heading">New Concepts We Learned</h2>



<ul class="wp-block-list">
<li><strong>Structured scientific result</strong> — A typed result object that keeps the calculated value together with useful information about the input, parameters, software, and execution.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Metadata</strong> — Data that describes other data. In our results, metadata gives the calculated output extra context, such as the Genome Toolkit version, algorithm name, and execution timestamp.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Inputs</strong> — The biological objects analyzed by an algorithm. For our current k-mer functions, this is compact metadata describing one sequence.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Parameters</strong> — Settings chosen for a particular algorithm run, such as <code>kmer="AA"</code> or <code>k_len=3</code>.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Output</strong> — The values actually computed by the algorithm, such as a count, a list of frequent k-mers, or their shared frequency.</li>
</ul>



<ul class="wp-block-list">
<li><strong><code>default_factory</code></strong> — A Pydantic <code>Field</code> option that calls a function whenever a new model instance needs its default value. We use it so every result receives a fresh UTC timestamp.</li>
</ul>



<ul class="wp-block-list">
<li><strong>UTC timestamp</strong> — A time recorded using a shared global time standard instead of a machine&#8217;s local time zone.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Compact sequence metadata</strong> — Instead of copying a potentially huge biological sequence into every result, we preserve its identifier, optional description, and length.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Reusable validated input</strong> — We can load and validate one biological sequence once, then pass the same <code>DNA</code> object into many independent algorithm runs without reloading or duplicating the complete sequence for every experiment.</li>
</ul>



<ul class="wp-block-list">
<li><strong><code>sequence_metadata()</code></strong> — A small plain helper function that converts a validated <code>Sequence</code> object into compact <code>SequenceMetadata</code>, while keeping the model itself focused only on the result schema.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Least-specific validated input type</strong> — A generic k-mer calculation needs sequence behavior but no DNA-specific rule, so it accepts <code>Sequence</code>. Our <code>DNA</code> model can still be passed because it inherits from <code>Sequence</code>.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Typed result classes</strong> — <code>KmerCountResult</code> and <code>FrequentKmersResult</code> make each algorithm&#8217;s successful return structure explicit to Python, editors, external callers, and future interfaces.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Serialization</strong> — Pydantic can convert the same structured Python result into JSON with <code>model_dump_json()</code>, without creating a separate algorithm implementation.</li>
</ul>



<p class="wp-block-paragraph">The central idea is simple:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">A scientific calculation should not lose its context the moment it returns a value.</p>
</blockquote>



<h2 class="wp-block-heading">What is Next?</h2>



<p class="wp-block-paragraph">Genome Toolkit now has a much clearer successful-computation path:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">biological data
      ↓
SequenceRecord
      ↓
validated biological model
      ↓
algorithm
      ↓
typed structured scientific result</pre></div>



<p class="wp-block-paragraph">The next question is what happens when something goes wrong.</p>



<p class="wp-block-paragraph">Our loaders and algorithms still have edge cases such as:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">invalid source structure
missing records
empty k-mer
invalid k-mer length</pre></div>



<p class="wp-block-paragraph">In Part 4.7, we will add automated tests and use those tests to define a clear failure contract for Genome Toolkit.</p>



<p class="wp-block-paragraph">We will add only the validation and custom exceptions that our actual tests show we need.</p>



<p class="wp-block-paragraph">The full source code for Genome Toolkit is available here:</p>



<p class="wp-block-paragraph"><a href="https://github.com/rebelC0der/Genome_Toolkit">https://github.com/rebelC0der/Genome_Toolkit</a></p>



<p class="wp-block-paragraph">I hope building typed scientific results, reusing one validated DNA sequence across multiple experiments, and preserving compact provenance for every algorithm run was useful for your bioinformatics and programming journey! If you found this article valuable and want to help us continue building rebelScience, please consider supporting our project. You can explore various ways to contribute here:</p>



<p class="wp-block-paragraph"><a href="https://rebelscience.club/cryptocurrency-donations/">https://rebelscience.club/cryptocurrency-donations/</a></p>



<p class="wp-block-paragraph">Until next time, rebelCoder, signing out.</p>



<h2 class="wp-block-heading">References</h2>



<ul class="wp-block-list">
<li>Pydantic models: <a href="https://docs.pydantic.dev/latest/concepts/models/">https://docs.pydantic.dev/latest/concepts/models/</a></li>



<li>Python <code>datetime</code>: <a href="https://docs.python.org/3/library/datetime.html">https://docs.python.org/3/library/datetime.html</a></li>
</ul>



<p class="wp-block-paragraph">Video version:</p>



<figure class="wp-block-embed aligncenter is-provider-youtube wp-block-embed-youtube"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Genome Toolkit. Part 4.4: Loading Sequences From Plain Text" width="640" height="360" src="https://www.youtube.com/embed/8X9dAnpZUSs?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>
]]></content:encoded></item><item><title>Genome Toolkit. Part 4.5: Adding a Minimal Streaming FASTA Loader</title><link>https://rebelscience.club/2026/09/genome-toolkit-part-4-5-adding-a-minimal-streaming-fasta-loader/</link><guid isPermaLink="true">https://rebelscience.club/2026/09/genome-toolkit-part-4-5-adding-a-minimal-streaming-fasta-loader/</guid><pubDate>Mon, 07 Sep 2026 11:36:14 GMT</pubDate><description>In this part, we add FASTA support to Genome Toolkit and move closer to working with real biological sequence data. We build a minimal streaming FASTA loader that can discover records, parse identifiers and descriptions, and load one sequence by identifier or index without storing unrelated sequences in memory. The data still flows through our existing SequenceRecord and validated DNA model, while the original k-mer algorithms keep producing the same results.
</description><content:encoded><![CDATA[
<p class="wp-block-paragraph">Welcome back to the Genome Toolkit series!</p>



<p class="wp-block-paragraph">In Part 4.4, we built Genome Toolkit’s first loading layer. We moved our original sequence out of <code>application.py</code>, created the neutral <code>SequenceRecord</code>, added <code>text.get_sequence()</code>, loaded a sequence from <code>sample.txt</code>, converted that parsed record into validated <code>DNA</code>, and kept both of our original k-mer algorithms unchanged.</p>



<p class="wp-block-paragraph">Our current data flow is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">sample.txt
    ↓
text.get_sequence()
    ↓
SequenceRecord
    ↓
DNA
    ↓
dna.sequence
    ↓
existing k-mer algorithms</pre></div>



<p class="wp-block-paragraph">That means most of the loading architecture is already in place.</p>



<p class="wp-block-paragraph">In Part 4.5, we do not need another record model or another biological model. We only need to extend the loading layer so Genome Toolkit can understand a much more useful biological sequence format: <strong>FASTA</strong>.</p>



<p class="wp-block-paragraph">We will add one new loader module:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">load/fasta.py</pre></div>



<p class="wp-block-paragraph">and extend our existing loading API.</p>



<p class="wp-block-paragraph">The new input side becomes:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">plain text ─┐
            ├→ SequenceRecord → DNA → dna.sequence → algorithms
FASTA ──────┘</pre></div>



<p class="wp-block-paragraph">Most importantly, we are still <strong>not changing <code>DNA</code> or either k-mer algorithm</strong>. The algorithms still accept a normal Python string, and our application will continue passing them:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">seq = dna.sequence</pre></div>



<p class="wp-block-paragraph">Part 4.6 is where we will finally upgrade the algorithm boundary itself.</p>



<h2 class="wp-block-heading">Starting With Our Working Project</h2>



<p class="wp-block-paragraph">We continue from the exact final state of Part 4.4:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">genome_toolkit/
├── .git/
├── .gitignore
├── .venv/
├── README.md
├── application.py
├── pyproject.toml
├── uv.lock
├── samples/
│   └── sample.txt
└── src/
    └── genome_toolkit/
        ├── __init__.py
        ├── py.typed
        ├── algorithms/
        │   ├── __init__.py
        │   └── kmer.py
        ├── sequence/
        │   ├── __init__.py
        │   ├── base.py
        │   └── dna.py
        └── load/
            ├── __init__.py
            ├── records.py
            └── text.py</pre></div>



<p class="wp-block-paragraph">Our existing loading boundary is already established:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">record = text.get_sequence(
    SAMPLES_DIR / &quot;sample.txt&quot;
)

dna = DNA.model_validate(
    record,
    from_attributes=True,
)</pre></div>



<p class="wp-block-paragraph">And the final bridge into our algorithms is still:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">seq = dna.sequence</pre></div>



<p class="wp-block-paragraph">so the scientific part of <code>application.py</code> remains:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">print(f&quot;Sequence: {seq}&quot;)
print(f&quot;k-mer: {kmer}&quot;)
print(f&quot;Repeats found: {count_kmer(seq, kmer)}&quot;)
print(f&quot;Most frequent k-mer: {find_most_frequent_kmers(seq, k_len)}&quot;)</pre></div>



<p class="wp-block-paragraph">Running:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run python application.py</pre></div>



<p class="wp-block-paragraph">still gives us:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">Sequence: AATTTTAAAAC
k-mer: AA
Repeats found: 4
Most frequent k-mer: ['TTT', 'AAA']</pre></div>



<p class="wp-block-paragraph">That output is our checkpoint again. We are going to change the source format without changing the biological validation or scientific calculations.</p>



<h2 class="wp-block-heading">What Is FASTA and Why Is It So Common?</h2>



<p class="wp-block-paragraph">FASTA is one of the most widely used formats for storing and exchanging biological sequences. It has been used in bioinformatics for decades and has effectively become a <strong>de facto standard</strong> because it is simple, human-readable, easy for software to parse, and supported by a huge range of biological databases and analysis tools.</p>



<p class="wp-block-paragraph">FASTA can store different biological sequence types, including:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">DNA
RNA
protein / amino-acid sequences</pre></div>



<p class="wp-block-paragraph">The FASTA format itself does not decide what those symbols mean. It gives us a simple record structure: a header followed by sequence data. Our biological models still decide whether the sequence is valid DNA, RNA, protein, or something else.</p>



<p class="wp-block-paragraph">FASTA is also one of those formats we will keep running into again and again in bioinformatics. If we download a bacterial genome, a viral genome, a chromosome, an assembled contig, a transcript sequence, or a protein sequence from a biological database, FASTA is one of the most common formats we will see.</p>



<p class="wp-block-paragraph">There is one useful distinction to remember. <strong>Reference and assembled sequences are very commonly distributed as FASTA, while raw sequencing reads are commonly distributed as FASTQ</strong>, because FASTQ stores a quality score for every sequenced symbol in addition to the sequence itself.</p>



<p class="wp-block-paragraph">So as Genome Toolkit moves from small examples toward real biological datasets and experiments, being able to read FASTA is not an optional convenience. It gives us access to one of the standard ways biological sequence data is stored and exchanged.</p>



<p class="wp-block-paragraph">If FASTA is completely new to you, I recommend reading the Wikipedia overview before continuing. It gives a useful introduction to the format, its history, and the basic record structure:</p>



<p class="wp-block-paragraph"><a href="https://en.wikipedia.org/wiki/FASTA_format">https://en.wikipedia.org/wiki/FASTA_format</a></p>



<p class="wp-block-paragraph">If you followed our earlier <strong>DNA Toolkit</strong> series, FASTA should already look familiar. We used FASTA files there to load biological sequences and work with them further, including extracting protein sequences from the loaded DNA.</p>



<p class="wp-block-paragraph">This time, however, we are building FASTA support directly into Genome Toolkit itself. We want the loader to do more than simply read one small example file: it should be able to scan FASTA records one line at a time, find the record we need, and avoid keeping unrelated sequences in memory. We will see exactly how that works later in this article when we build <code>get_sequence()</code>.</p>



<p class="wp-block-paragraph">At its simplest, one FASTA record looks like this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">&gt;identifier optional description
SEQUENCE</pre></div>



<p class="wp-block-paragraph">A single FASTA file can contain many records:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">&gt;original_example Original Genome Toolkit sequence
AATTTT
AAAAC

&gt;second_example Another small sequence
ACGT
ACGT</pre></div>



<p class="wp-block-paragraph">For the first record, we want to extract:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">identifier  → original_example
description → Original Genome Toolkit sequence
sequence    → AATTTTAAAAC</pre></div>



<p class="wp-block-paragraph">Notice that the sequence is wrapped across two lines in the file, but biologically it is still one continuous sequence.</p>



<p class="wp-block-paragraph">Compared with our plain-text loader, FASTA adds:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">headers
identifiers
optional descriptions
wrapped sequence lines
multiple records per file</pre></div>



<p class="wp-block-paragraph">So even though Part 4.5 changes only a few files, <code>fasta.py</code> itself contains enough new logic to deserve a proper walkthrough.</p>



<h2 class="wp-block-heading">Our FASTA Scope for Part 4.5</h2>



<p class="wp-block-paragraph">In this part, we are going to add support for the common FASTA structure used by NCBI and many other sequence resources:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">&gt;identifier optional description
SEQUENCE</pre></div>



<p class="wp-block-paragraph">The first whitespace-separated value after <code>&gt;</code> will become the identifier. Everything after it will be preserved as the optional description.</p>



<p class="wp-block-paragraph">Our new loader will understand this general FASTA structure, but it will not try to decode provider-specific metadata inside the header.</p>



<p class="wp-block-paragraph"><strong>UniProt</strong> is one of the major resources for protein sequences and protein annotation. Its FASTA headers can include information such as protein accessions, entry names, organism names, gene names, and other identifiers connected to the protein record.</p>



<p class="wp-block-paragraph"><strong>Ensembl</strong> is a major genome annotation resource. It provides reference genomes together with annotated genes, transcripts, proteins, and related genomic information for many species. Its FASTA headers can also include extra identifiers and metadata describing the sequence record.</p>



<p class="wp-block-paragraph">So although both resources can provide FASTA files, the text inside their headers can carry more information than the simple:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">&gt;identifier optional description</pre></div>



<p class="wp-block-paragraph">structure we need to understand for Genome Toolkit right now.</p>



<p class="wp-block-paragraph">We do not need to decode all of those provider-specific details yet. For Part 4.5, our goal is simply:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">read a FASTA file
discover records
select one record
return SequenceRecord</pre></div>



<p class="wp-block-paragraph">This simple FASTA loader will be more than enough for Genome Toolkit and many of the real genome-data experiments we are planning next.</p>



<p class="wp-block-paragraph">At the same time, it is useful to see where this loading layer could grow in the future.</p>



<p class="wp-block-paragraph">There are two different kinds of extensions we may eventually need:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">actual biological file formats
+
provider-specific interpretation of data inside those formats</pre></div>



<p class="wp-block-paragraph">For example, our loading package could one day grow into something like this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/
└── load/
    ├── records.py
    │
    ├── fasta.py       # generic FASTA: DNA / RNA / protein sequences
    ├── fastq.py       # sequence + per-base quality scores
    ├── genbank.py     # sequence + rich biological annotations/features
    ├── embl.py        # EMBL sequence records + annotations
    │
    ├── uniprot.py     # interpret UniProt-specific FASTA metadata
    └── ensembl.py     # interpret Ensembl-specific FASTA metadata</pre></div>



<p class="wp-block-paragraph">The important distinction is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">FASTA / FASTQ / GenBank / EMBL
→ actual biological data formats

UniProt / Ensembl / NCBI
→ data providers that may use those formats
  while adding their own metadata and conventions</pre></div>



<p class="wp-block-paragraph">So a generic FASTA record might still look like:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">record = SequenceRecord(
    identifier=&quot;M57671.1&quot;,
    description=&quot;Octodon degus insulin mRNA, complete cds&quot;,
    sequence=&quot;AATTTTAAAAC&quot;,
)</pre></div>



<p class="wp-block-paragraph">while a future UniProt-aware loader could extract more protein-specific metadata from a UniProt FASTA header:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">protein = Protein(
    identifier=&quot;P12345&quot;,
    entry_name=&quot;ABC_HUMAN&quot;,
    organism=&quot;Homo sapiens&quot;,
    taxonomy_id=9606,
    gene=&quot;ABC1&quot;,
    sequence=&quot;MKTLLVAG...&quot;,
)</pre></div>



<p class="wp-block-paragraph">A future FASTQ loader would solve a different problem because FASTQ stores sequence reads together with per-base quality information:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">read = SequenceRead(
    identifier=&quot;read_001&quot;,
    sequence=&quot;ACGTACGT&quot;,
    quality=&quot;IIIIIIII&quot;,
)</pre></div>



<p class="wp-block-paragraph">And a GenBank loader could preserve rich biological annotations and features together with the sequence:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">record = GenBankRecord(
    identifier=&quot;M57671.1&quot;,
    organism=&quot;Octodon degus&quot;,
    sequence=&quot;AATTTTAAAAC...&quot;,
    features=[...],
)</pre></div>



<p class="wp-block-paragraph">Other biological data, such as:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">GFF / GTF annotations
VCF variants</pre></div>



<p class="wp-block-paragraph">would probably deserve their own focused modules rather than being forced into the same sequence-loader shape.</p>



<p class="wp-block-paragraph">That is the long-term direction: Genome Toolkit can gradually become a more robust biological data-processing package by adding focused support for the formats and metadata we actually encounter in our work.</p>



<p class="wp-block-paragraph">But we are not going to build all of that now.</p>



<p class="wp-block-paragraph">For the experiments coming next, a simple generic FASTA loader is more than enough. We will add new loaders, richer metadata handling, and other format-specific functionality only when a real experiment gives us a reason to need them.</p>



<h2 class="wp-block-heading">What Are We Adding?</h2>



<p class="wp-block-paragraph">We need one new module:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/load/fasta.py</pre></div>



<p class="wp-block-paragraph">with three functions:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">_parse_header()     shared FASTA header parsing
get_headers()       discover the records in a FASTA file
get_sequence()      stream through the file and return one record</pre></div>



<p class="wp-block-paragraph">That is the whole FASTA layer for Part 4.5.</p>



<p class="wp-block-paragraph">The existing boundary from Part 4.4 stays the same:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">FASTA
  ↓
SequenceRecord
  ↓
DNA</pre></div>



<p class="wp-block-paragraph">The FASTA loader reads the file and returns neutral sequence data. Our <code>DNA</code> model still decides whether those symbols are valid DNA.</p>



<h2 class="wp-block-heading">Creating <code>fasta.py</code></h2>



<p class="wp-block-paragraph">Inside our existing loading package, create:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/load/fasta.py</pre></div>



<p class="wp-block-paragraph">Our loading package becomes:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">load/
├── __init__.py
├── fasta.py                   # &lt;-- NEW
├── records.py
└── text.py</pre></div>



<p class="wp-block-paragraph">Start with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;&quot;Streaming helpers for reading FASTA files.&quot;&quot;&quot;

from pathlib import Path

from .records import SequenceRecord</pre></div>



<p class="wp-block-paragraph">Both imports are familiar from Part 4.4. <code>Path</code> gives us a consistent way to work with the FASTA filepath, and <code>SequenceRecord</code> is still the neutral object returned by our loaders.</p>



<h2 class="wp-block-heading">Parsing One FASTA Header</h2>



<p class="wp-block-paragraph">Both public FASTA functions need to understand a header such as:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">&gt;M57671.1 Octodon degus insulin mRNA, complete cds</pre></div>



<p class="wp-block-paragraph">Add one small helper:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def _parse_header(line: str) -&gt; tuple[str, str | None]:
    &quot;&quot;&quot;Parse one FASTA header into its identifier and optional description.

    Args:
        line: FASTA header line including the leading `&gt;` marker.

    Returns:
        A tuple containing the sequence identifier and its optional
        description. The description is `None` when the header contains
        only an identifier.

    Raises:
        ValueError: If the header does not contain a sequence identifier.
    &quot;&quot;&quot;
    # Remove the leading &quot;&gt;&quot; marker and surrounding whitespace.
    header = line[1:].strip()

    # Reject a header with no identifier.
    if not header:
        raise ValueError(&quot;A FASTA header must contain a sequence identifier.&quot;)

    # Split the identifier from the optional description.
    parts = header.split(maxsplit=1)

    # Extract the identifier and optional description.
    identifier = parts[0]
    description = parts[1] if len(parts) == 2 else None

    # Return both values.
    return identifier, description</pre></div>



<p class="wp-block-paragraph">The leading underscore in:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">_parse_header()</pre></div>



<p class="wp-block-paragraph">marks it as an internal helper. Our public functions will be <code>get_headers()</code> and <code>get_sequence()</code>.</p>



<p class="wp-block-paragraph">The first line:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">header = line[1:].strip()</pre></div>



<p class="wp-block-paragraph">removes the leading <code>&gt;</code> and surrounding whitespace.</p>



<p class="wp-block-paragraph">Then:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">parts = header.split(maxsplit=1)</pre></div>



<p class="wp-block-paragraph">splits the header only once.</p>



<p class="wp-block-paragraph">For:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">M57671.1 Octodon degus insulin mRNA, complete cds</pre></div>



<p class="wp-block-paragraph">we get:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">identifier  → M57671.1
description → Octodon degus insulin mRNA, complete cds</pre></div>



<p class="wp-block-paragraph">Using <code>maxsplit=1</code> is important because the description can contain many words.</p>



<p class="wp-block-paragraph">Finally:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">return identifier, description</pre></div>



<p class="wp-block-paragraph">returns both values as:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">tuple[str, str | None]</pre></div>



<p class="wp-block-paragraph">The description is optional, so a header such as:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">&gt;M57671.1</pre></div>



<p class="wp-block-paragraph">simply returns:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">(&quot;M57671.1&quot;, None)</pre></div>



<h2 class="wp-block-heading">Discovering FASTA Records With <code>get_headers()</code></h2>



<p class="wp-block-paragraph">Now let us add our first public FASTA function.</p>



<p class="wp-block-paragraph">Its job is simple:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Scan the file and tell us which FASTA records are available.</p>
</blockquote>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def get_headers(
    filepath: str | Path,
) -&gt; list[tuple[str, str | None]]:
    &quot;&quot;&quot;Read FASTA headers and return the available records in file order.

    The function scans the FASTA source without storing sequence data.
    Each returned item contains the record identifier and its optional
    description.

    Args:
        filepath: Path to the FASTA file as a string or `Path` object.

    Returns:
        A list of `(identifier, description)` tuples in file order.
        `description` is `None` when a header contains only an identifier.

    Raises:
        ValueError: If the source contains no FASTA records, sequence data
            appears before the first header, or a FASTA header does not
            contain a sequence identifier.
    &quot;&quot;&quot;
    path = Path(filepath)
    headers: list[tuple[str, str | None]] = []</pre></div>



<p class="wp-block-paragraph">As in Part 4.4, we normalize the filepath with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">path = Path(filepath)</pre></div>



<p class="wp-block-paragraph">Then we create an empty list for the parsed headers.</p>



<p class="wp-block-paragraph">Now scan the file:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">with path.open(&quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
    for raw_line in file:
        line = raw_line.strip()

        if not line:
            continue

        if line.startswith(&quot;&gt;&quot;):
            headers.append(_parse_header(line))
            continue

        if not headers:
            raise ValueError(
                f&quot;'{path}' is not a valid FASTA source: &quot;
                &quot;sequence data appears before the first header.&quot;
            )</pre></div>



<p class="wp-block-paragraph">The logic is compact.</p>



<p class="wp-block-paragraph">Blank lines are ignored.</p>



<p class="wp-block-paragraph">If the line starts with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">&gt;</pre></div>



<p class="wp-block-paragraph">we parse it and append the resulting:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">(identifier, description)</pre></div>



<p class="wp-block-paragraph">tuple to <code>headers</code>.</p>



<p class="wp-block-paragraph">If we encounter sequence data before we have seen even one header:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not headers:</pre></div>



<p class="wp-block-paragraph">the file does not have the FASTA structure we expect, so we stop with a clear error.</p>



<p class="wp-block-paragraph">After the scan:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not headers:
    raise ValueError(f&quot;'{path}' does not contain any FASTA records.&quot;)

return headers</pre></div>



<p class="wp-block-paragraph">An empty file, or a file containing only blank lines, therefore cannot silently return an empty record list.</p>



<p class="wp-block-paragraph">The complete function is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def get_headers(
    filepath: str | Path,
) -&gt; list[tuple[str, str | None]]:
    &quot;&quot;&quot;Read FASTA headers and return the available records in file order.

    The function scans the FASTA source without storing sequence data.
    Each returned item contains the record identifier and its optional
    description.

    Args:
        filepath: Path to the FASTA file as a string or `Path` object.

    Returns:
        A list of `(identifier, description)` tuples in file order.
        `description` is `None` when a header contains only an identifier.

    Raises:
        ValueError: If the source contains no FASTA records, sequence data
            appears before the first header, or a FASTA header does not
            contain a sequence identifier.
    &quot;&quot;&quot;
    # Convert the supplied filepath into a Path.
    path = Path(filepath)

    # Prepare an empty list for discovered headers.
    headers: list[tuple[str, str | None]] = []

    # Open the FASTA file.
    with path.open(&quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
        # Read the file one line at a time.
        for raw_line in file:
            # Remove surrounding whitespace.
            line = raw_line.strip()

            # Skip empty lines.
            if not line:
                continue

            # If this is a header, parse and save it.
            if line.startswith(&quot;&gt;&quot;):
                headers.append(_parse_header(line))
                continue

            # Reject sequence data before the first header.
            if not headers:
                raise ValueError(
                    f&quot;'{path}' is not a valid FASTA source: &quot;
                    &quot;sequence data appears before the first header.&quot;
                )

    # Reject a file containing no FASTA headers.
    if not headers:
        raise ValueError(f&quot;'{path}' does not contain any FASTA records.&quot;)

    # Return all discovered headers.
    return headers</pre></div>



<p class="wp-block-paragraph">Notice that <code>get_headers()</code> does not store the sequence data. It scans the file and collects only the identifiers and descriptions we need for record discovery.</p>



<h2 class="wp-block-heading">Selecting One FASTA Record</h2>



<p class="wp-block-paragraph">Now we can build the main loader:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">get_sequence()</pre></div>



<p class="wp-block-paragraph">We want to select one record either by identifier:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">fasta.get_sequence(
    &quot;samples/sample.fasta&quot;,
    identifier=&quot;M57671.1&quot;,
)</pre></div>



<p class="wp-block-paragraph">or by its zero-based position:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">fasta.get_sequence(
    &quot;samples/sample.fasta&quot;,
    index=0,
)</pre></div>



<p class="wp-block-paragraph">Both calls will return one <code>SequenceRecord</code>.</p>



<h2 class="wp-block-heading">Creating the <code>get_sequence()</code> Boundary</h2>



<p class="wp-block-paragraph">Start with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def get_sequence(
    filepath: str | Path,
    *,
    identifier: str | None = None,
    index: int | None = None,
) -&gt; SequenceRecord:
    &quot;&quot;&quot;Return one FASTA record by identifier or zero-based index.

    Args:
        filepath: Path to the FASTA file.
        identifier: Identifier of the record to load.
        index: Zero-based position of the record to load.

    Returns:
        The selected FASTA record as a neutral `SequenceRecord`.

    Raises:
        ValueError: If selector arguments are invalid, sequence data appears
            before the first header, the requested record cannot be found, or
            the selected record contains no sequence data.
    &quot;&quot;&quot;</pre></div>



<p class="wp-block-paragraph">The <code>*</code> makes <code>identifier</code> and <code>index</code> keyword-only arguments.</p>



<p class="wp-block-paragraph">That gives us clear calls such as:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">identifier=&quot;M57671.1&quot;</pre></div>



<p class="wp-block-paragraph">or:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">index=0</pre></div>



<p class="wp-block-paragraph">instead of relying on positional values whose meaning is harder to see.</p>



<h2 class="wp-block-heading">Requiring One Selector</h2>



<p class="wp-block-paragraph">We want exactly one of these:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">identifier
index</pre></div>



<p class="wp-block-paragraph">So add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if (identifier is None) == (index is None):
    raise ValueError(&quot;Provide exactly one of 'identifier' or 'index'.&quot;)</pre></div>



<p class="wp-block-paragraph">The rule is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">identifier only → valid
index only      → valid
both            → invalid
neither         → invalid</pre></div>



<p class="wp-block-paragraph">Then clean and check the supplied value:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if identifier is not None:
    identifier = identifier.strip()

    if not identifier:
        raise ValueError(&quot;'identifier' must not be empty.&quot;)

if index is not None and index &lt; 0:
    raise ValueError(&quot;'index' is zero-based and must be non-negative.&quot;)</pre></div>



<p class="wp-block-paragraph">Our FASTA indexes are zero-based:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">first record  → 0
second record → 1
third record  → 2</pre></div>



<h2 class="wp-block-heading">Preparing the Record Search</h2>



<p class="wp-block-paragraph">Now add the small amount of state we need while moving through the file:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">path = Path(filepath)
current_index = 0
seen_header = False
selected_identifier: str | None = None
selected_description: str | None = None
sequence_parts: list[str] = []</pre></div>



<p class="wp-block-paragraph"><code>current_index</code> tracks the current FASTA record.</p>



<p class="wp-block-paragraph">We start directly at:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">0</pre></div>



<p class="wp-block-paragraph">because FASTA record indexes are zero-based. That gives us a simple reading order:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">check record 0
advance to 1
check record 1
advance to 2
...</pre></div>



<p class="wp-block-paragraph"><code>seen_header</code> lets us distinguish a real FASTA record from sequence data that appears before the first header, without using a special negative index value.</p>



<p class="wp-block-paragraph"><code>selected_identifier</code> and <code>selected_description</code> stay empty until we find the requested record.</p>



<p class="wp-block-paragraph"><code>sequence_parts</code> will store only the wrapped sequence lines belonging to that selected record.</p>



<h2 class="wp-block-heading">Streaming Through the FASTA File</h2>



<p class="wp-block-paragraph">Now scan the file:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">with path.open(&quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
    for raw_line in file:
        line = raw_line.strip()

        if not line:
            continue

        if line.startswith(&quot;&gt;&quot;):
            if selected_identifier is not None:
                break

            current_identifier, current_description = _parse_header(line)

            matches = (
                current_identifier == identifier
                if identifier is not None
                else current_index == index
            )

            if matches:
                selected_identifier = current_identifier
                selected_description = current_description

            current_index += 1
            continue</pre></div>



<p class="wp-block-paragraph">Every header starts another FASTA record.</p>



<p class="wp-block-paragraph">Once:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">selected_identifier is not None</pre></div>



<p class="wp-block-paragraph">the target record has already been found. The next header therefore means that selected sequence is complete, so:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">break</pre></div>



<p class="wp-block-paragraph">stops reading the file.</p>



<p class="wp-block-paragraph">For each header, we first parse and check the current record using its existing zero-based index. Only after that header has been processed do we advance:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">current_index += 1</pre></div>



<p class="wp-block-paragraph">So the order stays easy to follow:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">check record 0 → advance to 1
check record 1 → advance to 2
check record 2 → advance to 3</pre></div>



<h3 class="wp-block-heading">Matching by Identifier or Index</h3>



<p class="wp-block-paragraph">This part decides whether the current record is the one we want:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">matches = (
    current_identifier == identifier
    if identifier is not None
    else current_index == index
)</pre></div>



<p class="wp-block-paragraph">If the caller supplied an identifier, we compare identifiers.</p>



<p class="wp-block-paragraph">Otherwise, we compare the zero-based index.</p>



<p class="wp-block-paragraph">So:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">identifier=&quot;original_example&quot;</pre></div>



<p class="wp-block-paragraph">means:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">current_identifier == &quot;original_example&quot;</pre></div>



<p class="wp-block-paragraph">while:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">index=4</pre></div>



<p class="wp-block-paragraph">means:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">current_index == 4</pre></div>



<p class="wp-block-paragraph">If the record matches:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if matches:
    selected_identifier = current_identifier
    selected_description = current_description</pre></div>



<p class="wp-block-paragraph">we remember its metadata.</p>



<h3 class="wp-block-heading">Collecting Only the Selected Sequence</h3>



<p class="wp-block-paragraph">For a sequence line:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not seen_header:
    raise ValueError(
        f&quot;'{path}' is not a valid FASTA source: &quot;
        &quot;sequence data appears before the first header.&quot;
    )

if selected_identifier is not None:
    sequence_parts.append(&quot;&quot;.join(line.split()))</pre></div>



<p class="wp-block-paragraph"><code>seen_header</code> remains <code>False</code> until the first FASTA header appears, so sequence data before that point is invalid FASTA structure for our loader.</p>



<p class="wp-block-paragraph">Once the requested record has been selected, its sequence lines are added to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">sequence_parts</pre></div>



<p class="wp-block-paragraph">For:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">AATTTT
AAAAC</pre></div>



<p class="wp-block-paragraph">we collect:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">[&quot;AATTTT&quot;, &quot;AAAAC&quot;]</pre></div>



<p class="wp-block-paragraph">Records before the target are scanned, but their sequence lines are not stored.</p>



<p class="wp-block-paragraph">When the next header appears after the selected record, the loop stops.</p>



<h2 class="wp-block-heading">Finishing the Record</h2>



<p class="wp-block-paragraph">After the scan, we handle three simple cases.</p>



<p class="wp-block-paragraph">If we never saw a header:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not seen_header:
    raise ValueError(f&quot;'{path}' does not contain any FASTA records.&quot;)</pre></div>



<p class="wp-block-paragraph">If the requested identifier or index was never found:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if selected_identifier is None:
    target = identifier if identifier is not None else f&quot;index {index}&quot;

    raise ValueError(
        f&quot;No sequence found matching '{target}' in '{path}'.&quot;
    )</pre></div>



<p class="wp-block-paragraph">Then join the wrapped sequence lines:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">sequence = &quot;&quot;.join(sequence_parts)</pre></div>



<p class="wp-block-paragraph">If the selected record contains no sequence:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not sequence:
    raise ValueError(
        f&quot;Sequence '{selected_identifier}' in '{path}' &quot;
        &quot;does not contain sequence data.&quot;
    )</pre></div>



<p class="wp-block-paragraph">Finally, return our existing neutral record:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">return SequenceRecord(
    identifier=selected_identifier,
    description=selected_description,
    sequence=sequence,
)</pre></div>



<p class="wp-block-paragraph">The FASTA-specific work is finished.</p>



<h2 class="wp-block-heading">The Complete <code>get_sequence()</code> Function</h2>



<p class="wp-block-paragraph">Here is the complete function we just built:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def get_sequence(
    filepath: str | Path,
    *,
    identifier: str | None = None,
    index: int | None = None,
) -&gt; SequenceRecord:
    &quot;&quot;&quot;Return one FASTA record by identifier or zero-based index.

    Args:
        filepath: Path to the FASTA file.
        identifier: Identifier of the record to load.
        index: Zero-based position of the record to load.

    Returns:
        The selected FASTA record as a neutral `SequenceRecord`.

    Raises:
        ValueError: If selector arguments are invalid, sequence data appears
            before the first header, the requested record cannot be found, or
            the selected record contains no sequence data.
    &quot;&quot;&quot;
    # Require exactly one selector: identifier OR index.
    if (identifier is None) == (index is None):
        raise ValueError(&quot;Provide exactly one of 'identifier' or 'index'.&quot;)

    # Validate the identifier if one was provided.
    if identifier is not None:
        identifier = identifier.strip()

        if not identifier:
            raise ValueError(&quot;'identifier' must not be empty.&quot;)

    # Validate the zero-based index if one was provided.
    if index is not None and index &lt; 0:
        raise ValueError(&quot;'index' is zero-based and must be non-negative.&quot;)

    # Convert the supplied filepath into a Path.
    path = Path(filepath)

    # Start with the first FASTA record at index 0.
    current_index = 0

    # Track whether we have seen at least one FASTA header.
    seen_header = False

    # Prepare storage for the selected record.
    selected_identifier: str | None = None
    selected_description: str | None = None
    sequence_parts: list[str] = []

    # Open the FASTA file.
    with path.open(&quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
        # Read the file one line at a time.
        for raw_line in file:
            # Remove surrounding whitespace.
            line = raw_line.strip()

            # Skip empty lines.
            if not line:
                continue

            # If this is a FASTA header:
            if line.startswith(&quot;&gt;&quot;):
                # Stop if the selected record has already ended.
                if selected_identifier is not None:
                    break

                # Mark that a FASTA header has been seen.
                seen_header = True

                # Parse the current header.
                current_identifier, current_description = _parse_header(line)

                # Check whether this record matches the requested ID or index.
                matches = (
                    current_identifier == identifier
                    if identifier is not None
                    else current_index == index
                )

                # Remember the selected identifier and description.
                if matches:
                    selected_identifier = current_identifier
                    selected_description = current_description

                # Move to the next record number after processing this header.
                current_index += 1

                # Continue to the next line.
                continue

            # Reject sequence data before the first header.
            if not seen_header:
                raise ValueError(
                    f&quot;'{path}' is not a valid FASTA source: &quot;
                    &quot;sequence data appears before the first header.&quot;
                )

            # If this is our selected record, save the sequence line.
            if selected_identifier is not None:
                sequence_parts.append(&quot;&quot;.join(line.split()))

    # Reject a file containing no FASTA records.
    if not seen_header:
        raise ValueError(f&quot;'{path}' does not contain any FASTA records.&quot;)

    # Reject the request if the selected record was never found.
    if selected_identifier is None:
        target = identifier if identifier is not None else f&quot;index {index}&quot;

        raise ValueError(
            f&quot;No sequence found matching '{target}' in '{path}'.&quot;
        )

    # Join the selected sequence lines.
    sequence = &quot;&quot;.join(sequence_parts)

    # Reject a selected record containing no sequence data.
    if not sequence:
        raise ValueError(
            f&quot;Sequence '{selected_identifier}' in '{path}' &quot;
            &quot;does not contain sequence data.&quot;
        )

    # Return the selected data as a SequenceRecord.
    return SequenceRecord(
        identifier=selected_identifier,
        description=selected_description,
        sequence=sequence,
    )</pre></div>



<h2 class="wp-block-heading">What Makes This Streaming?</h2>



<p class="wp-block-paragraph">The key line is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">for raw_line in file:</pre></div>



<p class="wp-block-paragraph">Python gives us one line at a time instead of loading the entire FASTA file into another large string first.</p>



<p class="wp-block-paragraph">For <code>get_sequence()</code>:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">records before target
    scan, do not store their sequence

selected record
    store its sequence lines

next header
    stop reading

records after target
    never read</pre></div>



<p class="wp-block-paragraph">So memory mainly depends on the selected sequence rather than every sequence in the FASTA file.</p>



<p class="wp-block-paragraph">We still scan from the beginning until we reach the target. This is not random-access indexing, but it is exactly the behavior we need for our current experiments.</p>



<h2 class="wp-block-heading">Why Use <code>_parse_header()</code> as a Helper?</h2>



<p class="wp-block-paragraph">Both public functions need to interpret FASTA headers in exactly the same way.</p>



<p class="wp-block-paragraph">Without a shared helper:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">get_headers()     header parsing
get_sequence()    same header parsing again</pre></div>



<p class="wp-block-paragraph">With <code>_parse_header()</code>:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">get_headers()  ─┐
                ├→ _parse_header()
get_sequence() ─┘</pre></div>



<p class="wp-block-paragraph">we keep that format rule in one place.</p>



<p class="wp-block-paragraph"><code>get_headers()</code> can focus on discovering the available records, while <code>get_sequence()</code> can focus on finding and returning one selected record.</p>



<p class="wp-block-paragraph">Biological validation is still separate. The FASTA loader understands the file structure; <code>DNA</code> decides whether the selected sequence contains valid DNA symbols.</p>



<h2 class="wp-block-heading">The Complete <code>fasta.py</code></h2>



<p class="wp-block-paragraph">Our complete new module is now:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;&quot;Streaming helpers for reading FASTA files.&quot;&quot;&quot;

from pathlib import Path

from .records import SequenceRecord


def _parse_header(line: str) -&gt; tuple[str, str | None]:
    &quot;&quot;&quot;Parse one FASTA header into its identifier and optional description.

    Args:
        line: FASTA header line including the leading `&gt;` marker.

    Returns:
        A tuple containing the sequence identifier and its optional
        description. The description is `None` when the header contains
        only an identifier.

    Raises:
        ValueError: If the header does not contain a sequence identifier.
    &quot;&quot;&quot;
    # Remove the leading &quot;&gt;&quot; marker and surrounding whitespace.
    header = line[1:].strip()

    # Reject a header with no identifier.
    if not header:
        raise ValueError(&quot;A FASTA header must contain a sequence identifier.&quot;)

    # Split the identifier from the optional description.
    parts = header.split(maxsplit=1)

    # Extract the identifier and optional description.
    identifier = parts[0]
    description = parts[1] if len(parts) == 2 else None

    # Return both values.
    return identifier, description


def get_headers(
    filepath: str | Path,
) -&gt; list[tuple[str, str | None]]:
    &quot;&quot;&quot;Read FASTA headers and return the available records in file order.

    The function scans the FASTA source without storing sequence data.
    Each returned item contains the record identifier and its optional
    description.

    Args:
        filepath: Path to the FASTA file as a string or `Path` object.

    Returns:
        A list of `(identifier, description)` tuples in file order.
        `description` is `None` when a header contains only an identifier.

    Raises:
        ValueError: If the source contains no FASTA records, sequence data
            appears before the first header, or a FASTA header does not
            contain a sequence identifier.
    &quot;&quot;&quot;
    # Convert the supplied filepath into a Path.
    path = Path(filepath)

    # Prepare an empty list for discovered headers.
    headers: list[tuple[str, str | None]] = []

    # Open the FASTA file.
    with path.open(&quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
        # Read the file one line at a time.
        for raw_line in file:
            # Remove surrounding whitespace.
            line = raw_line.strip()

            # Skip empty lines.
            if not line:
                continue

            # If this is a header, parse and save it.
            if line.startswith(&quot;&gt;&quot;):
                headers.append(_parse_header(line))
                continue

            # Reject sequence data before the first header.
            if not headers:
                raise ValueError(
                    f&quot;'{path}' is not a valid FASTA source: &quot;
                    &quot;sequence data appears before the first header.&quot;
                )

    # Reject a file containing no FASTA headers.
    if not headers:
        raise ValueError(f&quot;'{path}' does not contain any FASTA records.&quot;)

    # Return all discovered headers.
    return headers


def get_sequence(
    filepath: str | Path,
    *,
    identifier: str | None = None,
    index: int | None = None,
) -&gt; SequenceRecord:
    &quot;&quot;&quot;Return one FASTA record by identifier or zero-based index.

    Args:
        filepath: Path to the FASTA file.
        identifier: Identifier of the record to load.
        index: Zero-based position of the record to load.

    Returns:
        The selected FASTA record as a neutral `SequenceRecord`.

    Raises:
        ValueError: If selector arguments are invalid, sequence data appears
            before the first header, the requested record cannot be found, or
            the selected record contains no sequence data.
    &quot;&quot;&quot;
    # Require exactly one selector: identifier OR index.
    if (identifier is None) == (index is None):
        raise ValueError(&quot;Provide exactly one of 'identifier' or 'index'.&quot;)

    # Validate the identifier if one was provided.
    if identifier is not None:
        identifier = identifier.strip()

        if not identifier:
            raise ValueError(&quot;'identifier' must not be empty.&quot;)

    # Validate the zero-based index if one was provided.
    if index is not None and index &lt; 0:
        raise ValueError(&quot;'index' is zero-based and must be non-negative.&quot;)

    # Convert the supplied filepath into a Path.
    path = Path(filepath)

    # Start with the first FASTA record at index 0.
    current_index = 0

    # Track whether we have seen at least one FASTA header.
    seen_header = False

    # Prepare storage for the selected record.
    selected_identifier: str | None = None
    selected_description: str | None = None
    sequence_parts: list[str] = []

    # Open the FASTA file.
    with path.open(&quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
        # Read the file one line at a time.
        for raw_line in file:
            # Remove surrounding whitespace.
            line = raw_line.strip()

            # Skip empty lines.
            if not line:
                continue

            # If this is a FASTA header:
            if line.startswith(&quot;&gt;&quot;):
                # Stop if the selected record has already ended.
                if selected_identifier is not None:
                    break

                # Mark that a FASTA header has been seen.
                seen_header = True

                # Parse the current header.
                current_identifier, current_description = _parse_header(line)

                # Check whether this record matches the requested ID or index.
                matches = (
                    current_identifier == identifier
                    if identifier is not None
                    else current_index == index
                )

                # Remember the selected identifier and description.
                if matches:
                    selected_identifier = current_identifier
                    selected_description = current_description

                # Move to the next record number after processing this header.
                current_index += 1

                # Continue to the next line.
                continue

            # Reject sequence data before the first header.
            if not seen_header:
                raise ValueError(
                    f&quot;'{path}' is not a valid FASTA source: &quot;
                    &quot;sequence data appears before the first header.&quot;
                )

            # If this is our selected record, save the sequence line.
            if selected_identifier is not None:
                sequence_parts.append(&quot;&quot;.join(line.split()))

    # Reject a file containing no FASTA records.
    if not seen_header:
        raise ValueError(f&quot;'{path}' does not contain any FASTA records.&quot;)

    # Reject the request if the selected record was never found.
    if selected_identifier is None:
        target = identifier if identifier is not None else f&quot;index {index}&quot;

        raise ValueError(
            f&quot;No sequence found matching '{target}' in '{path}'.&quot;
        )

    # Join the selected sequence lines.
    sequence = &quot;&quot;.join(sequence_parts)

    # Reject a selected record containing no sequence data.
    if not sequence:
        raise ValueError(
            f&quot;Sequence '{selected_identifier}' in '{path}' &quot;
            &quot;does not contain sequence data.&quot;
        )

    # Return the selected data as a SequenceRecord.
    return SequenceRecord(
        identifier=selected_identifier,
        description=selected_description,
        sequence=sequence,
    )</pre></div>



<h2 class="wp-block-heading">Exporting FASTA Beside Plain Text</h2>



<p class="wp-block-paragraph">We have added the implementation, so now update:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/load/__init__.py</pre></div>



<p class="wp-block-paragraph">Our current file is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;&quot;Sequence loaders and neutral records.&quot;&quot;&quot;

from . import text
from .records import SequenceRecord

__all__ = [&quot;SequenceRecord&quot;, &quot;text&quot;]</pre></div>



<p class="wp-block-paragraph">Add the new source:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;&quot;Sequence loaders and neutral records.&quot;&quot;&quot;

from . import fasta, text
from .records import SequenceRecord

__all__ = [&quot;SequenceRecord&quot;, &quot;fasta&quot;, &quot;text&quot;]</pre></div>



<p class="wp-block-paragraph">Now our public loading API grows naturally:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">text.get_sequence(...)
fasta.get_headers(...)
fasta.get_sequence(...)</pre></div>



<p class="wp-block-paragraph">We do not need names such as:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">get_text_sequence()
get_fasta_sequence()
get_fasta_headers()</pre></div>



<p class="wp-block-paragraph">in one flat namespace. The source module already tells us which format we are working with.</p>



<h2 class="wp-block-heading">Adding a Repeatable FASTA Sample</h2>



<p class="wp-block-paragraph">For Part 4.5, we will use a multi-record FASTA sample rather than a file created only around our short test sequence.</p>



<p class="wp-block-paragraph">Create:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">samples/sample.fasta</pre></div>



<p class="wp-block-paragraph">with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">&gt;M57671.1 Octodon degus insulin mRNA, complete cds
TGCGTTAGGCTAAACCTTGGGCCCGGCTTAGGCTAAACCTTGGGCCCGGCTTGGGCCCGGCTT
AGGCTAAACCTTGGGCCCGGCTTAGGCTAAACCTTGGGCCCGGCTTAGGCTAAACCTTGGGCC

&gt;ALPHA_GENE_4582 one of the alpha proteins
TGCGTTAGGCTAAACCTTGGGCCCGGCTTAGGCTAAACCTTGGGCCCGGCTTGGGCCCGGCTT
AGGCTAAACCTTGGGCCCGGCTTAGGCTAAACCTTGGGCCCGGCTTAGGCTAAACCTTGGGCC

&gt;BETA_REGULATOR_991 a regulatory sequence from the beta cluster
CCGGTTAAGCTTCCGGTTAAGCTTCCGGTTAAGCTTCCGGTTAAGCTTCCGGTTAAGCTTCC
GGTTAAGCTTCCGGTTAAGCTTCCGGTTAAGCTTCCGGTTAAGCTTCCGGTTAAGCTTCCGG

&gt;GAMMA_OPERON_SEQ3 a short sequence from the gamma operon region
GATTACAGATTACA

&gt;original_example Original Genome Toolkit test sequence
AATTTT
AAAAC</pre></div>



<p class="wp-block-paragraph">This gives us a much better FASTA sample for the loader because the file contains several records, different identifiers and descriptions, and sequences of different lengths.</p>



<p class="wp-block-paragraph">At the end, we keep our original Genome Toolkit test sequence:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">AATTTTAAAAC</pre></div>



<p class="wp-block-paragraph">as its own FASTA record:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">&gt;original_example Original Genome Toolkit test sequence
AATTTT
AAAAC</pre></div>



<p class="wp-block-paragraph">That lets us demonstrate a realistic multi-record FASTA file while still preserving the exact scientific checkpoint we have used throughout the series.</p>



<p class="wp-block-paragraph">Our samples directory is now:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">samples/
├── sample.txt
└── sample.fasta              # &lt;-- NEW</pre></div>



<h2 class="wp-block-heading">Checking the Available FASTA Records</h2>



<p class="wp-block-paragraph">Before changing our main application, let us verify <code>get_headers()</code> by itself.</p>



<p class="wp-block-paragraph">Use:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.load import fasta


headers = fasta.get_headers(&quot;samples/sample.fasta&quot;)
print(headers)</pre></div>



<p class="wp-block-paragraph">Running it should give us:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">[
    ('M57671.1', 'Octodon degus insulin mRNA, complete cds'),
    ('ALPHA_GENE_4582', 'one of the alpha proteins'),
    ('BETA_REGULATOR_991', 'a regulatory sequence from the beta cluster'),
    ('GAMMA_OPERON_SEQ3', 'a short sequence from the gamma operon region'),
    ('original_example', 'Original Genome Toolkit test sequence')
]</pre></div>



<p class="wp-block-paragraph">In a normal terminal, Python will usually print that list on one line. The important part is that Genome Toolkit discovers all five records in file order and keeps each identifier together with its description.</p>



<p class="wp-block-paragraph">The list position also gives us the zero-based indexes:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">index 0 → M57671.1
index 1 → ALPHA_GENE_4582
index 2 → BETA_REGULATOR_991
index 3 → GAMMA_OPERON_SEQ3
index 4 → original_example</pre></div>



<p class="wp-block-paragraph">For our main application, we will select our existing checkpoint by identifier:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">identifier=&quot;original_example&quot;</pre></div>



<p class="wp-block-paragraph">That makes the intention clearer than depending on where the record happens to appear in the file.</p>



<h2 class="wp-block-heading">Updating Our Existing <code>application.py</code></h2>



<p class="wp-block-paragraph">Part 4.4 used the plain-text source:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.load import text</pre></div>



<p class="wp-block-paragraph">and:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">record = text.get_sequence(
    SAMPLES_DIR / &quot;sample.txt&quot;
)</pre></div>



<p class="wp-block-paragraph">Part 4.5 changes only that source-specific part.</p>



<p class="wp-block-paragraph">Replace the import with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.load import fasta</pre></div>



<p class="wp-block-paragraph">and load our original Genome Toolkit test sequence by identifier:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">record = fasta.get_sequence(
    SAMPLES_DIR / &quot;sample.fasta&quot;,
    identifier=&quot;original_example&quot;,
)</pre></div>



<p class="wp-block-paragraph">Everything after the returned <code>SequenceRecord</code> stays exactly the same.</p>



<p class="wp-block-paragraph">The complete <code>application.py</code> is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from pathlib import Path

from genome_toolkit.algorithms import (
    count_kmer,
    find_most_frequent_kmers,
)
from genome_toolkit.load import fasta
from genome_toolkit.sequence import DNA


SAMPLES_DIR = Path(__file__).resolve().parent / &quot;samples&quot;


record = fasta.get_sequence(
    SAMPLES_DIR / &quot;sample.fasta&quot;,
    identifier=&quot;original_example&quot;,
)

dna = DNA.model_validate(
    record,
    from_attributes=True,
)

seq = dna.sequence
kmer = &quot;AA&quot;
k_len = 3


print(f&quot;Sequence: {seq}&quot;)
print(f&quot;k-mer: {kmer}&quot;)
print(f&quot;Repeats found: {count_kmer(seq, kmer)}&quot;)
print(f&quot;Most frequent k-mer: {find_most_frequent_kmers(seq, k_len)}&quot;)</pre></div>



<p class="wp-block-paragraph">Our workflow is now:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">sample.fasta
     ↓
fasta.get_sequence()
     ↓
SequenceRecord
     ↓
DNA.model_validate(...)
     ↓
validated DNA
     ↓
dna.sequence
     ↓
existing k-mer algorithms</pre></div>



<p class="wp-block-paragraph">Notice again what did <strong>not</strong> change:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/sequence/
src/genome_toolkit/algorithms/</pre></div>



<p class="wp-block-paragraph">We have not changed the algorithm signatures, loops, or return values.</p>



<h2 class="wp-block-heading">Running the Complete Part 4.5 Application</h2>



<p class="wp-block-paragraph">Run:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run python application.py</pre></div>



<p class="wp-block-paragraph">We should still see:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">Sequence: AATTTTAAAAC
k-mer: AA
Repeats found: 4
Most frequent k-mer: ['TTT', 'AAA']</pre></div>



<p class="wp-block-paragraph">The biological input now came from:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">samples/sample.fasta</pre></div>



<p class="wp-block-paragraph">instead of:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">samples/sample.txt</pre></div>



<p class="wp-block-paragraph">but the same validated sequence reached the same algorithms and produced the same result.</p>



<p class="wp-block-paragraph">That is exactly the kind of continuity we want during this refactor.</p>



<h2 class="wp-block-heading">Our New Project Structure</h2>



<p class="wp-block-paragraph">At the end of Part 4.5, our project looks like this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">genome_toolkit/
├── .git/
├── .gitignore
├── .venv/
├── README.md
├── application.py              # &lt;-- UPDATED
├── pyproject.toml
├── uv.lock
├── samples/
│   ├── sample.txt
│   └── sample.fasta            # &lt;-- NEW
└── src/
    └── genome_toolkit/
        ├── __init__.py
        ├── py.typed
        ├── algorithms/
        │   ├── __init__.py
        │   └── kmer.py
        ├── sequence/
        │   ├── __init__.py
        │   ├── base.py
        │   └── dna.py
        └── load/
            ├── __init__.py      # &lt;-- UPDATED
            ├── fasta.py         # &lt;-- NEW
            ├── records.py
            └── text.py</pre></div>



<p class="wp-block-paragraph">No dependency was added, so:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">pyproject.toml
uv.lock</pre></div>



<p class="wp-block-paragraph">remain unchanged.</p>



<p class="wp-block-paragraph">The only package implementation files changed in Part 4.5 are:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">load/fasta.py        # NEW
load/__init__.py     # UPDATED</pre></div>



<p class="wp-block-paragraph">plus:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">sample.fasta         # NEW
application.py       # UPDATED</pre></div>



<p class="wp-block-paragraph">That is a small structural change, even though the FASTA parser itself contains several new pieces of logic.</p>



<h2 class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">In Part 4.5, we extended the loading layer we built in Part 4.4 and added our first FASTA loader.</p>



<p class="wp-block-paragraph">The new <code>fasta.py</code> module gives us three focused functions:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">_parse_header()
get_headers()
get_sequence()</pre></div>



<p class="wp-block-paragraph">Together they let Genome Toolkit:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">parse FASTA headers
discover record identifiers and descriptions
select one record by identifier or index
join wrapped sequence lines
read the file incrementally
return SequenceRecord</pre></div>



<p class="wp-block-paragraph">Both loading paths now reach the same neutral object:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">plain text ─┐
            ├→ SequenceRecord
FASTA ──────┘</pre></div>



<p class="wp-block-paragraph">and the rest of our workflow remains unchanged:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">SequenceRecord
      ↓
DNA
      ↓
dna.sequence
      ↓
existing k-mer algorithms</pre></div>



<p class="wp-block-paragraph">This simple FASTA support is enough for many of the real genome-data experiments we are planning next. As our experiments grow, Genome Toolkit can grow with them by adding new loaders and richer biological-data support only when we actually need them.</p>



<h3 class="wp-block-heading">New Concepts We Learned</h3>



<ul class="wp-block-list">
<li><strong>FASTA</strong> — One of the most common formats for storing and exchanging DNA, RNA, and protein sequences.</li>
</ul>



<ul class="wp-block-list">
<li><strong>FASTA header</strong> — A line beginning with <code>&gt;</code> that contains a record identifier and may also contain a description.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Generic FASTA loader</strong> — Our loader understands the common FASTA record structure without trying to decode every provider-specific header convention.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Private helper function</strong> — <code>_parse_header()</code> keeps the shared header parsing rule in one place for both public FASTA functions.</li>
</ul>



<ul class="wp-block-list">
<li><strong><code>split(maxsplit=1)</code></strong> — Splits the identifier from the rest of the header while keeping a multi-word description together.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Tuple return values and unpacking</strong> — <code>_parse_header()</code> returns <code>(identifier, description)</code>, which we can assign directly to two variables.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Keyword-only arguments</strong> — The <code>*</code> in <code>get_sequence()</code> gives us clear calls such as <code>identifier="..."</code> or <code>index=0</code>.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Zero-based indexing</strong> — The first FASTA record is index <code>0</code>, the second is index <code>1</code>, and so on.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Conditional expression</strong> — <code>matches</code> chooses the identifier comparison when an identifier was supplied, otherwise it compares the record index.</li>
</ul>



<ul class="wp-block-list">
<li><strong><code>continue</code> and <code>break</code></strong> — <code>continue</code> moves to the next loop iteration, while <code>break</code> stops the scan once the selected FASTA record is complete.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Streaming record selection</strong> — <code>get_sequence()</code> reads the file line by line, ignores sequence data from records we do not need, stores only the selected sequence, and stops at the next header.</li>
</ul>



<p class="wp-block-paragraph">The most important architectural idea remains the same:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">The loader understands the file format. The biological model decides whether the selected symbols are valid DNA.</p>
</blockquote>



<h2 class="wp-block-heading">What is Next?</h2>



<p class="wp-block-paragraph">Our input side now supports both plain-text and FASTA sequence files:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">plain text ─┐
            ├→ SequenceRecord → DNA
FASTA ──────┘</pre></div>



<p class="wp-block-paragraph">Our two k-mer functions still accept a plain string:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">sequence: str</pre></div>



<p class="wp-block-paragraph">and return simple Python values:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">count_kmer()               → int
find_most_frequent_kmers() → list[str]</pre></div>



<p class="wp-block-paragraph">In Part 4.6, we will improve how those algorithms receive data and how they return scientific results.</p>



<p class="wp-block-paragraph">The actual k-mer calculations will stay the same.</p>



<p class="wp-block-paragraph">The full source code for Genome Toolkit is available here:</p>



<p class="wp-block-paragraph"><a href="https://github.com/rebelC0der/Genome_Toolkit">https://github.com/rebelC0der/Genome_Toolkit</a></p>



<p class="wp-block-paragraph">I hope adding our first FASTA loader and learning how to discover and stream biological sequence records through Genome Toolkit was useful for your bioinformatics and programming journey! If you found this article valuable and want to help us continue building rebelScience, please consider supporting our project. You can explore various ways to contribute here:</p>



<p class="wp-block-paragraph"><a href="https://rebelscience.club/cryptocurrency-donations/">https://rebelscience.club/cryptocurrency-donations/</a></p>



<p class="wp-block-paragraph">Until next time, rebelCoder, signing out.</p>



<h2 class="wp-block-heading">References</h2>



<ul class="wp-block-list">
<li>FASTA format overview: <a href="https://en.wikipedia.org/wiki/FASTA_format">https://en.wikipedia.org/wiki/FASTA_format</a></li>



<li>NCBI FASTA format: <a href="https://www.ncbi.nlm.nih.gov/genbank/fastaformat/">https://www.ncbi.nlm.nih.gov/genbank/fastaformat/</a></li>



<li>UniProt protein information and FASTA conventions: <a href="https://www.uniprot.org/help/protein">https://www.uniprot.org/help/protein</a></li>



<li>Ensembl data downloads and FASTA conventions: <a href="https://www.ensembl.org/info/data/ftp/index.html">https://www.ensembl.org/info/data/ftp/index.html</a></li>



<li>Python <code>pathlib</code>: <a href="https://docs.python.org/3/library/pathlib.html">https://docs.python.org/3/library/pathlib.html</a></li>
</ul>



<p class="wp-block-paragraph">Video version of this article:</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Genome Toolkit. Part 4.5: Adding a Minimal Streaming FASTA Loader" width="640" height="360" src="https://www.youtube.com/embed/zDochgYw7PQ?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>
]]></content:encoded></item><item><title>Genome Toolkit. Part 4.4: Loading Sequences From Plain Text</title><link>https://rebelscience.club/2026/09/genome-toolkit-part-4-4-loading-sequences-from-plain-text/</link><guid isPermaLink="true">https://rebelscience.club/2026/09/genome-toolkit-part-4-4-loading-sequences-from-plain-text/</guid><pubDate>Thu, 03 Sep 2026 10:40:36 GMT</pubDate><description>In Part 4.4, we take Genome Toolkit beyond hardcoded sequences and add our first external data source. We build a small plain-text loader, introduce a neutral `SequenceRecord`, convert the loaded data into our validated `DNA` model, and keep the existing k-mer algorithms unchanged. This gives us a clean path from a real file to validated biological data while preserving the scientific calculations we already trust.
</description><content:encoded><![CDATA[
<p class="wp-block-paragraph">Welcome back to the Genome Toolkit series!</p>



<p class="wp-block-paragraph">In Part 4.3, we added our first validated biological models. Genome Toolkit can now create a <code>DNA</code> object, normalize lowercase DNA to uppercase, reject unsupported symbols, and keep useful information such as the sequence identifier and description together with the biological data.</p>



<p class="wp-block-paragraph">Most importantly, we deliberately left our existing k-mer algorithms unchanged.</p>



<p class="wp-block-paragraph">They still accept a normal Python string:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">sequence: str</pre></div>



<p class="wp-block-paragraph">and our application passes them the validated sequence through:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">seq = dna.sequence</pre></div>



<p class="wp-block-paragraph">So the end of our current workflow still looks like this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">DNA(...)
    ↓
validated DNA
    ↓
dna.sequence
    ↓
our existing k-mer algorithms</pre></div>



<p class="wp-block-paragraph">That is exactly where we want to continue from.</p>



<p class="wp-block-paragraph">Our next limitation is no longer DNA validation. The sequence itself is still written directly inside <code>application.py</code>:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">sequence=&quot;aattttaaaac&quot;,</pre></div>



<p class="wp-block-paragraph">Real biological data usually comes from somewhere outside our Python source code, so in this part we are going to add the smallest possible loading layer and read one sequence from a plain-text file.</p>



<p class="wp-block-paragraph">We will add a small neutral <code>SequenceRecord</code>, create a plain-text loader, convert the loaded record into our existing <code>DNA</code> model, and then continue using the same two k-mer algorithms exactly as before.</p>



<p class="wp-block-paragraph">The new workflow will become:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">sample.txt
    ↓
text.get_sequence()
    ↓
SequenceRecord
    ↓
DNA
    ↓
dna.sequence
    ↓
our existing k-mer algorithms</pre></div>



<p class="wp-block-paragraph">Notice what is not changing:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">algorithms/kmer.py</pre></div>



<p class="wp-block-paragraph">We are not changing the algorithm signatures, the loops, or the return values in Part 4.4. This article is about getting biological data into Genome Toolkit from a file.</p>



<h2 class="wp-block-heading">Starting With Our Working Project</h2>



<p class="wp-block-paragraph">We continue from the exact final state of Part 4.3.</p>



<p class="wp-block-paragraph">Our project currently looks like this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">genome_toolkit/
├── .git/
├── .gitignore
├── .venv/
├── README.md
├── application.py
├── pyproject.toml
├── uv.lock
└── src/
    └── genome_toolkit/
        ├── __init__.py
        ├── py.typed
        ├── algorithms/
        │   ├── __init__.py
        │   └── kmer.py
        └── sequence/
            ├── __init__.py
            ├── base.py
            └── dna.py</pre></div>



<p class="wp-block-paragraph">Our current <code>application.py</code> creates the DNA directly:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">dna = DNA(
    identifier=&quot;example_dna&quot;,
    description=&quot;Part 4.3 demonstration sequence&quot;,
    sequence=&quot;aattttaaaac&quot;,
)

seq = dna.sequence</pre></div>



<p class="wp-block-paragraph">and the existing algorithms still receive:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">count_kmer(seq, kmer)
find_most_frequent_kmers(seq, k_len)</pre></div>



<p class="wp-block-paragraph">Running the complete Part 4.3 application gives us the same scientific checkpoint we have been preserving throughout the refactor:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">Sequence: AATTTTAAAAC
k-mer: AA
Repeats found: 4
Most frequent k-mer: ['TTT', 'AAA']</pre></div>



<p class="wp-block-paragraph">In Part 4.4, we want to keep that result while changing only where the original sequence comes from.</p>



<h2 class="wp-block-heading">What Are We Going to Add?</h2>



<p class="wp-block-paragraph">We need two small pieces inside a new loading package:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">SequenceRecord
text.get_sequence()</pre></div>



<p class="wp-block-paragraph"><code>SequenceRecord</code> will hold the values that a loader has parsed from an external source.</p>



<p class="wp-block-paragraph"><code>text.get_sequence()</code> will read one sequence from a plain-text file and return that record.</p>



<p class="wp-block-paragraph">Then our existing <code>DNA</code> model will decide whether the loaded symbols are actually valid DNA:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">plain-text file
      ↓
loader reads the file
      ↓
SequenceRecord
      ↓
DNA validates the biology
      ↓
dna.sequence
      ↓
existing algorithms</pre></div>



<p class="wp-block-paragraph">This separation is important because <strong>reading a file</strong> and <strong>deciding what the biological symbols mean</strong> are two different jobs.</p>



<h2 class="wp-block-heading">Why Not Return <code>DNA</code> Directly?</h2>



<p class="wp-block-paragraph">Imagine that our text file contains:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">ACGTACGT</pre></div>



<p class="wp-block-paragraph">That could be DNA.</p>



<p class="wp-block-paragraph">But a plain-text file could just as easily contain:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">ACGUACGU</pre></div>



<p class="wp-block-paragraph">for RNA, or:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">MKTLLVAG</pre></div>



<p class="wp-block-paragraph">for a protein sequence.</p>



<p class="wp-block-paragraph">The file format itself does not tell us which biological type the symbols are supposed to represent.</p>



<p class="wp-block-paragraph">If our text loader returned <code>DNA</code> directly:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">text loader
    ↓
DNA</pre></div>



<p class="wp-block-paragraph">the loader would be making two decisions at once:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">How do I read this file?
+
What biological type is this sequence?</pre></div>



<p class="wp-block-paragraph">Instead, we will keep those steps separate:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">text file
    ↓
SequenceRecord
    ↓
DNA / future RNA / future Protein</pre></div>



<p class="wp-block-paragraph">The loader tells us what it parsed. The biological model tells us whether those parsed symbols satisfy the rules for DNA, RNA, protein, or another future sequence type.</p>



<p class="wp-block-paragraph">This also means we can reuse the same loading code later without teaching the text loader about every biological model Genome Toolkit may eventually support.</p>



<h2 class="wp-block-heading">Creating the <code>load</code> Package</h2>



<p class="wp-block-paragraph">Inside:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/</pre></div>



<p class="wp-block-paragraph">create a new folder:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">load/</pre></div>



<p class="wp-block-paragraph">We will build it one file at a time.</p>



<p class="wp-block-paragraph">The first file we need is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">records.py</pre></div>



<p class="wp-block-paragraph">So our project temporarily becomes:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/
└── genome_toolkit/
    ├── algorithms/
    ├── sequence/
    └── load/                   # &lt;-- NEW
        └── records.py          # &lt;-- NEW</pre></div>



<h2 class="wp-block-heading">Creating a Neutral <code>SequenceRecord</code></h2>



<p class="wp-block-paragraph">Open:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/load/records.py</pre></div>



<p class="wp-block-paragraph">and add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;&quot;Lightweight records returned by sequence loaders.&quot;&quot;&quot;

from dataclasses import dataclass


@dataclass(frozen=True, slots=True)
class SequenceRecord:
    &quot;&quot;&quot;Parsed sequence data before biological validation.&quot;&quot;&quot;

    identifier: str
    sequence: str
    description: str | None = None</pre></div>



<p class="wp-block-paragraph">There are a few new Python ideas here, so let us look at them before moving on.</p>



<h3 class="wp-block-heading">What Is a Dataclass and Why Are We Using It?</h3>



<p class="wp-block-paragraph">We import:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from dataclasses import dataclass</pre></div>



<p class="wp-block-paragraph"><code>dataclass</code> comes from Python&#8217;s standard library. It is designed for small classes whose main job is to hold data.</p>



<p class="wp-block-paragraph">When we write:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">@dataclass(frozen=True, slots=True)
class SequenceRecord:
    identifier: str
    sequence: str
    description: str | None = None</pre></div>



<p class="wp-block-paragraph">Python turns those typed fields into a useful data object for us.</p>



<p class="wp-block-paragraph">Without <code>@dataclass</code>, we would need to write the basic setup ourselves:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">class SequenceRecord:
    def __init__(
        self,
        identifier: str,
        sequence: str,
        description: str | None = None,
    ):
        self.identifier = identifier
        self.sequence = sequence
        self.description = description</pre></div>



<p class="wp-block-paragraph">And that is only the beginning. If we also wanted the same useful object representation, easy comparisons, immutability, and fixed attributes, we would need to write even more code ourselves.</p>



<p class="wp-block-paragraph">With <code>@dataclass</code>, we get useful behavior automatically:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">typed named fields
automatic __init__()
useful __repr__()
optional immutability with frozen=True
compact memory and fixed attributes with slots=True
standard library only</pre></div>



<p class="wp-block-paragraph">For <code>SequenceRecord</code>, this fits very well because the object has no special behavior to invent. Its job is simply:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Carry parsed sequence data from a loader to a biological model.</p>
</blockquote>



<p class="wp-block-paragraph">It is still just a simple Python object carrying data. <code>@dataclass</code> removes the repetitive plumbing so we can focus on what the record actually contains.</p>



<p class="wp-block-paragraph">The line:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">@dataclass(frozen=True, slots=True)</pre></div>



<p class="wp-block-paragraph">is also a Python <strong>decorator</strong>. We already saw decorators such as <code>@field_validator</code> and <code>@classmethod</code> in Part 4.3. Here, the decorator tells Python to process <code>SequenceRecord</code> as a dataclass and apply the two options we supplied.</p>



<p class="wp-block-paragraph">Check out this very helpful video to learn more about why you should be using Python dataclasses in your propjects:</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Python dataclasses will save you HOURS, also featuring attrs" width="640" height="360" src="https://www.youtube.com/embed/vBH6GRJ1REM?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>



<h3 class="wp-block-heading">Why Not Use Another Pydantic Model?</h3>



<p class="wp-block-paragraph">We already use Pydantic for <code>Sequence</code> and <code>DNA</code>, so why not use it here too?</p>



<p class="wp-block-paragraph">Because <code>SequenceRecord</code> has a much smaller job.</p>



<p class="wp-block-paragraph">It represents <strong>parsed external data before biological validation</strong>:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">file
  ↓
SequenceRecord
  ↓
DNA validation</pre></div>



<p class="wp-block-paragraph">We do not need <code>SequenceRecord</code> to decide whether the sequence is DNA, normalize nucleotides, or provide Pydantic validation rules. Our existing biological models already do that.</p>



<p class="wp-block-paragraph">Compared with a plain dictionary such as:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">{
    &quot;id&quot;: &quot;sample&quot;,
    &quot;seq&quot;: &quot;ACGT&quot;,
}</pre></div>



<p class="wp-block-paragraph">our dataclass gives us named fields:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">record.identifier
record.sequence
record.description</pre></div>



<p class="wp-block-paragraph">along with type information, editor autocomplete, and one predictable shape.</p>



<h3 class="wp-block-heading">What Does <code>frozen=True</code> Do Here?</h3>



<p class="wp-block-paragraph">We used the same idea in our Pydantic model configuration in Part 4.3.</p>



<p class="wp-block-paragraph">For a dataclass:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">frozen=True</pre></div>



<p class="wp-block-paragraph">means that after we create the record, its fields cannot simply be reassigned.</p>



<p class="wp-block-paragraph">For example, we do not want loading code to create:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">record.sequence = &quot;AAAA&quot;</pre></div>



<p class="wp-block-paragraph">later by accident.</p>



<p class="wp-block-paragraph">The record represents what we parsed from the source file, so keeping those parsed values fixed makes the handoff into biological validation easier to reason about.</p>



<h3 class="wp-block-heading">What Does <code>slots=True</code> Do?</h3>



<p class="wp-block-paragraph">Normally, Python objects can often have new attributes added to them dynamically.</p>



<p class="wp-block-paragraph">For example, without slots, code could accidentally try to attach something unrelated such as:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">record.random_value = 123</pre></div>



<p class="wp-block-paragraph"><code>slots=True</code> restricts the object to the fields we actually defined:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">identifier
sequence
description</pre></div>



<p class="wp-block-paragraph">It also makes small data objects somewhat more compact in memory. For us, the main practical benefit is that <code>SequenceRecord</code> stays a simple, predictable data container instead of quietly growing arbitrary attributes.</p>



<h3 class="wp-block-heading">The Three Record Fields</h3>



<p class="wp-block-paragraph">Our record contains:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">identifier: str
sequence: str
description: str | None = None</pre></div>



<p class="wp-block-paragraph">The <code>identifier</code> gives the parsed sequence a name. The <code>sequence</code> contains the symbols read from the source, and <code>description</code> is optional because a simple text file may not provide one.</p>



<p class="wp-block-paragraph">At this point we have a useful shape for loaded sequence data, but nothing is reading a file yet. Now we can build the loader that creates this record.</p>



<h2 class="wp-block-heading">Adding the Plain-Text Loader</h2>



<p class="wp-block-paragraph">Create our second file:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/load/text.py</pre></div>



<p class="wp-block-paragraph">Our loading package now contains:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">load/
├── records.py
└── text.py                    # &lt;-- NEW</pre></div>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;&quot;Loader for plain-text sequence files.&quot;&quot;&quot;

from pathlib import Path

from .records import SequenceRecord


def get_sequence(filepath: str | Path) -&gt; SequenceRecord:
    &quot;&quot;&quot;Load one sequence and remove all whitespace.

    Args:
        filepath: Path to a plain-text file containing one sequence.

    Returns:
        A neutral sequence record containing the parsed sequence.

    Raises:
        ValueError: If the file contains no sequence data.
    &quot;&quot;&quot;
    path = Path(filepath)

    with path.open(&quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
        sequence = &quot;&quot;.join(&quot;&quot;.join(line.split()) for line in file)

    if not sequence:
        raise ValueError(f&quot;'{path}' does not contain sequence data.&quot;)

    return SequenceRecord(
        identifier=path.stem,
        sequence=sequence,
    )</pre></div>



<p class="wp-block-paragraph">This is the first actual file loader in Genome Toolkit, so let us go through it from top to bottom.</p>



<h2 class="wp-block-heading">Working With File Paths Using <code>Path</code></h2>



<p class="wp-block-paragraph">We begin with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from pathlib import Path</pre></div>



<p class="wp-block-paragraph"><code>pathlib</code> is part of Python&#8217;s standard library, and <code>Path</code> gives us an object for working with filesystem paths.</p>



<p class="wp-block-paragraph">Instead of treating a path only as a string such as:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;samples/sample.txt&quot;</pre></div>



<p class="wp-block-paragraph">we can work with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Path(&quot;samples/sample.txt&quot;)</pre></div>



<p class="wp-block-paragraph">and use useful path operations such as opening the file or getting its filename without the extension.</p>



<p class="wp-block-paragraph">Our function accepts:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">filepath: str | Path</pre></div>



<p class="wp-block-paragraph">The <code>|</code> means <strong>either type is accepted</strong>.</p>



<p class="wp-block-paragraph">So both of these are valid:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">text.get_sequence(&quot;samples/sample.txt&quot;)</pre></div>



<p class="wp-block-paragraph">and:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">text.get_sequence(Path(&quot;samples/sample.txt&quot;))</pre></div>



<p class="wp-block-paragraph">The first line inside the function:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">path = Path(filepath)</pre></div>



<p class="wp-block-paragraph">converts either form into one consistent <code>Path</code> object. From that point onward, the rest of the loader only needs to work with <code>path</code>.</p>



<h2 class="wp-block-heading">Opening the File</h2>



<p class="wp-block-paragraph">Next we have:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">with path.open(&quot;r&quot;, encoding=&quot;utf-8&quot;) as file:</pre></div>



<p class="wp-block-paragraph">The <code>"r"</code> means we are opening the file for <strong>reading</strong>.</p>



<p class="wp-block-paragraph"><code>encoding="utf-8"</code> tells Python how the text inside the file should be decoded. UTF-8 is the standard text encoding we will use for our sequence files.</p>



<p class="wp-block-paragraph">The <code>with</code> statement creates a <strong>context manager</strong>. In simple terms, Python opens the file for this block and automatically closes it again when the block finishes, even if something goes wrong while we are reading it.</p>



<p class="wp-block-paragraph">So instead of manually doing:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">open file
read file
remember to close file</pre></div>



<p class="wp-block-paragraph">we get:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">with file open
    read what we need
leave block
    file is closed automatically</pre></div>



<h2 class="wp-block-heading">Removing Whitespace From the Sequence</h2>



<p class="wp-block-paragraph">Inside the file block we use:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">sequence = &quot;&quot;.join(&quot;&quot;.join(line.split()) for line in file)</pre></div>



<p class="wp-block-paragraph">There are several small operations packed into this one line.</p>



<p class="wp-block-paragraph">For each line:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">line.split()</pre></div>



<p class="wp-block-paragraph">splits the line around whitespace.</p>



<p class="wp-block-paragraph">For example:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">&quot;AATT TTAAn&quot;</pre></div>



<p class="wp-block-paragraph">becomes pieces similar to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">&quot;AATT&quot;
&quot;TTAA&quot;</pre></div>



<p class="wp-block-paragraph">Then:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;.join(line.split())</pre></div>



<p class="wp-block-paragraph">joins those pieces back together without spaces:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">AATTTTAA</pre></div>



<p class="wp-block-paragraph">The expression:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">for line in file</pre></div>



<p class="wp-block-paragraph">processes the file one line at a time, and the outer:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;.join(...)</pre></div>



<p class="wp-block-paragraph">joins all of those cleaned lines into one final sequence.</p>



<p class="wp-block-paragraph">So a file containing:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">AATTTT
AAAAC</pre></div>



<p class="wp-block-paragraph">becomes:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">AATTTTAAAAC</pre></div>



<p class="wp-block-paragraph">For this simple plain-text format, whitespace is formatting. It is not part of the biological sequence we want to pass into our model.</p>



<h2 class="wp-block-heading">Rejecting an Empty Sequence</h2>



<p class="wp-block-paragraph">After reading the file we check:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">if not sequence:
    raise ValueError(f&quot;'{path}' does not contain sequence data.&quot;)</pre></div>



<p class="wp-block-paragraph">If the file contains no sequence after whitespace has been removed, there is nothing useful for the loader to return.</p>



<p class="wp-block-paragraph">Rather than creating an empty record and allowing the problem to travel further into our program, we stop here with a clear error.</p>



<p class="wp-block-paragraph">We will build a more deliberate error contract later in the series. For now, the ordinary <code>ValueError</code> from our technical plan is enough to communicate that this source did not contain the value our loader expected.</p>



<h2 class="wp-block-heading">Creating the <code>SequenceRecord</code></h2>



<p class="wp-block-paragraph">Finally:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">return SequenceRecord(
    identifier=path.stem,
    sequence=sequence,
)</pre></div>



<p class="wp-block-paragraph">creates the neutral record.</p>



<p class="wp-block-paragraph">The new expression:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">path.stem</pre></div>



<p class="wp-block-paragraph">means the filename without its extension.</p>



<p class="wp-block-paragraph">For:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">samples/sample.txt</pre></div>



<p class="wp-block-paragraph">the stem is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">sample</pre></div>



<p class="wp-block-paragraph">so the returned record contains approximately:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">identifier  → sample
sequence    → AATTTTAAAAC
description → None</pre></div>



<p class="wp-block-paragraph">Notice that the loader has still made <strong>no DNA decision</strong>.</p>



<p class="wp-block-paragraph">If the text file contained invalid DNA symbols, <code>text.get_sequence()</code> could still return a <code>SequenceRecord</code>. The biological check happens in the next step when we try to turn that record into <code>DNA</code>.</p>



<h2 class="wp-block-heading">Exposing Our Loading API</h2>



<p class="wp-block-paragraph">Now that the actual text loader exists, create:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/load/__init__.py</pre></div>



<p class="wp-block-paragraph">Our loading package is now:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">load/
├── __init__.py               # &lt;-- NEW
├── records.py
└── text.py</pre></div>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;&quot;Sequence loaders and neutral records.&quot;&quot;&quot;

from . import text
from .records import SequenceRecord

__all__ = [&quot;SequenceRecord&quot;, &quot;text&quot;]</pre></div>



<p class="wp-block-paragraph">This gives us a clean public loading API.</p>



<p class="wp-block-paragraph">Instead of importing the function from its internal module path:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.load.text import get_sequence</pre></div>



<p class="wp-block-paragraph">we can write:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.load import text</pre></div>



<p class="wp-block-paragraph">and then call:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">text.get_sequence(...)</pre></div>



<p class="wp-block-paragraph">Keeping the source name visible will also scale naturally when we add another loader:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">text.get_sequence(...)
fasta.get_sequence(...)</pre></div>



<p class="wp-block-paragraph">That is clearer than creating increasingly long names such as:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">get_text_sequence()
get_fasta_sequence()</pre></div>



<p class="wp-block-paragraph">in one flat namespace.</p>



<h2 class="wp-block-heading">Adding Our First Sample File</h2>



<p class="wp-block-paragraph">At the repository root, create a new folder:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">samples/</pre></div>



<p class="wp-block-paragraph">Then create:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">samples/sample.txt</pre></div>



<p class="wp-block-paragraph">with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">AATTTT
AAAAC</pre></div>



<p class="wp-block-paragraph">We are deliberately using the same original Genome Toolkit sequence we have used throughout the series:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">AATTTTAAAAC</pre></div>



<p class="wp-block-paragraph">The only difference is that it is now stored outside our Python code and wrapped across two lines.</p>



<p class="wp-block-paragraph">This gives us a very useful checkpoint:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">hardcoded sequence
        ↓
AATTTTAAAAC

plain-text file
        ↓
AATTTTAAAAC</pre></div>



<p class="wp-block-paragraph">If the loading layer is working correctly, the scientific calculation at the end of the workflow should still see the same sequence.</p>



<h2 class="wp-block-heading">Loading Our First <code>SequenceRecord</code></h2>



<p class="wp-block-paragraph">Now we can connect the new loader to our existing application.</p>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from pathlib import Path</pre></div>



<p class="wp-block-paragraph">and:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.load import text</pre></div>



<p class="wp-block-paragraph">Then define the location of our sample files:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">SAMPLES_DIR = Path(__file__).resolve().parent / &quot;samples&quot;</pre></div>



<p class="wp-block-paragraph">There are three useful path operations here:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">__file__
    current Python file

.resolve()
    absolute filesystem path

.parent
    directory containing application.py</pre></div>



<p class="wp-block-paragraph">Then:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">/ &quot;samples&quot;</pre></div>



<p class="wp-block-paragraph">uses <code>Path</code>&#8216;s <code>/</code> operator to build the path to our <code>samples/</code> directory.</p>



<p class="wp-block-paragraph">So regardless of where we run the command from, <code>application.py</code> can find the sample directory relative to its own location.</p>



<p class="wp-block-paragraph">Now load the file:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">record = text.get_sequence(
    SAMPLES_DIR / &quot;sample.txt&quot;
)</pre></div>



<p class="wp-block-paragraph">At this point:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">record.identifier</pre></div>



<p class="wp-block-paragraph">is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">sample</pre></div>



<p class="wp-block-paragraph">and:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">record.sequence</pre></div>



<p class="wp-block-paragraph">is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">AATTTTAAAAC</pre></div>



<p class="wp-block-paragraph">But this is still only parsed data. We have not yet asked our biological model whether the sequence is valid DNA.</p>



<h2 class="wp-block-heading">Converting the Record Into Validated <code>DNA</code></h2>



<p class="wp-block-paragraph">This is where Part 4.3 connects directly to our new loading layer.</p>



<p class="wp-block-paragraph">Our <code>SequenceRecord</code> contains:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">identifier
description
sequence</pre></div>



<p class="wp-block-paragraph">and our <code>DNA</code> model expects those same field names.</p>



<p class="wp-block-paragraph">Before we use any Pydantic shortcut, let us connect them manually so we can see exactly what is happening.</p>



<h3 class="wp-block-heading">First, Map the Fields Manually</h3>



<p class="wp-block-paragraph">After loading our record:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">record = text.get_sequence(
    SAMPLES_DIR / &quot;sample.txt&quot;
)</pre></div>



<p class="wp-block-paragraph">we can create the <code>DNA</code> object exactly the same way we created DNA in Part 4.3:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">dna = DNA(
    identifier=record.identifier,
    description=record.description,
    sequence=record.sequence,
)</pre></div>



<p class="wp-block-paragraph">There is nothing wrong with this approach.</p>



<p class="wp-block-paragraph">We are simply taking each value from:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">SequenceRecord</pre></div>



<p class="wp-block-paragraph">and assigning it to the matching field in:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">DNA</pre></div>



<p class="wp-block-paragraph">The mapping is easy to see:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">record.identifier   →   DNA.identifier
record.description  →   DNA.description
record.sequence     →   DNA.sequence</pre></div>



<p class="wp-block-paragraph">Our existing bridge to the algorithms can then remain:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">seq = dna.sequence</pre></div>



<p class="wp-block-paragraph">and the same algorithm calls still work:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">count_kmer(seq, kmer)
find_most_frequent_kmers(seq, k_len)</pre></div>



<p class="wp-block-paragraph">If we run:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run python application.py</pre></div>



<p class="wp-block-paragraph">the important scientific checkpoint is still:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">Sequence: AATTTTAAAAC
k-mer: AA
Repeats found: 4
Most frequent k-mer: ['TTT', 'AAA']</pre></div>



<p class="wp-block-paragraph">So we have already completed the important connection:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">sample.txt
    ↓
SequenceRecord
    ↓
manual field mapping
    ↓
DNA
    ↓
dna.sequence
    ↓
existing algorithms</pre></div>



<p class="wp-block-paragraph">Now we can simplify one repetitive part.</p>



<h3 class="wp-block-heading">Let Pydantic Read the Matching Fields for Us</h3>



<p class="wp-block-paragraph">Notice that both objects intentionally use the same field names:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">SequenceRecord       DNA
--------------       ---
identifier       →   identifier
description      →   description
sequence         →   sequence</pre></div>



<p class="wp-block-paragraph">Writing:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">dna = DNA(
    identifier=record.identifier,
    description=record.description,
    sequence=record.sequence,
)</pre></div>



<p class="wp-block-paragraph">is clear, but we are manually copying three matching fields.</p>



<p class="wp-block-paragraph">Because <code>DNA</code> is a Pydantic model, it already has:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">model_validate()</pre></div>



<p class="wp-block-paragraph">We can therefore replace the manual mapping with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">dna = DNA.model_validate(
    record,
    from_attributes=True,
)</pre></div>



<p class="wp-block-paragraph">This does not change our biological validation. It only changes how the values from <code>record</code> are supplied to the <code>DNA</code> model.</p>



<h3 class="wp-block-heading">What Does <code>model_validate()</code> Do?</h3>



<p class="wp-block-paragraph"><code>model_validate()</code> is a Pydantic method for creating and validating a model from another value.</p>



<p class="wp-block-paragraph">In our case:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">source object   → SequenceRecord
target model    → DNA</pre></div>



<p class="wp-block-paragraph">The important option is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from_attributes=True</pre></div>



<p class="wp-block-paragraph">That tells Pydantic to read matching values from the source object&#8217;s attributes.</p>



<p class="wp-block-paragraph">So instead of us manually writing:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">identifier=record.identifier
description=record.description
sequence=record.sequence</pre></div>



<p class="wp-block-paragraph">Pydantic reads those matching attributes for us.</p>



<p class="wp-block-paragraph">The flow becomes:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">SequenceRecord
      ↓
DNA.model_validate(
    record,
    from_attributes=True,
)
      ↓
read matching attributes
      ↓
run inherited Sequence rules
      ↓
run DNA validator
      ↓
valid → DNA object created
invalid → Pydantic validation error</pre></div>



<p class="wp-block-paragraph">This is important: <code>model_validate()</code> does <strong>not</strong> bypass anything we built in Part 4.3.</p>



<p class="wp-block-paragraph">If the loaded record contains:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">AATTTTZAAAC</pre></div>



<p class="wp-block-paragraph">our DNA validator still sees the <code>Z</code>, rejects it, and no valid <code>DNA</code> object is created.</p>



<p class="wp-block-paragraph">So our manual version:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">dna = DNA(
    identifier=record.identifier,
    description=record.description,
    sequence=record.sequence,
)</pre></div>



<p class="wp-block-paragraph">and our shorter Pydantic version:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">dna = DNA.model_validate(
    record,
    from_attributes=True,
)</pre></div>



<p class="wp-block-paragraph">both end at the same place:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">validated DNA object</pre></div>



<p class="wp-block-paragraph">The second version simply avoids repeating field-by-field assignments when the two objects were deliberately designed with matching names.</p>



<h3 class="wp-block-heading">When Should We Use This Conversion?</h3>



<p class="wp-block-paragraph">Automatic attribute conversion is convenient when the source object and target model intentionally share the same core schema.</p>



<p class="wp-block-paragraph">For our current models, that is exactly what we have.</p>



<p class="wp-block-paragraph">A few practical rules are useful to remember:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">record has extra fields
→ unused source attributes can be ignored

DNA has optional/defaulted fields
→ their defaults can still be used

DNA requires a field the record does not provide
→ validation fails

matching record field contains invalid DNA
→ DNA validation fails

field names do not match
→ map the fields explicitly</pre></div>



<p class="wp-block-paragraph">So:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">model_validate(..., from_attributes=True)</pre></div>



<p class="wp-block-paragraph">is not magic conversion between any two Python objects. It is a convenient way to replace the manual mapping we just wrote when the source and target intentionally use matching attributes.</p>



<h2 class="wp-block-heading">Updating Our Existing <code>application.py</code></h2>



<p class="wp-block-paragraph">Now we can update the complete application.</p>



<p class="wp-block-paragraph">In Part 4.3, we added several extra <code>print()</code> calls to demonstrate what our new <code>DNA</code> model could do: length, indexing, slicing, iteration, and JSON serialization. Those examples were useful while we were learning the model, but we do not need to keep all of them in every later version of <code>application.py</code>.</p>



<p class="wp-block-paragraph">From this point onward, we will return to a cleaner application that focuses on the current data flow and our original scientific checkpoint.</p>



<p class="wp-block-paragraph">Before Part 4.4:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">hardcoded string
      ↓
DNA
      ↓
dna.sequence
      ↓
algorithms</pre></div>



<p class="wp-block-paragraph">Now:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">sample.txt
      ↓
SequenceRecord
      ↓
DNA
      ↓
dna.sequence
      ↓
algorithms</pre></div>



<p class="wp-block-paragraph">Our algorithm boundary remains unchanged.</p>



<p class="wp-block-paragraph">Update <code>application.py</code> to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from pathlib import Path

from genome_toolkit.algorithms import (
    count_kmer,
    find_most_frequent_kmers,
)
from genome_toolkit.load import text
from genome_toolkit.sequence import DNA


SAMPLES_DIR = Path(__file__).resolve().parent / &quot;samples&quot;


record = text.get_sequence(
    SAMPLES_DIR / &quot;sample.txt&quot;
)

dna = DNA.model_validate(
    record,
    from_attributes=True,
)

seq = dna.sequence
kmer = &quot;AA&quot;
k_len = 3


print(f&quot;Sequence: {seq}&quot;)
print(f&quot;k-mer: {kmer}&quot;)
print(f&quot;Repeats found: {count_kmer(seq, kmer)}&quot;)
print(f&quot;Most frequent k-mer: {find_most_frequent_kmers(seq, k_len)}&quot;)</pre></div>



<p class="wp-block-paragraph">Compare the last two calls with Part 4.3:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">count_kmer(seq, kmer)
find_most_frequent_kmers(seq, k_len)</pre></div>



<p class="wp-block-paragraph">They are exactly the same.</p>



<p class="wp-block-paragraph">And:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">seq = dna.sequence</pre></div>



<p class="wp-block-paragraph">is still exactly the same bridge we established in Part 4.3.</p>



<p class="wp-block-paragraph">That means we have added external file loading without touching:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">src/genome_toolkit/algorithms/kmer.py</pre></div>



<p class="wp-block-paragraph">at all.</p>



<h2 class="wp-block-heading">Running the Complete Part 4.4 Application</h2>



<p class="wp-block-paragraph">Run:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv run python application.py</pre></div>



<p class="wp-block-paragraph">We should now see:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">Sequence: AATTTTAAAAC
k-mer: AA
Repeats found: 4
Most frequent k-mer: ['TTT', 'AAA']</pre></div>



<p class="wp-block-paragraph">Our loader still derives the identifier <code>sample</code> from <code>sample.txt</code>, and the validated <code>DNA</code> object still contains that information internally. We simply no longer print every model feature in the main application because those behaviors were already demonstrated in Part 4.3.</p>



<p class="wp-block-paragraph">The important scientific data is still:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">AATTTTAAAAC</pre></div>



<p class="wp-block-paragraph">and our original calculations still return:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">Repeats found: 4
Most frequent k-mer: ['TTT', 'AAA']</pre></div>



<p class="wp-block-paragraph">So our complete path is now:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">sample.txt
    ↓
text.get_sequence()
    ↓
SequenceRecord
    ↓
DNA.model_validate(...)
    ↓
validated DNA
    ↓
dna.sequence
    ↓
existing k-mer algorithms</pre></div>



<p class="wp-block-paragraph">We added an external source without changing the biological model and without changing either algorithm.</p>



<h2 class="wp-block-heading">Our New Project Structure</h2>



<p class="wp-block-paragraph">At the end of Part 4.4, our project looks like this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">genome_toolkit/
├── .git/
├── .gitignore
├── .venv/
├── README.md
├── application.py              # &lt;-- UPDATED
├── pyproject.toml
├── uv.lock
├── samples/                    # &lt;-- NEW
│   └── sample.txt              # &lt;-- NEW
└── src/
    └── genome_toolkit/
        ├── __init__.py
        ├── py.typed
        ├── algorithms/
        │   ├── __init__.py
        │   └── kmer.py
        ├── sequence/
        │   ├── __init__.py
        │   ├── base.py
        │   └── dna.py
        └── load/               # &lt;-- NEW
            ├── __init__.py     # &lt;-- NEW
            ├── records.py      # &lt;-- NEW
            └── text.py         # &lt;-- NEW</pre></div>



<p class="wp-block-paragraph">Notice what stayed untouched:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">algorithms/
sequence/
pyproject.toml
uv.lock</pre></div>



<p class="wp-block-paragraph">We did not add another dependency, and we did not redesign the model or algorithm layers.</p>



<p class="wp-block-paragraph">The new pieces are only the files needed to bring a simple external sequence into the workflow.</p>



<h2 class="wp-block-heading">Why This Small Loading Layer Matters</h2>



<p class="wp-block-paragraph">At first, reading one text file may not look like a large feature.</p>



<p class="wp-block-paragraph">But it gives Genome Toolkit an important new separation:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">where data comes from
        ↓
what data was parsed
        ↓
what biological type it represents
        ↓
what scientific calculation we run</pre></div>



<p class="wp-block-paragraph">Those steps can now evolve independently.</p>



<p class="wp-block-paragraph">A future FASTA loader can produce the same <code>SequenceRecord</code>. A database or API loader could eventually do the same. Our <code>DNA</code> model can validate those records without learning how each source works, and our k-mer algorithms can continue focusing only on their calculations.</p>



<p class="wp-block-paragraph">For Part 4.4, however, we keep that architecture deliberately small. We support one plain-text file containing one sequence, and nothing more.</p>



<h2 class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">In Part 4.4, we moved the original Genome Toolkit sequence out of <code>application.py</code> and into our first external sample file.</p>



<p class="wp-block-paragraph">We created a new <code>load/</code> package, introduced the neutral <code>SequenceRecord</code> dataclass, and built <code>text.get_sequence()</code> to read a plain-text sequence file, remove whitespace, reject an empty source, and return the parsed sequence together with an identifier.</p>



<p class="wp-block-paragraph">We then used Pydantic&#8217;s <code>DNA.model_validate(..., from_attributes=True)</code> to turn that neutral record into the same validated <code>DNA</code> model we built in Part 4.3.</p>



<p class="wp-block-paragraph">Most importantly, we did <strong>not</strong> modify our two k-mer algorithms. They still accept strings, and <code>application.py</code> still passes them:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">dna.sequence</pre></div>



<p class="wp-block-paragraph">through our existing:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">seq = dna.sequence</pre></div>



<p class="wp-block-paragraph">bridge.</p>



<p class="wp-block-paragraph">Our workflow has therefore grown from:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">hardcoded string
      ↓
DNA
      ↓
algorithm</pre></div>



<p class="wp-block-paragraph">to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">plain-text file
      ↓
SequenceRecord
      ↓
DNA
      ↓
dna.sequence
      ↓
algorithm</pre></div>



<p class="wp-block-paragraph">while the original k-mer calculations remain unchanged.</p>



<h3 class="wp-block-heading">New Concepts We Learned</h3>



<ul class="wp-block-list">
<li><strong><code>SequenceRecord</code></strong> — A small neutral object that carries sequence data parsed from an external source before we decide which biological model should validate it.</li>
</ul>



<ul class="wp-block-list">
<li><strong><code>dataclass</code></strong> — A Python standard-library tool for creating classes whose main purpose is to hold data. We use it for <code>SequenceRecord</code> so we get named fields, type information, and a predictable object shape without adding another validation framework.</li>
</ul>



<ul class="wp-block-list">
<li><strong><code>frozen=True</code> in a dataclass</strong> — Prevents the parsed record fields from being reassigned after the object is created. This helps the record continue to represent the data we actually loaded from the source.</li>
</ul>



<ul class="wp-block-list">
<li><strong><code>slots=True</code></strong> — Restricts the dataclass to the fields we explicitly defined and prevents arbitrary new attributes from being attached later. It also makes small data objects more compact.</li>
</ul>



<ul class="wp-block-list">
<li><strong><code>Path</code> and <code>pathlib</code></strong> — Python&#8217;s standard way to work with filesystem paths as objects. We use <code>Path</code> to open files, combine path components, resolve locations, and derive the sample identifier from the filename.</li>
</ul>



<ul class="wp-block-list">
<li><strong><code>str | Path</code></strong> — A type annotation meaning that our loader accepts either a normal path string or a <code>Path</code> object.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Context manager (<code>with</code>)</strong> — The <code>with</code> statement manages the lifetime of the open file for us. Python opens the file for the block and closes it automatically when the block ends.</li>
</ul>



<ul class="wp-block-list">
<li><strong>UTF-8</strong> — The text encoding we explicitly use when reading our sequence files so Python knows how to decode the file contents.</li>
</ul>



<ul class="wp-block-list">
<li><strong><code>Path.stem</code></strong> — Gives us the filename without its extension. <code>sample.txt</code> therefore gives our simple loaded record the identifier <code>sample</code>.</li>
</ul>



<ul class="wp-block-list">
<li><strong><code>model_validate(..., from_attributes=True)</code></strong> — Lets Pydantic create and validate our <code>DNA</code> model by reading matching attributes from <code>SequenceRecord</code>. The conversion still runs all of the <code>Sequence</code> and DNA validation rules from Part 4.3.</li>
</ul>



<p class="wp-block-paragraph">The most important new idea is that <strong>parsing external data and validating biology are separate steps</strong>. The loader tells us what it read; <code>DNA</code> decides whether those symbols are valid DNA.</p>



<h2 class="wp-block-heading">What is Next?</h2>



<p class="wp-block-paragraph">Our plain-text loader now gives Genome Toolkit a real external data source, but the format is deliberately minimal.</p>



<p class="wp-block-paragraph">A plain-text file does not contain the record structure commonly used by real biological sequence collections. FASTA files can contain:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">identifiers
descriptions
wrapped sequence lines
multiple biological records</pre></div>



<p class="wp-block-paragraph">So in Part 4.5, we will extend only the loading layer and add our first minimal streaming FASTA loader.</p>



<p class="wp-block-paragraph">The workflow will become:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">plain text ─┐
            ├→ SequenceRecord → DNA → dna.sequence → algorithms
FASTA ──────┘</pre></div>



<p class="wp-block-paragraph">And just like Part 4.4, Part 4.5 will leave our existing DNA model and k-mer algorithms unchanged.</p>



<p class="wp-block-paragraph">The full source code for Genome Toolkit is available here:</p>



<p class="wp-block-paragraph"><a href="https://github.com/rebelC0der/Genome_Toolkit">https://github.com/rebelC0der/Genome_Toolkit</a></p>



<p class="wp-block-paragraph">I hope adding our first plain-text sequence loader and connecting external sequence data to our validated DNA model was useful for your bioinformatics and programming journey! If you found this article valuable and want to help us continue building rebelScience, please consider supporting our project. You can explore various ways to contribute here:</p>



<p class="wp-block-paragraph"><a href="https://rebelscience.club/cryptocurrency-donations/">https://rebelscience.club/cryptocurrency-donations/</a></p>



<p class="wp-block-paragraph">Until next time, rebelCoder, signing out.</p>



<h2 class="wp-block-heading">References</h2>



<ul class="wp-block-list">
<li>Python <code>dataclasses</code>: <a href="https://docs.python.org/3/library/dataclasses.html">https://docs.python.org/3/library/dataclasses.html</a></li>



<li>Python <code>pathlib</code>: <a href="https://docs.python.org/3/library/pathlib.html">https://docs.python.org/3/library/pathlib.html</a></li>



<li>Pydantic model validation: <a href="https://docs.pydantic.dev/latest/concepts/models/">https://docs.pydantic.dev/latest/concepts/models/</a></li>
</ul>



<p class="wp-block-paragraph">Video version of this article:</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Genome Toolkit. Part 4.4: Loading Sequences From Plain Text" width="640" height="360" src="https://www.youtube.com/embed/9Xz_gZORj60?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>
]]></content:encoded></item><item><title>Genome Toolkit. Part 4.2: Modernizing the Existing Python Project</title><link>https://rebelscience.club/2026/08/genome-toolkit-part-4-2-modernizing-the-existing-python-project/</link><guid isPermaLink="true">https://rebelscience.club/2026/08/genome-toolkit-part-4-2-modernizing-the-existing-python-project/</guid><pubDate>Fri, 28 Aug 2026 14:08:44 GMT</pubDate><description>In Part 4.2, we modernize Genome Toolkit and turn our original project into a proper installable Python package. We move from Pipenv to uv, organize the code under src/, move our existing k-mer algorithms into the new package, and update application.py to use them. Most importantly, we keep the scientific calculations unchanged and verify that Genome Toolkit still produces exactly the same results.
</description><content:encoded><![CDATA[
<p class="wp-block-paragraph">Welcome back to the Genome Toolkit series!</p>



<p class="wp-block-paragraph">In Part 4.1, we looked at where Genome Toolkit is going and why it makes sense to turn our small educational project into a proper scientific Python package. Now we are finally going to start doing it.</p>



<p class="wp-block-paragraph">The important thing is that our scientific calculations already work. We have two k-mer algorithms, we know what input they receive, and we know exactly what output our current application produces. So before we change anything, we are going to use that working application as our reference point.</p>



<h2 class="wp-block-heading">Starting With the Working Project</h2>



<p class="wp-block-paragraph">We are continuing inside the same Genome Toolkit Git repository:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">genome_toolkit/
├── .git/
├── .gitignore
├── application.py
├── genome_toolkit.py
├── Pipfile
└── Pipfile.lock</pre></div>



<p class="wp-block-paragraph">The <code>.git/</code> directory is important because it contains the history of our project. We are improving the same Genome Toolkit we have already been building, so we keep that history and continue working in the same repository.</p>



<p class="wp-block-paragraph">Before touching the structure, let us run the original application one more time:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">python application.py</pre></div>



<p class="wp-block-paragraph">Or, if you are using the Code Runner extension in Visual Studio Code, you can run it with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Ctrl + Alt + N</pre></div>



<p class="wp-block-paragraph">We should get:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Sequence: AATTTTAAAAC
k-mer: AA
Repeats found: 4
Most frequent k-mer: ['TTT', 'AAA']</pre></div>



<p class="wp-block-paragraph">This output is our <strong>working checkpoint</strong>. We already know these calculations are correct for the example we have been using throughout the series, so after we reorganize the project, we will run the application again and compare the result with this exact output.</p>



<p class="wp-block-paragraph">That gives us a very simple goal for this article:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">old project structure
        ↓
modern Python package
        ↓
same scientific calculations</pre></div>



<p class="wp-block-paragraph">We are changing how Genome Toolkit is organized, not what our k-mer algorithms calculate.</p>



<h2 class="wp-block-heading">What Are We Going to Improve?</h2>



<p class="wp-block-paragraph">Right now, Genome Toolkit is still a very small Python project. We have our environment managed with Pipenv, our scientific code lives in one <code>genome_toolkit.py</code> file, and we create a <code>genomeToolkit</code> object before calling our algorithms.</p>



<p class="wp-block-paragraph">That worked perfectly for the first few parts of the series, but now we want to give Genome Toolkit a cleaner structure that will be easier to grow.</p>



<p class="wp-block-paragraph">By the end of this article, we will have:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">uv
        ↓
a modern Python project

src/genome_toolkit/
        ↓
our installable Genome Toolkit package

algorithms/
        ↓
our k-mer functions</pre></div>



<p class="wp-block-paragraph">We are not going to learn all of Python packaging at once. We will change one small thing at a time, explain why it helps, and keep checking that our original scientific calculations still work.</p>



<p class="wp-block-paragraph">The first thing we are going to modernize is how we manage the Python project itself.</p>



<h2 class="wp-block-heading">Moving From Pipenv to uv</h2>



<p class="wp-block-paragraph">When we started Genome Toolkit, we used <strong>Pipenv</strong> to create a Python environment and manage our project dependencies. That is why our repository currently contains:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Pipfile
Pipfile.lock</pre></div>



<p class="wp-block-paragraph">For the modern version of Genome Toolkit, we are going to use <strong>uv</strong>.</p>



<p class="wp-block-paragraph"><code>uv</code> is a modern Python project and package manager. It can create projects, manage Python environments, install dependencies, generate lock files, build packages, and run Python commands for us.</p>



<p class="wp-block-paragraph">If <code>uv</code> is new to you, Corey Schafer has an excellent detailed tutorial on it. We are going to use only the parts we need for Genome Toolkit, but if you want a deeper introduction to the tool, I highly recommend watching his video:</p>



<p class="wp-block-paragraph"><strong><a href="https://youtu.be/AMdG7IjgSPM">Python Tutorial: UV &#8211; A Faster, All-in-One Package Manager to Replace Pip and Venv</a></strong></p>



<p class="wp-block-paragraph">In practical terms, many of the jobs for which we previously used several Python tools can now be handled through one tool:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">project setup
environment
dependencies
lock file
package build
running commands
        ↓
       uv</pre></div>



<p class="wp-block-paragraph">For Genome Toolkit, this gives us a clean modern starting point without adding unnecessary tools.</p>



<h3 class="wp-block-heading">Installing <code>uv</code></h3>



<p class="wp-block-paragraph">Before we start using <code>uv</code>, we first need to install it.</p>



<p class="wp-block-paragraph">On macOS and Linux, run:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">curl -LsSf https://astral.sh/uv/install.sh | sh</pre></div>



<p class="wp-block-paragraph">On Windows, run:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;powershell&quot;,&quot;mime&quot;:&quot;application/x-powershell&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;PowerShell&quot;,&quot;language&quot;:&quot;PowerShell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;powershell&quot;}">powershell -ExecutionPolicy ByPass -c &quot;irm https://astral.sh/uv/install.ps1 | iex&quot;</pre></div>



<p class="wp-block-paragraph">That installs <code>uv</code> on our system so we can use commands such as:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">uv init
uv sync
uv run</pre></div>



<p class="wp-block-paragraph">If you already have <code>uv</code> installed, you can skip this step.</p>



<h3 class="wp-block-heading">Removing the Old Environment Files</h3>



<p class="wp-block-paragraph">We begin by removing the old Pipenv declarations:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">rm Pipfile Pipfile.lock</pre></div>



<p class="wp-block-paragraph">We are not deleting our Python code. <code>application.py</code> and <code>genome_toolkit.py</code> stay in place while we create the new package around them.</p>



<p class="wp-block-paragraph">Our existing <code>.gitignore</code> also needs a small update.</p>



<p class="wp-block-paragraph">Because this project already has its existing:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">.git/</pre></div>



<p class="wp-block-paragraph">Git repository, <code>uv</code> does not generate a new <code>.gitignore</code> for us. So instead of deleting the file, open the existing <code>.gitignore</code> and replace its contents with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}"># Python-generated files

__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info

# Virtual environments

.venv</pre></div>



<p class="wp-block-paragraph">This keeps temporary Python files, build files, and our local virtual environment out of the Git repository.</p>



<p class="wp-block-paragraph">We keep <code>.git/</code> exactly where it is because it contains the existing history of Genome Toolkit. At this point, the scientific code has not changed at all; we have only removed the old Pipenv configuration and updated the files that control our development environment.</p>



<h2 class="wp-block-heading">Initializing Genome Toolkit as a Library</h2>



<p class="wp-block-paragraph">Now we can initialize the existing directory as a <code>uv</code> library:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv init --lib --name genome-toolkit</pre></div>



<p class="wp-block-paragraph">There are two useful parts in this command. <code>--name genome-toolkit</code> gives the project its package distribution name, while <code>--lib</code> tells <code>uv</code> that Genome Toolkit is a <strong>library</strong>: reusable Python code that other programs can import and use.</p>



<p class="wp-block-paragraph">That distinction is useful for our project:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">library
provides reusable scientific functions and objects

application
uses those functions and decides what to show the user</pre></div>



<p class="wp-block-paragraph">Genome Toolkit itself is becoming the reusable library. Our existing <code>application.py</code> stays at the top level as a simple development and demonstration program that uses that library.</p>



<p class="wp-block-paragraph">After running the command, <code>uv</code> creates the modern project files and a <code>src/</code> package structure for us. The exact small details generated by <code>uv</code> can change between versions, so the important thing is to understand the structure rather than memorize every generated line.</p>



<p class="wp-block-paragraph">We should now see the beginning of a package layout similar to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">genome_toolkit/
├── .git/
├── .gitignore
├── application.py
├── genome_toolkit.py
├── pyproject.toml
├── README.md
└── src/
    └── genome_toolkit/
        ├── __init__.py
        └── py.typed</pre></div>



<p class="wp-block-paragraph">This is our first major improvement. We still have our original files at the repository root, but now we also have the place where our real installable <code>genome_toolkit</code> package will live:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">src/genome_toolkit/</pre></div>



<h2 class="wp-block-heading">What Is the <code>src/</code> Layout?</h2>



<p class="wp-block-paragraph">The new <code>src/</code> directory may look like an unnecessary extra folder at first. Why not simply put the package directly in the repository root?</p>



<p class="wp-block-paragraph">The basic idea is separation:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">repository files
        ↓
src/
        ↓
installable Python package</pre></div>



<p class="wp-block-paragraph">The repository contains things such as <code>README.md</code>, configuration files, tests, sample data, and our development application. The actual Python package that another project imports lives under <code>src/</code>.</p>



<p class="wp-block-paragraph">This gives us a clearer distinction between:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">the project repository
and
the installed genome_toolkit package</pre></div>



<p class="wp-block-paragraph">Later, when we write:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">import genome_toolkit</pre></div>



<p class="wp-block-paragraph">we want Python to use the installed package under <code>src/</code>, not accidentally find some unrelated file at the repository root.</p>



<p class="wp-block-paragraph">We will see one very practical example of why that matters later in this article when we remove the old <code>genome_toolkit.py</code> file.</p>



<h2 class="wp-block-heading">The New <code>pyproject.toml</code></h2>



<p class="wp-block-paragraph">One of the most important files created by <code>uv</code> is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">pyproject.toml</pre></div>



<p class="wp-block-paragraph">This is the main configuration file for a modern Python project. Our old setup used <code>Pipfile</code> for environment and dependency information, while <code>pyproject.toml</code> gives us one standard place for the package information that Python tools need.</p>



<p class="wp-block-paragraph">For Genome Toolkit, we keep the structure generated by <code>uv</code> and set the project information we actually own:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;toml&quot;,&quot;mime&quot;:&quot;text/x-toml&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;TOML&quot;,&quot;language&quot;:&quot;TOML&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;toml&quot;}">[project]
name = &quot;genome-toolkit&quot;
version = &quot;0.1.0&quot;
description = &quot;A small, typed bioinformatics toolkit for validated sequences and structured analyses.&quot;
readme = &quot;README.md&quot;
requires-python = &quot;&gt;=3.12&quot;
dependencies = []</pre></div>



<p class="wp-block-paragraph">Let us go through this from top to bottom.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;toml&quot;,&quot;mime&quot;:&quot;text/x-toml&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;TOML&quot;,&quot;language&quot;:&quot;TOML&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;toml&quot;}">name = &quot;genome-toolkit&quot;</pre></div>



<p class="wp-block-paragraph">This is the package distribution name.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;toml&quot;,&quot;mime&quot;:&quot;text/x-toml&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;TOML&quot;,&quot;language&quot;:&quot;TOML&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;toml&quot;}">version = &quot;0.1.0&quot;</pre></div>



<p class="wp-block-paragraph">Genome Toolkit is still at the beginning of its development, so we keep our current <code>0.1.0</code> version.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;toml&quot;,&quot;mime&quot;:&quot;text/x-toml&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;TOML&quot;,&quot;language&quot;:&quot;TOML&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;toml&quot;}">description = &quot;A small, typed bioinformatics toolkit for validated sequences and structured analyses.&quot;</pre></div>



<p class="wp-block-paragraph">This gives package tools a short description of what the project is.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;toml&quot;,&quot;mime&quot;:&quot;text/x-toml&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;TOML&quot;,&quot;language&quot;:&quot;TOML&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;toml&quot;}">readme = &quot;README.md&quot;</pre></div>



<p class="wp-block-paragraph">This tells packaging tools which file contains the longer project description.</p>



<p class="wp-block-paragraph">Then we have:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;toml&quot;,&quot;mime&quot;:&quot;text/x-toml&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;TOML&quot;,&quot;language&quot;:&quot;TOML&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;toml&quot;}">requires-python = &quot;&gt;=3.12&quot;</pre></div>



<p class="wp-block-paragraph">This tells users and package tools which Python versions Genome Toolkit supports. Notice that this does <strong>not</strong> say that every developer must use one exact Python 3.12 installation; it says that the package requires Python 3.12 or newer.</p>



<p class="wp-block-paragraph">Finally:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;toml&quot;,&quot;mime&quot;:&quot;text/x-toml&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;TOML&quot;,&quot;language&quot;:&quot;TOML&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;toml&quot;}">dependencies = []</pre></div>



<p class="wp-block-paragraph">At this point Genome Toolkit has no external runtime dependencies, so the list is empty. That will change soon, but we do not add dependencies before we actually need them.</p>



<h3 class="wp-block-heading">Removing the Local Python Version Pin</h3>



<p class="wp-block-paragraph">Depending on the current <code>uv</code> template, project initialization may also create:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">.python-version</pre></div>



<p class="wp-block-paragraph">That file can pin a local checkout to one particular development Python version.</p>



<p class="wp-block-paragraph">For Genome Toolkit, the compatibility rule we care about is already declared in <code>pyproject.toml</code>:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;toml&quot;,&quot;mime&quot;:&quot;text/x-toml&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;TOML&quot;,&quot;language&quot;:&quot;TOML&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;toml&quot;}">requires-python = &quot;&gt;=3.12&quot;</pre></div>



<p class="wp-block-paragraph">So if <code>.python-version</code> was generated, we remove it:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">rm .python-version</pre></div>



<p class="wp-block-paragraph">This keeps the package rule simple: Python 3.12 or newer.</p>



<h2 class="wp-block-heading">How Does Genome Toolkit Become an Installable Package?</h2>



<p class="wp-block-paragraph">When we ran:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">uv init --lib --name genome-toolkit</pre></div>



<p class="wp-block-paragraph"><code>uv</code> also added the small piece of configuration Python needs to turn our source code into an installable package.</p>



<p class="wp-block-paragraph">You will see it inside <code>pyproject.toml</code> under:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">[build-system]</pre></div>



<p class="wp-block-paragraph">We do not need to change it.</p>



<p class="wp-block-paragraph">For Genome Toolkit, our setup is very simple:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Python code
      ↓
uv
      ↓
installable genome_toolkit package</pre></div>



<p class="wp-block-paragraph">We are not compiling C or C++ code, creating special extensions, or doing anything unusual. Genome Toolkit is just a normal Python package, so the setup that <code>uv</code> generated for us already does the job.</p>



<p class="wp-block-paragraph">This is all we need to understand for now. Later, if our package ever needs a more complicated build process, we can learn about that when we actually need it.</p>



<h2 class="wp-block-heading">Moving Our Algorithms Into the Package</h2>



<p class="wp-block-paragraph">Now our package structure is ready, but both of our scientific algorithms are still sitting in the old root-level file:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">genome_toolkit.py</pre></div>



<p class="wp-block-paragraph">There is no reason to recreate them one at a time. They already belong together, so we can simply move the existing file into our new package.</p>



<p class="wp-block-paragraph">Inside:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">src/genome_toolkit/</pre></div>



<p class="wp-block-paragraph">create a new folder called:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">algorithms</pre></div>



<p class="wp-block-paragraph">Then move:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">genome_toolkit.py</pre></div>



<p class="wp-block-paragraph">into that folder and rename it to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">kmer.py</pre></div>



<p class="wp-block-paragraph">Finally, create an empty:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">__init__.py</pre></div>



<p class="wp-block-paragraph">inside the new <code>algorithms/</code> folder.</p>



<p class="wp-block-paragraph">We should now have:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">src/
└── genome_toolkit/
    └── algorithms/
        ├── __init__.py
        └── kmer.py</pre></div>



<p class="wp-block-paragraph">That already makes the purpose of the file much clearer:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">genome_toolkit
      ↓
algorithms
      ↓
kmer.py
      ↓
our k-mer algorithms</pre></div>



<p class="wp-block-paragraph">Both of our existing algorithms have now moved into the new package together. We have not changed how they work yet; we have simply given them a better home.</p>



<h3 class="wp-block-heading">Simplifying <code>kmer.py</code></h3>



<p class="wp-block-paragraph">Our old file wrapped both algorithms inside the <code>genomeToolkit</code> class.</p>



<p class="wp-block-paragraph">Originally, the two methods begin like this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def count_kmer(self, sequence, kmer):</pre></div>



<p class="wp-block-paragraph">and:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def find_most_frequent_kmers(self, sequence, k_len):</pre></div>



<p class="wp-block-paragraph">For Part 4.2, we are making only one small structural change to them: they become plain functions instead of class methods.</p>



<p class="wp-block-paragraph">We will also add type hints to the function signatures:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def count_kmer(sequence: str, kmer: str) -&gt; int:</pre></div>



<p class="wp-block-paragraph">and:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def find_most_frequent_kmers(
    sequence: str,
    k_len: int,
) -&gt; list[str]:</pre></div>



<p class="wp-block-paragraph">That means:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">remove self
    +
add type hints</pre></div>



<p class="wp-block-paragraph">The actual algorithms stay exactly the same.</p>



<p class="wp-block-paragraph">Update <code>kmer.py</code> to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;&quot;K-mer analysis algorithms.&quot;&quot;&quot;


def count_kmer(sequence: str, kmer: str) -&gt; int:
    &quot;&quot;&quot;
    Counts the number of times a specific k-mer appears in a given sequence,
    including overlapping k-mers.

    Parameters:
        sequence (str): The DNA sequence to search in.
        kmer (str): The specific k-mer to search for in the sequence.

    Returns:
        int: The number of times the k-mer appears in the sequence.
    &quot;&quot;&quot;
    kmer_count = 0

    for position in range(len(sequence) - (len(kmer) - 1)):
        if sequence[position : position + len(kmer)] == kmer:
            kmer_count += 1

    return kmer_count


def find_most_frequent_kmers(
    sequence: str,
    k_len: int,
) -&gt; list[str]:
    &quot;&quot;&quot;
    Finds the most frequent k-mers of a given length in a DNA string.

    Parameters:
        sequence (str): The DNA string to search.
        k_len (int): The length of the k-mers to search for.

    Returns:
        list: A list of the most frequent k-mers in the DNA string.
    &quot;&quot;&quot;
    kmer_frequencies = {}

    for i in range(len(sequence) - k_len + 1):
        kmer = sequence[i : i + k_len]
        if kmer in kmer_frequencies:
            kmer_frequencies[kmer] += 1
        else:
            kmer_frequencies[kmer] = 1

    highest_frequency = max(kmer_frequencies.values())

    return [
        kmer
        for kmer, frequency in kmer_frequencies.items()
        if frequency == highest_frequency
    ]</pre></div>



<p class="wp-block-paragraph">Notice what did <strong>not</strong> change:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">kmer_count = 0
kmer_frequencies = {}
the loops
the comparisons
the counting logic
the return logic</pre></div>



<p class="wp-block-paragraph">We are not improving or modernizing the algorithms themselves in this part. We are moving the same calculations into a proper package and changing the way we call them.</p>



<h2 class="wp-block-heading">Exposing Our Algorithm API</h2>



<p class="wp-block-paragraph">We now have:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">algorithms/
├── __init__.py
└── kmer.py</pre></div>



<p class="wp-block-paragraph">We could import directly from the <code>kmer.py</code> module:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.algorithms.kmer import count_kmer</pre></div>



<p class="wp-block-paragraph">That works, but we can give users a cleaner public entry point through:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">src/genome_toolkit/algorithms/__init__.py</pre></div>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;&quot;Bioinformatics algorithms.&quot;&quot;&quot;

from .kmer import count_kmer, find_most_frequent_kmers

__all__ = [&quot;count_kmer&quot;, &quot;find_most_frequent_kmers&quot;]</pre></div>



<p class="wp-block-paragraph">Now both algorithms can be imported from:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.algorithms import (
    count_kmer,
    find_most_frequent_kmers,
)</pre></div>



<p class="wp-block-paragraph">This is the public algorithm interface we want to expose.</p>



<p class="wp-block-paragraph">The line:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from .kmer import count_kmer, find_most_frequent_kmers</pre></div>



<p class="wp-block-paragraph">re-exports the two functions from <code>kmer.py</code> through the <code>algorithms</code> package.</p>



<p class="wp-block-paragraph">Then:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">__all__ = [&quot;count_kmer&quot;, &quot;find_most_frequent_kmers&quot;]</pre></div>



<p class="wp-block-paragraph">makes our intention explicit: these are the names this package is deliberately exposing as its public API.</p>



<p class="wp-block-paragraph">For now, we only expose the two algorithms we actually have.</p>



<p class="wp-block-paragraph">That is enough to reconnect our application immediately and check whether the refactor still produces the exact same result.</p>



<h2 class="wp-block-heading">Updating <code>application.py</code></h2>



<p class="wp-block-paragraph">Our algorithms are now inside the new package, so the next step is to update <code>application.py</code> to use them.</p>



<p class="wp-block-paragraph">We only need to change the import, remove the old <code>gt</code> object, and update the two places where we call our algorithms:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from genome_toolkit.algorithms import (  # CHANGE TO
    count_kmer,
    find_most_frequent_kmers,
)


# gt = genomeToolkit()  # REMOVE

seq = &quot;AATTTTAAAAC&quot;
kmer = &quot;AA&quot;
k_len = 3

print(f&quot;Sequence: {seq}&quot;)
print(f&quot;k-mer: {kmer}&quot;)
print(f&quot;Repeats found: {count_kmer(seq, kmer)}&quot;)  # UPDATED
print(
    &quot;Most frequent k-mer:&quot;,
    find_most_frequent_kmers(seq, k_len),  # UPDATED
)</pre></div>



<p class="wp-block-paragraph">That is it.</p>



<p class="wp-block-paragraph">We no longer import the old <code>genomeToolkit</code> class or create a <code>gt</code> object. Instead, we import our two functions directly from the new <code>algorithms</code> package and call them directly.</p>



<p class="wp-block-paragraph">Everything else stays exactly the same.</p>



<p class="wp-block-paragraph">Now we are ready to synchronize the new package and test it.</p>



<h2 class="wp-block-heading">Synchronizing and Testing the New Project</h2>



<p class="wp-block-paragraph">Now we want to test our changes immediately.</p>



<p class="wp-block-paragraph">Because Genome Toolkit is now an installable package under <code>src/</code>, we first synchronize the project environment, run this command from the project root:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">uv sync</pre></div>



<p class="wp-block-paragraph">The first time we run <code>uv sync</code>, <code>uv</code> creates our local virtual environment:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">.venv/</pre></div>



<p class="wp-block-paragraph">It also creates:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">uv.lock</pre></div>



<p class="wp-block-paragraph"><code>uv.lock</code> records the exact package versions used by the project so the same environment can be recreated consistently later.</p>



<p class="wp-block-paragraph"><code>uv sync</code> then installs our current Genome Toolkit package into the new <code>.venv</code> according to <code>pyproject.toml</code>.</p>



<p class="wp-block-paragraph">The first time this environment is created, VS Code or VSCodium may show a message like:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">We noticed a new environment has been created.
Do you want to select it for the workspace folder?</pre></div>



<p class="wp-block-paragraph">Just select <strong>Yes</strong>.</p>



<p class="wp-block-paragraph">If the message does not appear, or the editor is still using another Python installation, check the Python interpreter shown in the bottom-right corner of VS Code/VSCodium and select our new project environment. It should point to the <code>.venv</code> created inside the Genome Toolkit project, usually shown as something similar to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">('.venv': venv)</pre></div>



<p class="wp-block-paragraph">After this step, both the editor and our project environment know where:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">genome_toolkit</pre></div>



<p class="wp-block-paragraph">actually lives, so our new import can work normally.</p>



<p class="wp-block-paragraph">If you use <code>uv run</code>, <code>uv</code> can also synchronize the environment automatically before running the command. We are running <code>uv sync</code> explicitly here because it gives us a clear checkpoint and also prepares the <code>.venv</code> environment for editors and Code Runner.</p>



<p class="wp-block-paragraph">Now for the important part.</p>



<p class="wp-block-paragraph">Run:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">uv run python application.py</pre></div>



<p class="wp-block-paragraph">We should see:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Sequence: AATTTTAAAAC
k-mer: AA
Repeats found: 4
Most frequent k-mer: ['TTT', 'AAA']</pre></div>



<p class="wp-block-paragraph">The scientific output is exactly the same as before.</p>



<p class="wp-block-paragraph">That is the result we wanted. We changed the environment manager, package structure, imports, and the way our algorithms are exposed, and we also replaced a stateless class with plain functions.</p>



<p class="wp-block-paragraph">But <code>count_kmer()</code> still returns:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">4</pre></div>



<p class="wp-block-paragraph">and <code>find_most_frequent_kmers()</code> still returns:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">['TTT', 'AAA']</pre></div>



<p class="wp-block-paragraph">Perfect. This is the checkpoint we wanted: we moved the algorithms, simplified how we call them, reconnected <code>application.py</code>, and the scientific result is still exactly the same.</p>



<p class="wp-block-paragraph">Now that the important path works again, we can take care of two small package details before we finish.</p>



<h2 class="wp-block-heading">Keeping One Package Version</h2>



<p class="wp-block-paragraph">Now that our application works again, let us clean up one small package detail. The <code>uv</code> template also created:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">src/genome_toolkit/__init__.py</pre></div>



<p class="wp-block-paragraph">This is the top-level package initializer.</p>



<p class="wp-block-paragraph">We already declared our package version in <code>pyproject.toml</code>:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">version = &quot;0.1.0&quot;</pre></div>



<p class="wp-block-paragraph">We could write the same version again inside Python:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">__version__ = &quot;0.1.0&quot;</pre></div>



<p class="wp-block-paragraph">but then we would have two places to keep synchronized. Instead, we keep one source of truth, so replace the generated <code>src/genome_toolkit/__init__.py</code> with:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">&quot;&quot;&quot;Genome Toolkit package.&quot;&quot;&quot;

from importlib.metadata import version

__version__ = version(&quot;genome-toolkit&quot;)

__all__ = [&quot;__version__&quot;]</pre></div>



<p class="wp-block-paragraph">The new import:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">from importlib.metadata import version</pre></div>



<p class="wp-block-paragraph">comes from Python&#8217;s standard library. It lets our package read the version stored in the installed package metadata.</p>



<p class="wp-block-paragraph">Then:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">__version__ = version(&quot;genome-toolkit&quot;)</pre></div>



<p class="wp-block-paragraph">means Python can still tell us the Genome Toolkit version, but the actual version number remains declared in one place:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">pyproject.toml
        ↓
installed package metadata
        ↓
genome_toolkit.__version__</pre></div>



<p class="wp-block-paragraph">That is easier to maintain than copying <code>"0.1.0"</code> into multiple files.</p>



<p class="wp-block-paragraph">This may look like a small packaging detail now, but the version number will become much more important once we start using Genome Toolkit for real biological experiments.</p>



<p class="wp-block-paragraph">Imagine that later we analyze a real genome and save the result. If Genome Toolkit changes over time, we will want to know exactly which version of the toolkit produced that result:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">Genome Toolkit 0.1.0
        ↓
biological data
        ↓
scientific analysis
        ↓
saved result</pre></div>



<p class="wp-block-paragraph">That version becomes part of the scientific context of the experiment. If we return to the same data months later, or somebody else wants to repeat our analysis, knowing which Genome Toolkit version was used helps us reproduce the same computational setup.</p>



<p class="wp-block-paragraph">This connects directly to two ideas we introduced in Part 4.1: <strong>reproducibility</strong> and <strong>provenance</strong>. We will come back to both in much more detail in the upcoming articles, when Genome Toolkit starts returning structured scientific results and we begin recording where those results came from.</p>



<p class="wp-block-paragraph">For now, the important thing is simple: Genome Toolkit should have one reliable version number, and every part of the package should read that same version.</p>



<h2 class="wp-block-heading">What Is <code>py.typed</code>? In Simple Terms</h2>



<p class="wp-block-paragraph">There is one more small file that <code>uv</code> created for us:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">src/genome_toolkit/py.typed</pre></div>



<p class="wp-block-paragraph">The file is empty, and we leave it empty.</p>



<p class="wp-block-paragraph">So what does it actually do?</p>



<p class="wp-block-paragraph">In simple terms, <code>py.typed</code> is just a small <strong>marker file</strong>. It tells Python development tools:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Genome Toolkit includes type hints in its code, so you can use them.</p>
</blockquote>



<p class="wp-block-paragraph">For example, we now have functions like:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def count_kmer(sequence: str, kmer: str) -&gt; int:</pre></div>



<p class="wp-block-paragraph">The type hints tell us that:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">sequence → string
kmer     → string
result   → integer</pre></div>



<p class="wp-block-paragraph">And:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def find_most_frequent_kmers(
    sequence: str,
    k_len: int,
) -&gt; list[str]:</pre></div>



<p class="wp-block-paragraph">tells us that the function expects a string and an integer, and returns a list of strings.</p>



<p class="wp-block-paragraph">The <code>py.typed</code> file simply tells compatible editors and type-checking tools that these type hints are intentionally part of Genome Toolkit.</p>



<p class="wp-block-paragraph">Without <code>py.typed</code>, another project may import Genome Toolkit and still run perfectly, but some type-checking tools may not automatically treat our package&#8217;s type hints as part of its public interface.</p>



<p class="wp-block-paragraph">With <code>py.typed</code>, we are explicitly telling those tools:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">Genome Toolkit includes type information.
Please use it.</pre></div>



<p class="wp-block-paragraph">So if another developer passes the wrong kind of value to one of our functions, their editor or type checker has a better chance of warning them before the code even runs.</p>



<p class="wp-block-paragraph">We do not need to write anything inside <code>py.typed</code>. We just keep the empty file in the package.</p>



<h2 class="wp-block-heading">A Quick <code>README.md</code> Update</h2>



<p class="wp-block-paragraph">Before we finish the main part of Part 4.2, let us make one small but useful update to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">README.md</pre></div>



<p class="wp-block-paragraph"><code>uv</code> created this file when we initialized the project, and now we can add a few simple instructions so anyone who finds Genome Toolkit on GitHub knows how to run it after cloning the repository.</p>



<p class="wp-block-paragraph">Add:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">```markdown
## Running Genome Toolkit

After cloning the repository, synchronize the project environment:

```bash
uv sync
```

Then run Genome Toolkit:

```bash
uv run application.py
```</pre></div>



<p class="wp-block-paragraph">That is enough for now.</p>



<p class="wp-block-paragraph">Anyone cloning the project can immediately see the two steps they need:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">clone repository
      ↓
uv sync
      ↓
uv run application.py</pre></div>



<p class="wp-block-paragraph">As Genome Toolkit grows, we can keep expanding the README with installation instructions, examples, supported biological data, and other useful project information. For now, we only add what a new user actually needs to run the project.</p>



<h2 class="wp-block-heading">Before We Finish Part 4.2</h2>



<p class="wp-block-paragraph">And that is it for the main part of Part 4.2. Genome Toolkit is now running as a modern Python package, our two original k-mer algorithms are still producing the same results, and we have a much better foundation for everything we are going to build next.</p>



<p class="wp-block-paragraph">Before we close this part, however, I want to introduce two optional Pro Tips that will help us a lot as Genome Toolkit grows. They are not required for the project, so you can skip them completely if you want, but they are also useful habits that can serve you well in your own Python projects.</p>



<p class="wp-block-paragraph">The first is for those of us who use the <strong>Code Runner</strong> extension. Because our project now uses <code>uv</code> and its own <code>.venv</code>, Code Runner may need a small configuration change so it runs our code inside the correct environment.</p>



<p class="wp-block-paragraph">The second is about <strong>Ruff</strong>, a modern Python linter and formatter. It can automatically keep our code clean and consistently formatted as the project grows.</p>



<p class="wp-block-paragraph">If you want to keep the setup minimal, feel free to skip both. If you want a smoother development workflow going forward, I recommend following them.</p>



<h2 class="wp-block-heading">Pro Tip #1: Code Runner With <code>uv</code></h2>



<p class="wp-block-paragraph">If you have been following rebelScience for a while, you may already use the <strong>Code Runner</strong> extension in VS Code or VSCodium to run Python files quickly without typing a terminal command every time.</p>



<p class="wp-block-paragraph">The default shortcuts are:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Linux / Windows: Ctrl + Alt + N
macOS:           Cmd + Option + N</pre></div>



<p class="wp-block-paragraph">After moving Genome Toolkit to <code>uv</code>, there is one small thing we need to check.</p>



<p class="wp-block-paragraph"><code>uv</code> created our local Python environment inside:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">.venv/</pre></div>



<p class="wp-block-paragraph">But Code Runner may still try to use the global Python installation on your computer. If that happens, it may not be able to find our new installed <code>genome_toolkit</code> package.</p>



<p class="wp-block-paragraph">Fortunately, this is a simple one-time fix. We just need to tell Code Runner to use the Python interpreter already selected by VS Code or VSCodium.</p>



<ol class="wp-block-list">
<li>Open the Command Palette:</li>
</ol>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Ctrl + Shift + P</pre></div>



<p class="wp-block-paragraph">or on macOS:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Cmd + Shift + P</pre></div>



<ol start="2" class="wp-block-list">
<li>Search for <strong>Open User Settings</strong> and select the option with <strong>(JSON)</strong> in its name.</li>



<li>Find the existing:</li>
</ol>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;application/json&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;JSON&quot;,&quot;language&quot;:&quot;JSON&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;json&quot;}">&quot;code-runner.executorMap&quot;</pre></div>



<p class="wp-block-paragraph">section.</p>



<ol start="4" class="wp-block-list">
<li>Update only the Python entry to:</li>
</ol>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;application/json&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;JSON&quot;,&quot;language&quot;:&quot;JSON&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;json&quot;}">&quot;code-runner.executorMap&quot;: {
    &quot;python&quot;: &quot;$pythonPath -u $fullFileName&quot;
}</pre></div>



<p class="wp-block-paragraph">Leave your other language entries unchanged and save the file.</p>



<p class="wp-block-paragraph">Code Runner will now use the Python interpreter selected by the editor, which for our project should be the one inside <code>.venv/</code>.</p>



<p class="wp-block-paragraph">That means our quick:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Ctrl + Alt + N</pre></div>



<p class="wp-block-paragraph">shortcut can keep working exactly as before, but now it runs Genome Toolkit inside our new <code>uv</code> environment.</p>



<h2 class="wp-block-heading">Pro Tip #2: Automatic Formatting With Ruff</h2>



<p class="wp-block-paragraph">Here is another small setup improvement that can save us a lot of repetitive work as Genome Toolkit grows.</p>



<p class="wp-block-paragraph">When we work on the same project across many files and many sessions, formatting can slowly become inconsistent. Spacing changes, imports move around, and a tiny code change can create a much larger Git diff than it really needs to.</p>



<p class="wp-block-paragraph">For Python, we are going to use <strong>Ruff</strong>.</p>



<p class="wp-block-paragraph">Ruff is a modern Python <strong>linter</strong> and <strong>formatter</strong> built by Astral, the same team behind <code>uv</code>. A linter helps spot common code problems, while a formatter automatically keeps our Python code laid out consistently.</p>



<p class="wp-block-paragraph">And as always, Corey Schafer has an excellent detailed walkthrough if you want to understand Ruff beyond the small setup we need here:</p>



<p class="wp-block-paragraph"><strong><a href="https://youtu.be/828S-DMQog8">Python Tutorial: Ruff &#8211; A Fast Linter &amp; Formatter to Replace Multiple Tools and Improve Code Quality</a></strong></p>



<p class="wp-block-paragraph">If you use VS Code or VSCodium, the easiest setup is through the official <strong>Ruff</strong> extension published by Astral Software.</p>



<p class="wp-block-paragraph">Open the Extensions panel:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Ctrl + Shift + X</pre></div>



<p class="wp-block-paragraph">or on macOS:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Cmd + Shift + X</pre></div>



<p class="wp-block-paragraph">Search for <strong>Ruff</strong> and install or enable the official extension.</p>



<p class="wp-block-paragraph">Then open your User Settings JSON and add or verify:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;application/json&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;JSON&quot;,&quot;language&quot;:&quot;JSON&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;json&quot;}">{
    &quot;[python]&quot;: {
        &quot;editor.defaultFormatter&quot;: &quot;charliermarsh.ruff&quot;,
        &quot;editor.formatOnSave&quot;: true,
        &quot;editor.codeActionsOnSave&quot;: {
            &quot;source.fixAll.ruff&quot;: &quot;explicit&quot;,
            &quot;source.organizeImports.ruff&quot;: &quot;explicit&quot;
        }
    },
    &quot;ruff.nativeServer&quot;: &quot;on&quot;
}</pre></div>



<p class="wp-block-paragraph">The most important setting for our everyday workflow is:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;application/json&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;JSON&quot;,&quot;language&quot;:&quot;JSON&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;json&quot;}">&quot;editor.formatOnSave&quot;: true</pre></div>



<p class="wp-block-paragraph">Now every time we save a Python file, Ruff can automatically keep the formatting clean and consistent for us.</p>



<p class="wp-block-paragraph">Ruff is a development tool, not something Genome Toolkit needs in order to run. We therefore do not add it to the package&#8217;s normal runtime dependencies just to format our source code.</p>



<h2 class="wp-block-heading">Our New Project Structure</h2>



<p class="wp-block-paragraph">After synchronizing the project, the repository should now be approximately:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">genome_toolkit/
├── .git/
├── .gitignore
├── .venv/
├── README.md
├── application.py
├── pyproject.toml
├── uv.lock
└── src/
    └── genome_toolkit/
        ├── __init__.py
        ├── py.typed
        └── algorithms/
            ├── __init__.py
            └── kmer.py</pre></div>



<p class="wp-block-paragraph">This is still a very small project, which is good.</p>



<p class="wp-block-paragraph">We now have a proper place for the scientific package:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">src/genome_toolkit/</pre></div>



<p class="wp-block-paragraph">and a dedicated place for our current algorithms:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">src/genome_toolkit/algorithms/</pre></div>



<p class="wp-block-paragraph">As we continue through Parts 4.x, we can add the next pieces only when we actually need them.</p>



<h2 class="wp-block-heading">What Changed and What Stayed the Same?</h2>



<p class="wp-block-paragraph">Quite a lot changed structurally in this article.</p>



<p class="wp-block-paragraph">We moved from:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Pipenv
        ↓
uv</pre></div>



<p class="wp-block-paragraph">We moved from:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">loose genome_toolkit.py
        ↓
installable src/genome_toolkit package</pre></div>



<p class="wp-block-paragraph">And we moved from:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">stateless genomeToolkit object
        ↓
plain algorithm functions</pre></div>



<p class="wp-block-paragraph">Our application also imports the scientific functions from the package rather than constructing an object first.</p>



<p class="wp-block-paragraph">But the important scientific parts did not change:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">same input strings
same k-mer calculations
same int/list return values
same scientific results</pre></div>



<p class="wp-block-paragraph">That distinction is exactly what we wanted to see when we introduced refactoring in Part 4.1. We can improve the way our software is organized while keeping the scientific behavior we already understand.</p>



<h2 class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">In Part 4.2, we took our original working Genome Toolkit and turned it into a modern installable Python package. We moved from Pipenv to <code>uv</code>, created the <code>src/</code> package structure, moved our existing k-mer algorithms into their new home, simplified the old class methods into plain typed functions, and updated <code>application.py</code> to use them.</p>



<p class="wp-block-paragraph">Most importantly, the scientific calculations did not change. We reorganized the project, synchronized the new environment, and then confirmed that Genome Toolkit still produces exactly the same k-mer results as before. We now have a much cleaner foundation that will be easier to expand, test, share, and eventually use in real biological experiments.</p>



<h3 class="wp-block-heading">New Concepts We Learned</h3>



<ul class="wp-block-list">
<li><strong><code>uv</code></strong> — Our new Python project and package manager. It creates and manages the virtual environment, installs dependencies, generates the lock file, runs our project, and helps build the package. It replaces several separate project-management tasks with one modern tool.</li>



<li><strong><code>pyproject.toml</code></strong> — The main project configuration file. It tells Python tools what Genome Toolkit is, which Python version and dependencies it needs, its package version and description, and how the project should be built. It solves the problem of keeping the important project and package configuration in one standard place.</li>



<li><strong><code>README.md</code></strong> — Human-facing project documentation. It explains what the project does and, in our case, now includes the basic commands needed to run Genome Toolkit after cloning the repository. It helps other people, and future us, understand and use the project without first reading through the source code.</li>



<li><strong><code>uv.lock</code></strong> — Records the exact dependency versions selected by <code>uv</code>. For example, if <code>pyproject.toml</code> later allows a range of Pydantic versions, <code>uv.lock</code> records the exact version actually chosen for our project. This helps different machines recreate the same environment instead of silently installing slightly different versions.</li>



<li><strong><code>.venv/</code></strong> — Genome Toolkit&#8217;s private Python environment. It contains the Python environment and installed packages used by this project, keeping them isolated from other Python projects on the same computer. This prevents one project&#8217;s dependencies from easily interfering with another.</li>



<li><strong><code>src/</code></strong> — Contains the actual installable source code, such as <code>src/genome_toolkit/</code>. It separates the Python package itself from project files such as <code>README.md</code>, <code>pyproject.toml</code>, and <code>application.py</code>, and helps us work with Genome Toolkit the same way another installed project would.</li>



<li><strong><code>__init__.py</code></strong> — Used inside package directories such as <code>genome_toolkit/</code> and <code>algorithms/</code>. In our project, it helps define the package structure and can expose a cleaner public API. For example, instead of importing <code>count_kmer</code> from <code>genome_toolkit.algorithms.kmer</code>, we can expose it through <code>genome_toolkit.algorithms</code> and use the shorter import.</li>



<li><strong><code>__all__</code></strong> — Makes the names we intentionally expose from a module or package explicit. In <code>algorithms/__init__.py</code>, it tells readers and development tools that <code>count_kmer</code> and <code>find_most_frequent_kmers</code> are part of the public algorithm API we want people to use.</li>



<li><strong><code>py.typed</code></strong> — Usually an empty marker file saying that the installed package officially provides Python type annotations. It helps external tools such as Pyright, mypy, and IDEs understand that they can use Genome Toolkit&#8217;s type hints when checking code that imports our package.</li>



<li><strong>Type hints</strong> — Extra information in function signatures that describes the kinds of values a function expects and returns. For example, <code>sequence: str</code> tells us that <code>count_kmer()</code> expects a string, while <code>-> int</code> tells us that it returns an integer. They make our code easier to understand and allow editors and type-checking tools to catch some mistakes earlier.</li>



<li><strong>Plain functions instead of class methods</strong> — Our original k-mer algorithms lived inside the <code>genomeToolkit</code> class even though they did not need to store any object state. Moving them to plain functions makes them simpler to call and keeps the scientific code focused on the calculation itself, while preserving the exact same algorithm behavior.</li>



<li><strong>Package versioning</strong> — Genome Toolkit now keeps one authoritative version in <code>pyproject.toml</code> and reads that version from the installed package metadata. Later, when we run experiments on real biological data, recording which Genome Toolkit version produced a result will become important for reproducibility and provenance.</li>
</ul>



<p class="wp-block-paragraph">The most important thing we learned is that modernizing the structure of a scientific project does not mean rewriting the science. Our algorithms still perform the same calculations, but they now live inside a project that is much easier to install, understand, maintain, and grow.</p>



<h2 class="wp-block-heading">What is Next?</h2>



<p class="wp-block-paragraph">Genome Toolkit is now a real modern Python package.</p>



<p class="wp-block-paragraph">But our algorithms still accept this:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">sequence: str</pre></div>



<p class="wp-block-paragraph">And a Python string can contain almost anything:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">AATTTTAAAAC
HELLO
12345
???</pre></div>



<p class="wp-block-paragraph">Python does not know which one is a biological sequence.</p>



<p class="wp-block-paragraph">So in Part 4.3, we are going to solve the next problem:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">If an algorithm is supposed to work on biological sequences, should any random string be allowed to reach it?</p>
</blockquote>



<p class="wp-block-paragraph">We will introduce our first validated biological sequence models and start teaching Genome Toolkit what DNA actually looks like.</p>



<p class="wp-block-paragraph">The full source code for this part of Genome Toolkit is available here:</p>



<p class="wp-block-paragraph"><a href="https://github.com/rebelC0der/Genome_Toolkit/commits/main">https://github.com/rebelC0der/Genome_Toolkit/commits/main</a></p>



<p class="wp-block-paragraph">I hope this next step in building Genome Toolkit was useful for your bioinformatics and programming journey! If you found this article valuable and want to help us continue building rebelScience, please consider supporting our project. You can explore various ways to contribute <a href="https://rebelscience.club/cryptocurrency-donations/">here</a>.</p>



<p class="wp-block-paragraph">Until next time, rebelCoder, signing out.</p>



<p class="wp-block-paragraph">Video version of this article is available here: <a href="https://youtu.be/fPLjrExTQTM">https://youtu.be/fPLjrExTQTM</a></p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Genome Toolkit. Part 4.2: Modernizing the Existing Python Project" width="640" height="360" src="https://www.youtube.com/embed/fPLjrExTQTM?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>
]]></content:encoded></item><item><title>Genome Toolkit. Part 4.1: Building a Scientific Python Package</title><link>https://rebelscience.club/2026/08/genome-toolkit-part-4-1-building-a-scientific-python-package/</link><guid isPermaLink="true">https://rebelscience.club/2026/08/genome-toolkit-part-4-1-building-a-scientific-python-package/</guid><pubDate>Mon, 24 Aug 2026 12:21:46 GMT</pubDate><description>In Part 4.1, we take a step back and look at where Genome Toolkit is going next. We introduce refactoring, explain why our small project is ready to grow into a proper scientific Python package, and show how this gives us a cleaner foundation for future biological tools and experiments. We also look at something pretty exciting: by building Genome Toolkit properly now, we are gradually making it AI-ready for future APIs, MCP tools, and AI agents.
</description><content:encoded><![CDATA[
<p class="wp-block-paragraph">Welcome back to the Genome Toolkit series!</p>



<p class="wp-block-paragraph">If you have been following rebelScience and our Genome Toolkit series for a while, you know that it has been quite a while since our last video. A few years, actually. And a lot has changed across software engineering, bioinformatics, and scientific research.</p>



<p class="wp-block-paragraph">In Parts 1, 2, and 3, we built our first useful bioinformatics algorithms using a very small Python project:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;shell&quot;,&quot;mime&quot;:&quot;text/x-sh&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Shell&quot;,&quot;language&quot;:&quot;Shell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;shell&quot;}">genome_toolkit/
├── .git/
├── .gitignore
├── application.py
├── genome_toolkit.py
├── Pipfile
└── Pipfile.lock</pre></div>



<p class="wp-block-paragraph">That project already works. <code>application.py</code> creates our <code>genomeToolkit</code> object, runs the two k-mer algorithms we have built so far, and gives us:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Sequence: AATTTTAAAAC
k-mer: AA
Repeats found: 4
Most frequent k-mer: ['TTT', 'AAA']</pre></div>



<p class="wp-block-paragraph">This was exactly the right structure for learning our first algorithms. We could keep everything close together, focus on the biology and Python, and immediately see what our code produced.</p>



<p class="wp-block-paragraph">We are absolutely going to continue building Genome Toolkit. We want to add more biological sequence types, load real biological data from files and databases, add more bioinformatics algorithms, and eventually use those tools together in larger experiments.</p>



<p class="wp-block-paragraph">Before we add all of that, however, we are going to improve the structure underneath the project and turn Genome Toolkit into a small scientific Python package.</p>



<p class="wp-block-paragraph">That process is called <strong>refactoring</strong>. Refactoring means reorganizing and improving the structure of existing software without changing what its core functionality is supposed to do.</p>



<p class="wp-block-paragraph">And by doing this now, relatively early in the project, we are going to get something pretty amazing almost for free. While we are turning Genome Toolkit into a cleaner, more professional scientific Python package, we are also gradually making it <strong>AI-ready</strong>.</p>



<p class="wp-block-paragraph">We will see exactly what that means as we progress through Parts 4.x. For now, the important idea is simple: the same clean, tested Genome Toolkit that we use ourselves will also become much easier for APIs, MCP tools, and AI agents to use later, without having to rebuild our scientific logic every time.</p>



<p class="wp-block-paragraph">We will keep using the same working Genome Toolkit while we improve it step by step. <code>application.py</code> will remain our familiar test: after each major change, we can run it again and make sure our original calculations still work.</p>



<h2 class="wp-block-heading">A Note for the Biologists</h2>



<p class="wp-block-paragraph">The next few parts will contain more software engineering than Parts 1 through 3, but our biological goal is not changing. We are still building Genome Toolkit so we can work with biological sequences, run useful algorithms, and eventually combine those tools into real experiments.</p>



<p class="wp-block-paragraph">The workflow will stay familiar:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">biological data
      ↓
Genome Toolkit
      ↓
scientific result</pre></div>



<p class="wp-block-paragraph">We are simply going to make the code underneath that workflow cleaner, easier to test, and easier to expand.</p>



<p class="wp-block-paragraph">You do not need to become a Python packaging expert to use Genome Toolkit. If your main interest is biology, you can treat these parts as the engineering foundation underneath the experiments we will build later.</p>



<h2 class="wp-block-heading">What Refactoring Means for Genome Toolkit</h2>



<p class="wp-block-paragraph">Right now, our project is very simple:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">application.py
      ↓
genome_toolkit.py
      ↓
two working algorithms</pre></div>



<p class="wp-block-paragraph">As Genome Toolkit grows, we want something closer to:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;textile&quot;,&quot;mime&quot;:&quot;text/x-textile&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Textile&quot;,&quot;language&quot;:&quot;Textile&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;textile&quot;}">application.py
      ↓
genome_toolkit package
      ↓
biological sequences
(DNA, RNA, proteins, etc.)
      ↓
data loaders
(FASTA, NCBI, other formats and sources)
      ↓
algorithms
(k-mer analysis, future sequence algorithms, etc.)
      ↓
scientific results
(results together with useful context)</pre></div>



<p class="wp-block-paragraph">The important point is that the scientific purpose stays the same. We are not changing <code>count_kmer()</code> just because we are reorganizing the project, and we are not changing <code>find_most_frequent_kmers()</code> just because the files around it move.</p>



<p class="wp-block-paragraph">We will improve the project structure first. Later, when we add automated tests, we can look at real edge cases and deliberately decide whether any algorithm behavior needs to change.</p>



<h2 class="wp-block-heading">Why Build a Scientific Python Package?</h2>



<p class="wp-block-paragraph">Our current two-file project works, but it was designed for a much smaller job. As Genome Toolkit grows, we will need to handle more than just two algorithms.</p>



<p class="wp-block-paragraph">For example, we are going to need code for things such as:</p>



<ul class="wp-block-list">
<li>representing and checking biological sequences;</li>



<li>loading sequence data from plain-text and FASTA files;</li>



<li>running different families of bioinformatics algorithms;</li>



<li>returning scientific results with useful context;</li>



<li>testing that calculations and error cases behave correctly.</li>
</ul>



<p class="wp-block-paragraph">If all of that grows inside one file, the project quickly becomes difficult to understand and change. A package lets us separate those jobs into smaller parts that each have a clear purpose.</p>



<p class="wp-block-paragraph">For example:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">genome_toolkit/
├── sequence/
├── load/
└── algorithms/</pre></div>



<p class="wp-block-paragraph"><code>sequence/</code> can contain code for biological sequences such as DNA. <code>load/</code> can contain code for reading biological data from files. <code>algorithms/</code> can contain the scientific calculations themselves.</p>



<p class="wp-block-paragraph">This also makes Genome Toolkit much easier to reuse. Instead of treating it as a couple of Python files that belong to one project folder, we will be able to install it and use it like a normal Python library:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">import genome_toolkit</pre></div>



<p class="wp-block-paragraph">That means the same tested scientific code can later be used from another Python script, a Jupyter notebook, a larger research project, a web application, or another tool.</p>



<p class="wp-block-paragraph">For us, this is also a useful step from an educational project toward a real portfolio project. We are not only showing that we can write individual bioinformatics algorithms. We are showing that we can organize those algorithms into scientific software that other people can install, test, reuse, and expand.</p>



<p class="wp-block-paragraph">Our goal with this refactoring is to move from the following type of simple script output:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">[Genome Toolkit Initiated]

Sequence: AATTTTAAAAC
k-mer: AA
Repeats found: 4
Most frequent k-mer: ['TTT', 'AAA']</pre></div>



<p class="wp-block-paragraph">To a scientific results like these:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;application/x-json&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;JSON&quot;,&quot;language&quot;:&quot;JSON&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;json&quot;}">{
  &quot;metadata&quot;: {
    &quot;toolkit_version&quot;: &quot;0.1.0&quot;,
    &quot;algorithm&quot;: &quot;count_kmer&quot;,
    &quot;timestamp&quot;: &quot;2026-08-24T09:30:53.399181Z&quot;
  },
  &quot;inputs&quot;: {
    &quot;sequence&quot;: {
      &quot;identifier&quot;: &quot;M57671.1&quot;,
      &quot;description&quot;: &quot;Octodon degus insulin mRNA, complete cds&quot;,
      &quot;length&quot;: 126
    }
  },
  &quot;parameters&quot;: {
    &quot;kmer&quot;: &quot;CCTT&quot;
  },
  &quot;output&quot;: {
    &quot;count&quot;: 5
  }
}

{
  &quot;metadata&quot;: {
    &quot;toolkit_version&quot;: &quot;0.1.0&quot;,
    &quot;algorithm&quot;: &quot;find_most_frequent_kmers&quot;,
    &quot;timestamp&quot;: &quot;2026-08-24T09:30:53.400405Z&quot;
  },
  &quot;inputs&quot;: {
    &quot;sequence&quot;: {
      &quot;identifier&quot;: &quot;M57671.1&quot;,
      &quot;description&quot;: &quot;Octodon degus insulin mRNA, complete cds&quot;,
      &quot;length&quot;: 126
    }
  },
  &quot;parameters&quot;: {
    &quot;k_len&quot;: 5
  },
  &quot;output&quot;: {
    &quot;kmers&quot;: [
      &quot;CTTGG&quot;,
      &quot;TTGGG&quot;,
      &quot;TGGGC&quot;,
      &quot;GGGCC&quot;
    ],
    &quot;frequency&quot;: 6
  }
}</pre></div>



<h2 class="wp-block-heading">Preparing Genome Toolkit for APIs, MCP, and AI Agents</h2>



<p class="wp-block-paragraph">There is another reason this structure is becoming increasingly useful.</p>



<p class="wp-block-paragraph">Today, scientific software does not have to be used only by someone manually writing Python code. The same package can later be connected to a web interface, an API, or an AI agent.</p>



<p class="wp-block-paragraph">An <strong>API</strong>, or Application Programming Interface, gives one program a structured way to use another program. <strong>MCP</strong>, or Model Context Protocol, gives AI systems a standardized way to connect to external tools.</p>



<p class="wp-block-paragraph">We will explain both properly when they become relevant. For now, the important idea is simply that Genome Toolkit can become the tested scientific tool underneath those systems.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><a href="https://rebelscience.club/wp-content/uploads/2026/08/apu_mcp-2.png"><img decoding="async" width="1280" height="720" src="https://rebelscience.club/wp-content/uploads/2026/08/apu_mcp-2-1280x720.png" alt="" class="wp-image-2440" srcset="https://rebelscience.club/wp-content/uploads/2026/08/apu_mcp-2-1280x720.png 1280w, https://rebelscience.club/wp-content/uploads/2026/08/apu_mcp-2-512x288.png 512w, https://rebelscience.club/wp-content/uploads/2026/08/apu_mcp-2-768x432.png 768w, https://rebelscience.club/wp-content/uploads/2026/08/apu_mcp-2-1536x864.png 1536w, https://rebelscience.club/wp-content/uploads/2026/08/apu_mcp-2-24x14.png 24w, https://rebelscience.club/wp-content/uploads/2026/08/apu_mcp-2-36x20.png 36w, https://rebelscience.club/wp-content/uploads/2026/08/apu_mcp-2-48x27.png 48w, https://rebelscience.club/wp-content/uploads/2026/08/apu_mcp-2.png 1672w" sizes="(max-width: 1280px) 100vw, 1280px" /></a></figure>
</div>


<p class="wp-block-paragraph">Imagine asking an AI agent:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Find 100 genomes of this bacterium, run our k-mer analyses on them, compare the results, and prepare a summary.</p>
</blockquote>



<p class="wp-block-paragraph">Without dedicated scientific tools, the AI has to figure out much of that workflow by itself. It may search for genome sequences from different places, choose how to download them, write analysis code while it is working, pick libraries, and decide how to organize the calculations.</p>



<p class="wp-block-paragraph">That flexibility can be useful, but it creates a problem for science. Modern language models are <strong>non-deterministic</strong>, which means they can make different choices across separate runs. If the AI is also writing the algorithms and deciding where the data comes from every time, reproducing exactly the same experiment becomes much harder.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><a href="https://rebelscience.club/wp-content/uploads/2026/08/determ-1.png"><img decoding="async" width="1280" height="720" src="https://rebelscience.club/wp-content/uploads/2026/08/determ-1-1280x720.png" alt="" class="wp-image-2435" srcset="https://rebelscience.club/wp-content/uploads/2026/08/determ-1-1280x720.png 1280w, https://rebelscience.club/wp-content/uploads/2026/08/determ-1-512x288.png 512w, https://rebelscience.club/wp-content/uploads/2026/08/determ-1-768x432.png 768w, https://rebelscience.club/wp-content/uploads/2026/08/determ-1-1536x864.png 1536w, https://rebelscience.club/wp-content/uploads/2026/08/determ-1-24x14.png 24w, https://rebelscience.club/wp-content/uploads/2026/08/determ-1-36x20.png 36w, https://rebelscience.club/wp-content/uploads/2026/08/determ-1-48x27.png 48w, https://rebelscience.club/wp-content/uploads/2026/08/determ-1.png 1672w" sizes="(max-width: 1280px) 100vw, 1280px" /></a></figure>
</div>


<p class="wp-block-paragraph">Now imagine that the AI can use Genome Toolkit instead.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">AI agent
    ↓
Genome Toolkit tools
    ↓
tested scientific calculations
    ↓
structured results</pre></div>



<p class="wp-block-paragraph">The AI can still help organize the work, choose which tools to call, compare many results, and prepare a report. But the actual k-mer calculation can come from the same <code>count_kmer()</code> function that we already wrote, understand, and test.</p>



<p class="wp-block-paragraph">The same idea applies to loading biological data. Instead of inventing a new FASTA parser every time, the AI can use our loader. Instead of returning a number with no context, Genome Toolkit can later return a structured result that tells us which sequence, parameters, algorithm, and software version produced it.</p>



<p class="wp-block-paragraph">This is where <strong>reproducibility</strong> and <strong>provenance</strong> become important. Reproducibility means that we should be able to repeat the same scientific calculation using the same data, algorithm, parameters, and software version. Provenance means keeping enough information to understand where a result came from.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><a href="https://rebelscience.club/wp-content/uploads/2026/08/prov-1.png"><img decoding="async" width="1280" height="720" src="https://rebelscience.club/wp-content/uploads/2026/08/prov-1-1280x720.png" alt="" class="wp-image-2437" srcset="https://rebelscience.club/wp-content/uploads/2026/08/prov-1-1280x720.png 1280w, https://rebelscience.club/wp-content/uploads/2026/08/prov-1-512x288.png 512w, https://rebelscience.club/wp-content/uploads/2026/08/prov-1-768x432.png 768w, https://rebelscience.club/wp-content/uploads/2026/08/prov-1-1536x864.png 1536w, https://rebelscience.club/wp-content/uploads/2026/08/prov-1-24x14.png 24w, https://rebelscience.club/wp-content/uploads/2026/08/prov-1-36x20.png 36w, https://rebelscience.club/wp-content/uploads/2026/08/prov-1-48x27.png 48w, https://rebelscience.club/wp-content/uploads/2026/08/prov-1.png 1672w" sizes="(max-width: 1280px) 100vw, 1280px" /></a></figure>
</div>


<p class="wp-block-paragraph">We are not going to build all of that in this article. We will introduce each piece when we actually need it and can immediately see what problem it solves.</p>



<p class="wp-block-paragraph">For now, the high-level idea is enough:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">Python script / notebook / web app / AI agent
                    ↓
              Genome Toolkit
                    ↓
          tested scientific code
                    ↓
             scientific result</pre></div>



<p class="wp-block-paragraph">Genome Toolkit itself will stay focused on the science. Web servers, APIs, MCP connections, authentication, databases, and AI-agent logic can live outside the package and use Genome Toolkit as the scientific core underneath them.</p>



<h2 class="wp-block-heading">What We Will Build Next</h2>



<p class="wp-block-paragraph">Over the next few parts, we will gradually turn the same working project into a modern scientific Python package.</p>



<p class="wp-block-paragraph">At a high level, we will:</p>



<ul class="wp-block-list">
<li>modernize the project with <code>uv</code> and a proper Python package structure;</li>



<li>add validated biological sequence objects such as <code>DNA</code>;</li>



<li>load biological data from plain-text and FASTA files;</li>



<li>return more useful scientific results;</li>



<li>add automated tests and clearer error behavior.</li>
</ul>



<p class="wp-block-paragraph">We do not need to understand all of those pieces yet. We will introduce them one at a time, when we actually build and use them.</p>



<p class="wp-block-paragraph">The important thing to remember is where we are going:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;Python&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">biological data
      ↓
Genome Toolkit
      ↓
tested algorithms
      ↓
scientific results</pre></div>



<p class="wp-block-paragraph">Everything else we add is there to make that workflow easier to use, easier to trust, and easier to expand.</p>



<h2 class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">Our original Genome Toolkit project was exactly what we needed for learning our first algorithms. Now we want to grow it into a scientific Python package that can handle biological sequences, external data, more algorithms, better results, and automated testing without becoming difficult to maintain.</p>



<p class="wp-block-paragraph">The scientific goal stays the same. We are still building a practical bioinformatics toolkit, and our existing k-mer algorithms remain the starting point.</p>



<p class="wp-block-paragraph">The new package structure will also make Genome Toolkit much easier to reuse from other Python projects, notebooks, future web applications, APIs, MCP tools, and AI agents. Most importantly, it gives us a cleaner foundation for building scientific workflows that are easier to test, understand, and reproduce.</p>



<h3 class="wp-block-heading">What is Next?</h3>



<p class="wp-block-paragraph">In <strong>Genome Toolkit Part 4.2</strong>, we will finally start changing the project.</p>



<p class="wp-block-paragraph">We will begin by running our current <code>application.py</code> one more time and confirming the familiar output. Then we will modernize the same project with <code>uv</code>, create a proper Python package structure, move our existing k-mer algorithms into it, and run <code>application.py</code> again.</p>



<p class="wp-block-paragraph">That gives us a very simple first goal: change how Genome Toolkit is organized while keeping the scientific calculations working.</p>



<p class="wp-block-paragraph">From there, we will continue one useful step at a time.</p>



<p class="wp-block-paragraph">I hope this introduction to building our scientific Python package was useful for your bioinformatics and programming journey! If you found this article valuable and want to help us continue building rebelScience, please consider supporting our project. You can explore various ways to contribute <a href="https://rebelscience.club/cryptocurrency-donations/">here</a>.</p>



<p class="wp-block-paragraph">Until next time, rebelCoder, signing out.</p>



<p class="wp-block-paragraph">A video version of this article:</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Genome Toolkit. Part 4.1: Building a Scientific Python Package" width="640" height="360" src="https://www.youtube.com/embed/tkaVS_LCfpo?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>



<p class="wp-block-paragraph"></p>
]]></content:encoded></item></channel></rss>