CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-six

Python 2 and 3 compatibility utilities

Pending
Overview
Eval results
Files

metaclass.mddocs/

Metaclass and Decorator Utilities

Utilities for working with metaclasses and creating decorators that work across Python versions. These functions provide unified interfaces for metaclass usage and decorator creation that handle the syntax differences between Python 2 and 3.

Capabilities

Metaclass Utilities

Functions for creating classes with metaclasses in a cross-version compatible way.

def with_metaclass(meta: type, *bases: type) -> type
    """Create a base class with a metaclass."""

def add_metaclass(metaclass: type) -> Callable[[type], type]  
    """Class decorator for adding a metaclass."""

Usage Examples:

import six

# Method 1: Using with_metaclass for base class creation
class MyMeta(type):
    def __new__(cls, name, bases, dct):
        # Add custom behavior
        dct['created_by'] = 'MyMeta'
        return super(MyMeta, cls).__new__(cls, name, bases, dct)

# Create base class with metaclass
BaseClass = six.with_metaclass(MyMeta, object)

class MyClass(BaseClass):
    pass

print(MyClass.created_by)  # "MyMeta"

# Method 2: Using add_metaclass decorator
@six.add_metaclass(MyMeta)
class AnotherClass(object):
    def method(self):
        return "Hello"

print(AnotherClass.created_by)  # "MyMeta"

Decorator Utilities

Cross-version decorator utilities including functools.wraps replacement.

def wraps(wrapped: Callable) -> Callable[[Callable], Callable]
    """Decorator to wrap a function with functools.wraps behavior."""

This provides functools.wraps functionality across Python versions, preserving function metadata when creating decorators.

Usage Examples:

import six

# Create a decorator using six.wraps
def my_decorator(func):
    @six.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        result = func(*args, **kwargs)
        print(f"Finished {func.__name__}")
        return result
    return wrapper

@my_decorator
def greet(name):
    """Greet someone by name."""
    return f"Hello, {name}!"

# Function metadata is preserved
print(greet.__name__)  # "greet"
print(greet.__doc__)   # "Greet someone by name."
result = greet("Alice")  # Prints: Calling greet, Hello, Alice!, Finished greet

Unicode Compatibility Decorator

Class decorator for Python 2 unicode string compatibility.

def python_2_unicode_compatible(cls: type) -> type
    """Class decorator for unicode compatibility in Python 2."""

This decorator allows classes to define __str__ methods that return unicode strings in Python 2 and regular strings in Python 3.

Usage Example:

import six

@six.python_2_unicode_compatible
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __str__(self):
        # This will work correctly in both Python 2 and 3
        return f"Person(name='{self.name}', age={self.age})"
    
    def __repr__(self):
        return f"Person({self.name!r}, {self.age!r})"

# Works with unicode characters in both versions
person = Person("José", 30)
print(str(person))  # Handles unicode correctly
print(repr(person))

Advanced Metaclass Patterns

Custom Metaclass with six.with_metaclass

import six

class SingletonMeta(type):
    """Metaclass that creates singleton instances."""
    _instances = {}
    
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(SingletonMeta, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

# Create singleton base class
SingletonBase = six.with_metaclass(SingletonMeta, object)

class DatabaseConnection(SingletonBase):
    def __init__(self):
        self.connection_id = id(self)
    
    def connect(self):
        return f"Connected with ID: {self.connection_id}"

# Test singleton behavior
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2)  # True
print(db1.connect())  # Same connection ID

Registry Metaclass Pattern

import six

class RegistryMeta(type):
    """Metaclass that maintains a registry of all created classes."""
    registry = {}
    
    def __new__(cls, name, bases, dct):
        new_class = super(RegistryMeta, cls).__new__(cls, name, bases, dct)
        if name != 'BaseRegistered':  # Skip base class
            cls.registry[name] = new_class
        return new_class
    
    @classmethod
    def get_class(cls, name):
        return cls.registry.get(name)
    
    @classmethod
    def list_classes(cls):
        return list(cls.registry.keys())

# Create base class with registry metaclass
BaseRegistered = six.with_metaclass(RegistryMeta, object)

class WidgetA(BaseRegistered):
    pass

class WidgetB(BaseRegistered):
    pass

# Access registry
print(RegistryMeta.list_classes())  # ['WidgetA', 'WidgetB']
widget_class = RegistryMeta.get_class('WidgetA')
widget = widget_class()

Attribute Validation Metaclass

