I believe I've fixed this. I implemented a Schema.to_json(names) method, which recursively serializes schema objects to JSON-compatible structures, avoiding re-serializing schemas which we've already seen. (This also means avoiding serializing JSON just to deserialize it again.) I was able to get rid of some variables which tracked how the schema was originally defined, because this recursion is taking care of noticing that.
As I needed to, I removed some verbosity from the tests and removed some exception handling. It's very unhelpful when python tests catch exceptions, because they make it that much harder to track down the exact point of the failure. (An exception that propagates through a test is a test failure, so there's no need to separately mark the test as failed.) Printing extra information about what tests are running distracts from where the failures are occurring. I recommend the nose test runner (with flags --pdb --pdb-failure) for running the tests.
I've added a test that triggered this in the first place.