Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
51efe12
test: add golden compatibility coverage
olmos-keepsafe Jul 21, 2026
7c78ad3
refactor: apply Python syntax upgrades
olmos-keepsafe Jul 21, 2026
73a4f4b
chore: migrate packaging to pyproject
olmos-keepsafe Jul 21, 2026
23e2e6f
chore: normalize Python 3.11 workflows
olmos-keepsafe Jul 21, 2026
5519cd7
ci(item 1): migrate CircleCI config to 2.1
olmos-keepsafe Jul 21, 2026
abfd52c
chore(item 2): remove redundant dependency tree tooling
olmos-keepsafe Jul 21, 2026
7207600
chore(item 3): bump package version to 1.1.0
olmos-keepsafe Jul 21, 2026
5b6bb3d
build(item 4): generate requirements with pip-compile
olmos-keepsafe Jul 21, 2026
3f7c7cc
chore(item 6): remove completed pyupgrade tooling
olmos-keepsafe Jul 21, 2026
7cac220
docs(item 8): record clean migration proof
olmos-keepsafe Jul 21, 2026
a73f712
bump version in pyproject.toml to 2.0.0 since 1.x already exists pre-…
olmos-keepsafe Aug 26, 2026
ebb7109
Upgrade Mistune to 3.3.4 with compatibility fixtures
olmos-keepsafe Aug 26, 2026
5ebec55
chore: finalize 2.0.0 release workflow
olmos-keepsafe Aug 26, 2026
5fa63c8
docs: finalize migration release proof
olmos-keepsafe Aug 26, 2026
f503dcf
fix: preserve quoted Mistune link titles
olmos-keepsafe Aug 26, 2026
4769d84
perf: skip unused Mistune link indexes
olmos-keepsafe Aug 26, 2026
1fe9595
fix: restore Mistune parser facade parity
olmos-keepsafe Aug 26, 2026
4c3f16b
test: harden Mistune compatibility oracle
olmos-keepsafe Aug 26, 2026
7306ba0
refactor: clarify Mistune parser probe
olmos-keepsafe Aug 26, 2026
3bb941e
ci: consume compiled development lock
olmos-keepsafe Aug 27, 2026
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
83 changes: 83 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
version: 2.1

executors:
python311:
docker:
- image: cimg/python:3.11.13

commands:
restore_dependency_caches:
steps:
- restore_cache:
keys:
- v4-pip-{{ arch }}-{{ checksum ".python-version" }}-{{ checksum "pyproject.toml" }}-{{ checksum "requirements-dev.txt" }}
- v4-pip-{{ arch }}-{{ checksum ".python-version" }}-{{ checksum "requirements-dev.txt" }}
- v4-pip-{{ arch }}
- v4-pip-
- restore_cache:
keys:
- v4-venv-{{ arch }}-{{ checksum ".python-version" }}-{{ checksum "pyproject.toml" }}-{{ checksum "requirements-dev.txt" }}
- v4-venv-{{ arch }}-{{ checksum ".python-version" }}-{{ checksum "requirements-dev.txt" }}
- v4-venv-{{ arch }}
- v4-venv-
save_dependency_caches:
steps:
- save_cache:
key: v4-pip-{{ arch }}-{{ checksum ".python-version" }}-{{ checksum "pyproject.toml" }}-{{ checksum "requirements-dev.txt" }}
paths:
- ~/.local
- ~/.cache
- save_cache:
key: v4-venv-{{ arch }}-{{ checksum ".python-version" }}-{{ checksum "pyproject.toml" }}-{{ checksum "requirements-dev.txt" }}
paths:
- /home/circleci/project/venv

jobs:
prepare_cache:
executor: python311
steps:
- checkout
- restore_dependency_caches
- run:
name: Install dependencies
command: make ci-dev-install
- save_dependency_caches

lint:
executor: python311
steps:
- checkout
- restore_dependency_caches
- run:
name: Run the linter
command: make lint
- store_test_results:
path: build/test

test:
executor: python311
steps:
- checkout
- restore_dependency_caches
- run:
name: Run unit and golden fixture tests
command: make test-only
- store_test_results:
path: build/test
- store_artifacts:
path: build/coverage/coverage.xml
destination: coverage
- run:
name: Upload coverage to Codecov
command: bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN} -f build/coverage/coverage.xml || echo "Codecov did not collect coverage reports"

