First, the `docstring` goes inside of the class definition:

    class Circle(Shape):
        """
        Circle class attributes must be:
        radius=x
        """
        ...

This allows you to e.g. call `help(Circle)` in an interactive shell and get back this string. This is documented in [PEP257][4].


----------

To answer your question how the user is to know what the parameters are, just make them explicit in the constructors:

    class Circle(Shape):
        def __init__(self, radius):
            super().__init__(radius=radius)
            self.area = pi * radius**2
            self.perimeter = 2 * pi * radius

This way it is actually useful to override the `__init__` method. If all you do is call the parents constructor (without any change of arguments) you could just leave it out.

Note that I also moved the calculation of perimeter and area into the constructor. This way the class is usable right after construction and you don't need to call some arcane calculate this property function.

I also fixed your calculation for the perimeter and area of the circle.


----------


Normally I would probably make these an actual `property`:

    class Circle(Shape):
        def __init__(self, radius):
            super().__init__(radius=radius)
    
        @property
        def area(self):
            return pi * self.radius**2
    
        @property
        def perimeter(self):
            return 2 * pi * self.radius

But this has the disadvantage that you cannot discover it from the `__dict__` anymore. But then again, these are two fixed names, so you could hardcode them in `Shape.__str__`:

    class Shape:
        def __init__(self, **kwargs):
            for key, value in kwargs.items():
                setattr(self, key, value)
    
        def __str__(self):
            all_info = [self.__class__.__name__]
            for attribute, value in self.__dict__.items():
                if value > 0:
                    all_info.append(f"{attribute}: {value:.2f}")
            all_info.append(f"{area}: {self.area:.2f}")
            all_info.append(f"{perimeter}: {self.perimeter:.2f}")
            return "\n".join(all_info)

Note that I removed your string addition, because [it is very costly in Python][1] (strings are immutable, so adding two strings involves allocating a new string of the correct size and copying the content of the two strings there), especially if you do many of them, as you do here. Instead I used a list of strings, which I [`str.join`][2] in the end.

I also used the new [`f-string`][3] to make the formatting a lot easier to read.



  [1]: https://softwareengineering.stackexchange.com/questions/304445/why-is-s-better-than-for-concatenation
  [2]: https://www.tutorialspoint.com/python/string_join.htm
  [3]: https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-pep498
  [4]: https://www.python.org/dev/peps/pep-0257/