From 6508d1d29891a7b6afe09a5c661c735c8beff207 Mon Sep 17 00:00:00 2001 From: Seth G Date: Mon, 1 Apr 2024 18:25:52 +0200 Subject: [PATCH] Python MapScript updates for 8.2 Release (#7059) * Bump Python versions for CI * Use string for 3.10 * Update SWIG version * Switch to venv and use pip for install * Update README and remove Python 2.7 references * Switch back to virtualenv as venv is missing in Ubuntu CI. Use build for wheels rather than setup.py install * Add docstrings to shadow functions --- .github/workflows/build-mapscript-python.yml | 2 +- appveyor.yml | 6 +-- src/mapscript/python/CMakeLists.txt | 10 ++--- src/mapscript/python/README.rst | 42 +++++++++++--------- src/mapscript/python/pymodule.i | 20 ++++++++++ src/mapscript/python/requirements-dev.txt | 3 +- src/mapscript/python/setup.py.in | 1 - src/mapscript/swiginc/layer.i | 10 ++--- 8 files changed, 59 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build-mapscript-python.yml b/.github/workflows/build-mapscript-python.yml index 785bce95c0..8f13ca3107 100644 --- a/.github/workflows/build-mapscript-python.yml +++ b/.github/workflows/build-mapscript-python.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: [3.8, 3.9] + python-version: [3.8, 3.9, "3.10", 3.11, 3.12] env: MAPSCRIPT_PYTHON_ONLY: 'true' diff --git a/appveyor.yml b/appveyor.yml index 73dcce3f10..ce8a0de890 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,7 +11,7 @@ cache: environment: global: - SWIG_VER: swigwin-4.1.0 + SWIG_VER: swigwin-4.2.1 TWINE_USERNAME: mapserver TWINE_PASSWORD: secure: mHoJHeXdXbBNoDf7MA4ZEg== @@ -19,8 +19,6 @@ environment: # VS 2019 VS_VERSION: Visual Studio 16 2019 matrix: - - platform: x64 - Python_ROOT_DIR: c:/python37-x64 - platform: x64 Python_ROOT_DIR: c:/python38-x64 - platform: x64 @@ -29,6 +27,8 @@ environment: Python_ROOT_DIR: c:/python310-x64 - platform: x64 Python_ROOT_DIR: c:/python311-x64 + - platform: x64 + Python_ROOT_DIR: c:/python312-x64 matrix: fast_finish: true diff --git a/src/mapscript/python/CMakeLists.txt b/src/mapscript/python/CMakeLists.txt index ed3e55c8f0..7ab1d021b4 100644 --- a/src/mapscript/python/CMakeLists.txt +++ b/src/mapscript/python/CMakeLists.txt @@ -75,7 +75,7 @@ add_custom_command( DEPENDS mapscriptvenv.stamp OUTPUT mapscriptwheel.stamp WORKING_DIRECTORY ${OUTPUT_FOLDER} - COMMAND ${Python_VENV_SCRIPTS}/python setup.py bdist_wheel > wheel_build.log + COMMAND ${Python_VENV_SCRIPTS}/python -m build --wheel > wheel_build.log COMMENT "Building the mapscript Python wheel" ) @@ -108,21 +108,21 @@ add_custom_command( COMMENT "Copying files required to build Mapscript" ) -install( +install( CODE " SET(ENV{PYTHONPATH} \${Python_SITELIB}:\$ENV{PYTHONPATH}) - + if(DEFINED ENV{DESTDIR}) SET(PYTHON_ROOT \"--root=\$ENV{DESTDIR}\") endif() - + if(DEFINED CMAKE_INSTALL_PREFIX) SET(PYTHON_PREFIX \"--prefix=\${CMAKE_INSTALL_PREFIX}\") endif() execute_process( - COMMAND ${Python_EXECUTABLE} setup.py install \${PYTHON_ROOT} \${PYTHON_PREFIX} + COMMAND ${Python_EXECUTABLE} -m pip install \${PYTHON_ROOT} \${PYTHON_PREFIX} WORKING_DIRECTORY ${OUTPUT_FOLDER} ) " diff --git a/src/mapscript/python/README.rst b/src/mapscript/python/README.rst index 65224e7df9..93214424d9 100644 --- a/src/mapscript/python/README.rst +++ b/src/mapscript/python/README.rst @@ -2,13 +2,13 @@ Python MapScript for MapServer README ===================================== :Author: MapServer Team -:Last Updated: 2021-01-29 +:Last Updated: 2024-03-29 Introduction ------------ The Python mapscript module provides users an interface to `MapServer `_ -classes on any platform, and has been tested on Python versions 2.7 and 3.5+. +classes on any platform, and has been tested on Python 3.8+. The Python mapscript module is created using `SWIG `_ the the Simplified Wrapper and Interface Generator. This is used to create MapServer bindings in @@ -38,13 +38,13 @@ Advantages of ready-made wheels on PyPI include: Wheels are built based on the `Appveyor build environments `_. These are as follows at the time of writing: -+ Python 2.7 x32 -+ Python 2.7 x64 -+ Python 3.6 x64 -+ Python 3.7 x64 + Python 3.8 x64 ++ Python 3.9 x64 ++ Python 3.10 x64 ++ Python 3.11 x64 ++ Python 3.12 x64 -The mapscript wheels have been compiled using Visual Studio 2017 version 15 (``MSVC++ 14.11 _MSC_VER == 1911``). +The mapscript wheels have been compiled using Visual Studio 2022 version 17 (``MSVC++ 17.9 _MSC_VER == 1939``). Linux Wheels may also be available in the future using the `manylinux `_ project. No source distributions will be provided on PyPI - to build from source requires the full MapServer source code, @@ -79,6 +79,12 @@ If several folders are required (e.g. GDAL DLLs) multiple paths can be provided SET MAPSERVER_DLL_PATH=C:\MapServer\bin;C:\GDAL\bin +In PowerShell you can set this as follows: + +.. code-block:: ps1 + + $env:MAPSERVER_DLL_PATH="C:\MapServer\bin" + For Earlier Python Versions +++++++++++++++++++++++++++ @@ -93,14 +99,14 @@ Windows Binaries ++++++++++++++++ Windows binary packages can be downloaded from `GIS Internals `_. -To ensure compatibility with the wheels, please use identical release packages, e.g. ``release-1928-x64-gdal-3-2-mapserver-7-6`` -for mapscript 7.6. +To ensure compatibility with the wheels, please use identical release packages, e.g. ``release-1930-x64-gdal-3-8-4-mapserver-8-0-1`` +for mapscript 8.0.1. .. NOTE:: `MS4W `_ (MapServer for Windows) is a full installer that contains Python & Python MapScript already configured out-of-the-box, as well as default OGC web services and over 60 working mapfiles. -When using these packages the MapServer path will be similar to ``C:\release-1911-x64-gdal-2-3-mapserver-7-2\bin``. +When using these packages the MapServer path will be similar to ``C:\release-1930-x64-gdal-3-8-4-mapserver-8-0-1\bin``. Prior to installing mapscript it is recommended to update pip to the latest version with the following command: @@ -125,17 +131,17 @@ Now you should be able to import mapscript: .. code-block:: python python -c "import mapscript;print(mapscript.msGetVersion())" - MapServer version 7.6.0 OUTPUT=PNG OUTPUT=JPEG OUTPUT=KML SUPPORTS=PROJ SUPPORTS=AGG SUPPORTS=FREETYPE SUPPORTS=CAIRO SUPPORTS=SVG_SYMBOLS SUPPORTS=SVGCAIRO SUPPORTS=ICONV SUPPORTS=FRIBIDI SUPPORTS=WMS_SERVER SUPPORTS=WMS_CLIENT SUPPORTS=WFS_SERVER SUPPORTS=WFS_CLIENT SUPPORTS=WCS_SERVER SUPPORTS=SOS_SERVER SUPPORTS=FASTCGI SUPPORTS=THREADS SUPPORTS=GEOS SUPPORTS=PBF INPUT=JPEG INPUT=POSTGIS INPUT=OGR INPUT=GDAL INPUT=SHAPEFILE + MapServer version 8.0.1 PROJ version 9.3 GDAL version 3.9 OUTPUT=PNG OUTPUT=JPEG SUPPORTS=PROJ SUPPORTS=AGG SUPPORTS=FREETYPE SUPPORTS=CAIRO SUPPORTS=SVG_SYMBOLS SUPPORTS=SVGCAIRO SUPPORTS=ICONV SUPPORTS=FRIBIDI SUPPORTS=WMS_SERVER SUPPORTS=WMS_CLIENT SUPPORTS=WFS_SERVER SUPPORTS=WFS_CLIENT SUPPORTS=WCS_SERVER SUPPORTS=OGCAPI_SERVER SUPPORTS=FASTCGI SUPPORTS=THREADS SUPPORTS=GEOS SUPPORTS=PBF INPUT=JPEG INPUT=POSTGIS INPUT=OGR INPUT=GDAL INPUT=SHAPEFILE INPUT=FLATGEOBUF Installation on Unix -------------------- -For Unix users there are two approaches to installing mapscript. The first is to install the ``python-mapscript`` package using a package manager. For example on +For Unix users there are two approaches to installing mapscript. The first is to install the ``python3-mapscript`` package using a package manager. For example on Ubuntu the following command can be used: .. code-block:: bat - sudo apt-get install python-mapscript + sudo apt-get install python3-mapscript The second approach is to build and install the Python mapscript module from source. Full details on compiling MapServer from source are detailed on the `Compiling on Unix `_ page. To make sure Python mapscript is built alongside MapServer the following flag needs to be set: @@ -175,7 +181,6 @@ If the mapscript library is not on your ``PYTHONPATH`` you may see one of the fo .. code-block:: python - ImportError: No module named _mapscript # Python 2.x ModuleNotFoundError: No module named '_mapscript' # Python 3.x If the ``MapServer.dll`` cannot be found in your system paths (or ``MAPSERVER_DLL_PATH`` environment variable when using Python 3.8 @@ -257,8 +262,7 @@ suite. This process runs commands similar to the following: .. code-block:: bat - python -m pip install virtualenv - virtualenv mapscriptvenv + python -m venv mapscriptvenv python -m pip install --upgrade pip pip install -r requirements-dev.txt python setup.py bdist_wheel @@ -291,12 +295,12 @@ The mapscript module includes a test suite and a small sample dataset to check t pip install pytest -Make sure the MapServer binaries are on the system path, and that the PROJ_LIB variable has been set as this is required for many of the tests. +Make sure the MapServer binaries are on the system path, and that the PROJ_DATA variable has been set as this is required for many of the tests. .. code-block:: bat - SET PATH=C:\release-1928-x64-gdal-3-2-mapserver-7-6\bin;%PATH% - SET PROJ_LIB=C:\release-1928-x64-gdal-3-2-mapserver-7-6\bin\proj\SHARE + SET PATH=C:\release-1930-x64-gdal-3-8-4-mapserver-8-0-1\bin;%PATH% + SET PROJ_DATA=C:\release-1930-x64-gdal-3-8-4-mapserver-8-0-1\bin\proj\SHARE Finally run the command below to run the test suite: diff --git a/src/mapscript/python/pymodule.i b/src/mapscript/python/pymodule.i index 1c0509495b..d0d3dfaeca 100644 --- a/src/mapscript/python/pymodule.i +++ b/src/mapscript/python/pymodule.i @@ -271,12 +271,21 @@ MapServerChildError = _mapscript.MapServerChildError %feature("shadow") insertClass %{ def insertClass(*args): + """ + Insert a **copy** of the class into the layer at the requested *index*. + Default index of -1 means insertion at the end of the array of classes. Returns the index at which the class was inserted. + """ actualIndex=$action(*args) args[1].p_layer=args[0] return actualIndex%} + %feature("shadow") getClass %{ def getClass(*args): + """ + Fetch the requested class object at *i*. Returns NULL if the class index is out of the legal range. + The numclasses field contains the number of classes available, and the first class is index 0. + """ clazz = $action(*args) if clazz: if args and len(args)!=0: @@ -287,12 +296,20 @@ MapServerChildError = _mapscript.MapServerChildError %feature("shadow") insertLayer %{ def insertLayer(*args): + """ + Insert a copy of *layer* into the map at *index*. + The default value of *index* is -1, which means the last possible index. + Returns the index of the new layer, or -1 in the case of a failure. + """ actualIndex=$action(*args) args[1].p_map=args[0] return actualIndex%} %feature("shadow") getLayer %{ def getLayer(*args): + """ + Returns a reference to the layer at index *i*. + """ layer = $action(*args) if layer: if args and len(args)!=0: @@ -303,6 +320,9 @@ MapServerChildError = _mapscript.MapServerChildError %feature("shadow") getLayerByName %{ def getLayerByName(*args): + """ + Returns a reference to the named layer. + """ layer = $action(*args) if layer: if args and len(args)!=0: diff --git a/src/mapscript/python/requirements-dev.txt b/src/mapscript/python/requirements-dev.txt index 23c49907aa..405e52c12e 100644 --- a/src/mapscript/python/requirements-dev.txt +++ b/src/mapscript/python/requirements-dev.txt @@ -1,4 +1,5 @@ pytest pillow wheel>=0.38.0 -setuptools>=45.0.0 \ No newline at end of file +setuptools>=45.0.0 +build[virtualenv] \ No newline at end of file diff --git a/src/mapscript/python/setup.py.in b/src/mapscript/python/setup.py.in index 10b12cd28e..2d621cc9c1 100644 --- a/src/mapscript/python/setup.py.in +++ b/src/mapscript/python/setup.py.in @@ -28,7 +28,6 @@ setup( 'Intended Audience :: Science/Research', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: C', 'Programming Language :: C++', diff --git a/src/mapscript/swiginc/layer.i b/src/mapscript/swiginc/layer.i index 2b660738fc..f0cc1a757d 100644 --- a/src/mapscript/swiginc/layer.i +++ b/src/mapscript/swiginc/layer.i @@ -314,12 +314,12 @@ /// The numclasses field contains the number of classes available, and the first class is index 0. classObj *getClass(int i) { - classObj *result=NULL; + classObj *result=NULL; if (i >= 0 && i < self->numclasses) { - result=self->class[i]; - MS_REFCNT_INCR(result); - } - return result; + result=self->class[i]; + MS_REFCNT_INCR(result); + } + return result; } /// Returns the requested item. Items are attribute fields, and this method returns the