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.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.13+
  • 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.0.tar.gz (85.9 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.0-py3-none-any.whl (19.0 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for highlander_enum-0.9.0.tar.gz
Algorithm Hash digest
SHA256 36c2d1e27782165c95dbb737b2f229700347b869b20a35bf699168b8c54b386b
MD5 2a1c2329f0be2de2f3dd7fd1de403853
BLAKE2b-256 24f122541bb83fcc218402c17b203ac95045174dfece4513e4a15b6e1d6fa1f8

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for highlander_enum-0.9.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7785b8550c0486ed60926332991497ab956f604a3375ff1bfcc167cd9e3f83c4
MD5 ee726ca49aedec1907e5337c1d240308
BLAKE2b-256 6fd122f8ef4c6b89f7470c4fe39bd86cb8c872e9ca998010e83aac60a8085e0a

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