SkillAgentSearch skills...

Yuppy

Python Programming for the Privileged Class

Install / Use

/learn @kuujo/Yuppy
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Yuppy

Python Programming for the Privileged Class


Yuppy is released under the MIT License.

Yuppy is a small Python library that integrates seamlessly with your application to promote data integrity by supporting common object-oriented language features. It intends to provide fully integrated support for interfaces, abstract classes and methods, final classes and methods, and type hinting in a manner that preserves much of the dynamic nature of Python. Yuppy can improve the integrity of your data and the stability of your code without comprimising usability. It is easy to use and is intentionally designed to fit with the Python development culture, not circumvent it.

Table of contents


  1. Introduction
  2. A Complete Example
  3. Class Decorators
  4. Member Decorators
  5. Interfaces
  6. Type Hinting
"But type checking is bad!"

Yuppy does type checking in a manner that is in keeping with the dynamic nature of Python. Yuppy interface checks can be based on duck-typing, so any class can serve as a Yuppy interface. This feature simply serves as a more efficient way to determine whether any given object walks and talks like a duck.

A Complete Example

from yuppy import *

# Yuppy classes must either use the base yuppy.ClassType metaclass or use
# the @yuppy.yuppy decorator.
# In this case, we're creating an abstract base class "Apple" with a
# couple of abstract methods for getting attributes.
@abstract
class Apple(object):
  """An abstract apple."""
  @abstract
  def get_color(self):
    """Gets the apple color."""

  @abstract
  def set_color(self):
    """Sets the apple color."""

# Create an interface for objects that can be eaten.
@interface
class IEatable(object):
  """An interface that supports eating."""
  def eat(self):
    """Eats an apple."""

# Now, we can implement a concret "GreenApple" class. Note that we must
# implement the abstract get_color() and set_color() methods or else we
# have to explicitly declare the class once again @abstract. Similarly,
# we are implementing the IEatable interface, so we must implement all
# the IEatable methods or else declare the class abstract.
@implements(IEatable)
class GreenApple(Apple):
  """A concrete green apple."""
  # Don't allow the green apple color to be changed.
  color = const('green')

  # Create a float weight.
  weight = var(float)

  # Override the get_color() abstract method.
  def get_color(self):
    return self.color

  # Override the set_color() abstract method. We'll just raise an error.
  def set_color(self, value):
    raise AttributeError("Cannot set green apple color.")

  # Implement a method to set the green apple weight. Here we use type
  # hinting to ensure that the weight argument is an integer or float.
  # Note that we can also use interfaces for type hinting, including any
  # class that does not extend the yuppy.Interface class (which results
  # in an attribute-based comparison, e.g. duck typing).
  @params(weight=(int, float))
  def set_weight(self, weight):
    self.weight = weight

# Implement an implicit interface.
class ITree(object):
  def add_apple(self, apple):
    """Adds an apple to the tree."""
  def remove_apple(self, apple):
    """Removes an apple from the tree."""

# Even without extending the Interface class, we can use ITree as an interface.
@implements(ITree)
class Tree(object):
  apples = var(set)

  def __init__(self):
    self.apples = set()

  # We can use any class or interface for type hinting. If an Apple instance
  # is not passed as the 'apple' argument, the argument will be compared to
  # the Apple class to determine whether it is equivalent by attributes.
  @params(apple=Apple)
  def add_apple(self, apple):
    self.apples.add(apple)

  @params(apple=Apple)
  def remove_apple(self, apple):
    self.apples.remove(apple)

Of course, in the real world this would be an unrealistic example. Python's flexibility and features like data descriptors remove the need for getters and setters that are necessary in other languages (this is one reason that Yuppy does not attempt encapsulation). But, indeed, these features can be very useful in ultimately reducing the code required for error handling by helping ensure the integrity of data from the time it is set on an object or passed to an instance method.

Class Decorators

yuppy

Declares a Yuppy class definition.

yuppy(cls)

This decorator is not required to implement a Yuppy class. The recommended alternative to using the yuppy decorator is to use the yuppy.ClassType metaclass in your class definition. The decorator simply dynamically extends any class to use the yuppy.ClassType metaclass in its definition.

from yuppy import ClassType, yuppy

