Skip to main content

Better enums including flags with subsets in which there can be only one... set.

Project description

🗡️ Highlander Enum

Release Build status codecov Commit activity License

"There can be only one!" - A Python library for mutually exclusive flag enums with smart conflict resolution.

Highlander Enum provides ExFlag and OptionsFlag - specialized variations of Python's IntFlag enum designed for scenarios where certain flags must be mutually exclusive. Think command-line options where --verbose and --quiet can't both be active, or configuration settings where only one mode can be selected at a time.

✨ Key Features

  • 🚫 Mutually Exclusive Flags: Define groups where only one flag can be active
  • 🔀 Smart Conflict Resolution: Choose how conflicts are handled (RHS wins, LHS wins, or strict mode)
  • 🏃 Performance Optimized: Uses bitmasks for fast conflict detection during bitwise operations
  • 📋 Command-Line Ready: OptionsFlag includes aliases and help text for CLI tools
  • 🎯 Type Safe: Full type hints and comprehensive test coverage (100%)
  • 🐍 Modern Python: Supports Python 3.11, 3.12, and 3.13

🚀 Quick Start

Installation

pip install highlander-enum

Basic Usage

from highlander import ExFlag

class NetworkMode(ExFlag):
    # Basic flags that can be combined
    IPV4 = 1
    IPV6 = 2
    ENCRYPTION = 4
    COMPRESSION = 8

    # Mutually exclusive verbosity levels
    QUIET = 16
    VERBOSE = 32
    DEBUG = 64, (QUIET, VERBOSE)

# RHS (right-hand side) wins by default
mode = NetworkMode.QUIET | NetworkMode.VERBOSE
print(mode)  # NetworkMode.VERBOSE (VERBOSE wins)

# Non-conflicting flags combine normally
mode = NetworkMode.IPV4 | NetworkMode.ENCRYPTION | NetworkMode.VERBOSE
print(mode)  # NetworkMode.ENCRYPTION|IPV4|VERBOSE

🎛️ Conflict Resolution Strategies

Choose how conflicts should be resolved when mutually exclusive flags are combined:

RHS (Right-Hand Side) - Default

class LogLevel(ExFlag):  # Default: conflict="rhs"
    ERROR = 1
    WARN = 2
    INFO = 4, (ERROR, WARN)

result = LogLevel.ERROR | LogLevel.INFO
print(result)  # LogLevel.INFO (newer value wins)

LHS (Left-Hand Side)

class LogLevel(ExFlag, conflict="lhs"):
    ERROR = 1
    WARN = 2
    INFO = 4, (ERROR, WARN)

result = LogLevel.ERROR | LogLevel.INFO
print(result)  # LogLevel.ERROR (existing value wins)

Strict Mode

class LogLevel(ExFlag, conflict="strict"):
    ERROR = 1
    WARN = 2
    INFO = 4, (ERROR, WARN)

try:
    result = LogLevel.ERROR | LogLevel.INFO
except ValueError as e:
    print(e)  # LogLevel.INFO conflicts with LogLevel.ERROR

🖥️ Command-Line Options with OptionsFlag

Perfect for building CLI tools with rich help text and aliases:

from highlander import OptionsFlag

class ServerOptions(OptionsFlag):
    # Format: VALUE, [aliases], "help text", [exclusions]
    VERBOSE = 1, ["v", "verbose"], "Enable verbose logging"
    QUIET = 2, ["q", "quiet"], "Suppress all output"
    DEBUG = 4, ["d", "debug"], "Enable debug mode", (VERBOSE, QUIET)

    # Different tuple formats supported
    DAEMON = 8, "Run as daemon"  # Just help text
    CONFIG = 16, ["c", "config"], "Specify config file"  # No exclusions
    FORCE = 32, "Force operation", (DAEMON,)  # Help + exclusions

# Access help text and aliases
opt = ServerOptions.VERBOSE
print(f"Help: {opt.help}")      # Help: Enable verbose logging
print(f"Aliases: {opt.aliases}")  # Aliases: ['v', 'verbose']

