Python decorator to support static properties
You've masked a bug, in __setattr__ a property raises an AttributeError if the setter hasn't been defined. This causes you to overwrite the property.
(As you've said) There's no distinction between a class property and an instance property. You can change it so there is, but it doesn't allow the property to only be defined on the class, not the instance.
You can just define the static properties on the metaclass. This removes a lot of the headaches.
If you really want to define everything onto the class not the metaclass then you can make the metaclass hoist the wanted functions into a new metaclass. This means everything works as if you only defined two metaclasses with the properties correctly defined.


No fancy changes:

class MyMeta(type):
    @property
    def class_(self):
        return self._class

    @class_.setter
    def class_(self, value):
        self._class = value

    @property
    def class_instance(self):
        return self._class_instance

    @class_instance.setter
    def class_instance(self, value):
        self._class_instance = value

class Test(metaclass=MyMeta):
    class_instance = MyMeta.class_instance

    @property
    def instance(self):
        return self._instance

    @instance.setter
    def instance(self, value):
        self._instance = value


Hoisting:

class classproperty(property):
    pass


class classinstanceproperty(property):
    pass


class StaticProperty(type):
    def __new__(self, name, bases, props):
        class_properties = {}
        to_remove = {}
        for key, value in props.items():
            if isinstance(value, (classproperty, classinstanceproperty)):
                class_properties[key] = value
                if isinstance(value, classproperty):
                    to_remove[key] = value

        for key in to_remove:
            props.pop(key)

        HoistMeta = type('HoistMeta', (type,), class_properties)
        return HoistMeta(name, bases, props)


class Test(metaclass=StaticProperty):
    @classproperty
    def class_(self):
        return self._class

    @class_.setter
    def class_(self, value):
        self._class = value

    @classinstanceproperty
    def class_instance(self):
        return self._class_instance

    @class_instance.setter
    def class_instance(self, value):
        self._class_instance = value

    @property
    def instance(self):
        return self._instance

    @instance.setter
    def instance(self, value):
        self._instance = value




These both pass the following tests: (I could only get your approach to work with instance and class instance)


test = Test()
test._instance = None
test.instance = True
assert test._instance is True
assert test.instance is True
test.instance = False
assert test._instance is False
assert test.instance is False

Test._instance = None
Test.instance = True
Test.instance = False
assert Test._instance is None
test._instance = True
if Test._instance is not True:
    print("instance can't be used after modifying class")

Test._class_instance = None
Test.class_instance = True
assert Test._class_instance is True
Test.class_instance = False
assert Test._class_instance is False

test = Test()
test._class_instance = None
test.class_instance = True
assert test._class_instance is True
assert Test._class_instance is False
test.class_instance = False
assert test._class_instance is False

Test._class = None
Test.class_ = True
assert Test._class is True
Test.class_ = False
assert Test._class is False

test = Test()
test._class = None
test.class_ = True
assert test._class is None
assert Test._class is False
test.class_ = False
assert test._class is None