6/16/2025
These rules focus on Trio code in Python. They cover code quality, like using `flake8-async` and following PEP 8. For code structure, it details directory layout, module organization, and component architecture. Also includes design patterns, anti - patterns, performance, security, testing, and tooling guidance.
## Code Organization and Structure
- **Directory Structure:**
- A typical project structure might look like this:
my_project/
├── src/
│ ├── __init__.py
│ ├── main.py # Entry point
│ ├── utils.py # Utility functions
│ ├── services/
│ │ ├── __init__.py
│ │ ├── http_service.py
│ │ └── db_service.py
│ └── models/
│ ├── __init__.py
│ └── user.py
├── tests/
│ ├── __init__.py
│ ├── test_main.py
│ ├── test_utils.py
│ ├── services/
│ │ ├── test_http_service.py
│ │ └── test_db_service.py
│ └── models/
│ └── test_user.py
├── README.md
├── pyproject.toml # Project configuration (poetry, pipenv)
└── .gitignore
- Use a `src` directory to hold the main application code. This helps separate application code from configuration and documentation.
- Keep tests in a separate `tests` directory, mirroring the structure of `src`.
- **File Naming Conventions:**
- Use lowercase names for files and modules (e.g., `http_service.py`, `utils.py`).
- Test files should be prefixed with `test_` (e.g., `test_http_service.py`).
- **Module Organization:**
- Organize code into logical modules based on functionality (e.g., `services`, `models`, `utils`).
- Use `__init__.py` files to make directories importable as packages.
- Avoid circular dependencies between modules.
- **Component Architecture:**
- Consider using a layered architecture (e.g., presentation, business logic, data access) to separate concerns.
- Dependency Injection: Use dependency injection to make components more testable and reusable.
- **Code Splitting:**
- Break down large modules into smaller, more manageable files.
- Consider splitting modules based on functionality or responsibilities.
## Common Patterns and Anti-patterns
- **Design Patterns:**
- **Asynchronous Factory:** Use factories to create asynchronous resources (e.g., connections) to manage initialization efficiently.
python
async def create_db_connection():
conn = await connect_to_db()
return conn
async def main():
conn = await create_db_connection()
# Use the connection
- **Resource Pooling:** Implement resource pooling for database connections or network connections to reduce overhead.
- **Recommended Approaches:**
- Use nurseries for structured concurrency.
- Use streams (`trio.Stream`) for communication between tasks.
- Use channels (`trio.QueueChannel`) for passing data between tasks.
- **Anti-patterns:**
- **Sleeping without Cancellation:** Avoid using `time.sleep()` directly, as it blocks the event loop and ignores cancellation. Use `await trio.sleep()` instead.
- **Long-Running Synchronous Operations:** Don't perform CPU-bound operations directly in async functions. Offload them to separate threads or processes.
- **Ignoring Cancellation:** Ensure that your async functions handle cancellation requests (`trio.Cancelled`).
- **Unstructured Concurrency:** Avoid spawning tasks without proper management (e.g., without using nurseries). This can lead to orphaned tasks and difficult debugging.
- **State Management:**
- Immutable Data: Prefer immutable data structures to avoid race conditions and simplify reasoning about state.
- Task-Local Storage: Use `trio.TaskLocal` to store task-specific data.
- Thread-Safe Data Structures: If shared mutable state is necessary, use thread-safe data structures (e.g., `trio.Lock`, `trio.Semaphore`).
- **Error Handling:**
- Use `try...except` blocks to handle exceptions within async functions.
- Propagate exceptions appropriately to the nursery for proper error handling.
- Consider using exception groups to handle multiple exceptions that occur concurrently.
## Performance Considerations
- **Optimization Techniques:**
- Minimize context switching: Reduce unnecessary `await` calls.
- Use efficient data structures: Choose appropriate data structures for your specific use case.
- Avoid excessive copying: Use views or iterators when possible to avoid copying large data structures.
- **Memory Management:**
- Release resources promptly: Use `with` statements or `try...finally` blocks to ensure that resources are released even if exceptions occur.
- Avoid circular references: Be mindful of potential circular references, which can prevent garbage collection.
## Security Best Practices
- **Common Vulnerabilities:**
- **Race Conditions:** Be aware of potential race conditions when accessing shared mutable state.
- **Cancellation Errors:** Improper cancellation handling can lead to resource leaks or incorrect program behavior.
- **Input Validation:**
- Validate all external inputs to prevent injection attacks and other security vulnerabilities.
- Sanitize user inputs before using them in database queries or other sensitive operations.
- **Authentication and Authorization:**
- Use established authentication and authorization libraries.
- Implement proper access controls to protect sensitive data.
- **Data Protection:**
- Encrypt sensitive data at rest and in transit.
- Use secure communication protocols (e.g., HTTPS).
- **Secure API Communication:**
- Validate all API requests and responses.
- Implement rate limiting to prevent abuse.
## Testing Approaches
- **Unit Testing:**
- Use `trio.testing` to write unit tests for async functions.
- Use `trio.testing.MockClock` to control time in tests.
- Mock external dependencies to isolate the code being tested.
- **Integration Testing:**
- Test the interaction between different components of your application.
- Use real or simulated external services to test the integration with external systems.
- **End-to-End Testing:**
- Test the entire application flow from the user interface to the database.
- **Test Organization:**
- Organize tests in a directory structure that mirrors the structure of your application code.
- Write clear and concise test names that describe the behavior being tested.
- **Mocking and Stubbing:**
- Use mocking libraries like `unittest.mock` to replace external dependencies with mock objects.
- Use stubbing to provide predefined responses for external dependencies.
## Common Pitfalls and Gotchas
- **Frequent Mistakes:**
- Blocking the event loop with synchronous operations.
- Ignoring cancellation requests.
- Using `time.sleep()` instead of `trio.sleep()`.
- Not handling exceptions properly.
- **Edge Cases:**
- Handling timeouts and deadlines.
- Dealing with cancellation in complex workflows.
- Managing resources in the presence of exceptions and cancellations.
- **Version-Specific Issues:**
- Be aware of any known bugs or limitations in specific versions of Trio.
- **Compatibility Concerns:**
- Ensure compatibility between Trio and other libraries you are using.
- **Debugging Strategies:**
- Use debuggers like `pdb` or `ipdb` to step through your code and inspect variables.
- Use logging to track the execution flow of your application.
- Use Trio's built-in debugging tools to identify performance bottlenecks and other issues.
## Tooling and Environment
- **Recommended Development Tools:**
- VS Code with the Python extension.
- PyCharm.
- IPython or Jupyter Notebook for interactive development.
- **Build Configuration:**
- Use a build system like `poetry` or `pipenv` to manage dependencies.
- **Linting and Formatting:**
- Use `flake8` and `black` to ensure consistent code style.
- Configure your editor to automatically format code on save.
- **Deployment:**
- Use a process manager like `systemd` or `supervisor` to manage your application.
- **CI/CD Integration:**
- Use CI/CD tools like GitHub Actions or GitLab CI to automate testing and deployment.