import six

class ValidatedMeta(type):
    """Metaclass that adds attribute validation."""
    
    def __new__(cls, name, bases, dct):
        # Find validation rules
        validators = {}
        for key, value in list(dct.items()):
            if key.startswith('validate_'):
                attr_name = key[9:]  # Remove 'validate_' prefix
                validators[attr_name] = value
                del dct[key]  # Remove validator from class dict
        
        # Create the class
        new_class = super(ValidatedMeta, cls).__new__(cls, name, bases, dct)
        new_class._validators = validators
        
        # Override __setattr__ if not already defined
        if '__setattr__' not in dct:
            new_class.__setattr__ = cls._validated_setattr
        
        return new_class
    
    @staticmethod
    def _validated_setattr(self, name, value):
        if hasattr(self.__class__, '_validators') and name in self.__class__._validators:
            validator = self.__class__._validators[name]
            validator(self, value)
        object.__setattr__(self, name, value)

# Use the validation metaclass
@six.add_metaclass(ValidatedMeta)
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def validate_age(self, value):
        if not isinstance(value, int) or value < 0:
            raise ValueError("Age must be a non-negative integer")
    
    def validate_name(self, value):
        if not isinstance(value, six.string_types) or len(value) == 0:
            raise ValueError("Name must be a non-empty string")

# Test validation
person = Person("Alice", 30)
person.age = 25  # OK
try:
    person.age = -5  # Raises ValueError
except ValueError as e:
    print(f"Validation error: {e}")

Common Usage Patterns

import six

# Abstract base class pattern with metaclass
class AbstractMeta(type):
    """Metaclass for creating abstract base classes."""
    
    def __new__(cls, name, bases, dct):
        # Collect abstract methods
        abstract_methods = set()
        for base in bases:
            if hasattr(base, '_abstract_methods'):
                abstract_methods.update(base._abstract_methods)
        
        for key, value in dct.items():
            if getattr(value, '_abstract', False):
                abstract_methods.add(key)
            elif key in abstract_methods:
                abstract_methods.discard(key)  # Implemented
        
        new_class = super(AbstractMeta, cls).__new__(cls, name, bases, dct)
        new_class._abstract_methods = frozenset(abstract_methods)
        return new_class
    
    def __call__(cls, *args, **kwargs):
        if cls._abstract_methods:
            raise TypeError(f"Can't instantiate abstract class {cls.__name__} "
                          f"with abstract methods {', '.join(cls._abstract_methods)}")
        return super(AbstractMeta, cls).__call__(*args, **kwargs)

def abstract_method(func):
    """Decorator to mark methods as abstract."""
    func._abstract = True
    return func

# Create abstract base class
@six.add_metaclass(AbstractMeta) 
class Shape:
    @abstract_method
    def area(self):
        pass
    
    @abstract_method
    def perimeter(self):
        pass
    
    def describe(self):
        return f"Shape with area {self.area()} and perimeter {self.perimeter()}"

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height
    
    def perimeter(self):
        return 2 * (self.width + self.height)

# Usage
rect = Rectangle(5, 3)
print(rect.describe())  # Shape with area 15 and perimeter 16

# This would raise TypeError:
# shape = Shape()  # Can't instantiate abstract class

# Mixin pattern with metaclass support
class LoggingMixin:
    """Mixin that adds logging capabilities."""
    
    def log(self, message):
        class_name = self.__class__.__name__
        six.print_(f"[{class_name}] {message}")

@six.add_metaclass(ValidatedMeta)
class LoggedPerson(LoggingMixin):
    def __init__(self, name, age):
        self.name = name  
        self.age = age
        self.log(f"Created person: {name}, age {age}")
    
    def validate_age(self, value):
        if not isinstance(value, int) or value < 0:
            self.log(f"Invalid age validation: {value}")
            raise ValueError("Age must be a non-negative integer")
    
    def celebrate_birthday(self):
        old_age = self.age
        self.age += 1
        self.log(f"Happy birthday! Age changed from {old_age} to {self.age}")

# Usage
person = LoggedPerson("Bob", 25)  # [LoggedPerson] Created person: Bob, age 25
person.celebrate_birthday()       # [LoggedPerson] Happy birthday! Age changed from 25 to 26

Install with Tessl CLI

npx tessl i tessl/pypi-six

docs

execution.md

index.md

iterator-dict.md

metaclass.md

moves.md

string-bytes.md

testing.md

version-detection.md

tile.json