diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000000000000000000000000000000000000..42d62190033f822d7cf5958b7ffb9d77681fcf5d --- /dev/null +++ b/.flake8 @@ -0,0 +1,14 @@ +[flake8] +doctests = True +exclude = .git, .eggs, __pycache__, docs, dist, venv, .tox, env, envs +ignore = + # line too long, we use black + E501, + # line break before binary operator + W503, + # wrong flake defaults: see https://github.com/psf/black/issues/315, https://github.com/psf/black/issues/43 + E203, + W503, + W504 +per-file-ignores = + __init__.py:F401,E402 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..8f4a5d31d4806e0190b48ce24da82366aa3063d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,184 @@ +# requirements.txt is generated dynamically in this project (e.g. make requirements) +requirements.txt + +# junit reports (generated by tox) +report/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +envs/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# 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/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# pyc files: +*.pyc + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask instance folder +instance/ + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# vim files +*.sw* + +# VisualStudioCode +.vscode/* # Maybe .vscode/**/* instead - see comments +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +**/.history + +# folder +tmp/ +.idea +docs/source/ + +### Git ### +*.orig + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Latex temp files (in case you include a documentation in latex e.g.) +*.aux diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..2daefcf8570e3ee6714c45b3af044e67569fb161 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,109 @@ +image: python:latest + +# Change pip's cache directory to be inside the project directory since we can +# only cache local items. +variables: + PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" + +# Pip's cache doesn't store the python packages +# https://pip.pypa.io/en/stable/reference/pip_install/#caching +# +# If you want to also cache the installed packages, you have to install +# them in a virtualenv and cache it as well. +cache: + paths: + - .cache/pip + - venv/ + - .cache/apt + +stages: + - build + - test + - deploy + +before_script: + - pip install virtualenv + - virtualenv venv + - source venv/bin/activate + - poetry install + +dist: + stage: build + script: + - mkdir -p .cache/apt + - apt-get update -yqq + - apt-get install -y gfortran libopenblas-dev liblapack-dev + - apt-get -o dir::cache::archives=".cache/apt" install -y -qq gfortran liblapack-dev libgmp-dev + - python setup.py bdist_wheel + # an alternative approach is to install and run: + - pip install dist/* + # run the command here + artifacts: + paths: + - dist/*.whl + expire_in: 1h + only: + - tags + +pages: + stage: build + script: + - poetry install --only docs + - cd docs + - make html + - cd .. + - mkdir -p public + - rm -rf public/* + - mv docs/_build/html/* public/ # add it to pages. Pages is exposing public/index.html + only: + - master + cache: + paths: + - public + artifacts: + paths: + - public + - docs + +lint: + stage: test + before_script: + - pip install -q flake8 + script: + - flake8 + +test: + stage: test + script: + - pip --version + - pip install tox # you can also use tox + - tox + coverage: '/^TOTAL.+?(\d+\%)$/' + artifacts: + # paths: + # pa- report/unit + reports: + junit: + - report/junit.xml +pypi: + image: docker.km3net.de/base/python:3 + stage: deploy + cache: {} + before_script: + - echo "Starting upload to pypi" + script: + # Check if current_version is already uploaded + - VERSION=$((python -c "import configparser; config = configparser.ConfigParser(); config.read('setup.cfg'); print(config['bumpversion']['current_version'])") 2>&1) + - MODULE_NAME=$((python -c "import configparser; config = configparser.ConfigParser(); config.read('setup.cfg'); print(config['metadata']['name'])") 2>&1) + - PACKAGE_JSON_URL="https://pypi.org/pypi/$MODULE_NAME/json" + - apt-get install -qq -y jq + - PYPI_VERSIONS=$(curl -s "$PACKAGE_JSON_URL" | jq -r '.releases | keys | .[]' | sort -V) + - if [[ $PYPI_VERSIONS =~ $VERSION ]]; then echo "Version $VERSION is already uploaded!"; exit 1; fi + # Version not already uploaded so do it now. + - echo "Uploading version $VERSION" + - pip install -U twine + - python setup.py sdist + - twine upload dist/* + rules: + # for debuggin: git commit -am "deb" && git push && bumpversion patch && git tag -l --sort=-v:refname | head -n 1 | git push origin + - if: $CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/ \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b6d2326ebedc61e8324b46226aabeefae75dce69 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,29 @@ +default_language_version: + python: python3 +repos: +- repo: https://github.com/ambv/black + rev: 22.3.0 + hooks: + - id: black + language_version: python3.10 +- repo: https://gitlab.com/pycqa/flake8 + rev: 3.9.2 + hooks: + - id: flake8 +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.0.1 + hooks: + - id: check-json + - id: pretty-format-json + args: ['--indent', '4'] + - id: check-merge-conflict + - id: check-toml + - id: check-yaml + - id: debug-statements + - id: detect-private-key + - id: end-of-file-fixer +- repo: https://github.com/asottile/blacken-docs + rev: v1.12.1 + hooks: + - id: blacken-docs + additional_dependencies: [black==22.3.0] diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000000000000000000000000000000000000..1db793f37946026137fbe27e56e648887339190c --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,25 @@ +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + + +formats: all + +build: + image: latest + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +# Optionally set the version of Python and requirements required to build your docs +python: + version: 3.7 + install: + - method: pip + path: . + extra_requirements: + - docs diff --git a/AUTHORS.rst b/AUTHORS.rst new file mode 100644 index 0000000000000000000000000000000000000000..db7323ffb1f36e4403ea094afa0bd6d47c398fa3 --- /dev/null +++ b/AUTHORS.rst @@ -0,0 +1,17 @@ +======= +Credits +======= +This package was created with Cookiecutter_ and the `dboe/dough`_ project template. + +.. _Cookiecutter: https://github.com/audreyr/cookiecutter +.. _`dboe/dough`: https://gitlab.com/dboe/dough + +Development Lead +---------------- + +* Daniel Böckenhoff <dboe@ipp.mpg.de> + +Contributors +------------ + +None yet. Why not be the first? diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000000000000000000000000000000000000..408c188f04c5bbb182aa8bcd920c2b0d60a339e1 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,166 @@ +============ +Contributing +============ +.. + [contributing] + +.. highlight:: shell + +Contributions are welcome, and they are greatly appreciated! Every little bit +helps, and credit will always be given. + +You can contribute in many ways: + +Types of Contributions +---------------------- + +Report Bugs +~~~~~~~~~~~ + +Report bugs at <https://gitlab.mpcdf.mpg.de/dboe/rna/-/issues/>_. + +If you are reporting a bug, please include: + +* Your operating system name and version. +* Any details about your local setup that might be helpful in troubleshooting. +* Detailed steps to reproduce the bug. + +If you want quick feedback, it is helpful to mention specific developers +(@devloper_name) or @all. This will trigger a mail to the corresponding developer(s). + +Fix Bugs +~~~~~~~~ + +Look through the repository issues for bugs. Anything tagged with "bug" and "help +wanted" is open to whoever wants to implement it. + +Implement Features +~~~~~~~~~~~~~~~~~~ + +Look through the remote issues for features. Anything tagged with "enhancement" +and "help wanted" is open to whoever wants to implement it. + +Write Documentation +~~~~~~~~~~~~~~~~~~~ + +`rna` could always use more :ref name="Documentation":`documentation`, whether as part of the +official `rna` docs, in docstrings, or even on the web in blog posts, +articles, and such. + +Write pytest, unittests or doctests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`rna` profits a lot from better :ref name="Testing":`testing`. We encourage you to add unittests +with the `pytest` module (`unittest` module is OK, too) in the `tests` directory or doctests (as part of docstrings or in the documentation). + +Submit Feedback +~~~~~~~~~~~~~~~ + +The best way to send feedback is to file an `Issue <https://gitlab.mpcdf.mpg.de/dboe/rna/-/issues/>`_. + +If you are proposing a feature: + +* Explain in detail how it would work. +* Keep the scope as narrow as possible, to make it easier to implement. +* Remember that this is a volunteer-driven project, and that contributions + are welcome + +Get Started! +------------ + +Ready to contribute? Here's how to set up `rna` for local development. + +1. Fork the `rna` repo. +2. Clone your fork locally:: + + $ git clone git@gitlab.mpcdf.mpg.de:dboe/rna.git + +3. Set up your fork for local development:: + + $ cd rna/ + $ pip install .[dev] + +4. Step 3. already installed `pre-commit <https://pre-commit.com/>`_. Initialize it by running:: + + $ pre-commit install + +5. Create a branch for local development:: + + $ git checkout -b name-of-your-bugfix-or-feature + + Now you can make your changes locally. + +6. When you're done making changes, check that your changes pass flake8 and the + tests:: + + $ make test + +7. Commit your changes and push your branch to origin:: + + $ git add . + $ git commit -m "Your detailed description of your changes." + $ git push origin name-of-your-bugfix-or-feature + +8. Submit a pull request through the repository website. + +Pull Request Guidelines +----------------------- + +Before you submit a pull request, check that it meets these guidelines: + +1. The pull request should include tests. +2. If the pull request adds functionality, the docs should be updated. Put + your new functionality into a function with a docstring, and add the + feature to the list in README.rst. +3. The pull request should work for Python 3.5, 3.6, 3.7 and 3.8, and for PyPy. Check + <https://gitlab.mpcdf.mpg.de/dboe/rna/-/merge_requests/>_ + and make sure that the tests pass for all supported Python versions. + +Testing +------- + +To run tests, use:: + + $ make test + +To run a subset of tests, you have the following options:: + + $ pytest tests/test_package.py + + $ pytest tests/test_package.py::Test_rna::test_version_type + + $ pytest --doctest-modules docs/usage.rst + + $ pytest --doctest-modules rna/core.py -k "MyClass.funciton_with_doctest" + +Use the '--trace' option to directly jump into a pdb debugger on fails. Check out the coverage of your api with:: + + $ make coverage + +Documentation +------------- +To compile the documentation (including automatically generated module api docs), run:: + + $ make docs + +To run an auto-updating server, use + + $ make docs-serve + +Use doctests as much as possible in order to have tested examples in your documentation. + +Style guide +----------- +Please follow the `google style guide <https://google.github.io/styleguide/pyguide.html>`_ illustrated +by `this example <https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html>`_. + +Deploying +--------- + +A reminder for the maintainers on how to deploy. +Make sure all your changes are committed. +Then run:: + + $ make publish + +The CI will then deploy to PyPI if tests pass. \ No newline at end of file diff --git a/LICENSE.rst b/LICENSE.rst new file mode 100644 index 0000000000000000000000000000000000000000..2854fc17c17e5a4e5b16e10a40957009b2e9c0c8 --- /dev/null +++ b/LICENSE.rst @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023, Daniel Böckenhoff + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a3b3f26a1e81ddcf274e4bc0de81fe37590f526e --- /dev/null +++ b/Makefile @@ -0,0 +1,86 @@ +include tools/Makefile.envs + +test: FORCE + flake8 rna tests + pylint rna tests + py.test + +coverage: env + # coverage run $(MODULE) test + py.test --cov=$(MODULE) || true + # coverage report + coverage html + python -m webbrowser htmlcov/index.html + +clean: + coverage erase + rm -rf htmlcov + rm -rf docs/_build + rm -rf docs/source + rm -rf dist + rm -rf build + rm -rf report + rm -rf .tox + rm -rf .pytest_cache + rm -rf *.egg-info + # pre-commit clean + +publish: + @echo $(GITSTATUS) + @if [ -z $(GITSTATUS) ]; then \ + echo "Working directory clean."; \ + else \ + git status; \ + printf $(_WARN) "WAIT" "Your Git status is not clean!" ; \ + if $(MAKE) -s confirm ; then \ + printf $(_INFO) "OK" "Continuing" ; \ + else \ + printf $(_ERROR) "KO" "EXIT" ; \ + exit 1; \ + fi \ + fi + # call optional with argument: make part=minor publish + poetry version $(part) # possible: major / minor / patch + git commit -m "release-$(poetry version --short)" + git tag "release-$(poetry version --short)" + git push + git push --tags + +untag: + # remove last tag. mostly, because publishing failed + git tag -d v$(VERSION) + git push origin :refs/tags/v$(VERSION) + +requirements: pyproject.toml + poetry export --without-hashes --format=requirements.txt > requirements.txt + +docs-clean: + rm -rf docs/_build + +docs-serve: + ${CONDA_RUN}poetry install --only docs + sphinx-autobuild docs docs/_build/ -j auto --watch my_project + +docs-build: docs-clean + ${CONDA_RUN}poetry install --only docs + sphinx-build -M html docs docs/_build/ -E -a -j auto -W --keep-going + +docs: docs-build + # open the html slides + ${CONDA_RUN}python -m webbrowser docs/_build/html/index.html + +update: + # get up to date with the cookiecutter template 'dough' + # first check that no changes are existing + @echo $(GITSTATUS) + @if [ -z $(GITSTATUS) ]; then \ + echo "Working directory clean."; \ + else \ + git status; \ + echo "Your status is not clean! I can not update!"; \ + exit 1; \ + fi + # Starting upgrade + cookiecutter_project_upgrader + +FORCE: ; diff --git a/README.rst b/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..5821eb3b1fc4178795f1db6c1c618b5358d84336 --- /dev/null +++ b/README.rst @@ -0,0 +1,73 @@ +===================== +library documentation +===================== + +.. + [shields-start] + + +.. pypi +.. image:: https://img.shields.io/pypi/v/rna.svg + :target: https://pypi.python.org/pypi/rna +.. ci + .. image:: https://img.shields.io/travis/dboe/rna.svg + :target: https://travis-ci.com/dboe/rna +.. image:: https://gitlab.mpcdf.mpg.de/dboe/rna/badges/master/pipeline.svg + :target: https://gitlab.mpcdf.mpg.de/dboe/rna/commits/master + +.. coverage +.. image:: https://gitlab.mpcdf.mpg.de/dboe/rna/badges/master/coverage.svg + :target: https://gitlab.mpcdf.mpg.de/dboe/rna/commits/master + +.. readthedocs +.. image:: https://readthedocs.org/projects/rna/badge/?version=latest + :target: https://rna.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. pyup crosschecks your dependencies. Github is default, gitlab more complicated: https://pyup.readthedocs.io/en/latest/readme.html#run-your-first-update + .. image:: https://pyup.io/repos/github/dboe/rna/shield.svg + :target: https://pyup.io/repos/github/dboe/rna/ + :alt: Updates + +.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white + :target: https://github.com/pre-commit/pre-commit + :alt: pre-commit +.. + [shields-end] + +Overview +======== +.. + [overview-start] + +Basic and essential code building blocks of all pythons + +.. + [overview-end] + + +Licensed under the ``MIT License`` + +Resources +--------- +.. + [resources-start] + +* Source code: https://gitlab.mpcdf.mpg.de/dboe/rna +* Documentation: https://rna.readthedocs.io +* Pypi: https://pypi.python.org/pypi/rna +.. + [resources-end] + + +Features +-------- +.. + [features-start] + +The following features should be highlighted: + +* TODO + +.. + [features-end] diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..8607fce7795fab193404dac8f276a887a9c90a15 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,25 @@ +# Minimal makefile for Sphinx documentation +# +MODULE_PATH := $(shell cd ..; pwd) +MODULE := $(shell basename "$(MODULE_PATH)") + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +apidocs: $(MODULEPATH)/$(MODULE)/*.py apidoc-templates/* + sphinx-apidoc -o source/ ../$(MODULE) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/index.css b/docs/_static/index.css new file mode 100644 index 0000000000000000000000000000000000000000..40f044c1fe244332b88af989894dd41a0c5a96b8 --- /dev/null +++ b/docs/_static/index.css @@ -0,0 +1,73 @@ +@font-face { + font-family: 'inter'; + src: url('inter.ttf') format('truetype'); +} + +#landing-header { + width: 100%; + display: flex; + flex-wrap: wrap; + margin-left: auto; + margin-top: 1rem; + font-family: Inter; +} + +#landing-header > img { + width: 75%; + margin: auto; +} + +#landing-tagline { + width: 100%; + text-align: center; + font-size: xx-large; +} + + +#landing-overview { + width: 90%; + display: flex; + flex-wrap: wrap; + gap: 1rem; + margin-top: 4rem; + margin-left: auto; + margin-right: auto; + justify-content: space-around; +} + + + +@media (min-width: 992px) { + #landing-overview > section { + width: 45% !important; + } +} + +@media (min-width: 1400px) { + #landing-overview > section { + width: 30% !important; + } +} + +#landing-overview > section { + font-size: medium; + background-color: var(--pst-color-on-background); + padding: .75rem; + border-radius: 5px; + text-align: justify; +} + + + +#landing-overview > section h2 { + margin-top: 0; + font-weight: bold; + font-size: medium; + text-transform: uppercase; + color: #edb641; +} + + +#landing-overview > section > p { + width: 100%; +} \ No newline at end of file diff --git a/docs/_static/inter.ttf b/docs/_static/inter.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ec3164efa8fe938310f74b6156d3ad33ccb2fef6 Binary files /dev/null and b/docs/_static/inter.ttf differ diff --git a/docs/_static/style.css b/docs/_static/style.css new file mode 100644 index 0000000000000000000000000000000000000000..ca785ff3c5af458022c6a48edd3d319e3dfe51fb --- /dev/null +++ b/docs/_static/style.css @@ -0,0 +1,93 @@ +.article-container p { + text-align: justify; +} + +#version-warning { + top: 0; + position: sticky; + z-index: 60; + width: 100%; + height: 2.5rem; + display: flex; + column-gap: .5rem; + justify-content: center; + justify-items: center; + align-items: center; + background-color: #eee; + border-bottom: 2px solid #ae2828; +} + +@media (prefers-color-scheme: dark) { + body:not([data-theme="light"]) #version-warning { + background-color: black; + } +} + + +table.docutils { + clear: left; + float: left; + margin: 0 1rem 1rem; +} + + +/* theme customization */ + +html[data-theme="dark"] { + --color-background-primary: #1d2433; + --color-background-secondary: #0c101a; + --pst-color-on-background: #2e374a; + --pst-color-border: #2f2f2f; +} + +html[data-theme="light"] { + --color-background-primary: #f8f9fb; + --color-background-secondary: #fff; +} + +html[data-theme="light"], html[data-theme="dark"] { + --pst-color-background: var(--color-background-primary); + --pst-color-primary: #ffae57; +} + + +@media (min-width: 960px) { + .bd-page-width { + max-width: 100rem; + } +} + +.bd-sidebar-primary { + display: block; + flex: 0 0 18%; +} + + +.bd-content, .bd-sidebar-secondary { + background-color: var(--color-background-secondary); +} + + +.sd-tab-content { + box-shadow: 0 -0.0625rem var(--sd-color-tabs-overline); +} + +div.literal-block-wrapper, pre { + border: none; +} + +.code-block-caption { + background-color: var(--pst-color-surface); +} + +.navbar-persistent--mobile { + margin-left: unset; +} + +.navbar-persistent--mobile:last-of-type { + padding-left: 1rem; +} + +#navbar-icon-links a { + padding-top: .2rem; +} \ No newline at end of file diff --git a/docs/_static/versioning.js b/docs/_static/versioning.js new file mode 100644 index 0000000000000000000000000000000000000000..15f6c9b301978bd4e8583edc2d7696903bd0340c --- /dev/null +++ b/docs/_static/versioning.js @@ -0,0 +1,114 @@ +const loadVersions = async () => { + const res = await fetch(DOCUMENTATION_OPTIONS.URL_ROOT + "versions.json") + if (res.status !== 200) { + return null + } + return await res.json() +} + + +const getCurrentVersion = (versions) => { + const baseURL = new URL(DOCUMENTATION_OPTIONS.URL_ROOT, window.location).href + const parts = window.location.href.replace(baseURL, "").split("/") + if (!parts.length) { + return null + } + const maybeVersion = parts[0] + if (maybeVersion === "lib") { + return versions.latest + } + if (versions.versions.includes(maybeVersion)) { + return maybeVersion + } + return null +} + + +const addVersionWarning = (currentVersion, latestVersion) => { + if (currentVersion === latestVersion) { + return + } + + const navbarMain = document.getElementById("navbar-main") + if (!navbarMain) { + return + } + + const container = document.createElement("div") + container.id = "version-warning" + + const warningText = document.createElement("span") + warningText.textContent = `You are viewing the documentation for ${currentVersion === "dev" ? "a preview" : "an outdated"} version of this project.` + container.appendChild(warningText) + + const latestLink = document.createElement("a") + latestLink.textContent = "Click here to go to the latest version" + latestLink.href = DOCUMENTATION_OPTIONS.URL_ROOT + "lib" + container.appendChild(latestLink) + + navbarMain.before(container) +} + + +const formatVersionName = (version, isLatest) => version + (isLatest ? " (latest)" : "") + + +const addVersionSelect = (currentVersion, versionSpec) => { + const navEnd = document.getElementById("navbar-end") + if (!navEnd) { + return + } + + const container = document.createElement("div") + container.classList.add("navbar-nav") + + const dropdown = document.createElement("div") + dropdown.classList.add("dropdown") + container.appendChild(dropdown) + + const dropdownToggle = document.createElement("button") + dropdownToggle.classList.add("btn", "dropdown-toggle", "nav-item") + dropdownToggle.setAttribute("data-toggle", "dropdown") + dropdownToggle.setAttribute("type", "button") + dropdownToggle.textContent = `Version: ${formatVersionName(currentVersion, currentVersion === versionSpec.latest)}` + dropdown.appendChild(dropdownToggle) + + const dropdownContent = document.createElement("div") + dropdownContent.classList.add("dropdown-menu") + dropdown.appendChild(dropdownContent) + + for (const version of versionSpec.versions) { + const navItem = document.createElement("li") + navItem.classList.add("nav-item") + + const navLink = document.createElement("a") + navLink.classList.add("nav-link", "nav-internal") + navLink.href = DOCUMENTATION_OPTIONS.URL_ROOT + version + navLink.textContent = formatVersionName(version, version === versionSpec.latest) + navItem.appendChild(navLink) + + dropdownContent.appendChild(navItem) + } + + navEnd.prepend(container) +} + + +const setupVersioning = (versions) => { + if (versions === null) { + return + } + + const currentVersion = getCurrentVersion(versions) + if (currentVersion === null) { + return + } + + addVersionWarning(currentVersion, versions.latest) + addVersionSelect(currentVersion, versions) +} + + +window.addEventListener("DOMContentLoaded", () => { + loadVersions().then(setupVersioning) +}) diff --git a/docs/_templates/custom-class-template.rst b/docs/_templates/custom-class-template.rst new file mode 100644 index 0000000000000000000000000000000000000000..f73eda50ec593f7393241c5d6aa4c143a66c805b --- /dev/null +++ b/docs/_templates/custom-class-template.rst @@ -0,0 +1,34 @@ +{{ fullname | escape | underline}} + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} + :members: + :show-inheritance: + :inherited-members: + :special-members: __call__, __add__, __mul__ + + {% block methods %} + {% if methods %} + .. rubric:: {{ _('Methods') }} + + .. autosummary:: + :nosignatures: + {% for item in methods %} + {%- if not item.startswith('_') %} + ~{{ name }}.{{ item }} + {%- endif -%} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block attributes %} + {% if attributes %} + .. rubric:: {{ _('Attributes') }} + + .. autosummary:: + {% for item in attributes %} + ~{{ name }}.{{ item }} + {%- endfor %} + {% endif %} + {% endblock %} diff --git a/docs/_templates/custom-module-template.rst b/docs/_templates/custom-module-template.rst new file mode 100644 index 0000000000000000000000000000000000000000..d066d0e4dc2e34f399f55ea38d92ed4540787317 --- /dev/null +++ b/docs/_templates/custom-module-template.rst @@ -0,0 +1,66 @@ +{{ fullname | escape | underline}} + +.. automodule:: {{ fullname }} + + {% block attributes %} + {% if attributes %} + .. rubric:: Module attributes + + .. autosummary:: + :toctree: + {% for item in attributes %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block functions %} + {% if functions %} + .. rubric:: {{ _('Functions') }} + + .. autosummary:: + :toctree: + :nosignatures: + {% for item in functions %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block classes %} + {% if classes %} + .. rubric:: {{ _('Classes') }} + + .. autosummary:: + :toctree: + :template: custom-class-template.rst + :nosignatures: + {% for item in classes %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block exceptions %} + {% if exceptions %} + .. rubric:: {{ _('Exceptions') }} + + .. autosummary:: + :toctree: + {% for item in exceptions %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + +{% block modules %} +{% if modules %} +.. autosummary:: + :toctree: + :template: custom-module-template.rst + :recursive: +{% for item in modules %} + {{ item }} +{%- endfor %} +{% endif %} +{% endblock %} diff --git a/docs/_templates/navbar-nav.html b/docs/_templates/navbar-nav.html new file mode 100644 index 0000000000000000000000000000000000000000..72ed846db9401cf32d433408b92160e437407451 --- /dev/null +++ b/docs/_templates/navbar-nav.html @@ -0,0 +1,32 @@ +{% macro nav_link(label, target) -%} + <li class="nav-item"> + <a class="nav-link nav-internal" href="{{ target if target.startswith('http') else pathto(target) }}">{{ label }}</a> + </li> +{%- endmacro %} + + +<nav class="navbar-nav"> + <p class="sidebar-header-items__title" + role="heading" + aria-level="1" + aria-label="{{ _('Site Navigation') }}"> + {{ _("Site Navigation") }} + </p> + <ul id="navbar-main-elements" class="navbar-nav"> + {% for label, item in navbar_items | items %} + {% if item is string %} + {{ nav_link(label, item) }} + {% else %} + <div class="nav-item dropdown"> + <button class="btn dropdown-toggle nav-item" type="button" + data-toggle="dropdown">{{ label }}</button> + <ul class="dropdown-menu"> + {% for child_label, child_item in item | items %} + {{ nav_link(child_label, child_item) }} + {% endfor %} + </ul> + </div> + {% endif %} + {% endfor %} + </ul> +</nav> diff --git a/docs/about/index.rst b/docs/about/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..befeb16595a8379cde5ae67cac41e4f63c24ca18 --- /dev/null +++ b/docs/about/index.rst @@ -0,0 +1,5 @@ +===== +ABOUT +===== + +.. include:: ../../AUTHORS.rst diff --git a/docs/community/index.rst b/docs/community/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..ac7b6bcf35e98139c791cb5c4b94f08849972d65 --- /dev/null +++ b/docs/community/index.rst @@ -0,0 +1 @@ +.. include:: ../../CONTRIBUTING.rst diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000000000000000000000000000000000000..dd93a5aaf6262dcdaf829fa8b6c1b7c998ea5576 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,106 @@ +import os +import datetime +try: + import tomllib +except ModuleNotFoundError: + import tomli as tomllib + +__source_dir = "docs" +with open(os.path.join(os.path.dirname(__file__), "../pyproject.toml"), "rb") as f: + __config = tomllib.load(f) + +project = __config["tool"]["poetry"]["name"] +author = __config["tool"]["poetry"]["authors"][0] +copyright = str(datetime.date.today().year) + ", " + author +release = __config["tool"]["poetry"]["version"] + +extensions = [ + "sphinx.ext.intersphinx", + "sphinx.ext.autosectionlabel", + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx_design", + "sphinx_copybutton", + "sphinxcontrib.mermaid", + "sphinx.ext.viewcode", # Add links to highlighted source code + "sphinx.ext.doctest", # Test snippets in the documentation + "sphinx.ext.autosummary", + "sphinx_autodoc_typehints", # Automatically parse type hints +] +autosummary_generate = True # Turn on sphinx.ext.autosummary + +templates_path = ["_templates"] +exclude_patterns = ["_build"] + +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), +} + +napoleon_google_docstring = True +napoleon_include_special_with_doc = True +napoleon_use_admonition_for_examples = True +napoleon_use_admonition_for_notes = True +napoleon_use_admonition_for_references = False +napoleon_attr_annotations = True + +autosummary_generate = True # Turn on sphinx.ext.autosummary +html_show_sourcelink = False # Remove 'view source code' from top of page (for html, not python) +autodoc_inherit_docstrings = True # If no docstring, inherit from base class +set_type_checking_flag = True # Enable 'expensive' imports for sphinx_autodoc_typehints +nbsphinx_allow_errors = True # Continue through Jupyter errors +#autodoc_typehints = "description" # Sphinx-native method. Not as good as sphinx_autodoc_typehints +add_module_names = False # Remove namespaces from class/method signatures +autoclass_content = "both" # Both the class’ and the __init__ method’s docstring are concatenated +autodoc_class_signature = "separated" +autodoc_default_options = { + "special-members": "__init__", + "show-inheritance": True, +} +autodoc_member_order = "bysource" +autodoc_typehints_format = "short" + +always_document_param_types = True +typehints_defaults = "comma" + +auto_pytabs_min_version = (3, 7) +auto_pytabs_max_version = (3, 11) + +autosectionlabel_prefix_document = True + +suppress_warnings = ["autosectionlabel.*"] + +html_theme = "pydata_sphinx_theme" +html_static_path = ["_static"] +html_css_files = ["style.css"] +html_js_files = ["versioning.js"] +html_favicon = "images/favicon.ico" +html_logo = "images/logo.svg" +html_show_sourcelink = False +html_sidebars = {"about/*": []} +html_title = f"{project} Framework" + +html_theme_options = { + "use_edit_page_button": False, + "show_toc_level": 4, + "navbar_align": "left", + "icon_links": [ + { + "name": "Source", + "url": __config["tool"]["poetry"]["repository"], + "icon": f"fa-brands fa-{'github' if 'github' in __config['tool']['poetry']['repository'] else 'gitlab'}", + "type": "fontawesome", + }, + ], + "navbar_end": ["navbar-icon-links"], + "navbar_persistent": ["search-button", "theme-switcher"], +} + +html_context = { + "navbar_items": { + "Documentation": "lib/index", + "Community": { + "Contribution guide": "community/index", + }, + "About": "about/index", + } +} diff --git a/docs/cookiecutter_input.json b/docs/cookiecutter_input.json new file mode 100644 index 0000000000000000000000000000000000000000..6ba957deb75057490469eb920cc34cb4cca77974 --- /dev/null +++ b/docs/cookiecutter_input.json @@ -0,0 +1,20 @@ +{ + "_output_dir": "/mnt/data/git/rna/.git/cookiecutter", + "_template": "https://gitlab.com/dboe/dough.git", + "author": "Daniel B\u00f6ckenhoff", + "continuous_integration": "y", + "copyright_holder": "Daniel B\u00f6ckenhoff", + "copyright_license": "MIT License", + "data_science": "n", + "distribution_name": "rna", + "email": "dboe@ipp.mpg.de", + "keywords": "plotting, matplotlib, pyqtgraph, backend, logging, path, argparse", + "package_name": "rna", + "package_version": "0.7.11", + "pypi_username": "dboe", + "remote_namespace": "dboe", + "remote_provider": "gitlab.mpcdf.mpg.de", + "remote_username": "dboe", + "summary": "Basic and essential code building blocks of all pythons" +} + diff --git a/docs/images/favicon.ico b/docs/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e0482a1bebb6c86187c50d00045e5ed9e1316b58 Binary files /dev/null and b/docs/images/favicon.ico differ diff --git a/docs/images/logo.svg b/docs/images/logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..8e8ae8a9db12740245deaac106eadb7037d78179 --- /dev/null +++ b/docs/images/logo.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M13 21v2.5l-3-2-3 2V21h-.5A3.5 3.5 0 0 1 3 17.5V5a3 3 0 0 1 3-3h14a1 1 0 0 1 1 1v17a1 1 0 0 1-1 1h-7zm-6-2v-2h6v2h6v-3H6.5a1.5 1.5 0 0 0 0 3H7zM7 5v2h2V5H7zm0 3v2h2V8H7zm0 3v2h2v-2H7z"/></svg> \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..26fc81f8d013d8062ebbdde2b80368c0b2d54a4c --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,74 @@ +.. div:: sd-text-center sd-text-primary sd-fs-1 sd-font-weight-bolder -sd-text-nowra + + rna + +.. card:: + :text-align: center + :shadow: none + :class-card: sd-border-0 + + .. include:: ../README.rst + :start-after: [shields-start] + :end-before: [shields-end] + +.. grid:: 1 1 2 2 + :gutter: 1 + + .. grid-item:: + + .. grid:: 1 1 1 1 + :gutter: 1 + + .. grid-item-card:: Overview + :link: lib/overview/index + :link-type: doc + + .. include:: ../README.rst + :start-after: [overview-start] + :end-before: [overview-end] + + .. grid-item-card:: Installation + :link: lib/installation/index + :link-type: doc + + .. include:: lib/installation/index.rst + :start-after: [essence-start] + :end-before: [essence-end] + + .. grid-item:: + + .. grid:: 1 1 1 1 + :gutter: 1 + + .. grid-item-card:: Usage + :link: lib/usage/index + :link-type: doc + + .. include:: lib/usage/index.rst + :start-after: [essence-start] + :end-before: [essence-end] + + .. grid-item-card:: API reference + :link: lib/reference/index + :link-type: doc + :text-align: center + + :octicon:`codescan;5em;sd-text-info` + + .. grid-item-card:: API development + :link: community/index + :link-type: doc + + + .. code-block:: shell + + make install + make test + make publish + +.. toctree:: + :hidden: + + about/index + community/index + lib/index diff --git a/docs/lib/index.rst b/docs/lib/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..a05b601dac2bcae281461632068a79db1cd7b321 --- /dev/null +++ b/docs/lib/index.rst @@ -0,0 +1,17 @@ +Library documentation +===================== + +.. include:: overview/index.rst +.. include:: installation/index.rst +.. include:: usage/index.rst +.. include:: reference/index.rst + +.. toctree:: + :caption: Documentation + :hidden: + :maxdepth: 2 + + overview/index + installation/index + usage/index + reference/index \ No newline at end of file diff --git a/docs/lib/installation/index.rst b/docs/lib/installation/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..d280c1004f6147fdeb854cf6ac128c1aad39a430 --- /dev/null +++ b/docs/lib/installation/index.rst @@ -0,0 +1,70 @@ +============ +Installation +============ +.. + How to get it and set it up? + [installation] + +To install rna, you have the following options: + +.. tab-set:: + + .. tab-item:: PyPi :octicon:`package` + + The preferred method to install rna is to get the most recent stable release from PyPi: + + .. + [essence-start] + + .. code-block:: shell + + pip install rna + + .. + [essence-end] + + If you don't have `pip`_ installed, this `Python installation guide`_ can guide you through the process. + + .. _pip: https://pip.pypa.io + .. _Python installation guide: http://docs.python-guide.org/en/latest/starting/installation/ + + .. dropdown:: Extras + :icon: star + + Install a special extra: + :code:`pip install rna[extra]` + + All extras: + :code:`pip install rna[full]` + + .. tab-item:: Source :octicon:`code` + + First you have to retrieve the source code of rna. + You have the following options: + + .. tab-set:: + + .. tab-item:: Git :octicon:`git-branch` + + To clone the public repository run + + .. code-block:: shell + + $ git clone git://gitlab.mpcdf.mpg.de/dboe/rna + + .. tab-item:: Tarball :octicon:`gift` + + Either download the tarball `here <https://gitlab.mpcdf.mpg.de/dboe/rna/tarball/master>`_ or run + + .. code-block:: shell + + $ curl -OJL https://gitlab.mpcdf.mpg.de/dboe/rna/tarball/master + + + Once you have a copy of the source, navigate inside and install it with: + + .. code-block:: shell + + $ poetry install + + diff --git a/docs/lib/overview/index.rst b/docs/lib/overview/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..12114824fda68dc764ad18c14ccd04e4fce44c14 --- /dev/null +++ b/docs/lib/overview/index.rst @@ -0,0 +1,13 @@ +======== +Overview +======== +.. + What is it? Why should I use it? + [overview-start] + +.. include:: ../../../README.rst + :start-after: [overview-start] + :end-before: [overview-end] + +.. + [overview-end] diff --git a/docs/lib/reference/index.rst b/docs/lib/reference/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..ce3a9726d144e83b0c08a6bf70513810c0c24f1e --- /dev/null +++ b/docs/lib/reference/index.rst @@ -0,0 +1,13 @@ +============= +API reference +============= +.. + How to dive deeper? Give me the api? + [reference] + +.. autosummary:: +:toctree: _autosummary +:template: custom-module-template.rst +:recursive: + +.. automodule: rna diff --git a/docs/lib/usage/index.rst b/docs/lib/usage/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..391c7386466d9e691f822bec13be2050827ff9d1 --- /dev/null +++ b/docs/lib/usage/index.rst @@ -0,0 +1,18 @@ +===== +Usage +===== +.. + How to use? Give me a primer. + [usage] + +.. + [essence-start] + +To use rna in a project use + +.. code-block:: python + + import rna + +.. + [essence-end] diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000000000000000000000000000000000000..0234dc7c7b93212f16707b364bb26ddc316d1722 --- /dev/null +++ b/environment.yml @@ -0,0 +1,19 @@ +name: rna +channels: + - conda-forge + # We want to have a reproducible setup, so we don't want default channels, + # which may be different for different users. All required channels should + # be listed explicitly here. + - nodefaults +dependencies: + - python=3.10.* # or don't specify the version and use the latest stable Python + - mamba + - pip >=21.2 # pip must be mentioned explicitly, or conda-lock will fail + - pip: + - "poetry>=1.2.0a2" + - "conda-lock" + # - "pyyaml" + +plugins: + poetry: + - "poetry_bumpversion" # this is a field that is not defined by poetry but by dough diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..c5a72d7f892cc382b74eaca3b611e2dfae606ded --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,99 @@ +[tool.poetry] +name = "rna" +version = "0.7.11" +description = "Basic and essential code building blocks of all pythons" +authors = ["Daniel Böckenhoff <dboe@ipp.mpg.de>"] +license = "MIT License" +readme = "README.rst" +repository = "https://gitlab.mpcdf.mpg.de/dboe/rna" +documentation = "https://rna.readthedocs.io" +classifiers = [ + # find the full list of possible classifiers at https://pypi.org/classifiers/ + "Development Status :: 3 - Alpha", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", +] +keywords = ["plotting", " matplotlib", " pyqtgraph", " backend", " logging", " path", " argparse"] + +[tool.poetry.dependencies] +python = "^3.7" +pathlib = { version = "*", python = "<3.10" } + +[tool.poetry.group.docs] +optional = true + +[tool.poetry.group.docs.dependencies] +black = "^22.12.0" +httpx = "^0.23.2" +uvicorn = "^0.20.0" +sphinx-autobuild = "^2021.3.14" +"sphinx-autopackagesummary" = "^1.3" +sphinx-design = "^0.3.0" +sphinx = "^5.3.0" +sphinx-toolbox = "^3.2.0" +sphinx-copybutton = "^0.5.1" +sphinxcontrib-mermaid = "^0.7.1" +pydata-sphinx-theme = "^0.12.0" +sphinx-autodoc-typehints = "^1.22" + +[tool.poetry.group.dev.dependencies] +ensureconda = "*" # is part of conda-lock either way +conda-lock = "*" +tomli = { version = ">=1.1.0", python = "<3.11" } +pytest = "*" +pytest-cov = "*" +pytest-shutil = "*" +pytest-virtualenv = "*" +pytest-fixture-config = "*" +pytest-xdist = "*" +coverage = { version = "*", extras = ["toml"]} +pylint = "*" +flake8 = "*" +ipdb = "*" +mypy = "*" +twine = "*" # for publishing +pre-commit = "*" # https://pre-commit.com/ for hook managment +pre-commit-hooks = "*" +sphinx = "*" # for documentation +cookiecutter_project_upgrader = "*" + +[tool.poetry.extras] +config = ["hydra_core"] + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.coverage.report] +show_missing = true +exclude_lines = [ + "pragma: no cover", + "if False", +] + +[tool.poetry_bumpversion.file."your_package/__init__.py"] +search = "__version__ = '{current_version}'" + +[tool.poetry_bumpversion.file."docs/cookiecutter_input.json"] +search = "'package_version': '{current_version}'" + +[tool.pytest.ini_options] +addopts = "--doctest-modules --ignore=env --ignore=envs --ignore=docs" +junit_family = "xunit2" + +[tool.tox] +isolated_build = true +skip_missing_interpreters = true +envlist = "py{35,36,37,38,39,310}" +recreate = true + +[testenv] +deps = ["pytest"] +commands = ["pytest --import-mode importlib"] diff --git a/rna/__init__.py b/rna/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5b6b21b7cbfb628c6d3659f7ba346ed425daafac --- /dev/null +++ b/rna/__init__.py @@ -0,0 +1,5 @@ +"""Top-level package of rna.""" + +__author__ = """Daniel Böckenhoff""" +__email__ = "dboe@ipp.mpg.de" +__version__ = "0.7.11" diff --git a/rna/__main__.py b/rna/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..2acb97e947d01dfa561c192461beb9b2a4d8f642 --- /dev/null +++ b/rna/__main__.py @@ -0,0 +1,71 @@ +#!/user/bin/env python +""" +w7x option starter +""" +import sys +import argparse +import rna + + +class SomeAction(argparse.Action): + """Some actions.""" + + def __init__(self, option_strings, dest, nargs=None, **kwargs): + if nargs is not None: + raise ValueError("nargs not allowed") + super().__init__(option_strings, dest, **kwargs) + + def __call__(self, parser, namespace, values, option_string=None): + print( + f"Example action invoked by manage in namespace: {namespace} with values {values}" + " and option string {option_string}." + ) + setattr(namespace, self.dest, values) + + def showcase_dummy(self): + """ + You can define a method to expose functionality of the class + """ + print(self) + + +def manage(args_): + """Example function.""" + print("Managing!") + print(args_.x * args_.y) + + +def parse_args(args_): + """Parse args.""" + # create the top-level parser + parser = argparse.ArgumentParser(prog="rna app") + parser.add_argument( + "--version", + action="version", + version="v" + rna.__version__, + help="Show program's version number and exit", + ) + parser = argparse.ArgumentParser(prog="rna app") + + # subparsers + subparsers = parser.add_subparsers(help="sub-command help") + + # create the parser for the "test" command + example_sub_parser = subparsers.add_parser("manage", help="manage something") + example_sub_parser.add_argument("-x", type=int, default=1) + example_sub_parser.add_argument("-y", type=float, default=42.0) + example_sub_parser.set_defaults(func=manage) + + # If no arguments were used, print base-level help with possible commands. + if len(args_) == 0: + parser.print_help(file=sys.stderr) + sys.exit(1) + + args_ = parser.parse_args(args_) + # let argparse do the job of calling the appropriate function after + # argument parsing is complete + return args_.func(args_) + + +if __name__ == "__main__": + _ = parse_args(sys.argv[1:]) diff --git a/tests/.gitkeep b/tests/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9fa65bb13a89d7cf3a41ffb6a7a95839bdf50164 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Unit test package for rna.""" diff --git a/tests/test_package.py b/tests/test_package.py new file mode 100644 index 0000000000000000000000000000000000000000..f01fc7889744d44414388508dc463f20eabb5dc7 --- /dev/null +++ b/tests/test_package.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +"""Tests for `rna` package.""" + +import unittest +import rna + + +class TestPackage(unittest.TestCase): + """Tests for `rna` package.""" + + def setUp(self): + """Set up test fixtures, if any.""" + + def tearDown(self): + """Tear down test fixtures, if any.""" + + def test_version_type(self): + """Assure that version type is str.""" + + self.assertIsInstance(rna.__version__, str) diff --git a/tools/Makefile.envs b/tools/Makefile.envs new file mode 100644 index 0000000000000000000000000000000000000000..eff63f607b32289d155608cec8d4e079467ae897 --- /dev/null +++ b/tools/Makefile.envs @@ -0,0 +1,66 @@ +# Environment Management Makefile, adapted from https://github.com/hackalog/make_better_defaults +# +include tools/Makefile.include + +$(ENVLOCKFILE): environment.yml +ifeq (conda, $(VIRTUALENV)) + python -c "import tools; tools.env.create()" +else + @printf $(_ERROR) "env" "Unsupported Environment `$(VIRTUALENV)`. Use conda." ; + @exit 1 +endif + +.PHONY: env_create +## Set up virtual (conda) environment for this project +env_create: $(ENVLOCKFILE) +ifeq (conda,$(VIRTUALENV)) + @rm -f $(ENVLOCKFILE) + @echo + @printf $(_INFO) "env" "New conda env created. Activate with:" ; + @echo ">>> conda activate $(ENVDIR)" + @echo ">>> make env_update" +else + @printf $(_ERROR) "env" "Unsupported Environment `$(VIRTUALENV)`. Use conda." ; + @exit 1 +endif + +.PHONY: env_delete +## Delete the virtual (conda) environment for this project +env_delete: +ifeq (conda,$(VIRTUALENV)) + @printf $(_WARN) "env" "Deleting conda environment." + $(CONDA_EXE) env remove -p $(ENVDIR) + rm -f $(ENVLOCKFILE) +else + @printf $(_ERROR) "env" "Unsupported Environment `$(VIRTUALENV)`. Use conda." ; + @exit 1 +endif + +.PHONY: env_update +## Install or update Python Dependencies in the virtual (conda) environment +env_update: env_enabled $(ENVLOCKFILE) + +.PHONY: env_enabled +# Checktrues that the conda environment is active +env_enabled: +ifeq (conda,$(VIRTUALENV)) +ifneq (true,$(ENVACTIVE)) + @printf $(_ERROR) "env" "Run `$(VIRTUALENV) activate $(ENVDIR)` before proceeding." ; + @exit 1 +endif +else + @printf $(_ERROR) "env" "Unsupported Environment `$(VIRTUALENV)`. Use conda" ; + @exit 1 +endif + +.PHONY: check_lockfile +# Test that an environment lockfile exists +check_lockfile: +ifeq (X,X$(wildcard $(ENVLOCKFILE))) + @printf $(_ERROR) "env" "Run `make env_update` before proceeding..." ; + @exit 1 +endif + +.PHONY: env +## Check if environment is enabled and correctly configured +env: env_enabled check_lockfile $(ENVLOCKFILE) \ No newline at end of file diff --git a/tools/Makefile.include b/tools/Makefile.include new file mode 100644 index 0000000000000000000000000000000000000000..0dea8a8517dc0dc95a43688f1fbb84446e4c18ab --- /dev/null +++ b/tools/Makefile.include @@ -0,0 +1,53 @@ +SHELL := /bin/bash# Use bash syntax +CURRENT_PATH := $(shell pwd -P) +MODULE := $(shell basename "$(CURRENT_PATH)") +VERSION := $(shell python -c "import sys; import $(MODULE); sys.stdout.write($(MODULE).__version__)") +SOURCES := $(shell find $(MODULE) -name '*.py') +DOCUMENTATION := $(shell find . -name '*.rst') +SPHINXSOURCEDIR = ./docs +SPHINXBUILDDIR = docs/_build +GITSTATUS = $(shell git status --porcelain) +DEBUG_FILE := debug.txt +MODULE_NAME := my_project +PROJECT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +PYTHON_INTERPRETER := python3 +ARCH := $(shell $(PYTHON_INTERPRETER) -c "import platform; print(platform.platform())") +VIRTUALENV := conda +ENVLOCKFILE := conda-linux-64.lock # conda-$(ARCH).lock ??? how to make compatible with conda-lock +ENVDIR = $(CURRENT_PATH)/env +ENVACTIVE = false +ifeq (${CONDA_DEFAULT_ENV}, $(ENVDIR)) + ENVACTIVE = true +endif +CONDA_EXE := $(shell which mamba) +ifeq (,$(CONDA_EXE)) + CONDA_EXE := $(shell which conda) +endif +CONDA_RUN := +ifneq (true,$(ENVACTIVE)) + CONDA_RUN := conda run -p $(ENVDIR) +endif + +# OPTIONS +part ?= patch + +# The CI environment variable can be set to a non-empty string, +# it'll bypass this command that will "return true", as a "yes" answer. +# To use the "confirm" target inside another target, +# use the " if $(MAKE) -s confirm ; " syntax. +confirm: + @if [[ -z "$(CI)" ]]; then \ + REPLY="" ; \ + read -p "⚠ Are you sure? [y/n] > " -r ; \ + if [[ ! $$REPLY =~ ^[Yy]$$ ]]; then \ + printf $(_ERROR) "KO" "Stopping" ; \ + exit 1 ; \ + else \ + printf $(_INFO) "OK" "Continuing" ; \ + exit 0; \ + fi \ + fi +.PHONY: confirm +_WARN := "\033[33m[%s]\033[0m %s\n" # Yellow text for "printf" +_INFO := "\033[32m[%s]\033[0m %s\n" # Green text for "printf" +_ERROR := "\033[31m[%s]\033[0m %s\n" # Red text for "printf" diff --git a/tools/__init__.py b/tools/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1d8baba7c8a7f089bae6a6b86366d45c5ad4d0a2 --- /dev/null +++ b/tools/__init__.py @@ -0,0 +1,2 @@ +from . import process +from . import env diff --git a/tools/env.py b/tools/env.py new file mode 100644 index 0000000000000000000000000000000000000000..7c04446543c50946a5d1b2e2d9909a2c6923a851 --- /dev/null +++ b/tools/env.py @@ -0,0 +1,79 @@ +import shutil +from .process import run_shell_command +import tempfile +import os +from ensureconda.resolve import platform_subdir + + +TMPDIR = tempfile.gettempdir() +BOOTSTRAPDIR = os.path.join(TMPDIR, "bootstrap") +PROJECT_DIR = os.path.abspath(os.path.join(__file__, "../..")) +ENVIRONMENT_YAML = "environment.yml" +ENVIRONMENT_YAML_PATH = os.path.join(PROJECT_DIR, ENVIRONMENT_YAML) +POETRY_LOCK = "poetry.lock" +CONDA_LOCK = f"conda-{platform_subdir()}.lock" + + +def load(): + import yaml + from yaml.loader import SafeLoader + + # Open the file and load the file + with open(ENVIRONMENT_YAML_PATH) as f: + data = yaml.load(f, Loader=SafeLoader) + return data + + +def install(conda_run=""): + run_shell_command(f"{conda_run}poetry install") + run_shell_command(f"{conda_run}pre-commit install") + + +def update(conda_run=""): + # Re-generate Conda lock file(s) based on environment.yml + run_shell_command(f"{conda_run}conda-lock -k explicit --conda mamba") + # Update Conda packages based on re-generated lock file + run_shell_command(f"mamba update --file {CONDA_LOCK}") + # Update Poetry packages and re-generate poetry.lock + run_shell_command(f"{conda_run}poetry update") + + +def create(name="env"): + """ + This create routine was initially influenced by + https://stackoverflow.com/questions/70851048/does-it-make-sense-to-use-conda-poetry + """ + env_dir = os.path.join(PROJECT_DIR, name) + + if os.path.isfile(os.path.join(env_dir, POETRY_LOCK)): + run_shell_command(f"conda create -p {env_dir} --file {CONDA_LOCK}") + install() + + requirements_here = ["poetry", "mamba", "conda-lock"] + requirements_not_satisfied = any( + [shutil.which(name) is None for name in requirements_here] + ) + + run = "" + if requirements_not_satisfied: + # Use (and if necessary create) a bootstrap env + if not os.path.exists(BOOTSTRAPDIR): + run_shell_command( + f"conda create --yes -p {BOOTSTRAPDIR} -c conda-forge " + + " ".join(requirements_here) + ) + run = f"conda run -p {BOOTSTRAPDIR} " + + # Create conda lock file(s) from environment.yml + run_shell_command(f"{run}conda-lock -k explicit --conda mamba") + # Add conda-lock (and other packages, as needed) to pyproject.toml and poetry.lock + + run_shell_command(f"{run}mamba env create -f {ENVIRONMENT_YAML} -p {env_dir}") + run = f"conda run -p {env_dir} " + + install(conda_run=run) + + # Add conda and poetry lock files + run_shell_command("git add *.lock") + + run_shell_command("git commit -m 'lock-files'") diff --git a/tools/process.py b/tools/process.py new file mode 100644 index 0000000000000000000000000000000000000000..d911e4a55aeb893b7e43be550086c18acc93db76 --- /dev/null +++ b/tools/process.py @@ -0,0 +1,6 @@ +import subprocess + +def run_shell_command(command_line, *args, **kwargs): + command_line_args = command_line.split(" ") + print(f"Run command '{command_line}'") + subprocess.call(command_line_args, *args, **kwargs)