Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,20 @@ option. To additionally suppress all but critical log messages, use `--quiet`. F
via the `--log-level` option. `--debug` is equivalent to `--log-level=DEBUG`, and `--quiet` is equivalent to
`--log-level=CRITICAL --no-status`.

### Environment Variables
`GRAPHTAGE_BATCH_BACKEND` pins the backend that computes Levenshtein distances in batch. Graphtage picks one by batch
size: `python` prices a single pair at a time, and `numpy` advances a whole batch through one array pass per row. Both
return identical distances, so set this variable only to benchmark one backend against the other, or to work around a
problem with the automatic choice.

| Value | Effect |
|-------|--------|
| `python` | Price every pair one at a time. |
| `numpy` | Always use the batched scan. |
| unset | Pick by batch size. This is the default. |

An unrecognized value raises a `ValueError` rather than falling back to a default.

### Version Information
`--version` or `-v` writes a line such as `Graphtage version 0.4.0` to STDERR. If you pass it without any input files,
Graphtage prints the version and exits; if you pass input files as well, it prints the version and then computes the
Expand Down
19 changes: 17 additions & 2 deletions test/test_batch_distance.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ def mixed_corpus() -> list[str]:
]


def no_backend_pin():
"""Removes any ambient ``GRAPHTAGE_BATCH_BACKEND`` for the duration of the context.

A test that watches the *automatic* backend choice can only observe it when nothing pins one. Without this, a
developer who exports the variable to exercise the other backend gets spurious failures from tests that never
mention it.

"""
unpinned = {name: value for name, value in os.environ.items() if name != BACKEND_ENV_VAR}
return patch.dict(os.environ, unpinned, clear=True)


class TestBackendAgreement(TestCase):
def test_backends_agree_with_levenshtein_distance(self):
"""Every backend returns exactly what ``levenshtein_distance`` returns, over a varied corpus.
Expand Down Expand Up @@ -245,7 +257,8 @@ def test_environment_variable_overrides_the_automatic_choice(self):
with patch.dict(os.environ, {BACKEND_ENV_VAR: 'python'}):
all_pairs(corpus, corpus)
batched.assert_not_called()
all_pairs(corpus, corpus)
with no_backend_pin():
all_pairs(corpus, corpus)
self.assertTrue(batched.called, 'this batch should have gone to the numpy backend on its own')

def test_environment_variable_names_are_validated(self):
Expand Down Expand Up @@ -493,7 +506,9 @@ def test_one_big_pair_goes_to_a_vectorized_backend(self):
short_left, short_right = 'kitten', 'sitting'
self.assertGreaterEqual(len(long_left) * len(long_right), batch_distance.VECTORIZED_MIN_CELLS)
self.assertLess(len(short_left) * len(short_right), batch_distance.VECTORIZED_MIN_CELLS)
with patch.object(NumpyBackend, 'distances', autospec=True, side_effect=NumpyBackend.distances) as batched:
with no_backend_pin(), patch.object(
NumpyBackend, 'distances', autospec=True, side_effect=NumpyBackend.distances
) as batched:
self.assertEqual(levenshtein_distance(short_left, short_right), cost(short_left, short_right))
batched.assert_not_called()
self.assertEqual(levenshtein_distance(long_left, long_right), cost(long_left, long_right))
Expand Down