[Git][debian-gis-team/pywps][master] 11 commits: New upstream version 4.7.0

Bas Couwenberg (@sebastic) gitlab at salsa.debian.org
Mon Dec 15 13:37:19 GMT 2025



Bas Couwenberg pushed to branch master at Debian GIS Project / pywps


Commits:
35a8a6bd by Bas Couwenberg at 2025-12-15T13:00:30+01:00
New upstream version 4.7.0
- - - - -
27f65188 by Bas Couwenberg at 2025-12-15T13:00:31+01:00
Update upstream source from tag 'upstream/4.7.0'

Update to upstream version '4.7.0'
with Debian dir 60f3c8ce57fbe3cde7c14ea1e727a8d275a3c669
- - - - -
4d5188e2 by Bas Couwenberg at 2025-12-15T13:00:51+01:00
New upstream release.

- - - - -
a525f598 by Bas Couwenberg at 2025-12-15T13:05:10+01:00
Refresh patches.

- - - - -
33279af4 by Bas Couwenberg at 2025-12-15T13:09:49+01:00
Make pytest output extra verbose.

- - - - -
87d62929 by Bas Couwenberg at 2025-12-15T13:21:29+01:00
Don't install joblauncher.

- - - - -
8529210c by Bas Couwenberg at 2025-12-15T13:32:42+01:00
Replace python3-gdal with python3-fiona.

- - - - -
472d1a43 by Bas Couwenberg at 2025-12-15T13:43:50+01:00
Copy all files to build directory.

- - - - -
6f95b3bf by Bas Couwenberg at 2025-12-15T13:55:20+01:00
Fix nodoc build.

- - - - -
9dc0dd71 by Bas Couwenberg at 2025-12-15T14:36:54+01:00
Use pytest marker to skip online tests.

- - - - -
d1f63691 by Bas Couwenberg at 2025-12-15T14:36:54+01:00
Use autopkgtest-pkg-pybuild testsuite.

- - - - -


29 changed files:

- .github/workflows/main.yml
- + .readthedocs.yml
- − INSTALL.md
- + Makefile
- README.md
- VERSION.txt
- debian/changelog
- debian/control
- debian/patches/offline-tests.patch
- − debian/python3-pywps.install
- + debian/pywps-doc.docs
- − debian/pywps-doc.install
- debian/rules
- default-sample.cfg
- + environment.yml
- pyproject.toml
- pywps/__init__.py
- pywps/configuration.py
- pywps/inout/literaltypes.py
- pywps/templates/1.0.0/execute/main.xml
- pywps/templates/2.0.0/capabilities/main.xml
- pywps/validator/complexvalidator.py
- requirements-dev.txt
- requirements.txt
- setup.cfg
- setup.py
- tests/test_assync.py
- tests/validator/test_complexvalidators.py
- tox.ini


Changes:

=====================================
.github/workflows/main.yml
=====================================
@@ -1,4 +1,4 @@
-name: build ⚙️
+name: Build PyWPS ⚙️
 
 on:
   push:
@@ -7,51 +7,69 @@ on:
   pull_request:
 
 jobs:
+  lint:
+    name: Linting Suite
+    runs-on: ubuntu-latest
+    steps:
+      - name: Cancel previous runs
+        uses: styfle/cancel-workflow-action at 0.11.0
+        with:
+          access_token: ${{ github.token }}
+      - uses: actions/checkout at v4
+      - uses: actions/setup-python at v5
+        with:
+          python-version: "3.10"
+      - name: Install tox
+        run: |
+          pip install tox>=4.0
+      - name: Run linting suite ⚙️
+        run: |
+          tox -e lint
+
   test:
+    name: Testing with Python${{ matrix.python-version }}
+    needs: lint
     runs-on: ubuntu-latest
     strategy:
       matrix:
         include:
-          - tox-env: py38-extra
-            python-version: "3.8"
-          - tox-env: py39-extra
-            python-version: "3.9"
           - tox-env: py310-extra
             python-version: "3.10"
           - tox-env: py311-extra
             python-version: "3.11"
+          - tox-env: py312-extra
+            python-version: "3.12"
+          - tox-env: py313-extra
+            python-version: "3.13"
     steps:
-    - uses: actions/checkout at v3
+    - uses: actions/checkout at v4
     - name: Install packages 📦
       run: |
         sudo apt-get update
         sudo apt-get -y install libnetcdf-dev libhdf5-dev
-    - uses: actions/setup-python at v4
+    - uses: actions/setup-python at v5
       name: Setup Python ${{ matrix.python-version }}
       with:
         python-version: ${{ matrix.python-version }}
-    - name: Run flake8 ⚙️
-      run: |
-        pip install flake8
-        flake8 pywps
-      if: matrix.python-version == 3.8
     - name: Install tox 📦
       run: pip install "tox>=4.0"
     - name: Run tests with tox ⚙️
       run: tox -e ${{ matrix.tox-env }}
     - name: Run coveralls ⚙️
-      if: matrix.python-version == 3.8
+      if: matrix.python-version == 3.10
       uses: AndreMiras/coveralls-python-action at develop
 
   docs:
+    name: Build docs 🏗️
+    needs: lint
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout at v3
-      - uses: actions/setup-python at v4
-        name: Setup Python 3.8
+      - uses: actions/checkout at v4
+      - uses: actions/setup-python at v5
+        name: Setup Python 3.10
         with:
-          python-version: 3.8
-      - name: Build docs 🏗️
+          python-version: "3.10"
+      - name: Build documentation 🏗️
         run: |
           pip install -e .[dev]
           cd docs && make html


