diff --git a/.cruft.json b/.cruft.json new file mode 100644 index 0000000..5f8daef --- /dev/null +++ b/.cruft.json @@ -0,0 +1,19 @@ +{ + "template": "https://github.com/ecmwf-projects/cookiecutter-conda-package", + "commit": "3b34cec552be24da244f3ec6133685c83a4e9f6c", + "checkout": null, + "context": { + "cookiecutter": { + "project_name": "cads-mars-server", + "project_slug": "cads_mars_server", + "project_short_description": "A proxy MARS server and client designed for CADS", + "copyright_holder": "European Union", + "copyright_year": "2024", + "mypy_strict": false, + "integration_tests": "true", + "pypi": true, + "_template": "https://github.com/ecmwf-projects/cookiecutter-conda-package" + } + }, + "directory": null +} diff --git a/.github/workflows/on-push.yml b/.github/workflows/on-push.yml new file mode 100644 index 0000000..9c9c2bc --- /dev/null +++ b/.github/workflows/on-push.yml @@ -0,0 +1,235 @@ +name: on-push + +on: + push: + branches: + - main + tags: + - '*' + pull_request: + branches: + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash -l {0} + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - uses: pre-commit/action@v3.0.1 + + combine-environments: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - name: Install conda-merge + run: | + python -m pip install conda-merge + - name: Combine environments + run: | + for SUFFIX in ci integration; do + conda-merge ci/environment-$SUFFIX.yml environment.yml > ci/combined-environment-$SUFFIX.yml || exit + done + - uses: actions/upload-artifact@v4 + with: + name: combined-environments + path: ci/combined-environment-*.yml + + unit-tests: + name: unit-tests + needs: combine-environments + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.11'] + + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + name: combined-environments + path: ci + - name: Get current date + id: date + run: echo "date=$(date +%Y-%m-%d)" >> "${GITHUB_OUTPUT}" + - uses: mamba-org/setup-micromamba@v1 + with: + environment-file: ci/combined-environment-ci.yml + environment-name: DEVELOP + cache-environment: true + cache-environment-key: environment-${{ steps.date.outputs.date }} + cache-downloads-key: downloads-${{ steps.date.outputs.date }} + create-args: >- + python=${{ matrix.python-version }} + - name: Install package + run: | + python -m pip install --no-deps -e . + - name: Run tests + run: | + make unit-tests COV_REPORT=xml + + type-check: + needs: [combine-environments, unit-tests] + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + name: combined-environments + path: ci + - name: Get current date + id: date + run: echo "date=$(date +%Y-%m-%d)" >> "${GITHUB_OUTPUT}" + - uses: mamba-org/setup-micromamba@v1 + with: + environment-file: ci/combined-environment-ci.yml + environment-name: DEVELOP + cache-environment: true + cache-environment-key: environment-${{ steps.date.outputs.date }} + cache-downloads-key: downloads-${{ steps.date.outputs.date }} + create-args: >- + python=3.11 + - name: Install package + run: | + python -m pip install --no-deps -e . + - name: Run code quality checks + run: | + make type-check + + docs-build: + needs: [combine-environments, unit-tests] + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + name: combined-environments + path: ci + - name: Get current date + id: date + run: echo "date=$(date +%Y-%m-%d)" >> "${GITHUB_OUTPUT}" + - uses: mamba-org/setup-micromamba@v1 + with: + environment-file: ci/combined-environment-ci.yml + environment-name: DEVELOP + cache-environment: true + cache-environment-key: environment-${{ steps.date.outputs.date }} + cache-downloads-key: downloads-${{ steps.date.outputs.date }} + create-args: >- + python=3.11 + - name: Install package + run: | + python -m pip install --no-deps -e . + - name: Build documentation + run: | + make docs-build + + integration-tests: + needs: [combine-environments, unit-tests] + if: | + success() && true + runs-on: ubuntu-latest + + strategy: + matrix: + include: + - python-version: '3.6' + extra: -integration + + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + name: combined-environments + path: ci + - name: Get current date + id: date + run: echo "date=$(date +%Y-%m-%d)" >> "${GITHUB_OUTPUT}" + - uses: mamba-org/setup-micromamba@v1 + with: + environment-file: ci/combined-environment${{ matrix.extra }}.yml + environment-name: DEVELOP${{ matrix.extra }} + cache-environment: true + cache-environment-key: environment-${{ steps.date.outputs.date }} + cache-downloads-key: downloads-${{ steps.date.outputs.date }} + create-args: >- + python=${{ matrix.python-version }} + - name: Install package + run: | + python -m pip install --no-deps -e . + - name: Run tests + run: | + make unit-tests COV_REPORT=xml + + distribution: + runs-on: ubuntu-latest + needs: [unit-tests, type-check, docs-build, integration-tests] + if: | + always() && + needs.unit-tests.result == 'success' && + needs.type-check.result == 'success' && + needs.docs-build.result == 'success' && + (needs.integration-tests.result == 'success' || needs.integration-tests.result == 'skipped') + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Install package + run: | + python -m pip install --upgrade pip + python -m pip install build twine + - name: Build distribution + run: | + python -m build + - name: Check wheels + run: | + cd dist || exit + python -m pip install cads_mars_server*.whl || exit + python -m twine check --strict * || exit + python -c "import cads_mars_server" || exit + cd .. + - uses: actions/upload-artifact@v4 + with: + name: distribution + path: dist + + upload-to-pypi: + runs-on: ubuntu-latest + needs: distribution + if: | + always() && false && + needs.distribution.result == 'success' && + github.event_name == 'push' && + startsWith(github.ref, 'refs/tags') + environment: + name: pypi + url: https://pypi.org/p/cads-mars-server + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publish + + steps: + - uses: actions/download-artifact@v4 + with: + name: distribution + path: dist + - uses: pypa/gh-action-pypi-publish@v1.9.0 + with: + verbose: true diff --git a/.gitignore b/.gitignore index e2f6b76..46bc2fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ # setuptools-scm version.py -test.ipynb # Sphinx automatic generation of API docs/_api/ @@ -132,7 +131,7 @@ Temporary Items .idea/**/usage.statistics.xml .idea/**/dictionaries .idea/**/shelf -.idea/ + # AWS User-specific .idea/**/aws.xml @@ -200,7 +199,7 @@ fabric.properties # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser -.idea/ + ### PyCharm Patch ### # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 @@ -470,5 +469,3 @@ $RECYCLE.BIN/ *.lnk # End of https://www.toptal.com/developers/gitignore/api/python,jupyternotebooks,vim,visualstudiocode,pycharm,emacs,linux,macos,windows -test.ipynb -*.sh diff --git a/.pre-commit-config-cruft.yaml b/.pre-commit-config-cruft.yaml new file mode 100644 index 0000000..b55f24f --- /dev/null +++ b/.pre-commit-config-cruft.yaml @@ -0,0 +1,7 @@ +repos: +- repo: https://github.com/cruft/cruft + rev: 2.15.0 + hooks: + - id: cruft + entry: cruft update -y + additional_dependencies: [toml] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d92f099..a276540 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,26 +1,39 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.6.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-json - id: check-yaml + - id: check-toml + - id: check-added-large-files + - id: check-merge-conflict - id: debug-statements -- repo: https://github.com/PyCQA/isort - rev: 5.12.0 - hooks: - - id: isort -- repo: https://github.com/psf/black - rev: 22.10.0 - hooks: - - id: black + - id: mixed-line-ending - repo: https://github.com/keewis/blackdoc - rev: v0.3.8 + rev: v0.3.9 hooks: - id: blackdoc - additional_dependencies: [black==22.3.0] + additional_dependencies: [black==23.11.0] +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.5.2 + hooks: + - id: ruff + args: [--fix, --show-fixes] + - id: ruff-format - repo: https://github.com/executablebooks/mdformat - rev: 0.7.16 + rev: 0.7.17 hooks: - id: mdformat +- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks + rev: v2.14.0 + hooks: + - id: pretty-format-yaml + args: [--autofix, --preserve-quotes] + - id: pretty-format-toml + args: [--autofix] +- repo: https://github.com/gitleaks/gitleaks + rev: v8.18.4 + hooks: + - id: gitleaks diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d37e5ff --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM continuumio/miniconda3 + +WORKDIR /src/cads-mars-server + +COPY environment.yml /src/cads-mars-server/ + +RUN conda install -c conda-forge gcc python=3.11 \ + && conda env update -n base -f environment.yml + +COPY . /src/cads-mars-server + +RUN pip install --no-deps -e . diff --git a/LICENSE b/LICENSE index 261eeb9..e7339fb 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2024, European Union. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Makefile b/Makefile index 94f3ee5..8b89180 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ PROJECT := cads_mars_server +CONDA := conda +CONDAFLAGS := COV_REPORT := html default: qa unit-tests type-check @@ -12,4 +14,21 @@ unit-tests: type-check: python -m mypy . +conda-env-update: + $(CONDA) install -y -c conda-forge conda-merge + $(CONDA) run conda-merge environment.yml ci/environment-ci.yml > ci/combined-environment-ci.yml + $(CONDA) env update $(CONDAFLAGS) -f ci/combined-environment-ci.yml + +docker-build: + docker build -t $(PROJECT) . + +docker-run: + docker run --rm -ti -v $(PWD):/srv $(PROJECT) + +template-update: + pre-commit run --all-files cruft -c .pre-commit-config-cruft.yaml + +docs-build: + cd docs && rm -fr _api && make clean && make html + # DO NOT EDIT ABOVE THIS LINE, ADD COMMANDS BELOW diff --git a/README.md b/README.md index e9b01c2..97f2fe5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,47 @@ -# CADS MARS Server Client +# cads-mars-server -A micro MARS server-client pair for use in the CADS, and potentially more broadly in the future. +A proxy MARS server and client designed for CADS -The server should be deployed on N Virtual Machines, then the client can access these given appropriate configuration. +## Quick Start + +```python +>>> import cads_mars_server + +``` + +## Workflow for developers/contributors + +For best experience create a new conda environment (e.g. DEVELOP) with Python 3.11: + +``` +conda create -n DEVELOP -c conda-forge python=3.11 +conda activate DEVELOP +``` + +Before pushing to GitHub, run the following commands: + +1. Update conda environment: `make conda-env-update` +1. Install this package: `pip install -e .` +1. Sync with the latest [template](https://github.com/ecmwf-projects/cookiecutter-conda-package) (optional): `make template-update` +1. Run quality assurance checks: `make qa` +1. Run tests: `make unit-tests` +1. Run the static type checker: `make type-check` +1. Build the documentation (see [Sphinx tutorial](https://www.sphinx-doc.org/en/master/tutorial/)): `make docs-build` + +## License + +``` +Copyright 2024, European Union. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/cads_mars_server/__init__.py b/cads_mars_server/__init__.py index b5fdc75..319c6fb 100644 --- a/cads_mars_server/__init__.py +++ b/cads_mars_server/__init__.py @@ -1 +1,25 @@ -__version__ = "0.2.2" +"""A proxy MARS server and client designed for CADS.""" + +# Copyright 2024, European Union. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +try: + # NOTE: the `version.py` file must not be present in the git repository + # as it is generated by setuptools at install time + from .version import __version__ +except ImportError: # pragma: no cover + # Local copy or not installed with setuptools + __version__ = "999" + +__all__ = ["__version__"] diff --git a/cads_mars_server/cli.py b/cads_mars_server/cli.py index 39309bc..f1429ab 100644 --- a/cads_mars_server/cli.py +++ b/cads_mars_server/cli.py @@ -42,9 +42,7 @@ def mars_cli() -> None: default="./server.list", ) def this_client(request_file, target, uid, server_list) -> None: - """ - A MARS client is spawned to execute a request. The request should be passed as a JSON file. - """ + """Spawn a MARS client to execute a request. Pass the request as a JSON file.""" logging.basicConfig( level=logging.INFO, format="%(asctime)s %(process)d %(levelname)s %(module)s - %(funcName)s: %(message)s", @@ -120,9 +118,7 @@ def this_client(request_file, target, uid, server_list) -> None: def this_server( mars_executable, host, port, timeout, logdir, pidfile, daemonize ) -> None: - """ - Set up a MARS server to execute requests. - """ + """Set up a MARS server to execute requests.""" logger.info(f"Starting Server {host}:{port} {logdir}") _server = server.setup_server(mars_executable, host, port, timeout, logdir) diff --git a/cads_mars_server/client.py b/cads_mars_server/client.py index c9566ae..d915bc4 100644 --- a/cads_mars_server/client.py +++ b/cads_mars_server/client.py @@ -15,7 +15,7 @@ LOG = logging.getLogger(__name__) -class ConnectionWithKeepAlive(HTTPConnectionPool.ConnectionCls): +class ConnectionWithKeepAlive(HTTPConnectionPool.ConnectionCls): # type: ignore[valid-type,misc] def _new_conn(self): conn = super()._new_conn() conn.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) @@ -91,7 +91,6 @@ def _transfer(self, r): continue if chunk == b"EROR": - try: message = json.loads(next(r.raw.read_chunked())) LOG.error(f"Error received {message}") @@ -270,11 +269,9 @@ def __init__(self, urls, retries=3, delay=10, timeout=60, log=LOG): def execute(self, request, environ, target): random.shuffle(self.urls) saved = setproctitle.getproctitle() - request_id = environ.get("request_id", "unknown") + # request_id = environ.get("request_id", "unknown") try: - for url in self.urls: - # setproctitle.setproctitle(f"cads_mars_client {request_id} {url}") client = RemoteMarsClient( diff --git a/cads_mars_server/server.py b/cads_mars_server/server.py index fe1c38f..64cfb20 100644 --- a/cads_mars_server/server.py +++ b/cads_mars_server/server.py @@ -1,4 +1,3 @@ -import argparse import http.server import json import logging @@ -8,7 +7,6 @@ import signal import socket import socketserver -import struct import time import uuid @@ -208,7 +206,6 @@ def send_header( os.set_blocking(fd, True) while True: - ready, _, _ = select.select([fd, self.rfile], [], []) data = os.read(fd, self.wbufsize) @@ -304,10 +301,7 @@ def send_header( ) def do_GET(self): - """ - Retrieve the log file for the given UID. - """ - + """Retrieve the log file for the given UID.""" uid = self.path.split("/")[-1] LOG.info("GET %s", uid) @@ -332,10 +326,7 @@ def do_GET(self): self.wfile.write(f.read()) def do_DELETE(self): - """ - Delete the log file for the given UID. - """ - + """Delete the log file for the given UID.""" uid = self.path.split("/")[-1] LOG.info("DELETE %s", uid) @@ -358,10 +349,7 @@ def do_HEAD(self): self.end_headers() def handle(self): - """ - Close the accept socket so the main server can restart - without a "Address already in use" error. - """ + """Close the accept socket so the main server can restart without a "Address already in use" error.""" ACCEPT_SOCKET.close() return super().handle() diff --git a/ci/environment-ci.yml b/ci/environment-ci.yml new file mode 100644 index 0000000..3682a54 --- /dev/null +++ b/ci/environment-ci.yml @@ -0,0 +1,17 @@ +# environment-ci.yml: Additional dependencies to install in the CI environment. +channels: +- conda-forge +- nodefaults +dependencies: +- make +- mypy +- myst-parser +- pip +- pre-commit +- pydata-sphinx-theme +- pytest +- pytest-cov +- sphinx +- sphinx-autoapi +# DO NOT EDIT ABOVE THIS LINE, ADD DEPENDENCIES BELOW +- types-requests diff --git a/ci/environment-integration.yml b/ci/environment-integration.yml new file mode 100644 index 0000000..6a8511d --- /dev/null +++ b/ci/environment-integration.yml @@ -0,0 +1,9 @@ +# environment-integration.yml: Additional dependencies to install in the integration environment (e.g., pinned dependencies). +channels: +- conda-forge +- nodefaults +dependencies: +- make +- pytest +- pytest-cov +# DO NOT EDIT ABOVE THIS LINE, ADD DEPENDENCIES BELOW diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# 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) + +.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/.gitkeep b/docs/_static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/_templates/.gitkeep b/docs/_templates/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..0159aa8 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,76 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Import and path setup --------------------------------------------------- + +import os +import sys + +import cads_mars_server + +sys.path.insert(0, os.path.abspath("../")) + +# -- Project information ----------------------------------------------------- + +project = "cads_mars_server" +copyright = "2024, European Union" +author = "European Union" +version = cads_mars_server.__version__ +release = cads_mars_server.__version__ + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "autoapi.extension", + "myst_parser", + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", +] + +# autodoc configuration +autodoc_typehints = "none" + +# autoapi configuration +autoapi_dirs = ["../cads_mars_server"] +autoapi_ignore = ["*/version.py"] +autoapi_options = [ + "members", + "inherited-members", + "undoc-members", + "show-inheritance", + "show-module-summary", + "imported-members", +] +autoapi_root = "_api" + +# napoleon configuration +napoleon_google_docstring = False +napoleon_numpy_docstring = True +napoleon_preprocess_types = True + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "pydata_sphinx_theme" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..12e6706 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,16 @@ +# Welcome to cads_mars_server's documentation! + +A proxy MARS server and client designed for CADS. + +```{toctree} +:caption: 'Contents:' +:maxdepth: 2 + +API Reference <_api/cads_mars_server/index> +``` + +# Indices and tables + +- {ref}`genindex` +- {ref}`modindex` +- {ref}`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..32bb245 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/environment.yml b/environment.yml index d5a7c1f..09939d2 100644 --- a/environment.yml +++ b/environment.yml @@ -1,5 +1,12 @@ +# environment.yml: Mandatory dependencies only. channels: - conda-forge +- nodefaults +# EXAMPLE: +# dependencies: +# - package1 +# - package2 +# DO NOT EDIT ABOVE THIS LINE, ADD DEPENDENCIES BELOW AS SHOWN IN THE EXAMPLE dependencies: - click - requests diff --git a/pyproject.toml b/pyproject.toml index 2062922..06562fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,6 @@ [build-system] -requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"] +build-backend = "setuptools.build_meta" +requires = ["setuptools", "setuptools_scm"] # Unpinned to support py3.6 [project] classifiers = [ @@ -9,35 +10,60 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Topic :: Scientific/Engineering" ] dependencies = [ -"click", -"requests", -"setproctitle" + "click", + "requests", + "setproctitle" ] -description = "A micro MARS server and client for distributed services" +description = "A proxy MARS server and client designed for CADS" dynamic = ["version"] license = {file = "LICENSE"} name = "cads-mars-server" readme = "README.md" +[project.scripts] +cads-mars-server = "cads_mars_server.__main__:mars_cli" + +[tool.coverage.run] +branch = true + +[tool.mypy] +strict = false -[tool.isort] -profile = "black" +[tool.ruff] +# Same as Black. +indent-width = 4 +line-length = 88 -[tool.pydocstyle] -add_ignore = ["D1, D2, D4"] +[tool.ruff.lint] +ignore = [ + # pydocstyle: Missing Docstrings + "D1" +] +select = [ + # pyflakes + "F", + # pycodestyle + "E", + "W", + # isort + "I", + # pydocstyle + "D" +] + +[tool.ruff.lint.pycodestyle] +max-line-length = 110 + +[tool.ruff.lint.pydocstyle] convention = "numpy" [tool.setuptools] packages = ["cads_mars_server"] -[project.scripts] -cads_mars_server = "cads_mars_server.cli:mars_cli" - [tool.setuptools_scm] write_to = "cads_mars_server/version.py" write_to_template = ''' diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..57fe51f --- /dev/null +++ b/setup.cfg @@ -0,0 +1,25 @@ +# Needed for py3.6 +[metadata] +classifiers = + Development Status :: 2 - Pre-Alpha + Intended Audience :: Science/Research + License :: OSI Approved :: Apache Software License + Operating System :: OS Independent + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.6 + Topic :: Scientific/Engineering +description = A proxy MARS server and client designed for CADS +license = Apache 2 +name = cads-mars-server +readme = file: "README.md" + +[options] +install_requires = + click + requests + setproctitle + +[options.entry_points] +console_scripts = + cads-mars-server = cads_mars_server.__main__:mars_cli diff --git a/setup.py b/setup.py deleted file mode 100644 index 4a0c147..0000000 --- a/setup.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python -# (C) Copyright 2023 ECMWF. -# -# This software is licensed under the terms of the Apache Licence Version 2.0 -# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -# In applying this licence, ECMWF does not waive the privileges and immunities -# granted to it by virtue of its status as an intergovernmental organisation -# nor does it submit to any jurisdiction. -# - - -import io -import os - -import setuptools - - -def read(fname): - file_path = os.path.join(os.path.dirname(__file__), fname) - return io.open(file_path, encoding="utf-8").read() - - -version = None -for line in read("cads_mars_server/__init__.py").split("\n"): - if line.startswith("__version__"): - version = line.split("=")[-1].strip()[1:-1] - - -assert version - - -setuptools.setup( - name="cads-mars-server", - version=version, - description="A python client/server for calling MARS.", - long_description=read("README.md"), - long_description_content_type="text/markdown", - author="European Centre for Medium-Range Weather Forecasts (ECMWF)", - author_email="software.support@ecmwf.int", - license="Apache License Version 2.0", - url="https://github.com/ecmwf-projects/cads-mars-server", - packages=setuptools.find_packages(), - include_package_data=True, - install_requires=[ - "click", - "requests", - "setproctitle", - ], - zip_safe=True, - keywords="tool", - entry_points={ - "console_scripts": ["cads-mars-server=cads_mars_server.__main__:main"], - }, - classifiers=[ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Operating System :: OS Independent", - ], -) diff --git a/tests/test_00_version.py b/tests/test_00_version.py new file mode 100644 index 0000000..3f0f7d1 --- /dev/null +++ b/tests/test_00_version.py @@ -0,0 +1,5 @@ +import cads_mars_server + + +def test_version() -> None: + assert cads_mars_server.__version__ != "999" diff --git a/tests/test_10_cli.py b/tests/test_10_cli.py new file mode 100644 index 0000000..e30e488 --- /dev/null +++ b/tests/test_10_cli.py @@ -0,0 +1,8 @@ +import subprocess + + +def test_cli_help(): + captured = subprocess.run(["cads-mars-server", "--help"], stdout=subprocess.PIPE) + assert not captured.returncode + assert captured.stdout.decode().startswith("Usage: cads-mars-server") + assert not captured.stderr