Skip to content

7 Advanced Coding Standards

Shahm Najeeb edited this page Dec 7, 2024 · 3 revisions

Logicytics Coding Standards

Table of Contents

  1. Introduction
  2. Language Preference
  3. File Naming and Organization
  4. Coding Style
  5. Functions and Modularity
  6. Log Handling
  7. Version Control
  8. Documentation
  9. Compatibility
  10. Security Considerations
  11. Code Review Process
  12. Version control
  13. Dependency Management
  14. Conclusion

1. Introduction

Logicytics Coding Standards are designed to ensure consistency, readability, and maintainability throughout our codebase. These standards are crucial for collaborative development and long-term maintenance of Logicytics projects. Adhering to these practices ensures that our codebase remains clean, efficient, and easy to understand for all contributors.

2. Language Preference

Prefer Python, Batch, or PowerShell scripts for new additions:

  • Python: Ideal for complex logic and integrations
  • Batch: Suitable for simple Windows-specific tasks
  • PowerShell: Good for advanced Windows automation

If using other languages:

  • Ensure thorough documentation
  • Translate to EXE for compatibility

3. File Naming and Organization

Follow these guidelines:

3a. Directory Structure

  • Place files in appropriate directories based on functionality
  • Example structure:
Logicytics/
└── CODE/
|   └── # Normal CODE #
└── MODS/
    └── # CODE for MODS #

3b. File Naming Conventions

  • Use lowercase_with_underscores naming convention (e.g., my_script.py)
    • If the file should be ignored by the script running, start the file with an underscore (e.g., _my_script.py)
    • If the file is a library, start the file with __lib_ (e.g., __lib_class.py)

4. Coding Style

4a. General Guidelines

  • Indentation: Use 4 spaces for each level of nesting
  • Naming conventions:
    • Variables: lowercase_with_underscores
    • Functions/Methods: lowercase_with_underscores
    • Classes: CapitalizedWords
  • Line length: Limit lines to 79 characters (maximum 150)
  • Imports: Group imports alphabetically, separate standard library imports from project-specific ones
  • Avoid unnecessary comments and print statements - Use log.debug() and log.info() instead of print()

Example:

import os
from typing import Dict, Any
import psutil
import json

def collect_system_info() -> Dict[str, Any]:
    """Collects system information."""
    # Main logic
    info = {
        'os': os.name,
        'cpu_count': os.cpu_count(),
        'memory': psutil.virtual_memory().total / (1024 * 1024),
        'disk_space': psutil.disk_usage('/').total / (1024 * 1024)
    }
    return info

if __name__ == "__main__":
    system_info = collect_system_info()
    print(json.dumps(system_info, indent=2))

4b. Docstrings and Comments

  • Use triple quotes for docstrings
  • Explain function/class purpose, parameters, and return values
  • Use inline comments sparingly to explain complex logic or non-obvious decisions

Example:

from typing import Dict, Any, List
from logicytics import Log, DEBUG

if __name__ == "__main__":
    log = Log({"log_level": DEBUG})


def process_log_file(file_path: str) -> Dict[str, Any]:
    """
    Processes a log file and extracts relevant information.

    Args:
        file_path (str): Path to the log file

    Returns:
        Dict[str, Any]: Extracted log information

    Raises:
        IOError: If the file cannot be read
        ValueError: If the file content is invalid
    """
    # We're using a try-except block here because file operations can raise exceptions
    try:
        with open(file_path, 'r') as file:
            file.read()
            # Complex parsing logic here...
            parsed_log_info = parse_log_content()
            return {"file_path": file_path, "content": parsed_log_info}
    except IOError as e:
        log.error(f"Error reading file: {e}")
        raise
    except ValueError as e:
        log.warning(f"Invalid file content: {e}")
        return {}


def parse_log_content() -> List[Dict[str, str]]:
    """Parses log content and returns structured data."""
    # Implementation details...
    pass

4c. Type Hinting

  • Use built-in types like str, int, dict, list
  • For complex types, use typing module (e.g., List, Dict, Optional)
  • You can also use from __future__ import annotations to use the | operator for Union types

Example:

from __future__ import annotations
from typing import Dict, Optional, Any, Tuple

def analyze_process(process_id: int) -> Optional[Tuple[int, Dict[str, float]]] | None:
    """Analyzes a process and returns its details."""
    process_details = get_process_details(process_id)
    if not process_details:
        return None
    cpu_usage, memory_usage = calculate_resource_usage(process_details)
    return process_id, {'cpu': cpu_usage, 'memory': memory_usage}

def get_process_details(process_id: int) -> Optional[Dict[str, Any]]:
    """Retrieves process details."""
    # Implementation details...
    return {'Process ID': process_id}