=====================================
.readthedocs.yml
=====================================
@@ -0,0 +1,27 @@
+# .readthedocs.yaml
+# Read the Docs configuration file
+# See: https://docs.readthedocs.io/en/stable/config-file/v2.html
+
+version: 2  # Required
+
+# Documentation formats to build and make available
+formats:
+  - pdf
+  - epub
+
+build:
+  os: ubuntu-22.04
+  tools:
+    python: "3.11"  # Use standard CPython version; `mambaforge-22.9` is not valid here
+
+# Python-related configuration
+python:
+  install:
+    - method: pip
+      path: .
+      extra_requirements:
+        - dev
+
+# Sphinx configuration
+sphinx:
+  configuration: docs/conf.py


=====================================
INSTALL.md deleted
=====================================
@@ -1,44 +0,0 @@
-PyWPS 4 Installation
-====================
-
-Dependencies
-------------
-
-To use PyWPS 4 the third party libraries GIT and GDAL need to be installed in the system.
-
-In Debian based systems these can be installed with:
-
-    $ sudo apt-get install git python-gdal
-    
-In Windows systems a Git client should be installed (e.g. GitHub for Windows).
-    
-Install PyWPS 4
----------------
-
-Using pip: 
-
-    $ sudo pip install -e git+https://github.com/geopython/pywps.git@main#egg=pywps
-
-Or in alternative install it manually:
-
-    $ git clone https://github.com/geopython/pywps.git
-    
-    $ cd pywps/
-    
-    $ sudo pip install .
-
-Install example service
------------------------
-
-    $ git clone https://github.com/geopython/pywps-flask.git pywps-flask
-    
-
-Run example service
--------------------
-
-    $ python demo.py
-    
-Access example service
-----------------------
-
-    http://localhost:5000


=====================================
Makefile
=====================================
@@ -0,0 +1,104 @@
+# Configuration
+APP_ROOT := $(abspath $(lastword $(MAKEFILE_LIST))/..)
+
+# end of configuration
+
+define PRINT_HELP_PYSCRIPT
+import re, sys
+
+for line in sys.stdin:
+	match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line)
+	if match:
+		target, help = match.groups()
+		print("%-20s %s" % (target, help))
+	else:
+		match = re.match(r'^## (.*)$$', line)
+		if match:
+			help = match.groups()[0]
+			print("\n%s" % (help))
+endef
+export PRINT_HELP_PYSCRIPT
+
+.DEFAULT_GOAL := help
+
+help: ## print this help message. (Default)
+	@python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)
+
+## Build targets:
+
+install: ## install pywps
+	@echo "Installing pywps ..."
+	@-bash -c 'pip install -e .'
+
+develop: ## install pywps with development libraries
+	@echo "Installing development requirements for tests and docs ..."
+	@-bash -c 'pip install -e ".[dev]"'
+
+clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts
+
+clean-build: ## remove build artifacts
+	@echo "Removing build artifacts ..."
+	@-rm -fr build/
+	@-rm -fr dist/
+	@-rm -fr .eggs/
+	@-find . -name '*.egg-info' -exec rm -fr {} +
+	@-find . -name '*.egg' -exec rm -f {} +
+	@-find . -name '*.log' -exec rm -fr {} +
+	@-find . -name '*.sqlite' -exec rm -fr {} +
+
+clean-pyc: ## remove Python file artifacts
+	@echo "Removing Python file artifacts ..."
+	@-find . -name '*.pyc' -exec rm -f {} +
+	@-find . -name '*.pyo' -exec rm -f {} +
+	@-find . -name '*~' -exec rm -f {} +
+	@-find . -name '__pycache__' -exec rm -fr {} +
+
+clean-test: ## remove test and coverage artifacts
+	@echo "Removing test artifacts ..."
+	@-rm -fr .tox/
+	@-rm -f .coverage
+	@-rm -fr .pytest_cache
+
+clean-dist: clean  ## remove git ignored files and directories
+	@echo "Running 'git clean' ..."
+	@git diff --quiet HEAD || echo "There are uncommitted changes! Aborting 'git clean' ..."
+	## do not use git clean -e/--exclude here, add them to .gitignore instead
+	@-git clean -dfx
+
+clean-docs: ## remove documentation artifacts
+	@echo "Removing documentation artifacts ..."
+	$(MAKE) -C docs clean
+
+lint: ## check style with ruff
+	@echo "Running code style checks ..."
+	@bash -c 'ruff check pywps'
+
+## Testing targets:
+
+test: ## run tests quickly with the default Python (skip slow and online tests)
+	@echo "Running tests (skip slow and online tests) ..."
+	@bash -c 'pytest -v -m "not slow and not online" tests/'
+
+test-all: ## run all tests quickly with the default Python
+	@echo "Running all tests (including slow and online tests) ..."
+	@bash -c 'pytest -v tests/'
+
+## Sphinx targets:
+
+docs: clean-docs ## generate Sphinx HTML documentation, including API docs
+	@echo "Generating docs with Sphinx ..."
+	$(MAKE) -C docs html
+	@echo "Open your browser to: file:/$(APP_ROOT)/docs/build/html/index.html"
+	## do not execute xdg-open automatically since it hangs ReadTheDocs and job does not complete
+	@echo "xdg-open $(APP_ROOT)/docs/build/html/index.html"
+
+
+## Deployment targets:
+
+# dist: clean ## builds source and wheel package
+# 	python -m flit build
+# 	ls -l dist
+
+# release: dist ## package and upload a release
+# 	python -m flit publish dist/*
+


