We use the term "memory safety" very broadly; what we meant was the absence of runtime errors (e.g., out-of-bounds list accesses), uncaught exceptions, concurrent modification, basically anything that would lead the program to crash.
Those aren't memory unsafe in the original sense of the word in Python, but they still crash the program.
Additionally, we prove the absence of data races between threads.
x = Foo()
thread1.do_something_to_foo(x)
thread2.do_something_to_foo(x)
wait_on_threads(thread1, thread2) # however that works in python?
do_something_to_modified_foo(x)
Wouldn't that be thread safety? I thought memory safety was things like preventing access to uninitialized or freed memory. As far as I know there's no way to do such things in pure Python.