"""
Provides functions for creating simple properties.

If, inside a class definition, you write:

    attribute(foo=1, bar=2)

simple properties named 'foo' and 'bar' are created for this class.
Also, private instance variables '__foo' and '__bar' will be added
to instances of this class.

USEAGE:

# assumes attribute.py is on path
from attribute import *

class MyClass(object):
    readable(foo=1, bar=2) # or, attribute('r', foo=1, bar=2)
    writable(fro=3, boz=4) # or, attribute('w', fro=3, boz=4)
    attribute(baz=5)

This is equivalent to the following:

class MyClass(object):
    def __init__(self):
        self.__foo = 1
        self.__bar = 2
        self.__fro = 3
        self.__boz = 4
        self.__baz = 5

    def get_foo(self):
        return self.__foo
    def get_bar(self):
        return self.__bar
    def set_fro(self, value):
        self.__fro = value
    def set_boz(self, value):
        self.__boz = value
    def get_baz(self):
        return self.__baz
    def set_baz(self, value):
        self.__baz = value
    def del_baz(self):
        del self.__baz

    foo = property(fget=get_foo, doc="foo")
    bar = property(fget=get_bar, doc="bar")
    fro = property(fset=set_fro, doc="fro")
    boz = property(fset=set_boz, doc="boz")
    baz = property(fget=get_baz, fset=set_baz, fdel=del_baz, doc="baz")
"""

__all__ = ["attribute", "readable", "writable"]
__version__ = "3.0"
__author__ = "Sean Ross"
__credits__ = ["Guido van Rossum", "Garth Kidd"]
__created__ = "10/21/02"

import sys


def mangle(classname, attrname):
    """mangles name according to python name-mangling
    conventions for private variables"""
    return f"_{classname}__{attrname}"


def class_space(classlevel=3):
    "returns the calling class' name and dictionary"
    frame = sys._getframe(classlevel)
    classname = frame.f_code.co_name
    classdict = frame.f_locals
    return classname, classdict


# convenience function


def readable(**kwds):
    "returns one read-only property for each (key,value) pair in kwds"
    return _attribute(permission="r", **kwds)


# convenience function


def writable(**kwds):
    "returns one write-only property for each (key,value) pair in kwds"
    return _attribute(permission="w", **kwds)


# needed because of the way class_space is resolved in _attribute


def attribute(permission="rwd", **kwds):
    """returns one property for each (key,value) pair in kwds;
    each property provides the specified level of access(permission):
        'r': readable, 'w':writable, 'd':deletable
    """
    return _attribute(permission, **kwds)


# based on code by Guido van Rossum, comp.lang.python 2001-07-31


def _attribute(permission="rwd", **kwds):
    """returns one property for each (key,value) pair in kwds;
    each property provides the specified level of access(permission):
        'r': readable, 'w':writable, 'd':deletable
    """
    classname, classdict = class_space()

    def _property(attrname, default):
        propname, attrname = attrname, mangle(classname, attrname)
        if "r" in permission:

            def fget(self):
                value = default
                try:
                    value = getattr(self, attrname)
                except AttributeError:
                    setattr(self, attrname, default)
                return value

        else:
            fget = None

        if "w" in permission:

            def fset(self, value):
                setattr(self, attrname, value)

        else:
            fset = None

        if "d" in permission:

            def fdel(self):
                try:
                    delattr(self, attrname)
                except AttributeError:
                    pass
                # calling fget can restore this attribute, so remove property
                delattr(self.__class__, propname)

        else:
            fdel = None
        return property(fget=fget, fset=fset, fdel=fdel, doc=propname)

    for attrname, default in kwds.items():
        classdict[attrname] = _property(attrname, default)