=====================================
README.md
=====================================
@@ -7,21 +7,31 @@ the Open Geospatial Consortium. PyWPS is written in Python.
 [![Build Status](https://github.com/geopython/pywps/actions/workflows/main.yml/badge.svg)](https://github.com/geopython/pywps/actions/workflows/main.yml)
 [![Coverage Status](https://coveralls.io/repos/github/geopython/pywps/badge.svg?branch=main)](https://coveralls.io/github/geopython/pywps?branch=main)
 [![PyPI](https://img.shields.io/pypi/dm/pywps.svg)](https://pypi.org/project/pywps/)
+[![Conda](https://anaconda.org/conda-forge/pywps/badges/version.svg)](https://anaconda.org/channels/conda-forge/packages/pywps/overview)
 [![GitHub license](https://img.shields.io/github/license/geopython/pywps.svg)]()
 
 [![Join the chat at https://gitter.im/geopython/pywps](https://badges.gitter.im/geopython/pywps.svg)](https://gitter.im/geopython/pywps?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 
-# License
+## License
 
 As of PyWPS 4.0.0, PyWPS is released under an
 [MIT](https://en.wikipedia.org/wiki/MIT_License) license
 (see [LICENSE.txt](LICENSE.txt)).
 
-# Dependencies
+## Dependencies
 
 See [requirements.txt](requirements.txt) file
 
-# Run tests
+## Install
+
+Install it from GitHub:
+```bash
+$ git clone https://github.com/geopython/pywps.git
+$ cd pywps/
+$ pip install .
+```
+
+## Run tests
 
 ```bash
 pip install -r requirements-dev.txt
@@ -32,9 +42,37 @@ python -m coverage run --source=pywps -m unittest tests
 python -m coverage report -m
 ```
 
-# Run web application
+## Quick Guide with Conda
 
-## Example service
+Checkout source from GitHub:
+```bash
+$ git clone https://github.com/geopython/pywps.git
+$ cd pywps/
+```
+
+Build conda environment:
+```bash
+conda env create -f environment.yml
+```
+
+Install pywps:
+```bash
+make install
+```
+
+Or the development version:
+```bash
+make develop
+```
+
+Run tests:
+```bash
+make tests
+```
+
+## Run web application
+
+### Example service
 
 Clone the example service after having installed PyWPS:
 
@@ -44,7 +82,9 @@ cd pywps-flask
 python demo.py
 ```
 
-## Apache configuration
+Access example service: http://localhost:5000
+
+### Apache configuration
 
 1. Enable WSGI extension
 
@@ -92,7 +132,7 @@ python demo.py
     ```
 
 
-# Issues
+## Issues
 
 On Windows PyWPS does not support multiprocessing which is used when making
 requests storing the response document and updating the status to displaying


=====================================
VERSION.txt
=====================================
@@ -1 +1 @@
-4.6.0
+4.7.0


=====================================
debian/changelog
=====================================
@@ -1,14 +1,23 @@
-pywps (4.6.0-2) UNRELEASED; urgency=medium
+pywps (4.7.0-1) UNRELEASED; urgency=medium
 
   * Team upload.
+  * New upstream release.
   * Bump Standards-Version to 4.7.2, no changes.
   * Fix old FSF address in copyright file.
   * Mark pywps-doc as Multi-Arch: foreign.
   * Update lintian overrides.
   * Drop Rules-Requires-Root: no, default since dpkg 1.22.13.
   * Use test-build-validate-cleanup instead of test-build-twice.
-
- -- Bas Couwenberg <sebastic at debian.org>  Sun, 28 Jul 2024 20:00:42 +0200
+  * Refresh patches.
+  * Make pytest output extra verbose.
+  * Don't install joblauncher.
+  * Replace python3-gdal with python3-fiona.
+  * Copy all files to build directory.
+  * Fix nodoc build.
+  * Use pytest marker to skip online tests.
+  * Use autopkgtest-pkg-pybuild testsuite.
+
+ -- Bas Couwenberg <sebastic at debian.org>  Mon, 15 Dec 2025 13:00:40 +0100
 
 pywps (4.6.0-1) unstable; urgency=medium
 


=====================================
debian/control
=====================================
@@ -13,7 +13,7 @@ Build-Depends: debhelper-compat (= 13),
                pybuild-plugin-pyproject,
                python3-all,
                python3-dateutil,
-               python3-gdal,
+               python3-fiona,
                python3-humanize,
                python3-jinja2,
                python3-jsonschema,
@@ -21,35 +21,23 @@ Build-Depends: debhelper-compat (= 13),
                python3-markupsafe,
                python3-netcdf4,
                python3-owslib,
-               python3-pytest,
+               python3-pytest <!nocheck>,
                python3-requests,
                python3-setuptools,
-               python3-sphinx,
+               python3-sphinx <!nodoc>,
                python3-sqlalchemy,
                python3-werkzeug
 Standards-Version: 4.7.2
 Vcs-Browser: https://salsa.debian.org/debian-gis-team/pywps
 Vcs-Git: https://salsa.debian.org/debian-gis-team/pywps.git
 Homepage: https://pywps.org
+Testsuite: autopkgtest-pkg-pybuild
 
 Package: python3-pywps
 Architecture: all
-Depends: python3-dateutil,
-         python3-gdal,
-         python3-humanize,
-         python3-jinja2,
-         python3-jsonschema,
-         python3-lxml,
-         python3-markupsafe,
-         python3-owslib,
-         python3-requests,
-         python3-sqlalchemy,
-         python3-werkzeug,
-         ${python3:Depends},
+Depends: ${python3:Depends},
          ${misc:Depends}
-Recommends: python3-mapscript,
-            python3-netcdf4,
-            python3-pyproj
+Recommends: python3-netcdf4
 Suggests: grass-core,
           r-base
 Breaks: python-pywps (<< 4.2.1-2~)


=====================================
debian/patches/offline-tests.patch
=====================================
@@ -1,6 +1,6 @@
 Description: Disable tests that require network.
 Author: Bas Couwenberg <sebastic at debian.org>
-Forwarded: not-needed
+Forwarded: https://github.com/geopython/pywps/pull/698
 
 --- a/tests/test_ows.py
 +++ b/tests/test_ows.py
@@ -16,7 +16,7 @@ Forwarded: not-needed
                             supported_formats=[get_format('GEOTIFF')])],
                         grass_location='epsg:4326')
  
-+    @pytest.mark.skipif('OFFLINE_TESTS' in os.environ, reason="offline tests only")
++    @pytest.mark.online
      def test_wfs(self):
          if not service_ok('https://demo.mapserver.org'):
              self.skipTest("mapserver is unreachable")
@@ -24,27 +24,41 @@ Forwarded: not-needed
          # . the inclusion of output
          # . the type of output
  
-+    @pytest.mark.skipif('OFFLINE_TESTS' in os.environ, reason="offline tests only")
++    @pytest.mark.online
      def test_wcs(self):
          if not config.CONFIG.get('grass', 'gisbase'):
              self.skipTest('GRASS lib not found')
 --- a/tests/validator/test_complexvalidators.py
 +++ b/tests/validator/test_complexvalidators.py
-@@ -76,7 +76,8 @@ class ValidateTest(TestBase):
-         self.assertTrue(validategml(gml_input, MODE.NONE), 'NONE validation')
-         self.assertTrue(validategml(gml_input, MODE.SIMPLE), 'SIMPLE validation')
-         self.assertTrue(validategml(gml_input, MODE.STRICT), 'STRICT validation')
--        self.assertTrue(validategml(gml_input, MODE.VERYSTRICT), 'VERYSTRICT validation')
-+        if os.environ.get('OFFLINE_TESTS') == None:
-+            self.assertTrue(validategml(gml_input, MODE.VERYSTRICT), 'VERYSTRICT validation')
+@@ -69,6 +69,7 @@ class ValidateTest(TestBase):
+ 
+         return fake_input
+ 
++    @pytest.mark.online
+     def test_gml_validator(self):
+         """Test GML validator
+         """
+@@ -79,6 +80,7 @@ class ValidateTest(TestBase):
+         # self.assertTrue(validategml(gml_input, MODE.VERYSTRICT), 'VERYSTRICT validation')
          gml_input.stream.close()
  
-     def test_json_validator(self):
-@@ -133,6 +134,7 @@ class ValidateTest(TestBase):
++    @pytest.mark.online
+     @pytest.mark.xfail(reason="gml verystrict validation fails")
+     def test_gml_validator_verystrict(self):
+         """Test GML validator
+@@ -117,6 +119,7 @@ class ValidateTest(TestBase):
+         self.assertTrue(validateshapefile(shapefile_input, MODE.STRICT), 'STRICT validation')
+         shapefile_input.stream.close()
+ 
++    @pytest.mark.geotiff
+     def test_geotiff_validator(self):
+         """Test GeoTIFF validator
+         """
+@@ -141,6 +144,7 @@ class ValidateTest(TestBase):
          else:
              self.assertFalse(validatenetcdf(netcdf_input, MODE.STRICT), 'STRICT validation')
  
-+    @pytest.mark.skipif('OFFLINE_TESTS' in os.environ, reason="offline tests only")
++    @pytest.mark.online
      @pytest.mark.xfail(reason="test.opendap.org is offline")
      def test_dods_validator(self):
          opendap_input = ComplexInput('dods', 'opendap test', [FORMATS.DODS,])
@@ -54,7 +68,7 @@ Forwarded: not-needed
  class ExecuteTest(TestBase):
      """Test for Exeucte request KVP request"""
  
-+    @pytest.mark.skipif('OFFLINE_TESTS' in os.environ, reason="offline tests only")
++    @pytest.mark.online
      @pytest.mark.xfail(reason="test.opendap.org is offline")
      def test_dods(self):
          if not WITH_NC4:
@@ -72,7 +86,7 @@ Forwarded: not-needed
          with self.assertRaises(TypeError):
              self.iohandler[0].data = '5'
  
-+    @pytest.mark.skipif('OFFLINE_TESTS' in os.environ, reason="offline tests only")
++    @pytest.mark.online
      def test_url(self):
          if not service_ok('https://demo.mapserver.org'):
              self.skipTest("mapserver is unreachable")
@@ -80,7 +94,7 @@ Forwarded: not-needed
          b = self.complex_out.base64
          self.assertEqual(base64.b64decode(b).decode(), self.data)
  
-+    @pytest.mark.skipif('OFFLINE_TESTS' in os.environ, reason="offline tests only")
++    @pytest.mark.online
      def test_url_handler(self):
          wfsResource = 'http://demo.mapserver.org/cgi-bin/wfs?' \
                        'service=WFS&version=1.1.0&' \
@@ -88,7 +102,7 @@ Forwarded: not-needed
          )
  
  
-+ at pytest.mark.skipif('OFFLINE_TESTS' in os.environ, reason="offline tests only")
++ at pytest.mark.online
  class TestMetaLink(TestBase):
  
      def setUp(self) -> None:


=====================================
debian/python3-pywps.install deleted
=====================================
@@ -1,2 +0,0 @@
-usr/bin/*
-usr/lib/python3*


=====================================
debian/pywps-doc.docs
=====================================
@@ -0,0 +1 @@
+docs/_build/html


=====================================
debian/pywps-doc.install deleted
=====================================
@@ -1 +0,0 @@
-docs/_build/html usr/share/doc/pywps/


=====================================
debian/rules
=====================================
@@ -12,15 +12,15 @@
 # Enable hardening build flags
 export DEB_BUILD_MAINT_OPTIONS=hardening=+all
 
-export PYBUILD_BEFORE_TEST=cp -rv {dir}/pywps/schemas {build_dir}/pywps/
-export OFFLINE_TESTS=1
+export PYBUILD_NAME=pywps
+export PYBUILD_BEFORE_TEST=cp -rv {dir}/pywps {dir}/tests {build_dir}
+export PYBUILD_TEST_ARGS=-vv -m "not online and not geotiff"
 
 %:
 	dh $@ --buildsystem=pybuild
 
 execute_after_dh_auto_build:
+ifeq (,$(filter nodoc,$(DEB_BUILD_OPTIONS)))
 	(cd docs && PYTHONPATH=$(CURDIR) $(MAKE) html && \
 	 ln -s /usr/share/javascript/mathjax _build/html/_static/)
-
-override_dh_auto_test:
-	dh_auto_test || echo "Ignoring test failures"
+endif


=====================================
default-sample.cfg
=====================================
@@ -61,6 +61,36 @@ maxprocesses=30
 parallelprocesses=2
 storagetype=file
 
+# hardcoded default : tempfile.gettempdir()
+; temp_path=/tmp
+
+processes_path=
+# list of allowed input paths (file url input) seperated by ':'
+allowedinputpaths=
+
+# hardcoded default : tempfile.gettempdir()
+; workdir=
+
+# If this flag is enabled it will set the HOME environment for each process to
+# its current workdir (a temp folder).
+sethomedir=false
+
+# If this flag is true PyWPS will remove the process temporary workdir after
+# process has finished.
+cleantempdir=true
+
+# File storage outputs can be copied, moved or linked from the workdir to the
+# output folder.
+# Allowed functions: "copy", "move", "link" (hardcoded default "copy")
+storage_copy_function=copy
+
+# Handles the default mimetype for requests.
+# valid options: "text/xml", "application/json"
+default_mimetype=text/xml
+
+# Default indentation used for json data responses.
+json_indent=2
+
 # hardcoded default : tempfile.gettempdir()
 #temp_path=/tmp
 
@@ -96,6 +126,12 @@ json_indent=2
 [processing]
 mode=default
 
+# hardcoded default: os.path.dirname(os.path.realpath(sys.argv[0]))
+; path=
+
+# https://github.com/natefoo/slurm-drmaa
+drmaa_native_specification=
+
 # hardcoded default: os.path.dirname(os.path.realpath(sys.argv[0]))
 #path=
 


=====================================
environment.yml
=====================================
@@ -0,0 +1,23 @@
+name: pywps
+channels:
+ - conda-forge
+dependencies:
+ - python >=3.10,<3.14
+ - pip >=25.0
+ - owslib >=0.35.0
+ - requests >=2.32.5
+ - werkzeug >=3.1.4
+ - sqlalchemy >=2.0.44
+ - lxml >=6.0.2
+ - urllib3 >=2.5.0
+ - markupsafe >=3.0.3
+ - numpy >=1.22.2
+ - zarr <3
+ - fiona
+ - geotiff
+ # tests
+ - pytest
+ - ruff >=0.5.7
+ # docs
+ - sphinx >=7.0.0
+ 


=====================================
pyproject.toml
=====================================
@@ -1,3 +1,11 @@
 [build-system]
 requires = ["setuptools"]
 build-backend = "setuptools.build_meta"
+
+[tool.ruff]
+lint.select = ["E", "W", "F", "C90"]   # Flake8-equivalent rule families
+lint.ignore = ["F401", "E402", "C901"]
+
+line-length = 120
+exclude = ["tests"]
+


=====================================
pywps/__init__.py
=====================================
@@ -8,7 +8,7 @@ import os
 
 from lxml.builder import ElementMaker
 
-__version__ = "4.6.0"
+__version__ = "4.7.0"
 
 LOGGER = logging.getLogger('PYWPS')
 LOGGER.debug('setting core variables')


=====================================
pywps/configuration.py
=====================================
@@ -175,7 +175,7 @@ def load_configuration(cfgfiles=None):
     :param cfgfiles: list of configuration files
     """
 
-    global CONFIG
+    global CONFIG  # noqa
 
     load_hardcoded_configuration()
 
@@ -200,7 +200,7 @@ def load_configuration(cfgfiles=None):
 def _check_config():
     """Check some configuration values
     """
-    global CONFIG
+    global CONFIG  # noqa
 
     def checkdir(confid):
 


=====================================
pywps/inout/literaltypes.py
=====================================
@@ -32,7 +32,7 @@ def register_convert_type(name):
     """
 
     def _register_convert_type(function):
-        global LITERAL_DATA_TYPES
+        global LITERAL_DATA_TYPES  # noqa
         LITERAL_DATA_TYPES[name] = function
         return function
 


=====================================
pywps/templates/1.0.0/execute/main.xml
=====================================
@@ -10,7 +10,7 @@
         {% if wsdl %}
         <wps:WSDL xlink:href="{{ process.wsdl }}"/>
         {% endif %}
-	</wps:Process>
+    </wps:Process>
     <wps:Status creationTime="{{ status.time }}">
         {% if status.status == "accepted" %}
         <wps:ProcessAccepted percentCompleted="{{ status.percent_done }}">{{ status.message }}</wps:ProcessAccepted>
@@ -29,39 +29,39 @@
             </wps:ExceptionReport>
         </wps:ProcessFailed>
         {% endif %}
-	</wps:Status>
+    </wps:Status>
     {% if lineage %}
     {% if input_definitions %}
-	<wps:DataInputs>
+    <wps:DataInputs>
         {% for input in input_definitions %}
-		<wps:Input>
+        <wps:Input>
             <ows:Identifier>{{ input.identifier }}</ows:Identifier>
             <ows:Title>{{ get_translation(input, "title", language) }}</ows:Title>
             <ows:Abstract>{{ get_translation(input, "abstract", language) }}</ows:Abstract>
             {% if input.type == "complex" %}
-			<wps:Data>
+            <wps:Data>
                     <wps:ComplexData mimeType="{{ input.mimetype }}" encoding="{{ input.encoding }}" schema="{{ input.schema }}">{{ input.data | safe }}</wps:ComplexData>
-			</wps:Data>
+            </wps:Data>
             {% elif input.type == "literal" %}
-			<wps:Data>
+            <wps:Data>
                 <wps:LiteralData {% if input.uom %}uom="{{ input.uom.reference }}"{% endif %}>{{ input.data }}</wps:LiteralData>
-			</wps:Data>
+            </wps:Data>
             {% elif input.type == "bbox" %}
-			<wps:Data>
+            <wps:Data>
                 <wps:BoundingBoxData crs="{{ input.crs }}" dimensions="{{ input.dimensions }}">
                     <ows:LowerCorner>{% for c in input.ll %} {{ c }} {% endfor %}</ows:LowerCorner>
                     <ows:UpperCorner>{% for c in input.ur %} {{ c }} {% endfor %}</ows:UpperCorner>
                 </wps:BoundingBoxData>
-			</wps:Data>
+            </wps:Data>
             {% elif input.type == "reference" %}
             <wps:Reference xlink:href="{{ input.href }}" method="{{ input.method }}" mimeType="{{ input.mimetype }}" encoding="{{ input.encoding }}" schema="{{ input.schema }}"/>
             {% endif %}
-		</wps:Input>
+        </wps:Input>
         {% endfor %}
-	</wps:DataInputs>
+    </wps:DataInputs>
     {% endif %}
     {% if output_definitions %}
-	<wps:OutputDefinitions>
+    <wps:OutputDefinitions>
         {% for output in output_definitions %}
         {% if output.type in ["complex", "reference"] %}
         <wps:Output mimeType="{{ output.mimetype }}" encoding="{{ output.encoding }}" schema="{{ output.schema }}" asReference="{{ output.asreference }}">
@@ -71,38 +71,38 @@
             <ows:Identifier>{{ output.identifier }}</ows:Identifier>
             <ows:Title>{{ get_translation(output, "title", language) }}</ows:Title>
             <ows:Abstract>{{ get_translation(output, "abstract", language) }}</ows:Abstract>
-		</wps:Output>
+        </wps:Output>
         {% endfor %}
-	</wps:OutputDefinitions>
+    </wps:OutputDefinitions>
     {% endif %}
     {% endif %}
     {% if outputs %}
-	<wps:ProcessOutputs>
+    <wps:ProcessOutputs>
         {% for output in outputs %}
-		<wps:Output>
+        <wps:Output>
             <ows:Identifier>{{ output.identifier }}</ows:Identifier>
             <ows:Title>{{ get_translation(output, "title", language) }}</ows:Title>
             <ows:Abstract>{{ get_translation(output, "abstract", language) }}</ows:Abstract>
             {% if output.type == "reference" %}
             <wps:Reference href="{{ output.href }}" mimeType="{{ output.mimetype }}" encoding="{{ output.encoding }}" schema="{{ output.schema }}"/>
             {% elif output.type == "complex" %}
-			<wps:Data>
+            <wps:Data>
                     <wps:ComplexData mimeType="{{ output.mimetype }}" encoding="{{ output.encoding }}" schema="{{ output.schema }}">{{ output.data | safe }}</wps:ComplexData>
-			</wps:Data>
+            </wps:Data>
             {% elif output.type == "literal" %}
-			<wps:Data>
+            <wps:Data>
                 <wps:LiteralData {% if output.uom %}uom="{{ output.uom.reference }}"{% endif %} dataType="{{ output.data_type }}">{{ output.data }}</wps:LiteralData>
-			</wps:Data>
+            </wps:Data>
             {% elif output.type == "bbox" %}
-			<wps:Data>
+            <wps:Data>
                     <wps:BoundingBoxData crs="{{ output.crs }}" dimensions="{{ output.dimensions }}">
                         <ows:LowerCorner>{% for c in output.ll %} {{ c }} {% endfor %}</ows:LowerCorner>
                         <ows:UpperCorner>{% for c in output.ur %} {{ c }} {% endfor %}</ows:UpperCorner>
                     </wps:BoundingBoxData>
-			</wps:Data>
+            </wps:Data>
             {% endif %}
-		</wps:Output>
+        </wps:Output>
         {% endfor %}
-	</wps:ProcessOutputs>
+    </wps:ProcessOutputs>
     {% endif %}
 </wps:ExecuteResponse>


=====================================
pywps/templates/2.0.0/capabilities/main.xml
=====================================
@@ -1,24 +1,24 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- PyWPS {{ pywps_version }} -->
 <wps:Capabilities xmlns:ows="http://www.opengis.net/ows/2.0" xmlns:wps="http://www.opengis.net/wps/2.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wps/2.0 ../wps.xsd" service="WPS" version="2.0.0">
-	<ows:ServiceIdentification>
+    <ows:ServiceIdentification>
         <ows:Title>{{ title }}</ows:Title>
         <ows:Abstract>{{ abstract }}</ows:Abstract>
         <ows:Keywords>{% for keyword in keywords %}
             <ows:Keyword>{{ keyword }}</ows:Keyword>{% endfor %}
-		</ows:Keywords>
-		<ows:ServiceType>WPS</ows:ServiceType>
-		<ows:ServiceTypeVersion>2.0.0</ows:ServiceTypeVersion>
+        </ows:Keywords>
+        <ows:ServiceType>WPS</ows:ServiceType>
+        <ows:ServiceTypeVersion>2.0.0</ows:ServiceTypeVersion>
         <ows:Fees>{{ fees }}</ows:Fees>
         <ows:AccessConstraints>{% for ac in accessconstraints %}{{ ac }} {% endfor %}</ows:AccessConstraints>
-	</ows:ServiceIdentification>
-	<ows:ServiceProvider>
+    </ows:ServiceIdentification>
+    <ows:ServiceProvider>
         <ows:ProviderName>{{ provider.name }}</ows:ProviderName>
         <ows:ProviderSite xlink:href="{{ provider.site }}"/>
-		<ows:ServiceContact>
+        <ows:ServiceContact>
             <ows:individualname>{{ provider.individual }}</ows:individualname>
             <ows:positionname>{{ provider.position }}</ows:positionname>
-			<ows:ContactInfo>
+            <ows:ContactInfo>
                 <ows:Phone>
                     <ows:Voice>{{ provider.voice }}</ows:Voice>
                     <ows:Facsimile>{{ provider.fascimile }}</ows:Facsimile>
@@ -31,76 +31,76 @@
                     <ows:Country>{{ provider.address.country }}</ows:Country>
                     <ows:ElectronicMailAddress>{{ provider.address.email }}</ows:ElectronicMailAddress>
                 </ows:Address>
-			</ows:ContactInfo>
-		</ows:ServiceContact>
-	</ows:ServiceProvider>
-	<ows:OperationsMetadata>
-		<ows:Operation name="GetCapabilities">
-			<ows:DCP>
-				<ows:HTTP>
+            </ows:ContactInfo>
+        </ows:ServiceContact>
+    </ows:ServiceProvider>
+    <ows:OperationsMetadata>
+        <ows:Operation name="GetCapabilities">
+            <ows:DCP>
+                <ows:HTTP>
                     <ows:Get xlink:href="{{ serviceurl }}"/>
                     <ows:Post xlink:href="{{ serviceurl }}"/>
-				</ows:HTTP>
-			</ows:DCP>
-		</ows:Operation>
-		<ows:Operation name="DescribeProcess">
-			<ows:DCP>
-				<ows:HTTP>
+                </ows:HTTP>
+            </ows:DCP>
+        </ows:Operation>
+        <ows:Operation name="DescribeProcess">
+            <ows:DCP>
+                <ows:HTTP>
                     <ows:Get xlink:href="{{ serviceurl }}"/>
                     <ows:Post xlink:href="{{ serviceurl }}"/>
-				</ows:HTTP>
-			</ows:DCP>
-		</ows:Operation>
-		<ows:Operation name="Execute">
-			<ows:DCP>
-				<ows:HTTP>
+                </ows:HTTP>
+            </ows:DCP>
+        </ows:Operation>
+        <ows:Operation name="Execute">
+            <ows:DCP>
+                <ows:HTTP>
                     <ows:Post xlink:href="{{ serviceurl }}"/>
-				</ows:HTTP>
-			</ows:DCP>
-		</ows:Operation>
-		<ows:Operation name="GetStatus">
-			<ows:DCP>
-				<ows:HTTP>
+                </ows:HTTP>
+            </ows:DCP>
+        </ows:Operation>
+        <ows:Operation name="GetStatus">
+            <ows:DCP>
+                <ows:HTTP>
                     <ows:Get xlink:href="{{ serviceurl }}"/>
                     <ows:Post xlink:href="{{ serviceurl }}"/>
-				</ows:HTTP>
-			</ows:DCP>
-		</ows:Operation>
-		<ows:Operation name="GetResult">
-			<ows:DCP>
-				<ows:HTTP>
+                </ows:HTTP>
+            </ows:DCP>
+        </ows:Operation>
+        <ows:Operation name="GetResult">
+            <ows:DCP>
+                <ows:HTTP>
                     <ows:Get xlink:href="{{ serviceurl }}"/>
                     <ows:Post xlink:href="{{ serviceurl }}"/>
-				</ows:HTTP>
-			</ows:DCP>
-		</ows:Operation>
-		<ows:Operation name="Dismiss">
-			<ows:DCP>
-				<ows:HTTP>
+                </ows:HTTP>
+            </ows:DCP>
+        </ows:Operation>
+        <ows:Operation name="Dismiss">
+            <ows:DCP>
+                <ows:HTTP>
                     <ows:Get xlink:href="{{ serviceurl }}"/>
                     <ows:Post xlink:href="{{ serviceurl }}"/>
-				</ows:HTTP>
-			</ows:DCP>
-		</ows:Operation>
-	</ows:OperationsMetadata>
+                </ows:HTTP>
+            </ows:DCP>
+        </ows:Operation>
+    </ows:OperationsMetadata>
     <wps:Contents>{% for process in processes %}
             <wps:ProcessSummary jobControlOptions="{% for option in process.control_options %}{{ option }} {% endfor %}" wps:processVersion="{{ process.version }}">
 
-			<ows:Title>process.title</ows:Title>
-			<ows:Identifier>process.identifier</ows:Identifier>
+            <ows:Title>process.title</ows:Title>
+            <ows:Identifier>process.identifier</ows:Identifier>
             <ows:Abstract>{{ process.abstract }}</ows:Abstract>{% for metadata in process.metadata %}
             <ows:Metadata xlink:title="{{ metadata.title }}" xlink:type="{{ metadata.type }}"
-						{% if metadata.href != None %}
-							xlink:href="{{ metadata.href }}"
-						{% endif %}
-						{% if metadata.role != None %}
-							xlink:role="{{ metadata.role }}"
-						{% endif %}
-						/>
-						{% endfor %}
+                        {% if metadata.href != None %}
+                            xlink:href="{{ metadata.href }}"
+                        {% endif %}
+                        {% if metadata.role != None %}
+                            xlink:role="{{ metadata.role }}"
+                        {% endif %}
+                        />
+                        {% endfor %}
             <ows:Keywords>{% for keyword in process.keywords %}
                 <ows:Keyword>{{ keyword }}</ows:Keyword>{% endfor %}
             </ows:Keywords>
         </wps:ProcessSummary>{% endfor %}
-	</wps:Contents>
+    </wps:Contents>
 </wps:Capabilities>


=====================================
pywps/validator/complexvalidator.py
=====================================
@@ -223,7 +223,7 @@ def validategeojson(data_input, mode):
     >>> from io import StringIO
     >>> class FakeInput(object):
     ...     json = open('point.geojson','w')
-    ...     json.write('''{"type":"Feature", "properties":{}, "geometry":{"type":"Point", "coordinates":[8.5781228542328, 22.87500500679]}, "crs":{"type":"name", "properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}}}''')  # noqa
+    ...     json.write('''{"type":"Feature", "properties":{}, "geometry":{"type":"Point", "coordinates":[8.5781228542328, 22.87500500679]}, "crs":{"type":"name", "properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}}}''')
     ...     json.close()
     ...     file = 'point.geojson'
     >>> class FakeDataFormat(object):
@@ -232,7 +232,7 @@ def validategeojson(data_input, mode):
     >>> fake_input.data_format = FakeDataFormat()
     >>> validategeojson(fake_input, MODE.SIMPLE)
     True
-    """
+    """ # noqa
 
     LOGGER.info('validating GeoJSON; Mode: {}'.format(mode))
     passed = False


=====================================
requirements-dev.txt
=====================================
@@ -2,7 +2,7 @@ bump2version
 coverage
 coveralls
 docutils
-flake8
+ruff
 pylint
 pytest
 pytest-cov


=====================================
requirements.txt
=====================================
@@ -2,6 +2,8 @@ markupsafe
 sqlalchemy
 fiona
 geotiff
+tifffile <=2025.5.10
+zarr <3
 humanize
 jinja2
 jsonschema
@@ -10,3 +12,5 @@ owslib
 python-dateutil
 requests
 werkzeug
+urllib3>=2.5.0 # not directly required, pinned by Snyk to avoid a vulnerability
+numpy>=1.22.2 # not directly required, pinned by Snyk to avoid a vulnerability


=====================================
setup.cfg
=====================================
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 4.6.0
+current_version = 4.7.0
 commit = False
 tag = False
 parse = (?P<major>\d+)\.(?P<minor>\d+).(?P<patch>\d+)
@@ -16,12 +16,3 @@ replace = {new_version}
 
 [coverage:run]
 relative_files = True
-
-[flake8]
-ignore = 
-	F401
-	E402
-	W606
-max-line-length = 120
-exclude = tests
-


=====================================
setup.py
=====================================
@@ -49,17 +49,17 @@ CONFIG = {
         "Operating System :: OS Independent",
         "Programming Language :: Python",
         "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 3.8",
-        "Programming Language :: Python :: 3.9",
         "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: 3.11",
+        "Programming Language :: Python :: 3.12",
+        "Programming Language :: Python :: 3.13",
         "Topic :: Scientific/Engineering :: GIS",
     ],
     "install_requires": INSTALL_REQUIRES,
     "extras_require": dict(
         dev=DEV_REQUIRES,
     ),
-    "python_requires": ">=3.8,<4",
+    "python_requires": ">=3.10,<4",
     "packages": find_packages(exclude=["docs", "tests.*", "tests"]),
     "include_package_data": True,
     "scripts": [],


=====================================
tests/test_assync.py
=====================================
@@ -27,6 +27,7 @@ class ExecuteTest(TestBase):
         # Running processes using the MultiProcessing scheduler and a file-based database
         configuration.CONFIG.set('processing', 'mode', 'distributed')
 
+    @pytest.mark.xfail(reason="async fails")
     def test_async(self):
         client = client_for(Service(processes=[Sleep()]))
         wps = WPSExecution()


=====================================
tests/validator/test_complexvalidators.py
=====================================
@@ -76,6 +76,14 @@ class ValidateTest(TestBase):
         self.assertTrue(validategml(gml_input, MODE.NONE), 'NONE validation')
         self.assertTrue(validategml(gml_input, MODE.SIMPLE), 'SIMPLE validation')
         self.assertTrue(validategml(gml_input, MODE.STRICT), 'STRICT validation')
+        # self.assertTrue(validategml(gml_input, MODE.VERYSTRICT), 'VERYSTRICT validation')
+        gml_input.stream.close()
+
+    @pytest.mark.xfail(reason="gml verystrict validation fails")
+    def test_gml_validator_verystrict(self):
+        """Test GML validator
+        """
+        gml_input = self.get_input('gml/point.gml', 'point.xsd', FORMATS.GML.mime_type)
         self.assertTrue(validategml(gml_input, MODE.VERYSTRICT), 'VERYSTRICT validation')
         gml_input.stream.close()
 


=====================================
tox.ini
=====================================
@@ -1,9 +1,19 @@
 [tox]
 min_version = 4.0
-envlist = py{37,38,39,310,311}{-extra,}
-requires = pip >= 20.0
+envlist =
+    py{310,311,312,313}{-extra,},
+    lint
+requires = pip >=25.2
 opts = --verbose
 
+[testenv:lint]
+skip_install = true
+extras =
+deps =
+    ruff
+commands =
+    ruff check pywps
+
 [testenv]
 setenv =
     PYTEST_ADDOPTS = "--color=yes"



View it on GitLab: https://salsa.debian.org/debian-gis-team/pywps/-/compare/ba9375579387045397abd1325fe9174874ffdc4f...d1f636918c2342381d5838f788abd692e49504ed

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/pywps/-/compare/ba9375579387045397abd1325fe9174874ffdc4f...d1f636918c2342381d5838f788abd692e49504ed
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20251215/9927be1c/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list