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
Cogfor 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
loggingmodule:- 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
raiseto 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.txtfor production dependencies andrequirements_dev.txtfor development dependencies:
# Install production dependencies
pip install -r requirements.txt
# Install development dependencies
pip install -r requirements_dev.txtrequirements.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__.pyfile. 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.