Out of the box Stackoverflow Watcher does nothing special. It doesn’t even perform any type of filtering. The components that make up the underlying library are intentionally very bare-bones. This is because any custom functionality is added by subclassing one of these components. This gives you a lot of flexibility while at the same time, keeps everything very simple.
The `tag` argument to the “stack-watcher” command and Retriever class does not do any filtering, it simply grabs the feed from Stack Overflow for that specific tag. Real filtering is achieved through subclassing the Question class.
Components and Subclassing¶
The two main components can be found in the stack_watcher package.
A single Question (or Question subclass) instance represents an individual question on Stack Overflow. Question objects have the ability to ‘verify’ that they are relevant by comparing themselves to a set of rules. If all of the rules return True, then the question object is considered relevant.
A rule is any Python property in a Question subclass that begins with “is_”. This is inspired by the same technique used in unit testing with Python.
To run all of the rules in a question object, you can check the adheres_to_rules property. This will only return True if there are no rules or all of the rules return True.
If we had this Question subclass:
from stack_watcher import Question class MammothQuestion(Question): @property def is_asked_by_a_woolly_mammoth(self): return False
It would never be considered a relevant question:
>>> question = MammothQuestion() >>> question.adheres_to_rules False
This class is responsible for retrieving the questions from Stack Overflow. One of the reasons to subclass this would be to add functionality that helps make the retrieval more reliable, for instance: handling throttling or authentication.
In this example the MyQuestion class will only consider a question relevant if it’s unanswered and has 25 or more views.
from stack_watcher import Question class MyQuestion(Question): @property def is_not_answered(self): return not self.answered @property def is_eye_catching(self): return self.view_count >= 25
If I save this code into a file called questions.py I’ll be able to run the following command from the terminal to begin fetching questions that match this criteria:
$ stack-watcher --question questions.MyQuestion
A useful reason to subclass Retriever is to override the throttled() method and do something useful. For instance, instead of just waiting for the time out period to end - we could change the IP address and try again.
class ThrottleRetriever(Retriever): def throttled(self): self.change_ip_address() time.sleep(5) def change_ip_address(self): call(['sudo', 'systemctl', 'restart', 'openvpn@random'])
The change_ip_address() method could implement anything you need to do on your system to change the IP address. You could even add proxy server support here.