A Mutation names a module, and its snippet must occur exactly once in the whole file. That forces snippets to be
long enough to be unique across the file rather than clear about what they change, and it leaves a registration's
target saying less than a reader expects. Letting a mutation name a function, and searching that function's source
instead of the file, fixes both.
What happens today
Mutation.check reads the file the module was loaded from and requires the snippet to be there exactly once:
if (occurrences := source.count(self.old)) != 1:
return Result(Outcome.STALE, f"the snippet occurs {occurrences} times rather than once")
That rule is what stops a mutation breaking a line it was not aimed at, and it is worth keeping. What it costs is
visible in this repository: if metadata is None: occurs twice in src/update_time/sources/pypi.py, so the
registration on test_new_version_of_a_release_without_project_urls has to quote two lines to be unique, though
only the first is the line it means.
The target is a module even where the test invokes a function. Of the four registrations of the two pypi.py
mutations, three name a module whose snippet sits in a function the test never calls, so naming the function the
test does call would be misleading rather than helpful — unless the snippet is required to be inside it.
What would change
A mutation may name a callable as well as a module. The module is derived from the callable, which is reliable
through decorators: get_latest_version carries three and release_metadata is cached, and __module__ reports
update_time.sources.pypi for both. Where a callable is named, the snippet is searched in its source rather than in
the file, and a snippet absent from it is stale, exactly as a snippet absent from the file is today.
just mutate gains the same narrowing. Without it the two tools disagree: a snippet unique within a function but
repeated in the file is stale to the probe and fine to the registration, which breaks the loop this repository works
by — probe with just mutate, read what it killed, paste it into a registration.
What to settle
- Which targets. A function and a method, certainly. A class? A
property is not a function, so a mutation
naming one would have to reach its fget.
- What the window holds.
inspect.getsourcelines returns the decorator lines along with the function:
get_latest_version's source starts at @publication_date_reporting. So a mutation could aim at a decorator,
which may be wanted or may be a surprise worth excluding.
- How the narrowed replacement is spliced back, since the module executed for the run is still the whole file.
- How
just mutate names the function on the command line, and what it reports when the snippet is in the file
but outside the function named.
- Whether module targets stay. They should: a snippet in a private helper that no test names has no callable to
point at.
A
Mutationnames a module, and its snippet must occur exactly once in the whole file. That forces snippets to belong enough to be unique across the file rather than clear about what they change, and it leaves a registration's
target saying less than a reader expects. Letting a mutation name a function, and searching that function's source
instead of the file, fixes both.
What happens today
Mutation.checkreads the file the module was loaded from and requires the snippet to be there exactly once:That rule is what stops a mutation breaking a line it was not aimed at, and it is worth keeping. What it costs is
visible in this repository:
if metadata is None:occurs twice insrc/update_time/sources/pypi.py, so theregistration on
test_new_version_of_a_release_without_project_urlshas to quote two lines to be unique, thoughonly the first is the line it means.
The target is a module even where the test invokes a function. Of the four registrations of the two
pypi.pymutations, three name a module whose snippet sits in a function the test never calls, so naming the function the
test does call would be misleading rather than helpful — unless the snippet is required to be inside it.
What would change
A mutation may name a callable as well as a module. The module is derived from the callable, which is reliable
through decorators:
get_latest_versioncarries three andrelease_metadatais cached, and__module__reportsupdate_time.sources.pypifor both. Where a callable is named, the snippet is searched in its source rather than inthe file, and a snippet absent from it is stale, exactly as a snippet absent from the file is today.
just mutategains the same narrowing. Without it the two tools disagree: a snippet unique within a function butrepeated in the file is stale to the probe and fine to the registration, which breaks the loop this repository works
by — probe with
just mutate, read what it killed, paste it into a registration.What to settle
propertyis not a function, so a mutationnaming one would have to reach its
fget.inspect.getsourcelinesreturns the decorator lines along with the function:get_latest_version's source starts at@publication_date_reporting. So a mutation could aim at a decorator,which may be wanted or may be a surprise worth excluding.
just mutatenames the function on the command line, and what it reports when the snippet is in the filebut outside the function named.
point at.