From 05577bff719c1ff81d880c1992c4f9b58ddea4c2 Mon Sep 17 00:00:00 2001 From: Brett Graham Date: Fri, 30 Sep 2022 15:47:18 -0400 Subject: [PATCH] Initial commit - based off asdf-transform-schemas Copied asdf-transform-schemas Modified files for asdf-unit-schemas name (to match uri) Added units from asdf-standard Tested and built locally --- .flake8 | 5 + .gitattributes | 5 + .github/workflows/changelog.yml | 19 ++ .github/workflows/ci.yml | 111 ++++++++++++ .github/workflows/downstream.yml | 120 +++++++++++++ .github/workflows/publish-to-pypi.yml | 16 ++ .gitignore | 140 +++++++++++++++ .pre-commit-config.yaml | 49 +++++ .readthedocs.yml | 20 +++ CHANGES.rst | 4 + CONTRIBUTING.rst | 18 ++ LICENSE | 27 +++ README.md | 10 ++ bandit.yaml | 11 ++ docs/Makefile | 135 ++++++++++++++ docs/_static/custom.css | 43 +++++ docs/_static/logo.ico | Bin 0 -> 5694 bytes docs/_static/logo.pdf | Bin 0 -> 10668 bytes docs/_static/logo.png | Bin 0 -> 16762 bytes docs/_templates/autosummary/base.rst | 2 + docs/_templates/autosummary/class.rst | 2 + docs/_templates/autosummary/module.rst | 2 + docs/changes.rst | 6 + docs/conf.py | 157 ++++++++++++++++ docs/contributing.rst | 9 + docs/index.rst | 47 +++++ docs/legacy.rst | 8 + docs/make.bat | 170 ++++++++++++++++++ docs/manifests.rst | 18 ++ docs/units.rst | 12 ++ pyproject.toml | 98 ++++++++++ .../asdf-format.org/manifests/unit-1.0.0.yaml | 23 +++ .../asdf-format.org/manifests/unit-1.1.0.yaml | 29 +++ .../asdf-format.org/manifests/unit-1.2.0.yaml | 29 +++ .../asdf-format.org/manifests/unit-1.3.0.yaml | 29 +++ .../asdf-format.org/manifests/unit-1.4.0.yaml | 29 +++ .../asdf-format.org/manifests/unit-1.5.0.yaml | 29 +++ .../stsci.edu/schemas/defunit-1.0.0.yaml | 33 ++++ .../stsci.edu/schemas/quantity-1.1.0.yaml | 58 ++++++ resources/stsci.edu/schemas/unit-1.0.0.yaml | 20 +++ src/asdf_unit_schemas/__init__.py | 3 + src/asdf_unit_schemas/integration.py | 33 ++++ tests/test_integration.py | 54 ++++++ tox.ini | 37 ++++ 44 files changed, 1670 insertions(+) create mode 100644 .flake8 create mode 100644 .gitattributes create mode 100644 .github/workflows/changelog.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/downstream.yml create mode 100644 .github/workflows/publish-to-pypi.yml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 .readthedocs.yml create mode 100644 CHANGES.rst create mode 100644 CONTRIBUTING.rst create mode 100644 LICENSE create mode 100644 README.md create mode 100644 bandit.yaml create mode 100644 docs/Makefile create mode 100644 docs/_static/custom.css create mode 100644 docs/_static/logo.ico create mode 100644 docs/_static/logo.pdf create mode 100644 docs/_static/logo.png create mode 100644 docs/_templates/autosummary/base.rst create mode 100644 docs/_templates/autosummary/class.rst create mode 100644 docs/_templates/autosummary/module.rst create mode 100644 docs/changes.rst create mode 100644 docs/conf.py create mode 100644 docs/contributing.rst create mode 100644 docs/index.rst create mode 100644 docs/legacy.rst create mode 100644 docs/make.bat create mode 100644 docs/manifests.rst create mode 100644 docs/units.rst create mode 100644 pyproject.toml create mode 100644 resources/asdf-format.org/manifests/unit-1.0.0.yaml create mode 100644 resources/asdf-format.org/manifests/unit-1.1.0.yaml create mode 100644 resources/asdf-format.org/manifests/unit-1.2.0.yaml create mode 100644 resources/asdf-format.org/manifests/unit-1.3.0.yaml create mode 100644 resources/asdf-format.org/manifests/unit-1.4.0.yaml create mode 100644 resources/asdf-format.org/manifests/unit-1.5.0.yaml create mode 100644 resources/stsci.edu/schemas/defunit-1.0.0.yaml create mode 100644 resources/stsci.edu/schemas/quantity-1.1.0.yaml create mode 100644 resources/stsci.edu/schemas/unit-1.0.0.yaml create mode 100644 src/asdf_unit_schemas/__init__.py create mode 100644 src/asdf_unit_schemas/integration.py create mode 100644 tests/test_integration.py create mode 100644 tox.ini diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..52bc9db --- /dev/null +++ b/.flake8 @@ -0,0 +1,5 @@ +# this file exists to support an editable PEP517 install and for `flake8` (https://github.com/PyCQA/flake8/issues/234) +[flake8] +max-line-length = 120 +extend-ignore = E203 +exclude = .git, __pycache__, build, dist, eggs, *.egg, .tox diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..117ebd3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +* text eol=lf + +# Don't mess with these files +*.asdf binary +*.png binary diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 0000000..3fd356e --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,19 @@ +name: Changelog + +on: + pull_request: + types: [labeled, unlabeled, opened, synchronize, reopened] + +jobs: + changelog: + name: Confirm changelog entry + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + submodules: true + - name: Grep for PR number in CHANGES.rst + run: grep -P '\[[^\]]*#${{github.event.number}}[,\]]' CHANGES.rst + if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-changelog-entry-needed') }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5964840 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,111 @@ +name: CI + +on: + workflow_dispatch: + push: + branches: + - master + tags: + - "*" + pull_request: + branches: + +jobs: + tox: + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - name: Python 3.10 Schema validation tests + python-version: '3.10' + os: ubuntu-latest + toxenv: py310 + + - name: Python 3.9 Schema validation tests + python-version: 3.9 + os: ubuntu-latest + toxenv: py39 + + - name: Python 3.8 Schema validation tests + python-version: 3.8 + os: ubuntu-latest + toxenv: py38 + + - name: Twine check + python-version: 3.9 + os: ubuntu-latest + toxenv: twine + + - name: Code style checks + python-version: 3.9 + os: ubuntu-latest + toxenv: codestyle + + - name: macOS + python-version: 3.9 + os: macos-latest + toxenv: py39 + + - name: Windows + python-version: 3.9 + os: windows-latest + toxenv: py39 + + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install tox + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Run tox + run: tox -e ${{ matrix.toxenv }} + + full-dev: + name: Run schemas and dev extensions + runs-on: ubuntu-latest + steps: + - name: Checkout asdf-transform-schemas + uses: actions/checkout@v2 + with: + fetch-depth: 0 + path: asdf-transform-schemas + - name: Checkout astropy dev + uses: actions/checkout@v2 + with: + fetch-depth: 0 + repository: astropy/astropy + ref: main + path: astropy + - name: Checkout asdf-astropy dev + uses: actions/checkout@v2 + with: + fetch-depth: 0 + repository: astropy/asdf-astropy + ref: main + path: asdf-astropy + - name: Set up Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install asdf-transform-schemas + run: cd asdf-transform-schemas && pip install . + - name: Install astropy + run: cd astropy && pip install -e .[all,test] + - name: Install asdf-astropy + run: cd asdf-astropy && pip install -e .[test] + - name: Pip Freeze + run: pip freeze + - name: Run asdf-transform-schemas development tests + run: cd asdf-transform-schemas && pytest + - name: Run asdf-astropy development tests + run: cd asdf-astropy && pytest + - name: Run astropy development tests + run: cd astropy && pytest astropy/io/misc/asdf diff --git a/.github/workflows/downstream.yml b/.github/workflows/downstream.yml new file mode 100644 index 0000000..66f0be4 --- /dev/null +++ b/.github/workflows/downstream.yml @@ -0,0 +1,120 @@ +name: Downstream + +on: + workflow_dispatch: + schedule: + # Run every Monday at 6am UTC + - cron: '0 6 * * 1' + pull_request: + # We also want this workflow triggered if the `Downstream CI` label is + # added or present when PR is updated + types: + - synchronize + - labeled + push: + branches: + - '*.*.x' + tags: + - '*' + +env: + CRDS_SERVER_URL: https://jwst-crds.stsci.edu + CRDS_PATH: ~/crds_cache + CRDS_CLIENT_RETRY_COUNT: 3 + CRDS_CLIENT_RETRY_DELAY_SECONDS: 20 + +jobs: + common: + name: ${{ matrix.package_name }}@${{ matrix.ref }} unit tests + runs-on: ubuntu-latest + if: (github.repository == 'asdf-format/asdf-transform-schemas' && (github.event_name == 'schedule' || github.event_name == 'push' || github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'Downstream CI'))) + strategy: + fail-fast: false + matrix: + include: + - package_name: astropy + repository: astropy/astropy + ref: main + install_command: pip install -e .[test] + test_command: pytest + - package_name: gwcs + repository: spacetelescope/gwcs + ref: master + install_command: pip install -e .[test] + test_command: pytest + - package_name: jwst + repository: spacetelescope/jwst + ref: master + install_command: pip install -e .[test] + test_command: pytest + - package_name: specutils + repository: astropy/specutils + ref: main + install_command: pip install -e .[test] + test_command: pytest + - package_name: weldx + repository: BAMWelDX/weldx + ref: master + install_command: pip install -e .[test] + test_command: pytest weldx/tests/asdf_tests weldx/schemas --asdf-tests + - package_name: sunpy + repository: sunpy/sunpy + ref: main + install_command: pip install -e .[tests,all] + test_command: pytest sunpy/io/ + - package_name: dkist + repository: DKISTDC/dkist + ref: main + install_command: pip install -e .[tests] + test_command: pytest + - package_name: asdf-astropy + repository: astropy/asdf-astropy + ref: main + install_command: pip install -e .[test] + test_command: pytest + - package_name: asdf-standard + repository: asdf-format/asdf-standard + ref: master + install_command: pip install -e .[test] + test_command: pytest + - package_name: asdf-transform-schemas + repository: asdf-format/asdf-transform-schemas + ref: master + install_command: pip install -e .[test] + test_command: pytest + - package_name: asdf-wcs-schemas + repository: asdf-format/asdf-wcs-schemas + ref: main + install_command: pip install -e .[test] + test_command: pytest + - package_name: asdf-coordinates-schemas + repository: asdf-format/asdf-coordinates-schemas + ref: main + install_command: pip install -e .[test] + test_command: pytest + steps: + - name: Checkout asdf-transform-schemas + uses: actions/checkout@v2 + with: + fetch-depth: 0 + submodules: true + path: asdf-transform-schemas + - name: Checkout ${{ matrix.package_name }} + uses: actions/checkout@v2 + with: + fetch-depth: 0 + repository: ${{ matrix.repository }} + ref: ${{ matrix.ref }} + path: target + - name: Set up Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install asdf-transform-schemas + run: cd asdf-transform-schemas && pip install . + - name: Install remaining ${{ matrix.package_name }} dependencies + run: cd target && ${{ matrix.install_command }} + - name: Pip Freeze + run: pip freeze + - name: Run ${{ matrix.package_name}} tests + run: cd target && ${{ matrix.test_command }} diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml new file mode 100644 index 0000000..63d53d8 --- /dev/null +++ b/.github/workflows/publish-to-pypi.yml @@ -0,0 +1,16 @@ +name: Publish to PyPI + +on: + release: + types: [released] + +jobs: + publish: + uses: spacetelescope/action-publish_to_pypi/.github/workflows/workflow.yml@master + with: + test: false + build_platform_wheels: false # Set to true if your package contains a C extension + secrets: + user: ${{ secrets.PYPI_USERNAME_ASDF_MAINTAINER }} + password: ${{ secrets.PYPI_PASSWORD_ASDF_MAINTAINER }} # WARNING: Do not hardcode secret values here! If you want to use a different user or password, you can override this secret by creating one with the same name in your Github repository settings. + test_password: ${{ secrets.PYPI_PASSWORD_ASDF_MAINTAINER_TEST }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4b92eaf --- /dev/null +++ b/.gitignore @@ -0,0 +1,140 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST +*/*/_version.py + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +docs/generated/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# 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. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ee585e0 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,49 @@ +repos: + +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: check-added-large-files + - id: check-case-conflict + - id: check-yaml + args: ["--unsafe"] + - id: debug-statements + - id: end-of-file-fixer + - id: trailing-whitespace + +- repo: https://github.com/asottile/pyupgrade + rev: v2.38.2 + hooks: + - id: pyupgrade + args: ["--py38-plus"] + +- repo: https://github.com/PyCQA/autoflake + rev: v1.6.1 + hooks: + - id: autoflake + +- repo: https://github.com/pycqa/isort + rev: 5.10.1 + hooks: + - id: isort + +- repo: https://github.com/psf/black + rev: 22.8.0 + hooks: + - id: black + +- repo: https://github.com/asottile/blacken-docs + rev: v1.12.1 + hooks: + - id: blacken-docs + +- repo: https://github.com/PyCQA/flake8 + rev: 5.0.4 + hooks: + - id: flake8 + +- repo: https://github.com/PyCQA/bandit + rev: 1.7.4 + hooks: + - id: bandit + args: ["-c", "bandit.yaml"] diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..bae8352 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,20 @@ +version: 2 + +build: + os: ubuntu-20.04 + apt_packages: + - graphviz + tools: + python: "3.9" + +sphinx: + configuration: docs/conf.py + +python: + install: + - method: pip + path: . + extra_requirements: + - docs + +formats: all diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 0000000..913a1c9 --- /dev/null +++ b/CHANGES.rst @@ -0,0 +1,4 @@ +0.1.0 (2022-09-30) +------------------ + +- Initial release diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..64cefe4 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,18 @@ +Reporting Issues +---------------- + +Feature requests and bug reports for the Python implementation can be posted at +`asdf-unit-schemas's github page `_. + +Contributing Code and Documentation +----------------------------------- + +We love contributions! If you're interested in contributing to this project, please open a Pull Request +or issue and we would be glad to work with you. + +.. note:: + ASDF-unit-schemas is mostly a collection of schemas for the ASDF format. This means that + in addition to contributing a schema, you will likely also need some code to actually handle + serializing and deserializing your data. Typically, this code will need to be contributed to + :ref:`asdf-astropy `, which is a separate project. Please link your + pull requests and issues between the two projects. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..213661b --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2021 Association of Universities for Research in Astronomy. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..71841fb --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# asdf-unit-schemas +![CI](https://github.com/asdf-format/asdf-unit-schemas/workflows/CI/badge.svg) +![Downstream](https://github.com/asdf-format/asdf-unit-schemas/workflows/Downstream/badge.svg) +[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) + +This package provides ASDF schemas for validating unit tags. Users should not +need to install this directly; instead, install an implementation package such +as asdf-astropy, which includes asdf-unit-schemas as a dependency. diff --git a/bandit.yaml b/bandit.yaml new file mode 100644 index 0000000..db6ebb7 --- /dev/null +++ b/bandit.yaml @@ -0,0 +1,11 @@ +exclude_dirs: + - .eggs + - .git + - .pytest_cache + - .tox + - reference_files + - tests + - build + - dist + - docs + - __pycache__ diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..b824989 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,135 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +#This is needed with git because git doesn't create a dir if it's empty +$(shell [ -d "_static" ] || mkdir -p _static) + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR) + -rm -rf api + -rm -rf generated + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Astropy.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Astropy.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Astropy" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Astropy" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + make -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 0000000..3b01e4b --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1,43 @@ +div.admonition { + transition: width 0.5s; + -webkit-transition: width 0.5s; + overflow: hidden; +} + +div.admonition:active { + width: 150% +} + +div.note:active { + width: 100% +} + +div.highlight-yaml { + transition: width 0.5s; + -webkit-transition: width 0.5s; + overflow: hidden; +} + +div.highlight-yaml:active { + width: 150% +} + +div.highlight-default { + transition: width 0.5s; + -webkit-transition: width 0.5s; + overflow: hidden; +} + +div.highlight-default:active { + width: 150% +} + +div.highlight-python { + transition: width 0.5s; + -webkit-transition: width 0.5s; + overflow: hidden; +} + +div.highlight-python:active { + width: 150% +} diff --git a/docs/_static/logo.ico b/docs/_static/logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..b31899f2c89a14b6bf00b23bcbb4e397bf9f891c GIT binary patch literal 5694 zcmeHLc{r6@7yoq|Zu5j&U6tmmdD19N8qg#nG)hPbNy$7VB$267nhT+lF_cu&q)0R% z^OVfTy7RF2Ie5BVPj$cVukZO<&RJ`(-&%XWd+oi}dOZhd$NuKe2jiU~{T;wQkY-;7 z!Jd6N{wZqLt{vL9Z;uWgI-q06j_A~>6FPV9j4oZepljEz=+>eWzDQGu$eD%Px712r`@sH>}E?b@}_(9nRUrY5wsw6Jd7I;>y6 z9ve1nz{ZUmv1!vLY~H*X+S=OKvSkZ&bab$F>sILM>OxOX5BmE0*tTsO3=9mgefxIo z*s%kKhKAU=b0>_9j9_eR3==GeDyA1o{^ zU}wwy?9agT1{y92^|r=;#P1CnxORzaP%d&Ny)309;&L;OgoM zH#avNJa`c9?(R5r=nyR>kB_WKluCmeX!TwEM(+_-`G_;@5FBp@*{5jSt%#I0Mmkd%~!+qZ8cIXM}3?%cuMyLWN# z-aVwGq~QMj`$$bq#e)YA@bKY7JbLs9j~_qAlP6D*mX?O6PoLu1vu8+8Pe(>Z2A)5E zj?BzVym;{fSy@@g&d$cmmoM?^)hoPy{TgrHyg^P*4svsIk(ZZ;w{PDfKR+J@1qCQ9 zEJRUJ5sHh8QBqQZ($Z3tm6f5qyc`u36{xJN#JhLzP*qih_wV1My1E)QH8rTMtwmj3 z9qQ}r(a_L<#>Pen1OhZQHGzom_kL{n=ANUg;siA{YPJ~v!{k(gd z9y#%}z_d-v7lM~)PS6*+w&@lWH6pf6!MKA6~2M4&jXpZvCXKwj1DI(|WpyUd&v*1d)!kDES zHyRpmUjOZIkFzNXQ;)ZwTIe-VX6G2O9z!SfA2@PwZ>SB0_!bq<(0IOxh+bdy(U3A! znW`86}dk3aW$T+6YsT**^3M>F#de;L7}hr)dVJ; z`r65eD>}qA2(ec7TH#cqbOePKQ^DwcrSwee6f;#=TXKgf8n?N)DiF?5iKWcVQ~utQ zYL1<6q==m?SSmKiVyCJe`aa=1B1qe9H+Nk@31Y6il>8d~ zx&ASwOMeqfh6{!8=(vT4hziuBh|WzLUba4kp?W!#+)a%Y;W6_3aQmWlBKBLTY@yD10 z$IC2BqKS-IcHQE+sLKOjH7(?=ENvbuHw(!oDz>!cVD)IW-HN3}5JyP zTlYK5k&h^NMkK42Qzi^H@Zq-j>FmB_GwXXT$Yz=`OAC9}dF5|2%eYjA{GCb)-Pw*F zM8-3CDSzRn|G>6tr$JA~RO+xaGG&kh>+<4EJ_`1H$;3w_OieQu+N*wvL?^`(Hd2t( zCOW4;rH(9%nYT+5y+amj4R?9fv_)S0jI~c^)H+)oBePt^y&eVTYuW4MJy|nDXeMPF z2DUY?84C=zAqup#s4A*ujm73lVOGK!CCM~y1@es%?+!|5ur<@dbLRxnGL!e2J6)r+0}kwT_9d(>a7s%wS+ i&7h9`o59Mg;Xj-GZ=Uqk_kZ^UT7L-mI{3dlfxiH6e;n=r literal 0 HcmV?d00001 diff --git a/docs/_static/logo.pdf b/docs/_static/logo.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4aedaf22c22b7daff23acba0189cb63fcecbac9d GIT binary patch literal 10668 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 Xdh*q4Ce(ifK$DkN{#5?a`1}6>>dkqL literal 0 HcmV?d00001 diff --git a/docs/_templates/autosummary/base.rst b/docs/_templates/autosummary/base.rst new file mode 100644 index 0000000..1621719 --- /dev/null +++ b/docs/_templates/autosummary/base.rst @@ -0,0 +1,2 @@ +{% extends "autosummary_core/base.rst" %} +{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} diff --git a/docs/_templates/autosummary/class.rst b/docs/_templates/autosummary/class.rst new file mode 100644 index 0000000..0fa59f2 --- /dev/null +++ b/docs/_templates/autosummary/class.rst @@ -0,0 +1,2 @@ +{% extends "autosummary_core/class.rst" %} +{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} diff --git a/docs/_templates/autosummary/module.rst b/docs/_templates/autosummary/module.rst new file mode 100644 index 0000000..230cd6e --- /dev/null +++ b/docs/_templates/autosummary/module.rst @@ -0,0 +1,2 @@ +{% extends "autosummary_core/module.rst" %} +{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} diff --git a/docs/changes.rst b/docs/changes.rst new file mode 100644 index 0000000..773e5f7 --- /dev/null +++ b/docs/changes.rst @@ -0,0 +1,6 @@ +.. _change_log: + +Change Log +========== + +.. include:: ../CHANGES.rst diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..4a67fbb --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,157 @@ +# Astropy documentation build configuration file. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this file. +# +# All configuration values have a default. Some values are defined in +# the global Astropy configuration which is loaded here before anything else. +# See astropy.sphinx.conf for which values are set there. + +# 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. +# sys.path.insert(0, os.path.abspath('..')) +# IMPORTANT: the above commented section was generated by sphinx-quickstart, but +# is *NOT* appropriate for astropy or Astropy affiliated packages. It is left +# commented out with this explanation to make it clear why this should not be +# done. If the sys.path entry above is added, when the astropy.sphinx.conf +# import occurs, it will import the *source* version of astropy instead of the +# version installed (if invoked as "make html" or directly with sphinx), or the +# version in the build directory (if "python setup.py build_sphinx" is used). +# Thus, any C-extensions that are needed to build the documentation will *not* +# be accessible, and the documentation will not build correctly. + +import datetime +import os +import sys +from pathlib import Path + +# Ensure documentation examples are determinstically random. +import numpy +import tomli +from pkg_resources import get_distribution + +try: + numpy.random.seed(int(os.environ["SOURCE_DATE_EPOCH"])) +except KeyError: + pass + +try: + from sphinx_astropy.conf.v1 import * # noqa +except ImportError: + print("ERROR: the documentation requires the sphinx-astropy package to be installed") + sys.exit(1) + +# Get configuration information from `pyproject.toml` +with open(Path(__file__).parent.parent / "pyproject.toml", "rb") as configuration_file: + conf = tomli.load(configuration_file) +configuration = conf["project"] + +# -- General configuration ---------------------------------------------------- + +project = configuration["name"] +author = f"{configuration['authors'][0]['name']} <{configuration['authors'][0]['email']}>" +copyright = f"{datetime.datetime.now().year}, {configuration['authors'][0]}" + +release = get_distribution(configuration["name"]).version +version = ".".join(release.split(".")[:2]) + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.2' + +intersphinx_mapping["pypa-packaging"] = ("https://packaging.python.org/en/latest/", None) # noqa +intersphinx_mapping["asdf"] = ("https://asdf.readthedocs.io/en/latest/", None) # noqa +intersphinx_mapping["asdf-standard"] = ("https://asdf-standard.readthedocs.io/en/latest/", None) # noqa +intersphinx_mapping["asdf-astropy"] = ("https://asdf-astropy.readthedocs.io/en/latest/", None) # noqa +intersphinx_mapping["pytest"] = ("https://docs.pytest.org/en/latest/", None) # noqa + +# To perform a Sphinx version check that needs to be more specific than +# major.minor, call `check_sphinx_version("x.y.z")` here. +# check_sphinx_version("1.2.1") + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns.append("_templates") # noqa + +# This is added to the end of RST files - a good place to put substitutions to +# be used globally. +rst_epilog += """""" # noqa + +# -- Project information ------------------------------------------------------ + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. + +# -- Options for HTML output --------------------------------------------------- + +# A NOTE ON HTML THEMES +# The global astropy configuration uses a custom theme, 'bootstrap-astropy', +# which is installed along with astropy. A different theme can be used or +# the options for this theme can be modified by overriding some of the +# variables set in the global configuration. The variables set in the +# global configuration are listed below, commented out. + +# Add any paths that contain custom themes here, relative to this directory. +# To use a different custom theme, add the directory containing the theme. +# html_theme_path = [] + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. To override the custom theme, set this to the +# name of a builtin theme or the name of a custom theme in html_theme_path. +html_theme = "sphinx_rtd_theme" +html_theme_options = {} + +html_static_path = ["_static"] + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = "_static/logo.ico" +html_logo = "_static/logo.png" + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '' + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = f"{project} v{release}" + +# Output file base name for HTML help builder. +htmlhelp_basename = project + "doc" + + +# -- Options for LaTeX output -------------------------------------------------- + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [("index", project + ".tex", project + " Documentation", author, "manual")] + +latex_logo = "_static/logo.pdf" + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [("index", project.lower(), project + " Documentation", [author], 1)] + +sys.path.insert(0, os.path.join(os.path.abspath(os.path.dirname("__file__")), "sphinxext")) +extensions += ["sphinx_asdf"] # noqa + + +def setup(app): + app.add_css_file("custom.css") + + +# -- sphinx_asdf configuration --------------------------------------------- + +# Top-level directory containing ASDF schemas (relative to current directory) +asdf_schema_path = "../resources/stsci.edu" +# This is the prefix common to all schema IDs in this repository +asdf_schema_standard_prefix = "schemas" diff --git a/docs/contributing.rst b/docs/contributing.rst new file mode 100644 index 0000000..e39d33e --- /dev/null +++ b/docs/contributing.rst @@ -0,0 +1,9 @@ +.. _contributing: + +Contributing +============ + +We welcome feedback and contributions of all kinds. Contributions of code, +documentation, or general feedback are all appreciated. + +.. include:: ../CONTRIBUTING.rst diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..6114cea --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,47 @@ +.. _asdf-unit-schemas: + +********************** +ASDF Unit Schemas +********************** + +The ASDF Unit Schemas define a set of schemas for serializing the units defined by +:ref:`astropy.units ` for the ASDF file format. These schemas +are based upon the schemas in the :ref:`ASDF Standard ` and +are packaged for use by the :ref:`ASDF ` library. + +.. note:: + This is only a schema package, to use these schemas to serialize astropy units, + one must install the :ref:`asdf-astropy ` package. + +Included Resources +================== + +The following are listings of all the schemas provided by this package for ASDF. + +.. note:: + Typically, schemas are used in ASDF via their tag, which can be found in the manifest. + Using a transform in ASDF it is recommended that you use the tag instead of a direct + reference to the schema. When doing so make sure you are using the correct manifest version. + +.. toctree:: + :maxdepth: 1 + + units.rst + legacy.rst + manifests.rst + +Developer Resources +=================== + +.. toctree:: + :maxdepth: 1 + + contributing.rst + changes.rst + +Index +===== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/legacy.rst b/docs/legacy.rst new file mode 100644 index 0000000..ac71d76 --- /dev/null +++ b/docs/legacy.rst @@ -0,0 +1,8 @@ +.. _legacy-unit-schemas: + +Legacy Unit Schemas +======================== + +The legacy ``unit`` schemas: + +.. asdf-autoschemas:: diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..93dfe92 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,170 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Astropy.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Astropy.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/docs/manifests.rst b/docs/manifests.rst new file mode 100644 index 0000000..9c157d1 --- /dev/null +++ b/docs/manifests.rst @@ -0,0 +1,18 @@ +.. _manifests: + +Manifests +========= + +The ASDF tags (described by schemas) available under each ASDF unit schemas version are all described +by a single manifest document for that version. + +.. asdf-autoschemas:: + :schema_root: ../resources/asdf-format.org + :standard_prefix: manifests + + unit-1.0.0 + unit-1.1.0 + unit-1.2.0 + unit-1.3.0 + unit-1.4.0 + unit-1.5.0 diff --git a/docs/units.rst b/docs/units.rst new file mode 100644 index 0000000..40ef6c2 --- /dev/null +++ b/docs/units.rst @@ -0,0 +1,12 @@ +.. _unit-schemas: + +Unit Schemas +================= + +The current ``unit`` schemas: + +.. asdf-autoschemas:: + + defunit-1.0.0 + quantity-1.1.0 + unit-1.0.0 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..c317cd6 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,98 @@ +[project] +name = 'asdf_unit_schemas' +description = 'ASDF schemas for units' +readme = 'README.md' +requires-python = '>=3.8' +license = { file = 'LICENSE' } +authors = [{ name = 'The ASDF Developers', email = 'help@stsci.edu' }] +classifiers = [ + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Development Status :: 5 - Production/Stable', +] +dependencies = [ + 'asdf-standard >= 1.0.1', + 'importlib_resources >= 3; python_version<"3.9"', +] +dynamic = ['version'] + +[project.optional-dependencies] +docs = [ + 'tomli', + 'sphinx', + 'sphinx-asdf >= 0.1.3', + 'sphinx-astropy', + 'astropy >= 5.0.4', + 'graphviz', + 'matplotlib', + 'docutils', + 'sphinx-rtd-theme', +] +test = [ + 'asdf >= 2.8.0', + 'asdf-astropy', + 'scipy', + 'pytest', +] + +[project.urls] +'tracker' = 'https://github.com/asdf-format/asdf-unit-schemas/issues' +'documentation' = 'https://asdf-unit-schemas.readthedocs.io/en/stable' +'repository' = 'https://github.com/asdf-format/asdf-unit-schemas' + +[project.entry-points] +'asdf.resource_mappings' = { asdf_unit_schemas = 'asdf_unit_schemas.integration:get_resource_mappings' } + +[build-system] +requires = [ + "setuptools >=42", + "setuptools_scm[toml] >=3.4", + "wheel", +] +build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] +write_to = "src/asdf_unit_schemas/_version.py" + +[tool.setuptools] +packages = ["asdf_unit_schemas", "asdf_unit_schemas.resources"] + +[tool.setuptools.package-data] +"asdf_unit_schemas.resources" = ["resources/**/*.yaml"] + +[tool.setuptools.package-dir] +'' = "src" +"asdf_unit_schemas.resources" = "resources" + +[tool.pytest.ini_options] +asdf_schema_root = 'resources/stsci.edu/schemas' +asdf_schema_tests_enabled = 'true' +asdf_schema_ignore_unrecognized_tag = 'true' +testpaths = """ + tests + resources +""" +addopts = '--color=yes' + +[tool.black] +line-length = 120 +force-exclude = ''' +^/( + ( + \.eggs + | \.git + | \.pytest_cache + | \.tox + | build + | dist + )/ +) +''' + +[tool.isort] +profile = "black" +filter_files = true +line_length = 120 diff --git a/resources/asdf-format.org/manifests/unit-1.0.0.yaml b/resources/asdf-format.org/manifests/unit-1.0.0.yaml new file mode 100644 index 0000000..7892f48 --- /dev/null +++ b/resources/asdf-format.org/manifests/unit-1.0.0.yaml @@ -0,0 +1,23 @@ +id: asdf://asdf-format.org/unit/manifests/unit-1.0.0 +extension_uri: asdf://asdf-format.org/unit/extensions/unit-1.0.0 +title: Transform extension 1.0.0 +description: |- + A set of tags for serializing units. +tags: +- tag_uri: tag:stsci.edu:asdf/unit/defunit-1.0.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/defunit-1.0.0 + title: Define a new physical unit. + description: |- + Defines a new unit. It can be used to either: + + - Define a new base unit. + + - Create a new unit name that is a equivalent to a given unit. + + The new unit must be defined before any unit tags that use it. +- tag_uri: tag:stsci.edu:asdf/unit/unit-1.0.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/unit-1.0.0 + title: Physical unit. + description: |- + This represents a physical unit, in [VOUnit syntax, Version 1.0](http://www.ivoa.net/documents/VOUnits/index.html). + Where units are not explicitly tagged, they are assumed to be in VOUnit syntax. diff --git a/resources/asdf-format.org/manifests/unit-1.1.0.yaml b/resources/asdf-format.org/manifests/unit-1.1.0.yaml new file mode 100644 index 0000000..de9dbb7 --- /dev/null +++ b/resources/asdf-format.org/manifests/unit-1.1.0.yaml @@ -0,0 +1,29 @@ +id: asdf://asdf-format.org/unit/manifests/unit-1.1.0 +extension_uri: asdf://asdf-format.org/unit/extensions/unit-1.1.0 +title: Transform extension 1.1.0 +description: |- + A set of tags for serializing units. +tags: +- tag_uri: tag:stsci.edu:asdf/unit/defunit-1.0.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/defunit-1.0.0 + title: Define a new physical unit. + description: |- + Defines a new unit. It can be used to either: + + - Define a new base unit. + + - Create a new unit name that is a equivalent to a given unit. + + The new unit must be defined before any unit tags that use it. +- tag_uri: tag:stsci.edu:asdf/unit/quantity-1.1.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/quantity-1.1.0 + title: Represents a Quantity object from astropy + description: |- + A Quantity object represents a value that has some unit + associated with the number. +- tag_uri: tag:stsci.edu:asdf/unit/unit-1.0.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/unit-1.0.0 + title: Physical unit. + description: |- + This represents a physical unit, in [VOUnit syntax, Version 1.0](http://www.ivoa.net/documents/VOUnits/index.html). + Where units are not explicitly tagged, they are assumed to be in VOUnit syntax. diff --git a/resources/asdf-format.org/manifests/unit-1.2.0.yaml b/resources/asdf-format.org/manifests/unit-1.2.0.yaml new file mode 100644 index 0000000..b56cdb5 --- /dev/null +++ b/resources/asdf-format.org/manifests/unit-1.2.0.yaml @@ -0,0 +1,29 @@ +id: asdf://asdf-format.org/unit/manifests/unit-1.2.0 +extension_uri: asdf://asdf-format.org/unit/extensions/unit-1.2.0 +title: Transform extension 1.2.0 +description: |- + A set of tags for serializing units. +tags: +- tag_uri: tag:stsci.edu:asdf/unit/defunit-1.0.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/defunit-1.0.0 + title: Define a new physical unit. + description: |- + Defines a new unit. It can be used to either: + + - Define a new base unit. + + - Create a new unit name that is a equivalent to a given unit. + + The new unit must be defined before any unit tags that use it. +- tag_uri: tag:stsci.edu:asdf/unit/quantity-1.1.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/quantity-1.1.0 + title: Represents a Quantity object from astropy + description: |- + A Quantity object represents a value that has some unit + associated with the number. +- tag_uri: tag:stsci.edu:asdf/unit/unit-1.0.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/unit-1.0.0 + title: Physical unit. + description: |- + This represents a physical unit, in [VOUnit syntax, Version 1.0](http://www.ivoa.net/documents/VOUnits/index.html). + Where units are not explicitly tagged, they are assumed to be in VOUnit syntax. diff --git a/resources/asdf-format.org/manifests/unit-1.3.0.yaml b/resources/asdf-format.org/manifests/unit-1.3.0.yaml new file mode 100644 index 0000000..50eb50d --- /dev/null +++ b/resources/asdf-format.org/manifests/unit-1.3.0.yaml @@ -0,0 +1,29 @@ +id: asdf://asdf-format.org/unit/manifests/unit-1.3.0 +extension_uri: asdf://asdf-format.org/unit/extensions/unit-1.3.0 +title: Transform extension 1.3.0 +description: |- + A set of tags for serializing units. +tags: +- tag_uri: tag:stsci.edu:asdf/unit/defunit-1.0.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/defunit-1.0.0 + title: Define a new physical unit. + description: |- + Defines a new unit. It can be used to either: + + - Define a new base unit. + + - Create a new unit name that is a equivalent to a given unit. + + The new unit must be defined before any unit tags that use it. +- tag_uri: tag:stsci.edu:asdf/unit/quantity-1.1.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/quantity-1.1.0 + title: Represents a Quantity object from astropy + description: |- + A Quantity object represents a value that has some unit + associated with the number. +- tag_uri: tag:stsci.edu:asdf/unit/unit-1.0.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/unit-1.0.0 + title: Physical unit. + description: |- + This represents a physical unit, in [VOUnit syntax, Version 1.0](http://www.ivoa.net/documents/VOUnits/index.html). + Where units are not explicitly tagged, they are assumed to be in VOUnit syntax. diff --git a/resources/asdf-format.org/manifests/unit-1.4.0.yaml b/resources/asdf-format.org/manifests/unit-1.4.0.yaml new file mode 100644 index 0000000..43189c3 --- /dev/null +++ b/resources/asdf-format.org/manifests/unit-1.4.0.yaml @@ -0,0 +1,29 @@ +id: asdf://asdf-format.org/unit/manifests/unit-1.4.0 +extension_uri: asdf://asdf-format.org/unit/extensions/unit-1.4.0 +title: Transform extension 1.4.0 +description: |- + A set of tags for serializing units. +tags: +- tag_uri: tag:stsci.edu:asdf/unit/defunit-1.0.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/defunit-1.0.0 + title: Define a new physical unit. + description: |- + Defines a new unit. It can be used to either: + + - Define a new base unit. + + - Create a new unit name that is a equivalent to a given unit. + + The new unit must be defined before any unit tags that use it. +- tag_uri: tag:stsci.edu:asdf/unit/quantity-1.1.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/quantity-1.1.0 + title: Represents a Quantity object from astropy + description: |- + A Quantity object represents a value that has some unit + associated with the number. +- tag_uri: tag:stsci.edu:asdf/unit/unit-1.0.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/unit-1.0.0 + title: Physical unit. + description: |- + This represents a physical unit, in [VOUnit syntax, Version 1.0](http://www.ivoa.net/documents/VOUnits/index.html). + Where units are not explicitly tagged, they are assumed to be in VOUnit syntax. diff --git a/resources/asdf-format.org/manifests/unit-1.5.0.yaml b/resources/asdf-format.org/manifests/unit-1.5.0.yaml new file mode 100644 index 0000000..b22a96d --- /dev/null +++ b/resources/asdf-format.org/manifests/unit-1.5.0.yaml @@ -0,0 +1,29 @@ +id: asdf://asdf-format.org/unit/manifests/unit-1.5.0 +extension_uri: asdf://asdf-format.org/unit/extensions/unit-1.5.0 +title: Transform extension 1.5.0 +description: |- + A set of tags for serializing units. +tags: +- tag_uri: tag:stsci.edu:asdf/unit/defunit-1.0.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/defunit-1.0.0 + title: Define a new physical unit. + description: |- + Defines a new unit. It can be used to either: + + - Define a new base unit. + + - Create a new unit name that is a equivalent to a given unit. + + The new unit must be defined before any unit tags that use it. +- tag_uri: tag:stsci.edu:asdf/unit/quantity-1.1.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/quantity-1.1.0 + title: Represents a Quantity object from astropy + description: |- + A Quantity object represents a value that has some unit + associated with the number. +- tag_uri: tag:stsci.edu:asdf/unit/unit-1.0.0 + schema_uri: http://stsci.edu/schemas/asdf/unit/unit-1.0.0 + title: Physical unit. + description: |- + This represents a physical unit, in [VOUnit syntax, Version 1.0](http://www.ivoa.net/documents/VOUnits/index.html). + Where units are not explicitly tagged, they are assumed to be in VOUnit syntax. diff --git a/resources/stsci.edu/schemas/defunit-1.0.0.yaml b/resources/stsci.edu/schemas/defunit-1.0.0.yaml new file mode 100644 index 0000000..c85dc22 --- /dev/null +++ b/resources/stsci.edu/schemas/defunit-1.0.0.yaml @@ -0,0 +1,33 @@ +%YAML 1.1 +--- +$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" +id: "http://stsci.edu/schemas/asdf/unit/defunit-1.0.0" +title: Define a new physical unit. +description: | + Defines a new unit. It can be used to either: + + - Define a new base unit. + + - Create a new unit name that is a equivalent to a given unit. + + The new unit must be defined before any unit tags that use it. + +type: object +properties: + name: + description: The name of the new unit. + type: string + pattern: "[A-Za-z_][A-Za-z0-9_]+" + + unit: + description: | + The unit that the new name is equivalent to. It is optional, + and if not provided, or ``null``, this ``defunit`` defines a new + base unit. + + anyOf: + - $ref: "unit-1.0.0" + - type: "null" + +required: [name] +... diff --git a/resources/stsci.edu/schemas/quantity-1.1.0.yaml b/resources/stsci.edu/schemas/quantity-1.1.0.yaml new file mode 100644 index 0000000..3ae93aa --- /dev/null +++ b/resources/stsci.edu/schemas/quantity-1.1.0.yaml @@ -0,0 +1,58 @@ +%YAML 1.1 +--- +$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" +id: "http://stsci.edu/schemas/asdf/unit/quantity-1.1.0" + +title: > + Represents a Quantity object from astropy +description: | + A Quantity object represents a value that has some unit + associated with the number. + +examples: + - + - A quantity consisting of a scalar value and unit + - | + !unit/quantity-1.1.0 + value: 3.14159 + unit: km + + - + - A quantity consisting of a single value in an array + - | + !unit/quantity-1.1.0 + value: !core/ndarray-1.0.0 [2.71828] + unit: A + + - + - A quantity with an array of values + - | + !unit/quantity-1.1.0 + value: !core/ndarray-1.0.0 [1, 2, 3, 4] + unit: s + + - + - A quantity with an n-dimensional array of values + - | + !unit/quantity-1.1.0 + value: !core/ndarray-1.0.0 + datatype: float64 + data: [[1, 2, 3], + [4, 5, 6]] + unit: pc + + +type: object +properties: + value: + description: | + A vector of one or more values + anyOf: + - type: number + - $ref: "../core/ndarray-1.0.0" + unit: + description: | + The unit corresponding to the values + $ref: unit-1.0.0 +required: [value, unit] +... diff --git a/resources/stsci.edu/schemas/unit-1.0.0.yaml b/resources/stsci.edu/schemas/unit-1.0.0.yaml new file mode 100644 index 0000000..58e17b9 --- /dev/null +++ b/resources/stsci.edu/schemas/unit-1.0.0.yaml @@ -0,0 +1,20 @@ +%YAML 1.1 +--- +$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" +id: "http://stsci.edu/schemas/asdf/unit/unit-1.0.0" +title: Physical unit. +description: > + This represents a physical unit, in [VOUnit syntax, Version + 1.0](http://www.ivoa.net/documents/VOUnits/index.html). + + Where units are not explicitly tagged, they are assumed to be + in VOUnit syntax. +examples: + - + - Example unit + - | + !unit/unit-1.0.0 "2.1798721 10-18kg m2 s-2" + +type: string +pattern: "[\x00-\x7f]*" +... diff --git a/src/asdf_unit_schemas/__init__.py b/src/asdf_unit_schemas/__init__.py new file mode 100644 index 0000000..544125b --- /dev/null +++ b/src/asdf_unit_schemas/__init__.py @@ -0,0 +1,3 @@ +from ._version import version as __version__ + +__all__ = ["__version__"] diff --git a/src/asdf_unit_schemas/integration.py b/src/asdf_unit_schemas/integration.py new file mode 100644 index 0000000..3a7e332 --- /dev/null +++ b/src/asdf_unit_schemas/integration.py @@ -0,0 +1,33 @@ +import sys +from pathlib import Path + +if sys.version_info < (3, 9): + import importlib_resources +else: + import importlib.resources as importlib_resources + +from asdf_standard import DirectoryResourceMapping + +import asdf_unit_schemas + + +def get_resource_mappings(): + resources_root = importlib_resources.files(asdf_unit_schemas) / "resources" + if not resources_root.is_dir(): + # In an editable install, the resources directory will exist off the + # repository root: + resources_root = Path(__file__).parent.parent.parent / "resources" + if not resources_root.is_dir(): + raise RuntimeError("Missing resources directory") + + return [ + DirectoryResourceMapping( + resources_root / "stsci.edu" / "schemas", + "http://stsci.edu/schemas/asdf/unit/", + recursive=True, + ), + DirectoryResourceMapping( + resources_root / "asdf-format.org" / "manifests", + "asdf://asdf-format.org/unit/manifests/", + ), + ] diff --git a/tests/test_integration.py b/tests/test_integration.py new file mode 100644 index 0000000..053dd7f --- /dev/null +++ b/tests/test_integration.py @@ -0,0 +1,54 @@ +from pathlib import Path + +import asdf +import pytest +import yaml + + +def get_resources(): + resources_root = Path(__file__).parent.parent / "resources" + + return {str(path.relative_to(resources_root)): path for path in resources_root.glob("**/*.yaml")} + + +RESOURCES = get_resources() + + +@pytest.mark.parametrize("resource", RESOURCES) +def test_resource(resource): + resource_path = RESOURCES[resource] + resource_manager = asdf.get_config().resource_manager + + with resource_path.open("rb") as f: + resource_content = f.read() + resource = yaml.safe_load(resource_content) + resource_uri = resource["id"] + assert resource_manager[resource_uri] == resource_content + + +def get_manifests(): + manifests_root = Path(__file__).parent.parent / "resources" / "asdf-format.org" / "manifests" + + return {str(path.relative_to(manifests_root)): path for path in manifests_root.glob("*.yaml")} + + +MANIFESTS = get_manifests() + + +@pytest.mark.parametrize("manifest", MANIFESTS) +def test_manifest(manifest): + manifest_path = MANIFESTS[manifest] + resource_manager = asdf.get_config().resource_manager + + with manifest_path.open("rb") as f: + manifest_content = f.read() + manifest = yaml.safe_load(manifest_content) + + manifest_schema = asdf.schema.load_schema("asdf://asdf-format.org/core/schemas/extension_manifest-1.0.0") + + # The manifest must be valid against its own schema: + asdf.schema.validate(manifest, schema=manifest_schema) + + for tag_definition in manifest["tags"]: + # The tag's schema must be available: + assert tag_definition["schema_uri"] in resource_manager diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..8f132eb --- /dev/null +++ b/tox.ini @@ -0,0 +1,37 @@ +[tox] +envlist = py39, black, flake8 +isolated_build = True + +[testenv] +extras = + test +commands = + pip freeze + pytest + +[testenv:black] +deps = + black +commands= + black --check src tests + +[testenv:flake8] +deps = + flake8 +commands = + flake8 --count + +[testenv:twine] +deps= + twine +commands= + twine check {distdir}/* + +[testenv:codestyle] +skip_install = true +description = Run all style and file checks with pre-commit +deps = + pre-commit +commands = + pre-commit install-hooks + pre-commit run {posargs:--color always --all-files --show-diff-on-failure}