From b59897e07d53b30b2e8fd11180cf7dd94239afc3 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Wed, 6 Nov 2024 08:39:26 +0100 Subject: [PATCH 1/4] Update package to use snekpack (#171) --- .bumpversion.cfg | 28 - .cruft.json | 26 + {docs => .github}/CODE_OF_CONDUCT.md | 6 +- .github/CONTRIBUTING.md | 116 +++ .github/codecov.yml | 4 + .github/workflows/checks.yml | 46 - .github/workflows/cruft.yml | 79 ++ .github/workflows/ndex.yml | 2 +- .github/workflows/tests.yml | 78 ++ .gitignore | 786 +++++++++++++++++- .readthedocs.yml | 7 +- MANIFEST.in | 10 +- README.md | 201 +++-- docs/CONTRIBUTING.md | 108 +-- docs/source/conf.py | 68 +- docs/source/custom.rst | 2 +- docs/source/predict.rst | 7 +- notebooks/Process CLO Mappings.ipynb | 8 - notebooks/cancer-cell-line-mappings.ipynb | 2 +- notebooks/chemicals-unbiased-evaluation.ipynb | 2 - .../clinical-trials-indication-mapping.ipynb | 6 +- notebooks/curation-benefit.ipynb | 63 +- notebooks/curation-precision.ipynb | 1 - notebooks/hetionet-pathway-duplication.ipynb | 4 +- pyproject.toml | 277 +++++- scripts/add_mesh_xrefs_to_cl_owl.py | 9 +- scripts/add_mesh_xrefs_to_doid_owl.py | 9 +- scripts/add_mesh_xrefs_to_mondo.py | 6 +- scripts/add_mesh_xrefs_to_uberon_obo.py | 6 +- scripts/bulk_curation.py | 4 +- scripts/generate_agrovoc_mappings.py | 2 - scripts/generate_ccle_mappings.py | 2 - scripts/generate_chebi_mesh_mappings.py | 4 +- scripts/generate_cl_mesh_mappings.py | 6 +- scripts/generate_clo_mesh_mappings.py | 2 - scripts/generate_doid_mappings.py | 2 - scripts/generate_maxo_mesh_mappings.py | 2 - scripts/generate_mesh_uniprot_mappings.py | 4 +- scripts/generate_mondo_mappings.py | 2 - scripts/generate_pathway_mappings.py | 2 - scripts/generate_vo_drugbank.py | 2 - scripts/generate_vo_mesh_mappings.py | 3 - scripts/generate_wikipathways_orthologs.py | 4 +- scripts/import_compath.py | 2 - scripts/import_gilda_mappings.py | 6 +- scripts/kegg_mappings.py | 4 +- setup.cfg | 166 ---- src/biomappings/__init__.py | 2 - src/biomappings/__main__.py | 2 - src/biomappings/apicuron.py | 7 +- src/biomappings/cli.py | 4 - src/biomappings/contribute/obo.py | 10 +- src/biomappings/contribute/utils.py | 4 +- src/biomappings/export_sssom.py | 2 - src/biomappings/gilda_utils.py | 7 +- src/biomappings/graph.py | 8 +- src/biomappings/mapping_graph.py | 11 +- src/biomappings/paper_analysis.py | 10 +- src/biomappings/py.typed | 1 + src/biomappings/resources/__init__.py | 39 +- src/biomappings/resources/semapv.py | 8 +- src/biomappings/summary.py | 11 +- src/biomappings/testing.py | 29 +- src/biomappings/upload_ndex.py | 6 +- src/biomappings/utils.py | 19 +- src/biomappings/wsgi.py | 26 +- tests/__init__.py | 2 - tests/test_validity.py | 2 - tox.ini | 209 +++-- 69 files changed, 1824 insertions(+), 781 deletions(-) delete mode 100644 .bumpversion.cfg create mode 100644 .cruft.json rename {docs => .github}/CODE_OF_CONDUCT.md (98%) create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/codecov.yml delete mode 100644 .github/workflows/checks.yml create mode 100644 .github/workflows/cruft.yml create mode 100644 .github/workflows/tests.yml delete mode 100644 setup.cfg create mode 100644 src/biomappings/py.typed diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index 89d2593c..00000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,28 +0,0 @@ -[bumpversion] -current_version = 0.3.8-dev -commit = True -tag = False -parse = (?P\d+)\.(?P\d+)\.(?P\d+)(?:-(?P[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?P[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))? -serialize = - {major}.{minor}.{patch}-{release}+{build} - {major}.{minor}.{patch}+{build} - {major}.{minor}.{patch}-{release} - {major}.{minor}.{patch} - -[bumpversion:part:release] -optional_value = production -first_value = dev -values = - dev - production - -[bumpverion:part:build] -values = [0-9A-Za-z-]+ - -[bumpversion:file:docs/source/conf.py] -search = release = "{current_version}" -replace = release = "{new_version}" - -[bumpversion:file:setup.cfg] -search = version = {current_version} -replace = version = {new_version} diff --git a/.cruft.json b/.cruft.json new file mode 100644 index 00000000..d9f9e24e --- /dev/null +++ b/.cruft.json @@ -0,0 +1,26 @@ +{ + "template": "https://github.com/cthoyt/cookiecutter-snekpack", + "commit": "12edfcfa5f519467b5d834f0d4e706fb7cf4f065", + "checkout": null, + "context": { + "cookiecutter": { + "package_name": "biomappings", + "package_name_stylized": "Biomappings", + "short_description": "Curated and predicted mappings between biomedical identifiers in different namespaces", + "author_name": "Charles Tapley Hoyt", + "author_email": "cthoyt@gmail.com", + "github_organization_name": "biopragmatics", + "github_repository_name": "biomappings", + "command_line_interface": true, + "gitlab": false, + "runner": "tox", + "__runner": "tox -e", + "__runner_uv": "--with tox-uv tox -e", + "__runner_pip": "tox tox-uv", + "__runner_tests": "py", + "__gh_slug": "biopragmatics/biomappings", + "_template": "https://github.com/cthoyt/cookiecutter-snekpack" + } + }, + "directory": null +} diff --git a/docs/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md similarity index 98% rename from docs/CODE_OF_CONDUCT.md rename to .github/CODE_OF_CONDUCT.md index f124ae1f..bbcc13da 100644 --- a/docs/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -1,8 +1,4 @@ ---- -layout: page -title: Contributor Covenant Code of Conduct -permalink: /coc ---- +# Contributor Covenant Code of Conduct ## Our Pledge diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..5d680ad1 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,116 @@ +# Contributing + +Contributions to this repository are welcomed and encouraged. + +## Code Contribution + +This project uses the [GitHub Flow](https://guides.github.com/introduction/flow) +model for code contributions. Follow these steps: + +1. [Create a fork](https://help.github.com/articles/fork-a-repo) of the upstream + repository + at [`biopragmatics/biomappings`](https://github.com/biopragmatics/biomappings) + on your GitHub account (or in one of your organizations) +2. [Clone your fork](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) + with `git clone https://github.com//biomappings.git` +3. Make and commit changes to your fork with `git commit` +4. Push changes to your fork with `git push` +5. Repeat steps 3 and 4 as needed +6. Submit a pull request back to the upstream repository + +### Merge Model + +This repository +uses [squash merges](https://docs.github.com/en/github/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-pull-request-commits) +to group all related commits in a given pull request into a single commit upon +acceptance and merge into the main branch. This has several benefits: + +1. Keeps the commit history on the main branch focused on high-level narrative +2. Enables people to make lots of small commits without worrying about muddying + up the commit history +3. Commits correspond 1-to-1 with pull requests + +### Code Style + +This project uses `tox` for running code quality checks. Start by installing +it with `pip install tox tox-uv`. + +This project encourages the use of optional static typing. It +uses [`mypy`](http://mypy-lang.org/) as a type checker. You can check if +your code passes `mypy` with `tox -e mypy`. + +This project uses [`ruff`](https://docs.astral.sh/ruff/) to automatically +enforce a consistent code style. You can apply `ruff format` and other pre-configured +formatters with `tox -e format`. + +This project uses [`ruff`](https://docs.astral.sh/ruff/) and several plugins for +additional checks of documentation style, security issues, good variable +nomenclature, and more (see `pyproject.toml` for a list of Ruff plugins). You can check if your +code passes `ruff check` with `tox -e lint`. + +Each of these checks are run on each commit using GitHub Actions as a continuous +integration service. Passing all of them is required for accepting a +contribution. If you're unsure how to address the feedback from one of these +tools, please say so either in the description of your pull request or in a +comment, and we will help you. + +### Logging + +Python's builtin `print()` should not be used (except when writing to files), +it's checked by the +[`flake8-print` (T20)](https://docs.astral.sh/ruff/rules/#flake8-print-t20) plugin to `ruff`. If +you're in a command line setting or `main()` function for a module, you can use +`click.echo()`. Otherwise, you can use the builtin `logging` module by adding +`logger = logging.getLogger(__name__)` below the imports at the top of your +file. + +### Documentation + +All public functions (i.e., not starting with an underscore `_`) must be +documented using +the [sphinx documentation format](https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html#the-sphinx-docstring-format). +The [`darglint2`](https://github.com/akaihola/darglint2) tool +reports on functions that are not fully documented. + +This project uses [`sphinx`](https://www.sphinx-doc.org) to automatically build +documentation into a narrative structure. You can check that the documentation +builds properly in an isolated environment with `tox -e docs-test` and actually +build it locally with `tox -e docs`. + +### Testing + +Functions in this repository should be unit tested. These can either be written +using the `unittest` framework in the `tests/` directory or as embedded +doctests. You can check that the unit tests pass with `tox -e py` +and that the doctests pass with `tox -e doctests`. These tests are required to pass for +accepting a contribution. + +### Syncing your fork + +If other code is updated before your contribution gets merged, you might need to +resolve conflicts against the main branch. After cloning, you should add the +upstream repository with + +```shell +$ git remote add biopragmatics https://github.com/biopragmatics/biomappings.git +``` + +Then, you can merge upstream code into your branch. You can also use the GitHub +UI to do this by +following [this tutorial](https://docs.github.com/en/github/collaborating-with-pull-requests/working-with-forks/syncing-a-fork). + +### Python Version Compatibility + +This project aims to support all versions of Python that have not passed their +end-of-life dates. After end-of-life, the version will be removed from the Trove +qualifiers in the `pyproject.toml` and from the GitHub Actions testing +configuration. + +See https://endoflife.date/python for a timeline of Python release and +end-of-life dates. + +## Acknowledgements + +These code contribution guidelines are derived from +the [cthoyt/cookiecutter-snekpack](https://github.com/cthoyt/cookiecutter-snekpack) +Python package template. They're free to reuse and modify as long as they're properly acknowledged. diff --git a/.github/codecov.yml b/.github/codecov.yml new file mode 100644 index 00000000..2caff9e8 --- /dev/null +++ b/.github/codecov.yml @@ -0,0 +1,4 @@ +# see https://docs.codecov.com/v4.6/docs/codecov-yaml +ignore: + - "src/biomappings/__main__.py" + - "src/biomappings/cli.py" diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml deleted file mode 100644 index ebf64fc2..00000000 --- a/.github/workflows/checks.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Check mappings - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - lint: - name: Lint - runs-on: ubuntu-latest - strategy: - matrix: - python-version: [ "3.11", "3.8" ] - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: pip install tox - - name: Check manifest - run: tox -e manifest - - name: Check package metadata with Pyroma - run: tox -e pyroma - - name: Check code style with flake8 - run: tox -e flake8 - tests: - name: Tests - runs-on: ubuntu-latest - strategy: - matrix: - python-version: [ "3.11", "3.8" ] - steps: - - name: Check out repo - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: pip install tox - - name: Run checks - run: tox -e py diff --git a/.github/workflows/cruft.yml b/.github/workflows/cruft.yml new file mode 100644 index 00000000..7442c9f5 --- /dev/null +++ b/.github/workflows/cruft.yml @@ -0,0 +1,79 @@ +# from https://cruft.github.io/cruft/#automating-updates-with-github-actions + +name: Update repository with Cruft + +permissions: + contents: write + pull-requests: write + +on: + workflow_dispatch: + schedule: + - cron: "0 2 * * 1" # Every Monday at 2am + +jobs: + update: + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + include: + - add-paths: . + body: Use this to merge the changes to this repository. + branch: cruft/update + commit-message: "chore: accept new Cruft update" + title: New updates detected with Cruft + - add-paths: .cruft.json + body: Use this to reject the changes in this repository. + branch: cruft/reject + commit-message: "chore: reject new Cruft update" + title: Reject new updates detected with Cruft + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-python@v4 + with: + python-version: "3.10" + + - name: Install Cruft + run: pip3 install cruft + + - name: Check if update is available + continue-on-error: false + id: check + run: | + CHANGES=0 + if [ -f .cruft.json ]; then + if ! cruft check; then + CHANGES=1 + fi + else + echo "No .cruft.json file" + fi + + echo "has_changes=$CHANGES" >> "$GITHUB_OUTPUT" + + - name: Run update if available + if: steps.check.outputs.has_changes == '1' + run: | + git config --global user.email "you@example.com" + git config --global user.name "GitHub" + + cruft update --skip-apply-ask --refresh-private-variables + git restore --staged . + + - name: Create pull request + if: steps.check.outputs.has_changes == '1' + uses: peter-evans/create-pull-request@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + add-paths: ${{ matrix.add-paths }} + commit-message: ${{ matrix.commit-message }} + branch: ${{ matrix.branch }} + delete-branch: true + branch-suffix: timestamp + title: ${{ matrix.title }} + body: | + This is an autogenerated PR. ${{ matrix.body }} + + [Cruft](https://cruft.github.io/cruft/) has detected updates from the Cookiecutter repository. diff --git a/.github/workflows/ndex.yml b/.github/workflows/ndex.yml index 571ebd6f..81d8b6b4 100644 --- a/.github/workflows/ndex.yml +++ b/.github/workflows/ndex.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@master - uses: actions/setup-python@v2 with: - python-version: "3.9" + python-version: "3.12" - name: Install dependencies run: | pip install tox diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..f65dca94 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,78 @@ +# This file configures the continuous integration (CI) system on GitHub. +# Introductory materials can be found here: https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions. +# Documentation for editing this file can be found here: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions + +name: Tests + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + lint: + name: Code Quality + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ "3.12", "3.9" ] + tox-command: ["manifest", "lint", "pyroma", "mypy"] + steps: + - uses: actions/checkout@v4 + - name: "Install uv" + uses: "astral-sh/setup-uv@v3" + with: + enable-cache: true + cache-dependency-glob: "pyproject.toml" + - name: "Run command" + run: | + uvx -p ${{ matrix.python-version }} --with tox-uv tox -e ${{ matrix.tox-command }} + + docs: + name: Documentation + runs-on: ubuntu-latest + strategy: + matrix: + # We only test documentation on the latest version + # sphinx 8.0 / sphinx-rtd-theme 3.0 discontinued Python 3.9 support + # a year early, which prompted re-thinking about this. + python-version: [ "3.12" ] + steps: + - uses: actions/checkout@v4 + - name: "Install uv" + uses: "astral-sh/setup-uv@v3" + with: + enable-cache: true + cache-dependency-glob: "pyproject.toml" + - name: Install dependencies + run: | + sudo apt-get install graphviz + - name: Check RST conformity with doc8 + run: uvx -p ${{ matrix.python-version }} --with tox-uv tox -e doc8 + - name: Check docstring coverage + run: uvx -p ${{ matrix.python-version }} --with tox-uv tox -e docstr-coverage + - name: Check documentation build with Sphinx + run: uvx -p ${{ matrix.python-version }} --with tox-uv tox -e docs-test + tests: + name: Tests + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest ] + python-version: [ "3.12", "3.9" ] + steps: + - uses: actions/checkout@v4 + - name: "Install uv" + uses: "astral-sh/setup-uv@v3" + with: + enable-cache: true + cache-dependency-glob: "pyproject.toml" + - name: Test with pytest and generate coverage file + run: + uvx -p ${{ matrix.python-version }} --with tox-uv tox -e py + - name: Upload coverage report to codecov + uses: codecov/codecov-action@v4 + if: success() + with: + file: coverage.xml diff --git a/.gitignore b/.gitignore index 9e97f919..fefaf5f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,296 @@ +# Created by https://www.toptal.com/developers/gitignore/api/macos,linux,windows,python,jupyternotebooks,jetbrains,pycharm,vim,emacs,visualstudiocode,visualstudio +# Edit at https://www.toptal.com/developers/gitignore?templates=macos,linux,windows,python,jupyternotebooks,jetbrains,pycharm,vim,emacs,visualstudiocode,visualstudio + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + + +### JetBrains ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +### JupyterNotebooks ### +# gitignore template for Jupyter Notebooks +# website: http://jupyter.org/ + +.ipynb_checkpoints +*/.ipynb_checkpoints/* + +# IPython +profile_default/ +ipython_config.py + +# Remove previous ipynb_checkpoints +# git rm -r .ipynb_checkpoints/ + +### Linux ### + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# AWS User-specific + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# SonarLint plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream + +### Python ### # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -10,7 +303,6 @@ __pycache__/ .Python build/ develop-eggs/ -dist/ downloads/ eggs/ .eggs/ @@ -20,7 +312,6 @@ parts/ sdist/ var/ wheels/ -pip-wheel-metadata/ share/python-wheels/ *.egg-info/ .installed.cfg @@ -50,6 +341,7 @@ coverage.xml *.py,cover .hypothesis/ .pytest_cache/ +cover/ # Translations *.mo @@ -70,20 +362,21 @@ instance/ # Sphinx documentation docs/_build/ +docs/build docs/source/api # PyBuilder +.pybuilder/ target/ # Jupyter Notebook -.ipynb_checkpoints # IPython -profile_default/ -ipython_config.py # pyenv -.python-version +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. @@ -92,6 +385,13 @@ ipython_config.py # install all needed dependencies. #Pipfile.lock +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ @@ -129,9 +429,473 @@ dmypy.json # Pyre type checker .pyre/ -scratch/ -.DS_Store +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] -# Jekyll -.jekyll-cache -_site +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# Support for Project snippet scope + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +*.code-workspace + +# Local History for Visual Studio Code + +# Windows Installer files from build outputs + +# JetBrains Rider +*.sln.iml + +### VisualStudio Patch ### +# Additional files built by Visual Studio + +# End of https://www.toptal.com/developers/gitignore/api/macos,linux,windows,python,jupyternotebooks,jetbrains,pycharm,vim,emacs,visualstudiocode,visualstudio + +scratch/ diff --git a/.readthedocs.yml b/.readthedocs.yml index 5ee31686..5212949a 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -6,9 +6,11 @@ version: 2 # Set the version of Python and other tools you might need build: - os: ubuntu-20.04 + os: ubuntu-22.04 + apt_packages: + - graphviz tools: - python: "3.9" + python: "3.12" python: install: @@ -16,4 +18,3 @@ python: path: . extra_requirements: - docs - - gilda diff --git a/MANIFEST.in b/MANIFEST.in index 408b5351..2f421792 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,13 +1,11 @@ graft src graft tests -prune docs prune scripts -prune docs/_data -prune tests/.pytest_cache prune notebooks -prune .pytest_cache +prune tests/.pytest_cache +prune docs -global-exclude *.py[cod] __pycache__ *.so *.dylib .DS_Store *.gpickle +global-exclude *.py[cod] __pycache__ *.so *.dylib .DS_Store *.gpickle .idea/** include README.md LICENSE -exclude tox.ini .flake8 .bumpversion.cfg docs/index.md docs/_config.yml .readthedocs.yml +exclude tox.ini .readthedocs.yml .cruft.json diff --git a/README.md b/README.md index 716022b1..fd3447da 100644 --- a/README.md +++ b/README.md @@ -7,33 +7,28 @@

- - Check mappings - + + Tests - PyPI - + PyPI - PyPI - Python Version - + PyPI - Python Version - PyPI - License - + PyPI - License - Documentation Status - - - DOI - - - Code style: black - + Documentation Status + + Codecov status + + Cookiecutter template from @cthoyt + + Ruff - Powered by the Bioregistry - - - Contributor Covenant - + Powered by the Bioregistry + + Contributor Covenant + + DOI

Biomappings is a repository of community curated and predicted equivalences and @@ -141,7 +136,7 @@ locally. Rather than editing files locally, this repository also comes with a web-based curation interface. Install the code in development mode with the `web` option (which installs `flask` and `flask-bootstrap`) using: -```bash +```console $ git clone git+https://github.com/biopragmatics/biomappings.git $ cd biomappings $ git checkout -b your-branch-name @@ -150,7 +145,7 @@ $ pip install -e .[web] The web application can be run with: -```bash +```console $ biomappings web ``` @@ -173,27 +168,27 @@ There are three places where curators of Biomappings are credited: and counts contributions curator 3. A curation leaderboard is automatically uploaded to [APICURON](https://apicuron.org/database?resource_uri=https:%2F%2Fbiomappings.github.io%2Fbiomappings%2F). -## ⬇️ Installation +## 🚀 Installation The most recent release can be installed from [PyPI](https://pypi.org/project/biomappings/) with: -```bash -$ pip install biomappings +```console +python3 -m pip install biomappings ``` The most recent code and data can be installed directly from GitHub with: -```bash -$ pip install git+https://github.com/biopragmatics/biomappings.git +```console +python3 -m pip install git+https://github.com/biopragmatics/biomappings.git ``` To install in development mode and create a new branch, use the following: -```bash +```console $ git clone git+https://github.com/biopragmatics/biomappings.git $ cd biomappings -$ pip install -e . +$ python3 -m pip install -e . ``` ## 💪 Usage @@ -231,7 +226,7 @@ Full documentation can be found on [ReadTheDocs](https://biomappings.readthedocs ### ⚖️ License -Code is licensed under the MIT License. Data are licensed under the CC0 License. +The code in this package is licensed under the MIT License. Data are licensed under the CC0 License. ### 📖 Citation @@ -277,64 +272,152 @@ This package was created with [@audreyfeldroy](https://github.com/audreyfeldroy)
See developer instructions - The final section of the README is for if you want to get involved by making a code contribution. ### Development Installation To install in development mode, use the following: -```bash -$ git clone git+https://github.com/biopragmatics/biomappings.git -$ cd biomappings -$ pip install -e . +```console +git clone git+https://github.com/biopragmatics/biomappings.git +cd biomappings +python3 -m pip install -e . +``` + +### Updating Package Boilerplate + +This project uses `cruft` to keep boilerplate (i.e., configuration, contribution guidelines, documentation +configuration) +up-to-date with the upstream cookiecutter package. Update with the following: + +```console +python3 -m pip install cruft +cruft update ``` +More info on Cruft's update command is +available [here](https://github.com/cruft/cruft?tab=readme-ov-file#updating-a-project). + ### 🥼 Testing -After cloning the repository and installing `tox` with `pip install tox`, the unit tests in the `tests/` folder can be -run reproducibly with: +After cloning the repository and installing `tox` with +`python3 -m pip install tox tox-uv`, +the unit tests in the `tests/` folder can be run reproducibly with: -```shell -$ tox +```console +tox -e py ``` -Additionally, these tests are automatically re-run with each commit in a [GitHub Action](https://github.com/biopragmatics/biomappings/actions?query=workflow%3ATests). +Additionally, these tests are automatically re-run with each commit in a +[GitHub Action](https://github.com/biopragmatics/biomappings/actions?query=workflow%3ATests). ### 📖 Building the Documentation The documentation can be built locally using the following: -```shell -$ git clone git+https://github.com/biopragmatics/biomappings.git -$ cd biomappings -$ tox -e docs -$ open docs/build/html/index.html -``` +```console +git clone git+https://github.com/biopragmatics/biomappings.git +cd biomappings +tox -e docs +open docs/build/html/index.html +``` The documentation automatically installs the package as well as the `docs` -extra specified in the [`setup.cfg`](setup.cfg). `sphinx` plugins +extra specified in the [`pyproject.toml`](pyproject.toml). `sphinx` plugins like `texext` can be added there. Additionally, they need to be added to the `extensions` list in [`docs/source/conf.py`](docs/source/conf.py). +The documentation can be deployed to [ReadTheDocs](https://readthedocs.io) using +[this guide](https://docs.readthedocs.io/en/stable/intro/import-guide.html). +The [`.readthedocs.yml`](.readthedocs.yml) YAML file contains all the configuration you'll need. +You can also set up continuous integration on GitHub to check not only that +Sphinx can build the documentation in an isolated environment (i.e., with `tox -e docs-test`) +but also that [ReadTheDocs can build it too](https://docs.readthedocs.io/en/stable/pull-requests.html). + +#### Configuring ReadTheDocs + +1. Log in to ReadTheDocs with your GitHub account to install the integration + at https://readthedocs.org/accounts/login/?next=/dashboard/ +2. Import your project by navigating to https://readthedocs.org/dashboard/import then clicking the plus icon next to + your repository +3. You can rename the repository on the next screen using a more stylized name (i.e., with spaces and capital letters) +4. Click next, and you're good to go! + ### 📦 Making a Release +#### Configuring Zenodo + +[Zenodo](https://zenodo.org) is a long-term archival system that assigns a DOI to each release of your package. + +1. Log in to Zenodo via GitHub with this link: https://zenodo.org/oauth/login/github/?next=%2F. This brings you to a + page that lists all of your organizations and asks you to approve installing the Zenodo app on GitHub. Click "grant" + next to any organizations you want to enable the integration for, then click the big green "approve" button. This + step only needs to be done once. +2. Navigate to https://zenodo.org/account/settings/github/, which lists all of your GitHub repositories (both in your + username and any organizations you enabled). Click the on/off toggle for any relevant repositories. When you make + a new repository, you'll have to come back to this + +After these steps, you're ready to go! After you make "release" on GitHub (steps for this are below), you can navigate +to https://zenodo.org/account/settings/github/repository/biopragmatics/biomappings +to see the DOI for the release and link to the Zenodo record for it. + +#### Registering with the Python Package Index (PyPI) + +You only have to do the following steps once. + +1. Register for an account on the [Python Package Index (PyPI)](https://pypi.org/account/register) +2. Navigate to https://pypi.org/manage/account and make sure you have verified your email address. A verification email + might not have been sent by default, so you might have to click the "options" dropdown next to your address to get to + the "re-send verification email" button +3. 2-Factor authentication is required for PyPI since the end of 2023 (see + this [blog post from PyPI](https://blog.pypi.org/posts/2023-05-25-securing-pypi-with-2fa/)). This means + you have to first issue account recovery codes, then set up 2-factor authentication +4. Issue an API token from https://pypi.org/manage/account/token + +#### Configuring your machine's connection to PyPI + +You have to do the following steps once per machine. + +```console +$ uv tool install keyring +$ keyring set https://upload.pypi.org/legacy/ __token__ +$ keyring set https://test.pypi.org/legacy/ __token__ +``` + +Note that this deprecates previous workflows using `.pypirc`. + +#### Uploading to PyPI + After installing the package in development mode and installing -`tox` with `pip install tox`, the commands for making a new release are contained within the `finish` environment -in `tox.ini`. Run the following from the shell: +`tox` with `python3 -m pip install tox tox-uv`, +run the following from the console: -```shell -$ tox -e finish +```console +tox -e finish ``` This script does the following: -1. Uses [Bump2Version](https://github.com/c4urself/bump2version) to switch the version number in the `setup.cfg`, - `src/biomappings/version.py`, and [`docs/source/conf.py`](docs/source/conf.py) to not have the `-dev` suffix -2. Packages the code in both a tar archive and a wheel using [`build`](https://github.com/pypa/build) -3. Uploads to PyPI using [`twine`](https://github.com/pypa/twine). Be sure to have a `.pypirc` file configured to avoid the need for manual input at this - step +1. Uses [bump-my-version](https://github.com/callowayproject/bump-my-version) to switch the version number in + the `pyproject.toml`, `CITATION.cff`, `src/biomappings/version.py`, + and [`docs/source/conf.py`](docs/source/conf.py) to not have the `-dev` suffix +2. Packages the code in both a tar archive and a wheel using + [`uv build`](https://docs.astral.sh/uv/guides/publish/#building-your-package) +3. Uploads to PyPI using [`uv publish`](https://docs.astral.sh/uv/guides/publish/#publishing-your-package). 4. Push to GitHub. You'll need to make a release going with the commit where the version was bumped. 5. Bump the version to the next patch. If you made big changes and want to bump the version by minor, you can - use `tox -e bumpversion minor` after. + use `tox -e bumpversion -- minor` after. + +#### Releasing on GitHub + +1. Navigate + to https://github.com/biopragmatics/biomappings/releases/new + to draft a new release +2. Click the "Choose a Tag" dropdown and select the tag corresponding to the release you just made +3. Click the "Generate Release Notes" button to get a quick outline of recent changes. Modify the title and description + as you see fit +4. Click the big green "Publish Release" button + +This will trigger Zenodo to assign a DOI to your release as well. +
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 2eb09260..4246a928 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -40,7 +40,7 @@ anywhere. ### Editing Mappings Sometimes, it becomes clear that a mapping was not correct. If this is the case, -than anyone is free to turn a manually curated mapping into an incorrect +then anyone is free to turn a manually curated mapping into an incorrect mapping. However, the original contributor should be contacted (which should be possible via the `git blame` feature on GitHub as well as the ORCID identifier @@ -58,108 +58,4 @@ whose membership and conduct is described in the Biomappings's ## Code Contribution -This project uses the [GitHub Flow](https://guides.github.com/introduction/flow) -model for code contributions. Follow these steps: - -1. [Create a fork](https://help.github.com/articles/fork-a-repo) of the upstream - repository - at [`biopragmatics/biomappings`](https://github.com/biopragmatics/biomappings) - on your GitHub account (or in one of your organizations) -2. [Clone your fork](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) - with `git clone https://github.com//biomappings.git` -3. Make and commit changes to your fork with `git commit` -4. Push changes to your fork with `git push` -5. Repeat steps 3 and 4 as needed -6. Submit a pull request back to the upstream repository - -### Merge Model - -Biomappings uses [squash merges](https://docs.github.com/en/github/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-pull-request-commits) -to group all related commits in a given pull request into a single commit upon -acceptance and merge into the main branch. This has several benefits: - -1. Keeps the commit history on the main branch focused on high-level narrative -2. Enables people to make lots of small commits without worrying about muddying - up the commit history -3. Commits correspond 1-to-1 with pull requests - -### Code Style - -This project encourages the use of optional static typing. It -uses [`mypy`](http://mypy-lang.org/) as a type checker -and [`sphinx_autodoc_typehints`](https://github.com/agronholm/sphinx-autodoc-typehints) -to automatically generate documentation based on type hints. You can check if -your code passes `mypy` with `tox -e mypy`. - -This project uses [`black`](https://github.com/psf/black) to automatically -enforce a consistent code style. You can apply `black` and other pre-configured -linters with `tox -e lint`. - -This project uses [`flake8`](https://flake8.pycqa.org) and several plugins for -additional checks of documentation style, security issues, good variable -nomenclature, and more (see [`tox.ini`](tox.ini) for a list of flake8 plugins). You can check if your -code passes `flake8` with `tox -e flake8`. - -Each of these checks are run on each commit using GitHub Actions as a continuous -integration service. Passing all of them is required for accepting a -contribution. If you're unsure how to address the feedback from one of these -tools, please say so either in the description of your pull request or in a -comment, and we will help you. - -### Logging - -Python's builtin `print()` should not be used (except when writing to files), -it's checked by the -[`flake8-print`](https://github.com/jbkahn/flake8-print) plugin to `flake8`. If -you're in a command line setting or `main()` function for a module, you can use -`click.echo()`. Otherwise, you can use the builtin `logging` module by adding -`logger = logging.getLogger(__name__)` below the imports at the top of your -file. - -### Documentation - -All public functions (i.e., not starting with an underscore `_`) must be -documented using the [sphinx documentation format](https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html#the-sphinx-docstring-format). -The [`darglint`](https://github.com/terrencepreilly/darglint) plugin to `flake8` -reports on functions that are not fully documented. - -This project uses [`sphinx`](https://www.sphinx-doc.org) to automatically build -documentation into a narrative structure. You can check that the documentation -properly builds with `tox -e docs-test`. - -### Testing - -Functions in this repository should be unit tested. These can either be written -using the `unittest` framework in the `tests/` directory or as embedded -doctests. You can check that the unit tests pass with `tox -e py` and that the -doctests pass with `tox -e doctests`. These tests are required to pass for -accepting a contribution. - -### Syncing your fork - -If other code is updated before your contribution gets merged, you might need to -resolve conflicts against the main branch. After cloning, you should add the -upstream repository with - -```shell -$ git remote add biopragmatics https://github.com/biopragmatics/biomappings.git -``` - -Then, you can merge upstream code into your branch. You can also use the GitHub -UI to do this by following [this tutorial](https://docs.github.com/en/github/collaborating-with-pull-requests/working-with-forks/syncing-a-fork). - -### Python Version Compatibility - -This project aims to support all versions of Python that have not passed their -end-of-life dates. After end-of-life, the version will be removed from the Trove -qualifiers in the [`setup.cfg`](https://github.com/biopragmatics/biomappings/blob/main/setup.cfg) -and from the GitHub Actions testing configuration. - -See https://endoflife.date/python for a timeline of Python release and -end-of-life dates. - -#### Review of Pull Requests - -Review of edits to existing records is handled by the Biomappings Core -Development Team, whose membership and conduct is described in the Biomappings's -[Project Governance](GOVERNANCE.md). +See the CONTRIBUTING.md file in the GitHub Repository. \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index d03acf2f..87b747c4 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,17 +1,16 @@ -# -*- coding: utf-8 -*- -# -# Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# http://www.sphinx-doc.org/en/master/config +""" +Configuration file for the Sphinx documentation builder. -# -- Path setup -------------------------------------------------------------- +This file does only contain a selection of the most common options. For a +full list see the documentation: +http://www.sphinx-doc.org/en/master/config -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# +-- Path setup -------------------------------------------------------------- + +If extensions (or modules to document with autodoc) are in another directory, +add these directories to ``sys.path`` here. If the directory is relative to the +documentation root, use ``os.path.abspath`` to make it absolute, like shown here. +""" import os import re @@ -31,13 +30,26 @@ # The short X.Y version. parsed_version = re.match( - "(?P\d+)\.(?P\d+)\.(?P\d+)(?:-(?P[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?P[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?", + r"(?P\d+)\.(?P\d+)\.(?P\d+)(?:-(?P[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?P[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?", release, ) -version = parsed_version.expand("\g.\g.\g") +version = parsed_version.expand(r"\g.\g.\g") if parsed_version.group("release"): - tags.add("prerelease") + tags.add("prerelease") # noqa:F821 + + +# See https://about.readthedocs.com/blog/2024/07/addons-by-default/ +# Define the canonical URL if you are using a custom domain on Read the Docs +html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "") + +# See https://about.readthedocs.com/blog/2024/07/addons-by-default/ +# Tell Jinja2 templates the build is running on Read the Docs +if os.environ.get("READTHEDOCS", "") == "True": + if "html_context" not in globals(): + html_context = {} + html_context["READTHEDOCS"] = True + # -- General configuration --------------------------------------------------- @@ -82,7 +94,9 @@ # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = ".rst" +source_suffix = { + ".rst": "restructuredtext", +} # The master toctree document. master_doc = "index" @@ -139,7 +153,7 @@ # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = "Biomappingsdoc" +htmlhelp_basename = "biomappings_doc" # -- Options for LaTeX output ------------------------------------------------ @@ -200,7 +214,7 @@ "Biomappings Documentation", author, "Charles Tapley Hoyt", - "Community curated mappings between biomedical entities.", + "Curated and predicted mappings between biomedical identifiers in different namespaces", "Miscellaneous", ), ] @@ -227,12 +241,17 @@ # -- Options for intersphinx extension --------------------------------------- # Example configuration for intersphinx: refer to the Python standard library. +# Note: don't add trailing slashes, since sphinx adds "/objects.inv" to the end intersphinx_mapping = { - "python": ("https://docs.python.org/3/", None), - "pyobo": ("https://pyobo.readthedocs.io/en/stable/", None), - "gilda": ("https://gilda.readthedocs.io/en/stable/", None), - "pykeen": ("https://pykeen.readthedocs.io/en/stable/", None), - "bioregistry": ("https://bioregistry.readthedocs.io/en/stable/", None), + "python": ("https://docs.python.org/3", None), + "pyobo": ("https://pyobo.readthedocs.io/en/stable", None), + "gilda": ("https://gilda.readthedocs.io/en/stable", None), + "pykeen": ("https://pykeen.readthedocs.io/en/stable", None), + "bioregistry": ("https://bioregistry.readthedocs.io/en/stable", None), + "pandas": ("https://pandas.pydata.org/docs", None), + "sklearn": ("https://scikit-learn.org/stable", None), + "numpy": ("https://numpy.org/doc/stable", None), + "scipy": ("https://docs.scipy.org/doc/scipy", None), } autoclass_content = "both" @@ -240,3 +259,6 @@ # Don't sort alphabetically, explained at: # https://stackoverflow.com/questions/37209921/python-how-not-to-sort-sphinx-output-in-alphabetical-order autodoc_member_order = "bysource" + +todo_include_todos = True +todo_emit_warnings = True diff --git a/docs/source/custom.rst b/docs/source/custom.rst index 40ee454c..b1e20aac 100644 --- a/docs/source/custom.rst +++ b/docs/source/custom.rst @@ -84,4 +84,4 @@ as :class:`unittest` itself, :mod:`pytest`, or others. Therefore you can run the .. code-block:: python -m pip install biomappings[tests] - python -m pytest biomappings_custom.py \ No newline at end of file + python -m pytest biomappings_custom.py diff --git a/docs/source/predict.rst b/docs/source/predict.rst index cfd5eff2..b28006e3 100644 --- a/docs/source/predict.rst +++ b/docs/source/predict.rst @@ -27,10 +27,11 @@ The following examples have already been used to predict mappings 3. `generate_cl_mesh_mappings `_ implements a fully custom matching workflow, also built on Gilda (in case you want to roll your own) 4. `generate_mesh_uniprot_mappings `_ - uses rule-based matching between MeSH and UniProt proteins that relies on the fact that the MeSH terms were generated from UniProt names. + uses rule-based matching between MeSH and UniProt proteins that relies on the fact that the MeSH terms were + generated from UniProt names. 5. `generate_wikipathways_orthologs `_ - uses a rule-based method for matching orthologous pathways in WikiPathways that relies on the fact that the names are procedurally generated - with a certain template + uses a rule-based method for matching orthologous pathways in WikiPathways that relies on the fact that the names + are procedurally generated with a certain template We also have a work-in-progress example using :mod:`pykeen` for generating mappings based on knowledge graph embeddings (both in the transductive and inductive setting). diff --git a/notebooks/Process CLO Mappings.ipynb b/notebooks/Process CLO Mappings.ipynb index ba2ed449..cbafe78e 100644 --- a/notebooks/Process CLO Mappings.ipynb +++ b/notebooks/Process CLO Mappings.ipynb @@ -25,13 +25,7 @@ "metadata": {}, "outputs": [], "source": [ - "from collections import Counter, defaultdict\n", - "\n", - "import bioontologies\n", - "import bioregistry\n", - "import click\n", "import pandas as pd\n", - "import pyobo\n", "from semra.api import (\n", " filter_mappings,\n", " get_many_to_many,\n", @@ -40,9 +34,7 @@ ")\n", "from semra.io import get_sssom_df\n", "from semra.sources.clo import get_clo_mappings\n", - "from tqdm.auto import tqdm\n", "\n", - "from biomappings import PredictionTuple\n", "from biomappings.resources import (\n", " PREDICTIONS_HEADER,\n", " append_prediction_tuples,\n", diff --git a/notebooks/cancer-cell-line-mappings.ipynb b/notebooks/cancer-cell-line-mappings.ipynb index a64debe5..37864835 100644 --- a/notebooks/cancer-cell-line-mappings.ipynb +++ b/notebooks/cancer-cell-line-mappings.ipynb @@ -18,7 +18,7 @@ "outputs": [], "source": [ "import time\n", - "from collections import Counter, defaultdict\n", + "from collections import Counter\n", "\n", "import bioregistry\n", "import bioversions\n", diff --git a/notebooks/chemicals-unbiased-evaluation.ipynb b/notebooks/chemicals-unbiased-evaluation.ipynb index ce87bc25..12c1984a 100644 --- a/notebooks/chemicals-unbiased-evaluation.ipynb +++ b/notebooks/chemicals-unbiased-evaluation.ipynb @@ -20,14 +20,12 @@ "metadata": {}, "outputs": [], "source": [ - "import itertools as itt\n", "import random\n", "import time\n", "from collections import Counter\n", "from pathlib import Path\n", "\n", "import pandas as pd\n", - "from tqdm.auto import tqdm\n", "\n", "import biomappings" ] diff --git a/notebooks/clinical-trials-indication-mapping.ipynb b/notebooks/clinical-trials-indication-mapping.ipynb index 9b6801e2..8e5d146b 100644 --- a/notebooks/clinical-trials-indication-mapping.ipynb +++ b/notebooks/clinical-trials-indication-mapping.ipynb @@ -18,13 +18,11 @@ "outputs": [], "source": [ "import time\n", - "from collections import Counter, defaultdict\n", + "from collections import defaultdict\n", "\n", "import gilda\n", - "import matplotlib.pyplot as plt\n", "import pandas\n", "import pystow\n", - "import seaborn as sns\n", "from indra_cogex.sources.clinicaltrials import get_correct_mesh_id\n", "from tqdm.auto import tqdm\n", "\n", @@ -285,7 +283,7 @@ "none_mappable = 0\n", "n_trials = len(interventions)\n", "unique_chemicals = set()\n", - "for trial, mesh_ids in interventions.items():\n", + "for _trial, mesh_ids in interventions.items():\n", " n_mappable = 0\n", " for mesh_id in mesh_ids:\n", " chebi_id = mesh_chebi_mappings.get(mesh_id)\n", diff --git a/notebooks/curation-benefit.ipynb b/notebooks/curation-benefit.ipynb index 4134ea1b..c7cb395c 100644 --- a/notebooks/curation-benefit.ipynb +++ b/notebooks/curation-benefit.ipynb @@ -17,25 +17,11 @@ "metadata": {}, "outputs": [], "source": [ - "import json\n", "import sys\n", "import time\n", - "from collections import Counter, defaultdict\n", - "from dataclasses import dataclass\n", - "from functools import lru_cache\n", - "from textwrap import dedent\n", + "from collections import defaultdict\n", "\n", - "import bioontologies\n", - "import bioregistry\n", - "import bioversions\n", - "import matplotlib.pyplot as plt\n", "import pandas as pd\n", - "import pyobo\n", - "import pystow\n", - "from IPython.display import HTML\n", - "from matplotlib_venn import venn2\n", - "from tabulate import tabulate\n", - "from tqdm.auto import tqdm\n", "\n", "import biomappings\n", "from biomappings.paper_analysis import (\n", @@ -43,7 +29,6 @@ " Result,\n", " get_non_obo_mappings,\n", " get_obo_mappings,\n", - " get_primary_mappings,\n", " index_mappings,\n", ")" ] @@ -562,15 +547,15 @@ "]\n", "ctd_gene_chemical_df = EVALUATION.ensure_csv(\n", " url=CTD_CHEMICAL_GENE_URL,\n", - " read_csv_kwargs=dict(\n", - " sep=\"\\t\",\n", - " comment=\"#\",\n", - " header=None,\n", - " dtype=str,\n", - " keep_default_na=False,\n", - " usecols=[1],\n", - " squeeze=True,\n", - " ),\n", + " read_csv_kwargs={\n", + " \"sep\": \"\\t\",\n", + " \"comment\": \"#\",\n", + " \"header\": None,\n", + " \"dtype\": str,\n", + " \"keep_default_na\": False,\n", + " \"usecols\": [1],\n", + " \"squeeze\": True,\n", + " },\n", ")" ] }, @@ -665,15 +650,15 @@ "\"\"\"\n", "ctd_chemical_diseases_df = EVALUATION.ensure_csv(\n", " url=CTD_CHEMICAL_DISEASES_URL,\n", - " read_csv_kwargs=dict(\n", - " sep=\"\\t\",\n", - " comment=\"#\",\n", - " header=None,\n", - " dtype=str,\n", - " keep_default_na=False,\n", - " usecols=[4],\n", - " squeeze=True,\n", - " ),\n", + " read_csv_kwargs={\n", + " \"sep\": \"\\t\",\n", + " \"comment\": \"#\",\n", + " \"header\": None,\n", + " \"dtype\": str,\n", + " \"keep_default_na\": False,\n", + " \"usecols\": [4],\n", + " \"squeeze\": True,\n", + " },\n", ")\n", "ctd_chemical_diseases_df.head()" ] @@ -982,11 +967,11 @@ "\n", "side_effects_df = EVALUATION.ensure_csv(\n", " url=SIDER_URL,\n", - " read_csv_kwargs=dict(\n", - " dtype=str,\n", - " header=None,\n", - " names=SIDE_EFFECTS_HEADER,\n", - " ),\n", + " read_csv_kwargs={\n", + " \"dtype\": str,\n", + " \"header\": None,\n", + " \"names\": SIDE_EFFECTS_HEADER,\n", + " },\n", ")\n", "side_effects_df" ] diff --git a/notebooks/curation-precision.ipynb b/notebooks/curation-precision.ipynb index d96a4a8b..ad9f7cd4 100644 --- a/notebooks/curation-precision.ipynb +++ b/notebooks/curation-precision.ipynb @@ -7,7 +7,6 @@ "metadata": {}, "outputs": [], "source": [ - "import itertools as itt\n", "import time\n", "from collections import Counter\n", "\n", diff --git a/notebooks/hetionet-pathway-duplication.ipynb b/notebooks/hetionet-pathway-duplication.ipynb index cdac07b3..66388d11 100644 --- a/notebooks/hetionet-pathway-duplication.ipynb +++ b/notebooks/hetionet-pathway-duplication.ipynb @@ -18,12 +18,10 @@ "outputs": [], "source": [ "import itertools as itt\n", - "import json\n", "import sys\n", "import time\n", - "from collections import Counter, defaultdict\n", + "from collections import Counter\n", "\n", - "import bioregistry\n", "import networkx as nx\n", "import pandas as pd\n", "import pystow\n", diff --git a/pyproject.toml b/pyproject.toml index b5cdd8d4..2bd7a724 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,275 @@ # See https://setuptools.readthedocs.io/en/latest/build_meta.html [build-system] requires = ["setuptools", "wheel"] -build-backend = "setuptools.build_meta:__legacy__" +build-backend = "setuptools.build_meta" -[tool.black] +[project] +name = "biomappings" +version = "0.3.8-dev" +description = "Curated and predicted mappings between biomedical identifiers in different namespaces" +readme = "README.md" +authors = [ + { name = "Charles Tapley Hoyt", email = "cthoyt@gmail.com" } +] +maintainers = [ + { name = "Charles Tapley Hoyt", email = "cthoyt@gmail.com" } +] + +# See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#classifiers +# Search tags using the controlled vocabulary at https://pypi.org/classifiers +classifiers = [ + "Development Status :: 4 - Beta", + "Environment :: Console", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Framework :: Pytest", + "Framework :: tox", + "Framework :: Sphinx", + "Programming Language :: Python", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering :: Chemistry", + "Topic :: Scientific/Engineering :: Bio-Informatics", +] +keywords = [ + "snekpack", # please keep this keyword to credit the cookiecutter-snekpack template + "cookiecutter", + "biology", + "chemistry", + "ontologies", + "ontology", + "nlp", +] + +# License Information. This can be any valid SPDX identifiers that can be resolved +# with URLs like https://spdx.org/licenses/MIT +# See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license +license = { file = "LICENSE" } + +requires-python = ">=3.9" +dependencies = [ + "networkx", + "requests", + "click", + "pyyaml", + "tqdm", + "pystow>=0.2.7", + "bioregistry>=0.10.43", +] + +[project.optional-dependencies] +tests = [ + "pytest", + "coverage", +] +docs = [ + "sphinx>=8", + "sphinx-rtd-theme>=3.0", + "sphinx-click", + "sphinx_automodapi", +] +charts = [ + "matplotlib", + "seaborn", +] +pyobo = [ + "pyobo>=0.8.4", +] +apicuron = [ + "apicuron_client", +] +web = [ + "flask", + "bootstrap-flask", + "flask-wtf", +] +gilda = [ + "gilda", + "pyobo>=0.8.4", +] +ndex = [ + "ndex2", +] +exports = [ + "sssom", +] + +# See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#urls +[project.urls] +"Bug Tracker" = "https://github.com/biopragmatics/biomappings/issues" +Homepage = "https://github.com/biopragmatics/biomappings" +Repository = "https://github.com/biopragmatics/biomappings.git" +Documentation = "https://biomappings.readthedocs.io" + +[tool.setuptools] +package-dir = { "" = "src" } + +[tool.setuptools.packages.find] +# this implicitly sets `packages = ":find"` +where = ["src"] # list of folders that contain the packages (["."] by default) + +# See https://setuptools.pypa.io/en/latest/userguide/datafiles.html +[tool.setuptools.package-data] +"*" = ["*.*"] + + +[project.scripts] +biomappings = "biomappings.cli:main" + + +[tool.cruft] +skip = [ + "**/__init__.py", + "tests/*" +] + +# MyPy, see https://mypy.readthedocs.io/en/stable/config_file.html +[tool.mypy] +plugins = [ + "pydantic.mypy", +] + +# Doc8, see https://doc8.readthedocs.io/en/stable/readme.html#ini-file-usage +[tool.doc8] +max-line-length = 120 + +# Pytest, see https://docs.pytest.org/en/stable/reference/customize.html#pyproject-toml +[tool.pytest.ini_options] +markers = [ + "slow: marks tests as slow (deselect with '-m \"not slow\"')", +] + +# Coverage, see https://coverage.readthedocs.io/en/latest/config.html +[tool.coverage.run] +branch = true +source = [ + "biomappings", +] +omit = [ + "tests/*", + "docs/*", +] + +[tool.coverage.paths] +source = [ + "src/biomappings", + ".tox/*/lib/python*/site-packages/biomappings", +] + +[tool.coverage.report] +show_missing = true +exclude_lines = [ + "pragma: no cover", + "raise NotImplementedError", + "if __name__ == \"__main__\":", + "if TYPE_CHECKING:", + "def __str__", + "def __repr__", +] + +[tool.ruff] line-length = 100 -target-version = ["py37", "py38", "py39", "py310", "py311"] +extend-include = ["*.ipynb"] + +[tool.ruff.lint] +# See https://docs.astral.sh/ruff/rules +extend-select = [ + "F", # pyflakes + "E", # pycodestyle errors + "W", # pycodestyle warnings + "C90", # mccabe + "I", # isort + "UP", # pyupgrade + "D", # pydocstyle + "DOC", # pydoclint + "B", # bugbear + "S", # bandit + "T20", # print + "N", # pep8 naming + "ERA", # eradicate commented out code + "NPY", # numpy checks + "RUF", # ruff rules + "C4", # comprehensions +] +ignore = [ + "D105", # Missing docstring in magic method + "E203", # Black conflicts with the following + "E501", # FIXME + "N818", + "C901", +] + +# See https://docs.astral.sh/ruff/settings/#per-file-ignores +[tool.ruff.lint.per-file-ignores] +# Ignore security issues in the version.py, which are inconsistent +"src/biomappings/version.py" = ["S603", "S607"] +# Ignore commented out code in Sphinx configuration file +"docs/source/conf.py" = ["ERA001"] +# Prints are okay in notebooks and scripts +"notebooks/**/*.ipynb" = [ + "T201", + "E722", # FIXME + "S101", # FIXME + "S311", # FIXME + "ERA001", # FIXME + "D103", # FIXME - functions should be in package +] +"scripts/*.py" = ["T201"] +# Commented code in paper analysis might be useful later +"src/biomappings/paper_analysis.py" = ["ERA001", "S301"] + +[tool.ruff.lint.pydocstyle] +convention = "pep257" + +[tool.ruff.lint.isort] +relative-imports-order = "closest-to-furthest" +known-third-party = [ + "tqdm", +] +known-first-party = [ + "biomappings", + "tests", +] + +[tool.ruff.format] +# see https://docs.astral.sh/ruff/settings/#format_docstring-code-format +docstring-code-format = true + +[tool.bumpversion] +current_version = "0.3.8-dev" +parse = "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)(?:-(?P[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+(?P[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?" +serialize = [ + "{major}.{minor}.{patch}-{release}+{build}", + "{major}.{minor}.{patch}+{build}", + "{major}.{minor}.{patch}-{release}", + "{major}.{minor}.{patch}", +] +commit = true +tag = false + +[tool.bumpversion.parts.release] +optional_value = "production" +first_value = "dev" +values = [ + "dev", + "production", +] + +[[tool.bumpversion.files]] +filename = "pyproject.toml" +search = "version = \"{current_version}\"" +replace = "version = \"{new_version}\"" + +[[tool.bumpversion.files]] +filename = "docs/source/conf.py" +search = "release = \"{current_version}\"" +replace = "release = \"{new_version}\"" -[tool.isort] -profile = "black" -multi_line_output = 3 -include_trailing_comma = true -reverse_relative = true +[[tool.bumpversion.files]] +filename = "src/biomappings/version.py" +search = "VERSION = \"{current_version}\"" +replace = "VERSION = \"{new_version}\"" diff --git a/scripts/add_mesh_xrefs_to_cl_owl.py b/scripts/add_mesh_xrefs_to_cl_owl.py index d0d9909e..ab2d11ef 100644 --- a/scripts/add_mesh_xrefs_to_cl_owl.py +++ b/scripts/add_mesh_xrefs_to_cl_owl.py @@ -16,7 +16,7 @@ def add_xref(lines, node, xref): def_idx = None xref_entries = [] for idx, line in enumerate(lines): - if line.startswith("# Class: obo:%s" % node_owl): + if line.startswith(f"# Class: obo:{node_owl}"): look_for_xref = True if look_for_xref and line.startswith('AnnotationAssertion(oboInOwl:hasDbXref "'): def_idx = idx @@ -29,10 +29,7 @@ def add_xref(lines, node, xref): break if look_for_xref and not line.strip(): start_xref_idx = def_idx - xref_str = 'AnnotationAssertion(oboInOwl:hasDbXref obo:%s "%s"^^xsd:string)\n' % ( - node_owl, - xref, - ) + xref_str = f'AnnotationAssertion(oboInOwl:hasDbXref obo:{node_owl} "{xref}"^^xsd:string)\n' xref_entries.append(xref_str) xref_entries = sorted(xref_entries) xr_idx = xref_entries.index(xref_str) @@ -46,7 +43,7 @@ def add_xref(lines, node, xref): m for m in mappings if m["source prefix"] == "cl" and m["target prefix"] == "mesh" ] - with open(EDITABLE_OWL_PATH, "r") as fh: + with open(EDITABLE_OWL_PATH) as fh: lines = fh.readlines() for mapping in cl_mappings: diff --git a/scripts/add_mesh_xrefs_to_doid_owl.py b/scripts/add_mesh_xrefs_to_doid_owl.py index ce3f7621..fe4c5d5b 100644 --- a/scripts/add_mesh_xrefs_to_doid_owl.py +++ b/scripts/add_mesh_xrefs_to_doid_owl.py @@ -29,7 +29,7 @@ def add_xref(lines, node, xref): xref_entries = [] for idx, line in enumerate(lines): # First, find the class with the given ID and start looking for xrefs - if line.startswith("# Class: obo:%s" % node_owl): + if line.startswith(f"# Class: obo:{node_owl}"): blank_counter = 0 look_for_xref = True def_idx = idx + 2 @@ -60,10 +60,7 @@ def add_xref(lines, node, xref): start_xref_idx = def_idx + 1 # We now have to render the xref string and sort xrefs alphabetically # to make sure we put the new one in the right place - xref_str = 'AnnotationAssertion(oboInOwl:hasDbXref obo:%s "%s"^^xsd:string)\n' % ( - node_owl, - xref, - ) + xref_str = f'AnnotationAssertion(oboInOwl:hasDbXref obo:{node_owl} "{xref}"^^xsd:string)\n' xref_entries.append(xref_str) xref_entries = sorted(xref_entries) xr_idx = xref_entries.index(xref_str) @@ -113,7 +110,7 @@ def add_xref(lines, node, xref): ] # Read the OWL file - with open(EDITABLE_OWL_PATH, "r") as fh: + with open(EDITABLE_OWL_PATH) as fh: lines = fh.readlines() review_cols = [ diff --git a/scripts/add_mesh_xrefs_to_mondo.py b/scripts/add_mesh_xrefs_to_mondo.py index 2ed4caaa..8f2470bd 100644 --- a/scripts/add_mesh_xrefs_to_mondo.py +++ b/scripts/add_mesh_xrefs_to_mondo.py @@ -17,7 +17,7 @@ def add_xref(lines, node, xref): xref_entries = [] for idx, line in enumerate(lines): # If this is the block for the given node, we start looking for xrefs - if line == "id: %s\n" % node: + if line == f"id: {node}\n": look_for_xref = True continue # If we are looking for xrefs @@ -48,7 +48,7 @@ def add_xref(lines, node, xref): xref_entries.append(xref) xref_entries = sorted(xref_entries) xr_idx = xref_entries.index(xref) - lines.insert(start_xref_idx + xr_idx, 'xref: %s {source="MONDO:equivalentTo"}\n' % xref) + lines.insert(start_xref_idx + xr_idx, f'xref: {xref} {{source="MONDO:equivalentTo"}}\n') return lines @@ -56,7 +56,7 @@ def add_xref(lines, node, xref): mappings = load_mappings() mondo_mappings = [m for m in mappings if m["source prefix"] == "mondo"] - with open(EDITABLE_OBO_PATH, "r") as fh: + with open(EDITABLE_OBO_PATH) as fh: lines = fh.readlines() for mapping in mondo_mappings: diff --git a/scripts/add_mesh_xrefs_to_uberon_obo.py b/scripts/add_mesh_xrefs_to_uberon_obo.py index 76436b1c..a9d4d8dc 100644 --- a/scripts/add_mesh_xrefs_to_uberon_obo.py +++ b/scripts/add_mesh_xrefs_to_uberon_obo.py @@ -15,7 +15,7 @@ def add_xref(lines, node, xref): def_idx = None xref_entries = [] for idx, line in enumerate(lines): - if line == "id: %s\n" % node: + if line == f"id: {node}\n": look_for_xref = True if look_for_xref and line.startswith("def"): def_idx = idx @@ -31,7 +31,7 @@ def add_xref(lines, node, xref): xref_entries.append(xref) xref_entries = sorted(xref_entries) xr_idx = xref_entries.index(xref) - lines.insert(start_xref_idx + xr_idx, "xref: %s\n" % xref) + lines.insert(start_xref_idx + xr_idx, f"xref: {xref}\n") return lines @@ -39,7 +39,7 @@ def add_xref(lines, node, xref): mappings = load_mappings() uberon_mappings = [m for m in mappings if m["source prefix"] == "uberon"] - with open(EDITABLE_OBO_PATH, "r") as fh: + with open(EDITABLE_OBO_PATH) as fh: lines = fh.readlines() for mapping in uberon_mappings: diff --git a/scripts/bulk_curation.py b/scripts/bulk_curation.py index 7c69d08c..5bb8f30b 100644 --- a/scripts/bulk_curation.py +++ b/scripts/bulk_curation.py @@ -1,9 +1,7 @@ -# -*- coding: utf-8 -*- - """Utilities for automated curation.""" import logging -from typing import Mapping +from collections.abc import Mapping from biomappings.resources import ( append_true_mappings, diff --git a/scripts/generate_agrovoc_mappings.py b/scripts/generate_agrovoc_mappings.py index bd4cbb27..7c0e24db 100644 --- a/scripts/generate_agrovoc_mappings.py +++ b/scripts/generate_agrovoc_mappings.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Generate mappings from AGRO to AGROVOC. Note: this script requires a minimum of PyOBO v0.7.0 to run. diff --git a/scripts/generate_ccle_mappings.py b/scripts/generate_ccle_mappings.py index 9ff914a6..de75f1ae 100644 --- a/scripts/generate_ccle_mappings.py +++ b/scripts/generate_ccle_mappings.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Generate mappings to Gilda from given PyOBO prefixes.""" import click diff --git a/scripts/generate_chebi_mesh_mappings.py b/scripts/generate_chebi_mesh_mappings.py index 388905c5..41d73601 100644 --- a/scripts/generate_chebi_mesh_mappings.py +++ b/scripts/generate_chebi_mesh_mappings.py @@ -31,8 +31,8 @@ predictions = [] n_redundant = 0 for pair in mesh_chebi_simple: - chebi_term = [term for term in pair if term.db == "CHEBI"][0] - mesh_term = [term for term in pair if term.db == "MESH"][0] + chebi_term = next(term for term in pair if term.db == "CHEBI") + mesh_term = next(term for term in pair if term.db == "MESH") mappings = bio_ontology.get_mappings("MESH", mesh_term.id) if ("CHEBI", chebi_term.id) in mappings: diff --git a/scripts/generate_cl_mesh_mappings.py b/scripts/generate_cl_mesh_mappings.py index fe1df2cc..4322a66d 100644 --- a/scripts/generate_cl_mesh_mappings.py +++ b/scripts/generate_cl_mesh_mappings.py @@ -21,7 +21,7 @@ continue has_mesh_id = False - for value in [data.get("def", "")] + data.get("synonym", []) + data.get("xref", []): + for value in [data.get("def", ""), *data.get("synonym", []), *data.get("xref", [])]: if re.findall(mesh_tree_pattern, value) or re.findall(mesh_id_pattern, value): has_mesh_id = True break @@ -43,9 +43,9 @@ groundings = match.get_groundings() mesh_ids |= {id for ns, id in groundings if ns == "MESH"} if len(mesh_ids) > 1: - print("Multiple MESH IDs for %s" % node) + print(f"Multiple MESH IDs for {node}") elif len(mesh_ids) == 1: - mesh_id = list(mesh_ids)[0] + mesh_id = next(iter(mesh_ids)) mappings[node] = mesh_id diff --git a/scripts/generate_clo_mesh_mappings.py b/scripts/generate_clo_mesh_mappings.py index bcfca5f8..df4a7de1 100644 --- a/scripts/generate_clo_mesh_mappings.py +++ b/scripts/generate_clo_mesh_mappings.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Generate mappings to CLO from to MeSH.""" import click diff --git a/scripts/generate_doid_mappings.py b/scripts/generate_doid_mappings.py index 316c147c..ac0d1d06 100644 --- a/scripts/generate_doid_mappings.py +++ b/scripts/generate_doid_mappings.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Generate mappings to Gilda from given PyOBO prefixes.""" import click diff --git a/scripts/generate_maxo_mesh_mappings.py b/scripts/generate_maxo_mesh_mappings.py index 4cdd5925..ddf0d8cb 100644 --- a/scripts/generate_maxo_mesh_mappings.py +++ b/scripts/generate_maxo_mesh_mappings.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Generate mappings to Gilda from given PyOBO prefixes.""" import click diff --git a/scripts/generate_mesh_uniprot_mappings.py b/scripts/generate_mesh_uniprot_mappings.py index 5f90cc00..b48a0fd9 100644 --- a/scripts/generate_mesh_uniprot_mappings.py +++ b/scripts/generate_mesh_uniprot_mappings.py @@ -1,9 +1,7 @@ -# -*- coding: utf-8 -*- - """Append lexical mappings between MeSH and UniProt.""" import re -from typing import Iterable +from collections.abc import Iterable from indra.databases import hgnc_client, mesh_client diff --git a/scripts/generate_mondo_mappings.py b/scripts/generate_mondo_mappings.py index 9151ef71..f75b6e36 100644 --- a/scripts/generate_mondo_mappings.py +++ b/scripts/generate_mondo_mappings.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Generate mappings to Gilda from given PyOBO prefixes.""" import click diff --git a/scripts/generate_pathway_mappings.py b/scripts/generate_pathway_mappings.py index 62b2517c..c52ce9b0 100644 --- a/scripts/generate_pathway_mappings.py +++ b/scripts/generate_pathway_mappings.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Generate mappings to Gilda from given PyOBO prefixes.""" from biomappings.gilda_utils import append_gilda_predictions diff --git a/scripts/generate_vo_drugbank.py b/scripts/generate_vo_drugbank.py index b5fc596b..c7211ae6 100644 --- a/scripts/generate_vo_drugbank.py +++ b/scripts/generate_vo_drugbank.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Generate mappings using Gilda from VO to DrugBank.""" import click diff --git a/scripts/generate_vo_mesh_mappings.py b/scripts/generate_vo_mesh_mappings.py index fedc79e5..dc5e7c53 100644 --- a/scripts/generate_vo_mesh_mappings.py +++ b/scripts/generate_vo_mesh_mappings.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Generate mappings using Gilda from VO to MeSH.""" import bioontologies @@ -33,7 +31,6 @@ def main(): values = [ value.strip().replace(" ", "") for value in p.value_raw.strip().split(";") ] - # print(node.luid, values) for value in values: # TODO this is place to extract other mapping types if not value.lower().startswith("mesh:"): diff --git a/scripts/generate_wikipathways_orthologs.py b/scripts/generate_wikipathways_orthologs.py index f27074f6..6db878a8 100644 --- a/scripts/generate_wikipathways_orthologs.py +++ b/scripts/generate_wikipathways_orthologs.py @@ -1,9 +1,7 @@ -# -*- coding: utf-8 -*- - """Generate orthologous relations between WikiPathways.""" import itertools as itt -from typing import Iterable +from collections.abc import Iterable import pyobo from gilda.process import normalize diff --git a/scripts/import_compath.py b/scripts/import_compath.py index e1e6bfae..222fcd98 100644 --- a/scripts/import_compath.py +++ b/scripts/import_compath.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Import mappings from ComPath.""" import pandas as pd diff --git a/scripts/import_gilda_mappings.py b/scripts/import_gilda_mappings.py index 25733c86..f3160df8 100644 --- a/scripts/import_gilda_mappings.py +++ b/scripts/import_gilda_mappings.py @@ -1,10 +1,8 @@ -# -*- coding: utf-8 -*- - """Append lexical mapping predictions from Gilda.""" import csv import os -from typing import Iterable +from collections.abc import Iterable from biomappings import load_false_mappings, load_mappings from biomappings.resources import PredictionTuple, append_prediction_tuples @@ -78,7 +76,7 @@ def get_mappings() -> Iterable[PredictionTuple]: confidence = 0.95 primary_mappings = get_primary_mappings() curated_mappings = get_curated_mappings() - with open(GILDA_MAPPINGS, "r") as fh: + with open(GILDA_MAPPINGS) as fh: for _, mesh_id, mesh_name, db_ns, db_id, db_name in csv.reader(fh, delimiter="\t"): if ("mesh", mesh_id, db_ns_mappings[db_ns], db_id) in primary_mappings or ( "mesh", diff --git a/scripts/kegg_mappings.py b/scripts/kegg_mappings.py index f77ff622..0ff2eb66 100644 --- a/scripts/kegg_mappings.py +++ b/scripts/kegg_mappings.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- - """Generate mappings to Gilda from given PyOBO prefixes.""" -from typing import Iterable +from collections.abc import Iterable import gilda import gilda.grounder diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 81883d02..00000000 --- a/setup.cfg +++ /dev/null @@ -1,166 +0,0 @@ -########################## -# Setup.py Configuration # -########################## -# Configuring setup() -[metadata] -name = biomappings -version = 0.3.8-dev -description = Curated and predicted mappings between biomedical identifiers in different namespaces -long_description = file: README.md -long_description_content_type = text/markdown - -# Links -url = https://github.com/biopragmatics/biomappings -download_url = https://github.com/biopragmatics/biomappings/releases -project_urls = - Bug Tracker = https://github.com/biopragmatics/biomappings/issues - -# Author information -author = Charles Tapley Hoyt -author_email = cthoyt@gmail.com -maintainer = Charles Tapley Hoyt -maintainer_email = cthoyt@gmail.com - -# License information -license = MIT -license_files = LICENSE - -classifiers = - Development Status :: 4 - Beta - Environment :: Console - Intended Audience :: Science/Research - License :: OSI Approved :: MIT License - Operating System :: OS Independent - Programming Language :: Python - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3 :: Only - Topic :: Scientific/Engineering :: Chemistry - Topic :: Scientific/Engineering :: Bio-Informatics -keywords = - biology - -[options] -install_requires = - networkx - requests - click - pyyaml - tqdm - pystow>=0.2.7 - bioregistry>=0.10.43 - -zip_safe = false -include_package_data = True -python_requires = >=3.8 - -# Where is my code -packages = find: -package_dir = - = src - -[options.packages.find] -where = src - -[options.entry_points] -console_scripts = - biomappings = biomappings.cli:main - -[options.extras_require] -tests = - pytest - coverage -docs = - sphinx - sphinx-rtd-theme - sphinx-click - sphinx_automodapi -charts = - matplotlib - seaborn -pyobo = - pyobo>=0.8.4 -apicuron = - apicuron_client -web = - flask - bootstrap-flask - flask-wtf -gilda = - gilda - pyobo>=0.8.4 -ndex = - ndex2 -exports = - sssom - -# Doc8 Configuration # -# (doc8.ini) # -###################### -[doc8] -max-line-length = 120 - -########################## -# Coverage Configuration # -# (.coveragerc) # -########################## -[coverage:run] -branch = True -source = biomappings -omit = - tests/* - docs/* - src/biomappings/cli/* - src/biomappings/__main__.py - -[coverage:paths] -source = - src/biomappings - .tox/*/lib/python*/site-packages/biomappings - -[coverage:report] -show_missing = True -exclude_lines = - def __str__ - def __repr__ - -########################## -# Darglint Configuration # -########################## -[darglint] -docstring_style = sphinx -strictness = short - -######################### -# Flake8 Configuration # -# (.flake8) # -######################### -[flake8] -ignore = - S301 # pickle - S403 # pickle - S404 - S603 - W503 # Line break before binary operator (flake8 is wrong) - E203 # whitespace before ':' -exclude = - .tox, - .git, - __pycache__, - docs/source/conf.py, - build, - dist, - tests/fixtures/*, - *.pyc, - *.egg-info, - .cache, - .eggs, - data -max-line-length = 120 -max-complexity = 20 -import-order-style = pycharm -application-import-names = - biomappings - tests diff --git a/src/biomappings/__init__.py b/src/biomappings/__init__.py index 3e6bfd7a..8f5c92b2 100644 --- a/src/biomappings/__init__.py +++ b/src/biomappings/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Community curated mappings between biomedical entities.""" from .graph import get_false_graph, get_predictions_graph, get_true_graph # noqa:F401 diff --git a/src/biomappings/__main__.py b/src/biomappings/__main__.py index b8bd4b0e..023e2509 100644 --- a/src/biomappings/__main__.py +++ b/src/biomappings/__main__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """The biomappings CLI.""" from .cli import main diff --git a/src/biomappings/apicuron.py b/src/biomappings/apicuron.py index 9485732d..34b7b2e5 100644 --- a/src/biomappings/apicuron.py +++ b/src/biomappings/apicuron.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Upload curation statistics to APICURON. Run this with: @@ -12,7 +10,7 @@ """ import datetime -from typing import Iterable +from collections.abc import Iterable import click from apicuron_client import Description, Report, Submission, resubmit_curations @@ -72,8 +70,6 @@ def get_curation_payload() -> Submission: """Get curation payload dictionary for upload to APICURON.""" return Submission( resource_uri=DESCRIPTION.resource_uri, - # time_start=START, - # time_end=NOW, reports=list(iter_reports()), ) @@ -92,7 +88,6 @@ def iter_reports() -> Iterable[Report]: curator_orcid=provenance[len("orcid:") :], activity_term="novel_curation", resource_uri=DESCRIPTION.resource_uri, - # timestamp=NOW, entity_uri=entity_uri, ) diff --git a/src/biomappings/cli.py b/src/biomappings/cli.py index a478e9fe..03e9dc33 100644 --- a/src/biomappings/cli.py +++ b/src/biomappings/cli.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """The biomappings CLI.""" import sys @@ -84,8 +82,6 @@ def update(ctx: click.Context): ctx.invoke(sssom) click.secho("Generating charts", fg="green") ctx.invoke(charts) - # click.secho("Uploading to NDEx", fg="green") - # ctx.invoke(ndex) @main.command() diff --git a/src/biomappings/contribute/obo.py b/src/biomappings/contribute/obo.py index 001a8806..4075b64f 100644 --- a/src/biomappings/contribute/obo.py +++ b/src/biomappings/contribute/obo.py @@ -7,7 +7,7 @@ from copy import deepcopy from pathlib import Path -from typing import Any, Dict, List, Union +from typing import Any, Union import bioregistry import click @@ -40,8 +40,8 @@ def update_obo(*, prefix: str, path: Union[str, Path]) -> None: def update_obo_lines( - *, lines: List[str], mappings: List[Dict[str, Any]], progress: bool = True -) -> List[str]: + *, lines: list[str], mappings: list[dict[str, Any]], progress: bool = True +) -> list[str]: """Update the lines of an OBO file. :param mappings: Mappings to add @@ -73,8 +73,8 @@ def update_obo_lines( def add_xref( - lines: List[str], node: str, xref: str, xref_name: str, author_orcid: str -) -> List[str]: + lines: list[str], node: str, xref: str, xref_name: str, author_orcid: str +) -> list[str]: """Add xref to OBO file lines in the appropriate place.""" look_for_xref = False id_idx = None diff --git a/src/biomappings/contribute/utils.py b/src/biomappings/contribute/utils.py index 78ff66dd..5dca6c15 100644 --- a/src/biomappings/contribute/utils.py +++ b/src/biomappings/contribute/utils.py @@ -1,6 +1,6 @@ """Utilities for contributing back to upstream resources.""" -from typing import Any, Dict, List +from typing import Any from biomappings import load_mappings @@ -9,7 +9,7 @@ ] -def get_curated_mappings(prefix: str) -> List[Dict[str, Any]]: +def get_curated_mappings(prefix: str) -> list[dict[str, Any]]: """Get mappings for a given prefix.""" mappings = [] for mapping in load_mappings(): diff --git a/src/biomappings/export_sssom.py b/src/biomappings/export_sssom.py index 0f9e5640..26606050 100644 --- a/src/biomappings/export_sssom.py +++ b/src/biomappings/export_sssom.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Export Biomappings as SSSOM.""" import importlib.metadata diff --git a/src/biomappings/gilda_utils.py b/src/biomappings/gilda_utils.py index 129d4e35..d3f65bb0 100644 --- a/src/biomappings/gilda_utils.py +++ b/src/biomappings/gilda_utils.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- - """Utilities for generating predictions with pyobo/gilda.""" import logging from collections import defaultdict +from collections.abc import Iterable from pathlib import Path -from typing import Iterable, Optional, Tuple, Union +from typing import Optional, Union import bioregistry import pyobo @@ -135,5 +134,5 @@ def has_mapping(prefix: str, identifier: str, target_prefix: str) -> bool: return pyobo.get_xref(prefix, identifier, target_prefix) is not None -def _key(t: PredictionTuple) -> Tuple[str, str]: +def _key(t: PredictionTuple) -> tuple[str, str]: return t.source_prefix, t.source_name diff --git a/src/biomappings/graph.py b/src/biomappings/graph.py index d7ecaf69..f7922df4 100644 --- a/src/biomappings/graph.py +++ b/src/biomappings/graph.py @@ -1,13 +1,12 @@ -# -*- coding: utf-8 -*- - """Load Biomappings as a graph.""" import itertools as itt import logging import os from collections import Counter +from collections.abc import Collection, Iterable, Mapping, Sequence from operator import itemgetter -from typing import Collection, Iterable, List, Mapping, Optional, Sequence +from typing import Optional import click import networkx as nx @@ -198,7 +197,6 @@ def charts(): sns.kdeplot(component_densities, ax=axes[0][2]) axes[0][2].set_xlim([0.0, 1.0]) - # axes[0][2].set_yscale('log') axes[0][2].set_title("Density ($|V| > 2$)") axes[0][2].set_ylabel("") @@ -243,7 +241,7 @@ def charts(): plt.close(fig) -def _countplot_list(data: List[int], ax): +def _countplot_list(data: list[int], ax): import pandas as pd import seaborn as sns diff --git a/src/biomappings/mapping_graph.py b/src/biomappings/mapping_graph.py index 56b0df74..454e1c88 100644 --- a/src/biomappings/mapping_graph.py +++ b/src/biomappings/mapping_graph.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- - """Functions for working with the mapping graph.""" import itertools as itt from collections import defaultdict -from typing import TYPE_CHECKING, DefaultDict, Dict, Iterable, List, Optional +from collections.abc import Iterable +from typing import TYPE_CHECKING, Optional import networkx as nx import pyobo @@ -21,9 +20,9 @@ ] -def get_filter_from_semra(mappings: List["semra.Mapping"]) -> CMapping: +def get_filter_from_semra(mappings: list["semra.Mapping"]) -> CMapping: """Get a custom filter dictionary from a set of SeMRA mappings.""" - rv: DefaultDict[str, DefaultDict[str, Dict[str, str]]] = defaultdict(lambda: defaultdict(dict)) + rv: defaultdict[str, defaultdict[str, dict[str, str]]] = defaultdict(lambda: defaultdict(dict)) for mapping in mappings: rv[mapping.s.prefix][mapping.o.prefix][mapping.s.identifier] = mapping.o.identifier return rv @@ -37,7 +36,7 @@ def get_custom_filter(prefix: str, targets: Iterable[str]) -> CMapping: :returns: A filter 3-dictionary of source prefix to target prefix to source identifier to target identifier """ graph = mutual_mapping_graph([prefix, *targets]) - rv: DefaultDict[str, Dict[str, str]] = defaultdict(dict) + rv: defaultdict[str, dict[str, str]] = defaultdict(dict) for p, identifier in graph: if p != prefix: continue diff --git a/src/biomappings/paper_analysis.py b/src/biomappings/paper_analysis.py index 28bf4895..1414129a 100644 --- a/src/biomappings/paper_analysis.py +++ b/src/biomappings/paper_analysis.py @@ -3,9 +3,9 @@ import json import pickle from collections import defaultdict +from collections.abc import Iterable, Mapping from dataclasses import dataclass from pathlib import Path -from typing import DefaultDict, Dict, Iterable, Mapping, Tuple import bioontologies import bioregistry @@ -92,7 +92,7 @@ def _from_dicts( ), ) - def print(self): # noqa:T202 + def print(self): """Print a summary of value added statistics.""" print( # noqa:T201 tabulate( @@ -123,7 +123,7 @@ def get_primary_mappings( prefix: str, external_prefix: str, cache_path: Path, -) -> Tuple[str, Mapping[str, str]]: +) -> tuple[str, Mapping[str, str]]: """Get mappings from a given ontology (prefix) to another resource (external prefix).""" if cache_path.is_file(): d = json.loads(cache_path.read_text()) @@ -132,7 +132,7 @@ def get_primary_mappings( parse_results = bioontologies.get_obograph_by_prefix(prefix) version = parse_results.guess_version(prefix) graphs = parse_results.graph_document.graphs if parse_results.graph_document else [] - rv: Dict[str, str] = {} + rv: dict[str, str] = {} for graph in graphs: for node in tqdm( graph.nodes, @@ -166,7 +166,7 @@ def index_mappings(mappings: Iterable[Mapping[str, str]], path=None, force: bool with open(path, "rb") as file: return pickle.load(file) - rv: DefaultDict[str, DefaultDict[str, Dict[str, str]]] = defaultdict(lambda: defaultdict(dict)) + rv: defaultdict[str, defaultdict[str, dict[str, str]]] = defaultdict(lambda: defaultdict(dict)) for mapping in tqdm(mappings, unit_scale=True, unit="mapping"): source_prefix = mapping["source prefix"] diff --git a/src/biomappings/py.typed b/src/biomappings/py.typed new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/biomappings/py.typed @@ -0,0 +1 @@ + diff --git a/src/biomappings/resources/__init__.py b/src/biomappings/resources/__init__.py index 179bf00e..2ca94efe 100644 --- a/src/biomappings/resources/__init__.py +++ b/src/biomappings/resources/__init__.py @@ -1,25 +1,12 @@ -# -*- coding: utf-8 -*- - """Biomappings resources.""" import csv import itertools as itt import logging from collections import defaultdict +from collections.abc import Iterable, Mapping, Sequence from pathlib import Path -from typing import ( - Any, - DefaultDict, - Dict, - Iterable, - List, - Mapping, - NamedTuple, - Optional, - Sequence, - Tuple, - Union, -) +from typing import Any, NamedTuple, Optional, Union import bioregistry from tqdm.auto import tqdm @@ -239,7 +226,7 @@ def get_resource_file_path(fname) -> Path: return RESOURCE_PATH.joinpath(fname) -def _load_table(path: Union[str, Path]) -> List[Dict[str, str]]: +def _load_table(path: Union[str, Path]) -> list[dict[str, str]]: path = Path(path).resolve() if not path.is_file(): logger.warning("mappings file does not exist, returning empty list: %s", path) @@ -261,12 +248,12 @@ def _write_helper( mappings = sorted(mappings, key=mapping_sort_key) with open(path, mode) as file: if mode == "w": - print(*header, sep="\t", file=file) # noqa:T201 + print(*header, sep="\t", file=file) for line in mappings: - print(*[line[k] or "" for k in header], sep="\t", file=file) # noqa:T201 + print(*[line[k] or "" for k in header], sep="\t", file=file) -def mapping_sort_key(prediction: Mapping[str, str]) -> Tuple[str, ...]: +def mapping_sort_key(prediction: Mapping[str, str]) -> tuple[str, ...]: """Return a tuple for sorting mapping dictionaries.""" return ( prediction["source prefix"], @@ -282,7 +269,7 @@ def mapping_sort_key(prediction: Mapping[str, str]) -> Tuple[str, ...]: TRUE_MAPPINGS_PATH = get_resource_file_path("mappings.tsv") -def load_mappings(*, path: Union[str, Path, None] = None) -> List[Dict[str, str]]: +def load_mappings(*, path: Union[str, Path, None] = None) -> list[dict[str, str]]: """Load the mappings table.""" return _load_table(path or TRUE_MAPPINGS_PATH) @@ -339,7 +326,7 @@ def _lint_curated_mappings(path: Path, *, standardize: bool = False) -> None: FALSE_MAPPINGS_PATH = get_resource_file_path("incorrect.tsv") -def load_false_mappings(*, path: Optional[Path] = None) -> List[Dict[str, str]]: +def load_false_mappings(*, path: Optional[Path] = None) -> list[dict[str, str]]: """Load the false mappings table.""" return _load_table(path or FALSE_MAPPINGS_PATH) @@ -371,7 +358,7 @@ def lint_false_mappings(*, standardize: bool = False, path: Optional[Path] = Non UNSURE_PATH = get_resource_file_path("unsure.tsv") -def load_unsure(*, path: Optional[Path] = None) -> List[Dict[str, str]]: +def load_unsure(*, path: Optional[Path] = None) -> list[dict[str, str]]: """Load the unsure table.""" return _load_table(path or UNSURE_PATH) @@ -403,7 +390,7 @@ def lint_unsure_mappings(*, standardize: bool = False, path: Optional[Path] = No PREDICTIONS_PATH = get_resource_file_path("predictions.tsv") -def load_predictions(*, path: Union[str, Path, None] = None) -> List[Dict[str, str]]: +def load_predictions(*, path: Union[str, Path, None] = None) -> list[dict[str, str]]: """Load the predictions table.""" return _load_table(path or PREDICTIONS_PATH) @@ -467,7 +454,7 @@ def lint_predictions( *, standardize: bool = False, path: Optional[Path] = None, - additional_curated_mappings: Optional[List[Dict[str, str]]] = None, + additional_curated_mappings: Optional[list[dict[str, str]]] = None, ) -> None: """Lint the predictions file. @@ -591,7 +578,7 @@ def _check_filter( def get_curated_filter() -> Mapping[str, Mapping[str, Mapping[str, str]]]: """Get a filter over all curated mappings.""" - d: DefaultDict[str, DefaultDict[str, Dict[str, str]]] = defaultdict(lambda: defaultdict(dict)) + d: defaultdict[str, defaultdict[str, dict[str, str]]] = defaultdict(lambda: defaultdict(dict)) for m in itt.chain(load_mappings(), load_false_mappings(), load_unsure()): d[m["source prefix"]][m["target prefix"]][m["source identifier"]] = m["target identifier"] return {k: dict(v) for k, v in d.items()} @@ -601,7 +588,7 @@ def prediction_tuples_from_semra( mappings, *, confidence: float, -) -> List[PredictionTuple]: +) -> list[PredictionTuple]: """Get prediction tuples from SeMRA mappings.""" rows = [] for mapping in mappings: diff --git a/src/biomappings/resources/semapv.py b/src/biomappings/resources/semapv.py index 0230f51f..7d4475c3 100644 --- a/src/biomappings/resources/semapv.py +++ b/src/biomappings/resources/semapv.py @@ -25,10 +25,10 @@ def get_semapv(): "bio", "semapv", url=url, - read_csv_kwargs=dict( - usecols=[0, 1], - skiprows=1, - ), + read_csv_kwargs={ + "usecols": [0, 1], + "skiprows": 1, + }, ) df["ID"] = df["ID"].map(lambda s: s.removeprefix("semapv:")) rv = dict(df.values) diff --git a/src/biomappings/summary.py b/src/biomappings/summary.py index 3c95fa52..3b05a42b 100644 --- a/src/biomappings/summary.py +++ b/src/biomappings/summary.py @@ -1,12 +1,11 @@ -# -*- coding: utf-8 -*- - """Generate a summary for the Biomappings website.""" import itertools as itt import os import typing from collections import Counter -from typing import Iterable, Mapping, Optional +from collections.abc import Iterable, Mapping +from typing import Optional import click import yaml @@ -60,14 +59,14 @@ def export(): def _get_counter(mappings: Iterable[Mapping[str, str]]): - counter: typing.Counter[typing.Tuple[str, str]] = Counter() + counter: typing.Counter[tuple[str, str]] = Counter() for mapping in mappings: source, target = mapping["source prefix"], mapping["target prefix"] if source > target: source, target = target, source counter[source, target] += 1 return [ - dict(source=source, target=target, count=count) + {"source": source, "target": target, "count": count} for (source, target), count in counter.most_common() ] @@ -78,7 +77,7 @@ def _get_contributors(mappings: Iterable[Mapping[str, str]]): curators = {record["orcid"]: record for record in load_curators()} counter = Counter(_get_source(mapping["source"]) for mapping in mappings) return [ - dict(count=count, **curators[orcid]) if orcid else dict(count=count) + dict(count=count, **curators[orcid]) if orcid else {"count": count} for orcid, count in counter.most_common() ] diff --git a/src/biomappings/testing.py b/src/biomappings/testing.py index d7df4df7..7ca3237d 100644 --- a/src/biomappings/testing.py +++ b/src/biomappings/testing.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Validation tests for :mod:`biomappings`.""" import itertools as itt @@ -62,7 +60,7 @@ def _iter_groups(self): for i, mapping in enumerate(group, start=2): yield label, i, mapping - def test_prediction_types(self): + def test_prediction_types(self) -> None: """Test that the prediction type is pulled in properly.""" for line, mapping in enumerate(self.mappings, start=2): pt = mapping.get("prediction_type", "".strip()) @@ -87,7 +85,7 @@ def test_prediction_types(self): ) self.assertIn(tt[len("semapv:") :], semapv) - def test_relations(self): + def test_relations(self) -> None: """Test that the relation is a CURIE.""" for label, line, mapping in self._iter_groups(): parts = mapping["relation"].split(":") @@ -97,7 +95,7 @@ def test_relations(self): if prefix != "RO": self.assert_canonical_identifier(prefix, identifier, label, line) - def test_canonical_prefixes(self): + def test_canonical_prefixes(self) -> None: """Test that all mappings use canonical bioregistry prefixes.""" valid_prefixes = set(bioregistry.read_registry()) for label, line, mapping in self._iter_groups(): @@ -113,7 +111,7 @@ def test_canonical_prefixes(self): msg=f"Invalid prefix: {target_prefix} on {label}:{line}", ) - def test_normalized_identifiers(self): + def test_normalized_identifiers(self) -> None: """Test that all identifiers have been normalized (based on bioregistry definition).""" for label, line, mapping in self._iter_groups(): self.assert_canonical_identifier( @@ -140,7 +138,7 @@ def assert_canonical_identifier( except InvalidIdentifierPattern as e: self.fail(f"[{label}:{line}] {e}") - def test_contributors(self): + def test_contributors(self) -> None: """Test all contributors have an entry in the curators.tsv file.""" contributor_orcids = {row["orcid"] for row in load_curators()} for mapping in itt.chain(self.mappings, self.incorrect, self.unsure): @@ -151,9 +149,11 @@ def test_contributors(self): self.fail(msg=f'Add an entry with "{ss}" and your ORCID to {CURATORS_PATH}') self.assertIn(source[len("orcid:") :], contributor_orcids) - def test_cross_redundancy(self): + def test_cross_redundancy(self) -> None: """Test the redundancy of manually curated mappings and predicted mappings.""" - counter = defaultdict(lambda: defaultdict(list)) + counter: defaultdict[tuple[str, str, str, str], defaultdict[str, list[int]]] = defaultdict( + lambda: defaultdict(list) + ) for label, line, mapping in self._iter_groups(): counter[get_canonical_tuple(mapping)][label].append(line) @@ -169,7 +169,7 @@ def test_cross_redundancy(self): ) raise ValueError(f"{len(redundant)} are redundant: {msg}") - def assert_no_internal_redundancies(self, m: Mappings, tuple_cls): + def assert_no_internal_redundancies(self, m: Mappings, tuple_cls) -> None: """Assert that the list of mappings doesn't have any redundancies.""" counter = defaultdict(list) for line, mapping in enumerate(m, start=1): @@ -182,7 +182,7 @@ def assert_no_internal_redundancies(self, m: Mappings, tuple_cls): ) raise ValueError(f"{len(redundant)} are redundant: {msg}") - def test_predictions_sorted(self): + def test_predictions_sorted(self) -> None: """Test the predictions are in a canonical order.""" self.assertEqual( self.predictions, @@ -191,7 +191,7 @@ def test_predictions_sorted(self): ) self.assert_no_internal_redundancies(self.predictions, PredictionTuple) - def test_curations_sorted(self): + def test_curations_sorted(self) -> None: """Test the true curated mappings are in a canonical order.""" self.assertEqual( self.mappings, @@ -200,7 +200,7 @@ def test_curations_sorted(self): ) self.assert_no_internal_redundancies(self.mappings, MappingTuple) - def test_false_mappings_sorted(self): + def test_false_mappings_sorted(self) -> None: """Test the false curated mappings are in a canonical order.""" self.assertEqual( self.incorrect, @@ -209,7 +209,7 @@ def test_false_mappings_sorted(self): ) self.assert_no_internal_redundancies(self.incorrect, MappingTuple) - def test_unsure_sorted(self): + def test_unsure_sorted(self) -> None: """Test the unsure mappings are in a canonical order.""" self.assertEqual( self.unsure, @@ -231,6 +231,7 @@ class PathIntegrityTestCase(IntegrityTestCase): HERE = Path(__file__).parent.resolve() + class TestCustom(PathIntegrityTestCase): predictions_path = HERE.joinpath("predictions.tsv") positives_path = HERE.joinpath("positive.tsv") diff --git a/src/biomappings/upload_ndex.py b/src/biomappings/upload_ndex.py index 36295876..598c4102 100644 --- a/src/biomappings/upload_ndex.py +++ b/src/biomappings/upload_ndex.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Upload the Biomappings to NDEx. .. seealso:: https://www.ndexbio.org/viewer/networks/402d1fd6-49d6-11eb-9e72-0ac135e8bacf @@ -48,11 +46,11 @@ def ndex(username, password): cx.add_network_attribute("version", get_git_hash()) authors = sorted( - set( + { mapping["source"] for mapping in positive_mappings if mapping["source"].startswith("orcid:") - ) + } ) cx.add_network_attribute("author", authors, type="list_of_string") diff --git a/src/biomappings/utils.py b/src/biomappings/utils.py index 35d50e9d..3b1040fc 100644 --- a/src/biomappings/utils.py +++ b/src/biomappings/utils.py @@ -1,12 +1,11 @@ -# -*- coding: utf-8 -*- - """Utilities.""" import os import re +from collections.abc import Mapping from pathlib import Path -from subprocess import CalledProcessError, check_output # noqa: S404 -from typing import Any, Mapping, Optional, Tuple +from subprocess import CalledProcessError, check_output +from typing import Any, Optional import bioregistry @@ -81,8 +80,8 @@ def get_branch() -> str: def _git(*args: str) -> Optional[str]: with open(os.devnull, "w") as devnull: try: - ret = check_output( # noqa: S603,S607 - ["git", *args], + ret = check_output( # noqa: S603 + ["git", *args], # noqa:S607 cwd=os.path.dirname(__file__), stderr=devnull, ) @@ -104,7 +103,7 @@ def get_script_url(fname: str) -> str: return f"https://github.com/biomappings/biomappings/blob/{commit_hash}/scripts/{script_name}" -def get_canonical_tuple(mapping: Mapping[str, Any]) -> Tuple[str, str, str, str]: +def get_canonical_tuple(mapping: Mapping[str, Any]) -> tuple[str, str, str, str]: """Get the canonical tuple from a mapping entry.""" source = mapping["source prefix"], mapping["source identifier"] target = mapping["target prefix"], mapping["target identifier"] @@ -129,7 +128,7 @@ def __init__(self, prefix: str, norm_prefix: str): self.prefix = prefix self.norm_prefix = norm_prefix - def __str__(self) -> str: # noqa:D105 + def __str__(self) -> str: return f"{self.prefix} should be standardized to {self.norm_prefix}" @@ -159,7 +158,7 @@ def __init__(self, prefix: str, identifier: str, pattern): super().__init__(prefix, identifier) self.pattern = pattern - def __str__(self) -> str: # noqa:D105 + def __str__(self) -> str: return f"{self.prefix}:{self.identifier} does not match pattern {self.pattern}" @@ -176,7 +175,7 @@ def __init__(self, prefix: str, identifier: str, norm_identifier: str): super().__init__(prefix, identifier) self.norm_identifier = norm_identifier - def __str__(self) -> str: # noqa:D105 + def __str__(self) -> str: return f"{self.prefix}:{self.identifier} does not match normalized CURIE {self.prefix}:{self.norm_identifier}" diff --git a/src/biomappings/wsgi.py b/src/biomappings/wsgi.py index 1eb74837..7eb0320e 100644 --- a/src/biomappings/wsgi.py +++ b/src/biomappings/wsgi.py @@ -1,22 +1,15 @@ -# -*- coding: utf-8 -*- - """Web curation interface for :mod:`biomappings`.""" import getpass import os from collections import Counter, defaultdict +from collections.abc import Iterable, Mapping from copy import deepcopy from pathlib import Path from typing import ( Any, - Dict, - Iterable, - List, Literal, - Mapping, Optional, - Set, - Tuple, Union, ) @@ -99,7 +92,7 @@ def url_for_state(endpoint, state: State, **kwargs) -> str: def get_app( - target_curies: Optional[Iterable[Tuple[str, str]]] = None, + target_curies: Optional[Iterable[tuple[str, str]]] = None, predictions_path: Optional[Path] = None, positives_path: Optional[Path] = None, negatives_path: Optional[Path] = None, @@ -148,7 +141,7 @@ class Controller: def __init__( self, *, - target_curies: Optional[Iterable[Tuple[str, str]]] = None, + target_curies: Optional[Iterable[tuple[str, str]]] = None, predictions_path: Optional[Path] = None, positives_path: Optional[Path] = None, negatives_path: Optional[Path] = None, @@ -171,12 +164,12 @@ def __init__( self.negatives_path = negatives_path self.unsure_path = unsure_path - self._marked: Dict[int, str] = {} + self._marked: dict[int, str] = {} self.total_curated = 0 - self._added_mappings: List[Dict[str, Union[None, str, float]]] = [] + self._added_mappings: list[dict[str, Union[None, str, float]]] = [] self.target_ids = set(target_curies or []) - def predictions_from_state(self, state: State) -> Iterable[Tuple[int, Mapping[str, Any]]]: + def predictions_from_state(self, state: State) -> Iterable[tuple[int, Mapping[str, Any]]]: """Iterate over predictions from a state instance.""" return self.predictions( offset=state.offset, @@ -206,7 +199,7 @@ def predictions( sort: Optional[str] = None, same_text: Optional[bool] = None, provenance: Optional[str] = None, - ) -> Iterable[Tuple[int, Mapping[str, Any]]]: + ) -> Iterable[tuple[int, Mapping[str, Any]]]: """Iterate over predictions. :param offset: If given, offset the iteration by this number @@ -262,7 +255,6 @@ def count_predictions_from_state(self, state: State) -> int: target_query=state.target_query, target_prefix=state.target_prefix, prefix=state.prefix, - # sort=state.sort, # sort doesn't matter for count same_text=state.same_text, provenance=state.provenance, ) @@ -305,7 +297,7 @@ def _help_it_predictions( same_text: Optional[bool] = None, provenance: Optional[str] = None, ): - it: Iterable[Tuple[int, Mapping[str, Any]]] = enumerate(self._predictions) + it: Iterable[tuple[int, Mapping[str, Any]]] = enumerate(self._predictions) if self.target_ids: it = ( (line, p) @@ -369,7 +361,7 @@ def _help_it_predictions( return rv @staticmethod - def _help_filter(query: str, it, elements: Set[str]): + def _help_filter(query: str, it, elements: set[str]): query = query.casefold() return ( (line, prediction) diff --git a/tests/__init__.py b/tests/__init__.py index 77193dfc..305122be 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - """Tests for :mod:`biomappings`.""" diff --git a/tests/test_validity.py b/tests/test_validity.py index f87a39d7..6a1f8770 100644 --- a/tests/test_validity.py +++ b/tests/test_validity.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Validation tests for :mod:`biomappings`.""" from biomappings import ( diff --git a/tox.ini b/tox.ini index 7230b079..9571347d 100644 --- a/tox.ini +++ b/tox.ini @@ -11,23 +11,27 @@ isolated_build = True # These environments are run in order if you just use `tox`: envlist = # always keep coverage-clean first - # coverage-clean - # code linters/stylers - lint + coverage-clean + # code formatters + format + # format-docs + # Code quality assessment manifest pyroma - flake8 + lint mypy - # documentation linters/checkers + # Documentation quality assurance doc8 docstr-coverage docs-test # the actual tests py + doctests # always keep coverage-report last # coverage-report [testenv] +description = Run unit and integration tests. # Runs on the "tests" directory by default, or passes the positional # arguments from `tox -e py ... commands = @@ -35,7 +39,7 @@ commands = coverage combine coverage xml extras = - # See the [options.extras_require] entry in setup.cfg for "tests" + # See the [project.optional-dependencies] entry in pyproject.toml for "tests" tests [testenv:update] @@ -63,37 +67,48 @@ extras = passenv = APICURON_TOKEN +[testenv:coverage-clean] +description = Remove testing coverage artifacts. +deps = coverage +skip_install = true +commands = coverage erase + [testenv:doctests] +description = Test that documentation examples run properly. commands = xdoctest -m src deps = xdoctest pygments -[testenv:coverage-clean] -deps = coverage -skip_install = true -commands = coverage erase +[testenv:treon] +description = Test that notebooks can run to completion +commands = + treon notebooks/ +deps = + treon -[testenv:lint] +[testenv:format] +description = Format the code in a deterministic way using ruff. Note that ruff check should come before ruff format when using --fix (ref: https://github.com/astral-sh/ruff-pre-commit/blob/main/README.md) deps = - black[jupyter] - isort - nbqa + ruff skip_install = true commands = - black . - isort . - nbqa isort . -description = Run linters. + ruff check --fix + ruff format -[testenv:doclint] +[testenv:format-docs] +description = Run documentation linters. +# note that this doesn't work with sphinx-click +# or any other extension that adds extra directives deps = rstfmt +extras = + # See the [project.optional-dependencies] entry in pyproject.toml for "docs" + docs skip_install = true commands = rstfmt docs/source/ -description = Run documentation linters. [testenv:manifest] deps = check-manifest @@ -101,23 +116,15 @@ skip_install = true commands = check-manifest description = Check that the MANIFEST.in is written properly and give feedback on how to fix it. -[testenv:flake8] +[testenv:lint] +description = Check code quality using ruff and other tools. + skip_install = true deps = - darglint - flake8<5.0.0 - flake8-black - flake8-bandit - flake8-bugbear - flake8-colors - flake8-docstrings - flake8-isort - # flake8-print - pep8-naming - pydocstyle + ruff commands = - flake8 src/ scripts/ tests/ -description = Run the flake8 tool with several plugins (bandit, docstrings, import order, pep8 naming). See https://cthoyt.com/2020/04/25/how-to-code-with-me-flake8.html for more information. + ruff check + ruff format --check [testenv:pyroma] deps = @@ -128,38 +135,44 @@ commands = pyroma --min=10 . description = Run the pyroma tool to check the package friendliness of the project. [testenv:mypy] -deps = mypy +description = Run the mypy tool to check static typing on the project. +deps = + mypy + pydantic + types-PyYAML + types-tabulate skip_install = true commands = mypy --install-types --non-interactive --ignore-missing-imports src/ -description = Run the mypy tool to check static typing on the project. [testenv:doc8] skip_install = true deps = - sphinx doc8 +extras = + docs commands = doc8 docs/source/ description = Run the doc8 tool to check the style of the RST files in the project docs. [testenv:docstr-coverage] +description = Run the docstr-coverage tool to check documentation coverage. skip_install = true deps = docstr-coverage commands = docstr-coverage src/ tests/ --skip-private --skip-magic -description = Run the docstr-coverage tool to check documentation coverage [testenv:docs] -description = Build the documentation locally. +description = Build the documentation locally, allowing warnings. extras = + # See the [project.optional-dependencies] entry in pyproject.toml for "docs" docs gilda commands = - python -m sphinx -W -b html -d docs/build/doctrees docs/source docs/build/html + python -m sphinx -b html -d docs/build/doctrees docs/source docs/build/html [testenv:docs-test] -description = Test building the documentation in an isolated environment. +description = Test building the documentation in an isolated environment. Warnings are considered as errors via -W. changedir = docs extras = {[testenv:docs]extras} @@ -167,19 +180,16 @@ commands = mkdir -p {envtmpdir} cp -r source {envtmpdir}/source python -m sphinx -W -b html -d {envtmpdir}/build/doctrees {envtmpdir}/source {envtmpdir}/build/html - python -m sphinx -W -b coverage -d {envtmpdir}/build/doctrees {envtmpdir}/source {envtmpdir}/build/coverage - cat {envtmpdir}/build/coverage/c.txt - cat {envtmpdir}/build/coverage/python.txt + # python -m sphinx -W -b coverage -d {envtmpdir}/build/doctrees {envtmpdir}/source {envtmpdir}/build/coverage + # cat {envtmpdir}/build/coverage/c.txt + # cat {envtmpdir}/build/coverage/python.txt allowlist_externals = - /bin/cp - /bin/cat - /bin/mkdir - # for compatibility on GitHub actions - /usr/bin/cp - /usr/bin/cat - /usr/bin/mkdir + cp + cat + mkdir [testenv:coverage-report] +# TODO this is broken deps = coverage skip_install = true commands = @@ -191,54 +201,115 @@ commands = #################### [testenv:bumpversion] -commands = bumpversion {posargs} +description = Bump the version number +commands = bump-my-version bump {posargs} +skip_install = true +passenv = HOME +deps = + bump-my-version + +[testenv:bumpversion-release] +description = Remove the -dev tag from the version +commands = bump-my-version bump release --tag skip_install = true passenv = HOME deps = - bumpversion + bump-my-version [testenv:build] skip_install = true deps = - wheel - build + uv + setuptools commands = - python -m build --sdist --wheel --no-isolation + uv build --sdist --wheel --no-build-isolation + +############ +# Releases # +############ + +# In order to make a release to PyPI, you'll need to take the following steps: +# +# 1. Navigate to https://pypi.org/account/register/ to register for Test PyPI +# 2. Navigate to https://pypi.org/manage/account/ and request to re-send a verification email. +# This is not sent by default, and is required to set up 2-Factor Authentication. +# 3. Get account recovery codes +# 4. Set up 2-Factor Authentication +# 5. Get an API token from https://pypi.org/manage/account/token/ +# 6. Install keyring with `uv tool install keyring` +# 7. Add your token to keyring with `keyring set https://upload.pypi.org/legacy/ __token__` [testenv:release] description = Release the code to PyPI so users can pip install it skip_install = true deps = {[testenv:build]deps} - twine >= 1.5.0 + uv + keyring commands = {[testenv:build]commands} - twine upload --skip-existing dist/* + uv publish --username __token__ --keyring-provider subprocess --publish-url https://upload.pypi.org/legacy/ + +[testenv:finish] +description = + Run a workflow that removes -dev from the version, creates a tagged release on GitHub, + creates a release on PyPI, and bumps the version again. +skip_install = true +passenv = + HOME +deps = + {[testenv:release]deps} + bump-my-version +commands = + {[testenv:bumpversion-release]commands} + {[testenv:release]commands} + git push --tags + bump-my-version bump patch + git push +allowlist_externals = + git + +################# +# Test Releases # +################# + +# In order to test making a release to Test PyPI, you'll need to take the following steps: +# +# 1. Navigate to https://test.pypi.org/account/register/ to register for Test PyPI +# 2. Navigate to https://test.pypi.org/manage/account/ and request to re-send a verification email. +# This is not sent by default, and is required to set up 2-Factor Authentication. +# 3. Get account recovery codes +# 4. Set up 2-Factor Authentication +# 5. Get an API token from https://test.pypi.org/manage/account/token/ +# 6. Install keyring with `uv tool install keyring` +# 7. Add your token to keyring with `keyring set https://test.pypi.org/legacy/ __token__` [testenv:testrelease] description = Release the code to the test PyPI site skip_install = true deps = {[testenv:build]deps} - twine >= 1.5.0 + uv + keyring commands = {[testenv:build]commands} - twine upload --skip-existing --repository-url https://test.pypi.org/simple/ dist/* + uv publish --username __token__ --keyring-provider subprocess --publish-url https://test.pypi.org/legacy/ -[testenv:finish] +[testenv:testfinish] +description = + Run a workflow that removes -dev from the version, creates a tagged release on GitHub, + creates a release on Test PyPI, and bumps the version again. skip_install = true passenv = HOME - TWINE_USERNAME - TWINE_PASSWORD deps = - {[testenv:release]deps} - bump2version + {[testenv:testrelease]deps} + bump-my-version commands = - bump2version release --tag - {[testenv:release]commands} + {[testenv:bumpversion-release]commands} + {[testenv:testrelease]commands} git push --tags - bump2version patch + bump-my-version bump patch git push allowlist_externals = - /usr/bin/git + git From 6c207fd5f00f652f8740085c9c2b21515c42f901 Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Wed, 18 Dec 2024 10:24:42 +0100 Subject: [PATCH 2/4] Update cruft --- .cruft.json | 3 ++- .github/FUNDING.yml | 3 +++ .github/PULL_REQUEST_TEMPLATE.md | 17 ++++++++++++++ .github/workflows/cruft.yml | 7 +++--- .github/workflows/tests.yml | 15 ++++++++++++- README.md | 2 +- pyproject.toml | 36 +++++++++++++----------------- tox.ini | 38 +++++++++++++++++++++----------- 8 files changed, 81 insertions(+), 40 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.cruft.json b/.cruft.json index d9f9e24e..97f74b45 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/cthoyt/cookiecutter-snekpack", - "commit": "12edfcfa5f519467b5d834f0d4e706fb7cf4f065", + "commit": "d6ac604ce8428ddaaae55d372beef2621caed513", "checkout": null, "context": { "cookiecutter": { @@ -8,6 +8,7 @@ "package_name_stylized": "Biomappings", "short_description": "Curated and predicted mappings between biomedical identifiers in different namespaces", "author_name": "Charles Tapley Hoyt", + "author_github": "cthoyt", "author_email": "cthoyt@gmail.com", "github_organization_name": "biopragmatics", "github_repository_name": "biomappings", diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..439db4c0 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository +github: + - cthoyt diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..faa7c6b4 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,17 @@ + + +## Summary + + diff --git a/.github/workflows/cruft.yml b/.github/workflows/cruft.yml index 7442c9f5..07aa4d2b 100644 --- a/.github/workflows/cruft.yml +++ b/.github/workflows/cruft.yml @@ -2,9 +2,7 @@ name: Update repository with Cruft -permissions: - contents: write - pull-requests: write +permissions: {} on: workflow_dispatch: @@ -13,6 +11,9 @@ on: jobs: update: + permissions: + contents: write + pull-requests: write runs-on: ubuntu-latest strategy: fail-fast: true diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f65dca94..4e264877 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,6 +4,10 @@ name: Tests +# by default, give the GITHUB_TOKEN no permissions +# See https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token +permissions: {} + on: push: branches: [ master ] @@ -13,11 +17,16 @@ on: jobs: lint: name: Code Quality + permissions: + # give only read-only access to the contents of the repository + # this is the only permission this job requires, so keep it to the least privilege + # i.e., not to issues, discussions, actions, etc. + contents: read runs-on: ubuntu-latest strategy: matrix: python-version: [ "3.12", "3.9" ] - tox-command: ["manifest", "lint", "pyroma", "mypy"] + tox-command: ["lint", "pyroma", "mypy"] steps: - uses: actions/checkout@v4 - name: "Install uv" @@ -31,6 +40,8 @@ jobs: docs: name: Documentation + permissions: + contents: read runs-on: ubuntu-latest strategy: matrix: @@ -56,6 +67,8 @@ jobs: run: uvx -p ${{ matrix.python-version }} --with tox-uv tox -e docs-test tests: name: Tests + permissions: + contents: read runs-on: ${{ matrix.os }} strategy: matrix: diff --git a/README.md b/README.md index fd3447da..10c84fce 100644 --- a/README.md +++ b/README.md @@ -376,7 +376,7 @@ You only have to do the following steps once. #### Configuring your machine's connection to PyPI -You have to do the following steps once per machine. +You have to do the following steps once per machine. ```console $ uv tool install keyring diff --git a/pyproject.toml b/pyproject.toml index 2bd7a724..5f1ce15e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,8 @@ -# See https://setuptools.readthedocs.io/en/latest/build_meta.html [build-system] -requires = ["setuptools", "wheel"] -build-backend = "setuptools.build_meta" +requires = ["uv>=0.5.10,<0.6.0"] +# The uv backend entered preview mode in https://github.com/astral-sh/uv/pull/8886/files +# with the 0.5.0 release. See also https://github.com/astral-sh/uv/issues/3957 for tracking. +build-backend = "uv" [project] name = "biomappings" @@ -26,12 +27,15 @@ classifiers = [ "Framework :: Pytest", "Framework :: tox", "Framework :: Sphinx", + "Natural Language :: English", "Programming Language :: Python", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3 :: Only", + "Typing :: Typed", "Topic :: Scientific/Engineering :: Chemistry", "Topic :: Scientific/Engineering :: Bio-Informatics", ] @@ -45,10 +49,11 @@ keywords = [ "nlp", ] -# License Information. This can be any valid SPDX identifiers that can be resolved -# with URLs like https://spdx.org/licenses/MIT -# See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license -license = { file = "LICENSE" } +# License Information. +# See PEP-639 at https://peps.python.org/pep-0639/#add-license-files-key +license-files = [ + "LICENSE", +] requires-python = ">=3.9" dependencies = [ @@ -64,7 +69,7 @@ dependencies = [ [project.optional-dependencies] tests = [ "pytest", - "coverage", + "coverage[toml]", ] docs = [ "sphinx>=8", @@ -99,28 +104,17 @@ exports = [ ] # See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#urls +# and also https://packaging.python.org/en/latest/specifications/well-known-project-urls/ [project.urls] "Bug Tracker" = "https://github.com/biopragmatics/biomappings/issues" Homepage = "https://github.com/biopragmatics/biomappings" Repository = "https://github.com/biopragmatics/biomappings.git" Documentation = "https://biomappings.readthedocs.io" - -[tool.setuptools] -package-dir = { "" = "src" } - -[tool.setuptools.packages.find] -# this implicitly sets `packages = ":find"` -where = ["src"] # list of folders that contain the packages (["."] by default) - -# See https://setuptools.pypa.io/en/latest/userguide/datafiles.html -[tool.setuptools.package-data] -"*" = ["*.*"] - +Funding = "https://github.com/sponsors/" [project.scripts] biomappings = "biomappings.cli:main" - [tool.cruft] skip = [ "**/__init__.py", diff --git a/tox.ini b/tox.ini index 9571347d..23f1ef05 100644 --- a/tox.ini +++ b/tox.ini @@ -16,7 +16,6 @@ envlist = format # format-docs # Code quality assessment - manifest pyroma lint mypy @@ -28,7 +27,12 @@ envlist = py doctests # always keep coverage-report last - # coverage-report + coverage-report + +[testenv:.pkg] +# this special environment configures the build that tox does itself +set_env = + UV_PREVIEW=1 [testenv] description = Run unit and integration tests. @@ -41,6 +45,10 @@ commands = extras = # See the [project.optional-dependencies] entry in pyproject.toml for "tests" tests +set_env = + # this setting gets inherited into all environments, meaning + # that things that call uv commands don't require a --preview + UV_PREVIEW=1 [testenv:update] commands = @@ -110,12 +118,6 @@ skip_install = true commands = rstfmt docs/source/ -[testenv:manifest] -deps = check-manifest -skip_install = true -commands = check-manifest -description = Check that the MANIFEST.in is written properly and give feedback on how to fix it. - [testenv:lint] description = Check code quality using ruff and other tools. @@ -189,11 +191,9 @@ allowlist_externals = mkdir [testenv:coverage-report] -# TODO this is broken -deps = coverage +deps = coverage[toml] skip_install = true commands = - coverage combine coverage report #################### @@ -220,7 +220,6 @@ deps = skip_install = true deps = uv - setuptools commands = uv build --sdist --wheel --no-build-isolation @@ -240,7 +239,7 @@ commands = # 7. Add your token to keyring with `keyring set https://upload.pypi.org/legacy/ __token__` [testenv:release] -description = Release the code to PyPI so users can pip install it +description = Release the code to PyPI so users can pip install it, using credentials from keyring skip_install = true deps = {[testenv:build]deps} @@ -250,6 +249,19 @@ commands = {[testenv:build]commands} uv publish --username __token__ --keyring-provider subprocess --publish-url https://upload.pypi.org/legacy/ +[testenv:release-via-env] +description = Release the code to PyPI so users can pip install it, using credentials from the environment. +skip_install = true +deps = + {[testenv:build]deps} + uv +commands = + {[testenv:build]commands} + uv publish --publish-url https://upload.pypi.org/legacy/ +passenv = + UV_PUBLISH_USERNAME + UV_PUBLISH_PASSWORD + [testenv:finish] description = Run a workflow that removes -dev from the version, creates a tagged release on GitHub, From 1a03dc72a428e6c47fbf024158ad7a184834e00f Mon Sep 17 00:00:00 2001 From: Charles Tapley Hoyt Date: Wed, 18 Dec 2024 10:28:52 +0100 Subject: [PATCH 3/4] Pass ruff --- scripts/generate_chebi_mesh_mappings.py | 6 ++--- scripts/generate_cl_mesh_mappings.py | 2 +- scripts/generate_doid_mesh_mappings.py | 4 +-- scripts/generate_hp_mesh_mappings.py | 4 +-- scripts/generate_mondo_mesh_mappings.py | 4 +-- scripts/generate_uberon_mesh_mappings.py | 2 +- src/biomappings/gilda_utils.py | 2 +- src/biomappings/paper_analysis.py | 4 +-- src/biomappings/resources/__init__.py | 34 ++++++++++++------------ src/biomappings/utils.py | 14 +++++----- 10 files changed, 38 insertions(+), 38 deletions(-) diff --git a/scripts/generate_chebi_mesh_mappings.py b/scripts/generate_chebi_mesh_mappings.py index 41d73601..e3956ae5 100644 --- a/scripts/generate_chebi_mesh_mappings.py +++ b/scripts/generate_chebi_mesh_mappings.py @@ -26,7 +26,7 @@ continue mesh_chebi_simple.append(mesh_chebi_pair) - print("Found %d CHEBI-MESH mappings." % len(mesh_chebi_simple)) + print(f"Found {len(mesh_chebi_simple)} CHEBI-MESH mappings.") predictions = [] n_redundant = 0 @@ -53,7 +53,7 @@ predictions.append(pred) print( - "A total of %d mappings could be indirectly inferred from" - "INDRA ontology xrefs" % len(n_redundant) + f"A total of {n_redundant} mappings could be indirectly inferred from" + "INDRA ontology xrefs" ) append_prediction_tuples(predictions, deduplicate=True, sort=True) diff --git a/scripts/generate_cl_mesh_mappings.py b/scripts/generate_cl_mesh_mappings.py index 4322a66d..cf62a8f1 100644 --- a/scripts/generate_cl_mesh_mappings.py +++ b/scripts/generate_cl_mesh_mappings.py @@ -49,7 +49,7 @@ mappings[node] = mesh_id -print("Found %d CL->MESH mappings." % len(mappings)) +print(f"Found {len(mappings)} CL->MESH mappings.") predictions = [] for cl_id, mesh_id in mappings.items(): diff --git a/scripts/generate_doid_mesh_mappings.py b/scripts/generate_doid_mesh_mappings.py index cd634566..1eabe68d 100644 --- a/scripts/generate_doid_mesh_mappings.py +++ b/scripts/generate_doid_mesh_mappings.py @@ -57,7 +57,7 @@ mappings[node] = matches[0].term.id -print("Found %d DOID->MESH mappings." % len(mappings)) +print(f"Found {len(mappings)} DOID->MESH mappings.") # We makes sure that (i) the node is not already mappable to MESH and that # (ii) there isn't some other node that was not already mapped to the @@ -73,7 +73,7 @@ cnt = Counter(mappings.values()) mappings = {k: v for k, v in mappings.items() if cnt[v] == 1} -print("Found %d filtered DOID->MESH mappings." % len(mappings)) +print(f"Found {len(mappings)} filtered DOID->MESH mappings.") # We can now add the predictions predictions = [] diff --git a/scripts/generate_hp_mesh_mappings.py b/scripts/generate_hp_mesh_mappings.py index 67d1a8eb..95de2d00 100644 --- a/scripts/generate_hp_mesh_mappings.py +++ b/scripts/generate_hp_mesh_mappings.py @@ -66,7 +66,7 @@ if grounding[0] == "MESH": mappings[node] = matches[0].term.id -print("Found %d HP->MESH mappings." % len(mappings)) +print(f"Found {len(mappings)} HP->MESH mappings.") # We makes sure that (i) the node is not already mappable to MESH and that # (ii) there isn't some other node that was not already mapped to the @@ -82,7 +82,7 @@ cnt = Counter(mappings.values()) mappings = {k: v for k, v in mappings.items() if cnt[v] == 1} -print("Found %d filtered HP->MESH mappings." % len(mappings)) +print(f"Found {len(mappings)} filtered HP->MESH mappings.") # We can now add the predictions predictions = [] diff --git a/scripts/generate_mondo_mesh_mappings.py b/scripts/generate_mondo_mesh_mappings.py index df22d23e..10bba337 100644 --- a/scripts/generate_mondo_mesh_mappings.py +++ b/scripts/generate_mondo_mesh_mappings.py @@ -42,7 +42,7 @@ mappings[node] = matches[0].term.id -print("Found %d MONDO->MESH mappings." % len(mappings)) +print(f"Found {len(mappings)} MONDO->MESH mappings.") mappings = { k: v @@ -54,7 +54,7 @@ mappings = {k: v for k, v in mappings.items() if cnt[v] == 1} -print("Found %d MONDO->MESH mappings." % len(mappings)) +print(f"Found {len(mappings)} MONDO->MESH mappings.") predictions = [] for mondo_id, mesh_id in mappings.items(): diff --git a/scripts/generate_uberon_mesh_mappings.py b/scripts/generate_uberon_mesh_mappings.py index b795ac3f..34b34014 100644 --- a/scripts/generate_uberon_mesh_mappings.py +++ b/scripts/generate_uberon_mesh_mappings.py @@ -19,7 +19,7 @@ if matches and matches[0].term.db == "MESH": mappings[node] = matches[0].term.id -print("Found %d UBERON->MESH mappings." % len(mappings)) +print(f"Found {len(mappings)} UBERON->MESH mappings.") predictions = [] for uberon_id, mesh_id in mappings.items(): diff --git a/src/biomappings/gilda_utils.py b/src/biomappings/gilda_utils.py index d3f65bb0..44245606 100644 --- a/src/biomappings/gilda_utils.py +++ b/src/biomappings/gilda_utils.py @@ -16,10 +16,10 @@ __all__ = [ "append_gilda_predictions", - "iter_prediction_tuples", "filter_custom", "filter_existing_xrefs", "has_mapping", + "iter_prediction_tuples", ] logger = logging.getLogger(__name__) diff --git a/src/biomappings/paper_analysis.py b/src/biomappings/paper_analysis.py index 1414129a..bed5fc75 100644 --- a/src/biomappings/paper_analysis.py +++ b/src/biomappings/paper_analysis.py @@ -18,9 +18,9 @@ __all__ = [ "Result", - "get_primary_mappings", - "get_obo_mappings", "get_non_obo_mappings", + "get_obo_mappings", + "get_primary_mappings", "index_mappings", ] diff --git a/src/biomappings/resources/__init__.py b/src/biomappings/resources/__init__.py index 2ca94efe..8ac87d27 100644 --- a/src/biomappings/resources/__init__.py +++ b/src/biomappings/resources/__init__.py @@ -15,31 +15,31 @@ from biomappings.utils import OVERRIDE_MIRIAM, RESOURCE_PATH, get_canonical_tuple __all__ = [ - "MappingTuple", "MAPPINGS_HEADER", - "PredictionTuple", "PREDICTIONS_HEADER", + "MappingTuple", "Mappings", - "load_mappings", - "load_mappings_subset", - "append_true_mappings", - "append_true_mapping_tuples", - "write_true_mappings", - "load_false_mappings", + "PredictionTuple", "append_false_mappings", - "write_false_mappings", - "load_unsure", - "append_unsure_mappings", - "write_unsure_mappings", - "load_predictions", - "append_predictions", "append_prediction_tuples", - "write_predictions", - "remove_mappings", - "load_curators", + "append_predictions", + "append_true_mapping_tuples", + "append_true_mappings", + "append_unsure_mappings", "filter_predictions", "get_curated_filter", + "load_curators", + "load_false_mappings", + "load_mappings", + "load_mappings_subset", + "load_predictions", + "load_unsure", "prediction_tuples_from_semra", + "remove_mappings", + "write_false_mappings", + "write_predictions", + "write_true_mappings", + "write_unsure_mappings", ] logger = logging.getLogger(__name__) diff --git a/src/biomappings/utils.py b/src/biomappings/utils.py index 3b1040fc..3c07bf9e 100644 --- a/src/biomappings/utils.py +++ b/src/biomappings/utils.py @@ -10,17 +10,17 @@ import bioregistry __all__ = [ - "get_git_hash", - "get_script_url", - "get_canonical_tuple", - "UnregisteredPrefix", - "UnstandardizedPrefix", + "CMapping", "InvalidIdentifier", - "InvalidNormIdentifier", "InvalidIdentifierPattern", + "InvalidNormIdentifier", + "UnregisteredPrefix", + "UnstandardizedPrefix", "check_valid_prefix_id", + "get_canonical_tuple", "get_curie", - "CMapping", + "get_git_hash", + "get_script_url", ] HERE = Path(__file__).parent.resolve() From acef844764ea5250ec52e6e035af39080bf12a26 Mon Sep 17 00:00:00 2001 From: Krishna Udaiwal Date: Wed, 18 Dec 2024 04:33:01 -0500 Subject: [PATCH 4/4] Update README (#174) Fixed typo from old style git clone url --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 10c84fce..36bf8039 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ Rather than editing files locally, this repository also comes with a web-based c development mode with the `web` option (which installs `flask` and `flask-bootstrap`) using: ```console -$ git clone git+https://github.com/biopragmatics/biomappings.git +$ git clone https://github.com/biopragmatics/biomappings.git $ cd biomappings $ git checkout -b your-branch-name $ pip install -e .[web] @@ -186,7 +186,7 @@ python3 -m pip install git+https://github.com/biopragmatics/biomappings.git To install in development mode and create a new branch, use the following: ```console -$ git clone git+https://github.com/biopragmatics/biomappings.git +$ git clone https://github.com/biopragmatics/biomappings.git $ cd biomappings $ python3 -m pip install -e . ```