workflows:
test:
jobs:
- prepare_cache
- lint:
requires:
- prepare_cache
- test:
requires:
- prepare_cache
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.11
3.11.13
8 changes: 6 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
language: python
dist: jammy
python:
- "3.11"
- 3.11.9
# command to install dependencies
install:
- make dev
- python -m venv venv
- venv/bin/pip install -U "pip<26" "setuptools>=82.0.1" "wheel>=0.47.0"
- venv/bin/pip install -r requirements-dev.txt
- venv/bin/pip install --no-deps -e .
# command to run tests
script:
- venv/bin/python -VV
- make test
- make coverage
9 changes: 9 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
2.0.0 (2026-08-26)
------------------

- Require Python 3.11.
- Upgrade Mistune from 0.8.4 to 3.3.4 behind a compatibility parser that preserves the existing sdiff tree,
rendering, diff, HTML, list, link/image, and Zendesk behavior.
- Add self-contained Golden Mistune 0.8.4 Fixtures, a dual-version compatibility harness,
deterministic fuzz cases, and linear-time malformed-link regression coverage.

0.2.0 (2018-08-21)
------------------

Expand Down
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
recursive-include scripts *.py
recursive-include tests *.json *.md *.py
160 changes: 122 additions & 38 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,60 +1,144 @@
# Some simple testing tasks (sorry, UNIX only).
# Package development and verification tasks (UNIX only).

PYTHON=venv/bin/python3
PYTHON_VERSION_FILE=.python-version
PYTHON_VERSION=$(strip $(shell cat $(PYTHON_VERSION_FILE)))
PYTHON=venv/bin/python
PIP=venv/bin/pip
COVERAGE=venv/bin/coverage
TEST_RUNNER=venv/bin/pytest
TEST_RUNNER_FLAGS=-s --durations=3 --durations-min=0.005
PYTEST=venv/bin/pytest
FLAKE=venv/bin/flake8
FLAGS=
PIP_COMPILE=venv/bin/pip-compile
RG=rg
SDIFF_MISTUNE_084_ORACLE_REV?=12e7782208e4b458c8c4242882fda2377d9cba6b
MISTUNE_ORACLE_PYTHON?=$(abspath $(PYTHON))
MISTUNE_GOLDEN_FIXTURES=tests/fixtures/compatibility/golden_mistune_084_fixtures.json

PYTEST_SHARED_FLAGS=-s --durations=3 --durations-min=0.005
PYTEST_FLAGS=$(PYTEST_SHARED_FLAGS)
CI_COVERAGE_REPORT=

PYPICLOUD_HOST=pypicloud.getkeepsafe.local
TWINE=./venv/bin/twine
TWINE=venv/bin/twine
HOOK_PATH=$(shell git rev-parse --git-path hooks/pre-push)

update:
$(PIP) install -U pip
$(PIP) install -U .
ifdef CI
PYTEST_FLAGS += --junitxml=build/test/results.xml
CI_COVERAGE_REPORT=$(COVERAGE) xml -o build/coverage/coverage.xml
endif

build-dir:
mkdir -p build/test build/coverage

env:
test -d venv || python3 -m venv venv
@if [ -d "venv" ] && $(PIP) --version >/dev/null 2>&1 \
&& $(PYTHON) -c 'import platform, sys; sys.exit(platform.python_version() != "$(PYTHON_VERSION)")'; then \
echo "Reusing venv with Python $(PYTHON_VERSION)"; \
else \
echo "Creating venv with Python $(PYTHON_VERSION)"; \
if [ -d "venv" ]; then rm -rf venv; fi; \
python -m venv venv; \
fi
$(PIP) install -U "pip<26" "setuptools>=82.0.1" "wheel>=0.47.0"
$(PIP) install -e .

dev: env update
$(PIP) install .[tests,devtools]
dev: env
$(PIP) install -r requirements-dev.txt
$(PIP) install --no-deps -e .

install: env update
update:
$(PIP) install -U .

publish:
rm -rf dist
$(PYTHON) -m build .
$(TWINE) upload --verbose --sign --username developer --repository-url http://$(PYPICLOUD_HOST)/simple/ dist/*.whl
install: env

ci-env:
@if [ -d "venv" ] && $(PIP) --version >/dev/null 2>&1 \
&& $(PYTHON) -c 'import platform, sys; sys.exit(platform.python_version() != "$(PYTHON_VERSION)")'; then \
echo "Reusing cached CI venv, no need to recreate when it has not changed"; \
else \
echo "No valid cached venv found, creating a fresh venv"; \
if [ -d "venv" ]; then rm -rf venv; fi; \
python -m venv venv; \
$(PIP) install -U "pip<26" "setuptools>=82.0.1" "wheel>=0.47.0"; \
fi
ci-dev-install: ci-env
$(PIP) install -r requirements-dev.txt
$(PIP) install --no-deps -e .

