diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fec0546d..01f58e8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,15 +10,19 @@ on: jobs: test: runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.9', '3.14'] + fail-fast: false steps: - name: Checkout code uses: actions/checkout@v4 - - name: Set up Python + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: ${{ matrix.python-version }} - name: Install uv uses: astral-sh/setup-uv@v5 diff --git a/kmos/types.py b/kmos/types.py index 59167f10..7acc6afd 100644 --- a/kmos/types.py +++ b/kmos/types.py @@ -2156,7 +2156,10 @@ def prettify_xml(elem): sort_xml_attributes(elem) rough_string = ET.tostring(elem, encoding='utf-8') reparsed = minidom.parseString(rough_string) - return reparsed.toprettyxml(indent=' ') + pretty_xml = reparsed.toprettyxml(indent=' ') + # Unescape to newlines for consistency across Python versions + # (Python >= 3.14 escapes newlines in attributes, older versions don't) + return pretty_xml.replace(' ', '\n') def parse_chemical_expression(eq, process, project_tree): diff --git a/kmos/utils/__init__.py b/kmos/utils/__init__.py index b3f7efe6..4e08a836 100644 --- a/kmos/utils/__init__.py +++ b/kmos/utils/__init__.py @@ -388,21 +388,28 @@ def build(options): extra_flags = {} + # Add include path for src directory (needed for meson backend in Python >= 3.12) + # Use absolute path so meson can find include files from its temp build directory + if os.path.isdir('src'): + src_include = '-I' + os.path.abspath('src') + else: + src_include = '-I' + os.path.abspath('.') + if options.no_optimize: extra_flags['gfortran'] = ('-ffree-line-length-0 -ffree-form' ' -xf95-cpp-input -Wall -fimplicit-none' - ' -time -fmax-identifier-length=63') + ' -time -fmax-identifier-length=63 ' + src_include) extra_flags['gnu95'] = extra_flags['gfortran'] - extra_flags['intel'] = '-fpp -Wall -I/opt/intel/fc/10.1.018/lib' - extra_flags['intelem'] = '-fpp -Wall' + extra_flags['intel'] = '-fpp -Wall -I/opt/intel/fc/10.1.018/lib ' + src_include + extra_flags['intelem'] = '-fpp -Wall ' + src_include else: extra_flags['gfortran'] = ('-ffree-line-length-0 -ffree-form' ' -xf95-cpp-input -Wall -O3 -fimplicit-none' - ' -time -fmax-identifier-length=63') + ' -time -fmax-identifier-length=63 ' + src_include) extra_flags['gnu95'] = extra_flags['gfortran'] - extra_flags['intel'] = '-fast -fpp -Wall -I/opt/intel/fc/10.1.018/lib' - extra_flags['intelem'] = '-fast -fpp -Wall' + extra_flags['intel'] = '-fast -fpp -Wall -I/opt/intel/fc/10.1.018/lib ' + src_include + extra_flags['intelem'] = '-fast -fpp -Wall ' + src_include # FIXME extra_libs = '' @@ -444,6 +451,14 @@ def build(options): from numpy import f2py sys.argv = call os.environ["LIBRARY_PATH"] = os.environ.get("LIBRARY_PATH", "") + ":/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib" + + # Set FC for meson backend (Python >= 3.12) to compiler name only + # Meson has issues with FC containing full paths + if sys.version_info >= (3, 12): + fc_map = {'gfortran': 'gfortran', 'gnu95': 'gfortran', 'intel': 'ifort', 'intelem': 'ifort'} + if options.fcompiler in fc_map: + os.environ['FC'] = fc_map[options.fcompiler] + f2py.main() sys.argv = true_argv diff --git a/pyproject.toml b/pyproject.toml index 70fb0bcc..021ca146 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "kmos" -version = "0.4.1" # Managed by bump-my-version +version = "0.4.2" # Managed by bump-my-version description = "kMC modeling on steroids" readme = "README.md" requires-python = ">=3.9" @@ -31,6 +31,8 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Education", "Topic :: Scientific/Engineering :: Chemistry", "Topic :: Scientific/Engineering :: Physics", @@ -42,6 +44,8 @@ dependencies = [ "lxml", "numpy>=2.0.2", "pytest>=8.4.2", + "meson>=1.2.0", + "meson-python>=0.15.0", ] [project.urls] @@ -76,7 +80,7 @@ kmos = [ # Example: PYTHONPATH=. uv run pytest tests/ [tool.bumpversion] -current_version = "0.4.1" +current_version = "0.4.2" parse = "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)" serialize = ["{major}.{minor}.{patch}"] search = "{current_version}"