Maintain a clear, modular folder layout:
capy/
├── src/
│ ├── capy_backend/
│ │ ├── db/ # Database models and connection utilities
│ │ ├── mods/ # Additional modules for extended functionalities
│ │ ├── res/ # Static resources like images, JSON files, etc.
│ │ ├── utils/ # Utility/helper functions
│ ├── capy_discord/
│ │ ├── cogs/ # Separate modules for bot commands/features
│ │ ├── bot.py
│ ├── config.py # Configuration constants
│ └── main.py # Entry point of the bot
├── tests/ # Unit and integration tests
│ ├── capy_backend/
│ │ ├── db/ # Database models and connection utilities
│ │ ├── mods/ # Additional modules for extended functionalities
│ │ ├── res/ # Static resources like images, JSON files, etc.
│ │ ├── utils/ # Utility/helper functions
│ ├── capy_discord/
│ │ ├── cogs/ # Separate modules for bot commands/features
│ │ ├── bot.py
├── .env # Environment variables and secrets
├── requirements.txt # Python dependencies
├── requirements_dev.txt # Development dependencies
├── style.md # Coding style guide
├── test.md # Testing script
└── updater.py # Script for updating host
- Use a class-based design for modularity:
- Define a
Cog
for each bot feature (e.g., moderation, profiles). - Separate utility methods by category into individual files for clarity.
- Define a
- Use snake_case for variables and functions:
def get_user_profile(user_id: str) -> dict:
...
- Use PascalCase for class names:
class UserProfile:
...
- Use lowercase_with_underscores for file names (e.g.,
user_profiles.py
).
- Provide concise, meaningful inline comments above complex code:
# Fetch the user profile from the database
user_profile = db.get_user(user_id)
- Write docstrings for all public functions, methods, and classes. Follow the Google style for docstrings:
def fetch_user_data(user_id: str) -> dict:
"""
Fetch user data from the database.
Args:
user_id (str): The unique ID of the user.
Returns:
dict: The user profile data.
"""
- Include a brief description at the top of each file:
"""
This module handles MongoDB connection and operations
for user profiles.
"""
- Prioritize clarity over brevity:
# Good
for user in user_list:
if user.is_active():
active_users.append(user)
# Bad
active_users = [u for u in user_list if u.is_active()]
- Use descriptive collection and field names:
- Collection names: snake_case plural (e.g.,
user_profiles
). - Field names: camelCase (to match MongoDB standards).
- Collection names: snake_case plural (e.g.,
{
"_id": "123456",
"userName": "johndoe",
"profileSettings": {
"theme": "dark",
"notificationsEnabled": true
}
}
- Use Python's
logging
module:- Log at appropriate levels (
DEBUG
,INFO
,WARNING
,ERROR
,CRITICAL
). - Include timestamps and context:
- Log at appropriate levels (
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
- Use guard clauses to simplify complex conditional logic:
def process_user(user):
if not user.is_active():
return
# Process active user
...
- As compared to:
def process_user(user):
if user.is_active():
# Process active user
...
- Use helper methods to break down complex algorithms into smaller, manageable pieces, especially for methods with many tab scopes:
def complex_algorithm(data):
result = step_one(data)
result = step_two(result)
return step_three(result)
def step_one(data):
# Perform first step
...
def step_two(data):
# Perform second step
...
def step_three(data):
# Perform third step
...
- Catch specific exceptions, log meaningful messages, and use
raise
to propagate errors:
try:
db.connect()
except ConnectionError as e:
logging.error(f"Database connection failed: {e}")
raise
- Example:
def fetch_user_data(user_id: str) -> dict:
try:
user_data = db.get_user(user_id)
if not user_data:
raise ValueError(f"No user found with ID: {user_id}")
return user_data
except Exception as e:
logging.error(f"Error fetching user data: {e}")
raise
- Database should only be called from the context of the bot (e.g.,
self.bot.db
). Never instantiate another db, and db should not be used for utility methods:
class UserProfileCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.command()
async def get_profile(self, ctx, user_id: str):
user_profile = self.bot.db.get_user(user_id)
await ctx.send(user_profile)
- Use a virtual environment to manage dependencies:
python -m venv venv
source venv/bin/activate # On Windows use `venv\Scripts\activate`
- Use
requirements.txt
for production dependencies andrequirements_dev.txt
for development dependencies:
# Install production dependencies
pip install -r requirements.txt
# Install development dependencies
pip install -r requirements_dev.txt
requirements.txt
: Contains the dependencies required for the application to run in production.requirements_dev.txt
: Contains additional dependencies required for development, such as testing and linting tools.
Example requirements.txt
:
discord.py
pymongo
Example requirements_dev.txt
:
pytest
flake8
black
mypy
- Minimize the use of
from library import a, b, c, d
. Instead, import the entire module and use it with the module name to avoid namespace conflicts:
# Good
import os
import sys
# Bad
from os import path, mkdir, remove
from sys import argv, exit
- Follow the standard import order: standard library imports, third-party imports, and local imports. Separate each group with a blank line:
# Standard library imports
import os
import sys
# Third-party imports
import requests
# Local imports
from .utils import helper_function
- When adding new scripts to a module, ensure that the module's directory contains an
__init__.py
file. This file can be empty or used to initialize the module.
Before pushing your code to the repository, ensure the following steps are completed:
- Use black to automatically format your code files:
# Install black if not already installed
pip install black
# Format your code files
black path/to/your/code/file.py
- Use flake8 to check for style guide enforcement and code quality issues in your code files:
# Install flake8 if not already installed
pip install flake8
# Run flake8 on your code files
flake8 path/to/your/code/file.py
- Use mypy for static type checking in your code files:
# Install mypy if not already installed
pip install mypy
# Run mypy on your code files
mypy path/to/your/code/file.py
- Use pytest for running unit tests:
# Install pytest if not already installed
pip install pytest
# Run all tests
pytest
- Ensure all tests pass and code is properly formatted and linted before pushing:
# Add changes to the staging area
git add .
# Commit changes with a descriptive message
git commit -m "[Description of changes]"
# Push changes to the repository
git push origin [branch-name]
- Use descriptive commit messages:
[Feature] Add user profile creation functionality
[Fix] Resolve MongoDB connection issue
- Include a clear description of changes.
- Link to relevant issues.
- Request at least one reviewer for approval.