flake:
$(FLAKE) sdiff tests
$(FLAKE) sdiff tests scripts

check-msgpack:
@echo "Checking for direct msgpack imports..."
@! $(RG) -n --glob '*.py' '^(import msgpack|from msgpack)' sdiff tests \
|| (echo "ERROR: Unexpected direct msgpack import found." && exit 1)

lint: build-dir flake check-msgpack

test-only: build-dir
$(COVERAGE) erase
$(COVERAGE) run -m pytest $(PYTEST_FLAGS)
$(CI_COVERAGE_REPORT)

test: lint test-only

test: flake
$(COVERAGE) run -m pytest $(TEST_RUNNER_FLAGS)
vtest vtests: build-dir
$(COVERAGE) erase
$(COVERAGE) run -m pytest -v $(PYTEST_FLAGS)
$(CI_COVERAGE_REPORT)

vtest:
$(COVERAGE) run -m pytest -v $(TEST_RUNNER_FLAGS)
fixture-smoke:
$(PYTEST) -q tests/test_golden_compatibility.py tests/test_sdiff.py

testloop:
while sleep 1; do $(TEST_RUNNER) -s --lf $(TEST_RUNNER_FLAGS); done
import-smoke:
$(PYTHON) -c 'import importlib.metadata as m; import sdiff; from sdiff import MdParser, ZendeskHelpMdParser, diff, diff_links, diff_struct, renderer; print(m.version("sdiff"), MdParser.__name__, ZendeskHelpMdParser.__name__, renderer.TextRenderer.__name__)'

cov cover coverage:
smoke: fixture-smoke import-smoke

depcheck:
$(PIP) check

mistune-compat:
$(PYTHON) scripts/run_mistune_compat.py --oracle-revision "$(SDIFF_MISTUNE_084_ORACLE_REV)" \
--bootstrap-python "$(MISTUNE_ORACLE_PYTHON)" --expected-python "$(PYTHON_VERSION)" --target .

mistune-compat-refresh:
$(PYTHON) scripts/run_mistune_compat.py --oracle-revision "$(SDIFF_MISTUNE_084_ORACLE_REV)" \
--bootstrap-python "$(MISTUNE_ORACLE_PYTHON)" --expected-python "$(PYTHON_VERSION)" --target . \
--write-golden-fixtures $(MISTUNE_GOLDEN_FIXTURES)

requirements: dev
$(PIP_COMPILE) --annotation-style=line --output-file=requirements.txt pyproject.toml
$(PIP_COMPILE) --annotation-style=line --output-file=requirements-dev.txt --extra=dev pyproject.toml

coverage:
$(COVERAGE) report -m

cov cover:
$(COVERAGE) html --directory coverage
@echo "Coverage HTML written to coverage/index.html"

package: publish

publish:
$(PIP) install -U twine build
rm -rf dist
$(PYTHON) -m build .
$(TWINE) upload --verbose --sign --username developer --repository-url http://$(PYPICLOUD_HOST)/simple/ dist/*.whl

hooks:
cp git_hooks/pre-push $(HOOK_PATH)
chmod +x $(HOOK_PATH)

unhooks:
rm -f $(HOOK_PATH)

clean:
rm -rf `find . -name __pycache__`
rm -f `find . -type f -name '*.py[co]' `
rm -f `find . -type f -name '*~' `
rm -f `find . -type f -name '.*~' `
rm -f `find . -type f -name '@*' `
rm -f `find . -type f -name '#*#' `
rm -f `find . -type f -name '*.orig' `
rm -f `find . -type f -name '*.rej' `
find . -name __pycache__ -type d -prune -exec rm -rf {} +
find . -type f \( -name '*.py[co]' -o -name '*~' -o -name '.*~' -o -name '*.orig' -o -name '*.rej' \) -delete
rm -f .coverage
rm -rf coverage
rm -rf build
rm -rf venv

rm -rf build coverage dist sdiff.egg-info venv

.PHONY: all build env linux run pep test vtest testloop cov clean
.PHONY: build-dir check-msgpack ci-dev-install ci-env clean cov cover coverage depcheck dev env fixture-smoke \
flake hooks import-smoke install lint mistune-compat mistune-compat-refresh package publish requirements smoke \
test test-only unhooks update \
vtest vtests
Loading