linkml-reference-validator 0.3.0rc2__tar.gz → 0.3.0rc3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/PKG-INFO +1 -1
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/fulltext/pmc.py +10 -4
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/reference_fetcher.py +23 -6
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/sources/pmid.py +2 -6
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/models.py +12 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_pmc_modern_container.py +20 -4
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_serve_stale_html_optin.py +59 -0
- linkml_reference_validator-0.3.0rc3/tests/test_trust_cached_entries.py +81 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.claude/.mcp.json +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.claude/settings.json +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.copier-answers.yml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.devcontainer/devcontainer.json +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.devcontainer/post_create.sh +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.editorconfig +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/actions/claude-code-action/action.yml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/actions/claude-issue-summarize-action/action.yml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/actions/claude-issue-triage-action/action.yml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/ai-controllers.json +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/copilot-instructions.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/copilot-setup-steps.yml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/dependabot.yml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/workflows/claude-code-review.yml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/workflows/claude-issue-summarize.yml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/workflows/claude-issue-triage.yml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/workflows/claude-ontology-review.yml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/workflows/claude.yml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/workflows/deploy-docs.yaml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/workflows/dragon-ai.yml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/workflows/main.yaml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/workflows/pypi-publish.yaml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.gitignore +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.goosehints +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.pre-commit-config.yaml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.yamllint.yaml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/AGENTS.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/BUG_GEO_ACCESSION_TO_UID.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/BUG_ZENODO_DOI_AND_URL_HANDLING.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/CLAUDE.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/CLI-ISSUES.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/CODE_OF_CONDUCT.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/CONTRIBUTING.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/FEATURE_REQUEST_REPAIR.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/LICENSE +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/README.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/REVIEW2.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/REVIEW3.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/ai.just +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/copilot-instructions.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/about.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/assets/zotero-find-full-text.png +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/concepts/content-types.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/concepts/editorial-conventions.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/concepts/how-it-works.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/elements/.gitkeep +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/how-to/add-reference-source.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/how-to/export-cache-to-zotero.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/how-to/fetch-full-text-and-pdfs.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/how-to/repair-validation-errors.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/how-to/skip-unsupported-references.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/how-to/use-local-files-and-urls.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/how-to/validate-clinical-trials.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/how-to/validate-dois.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/how-to/validate-entrez.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/how-to/validate-obo-files.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/how-to/validate-titles.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/how-to/validate-urls.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/index.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/notebooks/01_getting_started.ipynb +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/notebooks/02_advanced_usage.ipynb +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/notebooks/03_python_api.ipynb +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/notebooks/04_obo_validation.ipynb +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/notebooks/05_geo_validation.ipynb +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/quickstart.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/reference/cli.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/reference/splitting.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/setup-guide.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/superpowers/plans/2026-06-12-modular-pdf-fulltext-fetching.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/superpowers/plans/2026-08-05-private-library-full-text.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/superpowers/specs/2026-06-12-modular-pdf-fulltext-fetching-design.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/superpowers/specs/2026-08-05-private-library-full-text-design.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/templates-linkml/README.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/todo.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/troubleshooting.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/tutorials/complete-workflow.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/examples/README.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/justfile +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/mkdocs.yml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/mypy.ini +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/notebooks/01_getting_started.ipynb +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/notebooks/02_advanced_usage.ipynb +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/notebooks/03_python_api.ipynb +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/notebooks/README.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/project/README.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/project.justfile +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/pyproject.toml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/pytest.ini +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/__init__.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/_version.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/cli/__init__.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/cli/cache.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/cli/lookup.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/cli/repair.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/cli/shared.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/cli/validate.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/__init__.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/acquire.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/csl_export.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/extract/__init__.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/extract/base.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/extract/html.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/extract/pdf.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/extract/xml.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/fulltext/__init__.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/fulltext/base.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/fulltext/epmc_preprint.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/fulltext/json_api.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/fulltext/loader.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/fulltext/openalex.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/fulltext/unpaywall.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/fulltext/zotero.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/identifiers.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/sources/__init__.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/sources/base.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/sources/clinicaltrials.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/sources/doi.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/sources/entrez.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/sources/file.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/sources/json_api.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/sources/loader.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/sources/ppr.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/sources/url.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/sources/utils.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/etl/text_extractor.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/field_detection.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/matching.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/plugins/REVIEW.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/plugins/__init__.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/plugins/reference_validation_plugin.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/validation/__init__.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/validation/fuzzy_text_utils.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/validation/repairer.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/src/linkml_reference_validator/validation/supporting_text_validator.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/__init__.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/conftest.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/data/test_data_deep_nesting_invalid.yaml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/data/test_data_deep_nesting_valid.yaml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/data/test_data_invalid.yaml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/data/test_data_valid.yaml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/data/test_schema.yaml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/data/test_schema_deep_nesting.yaml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/GEO_GSE12345.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/PMC_TEST001.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/PMC_TEST001.txt +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/PMC_TEST002.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/PMC_TEST002.txt +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/PMID_TEST001.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/PMID_TEST001.txt +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/PMID_TEST002.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/PMID_TEST002.txt +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/PMID_TEST003.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/PMID_TEST003.txt +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/clinicaltrials_NCT00000001.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/fulltext_html/README.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/fulltext_html/dspace-32894695.html +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/fulltext_html/iris-19049553.html +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/fulltext_html/plos-0000308.html +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/jats/PMC5593426.xml +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/jats/README.md +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/fixtures/sample.obo +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_absent_abstract_recovery.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_acquire.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_cache_author_recovery.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_cli.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_cli_cache_enrich.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_cli_cache_export.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_cli_cache_reference.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_cli_lookup.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_cli_repair.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_clinicaltrials_cache.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_clinicaltrials_integration.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_deep_nesting.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_doi_preprint.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_e2e_integration.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_empty_snippets.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_epmc_preprint_provider.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_extractor_fidelity.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_extractors.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_field_detection.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_frontmatter_and_doi_case.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_fulltext_loader.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_fulltext_providers.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_fuzzy_suggestions.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_geo_integration.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_html_fulltext_acceptance.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_identifiers.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_jats_tables.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_json_api_source.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_ligature_normalization.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_models.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_no_cache_downgrade.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_open_access_policy.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_optional_linkml_dependency.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_plugin_integration.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_pmc_fulltext.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_pmid_network.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_ppr_source.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_real_validation.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_reference_fetcher.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_repair.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_source_utils.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_sources.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_split_supporting_text.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_supporting_text_validator.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_text_extractor.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_title_validation.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_unknown_slot_traversal.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_validation_config.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_validation_counts.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/tests/test_zotero_provider.py +0 -0
- {linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/uv.lock +0 -0
|
@@ -46,10 +46,16 @@ class TransientFullTextError(RuntimeError):
|
|
|
46
46
|
#: same element. Matching it alone would match every page's ``<body>``, and the
|
|
47
47
|
#: structural test is the only thing standing between this fetch and caching a
|
|
48
48
|
#: bot-check interstitial served on an HTTP 200.
|
|
49
|
+
#:
|
|
50
|
+
#: Order is a priority order, not an alphabetical one: the first match wins, so
|
|
51
|
+
#: the current wrapper is tried before the legacy ones. It matters for a legacy
|
|
52
|
+
#: page carrying several ``tsec`` sections, where only the first is returned --
|
|
53
|
+
#: the previous code behaved the same way, so this is a known limit rather than
|
|
54
|
+
#: a regression, but reordering the tuple would change which section that is.
|
|
49
55
|
PMC_ARTICLE_BODY_CLASSES = ("main-article-body", "article-body", "tsec")
|
|
50
56
|
|
|
51
57
|
|
|
52
|
-
def
|
|
58
|
+
def find_pmc_article_body(soup: BeautifulSoup) -> Optional[Any]:
|
|
53
59
|
"""Return PMC's article-body element, or None if the page has none.
|
|
54
60
|
|
|
55
61
|
Matches on class rather than element name: the container moved from ``div``
|
|
@@ -65,9 +71,9 @@ def _find_pmc_article_body(soup: BeautifulSoup) -> Optional[Any]:
|
|
|
65
71
|
Examples:
|
|
66
72
|
>>> from bs4 import BeautifulSoup
|
|
67
73
|
>>> html = '<section class="body main-article-body"><p>Text.</p></section>'
|
|
68
|
-
>>>
|
|
74
|
+
>>> find_pmc_article_body(BeautifulSoup(html, "html.parser")) is not None
|
|
69
75
|
True
|
|
70
|
-
>>>
|
|
76
|
+
>>> find_pmc_article_body(BeautifulSoup("<body><p>x</p></body>", "html.parser")) is None
|
|
71
77
|
True
|
|
72
78
|
"""
|
|
73
79
|
for class_name in PMC_ARTICLE_BODY_CLASSES:
|
|
@@ -192,7 +198,7 @@ class PMCFullTextProvider(FullTextProvider):
|
|
|
192
198
|
)
|
|
193
199
|
|
|
194
200
|
soup = BeautifulSoup(response.content, "html.parser")
|
|
195
|
-
article_body =
|
|
201
|
+
article_body = find_pmc_article_body(soup)
|
|
196
202
|
if article_body:
|
|
197
203
|
# The region is selected here, but the text comes out of the shared
|
|
198
204
|
# extractor rather than a private copy of the paragraph walk, so
|
|
@@ -343,7 +343,18 @@ class ReferenceFetcher:
|
|
|
343
343
|
|
|
344
344
|
# Check disk cache
|
|
345
345
|
if not force_refresh:
|
|
346
|
-
cached = self._load_from_disk(
|
|
346
|
+
cached = self._load_from_disk(
|
|
347
|
+
normalized_reference_id,
|
|
348
|
+
allow_stale=self.config.trust_cached_entries,
|
|
349
|
+
allow_stale_html=self.config.trust_cached_entries,
|
|
350
|
+
)
|
|
351
|
+
if cached and self.config.trust_cached_entries:
|
|
352
|
+
# Trusted: serve what is on disk. No staleness re-fetch and no
|
|
353
|
+
# full-text retry, so nothing can shorten the body or replace it
|
|
354
|
+
# with a bot-check page.
|
|
355
|
+
return self._remember(
|
|
356
|
+
normalized_reference_id, FetchOutcome(content=cached)
|
|
357
|
+
)
|
|
347
358
|
if cached:
|
|
348
359
|
# A record cached as abstract_only may predate full-text support, or
|
|
349
360
|
# reflect a prior transient failure. Give the chain one more chance
|
|
@@ -521,9 +532,12 @@ class ReferenceFetcher:
|
|
|
521
532
|
cached.content_type,
|
|
522
533
|
)
|
|
523
534
|
|
|
524
|
-
# May its text be served? Asked
|
|
525
|
-
#
|
|
526
|
-
# falls back to the freshly fetched abstract
|
|
535
|
+
# May its text be served? Asked with the consumer's serve policy, the
|
|
536
|
+
# same one _stale_fallback uses. By default stale HTML is withheld and
|
|
537
|
+
# validation falls back to the freshly fetched abstract; a consumer that
|
|
538
|
+
# has opted in gets the cached body instead. This is the path a refresh
|
|
539
|
+
# normally takes -- the source still answers, with an abstract -- so it
|
|
540
|
+
# is the one most consumers see, not _stale_fallback.
|
|
527
541
|
servable = self._load_from_disk(
|
|
528
542
|
normalized_reference_id,
|
|
529
543
|
allow_stale=True,
|
|
@@ -1536,8 +1550,11 @@ class ReferenceFetcher:
|
|
|
1536
1550
|
):
|
|
1537
1551
|
logger.warning(
|
|
1538
1552
|
"Refusing stale HTML full text for %s: it may be a repository "
|
|
1539
|
-
"landing page.
|
|
1540
|
-
"
|
|
1553
|
+
"landing page. A re-fetch repairs it where the source still "
|
|
1554
|
+
"serves full text -- but not for a PMC-only article, which "
|
|
1555
|
+
"answers a Python HTTP client with a bot-check interstitial on "
|
|
1556
|
+
"an HTTP 200. Set serve_stale_html_full_text if this cache is "
|
|
1557
|
+
"one you review.",
|
|
1541
1558
|
reference_id,
|
|
1542
1559
|
)
|
|
1543
1560
|
return None
|
|
@@ -23,6 +23,7 @@ from Bio import Entrez # type: ignore
|
|
|
23
23
|
from bs4 import BeautifulSoup # type: ignore
|
|
24
24
|
import requests # type: ignore
|
|
25
25
|
|
|
26
|
+
from linkml_reference_validator.etl.fulltext.pmc import find_pmc_article_body
|
|
26
27
|
from linkml_reference_validator.models import ReferenceContent, ReferenceValidationConfig
|
|
27
28
|
from linkml_reference_validator.etl.extract.html import HTMLExtractor
|
|
28
29
|
from linkml_reference_validator.etl.extract import MIN_FULLTEXT_CHARS
|
|
@@ -582,12 +583,7 @@ class PMIDSource(ReferenceSource):
|
|
|
582
583
|
return None
|
|
583
584
|
|
|
584
585
|
soup = BeautifulSoup(response.content, "html.parser")
|
|
585
|
-
|
|
586
|
-
# ``div`` to a ``section`` and changed class, so both call sites went
|
|
587
|
-
# blind at once (dismech#12672).
|
|
588
|
-
from linkml_reference_validator.etl.fulltext.pmc import _find_pmc_article_body
|
|
589
|
-
|
|
590
|
-
article_body = _find_pmc_article_body(soup)
|
|
586
|
+
article_body = find_pmc_article_body(soup)
|
|
591
587
|
|
|
592
588
|
if article_body:
|
|
593
589
|
# Region selected here, text extracted by the shared extractor, for
|
|
@@ -524,6 +524,18 @@ class ReferenceValidationConfig(BaseModel):
|
|
|
524
524
|
"the entry is left un-stamped so a later run still retries."
|
|
525
525
|
),
|
|
526
526
|
)
|
|
527
|
+
trust_cached_entries: bool = Field(
|
|
528
|
+
default=False,
|
|
529
|
+
description=(
|
|
530
|
+
"If True, use a cache entry as it is on disk and never re-fetch it "
|
|
531
|
+
"because an extractor stamp is missing or old. Off by default, so "
|
|
532
|
+
"caches still migrate. Set it for a committed, reviewed cache, where "
|
|
533
|
+
"a re-fetch is not an improvement but a risk: it can shorten a body, "
|
|
534
|
+
"cache a bot-check page, or replace text a curator quoted. Imperfect "
|
|
535
|
+
"cached text is a content problem to fix deliberately, not on every "
|
|
536
|
+
"validation run."
|
|
537
|
+
),
|
|
538
|
+
)
|
|
527
539
|
full_text_providers: list[str] = Field(
|
|
528
540
|
default_factory=lambda: ["pmc", "epmc_preprint", "unpaywall", "openalex"],
|
|
529
541
|
description=(
|
|
@@ -18,7 +18,7 @@ from __future__ import annotations
|
|
|
18
18
|
import pytest
|
|
19
19
|
from bs4 import BeautifulSoup
|
|
20
20
|
|
|
21
|
-
from linkml_reference_validator.etl.fulltext.pmc import
|
|
21
|
+
from linkml_reference_validator.etl.fulltext.pmc import find_pmc_article_body
|
|
22
22
|
|
|
23
23
|
BODY = "<p>" + ("Real article prose. " * 40) + "</p>"
|
|
24
24
|
|
|
@@ -34,7 +34,7 @@ BODY = "<p>" + ("Real article prose. " * 40) + "</p>"
|
|
|
34
34
|
)
|
|
35
35
|
def test_article_containers_are_found(markup, label):
|
|
36
36
|
soup = BeautifulSoup(markup, "html.parser")
|
|
37
|
-
assert
|
|
37
|
+
assert find_pmc_article_body(soup) is not None, f"{label} should be recognised"
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
@pytest.mark.parametrize(
|
|
@@ -49,11 +49,27 @@ def test_article_containers_are_found(markup, label):
|
|
|
49
49
|
)
|
|
50
50
|
def test_pages_without_an_article_container_are_declined(markup, label):
|
|
51
51
|
soup = BeautifulSoup(markup, "html.parser")
|
|
52
|
-
assert
|
|
52
|
+
assert find_pmc_article_body(soup) is None, f"{label} must not be accepted"
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
def test_a_bare_body_element_is_not_an_article_container():
|
|
56
56
|
"""``<body>`` is on every page, so matching class="body" alone would accept
|
|
57
57
|
an interstitial. Only the article-body classes count."""
|
|
58
58
|
soup = BeautifulSoup(f"<html><body>{BODY}</body></html>", "html.parser")
|
|
59
|
-
assert
|
|
59
|
+
assert find_pmc_article_body(soup) is None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_a_challenge_page_carrying_a_classed_body_is_still_declined():
|
|
63
|
+
"""The interstitial arrives on an HTTP 200, so this selector is the only check.
|
|
64
|
+
|
|
65
|
+
PMC's challenge page carries a classed ``<body>``. None of its classes may
|
|
66
|
+
overlap the article-body list, or a bot-check page gets cached as full text.
|
|
67
|
+
"""
|
|
68
|
+
markup = (
|
|
69
|
+
'<html><body class="usa-page bot-check">'
|
|
70
|
+
"<h1>Checking your browser before accessing</h1>"
|
|
71
|
+
"<p>Enable JavaScript and cookies to continue.</p>"
|
|
72
|
+
"</body></html>"
|
|
73
|
+
)
|
|
74
|
+
soup = BeautifulSoup(markup, "html.parser")
|
|
75
|
+
assert find_pmc_article_body(soup) is None
|
|
@@ -22,6 +22,7 @@ from unittest.mock import patch
|
|
|
22
22
|
|
|
23
23
|
from linkml_reference_validator.etl.reference_fetcher import ReferenceFetcher
|
|
24
24
|
from linkml_reference_validator.models import (
|
|
25
|
+
ReferenceContent,
|
|
25
26
|
ReferenceValidationConfig,
|
|
26
27
|
)
|
|
27
28
|
|
|
@@ -73,3 +74,61 @@ def test_opting_in_does_not_stamp_or_rewrite_the_entry(tmp_path):
|
|
|
73
74
|
text = (tmp_path / "PMID_30929739.md").read_text(encoding="utf-8")
|
|
74
75
|
assert "extractor_version" not in text
|
|
75
76
|
assert BODY_SENTENCE in text
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _fetch_with_abstract_source(tmp_path, **cfg):
|
|
80
|
+
"""The path a refresh normally takes: the source answers, with an abstract.
|
|
81
|
+
|
|
82
|
+
``_stale_fallback`` needs the source to yield *nothing*. For a PMC-only
|
|
83
|
+
article PubMed still returns the abstract, so the refresh reaches
|
|
84
|
+
``_preserve_cached_full_text`` instead: the overwrite is refused, and then a
|
|
85
|
+
separate question decides what may be served. That second question is the
|
|
86
|
+
one the flag has to answer, and it is the one dismech actually hits.
|
|
87
|
+
"""
|
|
88
|
+
_cache(tmp_path)
|
|
89
|
+
fetcher = ReferenceFetcher(
|
|
90
|
+
ReferenceValidationConfig(
|
|
91
|
+
cache_dir=tmp_path, email="me@example.org", fetch_full_text=False, **cfg
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
fresh = ReferenceContent(
|
|
95
|
+
reference_id="PMID:30929739",
|
|
96
|
+
content="Just the abstract.",
|
|
97
|
+
content_type="abstract_only",
|
|
98
|
+
title="A paper",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
class _Source:
|
|
102
|
+
def fetch(self, identifier, config):
|
|
103
|
+
return fresh
|
|
104
|
+
|
|
105
|
+
with patch(
|
|
106
|
+
"linkml_reference_validator.etl.reference_fetcher"
|
|
107
|
+
".ReferenceSourceRegistry.get_source",
|
|
108
|
+
return_value=_Source,
|
|
109
|
+
):
|
|
110
|
+
return fetcher.fetch("PMID:30929739")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_refused_overwrite_serves_the_abstract_by_default(tmp_path):
|
|
114
|
+
content = _fetch_with_abstract_source(tmp_path)
|
|
115
|
+
assert content is not None
|
|
116
|
+
assert BODY_SENTENCE not in (content.content or ""), (
|
|
117
|
+
"by default the cached body stays withheld even though it was preserved"
|
|
118
|
+
)
|
|
119
|
+
assert "Just the abstract." in (content.content or "")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_refused_overwrite_serves_the_cached_body_when_opted_in(tmp_path):
|
|
123
|
+
content = _fetch_with_abstract_source(tmp_path, serve_stale_html_full_text=True)
|
|
124
|
+
assert content is not None
|
|
125
|
+
assert BODY_SENTENCE in (content.content or "")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_refused_overwrite_leaves_the_file_alone_either_way(tmp_path):
|
|
129
|
+
"""Serving is not overwriting: the entry keeps its body and stays un-stamped."""
|
|
130
|
+
for flag in (False, True):
|
|
131
|
+
_fetch_with_abstract_source(tmp_path, serve_stale_html_full_text=flag)
|
|
132
|
+
text = (tmp_path / "PMID_30929739.md").read_text(encoding="utf-8")
|
|
133
|
+
assert BODY_SENTENCE in text, f"body lost with flag={flag}"
|
|
134
|
+
assert "extractor_version" not in text, f"entry stamped with flag={flag}"
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""A committed cache can be used as it is, without a re-fetch on every run.
|
|
2
|
+
|
|
3
|
+
Staleness re-fetching is right for a cache the tool owns and can rebuild. For a
|
|
4
|
+
cache that is committed and reviewed it inverts: the re-fetch is the risk, not
|
|
5
|
+
the repair. It can shorten a body (dismech#12879), replace an entry with a
|
|
6
|
+
bot-check page served on an HTTP 200 (dismech#12867), or withhold text that
|
|
7
|
+
nothing can recover (dismech#12672).
|
|
8
|
+
|
|
9
|
+
`trust_cached_entries` says use the file. Off by default, so caches still
|
|
10
|
+
migrate for consumers that want that.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from unittest.mock import patch
|
|
16
|
+
|
|
17
|
+
import pytest
|
|
18
|
+
|
|
19
|
+
from linkml_reference_validator.etl.reference_fetcher import ReferenceFetcher
|
|
20
|
+
from linkml_reference_validator.models import ReferenceValidationConfig
|
|
21
|
+
|
|
22
|
+
BODY = "A sentence a curator quoted, present only in this cached body."
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _cache(tmp_path, content_type="full_text_html"):
|
|
26
|
+
(tmp_path / "PMID_20301575.md").write_text(
|
|
27
|
+
"---\nreference_id: PMID:20301575\ntitle: A paper\n"
|
|
28
|
+
f"content_type: {content_type}\n---\n\n## Content\n\n{BODY}\n",
|
|
29
|
+
encoding="utf-8",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _fetch(tmp_path, trust):
|
|
34
|
+
fetcher = ReferenceFetcher(
|
|
35
|
+
ReferenceValidationConfig(
|
|
36
|
+
cache_dir=tmp_path, email="me@example.org", trust_cached_entries=trust
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
calls = []
|
|
40
|
+
|
|
41
|
+
class _Source:
|
|
42
|
+
def fetch(self, identifier, config):
|
|
43
|
+
calls.append(identifier)
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
with patch(
|
|
47
|
+
"linkml_reference_validator.etl.reference_fetcher"
|
|
48
|
+
".ReferenceSourceRegistry.get_source",
|
|
49
|
+
return_value=_Source,
|
|
50
|
+
):
|
|
51
|
+
return fetcher.fetch("PMID:20301575"), calls
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@pytest.mark.parametrize(
|
|
55
|
+
"content_type", ["full_text_html", "full_text_xml", "abstract_only"]
|
|
56
|
+
)
|
|
57
|
+
def test_a_trusted_entry_is_served_without_any_fetch(tmp_path, content_type):
|
|
58
|
+
"""The point: no network call at all, so nothing can go wrong during one."""
|
|
59
|
+
_cache(tmp_path, content_type)
|
|
60
|
+
content, calls = _fetch(tmp_path, trust=True)
|
|
61
|
+
|
|
62
|
+
assert content is not None, f"{content_type} should be served from disk"
|
|
63
|
+
assert BODY in (content.content or "")
|
|
64
|
+
assert calls == [], f"a trusted entry must not be re-fetched, got {calls}"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_the_file_is_left_exactly_as_it_was(tmp_path):
|
|
68
|
+
"""Nothing is rewritten, so a validation run leaves the cache untouched."""
|
|
69
|
+
_cache(tmp_path)
|
|
70
|
+
before = (tmp_path / "PMID_20301575.md").read_text(encoding="utf-8")
|
|
71
|
+
_fetch(tmp_path, trust=True)
|
|
72
|
+
assert (tmp_path / "PMID_20301575.md").read_text(encoding="utf-8") == before
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_the_default_still_re_fetches(tmp_path):
|
|
76
|
+
"""Off by default: an unstamped entry is still treated as stale."""
|
|
77
|
+
_cache(tmp_path)
|
|
78
|
+
_content, calls = _fetch(tmp_path, trust=False)
|
|
79
|
+
assert calls == ["20301575"], (
|
|
80
|
+
"by default an unstamped entry is re-fetched; the flag must be opt-in"
|
|
81
|
+
)
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.claude/.mcp.json
RENAMED
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.claude/settings.json
RENAMED
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.copier-answers.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.github/dependabot.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/.pre-commit-config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/CODE_OF_CONDUCT.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/copilot-instructions.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/elements/.gitkeep
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/quickstart.md
RENAMED
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/reference/cli.md
RENAMED
|
File without changes
|
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/setup-guide.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/docs/troubleshooting.md
RENAMED
|
File without changes
|
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/examples/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/notebooks/README.md
RENAMED
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/project/README.md
RENAMED
|
File without changes
|
{linkml_reference_validator-0.3.0rc2 → linkml_reference_validator-0.3.0rc3}/project.justfile
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|