# Smart conflict resolution
flags = ServerOptions.QUIET | ServerOptions.DEBUG
print(flags)  # ServerOptions.DEBUG (DEBUG wins over QUIET)

🔧 Advanced Usage

Dynamic Exclusions

Add exclusions at runtime:

class DynamicFlag(ExFlag):
    A = 1
    B = 2
    C = 4

flag_a = DynamicFlag.A
flag_a.add_exclusions(DynamicFlag.B, DynamicFlag.C)

result = DynamicFlag.A | DynamicFlag.B
print(result)  # DynamicFlag.A (conflicts resolved)

Multiple Exclusion Groups

Create complex relationships between different groups of flags:

class UITheme(ExFlag):
    # Color schemes (mutually exclusive)
    DARK = 1
    LIGHT = 2
    HIGH_CONTRAST = 4, (DARK, LIGHT)

    # Size options (separate exclusion group)
    SMALL = 8
    MEDIUM = 16
    LARGE = 32, (SMALL, MEDIUM)

    # Independent options (no conflicts)
    ANIMATIONS = 64
    SOUND_EFFECTS = 128

# Mix and match from different groups
theme = UITheme.DARK | UITheme.LARGE | UITheme.ANIMATIONS
print(theme)  # UITheme.ANIMATIONS|DARK|LARGE

# Conflicts within groups are resolved
theme = UITheme.DARK | UITheme.LIGHT | UITheme.SMALL
print(theme)  # UITheme.LIGHT|SMALL (LIGHT wins over DARK)

Working with Integer Values

# Create flags from integer values with automatic conflict resolution
mixed_flags = NetworkMode(1 | 16 | 32)  # IPV4 + QUIET + VERBOSE
print(mixed_flags)  # NetworkMode.IPV4|QUIET (conflicts resolved)

# Check if flags are set
if NetworkMode.IPV4 in mixed_flags:
    print("IPv4 is enabled")

🛡️ Type Safety & IDE Support

Highlander Enum provides full type hints for excellent IDE support:

from highlander import ExFlag

class StatusFlag(ExFlag):
    IDLE = 1
    BUSY = 2, (IDLE,)
    ERROR = 4

def process_status(status: StatusFlag) -> str:
    if status & StatusFlag.ERROR:
        return "Error occurred"
    elif status & StatusFlag.BUSY:
        return "Currently busy"
    else:
        return "Ready"

# IDE will provide autocompletion and type checking
result = process_status(StatusFlag.BUSY | StatusFlag.ERROR)
print(result)  # "Error occurred"

📊 Performance

Highlander Enum is designed for performance with bitwise operations:

import timeit
from highlander import ExFlag

class PerfTest(ExFlag):
    A = 1
    B = 2, (A,)
    C = 4
    D = 8

# Fast bitwise operations with conflict resolution
def test_operations():
    return PerfTest.A | PerfTest.B | PerfTest.C

# Benchmark shows minimal overhead compared to standard IntFlag
print(f"Time per operation: {timeit.timeit(test_operations, number=100000):.6f}s")

🧪 Real-World Examples

File Processing Tool

from highlander import OptionsFlag

class FileProcessor(OptionsFlag):
    # Output verbosity (mutually exclusive)
    SILENT = 1, ["s", "silent"], "No output"
    NORMAL = 2, ["n", "normal"], "Normal output"
    VERBOSE = 4, ["v", "verbose"], "Verbose output", (SILENT, NORMAL)

    # Processing modes (mutually exclusive)
    FAST = 8, ["f", "fast"], "Fast processing"
    ACCURATE = 16, ["a", "accurate"], "Accurate processing", (FAST,)

    # Independent options
    BACKUP = 32, ["b", "backup"], "Create backups"
    COMPRESS = 64, ["c", "compress"], "Compress output"

