Simply patch what is unextendable.
An issue, that i can't extend a method __str__
, was encountered when i retrieved a list of objects from a third-party service and printed them to see the details, but i got uninformative output. Due to that, i dove into source code to figure out what can i get and came to a point that only tittle, text
attributes are printing of those objects and the method can't be extended, as a client library doesn't provide customization and it shouldn't indeed.
A rough example of objects retrieving.
from client_library import fetch_entities
def fecth_print():
entities = fetch_entities()
print(entities) # Print to see the details and examine the entities.
# Output is minimalistic:
# "Shop, Buy food"
# "Gym, Do squats"
Base class with __str__
method the entities
are of.
class Note(TopLevelNode):
"""Represents a Google Keep note."""
_TYPE = NodeType.Note
def __init__(self, **kwargs):
...
def _get_text_node(self):
...
@property
def text(self):
...
@text.setter
def text(self, value):
...
def __str__(self):
return '\n'.join([self.title, self.text])
Solution
Restriction is clear and what can be done to see more details and which i specifically need. An idea came into my mind, why not to reuse patching technique of a testing practice, unittest.mock
is powerful for such a case.
Things to do are simply define a function of a custom printing objects details and patch the __str__
method. So easy and tricky, isn't it?
Here we go.
from unittest.mock import patch
from contextlib import AbstractContextManager
def node_to_str(self: Note):
created = self.timestamps.created.isoformat(sep=' ', timespec='seconds')
updated = self.timestamps.updated.isoformat(sep=' ', timespec='seconds')
return f'Node<{self.type.value}, "{self.title}", "{self.text}", created="{created}", updated="{updated}">'
class Patch(AbstractContextManager):
def __enter__(self):
self.patchers = []
node_str_mock = patch('gkeepapi.node.Note.__str__', node_to_str)
self.patchers.append(node_str_mock)
for p in self.patchers:
p.start()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
for p in self.patchers:
p.stop()
Function node_to_str
is the way i'd like to see the object's details and the Patch
context-manager class is a wrapper to apply it gracefully.
At a final step running code looks very tidy.
from fetchig import fecth_print
if __name__ == '__main__':
from patching import Patch
with Patch():
fecth_print()
# Output looks better now:
# "Node<1, Shop, Buy food, created=2022-01-24 17:53:56Z, updated=2022-01-24 17:53:56Z>"
# "Node<1, Gym, Do squats, created=2022-01-24 17:53:56Z, updated=2022-01-24 17:53:56Z>"
It's a bad approach
Goal is reached, code isn't production consequently it's a quite good approach.