Your problem is in the loop:
while itr.next:
itr.next = new_node
itr = itr.next
When appending an item to an empty list, itr is initially is equal to new_node.
You set itr.next to new_node, so now new_node.next is equal to new_node. You have now created a cycle in your list, so it will loop forever.
When appending an item to your list, you should only be modifying the last element - the loop only serves to traverse the list to get to the end. It should look like:
def append(self, data=None):
new_node = Node(data)
if self.head is None:
self.head = new_node
else:
# you only need to traverse the list when it isn't empty
itr = self.head
while itr.next:
itr = itr.next
itr.next = new_node # only add the new node after traversing the list
That being said, this method of appending is O(n) complexity, and you can make it O(1) by keeping a pointer to the tail of the list and modifying the tail without traversing the list to find it.