def calculate_resource_usage(details: Dict[str, Any]) -> Tuple[float, float]:
    """Calculates CPU and memory usage based on process details."""
    # Implementation details...
    return details['cpu_usage'], details['memory_usage']

5. Functions and Modularity

  • Keep functions focused on a single responsibility
  • Use descriptive names for functions and variables
  • Group related functionality into modules
  • Aim for a maximum of 20-30 lines per function
  • Use meaningful parameter names and avoid excessive parameters (>4)

Example:

import os
from datetime import datetime
import zipfile

def create_backup(Source_dir: str, backup_dir: str) -> str:
    """Creates a backup of the source directory."""
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    Backup_name = f"{os.path.basename(Source_dir)}_{timestamp}.zip"
    backup_path = os.path.join(backup_dir, Backup_name)
    
    zip_files(Source_dir, Backup_path)
    return backup_path

def zip_files(source: str, destination: str) -> None:
    """Zips files from source to destination."""
    with zipfile.ZipFile(destination, 'w', compression=zipfile.ZIP_DEFLATED) as zipf:
        for root, _, files in os.walk(source):
            for file in files:
                file_path = os.path.join(root, file)
                rel_path = os.path.relpath(file_path, source)
                zipf.write(file_path, rel_path)

def verify_backup(backup_path: str) -> bool:
    """Verifies the integrity of a backup file."""
    try:
        with zipfile.ZipFile(backup_path, 'r') as zip_ref:
            zip_ref.testzip()
        return True
    except zipfile.BadZipFile:
        return False

# Usage
source_dir = "/path/to/source"
Backup_dir = "/path/to/backups"

Backup_path = create_backup(source_dir, Backup_dir)
if verify_backup(Backup_path):
    print(f"Backup created successfully: {Backup_path}")
else:
    print("Backup verification failed.")

6. Log Handling

6a. General Error Handling

  • Use specific exception types where possible
  • Provide meaningful error messages
  • Handle potential exceptions gracefully
    • Don't use print() - Use log.error(), log.warning() and log.critical() instead
    • To raise exceptions, use log.exception() with your message and Exception class type, like log.exception("Error Message", FileNotFoundError)
  • Attempt to only import needed items from the logicytics module, don't use wildcard imports! Example:
import socket
from typing import Dict, Any
from logicytics import Log, DEBUG

if __name__ == "__main__":
    log = Log({"log_level": DEBUG})

def collect_network_stats():
    try:
        # Network operations here
        network_stats = {"network": "stats"}  # Example
        log.info("Network stats collected successfully.")
        return network_stats
    except socket.timeout:
        log.error("Network timeout occurred. Retrying...")
        return collect_network_stats()
    except PermissionError:
        log.error("Insufficient permissions to access network information.")
        return None
    except Exception as e:
        log.critical(f"Unexpected error occurred: {e}")
        raise

def process_user_input(input_data: str) -> Dict[str, Any]:
    """Processes user input and returns structured data."""
    try:
        parsed_input = parse_input(input_data)
        validated_data = validate_input(parsed_input)
        processed_result = process_validated_data(validated_data)
        log.debug(f"Processed input: {input_data}")
        return {"status": "success", "result": processed_result}
    except ValueError as e:
        log.warning(f"Invalid input: {e}")
        return {"status": "error", "message": str(e)}
    except Exception as e:
        log.exception(f"Unexpected error during input processing: {e}", Exception)
        raise

def parse_input(input_data: str) -> Dict[str, Any]:
    """Parses raw input into structured data."""
    # Implementation details...
    return {"parsed_data": input_data}

def validate_input(parsed_data: Dict[str, Any]) -> Dict[str, Any]:
    """Validates parsed input data."""
    # Implementation details...
    return {"validated_data": parsed_data}

def process_validated_data(validated_data: Dict[str, Any]) -> Dict[str, Any]:
    """Processes validated input data."""
    # Implementation details...
    return {"processed_data": validated_data}

6b. Log Printing Rules

  • Python Users: Utilize in-built class __lib_class.py for colored output and structured logging. Avoid using print(); for newline characters, use log.newline() instead.
  • Other Languages: Start messages with appropriate keywords (INFO:, WARNING:, ERROR:, CRITICAL:) followed by the message. For example, "INFO: Your Message".

7. Version Control

  • Use Git for tracking changes
  • Create feature branches for new features or bug fixes
  • Use descriptive commit messages following the Conventional Commits format
  • Include relevant issue numbers in commit messages when applicable

Example commit message:

feat: add support for multi-threaded processing

- Implement ThreadPoolExecutor for parallel task execution
- Add configuration option for thread count
- Update documentation with usage examples

Closes #123

8. Documentation

