Complete guide for developers setting up a local development environment and contributing to NullifyPDF.
Important
Requires Python 3.13. Older versions are not compatible with PyMuPDF wheels.
Before starting, verify you have:
| 📦 Requirement | 💾 Space | 📝 Notes |
|---|---|---|
| Python 3.13 | 150 MB | Download |
| Git | 50 MB | Download |
| Virtual Environment | 2 GB | .venv/ auto-created by setup script |
| Disk Space | 3 GB | Dependencies + spaCy models |
| RAM | 4 GB | 8 GB recommended |
git clone https://github.com/overwrite00/NullifyPDF.git
cd NullifyPDF# Windows
py -3.13 --version
# macOS/Linux
python3.13 --versionShould output: Python 3.13.x
python setup_env.pyThis automatically:
- ✅ Creates
.venv/virtual environment - ✅ Installs dependencies
- ✅ Downloads spaCy models (EN + IT)
🪟 Windows (PowerShell)
.\.venv\Scripts\Activate.ps1If blocked by execution policy:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
.\.venv\Scripts\Activate.ps1🍎 macOS (Bash/Zsh)
source .venv/bin/activate🐧 Linux (Bash)
source .venv/bin/activateYour prompt should now show: (.venv) $
python NullifyPDF.pyIf the GUI opens → You're ready to develop! 🎉
NullifyPDF/
|-- NullifyPDF.py # Main PySide6 application
|-- privacy_core.py # Privacy modes and encrypted restore maps
|-- PDF_Checker.py # Heuristic verification utility
|-- setup_env.py # Environment setup script
|-- build_local.py # Lite/Full PyInstaller build script
|-- requirements.txt # Python dependencies
|-- scripts/
| `-- download_ocr_data.py # EN/IT OCR data downloader for Full builds
|-- tests/
| |-- test_validation.py
| |-- test_privacy_core.py
| `-- test_build_config.py
|-- images/
| `-- NullifyPDF.png
`-- .github/
`-- workflows/
|-- test_build.yml
|-- beta-release.yml
`-- release.yml
| Package | Version | Purpose |
|---|---|---|
| PySide6 | 6.11.1 | GUI framework (Qt6 bindings) |
| PyMuPDF | 1.28.0 | PDF manipulation and OCR bridge |
| presidio-analyzer | 2.2.364 | PII detection |
| spaCy | 3.8.14 | NLP for entity recognition |
| cryptography | 49.0.0 | Encrypted restore maps |
| pytest | 9.1.1 | Testing framework |
| Model | Size | Purpose |
|---|---|---|
en_core_web_md |
40 MB | English NER |
it_core_news_md |
45 MB | Italian NER |
These are downloaded automatically by setup_env.py.
Class Hierarchy:
NullifyPDF(QMainWindow)
|-- PDFListManager # Manages blocklist/allowlist
|-- AIWorker(QObject) # NLP/OCR scanning in thread
|-- PDFView(QGraphicsView) # PDF rendering and rectangle drawing
|-- privacy_core.py # Privacy modes and encrypted restore maps
`-- UI Components
|-- Sidebar
|-- Toolbar
|-- Progress bar
`-- DialogsKey Methods:
class NullifyPDF:
def __init__(self) # Initialize GUI
def load_path(self, path: str) # Load PDF file
def cmd_auto_ai(self) # Start AI/OCR scan
def apply_ai_to_page(self, i, data) # Receive AI results
def cmd_export(self) # Export privacy PDF
def user_draw_rect(self, rect) # Draw manual redactionSingle Responsibility:
- UI Thread — Rendering, events, dialog management
- AIWorker Thread — Text extraction, NLP analysis
Synchronization:
with QMutexLocker(self.mutex):
text = pdf_doc.get_text() # Safe access to PDF
# (no lock needed for NLP)See ARCHITECTURE.md for detailed system design.
pytest tests/ -vpytest tests/test_validation.py::TestPDFListManager::test_save_and_load_blocklist -vpytest tests/ --cov=. --cov-report=htmlOpens htmlcov/index.html in browser.
- ✅ PDFListManager — File I/O, persistence
- ✅ OCR Config — Tesseract language selection and tessdata discovery
- ✅ Privacy Core — Placeholder mapping and encrypted restore maps
- ✅ Build Config — Lite/Full build variant behavior
- ✅ Resource Paths — PyInstaller compatibility
python build_local.py --lite
python build_local.py --fullOutput: dist/NullifyPDF_vX.Y.Z_Windows_Lite.exe or dist/NullifyPDF_vX.Y.Z_Windows_Full.exe (on Windows)
- Cleans
build/anddist/directories - Detects your OS (Windows/macOS/Linux)
- Reads version from
NullifyPDF.py(__version__) - Compiles with PyInstaller
- Renames with version and variant:
NullifyPDF_v{VERSION}_{OS}_{Lite|Full}.exe
| OS | Output |
|---|---|
| Windows | Lite/Full .exe executables |
| macOS | Lite/Full .app bundle ZIPs |
| Linux | Lite/Full binary + .deb + .rpm packages |
Build fails on Windows with "RecursionError"
Cause: spaCy models too large for default recursion.
Fix: Already handled in .spec file. If issue persists:
# In build_local.py
import sys
sys.setrecursionlimit(5000)Build succeeds but executable won't run
- Check antivirus isn't blocking
- Run in debug mode:
NullifyPDF_vX.Y.Z_Windows_Lite.exeorNullifyPDF_vX.Y.Z_Windows_Full.exefrom PowerShell - Check
.stdoutfile if created - Report on GitHub
# 1. Update develop
git fetch origin
git checkout develop
git pull origin develop
# 2. Create feature branch
git checkout -b feature/my-feature
# 3. Make changes
# ... edit code ...
# 4. Test
pytest tests/ -v
python build_local.py --lite
# 5. Commit with clear message
git commit -m "feat(ai): add IBAN detection"
# 6. Push and open PR
git push origin feature/my-featuretype(scope): description
Optional longer explanation
Types: feat, fix, docs, refactor, test, chore, perf, ci
Examples:
feat(ai): add cryptocurrency address detection
fix(export): reduce memory usage in privacy export
docs: update installation guide
perf(allowlist): implement O(1) fast-path lookupSee CONTRIBUTING.md for details.
# Windows
$env:NULLIFYPDF_DEBUG = "true"
python NullifyPDF.py
# macOS/Linux
export NULLIFYPDF_DEBUG=true
python3.13 NullifyPDF.pyEffect: Logs verbose output to ~/.nullifypdf/logs/nullifypdf.log
Use logging, not print():
import logging
logger = logging.getLogger("nullifypdf")
logger.debug(f"Variable: {value}")
logger.info(f"Action completed: {result}")
logger.error(f"Error occurred: {exception}")Use Python debugger:
import pdb
pdb.set_trace() # Execution pauses hereThen in console:
l— List current linen— Next lines— Step into functionc— Continuep var— Print variable
100% of functions must have type hints:
# ✅ GOOD
def extract_text(pdf_path: str, page: int) -> str:
"""Extract text from page."""
...
# ❌ BAD
def extract_text(pdf_path, page):
"""Extract text from page."""
...def redact_entity(text: str, entity: str) -> str:
"""Replace entity with redaction marker.
Args:
text: Input text containing entity
entity: Entity to redact
Returns:
Text with entity replaced by [REDACTED]
Raises:
ValueError: If entity is empty
"""Use isort for automatic sorting:
pip install isort
isort NullifyPDF.py| File | Purpose |
|---|---|
NullifyPDF.py |
Main app, GUI, OCR, export logic |
privacy_core.py |
Placeholder and restore-map logic |
setup_env.py |
Environment setup |
build_local.py |
PyInstaller Lite/Full build |
PDF_Checker.py |
Post-processing utility |
requirements.txt |
Dependencies |
tests/ |
Unit and smoke tests |
| Feature | File | Method |
|---|---|---|
| Load PDF | NullifyPDF.py |
load_path() |
| Auto Redact | NullifyPDF.py |
cmd_auto_ai() |
| AI Processing | NullifyPDF.py |
AIWorker.run_scan() |
| Export | NullifyPDF.py |
cmd_export() |
| Blocklist/Allowlist | NullifyPDF.py |
PDFListManager |
Always validate file paths:
# ✅ GOOD — Validate before use
path = pathlib.Path(user_input).resolve()
if not path.parent.exists():
raise ValueError(f"Directory not found: {path.parent}")
# ❌ BAD — Direct user input
with open(user_input) as f:
...Never hardcode API keys or passwords.
- Avoid unbounded memory growth on large PDFs
- Keep long-running work off the UI thread
- Clean up temp files
| Resource | Link | Purpose |
|---|---|---|
| PyMuPDF | pymupdf.io | PDF API |
| PySide6 | doc.qt.io/qtforpython | GUI framework |
| spaCy | spacy.io | NLP models |
| Presidio | microsoft.github.io/presidio | PII detection |
| Python | python.org | Language reference |
A:
- Create feature branch:
git checkout -b feature/my-feature - Edit code following code standards
- Add tests:
pytest tests/test_my_feature.py - Run full test suite:
pytest tests/ -v - Build locally:
python build_local.py --lite - Commit and push
A:
- GitHub Actions runs tests on all 3 OS automatically
- Or use virtual machine (VirtualBox, Parallels) for local testing
A:
In AIWorker.run_scan():
- Use Presidio analyzer for regex patterns
- Use spaCy models for entity recognition
- Merge and deduplicate results
- Filter through allowlist
See ARCHITECTURE.md for AI pipeline details.
A:
pip install py-spy
# Profile running app
py-spy record -o profile.svg -- python NullifyPDF.py
# Analyze
py-spy top -- python NullifyPDF.pyReady to contribute? See CONTRIBUTING.md for:
- PR workflow
- Code review process
- Issue templates
- Commit message standards
- 📖 User Guide: USER_GUIDE.md
- 🐛 Troubleshooting: TROUBLESHOOTING.md
- 🏗️ Architecture: ARCHITECTURE.md
- 🤝 Contributing: CONTRIBUTING.md
- 💬 GitHub Discussions: Discussions
- 🐛 GitHub Issues: Issues
Last updated: 2026-07-29
← Troubleshooting | Back to README →