def process_files(options: FileProcessor):
    if options & FileProcessor.VERBOSE:
        print("Verbose mode enabled")
    if options & FileProcessor.BACKUP:
        print("Creating backups")

# Usage
opts = FileProcessor.VERBOSE | FileProcessor.ACCURATE | FileProcessor.BACKUP
process_files(opts)

Game Settings

from highlander import ExFlag

class GraphicsSettings(ExFlag):
    # Quality levels (mutually exclusive)
    LOW = 1
    MEDIUM = 2
    HIGH = 4
    ULTRA = 8, (LOW, MEDIUM, HIGH)

    # Independent features
    VSYNC = 16
    HDR = 32
    ANTIALIASING = 64

class GameConfig:
    def __init__(self):
        self.graphics = GraphicsSettings.MEDIUM | GraphicsSettings.VSYNC

    def upgrade_quality(self):
        # Automatically resolves conflicts
        self.graphics |= GraphicsSettings.HIGH

    def toggle_hdr(self):
        self.graphics ^= GraphicsSettings.HDR

config = GameConfig()
print(config.graphics)  # GraphicsSettings.VSYNC|MEDIUM

config.upgrade_quality()
print(config.graphics)  # GraphicsSettings.VSYNC|HIGH (conflict resolved)

🏗️ Development

Requirements

  • Python 3.11+
  • uv (recommended) or pip

Setup

git clone https://github.com/florean/highlander-enum.git
cd highlander-enum
make install  # Sets up virtual environment and pre-commit hooks

Testing

make test          # Run pytest with coverage
make check         # Run all quality checks (linting, type checking, etc.)
tox               # Test across multiple Python versions

Building Documentation

make docs         # Serve documentation locally
make docs-test    # Test documentation build

📈 Project Roadmap

For 1.0

  • New conflict resolutions: smallest wins and largest wins
  • More robust constraint specification at member definition
  • Better CLI integration for OptionsFlag - more helper methods or parser-specific subclasses
  • Solidify internal API and naming
  • More real-world usage

Future Enhancements

  • Allow inheriting from and extending existing enums

🤝 Contributing

Contributions are welcome! This project maintains 100% test coverage because reliability is paramount. Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add comprehensive tests for new functionality (we can help!)
  4. Ensure all tests pass and coverage remains 100%
  5. Submit a pull request

For bug reports, please open an issue. For feature requests or discussions about potential enhancements, start a discussion.

📄 License

This project is licensed under the Apache 2.0 License - see the LICENSE file for details.

🔗 Links


"In the end, there can be only one... flag active in each subset of flags, unless you use add_exclusions and only apply it to one side, in which case yo—🗡️"

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

highlander_enum-0.9.1.tar.gz (99.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

highlander_enum-0.9.1-py3-none-any.whl (19.6 kB view details)

Uploaded Python 3

File details

Details for the file highlander_enum-0.9.1.tar.gz.

File metadata

  • Download URL: highlander_enum-0.9.1.tar.gz
  • Upload date:
  • Size: 99.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.6.14

File hashes

Hashes for highlander_enum-0.9.1.tar.gz
Algorithm Hash digest
SHA256 33d672ce0ccb231114cb67cb170b66bbc4542823a33f9a525ae62abf58b08e8e
MD5 d692be2b7c872bfa2b11f03cffcf19be
BLAKE2b-256 3e9f55e006ce29c589f10905ca3bfe7c8d7f51d2fc9f9763d1afdea35ea9282d

See more details on using hashes here.

File details

Details for the file highlander_enum-0.9.1-py3-none-any.whl.

File metadata

File hashes

Hashes for highlander_enum-0.9.1-py3-none-any.whl
Algorithm Hash digest
SHA256 53b355efa0f9884757ae1f6a90a3889873656d8e7c2e76abb8b942b97db276a9
MD5 50f97e2dc3153dbb584772453a67922c
BLAKE2b-256 332c47adcb06723a75f952326c86e4f4229a627f95d6f23624816848f5dfc9ca

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page