How to write a spelling corrector (2016)
The unit tests worry me:    assert len(WORDS) == 32192
    assert sum(WORDS.values()) == 1115504
    assert WORDS.most_common(10) == [
     ('the', 79808),
     ('of', 40024),
     ('and', 38311),
     ('to', 28765),
     ('in', 22020),
     ('a', 21124),
     ('that', 12512),
     ('he', 12401),
     ('was', 11410),
     ('it', 10681)]
    assert WORDS['the'] == 79808

Those aren't testing the file open, or Counter, or read, but instead are tightly-coupling the tests to the exact corpus. The code really should have not hard-coded the corpus, and the tests should have supplied a smaller corpus of known length where each word's frequency+probability was obvious from inspection.There's another overfitted test just before this:    assert Counter(words('This is a test. 123; A TEST this is.')) == (
           Counter({'123': 1, 'a': 2, 'is': 2, 'test': 2, 'this': 2}))

What is this verifying? We know how a Counter works, we don't need to test it; and the previous test already established the correctness of words().I see a lot of junior engineers following this style of tightly-coupled, overfitted tests, and seen how a few years of growth can lead to tens of thousands of tests whose primary value is to provide Amazon with a predictable stream of AWS revenue (as we run all th tests) & make the engineer feel better for writing tests.