refactor: streamline onboarding modal by removing unnecessary slide preparation logic and enhancing loading state management for improved user experience
This commit is contained in:
356
.cursor/rules/click.mdc
Normal file
356
.cursor/rules/click.mdc
Normal file
@@ -0,0 +1,356 @@
|
||||
---
|
||||
description: Comprehensive best practices for developing robust and maintainable command-line interfaces using the Click library in Python. Covers code structure, patterns, performance, security, testing, and common pitfalls.
|
||||
globs: *.py
|
||||
---
|
||||
# Click CLI Library Best Practices
|
||||
|
||||
This document outlines best practices and coding standards for developing command-line interfaces (CLIs) in Python using the Click library. Click is a powerful and user-friendly library that simplifies the creation of beautiful and functional CLIs.
|
||||
|
||||
## 1. Code Organization and Structure
|
||||
|
||||
### 1.1. Directory Structure
|
||||
|
||||
Adopt a well-organized directory structure to enhance maintainability and scalability.
|
||||
|
||||
|
||||
mycli/
|
||||
├── mycli.py # Main application entry point (click command group)
|
||||
├── commands/
|
||||
│ ├── __init__.py # Makes 'commands' a Python package
|
||||
│ ├── cmd_foo.py # Implementation of 'foo' command
|
||||
│ ├── cmd_bar.py # Implementation of 'bar' command
|
||||
│ └── ...
|
||||
├── utils/
|
||||
│ ├── __init__.py # Utility functions (e.g., file I/O, API calls)
|
||||
│ ├── helper.py # Helper functions
|
||||
│ └── ...
|
||||
├── models/
|
||||
│ ├── __init__.py # Data models (e.g., classes, data structures)
|
||||
│ ├── data_model.py # Data models
|
||||
│ └── ...
|
||||
├── tests/
|
||||
│ ├── __init__.py # Test suite directory
|
||||
│ ├── test_mycli.py # Tests for main application
|
||||
│ ├── test_commands/
|
||||
│ │ ├── test_cmd_foo.py # Tests for 'foo' command
|
||||
│ │ └── ...
|
||||
│ └── ...
|
||||
├── README.md # Project documentation
|
||||
├── LICENSE # License information
|
||||
├── pyproject.toml # Project configuration (dependencies, build)
|
||||
└── .gitignore # Specifies intentionally untracked files that Git should ignore
|
||||
|
||||
|
||||
### 1.2. File Naming Conventions
|
||||
|
||||
* Use descriptive and consistent file names.
|
||||
* Main application file: `mycli.py` (or a similar name reflecting the application's purpose).
|
||||
* Command modules: `cmd_<command_name>.py` (e.g., `cmd_create.py`, `cmd_update.py`).
|
||||
* Utility modules: `helper.py`, `file_utils.py`, etc.
|
||||
* Test files: `test_<module_name>.py` (e.g., `test_mycli.py`, `test_cmd_create.py`).
|
||||
|
||||
### 1.3. Module Organization
|
||||
|
||||
* **Main Application Module (`mycli.py`):**
|
||||
* Define the main `click.group()` that serves as the entry point for the CLI.
|
||||
* Import and register subcommands from the `commands` package.
|
||||
* Handle global options and context management.
|
||||
* **Command Modules (`commands` package):**
|
||||
* Each command module should define a single `click.command()` decorated function.
|
||||
* Command functions should encapsulate the logic for that specific command.
|
||||
* Import necessary utility functions and data models from the `utils` and `models` packages.
|
||||
* **Utility Modules (`utils` package):**
|
||||
* Provide reusable functions for common tasks, such as file I/O, API calls, data validation, etc.
|
||||
* Keep utility functions generic and independent of specific commands.
|
||||
* **Data Models (`models` package):**
|
||||
* Define classes and data structures to represent the data used by the CLI application.
|
||||
* Use dataclasses or attrs for creating data models with less boilerplate.
|
||||
|
||||
### 1.4. Component Architecture
|
||||
|
||||
* **Separation of Concerns:** Clearly separate the CLI interface from the application logic.
|
||||
* **Command Layer:** Click commands handle user input, argument parsing, and invoking the underlying application logic.
|
||||
* **Service Layer:** Implement the core application logic in separate modules or classes (services).
|
||||
* **Data Access Layer:** Encapsulate data access and persistence logic in dedicated modules or classes.
|
||||
|
||||
### 1.5. Code Splitting Strategies
|
||||
|
||||
* **Command-Based Splitting:** Split the application into separate modules based on the CLI commands.
|
||||
* **Feature-Based Splitting:** Group related commands and utilities into feature-specific modules.
|
||||
* **Layered Splitting:** Divide the application into layers (CLI, service, data access) and organize modules accordingly.
|
||||
|
||||
## 2. Common Patterns and Anti-patterns
|
||||
|
||||
### 2.1. Design Patterns
|
||||
|
||||
* **Command Pattern:** Each CLI command is represented by a separate class or function, making it easy to add, remove, or modify commands.
|
||||
* **Factory Pattern:** Use factories to create objects based on CLI arguments (e.g., creating different types of data exporters based on the `--format` option).
|
||||
* **Dependency Injection:** Inject dependencies (e.g., API clients, database connections) into command functions to improve testability and flexibility.
|
||||
* **Context Object:** Use Click's context object to store and share data across commands (see examples in the Click documentation).
|
||||
|
||||
### 2.2. Recommended Approaches
|
||||
|
||||
* **Configuration Management:** Use environment variables or configuration files to manage application settings.
|
||||
* **Logging:** Implement comprehensive logging using the `logging` module to track application behavior and diagnose issues.
|
||||
* **Progress Bars:** Use Click's progress bar (`click.progressbar`) to provide visual feedback for long-running tasks.
|
||||
* **Interactive Prompts:** Use Click's prompts (`click.prompt`) to gather user input interactively.
|
||||
* **File Handling:** Use `click.File` to handle file I/O with automatic error checking and encoding support.
|
||||
* **Exception Handling:** Use try-except blocks to gracefully handle exceptions and provide informative error messages to the user.
|
||||
* **Testing:** Implement a comprehensive test suite to ensure the CLI application's correctness and reliability.
|
||||
|
||||
### 2.3. Anti-patterns and Code Smells
|
||||
|
||||
* **Tight Coupling:** Avoid tight coupling between the CLI interface and the application logic.
|
||||
* **Global State:** Minimize the use of global variables and mutable global state.
|
||||
* **Hardcoded Values:** Avoid hardcoding values in the code; use configuration files or environment variables instead.
|
||||
* **Duplicated Code:** Refactor duplicated code into reusable functions or classes.
|
||||
* **Lack of Error Handling:** Neglecting to handle exceptions can lead to unexpected crashes and poor user experience.
|
||||
* **Inadequate Testing:** Insufficient testing can result in undetected bugs and regressions.
|
||||
* **Overly Complex Commands:** Break down overly complex commands into smaller, more manageable subcommands.
|
||||
|
||||
### 2.4. State Management
|
||||
|
||||
* **Context Object:** Use `click.Context.obj` to store and share state between commands. This is the recommended approach for passing data between different parts of your application within a single CLI invocation.
|
||||
* **Environment Variables:** Use environment variables for global configuration settings that rarely change.
|
||||
* **Files:** Store persistent state (e.g., user preferences, cached data) in files.
|
||||
* **Databases:** For more complex state management, use a database.
|
||||
|
||||
### 2.5. Error Handling
|
||||
|
||||
* **`try...except` Blocks:** Wrap potentially failing operations in `try...except` blocks to catch exceptions.
|
||||
* **Click's `click.ClickException`:** Raise `click.ClickException` to display user-friendly error messages. This will ensure that the error message is formatted correctly and displayed to the user in a consistent manner.
|
||||
* **Custom Exception Classes:** Define custom exception classes for specific error conditions.
|
||||
* **Logging:** Log all errors to aid in debugging and troubleshooting.
|
||||
* **Exit Codes:** Use appropriate exit codes to indicate the success or failure of a command.
|
||||
|
||||
python
|
||||
import click
|
||||
|
||||
@click.command()
|
||||
@click.option('--input', '-i', required=True, type=click.Path(exists=True, dir_okay=False, readable=True))
|
||||
def process_file(input):
|
||||
try:
|
||||
with open(input, 'r') as f:
|
||||
# Process the file content
|
||||
content = f.read()
|
||||
click.echo(f'Processing file: {input}')
|
||||
except FileNotFoundError:
|
||||
raise click.ClickException(f'File not found: {input}')
|
||||
except IOError:
|
||||
raise click.ClickException(f'Could not read file: {input}')
|
||||
except Exception as e:
|
||||
click.echo(f'An unexpected error occurred: {e}', err=True)
|
||||
raise # Re-raise the exception for higher-level handling or logging
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
process_file()
|
||||
except click.ClickException as e:
|
||||
click.echo(f'Error: {e}', err=True)
|
||||
|
||||
|
||||
## 3. Performance Considerations
|
||||
|
||||
### 3.1. Optimization Techniques
|
||||
|
||||
* **Minimize I/O Operations:** Reduce the number of file I/O and network operations.
|
||||
* **Use Efficient Data Structures:** Choose appropriate data structures (e.g., sets, dictionaries) for optimal performance.
|
||||
* **Caching:** Cache frequently accessed data to reduce redundant computations.
|
||||
* **Profiling:** Use profiling tools to identify performance bottlenecks.
|
||||
|
||||
### 3.2. Memory Management
|
||||
|
||||
* **Large Datasets:** When dealing with large datasets, use generators or iterators to process data in chunks.
|
||||
* **Object Creation:** Avoid creating unnecessary objects.
|
||||
* **Resource Management:** Release resources (e.g., file handles, network connections) when they are no longer needed.
|
||||
|
||||
### 3.3. Bundle Size Optimization
|
||||
|
||||
* **Dependency Management:** Use a virtual environment to isolate project dependencies.
|
||||
* **Tree Shaking:** Use tools like `pyinstaller` to remove unused code from the final executable.
|
||||
|
||||
### 3.4. Lazy Loading
|
||||
|
||||
* **Import on Demand:** Import modules only when they are needed.
|
||||
* **Command Loading:** Load command modules only when the corresponding command is invoked.
|
||||
|
||||
## 4. Security Best Practices
|
||||
|
||||
### 4.1. Common Vulnerabilities
|
||||
|
||||
* **Command Injection:** Prevent command injection by carefully validating user input.
|
||||
* **Path Traversal:** Avoid path traversal vulnerabilities by sanitizing file paths.
|
||||
* **Sensitive Data Exposure:** Protect sensitive data (e.g., passwords, API keys) by storing them securely and avoiding logging them.
|
||||
|
||||
### 4.2. Input Validation
|
||||
|
||||
* **Type Checking:** Use Click's type system to validate the type of user input.
|
||||
* **Range Checking:** Validate that numerical inputs fall within acceptable ranges.
|
||||
* **Regular Expressions:** Use regular expressions to validate string inputs.
|
||||
* **Whitelist:** Validate inputs against a whitelist of allowed values.
|
||||
* **Sanitization:** Sanitize user inputs to remove potentially harmful characters.
|
||||
|
||||
python
|
||||
import click
|
||||
import re
|
||||
|
||||
@click.command()
|
||||
@click.option('--email', '-e', required=True)
|
||||
def send_email(email):
|
||||
# Input validation using regex
|
||||
if not re.match(r"^[\w\.-]+@([\w-]+\.)+[\w-]{2,4}$", email):
|
||||
raise click.ClickException("Invalid email format.")
|
||||
click.echo(f"Sending email to {email}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
send_email()
|
||||
|
||||
|
||||
|
||||
### 4.3. Authentication and Authorization
|
||||
|
||||
* **API Keys:** Use API keys to authenticate users and authorize access to resources.
|
||||
* **OAuth:** Implement OAuth 2.0 for secure API authentication.
|
||||
* **Role-Based Access Control (RBAC):** Implement RBAC to control access to commands and resources based on user roles.
|
||||
|
||||
### 4.4. Data Protection
|
||||
|
||||
* **Encryption:** Encrypt sensitive data at rest and in transit.
|
||||
* **Hashing:** Hash passwords and other sensitive data using strong hashing algorithms.
|
||||
* **Data Masking:** Mask sensitive data in logs and other outputs.
|
||||
|
||||
### 4.5. Secure API Communication
|
||||
|
||||
* **HTTPS:** Use HTTPS for all API communication.
|
||||
* **TLS/SSL:** Use TLS/SSL certificates to encrypt data in transit.
|
||||
* **API Rate Limiting:** Implement API rate limiting to prevent abuse.
|
||||
|
||||
## 5. Testing Approaches
|
||||
|
||||
### 5.1. Unit Testing
|
||||
|
||||
* **Test Individual Functions:** Write unit tests for individual functions and classes.
|
||||
* **Mock Dependencies:** Mock external dependencies (e.g., API calls, database connections) to isolate the code under test.
|
||||
* **Test Edge Cases:** Test edge cases and boundary conditions to ensure code robustness.
|
||||
* **Use `click.testing.CliRunner`:** Use `click.testing.CliRunner` to simulate CLI invocations and verify the output.
|
||||
|
||||
### 5.2. Integration Testing
|
||||
|
||||
* **Test Command Combinations:** Test combinations of commands and options to ensure they work together correctly.
|
||||
* **Test Real Dependencies:** Test with real dependencies (e.g., a test database) to ensure the application integrates properly.
|
||||
|
||||
### 5.3. End-to-End Testing
|
||||
|
||||
* **Test Full Workflow:** Test the entire CLI application workflow from start to finish.
|
||||
* **Automate Tests:** Automate end-to-end tests to ensure continuous integration and continuous delivery.
|
||||
|
||||
### 5.4. Test Organization
|
||||
|
||||
* **Separate Test Directory:** Create a separate `tests` directory for all tests.
|
||||
* **Mirror Source Structure:** Mirror the source code structure in the test directory.
|
||||
* **Descriptive Test Names:** Use descriptive test names to clearly indicate what each test is verifying.
|
||||
|
||||
### 5.5. Mocking and Stubbing
|
||||
|
||||
* **`unittest.mock`:** Use the `unittest.mock` module to create mock objects and stubs.
|
||||
* **Mock External Dependencies:** Mock external dependencies to isolate the code under test.
|
||||
* **Stub Return Values:** Stub return values to control the behavior of mocked objects.
|
||||
|
||||
python
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
from click.testing import CliRunner
|
||||
from mycli import cli # Assuming your main script is named mycli.py
|
||||
|
||||
class TestMyCLI(unittest.TestCase):
|
||||
|
||||
def test_hello_world(self):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ['hello', '--name', 'TestUser'])
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
self.assertEqual(result.output.strip(), 'Hello, TestUser!')
|
||||
|
||||
@patch('mycli.commands.cmd_foo.get_data') # Assuming cmd_foo.py has a function get_data to mock
|
||||
def test_foo_command(self, mock_get_data):
|
||||
mock_get_data.return_value = ['data1', 'data2']
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ['foo'])
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
self.assertIn('data1', result.output)
|
||||
self.assertIn('data2', result.output)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
## 6. Common Pitfalls and Gotchas
|
||||
|
||||
### 6.1. Frequent Mistakes
|
||||
|
||||
* **Forgetting `@click.command()` or `@click.group()`:** Commands will not be registered without these decorators.
|
||||
* **Incorrect Argument Types:** Using the wrong type for a `click.argument` or `click.option` can lead to unexpected behavior.
|
||||
* **Missing `required=True`:** Forgetting to specify `required=True` for mandatory arguments or options.
|
||||
* **Not Handling Exceptions:** Failing to handle exceptions can cause the CLI to crash.
|
||||
* **Incorrect File Paths:** Providing incorrect file paths to `click.Path` can lead to errors.
|
||||
|
||||
### 6.2. Edge Cases
|
||||
|
||||
* **Unicode Handling:** Ensure proper handling of Unicode characters in input and output.
|
||||
* **Large Input Files:** Handle large input files efficiently to avoid memory issues.
|
||||
* **Concurrent Access:** Handle concurrent access to shared resources (e.g., files, databases) properly.
|
||||
|
||||
### 6.3. Version-Specific Issues
|
||||
|
||||
* **Compatibility:** Check for compatibility issues between Click and other libraries.
|
||||
* **Deprecated Features:** Be aware of deprecated features and plan for migration.
|
||||
|
||||
### 6.4. Compatibility Concerns
|
||||
|
||||
* **Python Versions:** Ensure compatibility with supported Python versions.
|
||||
* **Operating Systems:** Test the CLI application on different operating systems (Windows, macOS, Linux).
|
||||
* **Terminal Emulators:** Be aware of potential compatibility issues with different terminal emulators.
|
||||
|
||||
### 6.5. Debugging Strategies
|
||||
|
||||
* **`print()` Statements:** Use `print()` statements for basic debugging.
|
||||
* **Debuggers:** Use debuggers (e.g., `pdb`, `ipdb`) for more advanced debugging.
|
||||
* **Logging:** Use logging to track application behavior and diagnose issues.
|
||||
* **Click's `echo()` Function:** Use Click's `echo()` to ensure consistent output across different platforms and terminal configurations. Also, use `err=True` to distinguish error messages clearly.
|
||||
|
||||
## 7. Tooling and Environment
|
||||
|
||||
### 7.1. Recommended Tools
|
||||
|
||||
* **Virtual Environments:** Use virtual environments (e.g., `venv`, `virtualenv`) to isolate project dependencies.
|
||||
* **Package Manager:** Use `pip` for installing and managing Python packages.
|
||||
* **Text Editor/IDE:** Use a text editor or IDE with Python support (e.g., VS Code, PyCharm).
|
||||
* **Linting Tools:** Use linting tools (e.g., `flake8`, `pylint`) to enforce coding style and identify potential errors.
|
||||
* **Formatting Tools:** Use formatting tools (e.g., `black`, `autopep8`) to automatically format code.
|
||||
* **Testing Frameworks:** Use testing frameworks (e.g., `unittest`, `pytest`) to write and run tests.
|
||||
|
||||
### 7.2. Build Configuration
|
||||
|
||||
* **`pyproject.toml`:** Use `pyproject.toml` to specify project dependencies and build configuration.
|
||||
* **`setup.py`:** Use `setup.py` to define the project's metadata and entry points.
|
||||
* **Build Tools:** Use build tools (e.g., `setuptools`, `poetry`) to package and distribute the CLI application.
|
||||
|
||||
### 7.3. Linting and Formatting
|
||||
|
||||
* **`flake8`:** Use `flake8` to check for PEP 8 violations and other coding style issues.
|
||||
* **`pylint`:** Use `pylint` for more comprehensive code analysis.
|
||||
* **`black`:** Use `black` to automatically format code according to PEP 8.
|
||||
* **Pre-commit Hooks:** Use pre-commit hooks to automatically run linting and formatting tools before committing code.
|
||||
|
||||
### 7.4. Deployment
|
||||
|
||||
* **Package Managers:** Deploy the CLI application using package managers (e.g., `pip`, `conda`).
|
||||
* **Executable Bundles:** Create standalone executable bundles using tools like `pyinstaller` or `cx_Freeze`.
|
||||
* **Containers:** Deploy the CLI application in containers (e.g., Docker) for portability and scalability.
|
||||
|
||||
### 7.5. CI/CD Integration
|
||||
|
||||
* **Continuous Integration:** Integrate the CLI application into a CI/CD pipeline (e.g., Jenkins, GitLab CI, GitHub Actions).
|
||||
* **Automated Testing:** Automate testing as part of the CI/CD pipeline.
|
||||
* **Automated Deployment:** Automate deployment to staging and production environments.
|
||||
|
||||
By following these best practices, developers can create robust, maintainable, and user-friendly CLI applications using the Click library.
|
||||
Reference in New Issue
Block a user