Skip to content
Draft
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
81 changes: 81 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
version: 2.1

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

commands:
restore_dependency_caches:
steps:
- restore_cache:
keys:
- v3-pip-{{ arch }}-{{ checksum "pyproject.toml" }}
- v3-pip-{{ arch }}
- v3-pip-
- restore_cache:
keys:
- v3-venv-{{ arch }}-{{ checksum "pyproject.toml" }}
- v3-venv-{{ arch }}
- v3-venv-
save_dependency_caches:
steps:
- save_cache:
key: v3-pip-{{ arch }}-{{ checksum "pyproject.toml" }}
paths:
- ~/.local
- ~/.cache
- save_cache:
key: v3-venv-{{ arch }}-{{ checksum "pyproject.toml" }}
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
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
recursive-include tests/fixtures *.json *.md
134 changes: 97 additions & 37 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,60 +1,120 @@
# Some simple testing tasks (sorry, UNIX only).
# Package development and verification tasks (UNIX only).

PYTHON=venv/bin/python3
PYTHON_VERSION=3.11.13
BOOTSTRAP_PYTHON?=python3.11
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

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
test -d venv || $(BOOTSTRAP_PYTHON) -m venv venv
$(PIP) install -U "pip<26" "setuptools>=82.0.1" "wheel>=0.47.0"
$(PIP) install -e .

dev: env
$(PIP) install -e '.[dev]'

dev: env update
$(PIP) install .[tests,devtools]
update:
$(PIP) install -U .

install: env update
install: env

publish:
rm -rf dist
$(PYTHON) -m build .
$(TWINE) upload --verbose --sign --username developer --repository-url http://$(PYPICLOUD_HOST)/simple/ dist/*.whl
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; \
$(BOOTSTRAP_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 -e '.[dev]'

flake:
$(FLAKE) sdiff tests

test: flake
$(COVERAGE) run -m pytest $(TEST_RUNNER_FLAGS)
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)

vtest:
$(COVERAGE) run -m pytest -v $(TEST_RUNNER_FLAGS)
test: lint test-only

testloop:
while sleep 1; do $(TEST_RUNNER) -s --lf $(TEST_RUNNER_FLAGS); done
vtest vtests: build-dir
$(COVERAGE) erase
$(COVERAGE) run -m pytest -v $(PYTEST_FLAGS)
$(CI_COVERAGE_REPORT)

cov cover coverage:
fixture-smoke:
$(PYTEST) -q tests/test_golden_compatibility.py tests/test_sdiff.py

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__)'

smoke: fixture-smoke import-smoke

depcheck:
$(PIP) check

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:
$(PYTHON) -m build

publish: package
$(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 package publish requirements smoke test test-only unhooks update vtest vtests
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,37 @@
# md-sdiff

Diffs to markdown texts only based on their structure. Ignores content. Helpful to diff 2 files that contain the same content in different languages.

## Python API

```python
import sdiff
from sdiff import ZendeskHelpMdParser

left, right, errors = sdiff.diff(
'# English title',
'# Deutscher Titel',
parser_cls=ZendeskHelpMdParser,
)
```

The package intentionally has no command-line or service entry point. Public behavior is exposed through
`sdiff.diff`, the parser classes, renderer classes, and the model/error objects returned by comparisons.

## Python 3.11 development

```sh
make dev
make lint
make test
make fixture-smoke
make depcheck
make requirements
```

`make requirements` uses `pip-compile` to regenerate `requirements.txt` and `requirements-dev.txt` from
`pyproject.toml`.

Exact parser, renderer, and diff behavior is protected by
`tests/fixtures/golden/python311_compatibility.json`. Migration scope and proof results are recorded in
`docs/python311-migration-contract.md`.
Loading