__repr__ is supposed to give a human-readable representation of the object...
Return a string containing a printable representation of an object.
For many types, this function makes an attempt to return a string that would yield an object with the same value when passed to eval(), otherwise the representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object. A class can control what this function returns for its instances by defining a __repr__() method.
You are returning a function.
Try:
>>> class Magic:
...     def __repr__(self):
...         def inner():
...             return "It works!"
...         return inner()
...
>>> repr(Magic())
'It works!'
It's also worth reading up on the @property decorator, and decorators in general... See this answer.
To answer youy "When is the inner() function actually getting called?", see below. It's getting called when repr() calls Magic.__repr__() on your behalf.
#!/usr/bin/env python3
def my_decorator(info):
    def my_dec_inner(self):
        return "Testing"
    return my_dec_inner
class Magic:
    @my_decorator
    def __repr__(self):
        def inner():
            return "It works!"
        return inner
m = Magic()
# these are nearly synonymous
print(m.__repr__())
print(repr(m))
# this is actually a 'bound function' - my_decorator
print(m.__repr__)
Testing
Testing
<bound method Magic.my_dec_inner of Testing>