@yuppy
class Apple(object):
  """This is a Yuppy class."""

class Apple(object):
  """This is also a Yuppy class."""
  __metaclass__ = ClassType

abstract

Creates an abstract class.

abstract(cls)

Abstract classes are classes that cannot themselves be instantiated, but can be extended and instantiated. An abstract class can contain any number of abstract methods. When an abstract class is extended, the extending class must override all the abstract methods or else declare itself abstract.

Example
from yuppy import abstract

@abstract
class Apple(object):
  """An abstract apple."""
  weight = var(float)

  def get_weight(self):
    return self.weight

  def set_weight(self, weight):
    self.weight = weight

class GreenApple(Apple):
  """A concrete green apple."""

We will be able to create instances of GreenApple, which inherits from Apple, but any attempts to instantiate an Apple will result in a TypeError.

>>> apple = GreenApple()
>>> apple.set_weight(1.0)
>>> apple.get_weight()
1.0
>>> apple = Apple()
TypeError: Cannot instantiate abstract class 'Apple'.

final

Declares a class definition to be final.

The final Yuppy decorator is, well, final, which allows users to define classes that cannot be extended. This is a common feature in several other object-oriented languages.

final(cls)
Example
from yuppy import final

@final
class Apple(object):
  weight = var(float, default=None)
>>> apple = Apple()
>>> class GreenApple(Apple):
...   pass
...
TypeError: ...

Member Decorators

variable

Creates a variable attribute.

variable([default=None[, validate=None[, *types]]])
var([default=None[, validate=None[, *types]]])
Example
from yuppy import yuppy, var

@yuppy
class Apple(object):
  foo = var(int, default=None, validate=lambda x: x == 1)
>>> apple = Apple()

static

Creates a static attribute.

Static Yuppy members are equivalent to standard Python class members. This is essentially the same parallel that exists between Python's class members and static variables in many other object-oriented languages. With Yuppy we can use the static decorator to create static methods or properties.

static([default=None[, validate=None[, *types]]])
Example
from yuppy import yuppy, static

@yuppy
class Apple(object):
  """An abstract apple."""
  weight = static(float, default=None)

With static members, changes to a member variable will be applied to all instances of the class. So, even after instantiating a new instance of the class, the weight attribute value will remain the same.

>>> apple1 = Apple()
>>> apple1.weight
None
>>> apple1.weight = 2.0
>>> apple1.weight
2.0
>>> apple2 = Apple()
>>> apple2.weight
2.0

constant

Creates a constant attribute.

Constants are attributes which have a permanent value. They can be used for any value which should never change within the application, such as an application port number, for instance. With Yuppy we can use the const decorator to create a constant, passing a single permanent value to the constructor.

constant(value)
const(value)
Example
from yuppy import yuppy, const

@yuppy
class RedApple(object):
  color = const('red')
>>> RedApple.color
'red'
>>> apple = RedApple()
>>> apple.color
'red'
>>> RedApple.color = 'blue'
AttributeError: Cannot override 'RedApple' attribute 'color' by assignment.
>>> RedApple.color
'red'
>>> apple.color
'red'
>>> apple = RedApple()
>>> apple.color
'red'
>>> apple.color = 'blue'
AttributeError: Cannot override 'Apple' attribute 'color' by assignment.

method

Creates a method attribute.

method(callback)
Example
from yuppy import yuppy, var, method

@yuppy
class Apple(object):
  color = var(default='red')

  @method
  def getcolor(self):
    return self.color
>>> apple = Apple()
>>> apple.getcolor()
'red'

abstract

Creates an abstract method.

abstract(method)

Abstract methods can be applied to any python class, even without declaring the class to be abstract. This means that if the method is not re-defined in a child class, an AttributeError will be raised if the abstract method is accessed. Therefore, it is strongly recommended that any class that contains abstract methods be declared abstract.

Example
from yuppy import abstract

@abstract
class Apple(object):
  """An abstract apple."""
  @abstract
  def get_color(self):
    """Gets the appl
View on GitHub
GitHub Stars92
CategoryDevelopment
Updated10mo ago
Forks3

Languages

Python

Security Score

87/100

Audited on May 9, 2025

No findings