We've all had that moment of typing a filename into search and getting nothing back, because the file you're looking for is buried three folders deep and you only remember one sentence from inside it, not what you called it. That's the problem we set out to solve with Flora Find,and we decided to build the whole thing using nothing but the Python Standard Library.
1. The Problem
Finding a file by name is easy. Finding a file by what's actually written inside it is not, at least not without some kind of search index sitting underneath your file system.
Most operating systems offer some form of built-in search, but it's often slow, inconsistent across platforms, or limited to filenames and basic metadata rather than the actual content of a document. Doing a proper content search yourself usually means reaching for a search library, a web framework to expose it, and maybe a database ORM to store the index — three or four dependencies before you've written a single line of your own logic.
That's fine for a production application. But for a hackathon built around the constraint of zero third-party dependencies, none of that was on the table. We had to figure out how much of a real search engine we could build using only what ships with Python itself.
2. Our Project:Flora Find
Flora Find is a local, private, full-text search engine for your own files. It scans a directory (by default, your home folder), extracts readable text from supported file types, builds a searchable index, and lets you search that index through either a web interface or a terminal interface.
Everything runs on your own machine. Nothing is uploaded anywhere,the web server only listens on 127.0.0.1, so it's not reachable from outside your computer at all.
What it actually does:
-
Scans your files and skips hidden and system directories automatically, so it doesn't waste time indexing things like
.gitfolders or OS-internal folders. -
Extracts text from
.txt,.py,.md,.csv,.json,.html/.htm,.xml,.log, and.docxfiles. - Builds a persistent index so you don't have to re-scan everything every time — only new or modified files get re-processed on subsequent runs.
- Ranks results with TF-IDF, so documents that match more of your search terms, and match them more meaningfully, rise to the top.
- Shows highlighted snippets of where your search terms actually appear in each result, instead of just a filename.
- Lets you filter by file type and open a result directly in its default application, right from the search results.
- Works two ways — a web UI that opens automatically in your browser, and a terminal menu for anyone who prefers the command line.
3. How to Use Flora Find
The basic workflow is intentionally simple:
- Run
python main.pyto launch the web UI (orpython main.py --clifor the terminal version). - On first use, click "Index / Scan Files" to build the search index — this reads through your files once and stores what it finds.
- Type a search query into the search bar.
- Flora Find tokenizes your query, looks up matching documents in the index, and ranks them by relevance.
- Results appear with highlighted snippets showing where your terms matched.
- Click a result to open the file directly in its default application.
On future runs, indexing is incremental,only files that are new or have changed since the last scan get re-processed, so it's fast after the first pass.
4. The Zero-Dependency Challenge
This was the core constraint of the whole project, and honestly the part that shaped the most decisions.
Normally, building something like this, we'd have reached for a web framework to serve the UI, a proper search or ranking library to handle relevance scoring, and possibly a library to parse .docx files, since Word's format isn't plain text. Zero dependency meant none of that was an option-everything had to come from Python's own standard library.
| Normally Used | Our Approach |
|---|---|
| Flask / FastAPI (web framework) |
http.server's ThreadingHTTPServer and BaseHTTPRequestHandler, handling routing and JSON responses manually |
python-docx (Word file parsing) |
zipfile to unzip the .docx, then xml.etree.ElementTree to parse word/document.xml directly, since a .docx is really just a zipped XML document |
scikit-learn / nltk (search ranking) |
A hand-written TF-IDF implementation using math.log, with our own tokenizer and stopword filtering built on re
|
BeautifulSoup (HTML parsing) |
html.parser, subclassed into a small text-extractor that strips tags itself |
| SQLAlchemy or another ORM | Raw sqlite3, writing our own schema and queries for the inverted index |
watchdog (file change detection) |
Comparing stored modification timestamps against the file system on every scan, using os and pathlib
|
The hardest part wasn't any single replacement — it was realizing how much invisible work these libraries normally do for you. Writing our own TF-IDF scoring meant actually understanding the formula, not just calling .fit_transform(). Parsing .docx by hand meant learning that it's a ZIP archive with XML inside, something we'd genuinely never thought about before.
What we took away from it is a much clearer sense of what these libraries are actually doing under the hood,which made the "zero dependency" constraint feel less like a limitation and more like a genuinely useful exercise in understanding our own tools.
5. Challenges and Bugs We Faced
Challenge 1 : The Dependency Checker Flagged Everything as Missing
Problem:
We built a script called dependency_proof.py to verify, programmatically, that Flora Find really doesn't import anything outside the standard library. The first time we ran it, it reported that every single file in the project was unclassified — it didn't even recognize our own modules like scanner or indexer as part of the project.
Root Cause:
The script excludes certain folders from its scan, like __pycache__ and .git, by checking each file's path against a list of excluded names. The bug was that it checked the file's full absolute path rather than the path relative to the project root. We happened to be running it from inside a folder that had "build" in its name, and "build" was on the exclusion list — so that outer folder name matched, and the check silently skipped every file inside it before ever looking at what was in scanner/ or indexer/.
Fix:
We changed the check to only look at path components relative to the project root, so exclusion rules apply to folders that are actually part of the project structure, not to whatever directory happens to contain it.
What We Learned:
This kind of bug is sneaky precisely because it doesn't crash — it just quietly produces a wrong, misleadingly clean result. It taught us to test path-handling logic from more than one working directory, not just wherever we happened to be developing at the time.
Challenge 2:Our "Unchanged File" Test Kept Failing
Problem:
We wrote a test to confirm that re-running the indexer on files that hadn't changed would skip them instead of re-indexing them. The test kept failing — the indexer treated the same files as "modified" on every run, even when nothing about them had actually changed.
Root Cause:
The test used a fake version of our file scanner so it wouldn't need to touch real files. The problem was that this fake scanner rewrote the test files to disk every time it was called, including on the second indexing run. Rewriting a file updates its modification timestamp, so from the indexer's point of view, the file genuinely looked new each time — even though the content hadn't changed at all.
Fix:
We changed the fake scanner to write the test files to disk once, up front, and then just read their existing timestamp on every later call instead of rewriting them.
What We Learned:
A test double needs to accurately mimic the real thing it's standing in for — not just produce the right-looking output. A fake that's "too eager" can quietly introduce a bug that has nothing to do with the actual code being tested.
[Insert screenshot: error/bug]
[Insert screenshot: corrected implementation]
6. What We Learned
Beyond the specific bugs, the project taught us a few broader things:
-
The standard library is more capable than it gets credit for. Between
sqlite3,http.server, andhtml.parser, we covered ground we assumed would need external packages. - Designing your own modules forces clearer boundaries. Splitting the project into a scanner, an indexer, a search engine, and a thin controller layer made it much easier to test each piece in isolation.
- Debugging path and timestamp issues requires patience. Both of our major bugs came from subtle, silent failures rather than crashes — a good reminder to actually verify assumptions instead of trusting that "no error" means "correct."
- Integrating each other's work went more smoothly once interfaces were clear. Once the controller layer was in place, whoever was working on the web UI or the CLI didn't need to know how indexing worked internally.
- Dependencies are a design decision, not just a convenience. Being forced to avoid them made us think harder about what we actually needed versus what we were reaching for out of habit.
7. Why Zero Dependency Matters
We don't think "zero dependencies" is automatically better software — plenty of well-built, secure, maintainable projects rely heavily on external libraries, for good reason. But going through this constraint made a few practical benefits clear:
-
Simpler setup.
pip install -r requirements.txtinstalls nothing, so there's no version-resolution step that can fail on someone else's machine. - Easier reproducibility. Anyone with Python 3.8+ can run the project exactly as we built it, with no risk of a dependency being unavailable or behaving differently across versions.
- No dependency or version conflicts. There's simply nothing to conflict with.
- A clearer picture of what your own code is actually doing, since there's no library quietly handling something behind the scenes.
It's a trade-off, not a free win — we spent real time writing things a library would normally hand us. But for a project of this size, that trade felt worth it.
8. Takeaway:
Building Flora Find under a zero-dependency constraint didn't just limit what tools we could reach for — it changed how we thought about the problem. Instead of assembling libraries, we had to actually understand the mechanics of what we were building: how TF-IDF scoring works, how a .docx file is structured, how a basic web server handles a request. That's the part of this hackathon we'll probably remember longest.
9. Try Flora Find
- GitHub repository: https://github.com/pthanmayee908/local_search_engine
- Demo video: https://youtu.be/Z101TAMIH8E
To try it yourself: clone the repository, run
python main.py, click "Index / Scan Files" once, and start searching. No installation step beyond having Python 3.8 or newer=see the project'sBUILD.mdfor full setup details.


Top comments (0)