diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..58f34cd --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,21 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.241.1/containers/python-3/.devcontainer/base.Dockerfile +# FROM ubuntu:latest +# [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.10, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster +ARG VARIANT="3.10-bullseye" +FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} + +# # [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 +ARG NODE_VERSION="none" +RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi + +# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image. +# COPY requirements.txt /tmp/pip-tmp/ +# RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \ +# && rm -rf /tmp/pip-tmp + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends + +# [Optional] Uncomment this line to install global node packages. +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..13b0199 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,52 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.241.1/containers/python-3 +{ + "name": "Python 3", + "runArgs": [ + "--net", + "host" + ], + "build": { + "dockerfile": "Dockerfile", + "context": "..", + "args": { + // Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6 + // Append -bullseye or -buster to pin to an OS version. + // Use -bullseye variants on local on arm64/Apple Silicon. + "VARIANT": "3.11", + // Options + "NODE_VERSION": "none" + } + }, + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + "python.defaultInterpreterPath": "/usr/local/bin/python", + "python.linting.enabled": true, + "python.linting.pylintEnabled": true, + "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", + "python.formatting.blackPath": "/usr/local/py-utils/bin/black", + "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", + "python.linting.banditPath": "/usr/local/py-utils/bin/bandit", + "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", + "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", + "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", + "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", + "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint" + }, + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance" + ] + } + }, + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "pip3 install --user -r requirements.txt", + // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "root" +} \ No newline at end of file diff --git a/.github/CONTRIBUTING.rst b/.github/CONTRIBUTING.rst new file mode 100644 index 0000000..c7e7794 --- /dev/null +++ b/.github/CONTRIBUTING.rst @@ -0,0 +1,35 @@ +Contributing to the project +=========================== + +Contributions and issues are most welcome! All issues and pull requests are +handled through GitHub_. Also, please check for any existing issues before +filing a new one. If you have a great idea but it involves big changes, please +file a ticket before making a pull request! We want to make sure you don't spend +your time coding something that might not fit the scope of the project. + +.. _GitHub: https://github.com/DiamondLightSource/diffcalc-api/issues + +Issue or Discussion? +-------------------- + +Github also offers discussions_ as a place to ask questions and share ideas. If +your issue is open ended and it is not obvious when it can be "closed", please +raise it as a discussion instead. + +.. _discussions: https://github.com/DiamondLightSource/diffcalc-api/discussions + +Code coverage +------------- + +While 100% code coverage does not make a library bug-free, it significantly +reduces the number of easily caught bugs! Please make sure coverage remains the +same or is improved by a pull request! + +Developer guide +--------------- + +The `Developer Guide`_ contains information on setting up a development +environment, running the tests and what standards the code and documentation +should follow. + +.. _Developer Guide: https://diamondlightsource.github.io/diffcalc-api/master/developer/how-to/contribute.html diff --git a/.github/actions/install_requirements/action.yml b/.github/actions/install_requirements/action.yml new file mode 100644 index 0000000..25a146d --- /dev/null +++ b/.github/actions/install_requirements/action.yml @@ -0,0 +1,58 @@ +name: Install requirements +description: Run pip install with requirements and upload resulting requirements +inputs: + requirements_file: + description: Name of requirements file to use and upload + required: true + install_options: + description: Parameters to pass to pip install + required: true + python_version: + description: Python version to install + default: "3.x" + +runs: + using: composite + + steps: + - name: Setup python + uses: actions/setup-python@v4 + with: + python-version: ${{ inputs.python_version }} + + - name: Pip install + run: | + touch ${{ inputs.requirements_file }} + # -c uses requirements.txt as constraints, see 'Validate requirements file' + pip install -c ${{ inputs.requirements_file }} ${{ inputs.install_options }} + shell: bash + + - name: Create lockfile + run: | + mkdir -p lockfiles + pip freeze --exclude-editable > lockfiles/${{ inputs.requirements_file }} + # delete the self referencing line and make sure it isn't blank + sed -i '/file:/d' lockfiles/${{ inputs.requirements_file }} + shell: bash + + - name: Upload lockfiles + uses: actions/upload-artifact@v3 + with: + name: lockfiles + path: lockfiles + + # This eliminates the class of problems where the requirements being given no + # longer match what the packages themselves dictate. E.g. In the rare instance + # where I install some-package which used to depend on vulnerable-dependency + # but now uses good-dependency (despite being nominally the same version) + # pip will install both if given a requirements file with -r + - name: If requirements file exists, check it matches pip installed packages + run: | + if [ -s ${{ inputs.requirements_file }} ]; then + if ! diff -u ${{ inputs.requirements_file }} lockfiles/${{ inputs.requirements_file }}; then + echo "Error: ${{ inputs.requirements_file }} need the above changes to be exhaustive" + exit 1 + fi + fi + shell: bash + diff --git a/.github/pages/make_switcher.py b/.github/pages/make_switcher.py new file mode 100644 index 0000000..39c1277 --- /dev/null +++ b/.github/pages/make_switcher.py @@ -0,0 +1,99 @@ +import json +import logging +from argparse import ArgumentParser +from pathlib import Path +from subprocess import CalledProcessError, check_output +from typing import List, Optional + + +def report_output(stdout: bytes, label: str) -> List[str]: + ret = stdout.decode().strip().split("\n") + print(f"{label}: {ret}") + return ret + + +def get_branch_contents(ref: str) -> List[str]: + """Get the list of directories in a branch.""" + stdout = check_output(["git", "ls-tree", "-d", "--name-only", ref]) + return report_output(stdout, "Branch contents") + + +def get_sorted_tags_list() -> List[str]: + """Get a list of sorted tags in descending order from the repository.""" + stdout = check_output(["git", "tag", "-l", "--sort=-v:refname"]) + return report_output(stdout, "Tags list") + + +def get_versions(ref: str, add: Optional[str], remove: Optional[str]) -> List[str]: + """Generate the file containing the list of all GitHub Pages builds.""" + # Get the directories (i.e. builds) from the GitHub Pages branch + try: + builds = set(get_branch_contents(ref)) + except CalledProcessError: + builds = set() + logging.warning(f"Cannot get {ref} contents") + + # Add and remove from the list of builds + if add: + builds.add(add) + if remove: + assert remove in builds, f"Build '{remove}' not in {sorted(builds)}" + builds.remove(remove) + + # Get a sorted list of tags + tags = get_sorted_tags_list() + + # Make the sorted versions list from main branches and tags + versions: List[str] = [] + for version in ["master", "main"] + tags: + if version in builds: + versions.append(version) + builds.remove(version) + + # Add in anything that is left to the bottom + versions += sorted(builds) + print(f"Sorted versions: {versions}") + return versions + + +def write_json(path: Path, repository: str, versions: str): + org, repo_name = repository.split("/") + struct = [ + dict(version=version, url=f"https://{org}.github.io/{repo_name}/{version}/") + for version in versions + ] + text = json.dumps(struct, indent=2) + print(f"JSON switcher:\n{text}") + path.write_text(text) + + +def main(args=None): + parser = ArgumentParser( + description="Make a versions.txt file from gh-pages directories" + ) + parser.add_argument( + "--add", + help="Add this directory to the list of existing directories", + ) + parser.add_argument( + "--remove", + help="Remove this directory from the list of existing directories", + ) + parser.add_argument( + "repository", + help="The GitHub org and repository name: ORG/REPO", + ) + parser.add_argument( + "output", + type=Path, + help="Path of write switcher.json to", + ) + args = parser.parse_args(args) + + # Write the versions file + versions = get_versions("origin/gh-pages", args.add, args.remove) + write_json(args.output, args.repository, versions) + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml index 738606e..0135185 100644 --- a/.github/workflows/code.yml +++ b/.github/workflows/code.yml @@ -1,130 +1,200 @@ name: Code CI -on: [push] +on: + push: + pull_request: + schedule: + # Run weekly to check latest versions of dependencies + - cron: "0 8 * * WED" +env: + # The target python version, which must match the Dockerfile version + CONTAINER_PYTHON: "3.11" jobs: lint: - strategy: - fail-fast: false - matrix: - os: ["ubuntu-latest"] - python: ["3.9"] + # pull requests are a duplicate of a branch push if within the same repo. + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository + runs-on: ubuntu-latest - runs-on: ${{ matrix.os }} steps: - - name: Checkout Source + - name: Checkout uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Setup python - uses: actions/setup-python@v4 + - name: Install python packages + uses: ./.github/actions/install_requirements with: - python-version: ${{ matrix.python }} - - - name: Install dependancies - run: | - python -m pip install --upgrade pip - pip install .[dev] -r requirements.txt - - name: Run black, flake8, mypy - run: | - pre-commit run --all-files --show-diff-on-failure --color=always -v + requirements_file: requirements-dev-3.x.txt + install_options: -e .[dev] - sdist_wheel: + - name: Lint + run: tox -e pre-commit,mypy + + test: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository strategy: fail-fast: false matrix: - os: ["ubuntu-latest"] - python: ["3.9"] + os: ["ubuntu-latest"] # can add windows-latest, macos-latest + python: ["3.9", "3.10", "3.11"] + install: ["-e .[dev]"] + # Make one version be non-editable to test both paths of version code + include: + - os: "ubuntu-latest" + python: "3.8" + install: ".[dev]" runs-on: ${{ matrix.os }} + env: + # https://github.com/pytest-dev/pytest/issues/2042 + PY_IGNORE_IMPORTMISMATCH: "1" + steps: - - name: Checkout Source + - name: Checkout uses: actions/checkout@v3 with: + # Need this to get version number from last tag fetch-depth: 0 - - name: Setup python - uses: actions/setup-python@v4 + - name: Install python packages + uses: ./.github/actions/install_requirements + with: + python_version: ${{ matrix.python }} + requirements_file: requirements-test-${{ matrix.os }}-${{ matrix.python }}.txt + install_options: ${{ matrix.install }} + + - name: List dependency tree + run: pipdeptree + + - name: Run tests + run: pytest + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + name: ${{ matrix.python }}/${{ matrix.os }} + files: cov.xml + + dist: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository + runs-on: "ubuntu-latest" + + steps: + - name: Checkout + uses: actions/checkout@v3 with: - python-version: ${{ matrix.python }} + # Need this to get version number from last tag + fetch-depth: 0 - - name: Build Sdist - # Set SOURCE_DATE_EPOCH from git commit for reproducible build - # https://reproducible-builds.org/ - # Set group writable and umask to do the same to match inside DLS + - name: Build sdist and wheel run: | - chmod -R g+w . - umask 0002 - SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) pipx run --python $(which python${{ matrix.python }}) build --sdist --wheel - - name: Upload Sdist as artifact + export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) && \ + pipx run build + + - name: Upload sdist and wheel as artifacts uses: actions/upload-artifact@v3 with: name: dist - path: dist/* + path: dist - test: - strategy: - fail-fast: false - matrix: - os: ["ubuntu-latest"] # can add windows-latest, macos-latest - python: ["3.8", "3.9", "3.10"] - deploy: [false] + - name: Check for packaging errors + run: pipx run twine check --strict dist/* - include: - # Add an extra Python3.8 runner to use the lockfile - - os: "ubuntu-latest" - python: "3.8" - deploy: true + - name: Install python packages + uses: ./.github/actions/install_requirements + with: + python_version: ${{env.CONTAINER_PYTHON}} + requirements_file: requirements.txt + install_options: dist/*.whl - runs-on: ${{ matrix.os }} - env: - # https://github.com/pytest-dev/pytest/issues/2042 - PY_IGNORE_IMPORTMISMATCH: "1" + - name: Test module --version works using the installed wheel + # If more than one module in src/ replace with module name to test + run: python -m $(ls src | head -1) --version + + container: + needs: [lint, dist, test] + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write steps: - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v3 + + # image names must be all lower case + - name: Generate image repo name + run: echo IMAGE_REPOSITORY=ghcr.io/$(tr '[:upper:]' '[:lower:]' <<< "${{ github.repository }}") >> $GITHUB_ENV + + - name: Download wheel and lockfiles + uses: actions/download-artifact@v3 with: - fetch-depth: 0 + path: artifacts/ - - name: Setup python - uses: actions/setup-python@v4 + - name: Log in to GitHub Docker Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v2 with: - python-version: ${{ matrix.python }} - - - name: Install dependencies - if: ${{ !matrix.deploy }} - run: | - python -m pip install --upgrade pip - pip install .[dev] -r requirements.txt + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - - name: Install dependencies (deploy) - if: ${{ matrix.deploy }} - run: | - python -m pip install --upgrade pip - pip install .[dev] -r requirements.txt + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ env.IMAGE_REPOSITORY }} + tags: | + type=ref,event=tag + type=raw,value=latest - - name: Run tests with pytest - run: | - pytest + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v2 + + - name: Build runtime image + uses: docker/build-push-action@v3 + with: + build-args: | + PIP_OPTIONS=-r lockfiles/requirements.txt dist/*.whl + push: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags') }} + load: ${{ ! (github.event_name == 'push' && startsWith(github.ref, 'refs/tags')) }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + context: artifacts/ + file: ./Dockerfile + # If you have a long docker build, uncomment the following to turn on caching + # For short build times this makes it a little slower + #cache-from: type=gha + #cache-to: type=gha,mode=max + + - name: Test cli works in runtime image + run: docker run ${{ env.IMAGE_REPOSITORY }} --version release: - needs: [lint, sdist_wheel, test] - runs-on: ubuntu-latest # upload to PyPI and make a release on every tag - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + needs: [lint, dist, test] + if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags') }} + runs-on: ubuntu-latest + env: + HAS_PYPI_TOKEN: ${{ secrets.PYPI_TOKEN != '' }} + steps: - uses: actions/download-artifact@v3 - with: - name: dist - path: dist + + - name: Fixup blank lockfiles + # Github release artifacts can't be blank + run: for f in lockfiles/*; do [ -s $f ] || echo '# No requirements' >> $f; done - name: Github Release # We pin to the SHA, not the tag, for security reasons. # https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15 with: - files: dist/* + prerelease: ${{ contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc') }} + files: | + dist/* + lockfiles/* generate_release_notes: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml deleted file mode 100644 index ea6d462..0000000 --- a/.github/workflows/container.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Container CI - -on: - push: - tags: - - '*' - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - -jobs: - build-and-push-image: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Log in to the Container registry - uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@57396166ad8aefe6098280995947635806a0e6ea - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build and push Docker tagged image - uses: docker/build-push-action@c56af957549030174b10d6867f20e78cfd7debc5 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/.github/workflows/docs-clean.yml b/.github/workflows/docs-clean.yml new file mode 100644 index 0000000..a67e188 --- /dev/null +++ b/.github/workflows/docs-clean.yml @@ -0,0 +1,43 @@ +name: Docs Cleanup CI + +# delete branch documentation when a branch is deleted +# also allow manually deleting a documentation version +on: + delete: + workflow_dispatch: + inputs: + version: + description: "documentation version to DELETE" + required: true + type: string + +jobs: + remove: + if: github.event.ref_type == 'branch' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: gh-pages + + - name: removing documentation for branch ${{ github.event.ref }} + if: ${{ github.event_name != 'workflow_dispatch' }} + run: echo "REF_NAME=${{ github.event.ref }}" >> $GITHUB_ENV + + - name: manually removing documentation version ${{ github.event.inputs.version }} + if: ${{ github.event_name == 'workflow_dispatch' }} + run: echo "REF_NAME=${{ github.event.inputs.version }}" >> $GITHUB_ENV + + - name: Sanitize ref name for docs version + run: echo "DOCS_VERSION=${REF_NAME//[^A-Za-z0-9._-]/_}" >> $GITHUB_ENV + + - name: update index and push changes + run: | + rm -r $DOCS_VERSION + python make_switcher.py --remove $DOCS_VERSION ${{ github.repository }} switcher.json + git config --global user.name 'GitHub Actions Docs Cleanup CI' + git config --global user.email 'GithubActionsCleanup@noreply.github.com' + git commit -am "Removing redundant docs version $DOCS_VERSION" + git push diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..c510d57 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,53 @@ +name: Docs CI + +on: + push: + pull_request: + +jobs: + docs: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository + runs-on: ubuntu-latest + + steps: + - name: Avoid git conflicts when tag and branch pushed at same time + if: startsWith(github.ref, 'refs/tags') + run: sleep 60 + + - name: Checkout + uses: actions/checkout@v3 + with: + # Need this to get version number from last tag + fetch-depth: 0 + + - name: Install system packages + # Can delete this if you don't use graphviz in your docs + run: sudo apt-get install graphviz + + - name: Install python packages + uses: ./.github/actions/install_requirements + with: + requirements_file: requirements-dev-3.x.txt + install_options: -e .[dev] + + - name: Build docs + run: tox -e docs + + - name: Sanitize ref name for docs version + run: echo "DOCS_VERSION=${GITHUB_REF_NAME//[^A-Za-z0-9._-]/_}" >> $GITHUB_ENV + + - name: Move to versioned directory + run: mv build/html .github/pages/$DOCS_VERSION + + - name: Write switcher.json + run: python .github/pages/make_switcher.py --add $DOCS_VERSION ${{ github.repository }} .github/pages/switcher.json + + - name: Publish Docs to gh-pages + if: github.event_name == 'push' && github.actor != 'dependabot[bot]' + # We pin to the SHA, not the tag, for security reasons. + # https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions + uses: peaceiris/actions-gh-pages@64b46b4226a4a12da2239ba3ea5aa73e3163c75b # v3.9.1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: .github/pages + keep_files: true diff --git a/.github/workflows/linkcheck.yml b/.github/workflows/linkcheck.yml new file mode 100644 index 0000000..1140e52 --- /dev/null +++ b/.github/workflows/linkcheck.yml @@ -0,0 +1,24 @@ +name: Link Check + +on: + workflow_dispatch: + schedule: + # Run weekly to check URL links still resolve + - cron: "0 8 * * WED" + +jobs: + docs: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install python packages + uses: ./.github/actions/install_requirements + with: + requirements_file: requirements-dev-3.x.txt + install_options: -e .[dev] + + - name: Check links + run: tox -e docs build -- -b linkcheck \ No newline at end of file diff --git a/.gitignore b/.gitignore index a6dca8a..3e6067b 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ var/ *.egg-info/ .installed.cfg *.egg +**/_version.py # PyInstaller # Usually these files are written by a python script from a template @@ -68,6 +69,3 @@ target/ /venv/ /lightweight-venv/ /installed.files - -# Development container -.devcontainer/ \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 1efd502..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,4 +0,0 @@ -include: - - project: 'controls/reports/ci_templates' - ref: master - file: 'python3/dls_py3_template.yml' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index efc0a17..59bf16c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,6 +15,7 @@ repos: language: system entry: black --check --diff types: [python] + exclude: ^src\/diffcalc_api\/_version.py - id: flake8 name: Run flake8 diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 734f215..8192299 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,9 @@ { "recommendations": [ - "ms-python.vscode-pylance", + "ms-vscode-remote.remote-containers", "ms-python.python", + "tamasfe.even-better-toml", + "redhat.vscode-yaml", "ryanluker.vscode-coverage-gutters" ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 7aac7b7..2472acf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,12 +11,5 @@ "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.organizeImports": true - }, - // "files.associations": { - // "test_*.py": "test" - // }, - // "[test]": { - // "editor.formatOnSave": false - // }, - "esbonio.sphinx.confDir": "" + } } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index dda6870..b29728b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -6,7 +6,7 @@ { "type": "shell", "label": "Tests with coverage", - "command": "pytest", + "command": "tox -p", "options": { "cwd": "${workspaceRoot}" }, diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index d499c29..0000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,122 +0,0 @@ -Contributing -============ - -Contributions and issues are most welcome! All issues and pull requests are -handled through GitHub_. Also, please check for any existing issues before -filing a new one. If you have a great idea but it involves big changes, please -file a ticket before making a pull request! We want to make sure you don't spend -your time coding something that might not fit the scope of the project. - -.. _GitHub: git@github.com:DiamondLightSource/diffcalc-api.git - -Running the tests ------------------ - -To get the source source code and run the unit tests, run:: - - $ git clone git@github.com:DiamondLightSource/diffcalc-api.git - $ cd diffcalc-api - $ pip install -e .[dev] - $ pytest - -While 100% code coverage does not make a library bug-free, it significantly -reduces the number of easily caught bugs! Please make sure coverage remains the -same or is improved by a pull request! - -Code Styling ------------- - -The code in this repository conforms to standards set by the following tools: - -- black_ for code formatting -- flake8_ for style checks -- isort_ for import ordering -- mypy_ for static type checking - -These checks will be run by pre-commit_. You can either choose to run these -tests on all files tracked by git:: - - $ pre-commit run --all-files --show-diff-on-failure --color=always -v - -Or you can install a pre-commit hook that will run each time you do a ``git -commit`` on just the files that have changed:: - - $ pre-commit install - -.. _black: https://github.com/psf/black -.. _flake8: https://flake8.pycqa.org/en/latest/ -.. _isort: https://github.com/PyCQA/isort -.. _mypy: https://github.com/python/mypy -.. _pre-commit: https://pre-commit.com/ - -Docstrings are pre-processed using the Sphinx Napoleon extension. As such, -google-style_ is considered as standard for this repository. Please use type -hints in the function signature for types. For example:: - - def func(arg1: str, arg2: int) -> bool: - """Summary line. - - Extended description of function. - - Args: - arg1: Description of arg1 - arg2: Description of arg2 - - Returns: - Description of return value - """ - return True - -.. _google-style: https://sphinxcontrib-napoleon.readthedocs.io/en/latest/index.html#google-vs-numpy - -Documentation -------------- - -Documentation is contained in the ``docs`` directory and extracted from -docstrings of the API. - -Docs follow the underlining convention:: - - Headling 1 (page title) - ======================= - - Heading 2 - --------- - - Heading 3 - ~~~~~~~~~ - -You can build the docs from the project directory by running:: - - $ sphinx-build -EWT --keep-going docs build/html - $ firefox build/html/index.html - -Release Process ---------------- - -To make a new release, please follow this checklist: - -- Choose a new PEP440 compliant release number -- Git tag the version -- Push to GitHub and the actions will make a release on pypi -- Push to internal gitlab and do a dls-release.py of the tag -- Check and edit for clarity the autogenerated GitHub release_ - -.. _release: https://github.com/DiamondLightSource/diffcalc-api/releases - -Updating the tools ------------------- - -This module is merged with the dls-python3-skeleton_. This is a generic -Python project structure which provides a means to keep tools and -techniques in sync between multiple Python projects. To update to the -latest version of the skeleton, run:: - - $ git pull https://github.com/dls-controls/dls-python3-skeleton skeleton - -Any merge conflicts will indicate an area where something has changed that -conflicts with the setup of the current module. Check the `closed pull requests -`_ -of the skeleton module for more details. - -.. _dls-python3-skeleton: https://dls-controls.github.io/dls-python3-skeleton diff --git a/Dockerfile b/Dockerfile index ed6e903..42cd4b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,11 @@ #Install python and copy over repo files FROM python:3.8 +ARG PIP_OPTIONS=. ENV ENV_DIR /diffcalc_api WORKDIR ${ENV_DIR} - COPY . ${ENV_DIR} -#Install dependencies -RUN pip install . -c requirements.txt - +RUN pip install ${PIP_OPTIONS} -#Run the API -CMD ["uvicorn", "src.diffcalc_api.server:app", "--host", "0.0.0.0"] \ No newline at end of file +ENTRYPOINT ["diffcalc_api"] \ No newline at end of file diff --git a/docs/_static/theme_overrides.css b/docs/_static/theme_overrides.css deleted file mode 100644 index 5fd9b72..0000000 --- a/docs/_static/theme_overrides.css +++ /dev/null @@ -1,34 +0,0 @@ -/* override table width restrictions */ -@media screen and (min-width: 639px) { - .wy-table-responsive table td { - /* !important prevents the common CSS stylesheets from - overriding this as on RTD they are loaded after this stylesheet */ - white-space: normal !important; - } -} - -/* override table padding */ -.rst-content table.docutils th, .rst-content table.docutils td { - padding: 4px 6px; -} - -/* Add two-column option */ -@media only screen and (min-width: 1000px) { - .columns { - padding-left: 10px; - padding-right: 10px; - float: left; - width: 50%; - min-height: 145px; - } -} - -.endcolumns { - clear: both -} - -/* Hide toctrees within columns and captions from all toctrees. - This is what makes the include trick in index.rst work */ -.columns .toctree-wrapper, .toctree-wrapper .caption-text { - display: none; -} diff --git a/docs/conf.py b/docs/conf.py index 23e582a..d60466a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -4,6 +4,12 @@ # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html +import sys +from pathlib import Path +from subprocess import check_output + +import requests + import diffcalc_api # -- General configuration ------------------------------------------------ @@ -16,8 +22,10 @@ # The short X.Y version. if "+" in release: - # Not on a tag - version = "master" + # Not on a tag, use branch name + root = Path(__file__).absolute().parent.parent + git_branch = check_output("git branch --show-current".split(), cwd=root) + version = git_branch.decode().strip() else: version = release @@ -32,17 +40,30 @@ "sphinx.ext.viewcode", # Adds the inheritance-diagram generation directive "sphinx.ext.inheritance_diagram", + # Add a copy button to each code block + "sphinx_copybutton", + # For the card element + "sphinx_design", ] # If true, Sphinx will warn about all references where the target cannot # be found. -nitpicky = False +nitpicky = True # A list of (type, target) tuples (by default empty) that should be ignored when # generating warnings in "nitpicky mode". Note that type should include the # domain name if present. Example entries would be ('py:func', 'int') or # ('envvar', 'LD_LIBRARY_PATH'). -nitpick_ignore = [("py:func", "int")] +nitpick_ignore = [ + ("py:class", "NoneType"), + ("py:class", "'str'"), + ("py:class", "'float'"), + ("py:class", "'int'"), + ("py:class", "'bool'"), + ("py:class", "'object'"), + ("py:class", "'id'"), + ("py:class", "typing_extensions.Literal"), +] # Both the class’ and the __init__ method’s docstring are concatenated and # inserted into the main body of the autoclass directive @@ -84,27 +105,84 @@ # Common links that should be available on every page rst_epilog = """ -.. _Diamond Light Source: - http://www.diamond.ac.uk +.. _Diamond Light Source: http://www.diamond.ac.uk +.. _black: https://github.com/psf/black +.. _flake8: https://flake8.pycqa.org/en/latest/ +.. _isort: https://github.com/PyCQA/isort +.. _mypy: http://mypy-lang.org/ +.. _pre-commit: https://pre-commit.com/ """ -# Ignore localhost links for period check that links in docs are valid +# Ignore localhost links for periodic check that links in docs are valid linkcheck_ignore = [r"http://localhost:\d+/"] +# Set copy-button to ignore python and bash prompts +# https://sphinx-copybutton.readthedocs.io/en/latest/use.html#using-regexp-prompt-identifiers +copybutton_prompt_text = r">>> |\.\.\. |\$ |In \[\d*\]: | {2,5}\.\.\.: | {5,8}: " +copybutton_prompt_is_regexp = True + # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = "sphinx_rtd_theme_github_versions" - -# Options for the sphinx rtd theme, use DLS blue -html_theme_options = dict(style_nav_header_background="rgb(7, 43, 93)") - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] +html_theme = "pydata_sphinx_theme" +github_repo = project +github_user = "DiamondLightSource" +switcher_json = f"https://{github_user}.github.io/{github_repo}/switcher.json" +switcher_exists = requests.get(switcher_json).ok +if not switcher_exists: + print( + "*** Can't read version switcher, is GitHub pages enabled? \n" + " Once Docs CI job has successfully run once, set the " + "Github pages source branch to be 'gh-pages' at:\n" + f" https://github.com/{github_user}/{github_repo}/settings/pages", + file=sys.stderr, + ) + +# Theme options for pydata_sphinx_theme +# We don't check switcher because there are 3 possible states for a repo: +# 1. New project, docs are not published so there is no switcher +# 2. Existing project with latest skeleton, switcher exists and works +# 3. Existing project with old skeleton that makes broken switcher, +# switcher exists but is broken +# Point 3 makes checking switcher difficult, because the updated skeleton +# will fix the switcher at the end of the docs workflow, but never gets a chance +# to complete as the docs build warns and fails. +html_theme_options = dict( + logo=dict( + text=project, + ), + use_edit_page_button=True, + github_url=f"https://github.com/{github_user}/{github_repo}", + icon_links=[ + dict( + name="PyPI", + url=f"https://pypi.org/project/{project}", + icon="fas fa-cube", + ) + ], + switcher=dict( + json_url=switcher_json, + version_match=version, + ), + check_switcher=False, + navbar_end=["theme-switcher", "icon-links", "version-switcher"], + external_links=[ + dict( + name="Release Notes", + url=f"https://github.com/{github_user}/{github_repo}/releases", + ) + ], +) + +# A dictionary of values to pass into the template engine’s context for all pages +html_context = dict( + github_user=github_user, + github_repo=project, + github_version=version, + doc_path="docs", +) # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. html_show_sphinx = False @@ -112,9 +190,6 @@ # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. html_show_copyright = False -# Add some CSS classes for columns and other tweaks in a custom css file -html_css_files = ["theme_overrides.css"] - # Logo html_logo = "images/dls-logo.svg" html_favicon = "images/dls-favicon.ico" diff --git a/docs/developer/explanations/decisions.rst b/docs/developer/explanations/decisions.rst new file mode 100644 index 0000000..5841e6e --- /dev/null +++ b/docs/developer/explanations/decisions.rst @@ -0,0 +1,17 @@ +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, You can obtain one at http://mozilla.org/MPL/2.0/. + +Architectural Decision Records +============================== + +We record major architectural decisions in Architecture Decision Records (ADRs), +as `described by Michael Nygard +`_. +Below is the list of our current ADRs. + +.. toctree:: + :maxdepth: 1 + :glob: + + decisions/* \ No newline at end of file diff --git a/docs/developer/explanations/decisions/0001-record-architecture-decisions.rst b/docs/developer/explanations/decisions/0001-record-architecture-decisions.rst new file mode 100644 index 0000000..b2d3d0f --- /dev/null +++ b/docs/developer/explanations/decisions/0001-record-architecture-decisions.rst @@ -0,0 +1,26 @@ +1. Record architecture decisions +================================ + +Date: 2022-02-18 + +Status +------ + +Accepted + +Context +------- + +We need to record the architectural decisions made on this project. + +Decision +-------- + +We will use Architecture Decision Records, as `described by Michael Nygard +`_. + +Consequences +------------ + +See Michael Nygard's article, linked above. To create new ADRs we will copy and +paste from existing ones. diff --git a/docs/developer/how-to/build-docs.rst b/docs/developer/how-to/build-docs.rst new file mode 100644 index 0000000..79e3f78 --- /dev/null +++ b/docs/developer/how-to/build-docs.rst @@ -0,0 +1,38 @@ +Build the docs using sphinx +=========================== + +You can build the `sphinx`_ based docs from the project directory by running:: + + $ tox -e docs + +This will build the static docs on the ``docs`` directory, which includes API +docs that pull in docstrings from the code. + +.. seealso:: + + `documentation_standards` + +The docs will be built into the ``build/html`` directory, and can be opened +locally with a web browse:: + + $ firefox build/html/index.html + +Autobuild +--------- + +You can also run an autobuild process, which will watch your ``docs`` +directory for changes and rebuild whenever it sees changes, reloading any +browsers watching the pages:: + + $ tox -e docs autobuild + +You can view the pages at localhost:: + + $ firefox http://localhost:8000 + +If you are making changes to source code too, you can tell it to watch +changes in this directory too:: + + $ tox -e docs autobuild -- --watch src + +.. _sphinx: https://www.sphinx-doc.org/ \ No newline at end of file diff --git a/docs/developer/how-to/contribute.rst b/docs/developer/how-to/contribute.rst new file mode 100644 index 0000000..65b992f --- /dev/null +++ b/docs/developer/how-to/contribute.rst @@ -0,0 +1 @@ +.. include:: ../../../.github/CONTRIBUTING.rst diff --git a/docs/developer/how-to/lint.rst b/docs/developer/how-to/lint.rst new file mode 100644 index 0000000..1086c3c --- /dev/null +++ b/docs/developer/how-to/lint.rst @@ -0,0 +1,38 @@ +Run linting using pre-commit +============================ + +Code linting is handled by black_, flake8_ and isort_ run under pre-commit_. + +Running pre-commit +------------------ + +You can run the above checks on all files with this command:: + + $ tox -e pre-commit + +Or you can install a pre-commit hook that will run each time you do a ``git +commit`` on just the files that have changed:: + + $ pre-commit install + +Fixing issues +------------- + +If black reports an issue you can tell it to reformat all the files in the +repository:: + + $ black . + +Likewise with isort:: + + $ isort . + +If you get any flake8 issues you will have to fix those manually. + +VSCode support +-------------- + +The ``.vscode/settings.json`` will run black and isort formatters as well as +flake8 checking on save. Issues will be highlighted in the editor window. + + diff --git a/docs/developer/how-to/make-release.rst b/docs/developer/how-to/make-release.rst new file mode 100644 index 0000000..c932fbb --- /dev/null +++ b/docs/developer/how-to/make-release.rst @@ -0,0 +1,16 @@ +Make a release +============== + +To make a new release, please follow this checklist: + +- Choose a new PEP440 compliant release number (see https://peps.python.org/pep-0440/) +- Go to the GitHub release_ page +- Choose ``Draft New Release`` +- Click ``Choose Tag`` and supply the new tag you chose (click create new tag) +- Click ``Generate release notes``, review and edit these notes +- Choose a title and click ``Publish Release`` + +Note that tagging and pushing to the main branch has the same effect except that +you will not get the option to edit the release notes. + +.. _release: https://github.com/DiamondLightSource/diffcalc_api/releases \ No newline at end of file diff --git a/docs/developer/how-to/pin-requirements.rst b/docs/developer/how-to/pin-requirements.rst new file mode 100644 index 0000000..726d60b --- /dev/null +++ b/docs/developer/how-to/pin-requirements.rst @@ -0,0 +1,74 @@ +Pinning Requirements +==================== + +Introduction +------------ + +By design this project only defines dependencies in one place, i.e. in +the ``requires`` table in ``pyproject.toml``. + +In the ``requires`` table it is possible to pin versions of some dependencies +as needed. For library projects it is best to leave pinning to a minimum so +that your library can be used by the widest range of applications. + +When CI builds the project it will use the latest compatible set of +dependencies available (after applying your pins and any dependencies' pins). + +This approach means that there is a possibility that a future build may +break because an updated release of a dependency has made a breaking change. + +The correct way to fix such an issue is to work out the minimum pinning in +``requires`` that will resolve the problem. However this can be quite hard to +do and may be time consuming when simply trying to release a minor update. + +For this reason we provide a mechanism for locking all dependencies to +the same version as a previous successful release. This is a quick fix that +should guarantee a successful CI build. + +Finding the lock files +---------------------- + +Every release of the project will have a set of requirements files published +as release assets. + +For example take a look at the release page for diffcalc_api-cli here: +https://github.com/DiamondLightSource/diffcalc_api-cli/releases/tag/3.3.0 + +There is a list of requirements*.txt files showing as assets on the release. + +There is one file for each time the CI installed the project into a virtual +environment. There are multiple of these as the CI creates a number of +different environments. + +The files are created using ``pip freeze`` and will contain a full list +of the dependencies and sub-dependencies with pinned versions. + +You can download any of these files by clicking on them. It is best to use +the one that ran with the lowest Python version as this is more likely to +be compatible with all the versions of Python in the test matrix. +i.e. ``requirements-test-ubuntu-latest-3.8.txt`` in this example. + +Applying the lock file +---------------------- + +To apply a lockfile: + +- copy the requirements file you have downloaded to the root of your + repository +- rename it to requirements.txt +- commit it into the repo +- push the changes + +The CI looks for a requirements.txt in the root and will pass it to pip +when installing each of the test environments. pip will then install exactly +the same set of packages as the previous release. + +Removing dependency locking from CI +----------------------------------- + +Once the reasons for locking the build have been resolved it is a good idea +to go back to an unlocked build. This is because you get an early indication +of any incoming problems. + +To restore unlocked builds in CI simply remove requirements.txt from the root +of the repo and push. diff --git a/docs/developer/how-to/run-tests.rst b/docs/developer/how-to/run-tests.rst new file mode 100644 index 0000000..d2e0364 --- /dev/null +++ b/docs/developer/how-to/run-tests.rst @@ -0,0 +1,12 @@ +Run the tests using pytest +========================== + +Testing is done with pytest_. It will find functions in the project that `look +like tests`_, and run them to check for errors. You can run it with:: + + $ tox -e pytest + +It will also report coverage to the commandline and to ``cov.xml``. + +.. _pytest: https://pytest.org/ +.. _look like tests: https://docs.pytest.org/explanation/goodpractices.html#test-discovery diff --git a/docs/developer/how-to/static-analysis.rst b/docs/developer/how-to/static-analysis.rst new file mode 100644 index 0000000..065920e --- /dev/null +++ b/docs/developer/how-to/static-analysis.rst @@ -0,0 +1,8 @@ +Run static analysis using mypy +============================== + +Static type analysis is done with mypy_. It checks type definition in source +files without running them, and highlights potential issues where types do not +match. You can run it with:: + + $ tox -e mypy diff --git a/docs/developer/how-to/test-container.rst b/docs/developer/how-to/test-container.rst new file mode 100644 index 0000000..a4a43a6 --- /dev/null +++ b/docs/developer/how-to/test-container.rst @@ -0,0 +1,25 @@ +Container Local Build and Test +============================== + +CI builds a runtime container for the project. The local tests +checks available via ``tox -p`` do not verify this because not +all developers will have docker installed locally. + +If CI is failing to build the container, then it is best to fix and +test the problem locally. This would require that you have docker +or podman installed on your local workstation. + +In the following examples the command ``docker`` is interchangeable with +``podman`` depending on which container cli you have installed. + +To build the container and call it ``test``:: + + cd + docker build -t test . + +To verify that the container runs:: + + docker run -it test --help + +You can pass any other command line parameters to your application +instead of --help. diff --git a/docs/developer/how-to/update-tools.rst b/docs/developer/how-to/update-tools.rst new file mode 100644 index 0000000..c1075ee --- /dev/null +++ b/docs/developer/how-to/update-tools.rst @@ -0,0 +1,16 @@ +Update the tools +================ + +This module is merged with the python3-pip-skeleton_. This is a generic +Python project structure which provides a means to keep tools and +techniques in sync between multiple Python projects. To update to the +latest version of the skeleton, run:: + + $ git pull --rebase=false https://github.com/DiamondLightSource/python3-pip-skeleton + +Any merge conflicts will indicate an area where something has changed that +conflicts with the setup of the current module. Check the `closed pull requests +`_ +of the skeleton module for more details. + +.. _python3-pip-skeleton: https://DiamondLightSource.github.io/python3-pip-skeleton diff --git a/docs/developer/index.rst b/docs/developer/index.rst new file mode 100644 index 0000000..8a6369b --- /dev/null +++ b/docs/developer/index.rst @@ -0,0 +1,64 @@ +Developer Guide +=============== + +Documentation is split into four categories, also accessible from links in the +side-bar. + +.. grid:: 2 + :gutter: 4 + + .. grid-item-card:: :material-regular:`directions_run;3em` + + .. toctree:: + :caption: Tutorials + :maxdepth: 1 + + tutorials/dev-install + + +++ + + Tutorials for getting up and running as a developer. + + .. grid-item-card:: :material-regular:`task;3em` + + .. toctree:: + :caption: How-to Guides + :maxdepth: 1 + + how-to/contribute + how-to/build-docs + how-to/run-tests + how-to/static-analysis + how-to/lint + how-to/update-tools + how-to/make-release + how-to/pin-requirements + how-to/test-container + + +++ + + Practical step-by-step guides for day-to-day dev tasks. + + .. grid-item-card:: :material-regular:`apartment;3em` + + .. toctree:: + :caption: Explanations + :maxdepth: 1 + + explanations/decisions + + +++ + + Explanations of how and why the architecture is why it is. + + .. grid-item-card:: :material-regular:`description;3em` + + .. toctree:: + :caption: Reference + :maxdepth: 1 + + reference/standards + + +++ + + Technical reference material on standards in use. diff --git a/docs/developer/reference/standards.rst b/docs/developer/reference/standards.rst new file mode 100644 index 0000000..b78a719 --- /dev/null +++ b/docs/developer/reference/standards.rst @@ -0,0 +1,64 @@ +Standards +========= + +This document defines the code and documentation standards used in this +repository. + +Code Standards +-------------- + +The code in this repository conforms to standards set by the following tools: + +- black_ for code formatting +- flake8_ for style checks +- isort_ for import ordering +- mypy_ for static type checking + +.. seealso:: + + How-to guides `../how-to/lint` and `../how-to/static-analysis` + +.. _documentation_standards: + +Documentation Standards +----------------------- + +Docstrings are pre-processed using the Sphinx Napoleon extension. As such, +google-style_ is considered as standard for this repository. Please use type +hints in the function signature for types. For example: + +.. code:: python + + def func(arg1: str, arg2: int) -> bool: + """Summary line. + + Extended description of function. + + Args: + arg1: Description of arg1 + arg2: Description of arg2 + + Returns: + Description of return value + """ + return True + +.. _google-style: https://sphinxcontrib-napoleon.readthedocs.io/en/latest/index.html#google-vs-numpy + +Documentation is contained in the ``docs`` directory and extracted from +docstrings of the API. + +Docs follow the underlining convention:: + + Headling 1 (page title) + ======================= + + Heading 2 + --------- + + Heading 3 + ~~~~~~~~~ + +.. seealso:: + + How-to guide `../how-to/build-docs` \ No newline at end of file diff --git a/docs/developer/tutorials/dev-install.rst b/docs/developer/tutorials/dev-install.rst new file mode 100644 index 0000000..4b008d8 --- /dev/null +++ b/docs/developer/tutorials/dev-install.rst @@ -0,0 +1,60 @@ +Developer install +================= + +These instructions will take you through the minimal steps required to get a dev +environment setup, so you can run the tests locally. + +Clone the repository +-------------------- + +First clone the repository locally using `Git +`_:: + + $ git clone git://github.com/DiamondLightSource/diffcalc_api.git + +Install dependencies +-------------------- + +You can choose to either develop on the host machine using a `venv` (which +requires python 3.8 or later) or to run in a container under `VSCode +`_ + +.. tab-set:: + + .. tab-item:: Local virtualenv + + .. code:: + + $ cd diffcalc_api + $ python3 -m venv venv + $ source venv/bin/activate + $ pip install -e '.[dev]' + + .. tab-item:: VSCode devcontainer + + .. code:: + + $ vscode diffcalc_api + # Click on 'Reopen in Container' when prompted + # Open a new terminal + +See what was installed +---------------------- + +To see a graph of the python package dependency tree type:: + + $ pipdeptree + +Build and test +-------------- + +Now you have a development environment you can run the tests in a terminal:: + + $ tox -p + +This will run in parallel the following checks: + +- `../how-to/build-docs` +- `../how-to/run-tests` +- `../how-to/static-analysis` +- `../how-to/lint` diff --git a/docs/explanations.rst b/docs/explanations.rst deleted file mode 100644 index 157b608..0000000 --- a/docs/explanations.rst +++ /dev/null @@ -1,11 +0,0 @@ -:orphan: - -Explanations -============ - -Explanation of how the library works and why it works that way. - -.. toctree:: - :caption: Explanations - - explanations/test diff --git a/docs/explanations/test.rst b/docs/explanations/test.rst deleted file mode 100644 index 3d20cdc..0000000 --- a/docs/explanations/test.rst +++ /dev/null @@ -1,9 +0,0 @@ -Why is something the way it is -============================== - -Often, reading the code will not explain *why* it is written that way. These -explanations should be grouped together in articles here. They might include -history of diffcalc_api, architectural decisions, or the -real world tests that influenced the design of diffcalc_api. - -Nothing yet to explain. \ No newline at end of file diff --git a/docs/genindex.rst b/docs/genindex.rst new file mode 100644 index 0000000..93eb8b2 --- /dev/null +++ b/docs/genindex.rst @@ -0,0 +1,5 @@ +API Index +========= + +.. + https://stackoverflow.com/a/42310803 diff --git a/docs/how-to.rst b/docs/how-to.rst deleted file mode 100644 index 9bc0dae..0000000 --- a/docs/how-to.rst +++ /dev/null @@ -1,11 +0,0 @@ -:orphan: - -How-to Guides -============= - -Practical step-by-step guides for the more experienced user. - -.. toctree:: - :caption: How-to Guides - - how-to/test diff --git a/docs/how-to/test.rst b/docs/how-to/test.rst deleted file mode 100644 index a03755e..0000000 --- a/docs/how-to/test.rst +++ /dev/null @@ -1,9 +0,0 @@ -How to accomplish a task -======================== - -Here you would explain how to use diffcalc_api to accomplish -a particular task. It doesn't have to be an exhaustive guide like the tutorials, -just enough information to show someone who knows what they want to do, how to -accomplish that task. - -No tasks yet to accomplish. \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 9bde8ad..443e8cc 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,48 +1,29 @@ +:html_theme.sidebar_secondary.remove: + .. include:: ../README.rst :end-before: when included in index.rst - How the documentation is structured ----------------------------------- -Documentation is split into four categories, also accessible from links in the -side-bar. - -.. rst-class:: columns - -`tutorials` -~~~~~~~~~~~ - -.. include:: tutorials.rst - :start-after: ========= - -.. rst-class:: columns - -`how-to` -~~~~~~~~ - -.. include:: how-to.rst - :start-after: ============= - -.. rst-class:: columns - -`explanations` -~~~~~~~~~~~~~~ +The documentation is split into 2 sections: -.. include:: explanations.rst - :start-after: ============ +.. grid:: 2 -.. rst-class:: columns + .. grid-item-card:: :material-regular:`person;4em` + :link: user/index + :link-type: doc -`reference` -~~~~~~~~~~~ + The User Guide contains documentation on how to install and use diffcalc_api. -.. include:: reference.rst - :start-after: ========= + .. grid-item-card:: :material-regular:`code;4em` + :link: developer/index + :link-type: doc -.. rst-class:: endcolumns + The Developer Guide contains documentation on how to develop and contribute changes back to diffcalc_api. -About the documentation -~~~~~~~~~~~~~~~~~~~~~~~ +.. toctree:: + :hidden: -`Why is the documentation structured this way? `_ + user/index + developer/index diff --git a/docs/reference.rst b/docs/reference.rst deleted file mode 100644 index 74ebb74..0000000 --- a/docs/reference.rst +++ /dev/null @@ -1,18 +0,0 @@ -:orphan: - -Reference -========= - -Practical step-by-step guides for the more experienced user. - -.. toctree:: - :caption: Reference - - reference/api - reference/contributing - Releases - Index - -.. - Index link above is a hack to make genindex.html a relative link - https://stackoverflow.com/a/31820846 diff --git a/docs/reference/api.rst b/docs/reference/api.rst deleted file mode 100644 index 5cd3ac2..0000000 --- a/docs/reference/api.rst +++ /dev/null @@ -1,26 +0,0 @@ -API -=== - -.. automodule:: diffcalc_api - - ``diffcalc_api`` - ----------------------------------- - -Internal API reference for diffcalc_api - -Mix verbose text with docstring and signature extraction by -using ``autoclass`` and ``autofunction`` directives instead of -``automodule`` below. - -.. data:: diffcalc_api.__version__ - :type: str - - Version number as calculated by https://github.com/dls-controls/versiongit - -.. .. automodule:: diffcalc_api.hello -.. :members: - -.. ``diffcalc_api.hello`` -.. ----------------------------------------- - -No other modules yet. \ No newline at end of file diff --git a/docs/reference/contributing.rst b/docs/reference/contributing.rst deleted file mode 100644 index ac7b6bc..0000000 --- a/docs/reference/contributing.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../../CONTRIBUTING.rst diff --git a/docs/tutorials.rst b/docs/tutorials.rst deleted file mode 100644 index dfdef50..0000000 --- a/docs/tutorials.rst +++ /dev/null @@ -1,11 +0,0 @@ -:orphan: - -Tutorials -========= - -Tutorials for installation, library and commandline usage. New users start here. - -.. toctree:: - :caption: Tutorials - - tutorials/installation diff --git a/docs/tutorials/installation.rst b/docs/tutorials/installation.rst deleted file mode 100644 index b906422..0000000 --- a/docs/tutorials/installation.rst +++ /dev/null @@ -1,50 +0,0 @@ -Installation -============ - -.. note:: - - For installation inside DLS, please see the internal documentation on - ``dls-python3`` and ``pip``. Although these instructions will work - inside DLS, they are intended for external use. - - If you want to contribute to the library itself, please follow - the `../reference/contributing` instructions. - - -Check your version of python ----------------------------- - -You will need python 3.7 or later. You can check your version of python by -typing into a terminal:: - - python3 --version - - -Create a virtual environment ----------------------------- - -It is recommended that you install into a “virtual environment” so this -installation will not interfere with any existing Python software:: - - python3 -m venv /path/to/venv - source /path/to/venv/bin/activate - - -Installing the library ----------------------- - -You can now use ``pip`` to install the library:: - - python3 -m pip install diffcalc_api - -If you require a feature that is not currently released you can also install -from github:: - - python3 -m pip install git+git://github.com/dls-controls/diffcalc_api.git - -The library should now be installed and the commandline interface on your path. -You can check diffcalc_api has been correctly installed by typing:: - - python -c "import diffcalc_api" - -If you observe any output, repeat the steps above and observe the outputs. \ No newline at end of file diff --git a/docs/user/explanations/docs-structure.rst b/docs/user/explanations/docs-structure.rst new file mode 100644 index 0000000..f25a09b --- /dev/null +++ b/docs/user/explanations/docs-structure.rst @@ -0,0 +1,18 @@ +About the documentation +----------------------- + + :material-regular:`format_quote;2em` + + The Grand Unified Theory of Documentation + + -- David Laing + +There is a secret that needs to be understood in order to write good software +documentation: there isn't one thing called *documentation*, there are four. + +They are: *tutorials*, *how-to guides*, *technical reference* and *explanation*. +They represent four different purposes or functions, and require four different +approaches to their creation. Understanding the implications of this will help +improve most documentation - often immensely. + +`More information on this topic. `_ diff --git a/docs/user/how-to/run-container.rst b/docs/user/how-to/run-container.rst new file mode 100644 index 0000000..173d481 --- /dev/null +++ b/docs/user/how-to/run-container.rst @@ -0,0 +1,15 @@ +Run in a container +================== + +Pre-built containers with diffcalc_api and its dependencies already +installed are available on `Github Container Registry +`_. + +Starting the container +---------------------- + +To pull the container from github container registry and run:: + + $ docker run ghcr.io/DiamondLightSource/diffcalc_api:main --version + +To get a released version, use a numbered release instead of ``main``. diff --git a/docs/user/index.rst b/docs/user/index.rst new file mode 100644 index 0000000..2c94a0c --- /dev/null +++ b/docs/user/index.rst @@ -0,0 +1,57 @@ +User Guide +========== + +Documentation is split into four categories, also accessible from links in the +side-bar. + +.. grid:: 2 + :gutter: 4 + + .. grid-item-card:: :material-regular:`directions_walk;3em` + + .. toctree:: + :caption: Tutorials + :maxdepth: 1 + + tutorials/installation + + +++ + + Tutorials for installation and typical usage. New users start here. + + .. grid-item-card:: :material-regular:`directions;3em` + + .. toctree:: + :caption: How-to Guides + :maxdepth: 1 + + how-to/run-container + + +++ + + Practical step-by-step guides for the more experienced user. + + .. grid-item-card:: :material-regular:`info;3em` + + .. toctree:: + :caption: Explanations + :maxdepth: 1 + + explanations/docs-structure + + +++ + + Explanations of how the library works and why it works that way. + + .. grid-item-card:: :material-regular:`menu_book;3em` + + .. toctree:: + :caption: Reference + :maxdepth: 1 + + reference/api + ../genindex + + +++ + + Technical reference material including APIs and release notes. diff --git a/docs/user/reference/api.rst b/docs/user/reference/api.rst new file mode 100644 index 0000000..32adb68 --- /dev/null +++ b/docs/user/reference/api.rst @@ -0,0 +1,14 @@ +API +=== + +.. automodule:: diffcalc_api + + ``diffcalc_api`` + ----------------------------------- + +This is the internal API reference for diffcalc_api + +.. data:: diffcalc_api.__version__ + :type: str + + Version number as calculated by https://github.com/pypa/setuptools_scm diff --git a/docs/user/tutorials/installation.rst b/docs/user/tutorials/installation.rst new file mode 100644 index 0000000..e562d03 --- /dev/null +++ b/docs/user/tutorials/installation.rst @@ -0,0 +1,38 @@ +Installation +============ + +Check your version of python +---------------------------- + +You will need python 3.8 or later. You can check your version of python by +typing into a terminal:: + + $ python3 --version + + +Create a virtual environment +---------------------------- + +It is recommended that you install into a “virtual environment” so this +installation will not interfere with any existing Python software:: + + $ python3 -m venv /path/to/venv + $ source /path/to/venv/bin/activate + + +Installing the library +---------------------- + +You can now use ``pip`` to install the library and its dependencies:: + + $ python3 -m pip install diffcalc_api + +If you require a feature that is not currently released you can also install +from github:: + + $ python3 -m pip install git+https://github.com/DiamondLightSource/diffcalc_api.git + +The library should now be installed and the commandline interface on your path. +You can check the version that has been installed by typing:: + + $ diffcalc_api --version diff --git a/pyproject.toml b/pyproject.toml index d99ac1f..8724a78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,123 @@ [build-system] -# To get a reproducible wheel, wheel must be pinned to the same version as in -# dls-python3, and setuptools must produce the same dist-info. Cap setuptools -# to the last version that didn't add License-File to METADATA -requires = ["setuptools<57", "wheel==0.33.1"] -build-backend = "setuptools.build_meta" \ No newline at end of file +requires = ["setuptools>=64", "setuptools_scm[toml]>=6.2", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "diffcalc-api" +description = "API for diffcalc-core python 3 package" +classifiers = [ + "Development Status :: 3 - Alpha", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] +dependencies = [ + "diffcalc-core @ git+https://github.com/DiamondLightSource/diffcalc-core.git@getMiscut", + "fastapi", + "uvicorn", + "pymongo", + "motor", + "requests", +] # Add project dependencies here, e.g. ["click", "numpy"] +dynamic = ["version"] +license.file = "LICENSE" +readme = "README.rst" +requires-python = ">=3.8" + +[project.optional-dependencies] +dev = [ + "black", + "mypy", + "flake8-isort", + "Flake8-pyproject", + "pipdeptree", + "pre-commit", + "pydata-sphinx-theme>=0.12", + "pytest-cov", + "sphinx-autobuild", + "sphinx-copybutton", + "sphinx-design", + "tox-direct", + "types-mock", + "typed-ast", + "pep8-naming", + "mock", + "pydocstyle", + "mongomock", + "httpx", +] + +[project.scripts] +diffcalc_api = "diffcalc_api.__main__:main" + +[project.urls] +GitHub = "https://github.com/DiamondLightSource/diffcalc-api" + +[[project.authors]] # Further authors may be added by duplicating this section +email = "rose.yemelyanova@diamond.ac.uk" +name = "Rose Yemelyanova" + + +[tool.setuptools_scm] +write_to = "src/diffcalc_api/_version.py" + +[tool.mypy] +ignore_missing_imports = true # Ignore missing stubs in imported modules + +[tool.isort] +float_to_top = true +profile = "black" + +[tool.flake8] +extend-ignore = [ + "E203", # See https://github.com/PyCQA/pycodestyle/issues/373 + "F811", # support typing.overload decorator + "F722", # allow Annotated[typ, some_func("some string")] +] +max-line-length = 88 # Respect black's line length (default 88), +exclude = [".tox", "venv"] + + +[tool.pytest.ini_options] +# Run pytest with all our checkers, and don't spam us with massive tracebacks on error +addopts = """ + --tb=native -vv --doctest-modules --doctest-glob="*.rst" + --cov=diffcalc_api --cov-report term --cov-report xml:cov.xml + """ +# https://iscinumpy.gitlab.io/post/bound-version-constraints/#watch-for-warnings +filterwarnings = "error" +# Doctest python code in docs, python code in src docstrings, test functions in tests +testpaths = "docs src tests" + +[tool.coverage.run] +data_file = "/tmp/diffcalc_api.coverage" + +[tool.coverage.paths] +# Tests are run from installed location, map back to the src directory +source = ["src", "**/site-packages/"] + +# tox must currently be configured via an embedded ini string +# See: https://github.com/tox-dev/tox/issues/999 +[tool.tox] +legacy_tox_ini = """ +[tox] +skipsdist=True + +[testenv:{pre-commit,mypy,pytest,docs}] +# Don't create a virtualenv for the command, requires tox-direct plugin +direct = True +passenv = * +allowlist_externals = + pytest + pre-commit + mypy + sphinx-build + sphinx-autobuild +commands = + pytest: pytest {posargs} + mypy: mypy src tests {posargs} + pre-commit: pre-commit run --all-files {posargs} + docs: sphinx-{posargs:build -EW --keep-going} -T docs build/html +""" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index cc2a8a1..0000000 --- a/requirements.txt +++ /dev/null @@ -1,63 +0,0 @@ -alabaster==0.7.12 -anyio==3.6.2 -attrs==22.2.0 -babel==2.11.0 -black==22.12.0 -certifi==2022.12.7 -cfgv==3.3.1 -click==8.1.3 -coverage==7.0.4 -git+https://github.com/DiamondLightSource/diffcalc-core@addExtraFunctions#egg=diffcalc-core -distlib==0.3.6 -docutils==0.19 -fastapi==0.89.0 -filelock==3.9.0 -flake8==6.0.0 -flake8-isort==6.0.0 -h11==0.14.0 -identify==2.5.12 -idna==3.4 -imagesize==1.4.1 -importlib-metadata==6.0.0 -iniconfig==2.0.0 -isort==5.11.4 -jinja2==3.1.2 -markupsafe==2.1.1 -mccabe==0.7.0 -mock==5.0.1 -motor==3.1.1 -mongomock==4.1.2 -mypy==0.991 -mypy-extensions==0.4.3 -nodeenv==1.7.0 -numpy==1.24.1 -packaging==23.0 -pathspec==0.10.3 -pep8-naming==0.13.3 -platformdirs==2.6.2 -pluggy==1.0.0 -pre-commit==2.21.0 -py==1.11.0 -pycodestyle==2.10.0 -pydantic==1.10.4 -pygments==2.14.0 -pyparsing==3.0.9 -pytest==7.2.0 -pytest-cov==4.0.0 -pytz==2022.7 -pyyaml==6.0 -pymongo==4.3.3 -requests==2.28.1 -scipy==1.10.0 -setuptools==65.6.3 -sniffio==1.3.0 -snowballstemmer==2.2.0 -toml==0.10.2 -tomli==2.0.1 -typed-ast==1.5.4 -typing-extensions==4.4.0 -urllib3==1.26.13 -uvicorn==0.20.0 -virtualenv==20.17.1 -virtualenv-clone==0.5.7 -zipp==3.11.0 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 3fb9d2d..0000000 --- a/setup.cfg +++ /dev/null @@ -1,96 +0,0 @@ -[metadata] -name = diffcalc-api -description = API for diffcalc-core python 3 package developed by Irakli Sikharulidze -url = https://github.com/DiamondLightSource/diffcalc-api -author = Rose Yemelyanova -author_email = rose.yemelyanova@diamond.ac.uk -license = Apache License 2.0 -long_description = file: README.rst -long_description_content_type = text/x-rst -classifiers = - Development Status :: 4 - Beta - License :: OSI Approved :: Apache Software License - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - -[options] -python_requires = >=3.7 -packages = find: -# =src is interpreted as {"": "src"} -# as per recommendation here https://hynek.me/articles/testing-packaging/ -package_dir = - =src - -# Specify any package dependencies below. -install_requires = - diffcalc-core - fastapi - uvicorn - pymongo - motor - requests - -[options.extras_require] -# For development tests/docs -dev = - black - isort - pytest-cov - mypy - flake8 - flake8-isort - pre-commit - typed-ast - pep8-naming - mock - pydocstyle - mongomock - httpx - -[options.packages.find] -where = src - -# Specify any package data to be included in the wheel below. -# [options.package_data] -# diffcalc-api = -# subpackage/*.yaml - -[mypy] -# Ignore missing stubs for modules we use -ignore_missing_imports = True - -[isort] -profile=black -float_to_top=true - -[flake8] -# Make flake8 respect black's line length (default 88), -max-line-length = 88 -extend-ignore = - # See https://github.com/PyCQA/pycodestyle/issues/373 - E203, - # support typing.overload decorator - F811, - -[tool:pytest] -# Run pytest with all our checkers, and don't spam us with massive tracebacks on error -addopts = - --tb=native -vv --doctest-modules --doctest-glob="*.rst" - --cov=diffcalc_api --cov-report term --cov-report xml:cov.xml -# https://iscinumpy.gitlab.io/post/bound-version-constraints/#watch-for-warnings -# filterwarnings = error #Uncomment later. -# Doctest python code in docs, python code in src docstrings, test functions in tests -testpaths = - src tests - -[coverage:run] -# This is covered in the versiongit test suite so exclude it here -omit = */_version_git.py -data_file = /tmp/diffcalc_api.coverage - -[coverage:paths] -# Tests are run from installed location, map back to the src directory -source = - src - **/site-packages/ diff --git a/setup.py b/setup.py deleted file mode 100644 index 39de827..0000000 --- a/setup.py +++ /dev/null @@ -1,13 +0,0 @@ -# type: ignore -import glob -import importlib.util - -from setuptools import setup - -# Import ._version_git.py without importing -path = glob.glob(__file__.replace("setup.py", "src/*/_version_git.py"))[0] -spec = importlib.util.spec_from_file_location("_version_git", path) -vg = importlib.util.module_from_spec(spec) -spec.loader.exec_module(vg) - -setup(cmdclass=vg.get_cmdclass(), version=vg.__version__) diff --git a/src/diffcalc_api/__init__.py b/src/diffcalc_api/__init__.py index c8ba73b..328360a 100644 --- a/src/diffcalc_api/__init__.py +++ b/src/diffcalc_api/__init__.py @@ -1,6 +1,6 @@ """API to expose diffcalc-core methods.""" from . import config, database, openapi, server -from ._version_git import __version__ +from ._version import __version__ __all__ = ["__version__", "server", "config", "database", "openapi"] diff --git a/src/diffcalc_api/__main__.py b/src/diffcalc_api/__main__.py new file mode 100644 index 0000000..d4effdf --- /dev/null +++ b/src/diffcalc_api/__main__.py @@ -0,0 +1,20 @@ +"""Entrypoint to this library.""" +from argparse import ArgumentParser + +from uvicorn import run + +from . import __version__ + +__all__ = ["main"] + + +def main(args=None): + """Start the fastAPI server, or query the version of the API.""" + parser = ArgumentParser() + parser.add_argument("--version", action="version", version=__version__) + args = parser.parse_args(args) + run("diffcalc_api.server:app") + + +if __name__ == "__main__": + main() diff --git a/src/diffcalc_api/_version.py b/src/diffcalc_api/_version.py new file mode 100644 index 0000000..031e059 --- /dev/null +++ b/src/diffcalc_api/_version.py @@ -0,0 +1,4 @@ +# file generated by setuptools_scm +# don't change, don't track in version control +__version__ = version = '0.4.2.dev15+gdcbe255.d20230222' +__version_tuple__ = version_tuple = (0, 4, 2, 'dev15', 'gdcbe255.d20230222') diff --git a/src/diffcalc_api/_version_git.py b/src/diffcalc_api/_version_git.py deleted file mode 100644 index bb7f0c2..0000000 --- a/src/diffcalc_api/_version_git.py +++ /dev/null @@ -1,100 +0,0 @@ -# Compute a version number from a git repo or archive - -# This file is released into the public domain. Generated by: -# versiongit-2.1 (https://github.com/dls-controls/versiongit) -import re -import sys -from pathlib import Path -from subprocess import STDOUT, CalledProcessError, check_output - -# These will be filled in if git archive is run or by setup.py cmdclasses -GIT_REFS = "$Format:%D$" -GIT_SHA1 = "$Format:%h$" - -# Git describe gives us sha1, last version-like tag, and commits since then -CMD = "git describe --tags --dirty --always --long --match=[0-9]*[-.][0-9]*" - - -def get_version_from_git(path=None): - """Try to parse version from git describe, fallback to git archive tags.""" - tag, plus, suffix = "0.0", "untagged", "" - if not GIT_SHA1.startswith("$"): - # git archive or the cmdclasses below have filled in these strings - sha1 = GIT_SHA1 - for ref_name in GIT_REFS.split(", "): - if ref_name.startswith("tag: "): - # git from 1.8.3 onwards labels archive tags "tag: TAGNAME" - tag, plus = ref_name[5:], "0" - else: - if path is None: - # If no path to git repo, choose the directory this file is in - path = Path(__file__).absolute().parent - # output is TAG-NUM-gHEX[-dirty] or HEX[-dirty] - try: - cmd_out = check_output(CMD.split(), stderr=STDOUT, cwd=path) - except Exception as e: - sys.stderr.write("%s: %s\n" % (type(e).__name__, str(e))) - if isinstance(e, CalledProcessError): - sys.stderr.write("-> %s" % e.output.decode()) - return "0.0+unknown", None, e - else: - out = cmd_out.decode().strip() - if out.endswith("-dirty"): - out = out[:-6] - suffix = ".dirty" - if "-" in out: - # There is a tag, extract it and the other pieces - match = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", out) - tag, plus, sha1 = match.groups() - else: - # No tag, just sha1 - sha1 = out - # Replace dashes in tag for dots - tag = tag.replace("-", ".") - if plus != "0" or suffix: - # Not on a tag, add additional info - tag = f"{tag}+{plus}.g{sha1}{suffix}" - return tag, sha1, None - - -__version__, git_sha1, git_error = get_version_from_git() - - -def get_cmdclass(build_py=None, sdist=None): - """Create cmdclass dict to pass to setuptools.setup. - - Create cmdclass dict to pass to setuptools.setup which will write a - _version_static.py file in our resultant sdist, wheel or egg. - """ - if build_py is None: - from setuptools.command.build_py import build_py - if sdist is None: - from setuptools.command.sdist import sdist - - def make_version_static(base_dir: str, pkg: str): - vg = Path(base_dir) / pkg.split(".")[0] / "_version_git.py" - if vg.is_file(): - lines = open(vg).readlines() - with open(vg, "w") as f: - for line in lines: - # Replace GIT_* with static versions - if line.startswith("GIT_SHA1 = "): - f.write("GIT_SHA1 = '%s'\n" % git_sha1) - elif line.startswith("GIT_REFS = "): - f.write("GIT_REFS = 'tag: %s'\n" % __version__) - else: - f.write(line) - - class BuildPy(build_py): - def run(self): - build_py.run(self) - for pkg in self.packages: - make_version_static(self.build_lib, pkg) - - class Sdist(sdist): - def make_release_tree(self, base_dir, files): - sdist.make_release_tree(self, base_dir, files) - for pkg in self.distribution.packages: - make_version_static(base_dir, pkg) - - return dict(build_py=BuildPy, sdist=Sdist) diff --git a/src/diffcalc_api/config.py b/src/diffcalc_api/config.py index 3a7f2d8..8c82e88 100644 --- a/src/diffcalc_api/config.py +++ b/src/diffcalc_api/config.py @@ -4,7 +4,7 @@ from pydantic import BaseSettings -from ._version_git import __version__ +from ._version import __version__ release = __version__ diff --git a/src/diffcalc_api/services/ub.py b/src/diffcalc_api/services/ub.py index 400c521..d4f6398 100644 --- a/src/diffcalc_api/services/ub.py +++ b/src/diffcalc_api/services/ub.py @@ -883,7 +883,7 @@ async def calculate_vector_from_hkl_and_offset( if ubcalc.UB is None: raise NoUbMatrixError() - offset_hkl = ubcalc.calc_vector_wrt_hkl_and_offset( + offset_hkl = ubcalc.get_hkl_from_polar_transform( (hkl_ref.h, hkl_ref.k, hkl_ref.l), polar_angle, azimuth_angle ) @@ -926,7 +926,7 @@ async def calculate_offset_from_vector_and_hkl( if hkl_offset == hkl_ref: offset = (0.0, 0.0, 1.0) else: - offset = ubcalc.calc_offset_wrt_vector_and_hkl( + offset = ubcalc.get_polar_transform_from_hkl( (hkl_offset.h, hkl_offset.k, hkl_offset.l), (hkl_ref.h, hkl_ref.k, hkl_ref.l), ) diff --git a/tests/test_ubcalc.py b/tests/test_ubcalc.py index 34e5791..f9d88b5 100644 --- a/tests/test_ubcalc.py +++ b/tests/test_ubcalc.py @@ -1,5 +1,4 @@ import ast -import math from ast import literal_eval from math import radians from typing import Dict @@ -459,21 +458,22 @@ def test_set_miscut_with_existing_u(): ubcalc.calc_ub() ubcalc.n_phi = [0.0, 1.0, 0.0] - angle_in_rad = math.radians(11) + angle = 11 rotation_axis = {"x": 0, "y": 1, "z": 0} client.put( "/ub/test/miscut?collection=B07", - params={"angle": angle_in_rad, "add_miscut": True}, + params={"angle": angle, "add_miscut": True}, json=rotation_axis, ) get_response = client.get("/ub/test/miscut?collection=B07") get_response_payload = ast.literal_eval(get_response.content.decode())["payload"] - assert get_response_payload["angle"] == 1.0540812808041131 + assert get_response_payload["angle"] == pytest.approx(60.39440865381988) +# TODO: PR to change get_miscut to return in degrees... current returns in rad. def test_set_and_get_miscut(): ubcalc = UBCalculation() hkl = HklCalculation(ubcalc, Constraints()) @@ -482,19 +482,19 @@ def test_set_and_get_miscut(): # angle = 0.85212 # rotation_axis = {"x": -0.25142, "y": -0.96788, "z": 0.0} - angle_in_rad = math.radians(11) + angle = 11 rotation_axis = {"x": 0, "y": 1, "z": 0} client.put( "/ub/test/miscut?collection=B07", - params={"angle": angle_in_rad, "add_miscut": False}, + params={"angle": angle, "add_miscut": False}, json=rotation_axis, ) get_response = client.get("/ub/test/miscut?collection=B07") get_response_payload = ast.literal_eval(get_response.content.decode())["payload"] - assert get_response_payload["angle"] == pytest.approx(angle_in_rad) + assert get_response_payload["angle"] == pytest.approx(angle) for key, value in get_response_payload["rotation_axis"].items(): assert np.round(value, 5) == np.round(rotation_axis[key], 5)