{"id":31936136,"url":"https://github.com/dnouri/pytest-bashdoctest","last_synced_at":"2025-10-14T07:22:07.148Z","repository":{"id":317825415,"uuid":"1068988438","full_name":"dnouri/pytest-bashdoctest","owner":"dnouri","description":"A pytest plugin for testing bash command examples in markdown documentation","archived":false,"fork":false,"pushed_at":"2025-10-03T08:56:37.000Z","size":38,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-03T10:23:42.048Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dnouri.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-03T08:19:58.000Z","updated_at":"2025-10-03T08:55:54.000Z","dependencies_parsed_at":"2025-10-03T10:24:33.132Z","dependency_job_id":"756a5291-d73c-4237-ae2e-eac3e21e2462","html_url":"https://github.com/dnouri/pytest-bashdoctest","commit_stats":null,"previous_names":["dnouri/pytest-bashdoctest"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/dnouri/pytest-bashdoctest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dnouri%2Fpytest-bashdoctest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dnouri%2Fpytest-bashdoctest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dnouri%2Fpytest-bashdoctest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dnouri%2Fpytest-bashdoctest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dnouri","download_url":"https://codeload.github.com/dnouri/pytest-bashdoctest/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dnouri%2Fpytest-bashdoctest/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279018216,"owners_count":26086303,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-14T07:22:01.787Z","updated_at":"2025-10-14T07:22:07.141Z","avatar_url":"https://github.com/dnouri.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pytest-bashdoctest\n\n[![PyPI version](https://img.shields.io/pypi/v/pytest-bashdoctest.svg)](https://pypi.org/project/pytest-bashdoctest/)\n[![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)\n[![CI Status](https://img.shields.io/github/actions/workflow/status/dnouri/pytest-bashdoctest/test.yml?branch=master)](https://github.com/dnouri/pytest-bashdoctest/actions)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nA pytest plugin for testing bash command examples in markdown documentation.\n\n## Why pytest-bashdoctest?\n\nAPI documentation gets outdated fast. Manually testing curl examples is tedious. This plugin automatically tests bash code blocks in your markdown files against actual API responses, keeping docs and code in sync.\n\n## Installation\n\n```bash\npip install pytest-bashdoctest\n```\n\nOr with uv:\n\n```bash\nuv add pytest-bashdoctest\n```\n\n## Quick Start\n\n### 1. Write testable examples\n\nIn your `API.md`:\n\n````markdown\n## User API\n\n```bash\n$ curl -s \"https://api.github.com/users/dnouri\"\n{\n  \"login\": \"dnouri\",\n  ...\n  \"name\": \"Daniel Nouri\",\n  ...\n  \"bio\": \"Machine Learning and Programming\",\n  ...\n}\n```\n````\n\nThe `...` pattern matches any content, perfect for skipping dynamic or irrelevant fields.\n\n### 2. Run tests\n\n```bash\npytest --bashdoctest API.md -v\n```\n\nYour documentation is now executable and verified.\n\n**Note:** The `--bashdoctest` flag is required to enable bash doctest collection. This prevents conflicts with other pytest plugins that also process markdown files (like pytest-markdown-docs).\n\n## ELLIPSIS Patterns\n\n### Line-Level: Skip Blocks\n\nUse standalone `...` to skip entire sections:\n\n````markdown\n```bash\n$ curl -s \"https://api.github.com/users/dnouri\"\n{\n  \"login\": \"dnouri\",\n  ...\n  \"type\": \"User\",\n  ...\n  \"bio\": \"Machine Learning and Programming\",\n  ...\n}\n```\n````\n\nShow the fields that matter, skip the rest.\n\n### String-Level: Partial Matching\n\nUse `...` inside strings for dynamic values:\n\n````markdown\n```bash\n$ curl -s \"https://api.github.com/users/dnouri\"\n{\n  \"login\": \"dnouri\",\n  ...\n  \"avatar_url\": \"https://avatars.githubusercontent.com/u/...?v=4\",\n  ...\n  \"created_at\": \"2009-...-...T...Z\",\n  ...\n}\n```\n````\n\nUseful for URLs, UUIDs, timestamps, and other values that change but follow a pattern.\n\n### Collection-Level: Objects and Arrays\n\nUse `{...}` and `[...]` to match entire structures:\n\n````markdown\n```bash\n$ curl -s \"https://api.github.com/repos/dnouri/nolearn\"\n{\n  ...\n  \"name\": \"nolearn\",\n  ...\n  \"owner\": {...},\n  ...\n  \"license\": {...},\n  ...\n}\n```\n````\n\nFocus on the structure you care about without showing every detail.\n\n### Combining Patterns\n\nMix all three for flexible matching:\n\n````markdown\n```bash\n$ curl -s \"https://api.github.com/users/dnouri\"\n{\n  \"login\": \"dnouri\",\n  ...\n  \"avatar_url\": \"https://avatars.githubusercontent.com/...\",\n  ...\n  \"bio\": \"Machine Learning and Programming\",\n  ...\n  \"public_repos\": ...,\n  ...\n}\n```\n````\n\n## Configuration\n\n### Specifying Files to Test\n\n**Explicit paths:**\n```bash\npytest --bashdoctest README.md -v\npytest --bashdoctest docs/api.md -v\n```\n\n**Via configuration** in `pyproject.toml`:\n```toml\n[tool.pytest.ini_options]\ntestpaths = [\"tests\", \"README.md\", \"docs\"]\n```\n\nThen `pytest --bashdoctest` will test both your test suite and documentation.\n\n### Environment Variables\n\n**The Problem:** Your bash examples often need API keys, custom URLs, or other configuration. Hard-coding these in documentation is insecure and inflexible.\n\n**The Solution:** The `bashdoctest_env` fixture. This session-scoped fixture provides environment variables to all bash examples in your markdown files.\n\n**When you DON'T need it:** If your bash examples only use public APIs without authentication (like the GitHub examples in this README), you don't need to define this fixture. The plugin provides a default empty fixture.\n\n**When you DO need it:** If your examples need API keys, custom URLs, or other config, create `conftest.py`:\n\n```python\nimport os\nimport pytest\n\n@pytest.fixture(scope=\"session\")\ndef bashdoctest_env():\n    \"\"\"Environment variables for bash documentation examples.\"\"\"\n    api_key = os.getenv(\"API_KEY\")\n    if not api_key:\n        pytest.skip(\"API_KEY not set - skipping bash doctest examples\")\n\n    return {\n        \"API_KEY\": api_key,\n        \"API_URL\": os.getenv(\"API_URL\", \"https://api.example.com\"),\n    }\n```\n\nUse in your markdown files:\n\n````markdown\n```\n$ curl -s \"$API_URL/users\" -H \"Authorization: Bearer $API_KEY\"\n{\n  \"users\": [...]\n}\n```\n````\n\nThe environment variables from your `bashdoctest_env` fixture are merged with `os.environ` when executing commands, so `$API_KEY` and `$API_URL` will be available to all bash examples.\n\n## How It Works\n\n1. Plugin activates when you pass `--bashdoctest` flag\n2. Collects markdown files you specify (explicit paths or via `testpaths`)\n3. Parser extracts bash code blocks (only files with actual bash examples)\n4. Each bash block becomes a test item\n5. Commands execute with your `bashdoctest_env` variables merged into environment\n6. Output matches against expected using ELLIPSIS rules\n\n**Architecture:**\n```\nsrc/pytest_bashdoctest/\n   parser.py      # Extract bash blocks\n   matcher.py     # ELLIPSIS matching\n   executor.py    # Run commands\n   formatter.py   # Format failures\n   plugin.py      # Pytest hooks\n```\n\nCore modules are pure Python (no pytest dependency) for portability.\n\n## Testing the Plugin\n\n```bash\n# Run unit tests\npytest tests/ -v\n\n# Test with coverage\npytest tests/ --cov=pytest_bashdoctest --cov-report=term-missing\n\n# Test this README (dogfooding!)\npytest --bashdoctest README.md -v\n```\n\n## Limitations\n\n- Commands timeout after 30 seconds (configurable in `executor.py`)\n- Interactive commands will hang\n- Uses `shell=True` (commands come from your trusted markdown files)\n- Output buffering may differ for very large responses\n\n## Development\n\n### Setup\n\n```bash\ngit clone https://github.com/dnouri/pytest-bashdoctest.git\ncd pytest-bashdoctest\n\n# Install dependencies\nuv sync --dev\n\n# Install pre-commit tool globally (recommended, one-time setup)\nuv tool install pre-commit\n\n# Install git hooks (runs automatically on every commit)\npre-commit install\n```\n\n### Running Tests\n\n```bash\n# Run unit tests\nuv run pytest tests/ -v\n\n# Test README examples\nuv run pytest --bashdoctest README.md -v\n\n# Run with coverage\nuv run pytest tests/ --cov=pytest_bashdoctest --cov-report=term-missing\n```\n\n### Code Quality\n\nPre-commit hooks automatically run on every commit to ensure code quality. To run manually:\n\n```bash\n# Run all pre-commit hooks (after uv tool install pre-commit)\npre-commit run --all-files\n\n# Or using project dependencies\nuv run pre-commit run --all-files\n\n# Run specific checks\nuv run ruff check .              # Linting\nuv run ruff format .             # Auto-format code\nuv run ruff check --fix .        # Auto-fix linting issues\n```\n\nTo skip pre-commit hooks (not recommended):\n\n```bash\ngit commit --no-verify -m \"message\"\n```\n\n## Releasing\n\nUpdate `version` in `pyproject.toml`, then:\n\n```bash\ngit tag v2025.10.4 \u0026\u0026 git push --tags\n```\n\nPublishes to PyPI automatically via GitHub Actions.\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdnouri%2Fpytest-bashdoctest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdnouri%2Fpytest-bashdoctest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdnouri%2Fpytest-bashdoctest/lists"}