8a. Project-Level Documentation

  • Maintain an up-to-date README.md file in the project root
  • Include information about:
    • Project overview
    • Installation instructions
    • Usage examples
    • Contributing guidelines
    • License information

Example README.md structure:

# Logicytics Project

A comprehensive toolkit for system analysis and automation.

## Table of Contents

- [Installation](#installation)
- [Usage](#usage)
- [Contributing](#contributing)
- [License](#license)

## Installation

To install Logicytics, follow these steps:
`git clone https://github.com/DefinetlyNotAI/Logicytics.git`
`pip install -r requirements.txt`

## Usage

Basic usage example:

"""
from logicytics import SystemAnalyzer

analyzer = SystemAnalyzer()
report = analyzer.generate_report()
print(report.summary())
"""

## Contributing

See our [CONTRIBUTING.md](CONTRIBUTING.md) file for details on how to contribute.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

8b. Module and Function Documentation

  • Use docstrings for all modules, classes, functions, and methods
  • Follow the Google Python Style Guide for docstring formatting
  • Include information about parameters, return values, exceptions raised, and any side effects

Example module documentation:

"""
Module for handling network-related operations.

Classes:
    NetworkInterface: Represents a network interface.
    PacketSniffer: Sniffs packets on a given interface.

Functions:
    get_network_interfaces(): Returns a list of available network interfaces.
    ping_host(host: str): Pings a host and returns the result.
"""

from typing import List


class NetworkInterface:
    """Represents a network interface."""

    def __init__(self, name: str):
        """
        Initializes a NetworkInterface object.

        Args:
            name (str): Name of the network interface.
        """
        self.name = name

    def get_ip_address(self) -> str:
        """
        Gets the IP address associated with this interface.

        Returns:
            str: The IP address of the interface.

        Raises:
            ValueError: If the interface doesn't have an IP address assigned.
        """
        # Implementation details...


def get_network_interfaces() -> List[NetworkInterface]:
    """
    Retrieves a list of available network interfaces.

    Returns:
        List[NetworkInterface]: A list of NetworkInterface objects representing available interfaces.
    """
    # Implementation details...


def ping_host() -> bool:
    """
    Pings a host and returns the result.

    Returns:
        bool: True if the host responds, False otherwise.

    Raises:
        socket.error: If there's a problem with the network connection.
    """
    # Implementation details...

8c. Inline Comments

  • Use inline comments sparingly to explain complex logic or non-obvious decisions
  • Keep comments concise and relevant
  • Update comments when modifying code to ensure they remain accurate

Example:

import zlib
from typing import Dict, Any
import json


def calculate_checksum(data: bytes) -> int:
    """
    Calculates the checksum of the given data using CRC32 algorithm.

    Args:
        data (bytes): Data to calculate checksum for.

    Returns:
        int: The calculated checksum.
    """
    # We're using zlib.crc32 here because it provides a fast and reliable CRC32 implementation
    return zlib.crc32(data) & 0xffffffff

def parse_config(config_str: str) -> Dict[str, Any]:
    """
    Parses a configuration string into a dictionary.

    Args:
        config_str (str): Configuration string in JSON format.

    Returns:
        Dict[str, Any]: Parsed configuration dictionary.

    Raises:
        json.JSONDecodeError: If the input string is not valid JSON.
    """
    # We're using json.loads instead of eval for security reasons
    # It safely parses JSON strings without executing arbitrary code
    return json.loads(config_str)

9. Compatibility

  • Ensure new additions don't break existing features
  • Use version control tags to mark major releases
  • Implement backwards compatibility for at least one minor version
  • Document any breaking changes in release notes

Example of documenting breaking changes:

Breaking Changes

  1. Removed deprecated old_function() method. Use new_method() instead.
  2. Changed default behavior of process_data(). Now requires explicit verbose=False parameter for silent operation.

New Features

  • Added support for parallel processing in analyze_system()
  • Introduced new config_manager class for centralized configuration management

Bug Fixes

  • Fixed issue with incorrect timestamp parsing in parse_log_file()
  • Resolved memory leak in continuous_monitoring() function

10. Security Considerations

10a. Input Validation

  • Always validate and sanitize user inputs
  • Use whitelisting approach for allowed inputs
  • Implement proper error handling for invalid inputs

Example:

import re
import bcrypt

def validate_username(username: str) -> bool:
    """
    Validates a username against security criteria.

    Args:
        username (str): Username to validate.

    Returns:
        bool: True if the username is valid, False otherwise.
    """
    pattern = r'^[a-zA-Z0-9_]{3,16}$'
    return bool(re.match(pattern, username))

def create_user(username: str, password: str) -> dict:
    """
    Creates a new user account.

    Args:
        username (str): Desired username.
        password (str): Password for the new account.

    Returns:
        User: Created user object if successful, None otherwise.

    Raises:
        ValueError: If the username or password is invalid.
    """
    if not validate_username(username):
        raise ValueError("Invalid username")
    
    if len(password) < 8:
        raise ValueError("Password must be at least 8 characters long")

    # Hash password before storing
    hashed_password = hash_password(password)
    return {username: hashed_password}

def hash_password(password: str) -> str:
    """
    Hashes a password using bcrypt.

    Args:
        password (str): Password to hash.

    Returns:
        str: Hashed password string.
    """
    salt = bcrypt.gensalt()
    return str(bcrypt.hashpw(password.encode(), salt))

10b. Secure Data Handling

  • Use secure methods for data encryption and decryption
  • Implement proper key management practices
  • Avoid hardcoding sensitive information in source code

Example:

from cryptography.fernet import Fernet

class SecureDataHandler:
    def __init__(self, key: str):
        self.key = key.encode()

    def encrypt_data(self, data: str) -> str:
        """
        Encrypts the given data using Fernet symmetric encryption.

        Args:
            data (str): Data to encrypt.

        Returns:
            str: Encrypted data as a URL-safe base64-encoded string.
        """
        cipher_suite = Fernet(self.key)
        cipher_text = cipher_suite.encrypt(data.encode())
        return cipher_text.decode()

    def decrypt_data(self, encrypted_data: str) -> str:
        """
        Decrypts the given encrypted data.

        Args:
            encrypted_data (str): Encrypted data to decrypt.

        Returns:
            str: Decrypted data.

        Raises:
            ValueError: If the decryption fails.
        """
        try:
            cipher_suite = Fernet(self.key)
            plain_text = cipher_suite.decrypt(encrypted_data.encode())
            return plain_text.decode()
        except Exception as e:
            raise ValueError(f"Decryption failed: {e}")

# Usage example
handler = SecureDataHandler(str(Fernet.generate_key()))
secret_message = "This is a secret message"
encrypted_message = handler.encrypt_data(secret_message)
decrypted_message = handler.decrypt_data(encrypted_message)

assert secret_message == decrypted_message

11. Code Review Process

11a. Pre-Review Checklist

Before submitting code for review:

  1. Run linters and formatters (black .)
  2. Perform manual testing of new functionality
  3. Update documentation
  4. Ensure code adheres to coding standards

11b. Review Guidelines

During code review:

  1. Focus on code quality, readability, and maintainability
  2. Check for adherence to coding standards
  3. Evaluate performance implications
  4. Assess security aspects
  5. Look for opportunities for refactoring or optimization

Example code review checklist:

# Code Review Checklist

## General
- Does the code follow Logicytics Coding Standards?
- Are all functions/classes properly documented?

## Performance
- Are there any obvious performance bottlenecks?
- Is memory usage optimized?

## Security
- Are inputs properly validated and sanitized?
- Are sensitive data handled securely?

## Testing
- Are appropriate unit tests included?
- Do tests cover edge cases?

## Architecture
- Does the code fit well with the overall project architecture?
- Are dependencies properly managed?

## Readability
- Is the code easy to understand?
- Are variable/function names descriptive?

## Comments
- Are comments clear and concise?
- Are complex logic parts commented?

## Error Handling
- Are exceptions handled appropriately?
- Are error messages informative?

## Documentation
- Is the README updated if necessary?
- Are any new APIs or interfaces documented?

## Best Practices
- Are design patterns used appropriately?
- Is the code DRY (Don't Repeat Yourself)?

## Suggestions
- Any suggestions for improvement or refactoring?

12. Dependency Management

12a. Package Version Control

Use semantic versioning for Logicytics packages:

  • MAJOR.MINOR.PATCH format
  • Increment MAJOR for breaking changes
  • Increment MINOR for new features
  • Increment PATCH for bug fixes

12b. Dependency Security

Implement dependency security checks:

  1. Use tools like pip-compile or poetry for dependency management
  2. Regularly update dependencies
  3. Scan for known vulnerabilities

13. Conclusion

The Logicytics Coding Standards document provides a comprehensive guide for developing high-quality, maintainable, and secure software. By following these guidelines, developers can ensure consistency across projects, improve code readability, and reduce potential bugs and security vulnerabilities.

Remember to regularly review and update these standards to reflect best practices and new technologies. Encourage team members to contribute to the evolution of these standards through continuous feedback and discussion.

By adhering to these coding standards, Logicytics aims to deliver exceptional software solutions while fostering a culture of excellence in software engineering.

This concludes the Logicytics Coding Standards document. Remember to adapt and expand these standards as needed for your specific projects and organizational requirements.