Skip to content

Compare two specs#

Diff two spec files so that the diff shows only what the specs state differently. A plain text diff also shows the order of declarations, spacing and the order of the terms in a sum. The canonical form removes those differences. Comparing two specs lists what the form sorts and what it keeps.

  1. Write one spec in the canonical form.
python -m mathspec canonical spec.yaml

The form goes to stdout. -o canonical.yaml writes it to a file instead. A file the language refuses prints its message on stderr and exits with status 1.

  1. Diff two files once. Give each file to the command, and diff the two outputs:
diff <(python -m mathspec canonical before.yaml) <(python -m mathspec canonical after.yaml)
  1. Make git diff show the canonical form. Tell git which files are specs, and which command writes them out:
echo 'specs/*.yaml diff=mathspec' >> .gitattributes
git config diff.mathspec.textconv "python -m mathspec canonical"

The files in the repository stay as you wrote them. Only the diff changes. A commit that writes sum(dispatch * cost) as sum(cost * dispatch) shows no difference. A commit that changes a coefficient shows one line:

 objective:
   sense: minimize
-  expression: sum(cost * dispatch)
+  expression: sum((2 * cost) * dispatch)

Match only spec files in .gitattributes. Git runs the command on every file the pattern matches, and a YAML file that is not a spec fails to load.

  1. Keep specs in the canonical form in CI. Then the files themselves diff the way the form does, with no git setup. --write rewrites a file in the form. The form holds no YAML comments, so --write drops them:
python -m mathspec canonical --write spec.yaml

--check writes nothing. It exits with status 1 if the file is not in the form, and names the rewrite:

spec.yaml is not in the canonical form. Run `python -m mathspec canonical --write spec.yaml` to rewrite it.

Check every spec, and fail the job if one of them fails:

status=0
for spec in specs/*.yaml; do python -m mathspec canonical --check "$spec" || status=1; done
exit $status
  1. Compare from Python where the comparison is one step of a longer script. to_yaml writes the same text with canonical=True:
import mathspec as ms

before = ms.to_spec('before.yaml').to_yaml(canonical=True)
after = ms.to_spec('after.yaml').to_yaml(canonical=True)
print(before == after)