Counting transparently

Step 5: Counting transparently

A decorator does not have to change the data at all. It can add invisible behavior like logging, metrics or caching while passing the payload straight through. This is how you slot instrumentation into a stream pipeline without touching the source or the caller.

Implement Counting. Its constructor (already provided) sets self.calls to 0. Its read() must increment self.calls by one and then return the wrapped text completely unchanged. To the pipeline it is transparent; on the side it records how many times read() was invoked. Because it keeps the same interface, it can sit anywhere in a stack of decorators and the output is unchanged.

Hints
Starter code
class Source:
    def __init__(self, text):
        self.text = text

    def read(self):
        return self.text


class Decorator:
    def __init__(self, wrapped):
        self.wrapped = wrapped

    def read(self):
        return self.wrapped.read()


class UpperCase(Decorator):
    def read(self):
        return self.wrapped.read().upper()


class Reverse(Decorator):
    def read(self):
        return self.wrapped.read()[::-1]


class Repeat(Decorator):
    def __init__(self, wrapped, times):
        super().__init__(wrapped)
        self.times = times

    def read(self):
        return self.wrapped.read() * self.times


class Counting(Decorator):
    def __init__(self, wrapped):
        super().__init__(wrapped)
        self.calls = 0

    def read(self):
        """Count this call, then delegate unchanged to the wrapped source."""
        # TODO:
        #   self.calls += 1
        #   return self.wrapped.read()
        pass
Write it on a laptop

The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.