diff --git a/HISTORY.rst b/HISTORY.rst index 5b525106a..75dab1ab3 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -65,6 +65,7 @@ Unreleased Changes * Add support for latest GDAL versions; remove test-specific constraint on GDAL versions from invest requirements. https://github.com/natcap/invest/issues/916 + * Updated to Cython 3 (https://github.com/natcap/invest/issues/556) * Annual Water Yield * Added the results_suffix to a few intermediate files where it was missing. https://github.com/natcap/invest/issues/1517 diff --git a/pyproject.toml b/pyproject.toml index 3ce0308f1..fadad2960 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,7 @@ invest = "natcap.invest.cli:main" # that we can provide a much easier build experience so long as GDAL is # available at runtime. requires = [ - 'setuptools>=61', 'wheel', 'setuptools_scm>=8.0', 'cython', 'babel', + 'setuptools>=61', 'wheel', 'setuptools_scm>=8.0', 'cython>=3.0.0', 'babel', 'oldest-supported-numpy' ] build-backend = "setuptools.build_meta" diff --git a/requirements-dev.txt b/requirements-dev.txt index 2a5c20881..63e178ef7 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -10,7 +10,6 @@ # scripts/convert-requirements-to-conda-yml.py as though it can only be found # on pip. -Cython<3.0.0 virtualenv>=12.0.1 pytest pytest-subtests diff --git a/setup.py b/setup.py index a05b74314..1709e3318 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ import platform import subprocess -import Cython.Build +from Cython.Build import cythonize import numpy from setuptools import setup from setuptools.command.build_py import build_py as _build_py @@ -46,14 +46,14 @@ def run(self): setup( install_requires=_REQUIREMENTS, - ext_modules=[ + ext_modules=cythonize([ Extension( name=f'natcap.invest.{package}.{module}', sources=[f'src/natcap/invest/{package}/{module}.pyx'], - include_dirs=[numpy.get_include()], extra_compile_args=compiler_and_linker_args, extra_link_args=compiler_and_linker_args, - language='c++' + language='c++', + define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")] ) for package, module in [ ('delineateit', 'delineateit_core'), ('recreation', 'out_of_core_quadtree'), @@ -62,9 +62,9 @@ def run(self): ('sdr', 'sdr_core'), ('seasonal_water_yield', 'seasonal_water_yield_core') ] - ], + ], compiler_directives={'language_level': '3'}), + include_dirs=[numpy.get_include()], cmdclass={ - 'build_ext': Cython.Build.build_ext, 'build_py': build_py } ) diff --git a/src/natcap/invest/delineateit/delineateit_core.pyx b/src/natcap/invest/delineateit/delineateit_core.pyx index 979423e62..0b229a814 100644 --- a/src/natcap/invest/delineateit/delineateit_core.pyx +++ b/src/natcap/invest/delineateit/delineateit_core.pyx @@ -1,4 +1,3 @@ -# cython: language_level=3 import numpy import pygeoprocessing cimport numpy @@ -105,5 +104,3 @@ cpdef cset[cpair[double, double]] calculate_pour_point_array( # return set of (x, y) coordinates referenced to the same coordinate system # as the original raster return pour_points - - diff --git a/src/natcap/invest/ndr/ndr_core.pyx b/src/natcap/invest/ndr/ndr_core.pyx index f39d35b03..d921b1bc5 100644 --- a/src/natcap/invest/ndr/ndr_core.pyx +++ b/src/natcap/invest/ndr/ndr_core.pyx @@ -1,5 +1,3 @@ -# cython: profile=False -# cython: language_level=2 import tempfile import logging import os @@ -129,9 +127,9 @@ cdef class _ManagedRaster: self.block_xbits = numpy.log2(self.block_xsize) self.block_ybits = numpy.log2(self.block_ysize) self.block_nx = ( - self.raster_x_size + (self.block_xsize) - 1) / self.block_xsize + self.raster_x_size + (self.block_xsize) - 1) // self.block_xsize self.block_ny = ( - self.raster_y_size + (self.block_ysize) - 1) / self.block_ysize + self.raster_y_size + (self.block_ysize) - 1) // self.block_ysize self.lru_cache = new LRUCache[int, double*](MANAGED_RASTER_N_BLOCKS) self.raster_path = raster_path @@ -197,7 +195,7 @@ cdef class _ManagedRaster: if dirty_itr != self.dirty_blocks.end(): self.dirty_blocks.erase(dirty_itr) block_xi = block_index % self.block_nx - block_yi = block_index / self.block_nx + block_yi = block_index // self.block_nx # we need the offsets to subtract from global indexes for # cached array @@ -261,7 +259,7 @@ cdef class _ManagedRaster: cdef void _load_block(self, int block_index) except *: cdef int block_xi = block_index % self.block_nx - cdef int block_yi = block_index / self.block_nx + cdef int block_yi = block_index // self.block_nx # we need the offsets to subtract from global indexes for cached array cdef int xoff = block_xi << self.block_xbits @@ -322,7 +320,7 @@ cdef class _ManagedRaster: self.dirty_blocks.erase(dirty_itr) block_xi = block_index % self.block_nx - block_yi = block_index / self.block_nx + block_yi = block_index // self.block_nx xoff = block_xi << self.block_xbits yoff = block_yi << self.block_ybits @@ -488,7 +486,7 @@ def ndr_eff_calculation( # hasn't already been set for processing. flat_index = processing_stack.top() processing_stack.pop() - global_row = flat_index / n_cols + global_row = flat_index // n_cols global_col = flat_index % n_cols crit_len = crit_len_raster.get(global_col, global_row) diff --git a/src/natcap/invest/recreation/out_of_core_quadtree.pyx b/src/natcap/invest/recreation/out_of_core_quadtree.pyx index 89e5a2f95..bc5369617 100644 --- a/src/natcap/invest/recreation/out_of_core_quadtree.pyx +++ b/src/natcap/invest/recreation/out_of_core_quadtree.pyx @@ -1,5 +1,3 @@ -# cython: profile=True -# cython: language_level=2 """A hierarchical spatial index for fast culling of points in 2D space.""" import os @@ -22,7 +20,7 @@ from osgeo import osr cimport numpy MAX_BYTES_TO_BUFFER = 2**27 # buffer a little over 128 megabytes -import buffered_numpy_disk_map +from natcap.invest.recreation import buffered_numpy_disk_map _ARRAY_TUPLE_TYPE = ( buffered_numpy_disk_map.BufferedNumpyDiskMap._ARRAY_TUPLE_TYPE) diff --git a/src/natcap/invest/scenic_quality/viewshed.pyx b/src/natcap/invest/scenic_quality/viewshed.pyx index 29ff551d2..1d9c48943 100644 --- a/src/natcap/invest/scenic_quality/viewshed.pyx +++ b/src/natcap/invest/scenic_quality/viewshed.pyx @@ -1,5 +1,3 @@ -# coding=UTF-8 -# cython: language_level=2 """ Implements the Wang et al (2000) viewshed based on reference planes. @@ -307,9 +305,9 @@ cdef class _ManagedRaster: self.block_xbits = numpy.log2(self.block_xsize) self.block_ybits = numpy.log2(self.block_ysize) self.block_nx = ( - self.raster_x_size + (self.block_xsize) - 1) / self.block_xsize + self.raster_x_size + (self.block_xsize) - 1) // self.block_xsize self.block_ny = ( - self.raster_y_size + (self.block_ysize) - 1) / self.block_ysize + self.raster_y_size + (self.block_ysize) - 1) // self.block_ysize self.lru_cache = new LRUCache[int, double*](MANAGED_RASTER_N_BLOCKS) self.raster_path = raster_path @@ -375,7 +373,7 @@ cdef class _ManagedRaster: if dirty_itr != self.dirty_blocks.end(): self.dirty_blocks.erase(dirty_itr) block_xi = block_index % self.block_nx - block_yi = block_index / self.block_nx + block_yi = block_index // self.block_nx # we need the offsets to subtract from global indexes for # cached array @@ -439,7 +437,7 @@ cdef class _ManagedRaster: cdef void _load_block(self, int block_index) except *: cdef int block_xi = block_index % self.block_nx - cdef int block_yi = block_index / self.block_nx + cdef int block_yi = block_index // self.block_nx # we need the offsets to subtract from global indexes for cached array cdef int xoff = block_xi << self.block_xbits @@ -500,7 +498,7 @@ cdef class _ManagedRaster: self.dirty_blocks.erase(dirty_itr) block_xi = block_index % self.block_nx - block_yi = block_index / self.block_nx + block_yi = block_index // self.block_nx xoff = block_xi << self.block_xbits yoff = block_yi << self.block_ybits diff --git a/src/natcap/invest/sdr/sdr_core.pyx b/src/natcap/invest/sdr/sdr_core.pyx index 45b71b25b..69fc7500e 100644 --- a/src/natcap/invest/sdr/sdr_core.pyx +++ b/src/natcap/invest/sdr/sdr_core.pyx @@ -1,5 +1,3 @@ -# cython: profile=False -# cython: language_level=3 import logging import os @@ -196,7 +194,7 @@ cdef class _ManagedRaster: if dirty_itr != self.dirty_blocks.end(): self.dirty_blocks.erase(dirty_itr) block_xi = block_index % self.block_nx - block_yi = block_index / self.block_nx + block_yi = block_index // self.block_nx # we need the offsets to subtract from global indexes for # cached array diff --git a/src/natcap/invest/seasonal_water_yield/seasonal_water_yield_core.pyx b/src/natcap/invest/seasonal_water_yield/seasonal_water_yield_core.pyx index 6c6aad9ea..7902c3243 100644 --- a/src/natcap/invest/seasonal_water_yield/seasonal_water_yield_core.pyx +++ b/src/natcap/invest/seasonal_water_yield/seasonal_water_yield_core.pyx @@ -1,5 +1,3 @@ -# cython: profile=False -# cython: language_level=2 import logging import os import collections @@ -146,9 +144,9 @@ cdef class _ManagedRaster: self.block_xbits = numpy.log2(self.block_xsize) self.block_ybits = numpy.log2(self.block_ysize) self.block_nx = ( - self.raster_x_size + (self.block_xsize) - 1) / self.block_xsize + self.raster_x_size + (self.block_xsize) - 1) // self.block_xsize self.block_ny = ( - self.raster_y_size + (self.block_ysize) - 1) / self.block_ysize + self.raster_y_size + (self.block_ysize) - 1) // self.block_ysize self.lru_cache = new LRUCache[int, double*](MANAGED_RASTER_N_BLOCKS) self.raster_path = raster_path @@ -214,7 +212,7 @@ cdef class _ManagedRaster: if dirty_itr != self.dirty_blocks.end(): self.dirty_blocks.erase(dirty_itr) block_xi = block_index % self.block_nx - block_yi = block_index / self.block_nx + block_yi = block_index // self.block_nx # we need the offsets to subtract from global indexes for # cached array @@ -278,7 +276,7 @@ cdef class _ManagedRaster: cdef void _load_block(self, int block_index) except *: cdef int block_xi = block_index % self.block_nx - cdef int block_yi = block_index / self.block_nx + cdef int block_yi = block_index // self.block_nx # we need the offsets to subtract from global indexes for cached array cdef int xoff = block_xi << self.block_xbits @@ -339,7 +337,7 @@ cdef class _ManagedRaster: self.dirty_blocks.erase(dirty_itr) block_xi = block_index % self.block_nx - block_yi = block_index / self.block_nx + block_yi = block_index // self.block_nx xoff = block_xi << self.block_xbits yoff = block_yi << self.block_ybits