voice-clone/
├── src/
│ ├── voice_clone/ # ← Main Python package
│ │ ├── __init__.py
│ │ ├── cli.py
│ │ ├── audio/
│ │ ├── model/
│ │ └── utils/
│ └── voice_clone_cli.egg-info/ # ← Auto-generated metadata (gitignored)
├── tests/
├── data/
├── config/
└── pyproject.toml
This project uses the "src layout" pattern, which is a Python packaging best practice.
Flat layout (NOT recommended):
project/
├── voice_clone/
│ ├── __init__.py
│ └── cli.py
├── tests/
└── setup.py
Issues:
- ❌ Can import code without installing it
- ❌ Tests might pass locally but fail after installation
- ❌ Confusing namespace (is it installed or local?)
- ❌ Hard to distinguish package code from project files
src layout (RECOMMENDED):
project/
├── src/
│ └── voice_clone/
│ ├── __init__.py
│ └── cli.py
├── tests/
└── pyproject.toml
Benefits:
- ✅ Forces proper installation: Can't import without
pip install -e . - ✅ Tests are realistic: Tests run against installed package
- ✅ Clear separation: Package code is isolated in
src/ - ✅ Correct namespace: Import as
voice_clone, notsrc.voice_clone - ✅ Industry standard: Used by major Python projects
# Install in development mode
pip install -e .
# This creates:
# - src/voice_clone_cli.egg-info/ (metadata)
# - Links package to site-packages# After installation, you can import:
from voice_clone.cli import cli
from voice_clone.audio.processor import AudioProcessor
# NOT:
from src.voice_clone.cli import cli # ❌ Wrong!In pyproject.toml:
[project]
name = "voice-clone-cli" # Package name on PyPI
[project.scripts]
voice-clone = "voice_clone.cli:cli" # CLI entry pointThe package name (voice-clone-cli) is different from the import name (voice_clone):
- Package name: Used for
pip install voice-clone-cli - Import name: Used for
import voice_clone
This directory contains package metadata generated during installation.
voice_clone_cli.egg-info/
├── PKG-INFO # Package information (name, version, author)
├── SOURCES.txt # List of all package files
├── dependency_links.txt # External dependency links
├── entry_points.txt # CLI commands (voice-clone)
└── top_level.txt # Top-level modules (voice_clone)
Generated automatically when you run:
pip install -e . # Development mode
pip install . # Regular install
python -m build # Build packageNO! ❌
This directory should be in .gitignore because:
- It's auto-generated (like
__pycache__) - It's specific to your local installation
- It changes every time you install
- Other developers will generate their own
Our .gitignore already covers it:
*.egg-info/pip install voice-clone-cli- Defined in
pyproject.tomlasname = "voice-clone-cli" - Used for distribution (PyPI, pip)
- Can contain hyphens
import voice_clone- Defined by directory name:
src/voice_clone/ - Used in Python code
- Must be valid Python identifier (no hyphens)
voice-clone --help- Defined in
pyproject.tomlasvoice-clone = "voice_clone.cli:cli" - Can contain hyphens
- Maps to Python function
voice-clone/
├── src/
│ └── voice_clone/ # Package code
│ ├── __init__.py
│ ├── cli.py
│ ├── audio/
│ │ ├── __init__.py
│ │ ├── processor.py
│ │ └── validator.py
│ ├── model/
│ │ ├── __init__.py
│ │ ├── manager.py
│ │ └── generator.py
│ └── utils/
│ ├── __init__.py
│ ├── logger.py
│ └── helpers.py
├── tests/ # Test code
│ ├── test_audio.py
│ ├── test_model.py
│ └── test_cli.py
├── data/ # Data files (gitignored)
├── config/ # Configuration
├── scripts/ # Utility scripts
├── docs/ # Documentation
└── pyproject.toml # Package configuration
voice-clone/
├── voice_clone/ # Package at root (flat layout)
│ ├── __init__.py
│ └── cli.py
├── test_audio.py # Tests mixed with code
├── data/ # Data at root
└── setup.py
A: Because you need a package (directory with __init__.py) to:
- Organize code into modules
- Create proper namespace
- Support subpackages (audio/, model/, utils/)
- Enable relative imports
A: Yes! For example:
src/
├── voice_clone/ # Main package
│ └── ...
└── voice_clone_tools/ # Additional package
└── ...
A: You need to:
- Rename
src/voice_clone/tosrc/new_name/ - Update
pyproject.toml:[project.scripts] voice-clone = "new_name.cli:cli"
- Update all imports in code
- Reinstall:
pip install -e .
A: After pip install -e .:
# Should work:
python -c "import voice_clone; print(voice_clone.__file__)"
# Output: /path/to/src/voice_clone/__init__.py
# Should work:
voice-clone --help
# Should NOT work (if it does, structure is wrong):
python -c "import src.voice_clone" # Should fail- Python Packaging User Guide - src layout
- Setuptools - Package Discovery
- PEP 517 - Build System
- PEP 518 - pyproject.toml
| Aspect | Value | Purpose |
|---|---|---|
| Package directory | src/voice_clone/ |
Contains Python code |
| Package name | voice-clone-cli |
For pip install |
| Import name | voice_clone |
For Python imports |
| CLI command | voice-clone |
For terminal |
| Metadata dir | voice_clone_cli.egg-info/ |
Auto-generated, gitignored |
| Layout pattern | src layout | Best practice |
The structure is correct and follows Python packaging best practices! 🎉