The "Pythonic" way to report that you've failed to achieve what you were asked to do is to raise an exception. This makes your code fairly simple:
def removeRec(node, value):
if isinstance(node, EmptyNode):
raise ValueError("Cannot remove value from an empty list")
elif node.data == value:
return node.next
else:
node.next = removeRec(node.next, value)
return node
This would work with your existing remove function, leaving it to the caller to handle the exception in the calling code. However, if you wanted the remove function to return a Boolean value indicating success or failure, you could catch the exception there:
def remove(lst, value):
try:
lst.head = removeRec(lst.head, value)
return True # only reached if no exception was raised by the recursion
except ValueError:
return False
If you're dead set against using exceptions for some reason (such as being in a class where they've not been taught yet), you could encode the failure of the removal in the return value from the recursive calls, but then you need to do extra checking for it, to avoid damaging your list:
def removeRec(node, value):
if isinstance(node, EmptyNode):
print("Cannot remove value from an empty list")
return None # your code did this implicitly, I'm being explicit
elif node.data == value:
return node.next
else:
rec_result = removeRec(node.next, value)
if rec_result is None:
return rec_result
else:
node.next = rec_result
return node
You then can do the same check from the recursive case in the non-recursive function, and turn the result value into a boolean:
def remove(lst, value):
rec_result = removeRec(lst.head, value)
if rec_result is None:
return False
else:
lst.head = rec_result
return True