From 7e769e7d15d408b86ac5d28d14c7ced1aacaad1f Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 22 Jan 2025 23:09:34 +0700 Subject: [PATCH 01/13] Test pip editable install with meson --- .github/workflows/ci-meson.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml index ab073aae87c..5d7d4b05cd5 100644 --- a/.github/workflows/ci-meson.yml +++ b/.github/workflows/ci-meson.yml @@ -24,6 +24,7 @@ jobs: matrix: os: [ubuntu] python: ['3.11', '3.12'] + editable: [false, true] steps: - uses: actions/checkout@v4 @@ -70,7 +71,7 @@ jobs: export CC="ccache $CC" export CXX="ccache $CXX" # Use --no-deps and pip check below to verify that all necessary dependencies are installed via conda - pip install --no-build-isolation --no-deps --config-settings=builddir=builddir . -v + pip install --no-build-isolation --no-deps --config-settings=builddir=builddir ${{ matrix.editable == 'true' && '--editable' || '' }} . -v - name: Verify dependencies shell: bash -l {0} From fc4d1a3b434e522826770182eea61974218f64e7 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 22 Jan 2025 23:14:33 +0700 Subject: [PATCH 02/13] Add editable status to name --- .github/workflows/ci-meson.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml index 5d7d4b05cd5..125fe792ee3 100644 --- a/.github/workflows/ci-meson.yml +++ b/.github/workflows/ci-meson.yml @@ -16,7 +16,7 @@ concurrency: jobs: test: - name: Conda (${{ matrix.os }}, Python ${{ matrix.python }}) + name: Conda (${{ matrix.os }}, Python ${{ matrix.python }}, editable: ${{ matrix.editable }}) runs-on: ${{ matrix.os }}-latest strategy: From 99e92212ecfb32b2c9dc2c333310e343fb1ad3a1 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 22 Jan 2025 23:14:54 +0700 Subject: [PATCH 03/13] Fix bug? --- .github/workflows/ci-meson.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml index 125fe792ee3..42bb3dfe902 100644 --- a/.github/workflows/ci-meson.yml +++ b/.github/workflows/ci-meson.yml @@ -71,7 +71,7 @@ jobs: export CC="ccache $CC" export CXX="ccache $CXX" # Use --no-deps and pip check below to verify that all necessary dependencies are installed via conda - pip install --no-build-isolation --no-deps --config-settings=builddir=builddir ${{ matrix.editable == 'true' && '--editable' || '' }} . -v + pip install --no-build-isolation --no-deps --config-settings=builddir=builddir ${{ matrix.editable && '--editable' || '' }} . -v - name: Verify dependencies shell: bash -l {0} From ec4531f73a44eb5afe8ed79350222557c6ec2094 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 22 Jan 2025 23:17:25 +0700 Subject: [PATCH 04/13] Fix another bug --- .github/workflows/ci-meson.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml index 42bb3dfe902..6d82586663d 100644 --- a/.github/workflows/ci-meson.yml +++ b/.github/workflows/ci-meson.yml @@ -16,7 +16,7 @@ concurrency: jobs: test: - name: Conda (${{ matrix.os }}, Python ${{ matrix.python }}, editable: ${{ matrix.editable }}) + name: 'Conda (${{ matrix.os }}, Python ${{ matrix.python }}, editable: ${{ matrix.editable }})' runs-on: ${{ matrix.os }}-latest strategy: From fba9bf7e12c5175fcd854baa712db41ca6aa63e5 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Thu, 23 Jan 2025 00:18:05 +0700 Subject: [PATCH 05/13] Hope this works --- .github/workflows/ci-meson.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml index 6d82586663d..6ab8ced8b34 100644 --- a/.github/workflows/ci-meson.yml +++ b/.github/workflows/ci-meson.yml @@ -81,7 +81,9 @@ jobs: shell: bash -l {0} run: | # We don't install sage_setup, so don't try to test it - rm -R ./src/sage_setup/ + # If editable then deleting the directory will cause sage to detect rebuild, which will cause ninja to fail + # so we don't delete the directory in this case + ${{ matrix.editable && 'true' || 'rm -R ./src/sage_setup/' }} ./sage -t --all -p4 - name: Upload log From 4d5e89d6ab4e9b0600623a4f4861e5203f83d9a3 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Thu, 23 Jan 2025 07:53:14 +0700 Subject: [PATCH 06/13] Better name --- .github/workflows/ci-meson.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml index 6ab8ced8b34..56d01aaffbb 100644 --- a/.github/workflows/ci-meson.yml +++ b/.github/workflows/ci-meson.yml @@ -16,7 +16,7 @@ concurrency: jobs: test: - name: 'Conda (${{ matrix.os }}, Python ${{ matrix.python }}, editable: ${{ matrix.editable }})' + name: Conda (${{ matrix.os }}, Python ${{ matrix.python }}${{ matrix.editable && ', editable' || '' }}) runs-on: ${{ matrix.os }}-latest strategy: From c0e44723978a47703f89909589bd36c1b37b7579 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 1 Feb 2025 17:39:08 +0700 Subject: [PATCH 07/13] Avoid issues with artifact name collision --- .github/workflows/ci-meson.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml index 56d01aaffbb..930eca0f918 100644 --- a/.github/workflows/ci-meson.yml +++ b/.github/workflows/ci-meson.yml @@ -90,5 +90,5 @@ jobs: uses: actions/upload-artifact@v4.5.0 if: failure() with: - name: ${{ runner.os }}-meson-${{ matrix.python }}-log + name: ${{ runner.os }}-meson-${{ matrix.python }}${{ matrix.editable && '-editable' || '' }}-log path: builddir/meson-logs/ From e366e2a7607971e4943e512898e23a5d769e464c Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 1 Feb 2025 18:01:49 +0700 Subject: [PATCH 08/13] Use import_module instead of find_spec --- src/sage/misc/dev_tools.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sage/misc/dev_tools.py b/src/sage/misc/dev_tools.py index b11b2078129..2a49310d66a 100644 --- a/src/sage/misc/dev_tools.py +++ b/src/sage/misc/dev_tools.py @@ -171,7 +171,7 @@ def load_submodules(module=None, exclude_pattern=None): load sage.geometry.polyhedron.ppl_lattice_polygon... succeeded """ from .package_dir import walk_packages - import importlib.util + import importlib if module is None: import sage @@ -195,12 +195,8 @@ def load_submodules(module=None, exclude_pattern=None): try: sys.stdout.write("load %s..." % module_name) sys.stdout.flush() - # see - # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly - spec = importer.find_spec(module_name) - module = importlib.util.module_from_spec(spec) - sys.modules[module_name] = module - spec.loader.exec_module(module) + module = importlib.import_module(module_name) + assert sys.modules[module_name] is module sys.stdout.write(" succeeded\n") except (ValueError, AttributeError, TypeError, ImportError): # we might get error because of cython code that has been From 40c0b191526f9ff7246cb55f3ddfce970db0697a Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Tue, 11 Feb 2025 23:59:10 +0700 Subject: [PATCH 09/13] Fix more doctests in meson_editable install --- src/sage/doctest/external.py | 4 +++ src/sage/doctest/sources.py | 10 +++++++ src/sage/env.py | 4 +++ src/sage/features/meson_editable.py | 46 +++++++++++++++++++++++++++++ src/sage/misc/package_dir.py | 36 ++++++++++++++++++++-- 5 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/sage/features/meson_editable.py diff --git a/src/sage/doctest/external.py b/src/sage/doctest/external.py index 45b3987de05..2cc374f0763 100644 --- a/src/sage/doctest/external.py +++ b/src/sage/doctest/external.py @@ -366,6 +366,10 @@ def external_features(): r""" Generate the features that are only to be tested if ``--optional=external`` is used. + .. SEEALSO:: + + :func:`sage.features.all.all_features` + EXAMPLES:: sage: from sage.doctest.external import external_features diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index 7589f62922b..01d24d67634 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -87,8 +87,18 @@ def get_basename(path): sage: import os sage: get_basename(sage.doctest.sources.__file__) 'sage.doctest.sources' + + :: + + sage: # optional - !meson_editable sage: get_basename(os.path.join(sage.structure.__path__[0], 'element.pxd')) 'sage.structure.element.pxd' + + TESTS:: + + sage: # optional - meson_editable + sage: get_basename(os.path.join(os.path.dirname(sage.structure.__file__), 'element.pxd')) + 'sage.structure.element.pxd' """ if path is None: return None diff --git a/src/sage/env.py b/src/sage/env.py index 060eb2209a9..826c0343ac8 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -319,6 +319,10 @@ def sage_include_directories(use_sources=False): sage: dirs = sage.env.sage_include_directories(use_sources=True) sage: any(os.path.isfile(os.path.join(d, file)) for d in dirs) True + + :: + + sage: # optional - !meson_editable (no need, see :issue:`39275`) sage: dirs = sage.env.sage_include_directories(use_sources=False) sage: any(os.path.isfile(os.path.join(d, file)) for d in dirs) True diff --git a/src/sage/features/meson_editable.py b/src/sage/features/meson_editable.py new file mode 100644 index 00000000000..25969e562b4 --- /dev/null +++ b/src/sage/features/meson_editable.py @@ -0,0 +1,46 @@ +r""" +Feature for testing if Meson editable install is used. +""" + +import sys +from . import Feature, FeatureTestResult + + +class MesonEditable(Feature): + r""" + A :class:`~sage.features.Feature` describing if Meson editable install is used. + + EXAMPLES:: + + sage: from sage.features.meson_editable import MesonEditable + sage: MesonEditable() + Feature('meson_editable') + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.meson_editable import MesonEditable + sage: MesonEditable() is MesonEditable() + True + """ + Feature.__init__(self, 'meson_editable') + + def _is_present(self): + r""" + Test whether Meson editable install is used. + + EXAMPLES:: + + sage: from sage.features.meson_editable import MesonEditable + sage: Internet()._is_present() # random + FeatureTestResult('meson_editable', True) + """ + import sage + import sys + result = type(sage.__loader__).__module__ == '_sagemath_editable_loader' + return FeatureTestResult(self, result) + + +def all_features(): + return [MesonEditable()] diff --git a/src/sage/misc/package_dir.py b/src/sage/misc/package_dir.py index 6b6ca12803f..b9742a6d1c2 100644 --- a/src/sage/misc/package_dir.py +++ b/src/sage/misc/package_dir.py @@ -261,6 +261,7 @@ def is_package_or_sage_namespace_package_dir(path, *, distribution_filter=None): :mod:`sage.cpython` is an ordinary package:: + sage: # optional - !meson_editable sage: from sage.misc.package_dir import is_package_or_sage_namespace_package_dir sage: directory = sage.cpython.__path__[0]; directory '.../sage/cpython' @@ -270,24 +271,48 @@ def is_package_or_sage_namespace_package_dir(path, *, distribution_filter=None): :mod:`sage.libs.mpfr` only has an ``__init__.pxd`` file, but we consider it a package directory for consistency with Cython:: + sage: # optional - !meson_editable sage: directory = os.path.join(sage.libs.__path__[0], 'mpfr'); directory '.../sage/libs/mpfr' - sage: is_package_or_sage_namespace_package_dir(directory) # known bug (seen in build.yml) + sage: is_package_or_sage_namespace_package_dir(directory) True :mod:`sage` is designated to become an implicit namespace package:: + sage: # optional - !meson_editable sage: directory = sage.__path__[0]; directory '.../sage' - sage: is_package_or_sage_namespace_package_dir(directory) # known bug (seen in build.yml) + sage: is_package_or_sage_namespace_package_dir(directory) True Not a package:: + sage: # optional - !meson_editable sage: directory = os.path.join(sage.symbolic.__path__[0], 'ginac'); directory # needs sage.symbolic '.../sage/symbolic/ginac' sage: is_package_or_sage_namespace_package_dir(directory) # needs sage.symbolic False + + TESTS:: + + sage: # optional - meson_editable + sage: from sage.misc.package_dir import is_package_or_sage_namespace_package_dir + sage: directory = os.path.dirname(sage.cpython.__file__); directory + '.../sage/cpython' + sage: is_package_or_sage_namespace_package_dir(directory) + True + + sage: # optional - meson_editable + sage: directory = os.path.join(os.path.dirname(sage.libs.__file__), 'mpfr'); directory + '.../sage/libs/mpfr' + sage: is_package_or_sage_namespace_package_dir(directory) + True + + sage: # optional - meson_editable, sage.symbolic + sage: directory = os.path.join(os.path.dirname(sage.symbolic.__file__), 'ginac'); directory + '.../sage/symbolic/ginac' + sage: is_package_or_sage_namespace_package_dir(directory) + False """ if os.path.exists(os.path.join(path, '__init__.py')): # ordinary package return True @@ -345,8 +370,15 @@ def walk_packages(path=None, prefix='', onerror=None): EXAMPLES:: + sage: # optional - !meson_editable sage: sorted(sage.misc.package_dir.walk_packages(sage.misc.__path__)) # a namespace package [..., ModuleInfo(module_finder=FileFinder('.../sage/misc'), name='package_dir', ispkg=False), ...] + + TESTS:: + + sage: # optional - meson_editable + sage: sorted(sage.misc.package_dir.walk_packages(sage.misc.__path__)) + [..., ModuleInfo(module_finder=<...MesonpyPathFinder object...>, name='package_dir', ispkg=False), ...] """ # Adapted from https://github.com/python/cpython/blob/3.11/Lib/pkgutil.py From 88ecd7a9ca5c8a71fdda60e0735296a86417ac2c Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 12 Feb 2025 10:20:27 +0700 Subject: [PATCH 10/13] Revert "Fix more doctests in meson_editable install" This reverts commit 40c0b191526f9ff7246cb55f3ddfce970db0697a. --- src/sage/doctest/external.py | 4 --- src/sage/doctest/sources.py | 10 ------- src/sage/env.py | 4 --- src/sage/features/meson_editable.py | 46 ----------------------------- src/sage/misc/package_dir.py | 36 ++-------------------- 5 files changed, 2 insertions(+), 98 deletions(-) delete mode 100644 src/sage/features/meson_editable.py diff --git a/src/sage/doctest/external.py b/src/sage/doctest/external.py index 2cc374f0763..45b3987de05 100644 --- a/src/sage/doctest/external.py +++ b/src/sage/doctest/external.py @@ -366,10 +366,6 @@ def external_features(): r""" Generate the features that are only to be tested if ``--optional=external`` is used. - .. SEEALSO:: - - :func:`sage.features.all.all_features` - EXAMPLES:: sage: from sage.doctest.external import external_features diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index 01d24d67634..7589f62922b 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -87,18 +87,8 @@ def get_basename(path): sage: import os sage: get_basename(sage.doctest.sources.__file__) 'sage.doctest.sources' - - :: - - sage: # optional - !meson_editable sage: get_basename(os.path.join(sage.structure.__path__[0], 'element.pxd')) 'sage.structure.element.pxd' - - TESTS:: - - sage: # optional - meson_editable - sage: get_basename(os.path.join(os.path.dirname(sage.structure.__file__), 'element.pxd')) - 'sage.structure.element.pxd' """ if path is None: return None diff --git a/src/sage/env.py b/src/sage/env.py index 826c0343ac8..060eb2209a9 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -319,10 +319,6 @@ def sage_include_directories(use_sources=False): sage: dirs = sage.env.sage_include_directories(use_sources=True) sage: any(os.path.isfile(os.path.join(d, file)) for d in dirs) True - - :: - - sage: # optional - !meson_editable (no need, see :issue:`39275`) sage: dirs = sage.env.sage_include_directories(use_sources=False) sage: any(os.path.isfile(os.path.join(d, file)) for d in dirs) True diff --git a/src/sage/features/meson_editable.py b/src/sage/features/meson_editable.py deleted file mode 100644 index 25969e562b4..00000000000 --- a/src/sage/features/meson_editable.py +++ /dev/null @@ -1,46 +0,0 @@ -r""" -Feature for testing if Meson editable install is used. -""" - -import sys -from . import Feature, FeatureTestResult - - -class MesonEditable(Feature): - r""" - A :class:`~sage.features.Feature` describing if Meson editable install is used. - - EXAMPLES:: - - sage: from sage.features.meson_editable import MesonEditable - sage: MesonEditable() - Feature('meson_editable') - """ - def __init__(self): - r""" - TESTS:: - - sage: from sage.features.meson_editable import MesonEditable - sage: MesonEditable() is MesonEditable() - True - """ - Feature.__init__(self, 'meson_editable') - - def _is_present(self): - r""" - Test whether Meson editable install is used. - - EXAMPLES:: - - sage: from sage.features.meson_editable import MesonEditable - sage: Internet()._is_present() # random - FeatureTestResult('meson_editable', True) - """ - import sage - import sys - result = type(sage.__loader__).__module__ == '_sagemath_editable_loader' - return FeatureTestResult(self, result) - - -def all_features(): - return [MesonEditable()] diff --git a/src/sage/misc/package_dir.py b/src/sage/misc/package_dir.py index b9742a6d1c2..6b6ca12803f 100644 --- a/src/sage/misc/package_dir.py +++ b/src/sage/misc/package_dir.py @@ -261,7 +261,6 @@ def is_package_or_sage_namespace_package_dir(path, *, distribution_filter=None): :mod:`sage.cpython` is an ordinary package:: - sage: # optional - !meson_editable sage: from sage.misc.package_dir import is_package_or_sage_namespace_package_dir sage: directory = sage.cpython.__path__[0]; directory '.../sage/cpython' @@ -271,48 +270,24 @@ def is_package_or_sage_namespace_package_dir(path, *, distribution_filter=None): :mod:`sage.libs.mpfr` only has an ``__init__.pxd`` file, but we consider it a package directory for consistency with Cython:: - sage: # optional - !meson_editable sage: directory = os.path.join(sage.libs.__path__[0], 'mpfr'); directory '.../sage/libs/mpfr' - sage: is_package_or_sage_namespace_package_dir(directory) + sage: is_package_or_sage_namespace_package_dir(directory) # known bug (seen in build.yml) True :mod:`sage` is designated to become an implicit namespace package:: - sage: # optional - !meson_editable sage: directory = sage.__path__[0]; directory '.../sage' - sage: is_package_or_sage_namespace_package_dir(directory) + sage: is_package_or_sage_namespace_package_dir(directory) # known bug (seen in build.yml) True Not a package:: - sage: # optional - !meson_editable sage: directory = os.path.join(sage.symbolic.__path__[0], 'ginac'); directory # needs sage.symbolic '.../sage/symbolic/ginac' sage: is_package_or_sage_namespace_package_dir(directory) # needs sage.symbolic False - - TESTS:: - - sage: # optional - meson_editable - sage: from sage.misc.package_dir import is_package_or_sage_namespace_package_dir - sage: directory = os.path.dirname(sage.cpython.__file__); directory - '.../sage/cpython' - sage: is_package_or_sage_namespace_package_dir(directory) - True - - sage: # optional - meson_editable - sage: directory = os.path.join(os.path.dirname(sage.libs.__file__), 'mpfr'); directory - '.../sage/libs/mpfr' - sage: is_package_or_sage_namespace_package_dir(directory) - True - - sage: # optional - meson_editable, sage.symbolic - sage: directory = os.path.join(os.path.dirname(sage.symbolic.__file__), 'ginac'); directory - '.../sage/symbolic/ginac' - sage: is_package_or_sage_namespace_package_dir(directory) - False """ if os.path.exists(os.path.join(path, '__init__.py')): # ordinary package return True @@ -370,15 +345,8 @@ def walk_packages(path=None, prefix='', onerror=None): EXAMPLES:: - sage: # optional - !meson_editable sage: sorted(sage.misc.package_dir.walk_packages(sage.misc.__path__)) # a namespace package [..., ModuleInfo(module_finder=FileFinder('.../sage/misc'), name='package_dir', ispkg=False), ...] - - TESTS:: - - sage: # optional - meson_editable - sage: sorted(sage.misc.package_dir.walk_packages(sage.misc.__path__)) - [..., ModuleInfo(module_finder=<...MesonpyPathFinder object...>, name='package_dir', ispkg=False), ...] """ # Adapted from https://github.com/python/cpython/blob/3.11/Lib/pkgutil.py From 85fb2e6c9ae52ece34054e03324af6fb3d6df7d6 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Tue, 11 Feb 2025 23:59:10 +0700 Subject: [PATCH 11/13] Fix more doctests in meson_editable install --- src/sage/doctest/external.py | 4 +++ src/sage/doctest/sources.py | 10 +++++++ src/sage/env.py | 4 +++ src/sage/features/meson_editable.py | 45 +++++++++++++++++++++++++++++ src/sage/misc/package_dir.py | 36 +++++++++++++++++++++-- 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/sage/features/meson_editable.py diff --git a/src/sage/doctest/external.py b/src/sage/doctest/external.py index 45b3987de05..2cc374f0763 100644 --- a/src/sage/doctest/external.py +++ b/src/sage/doctest/external.py @@ -366,6 +366,10 @@ def external_features(): r""" Generate the features that are only to be tested if ``--optional=external`` is used. + .. SEEALSO:: + + :func:`sage.features.all.all_features` + EXAMPLES:: sage: from sage.doctest.external import external_features diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index 7589f62922b..01d24d67634 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -87,8 +87,18 @@ def get_basename(path): sage: import os sage: get_basename(sage.doctest.sources.__file__) 'sage.doctest.sources' + + :: + + sage: # optional - !meson_editable sage: get_basename(os.path.join(sage.structure.__path__[0], 'element.pxd')) 'sage.structure.element.pxd' + + TESTS:: + + sage: # optional - meson_editable + sage: get_basename(os.path.join(os.path.dirname(sage.structure.__file__), 'element.pxd')) + 'sage.structure.element.pxd' """ if path is None: return None diff --git a/src/sage/env.py b/src/sage/env.py index 060eb2209a9..826c0343ac8 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -319,6 +319,10 @@ def sage_include_directories(use_sources=False): sage: dirs = sage.env.sage_include_directories(use_sources=True) sage: any(os.path.isfile(os.path.join(d, file)) for d in dirs) True + + :: + + sage: # optional - !meson_editable (no need, see :issue:`39275`) sage: dirs = sage.env.sage_include_directories(use_sources=False) sage: any(os.path.isfile(os.path.join(d, file)) for d in dirs) True diff --git a/src/sage/features/meson_editable.py b/src/sage/features/meson_editable.py new file mode 100644 index 00000000000..a110648b873 --- /dev/null +++ b/src/sage/features/meson_editable.py @@ -0,0 +1,45 @@ +r""" +Feature for testing if Meson editable install is used. +""" + +import sys +from . import Feature, FeatureTestResult + + +class MesonEditable(Feature): + r""" + A :class:`~sage.features.Feature` describing if Meson editable install is used. + + EXAMPLES:: + + sage: from sage.features.meson_editable import MesonEditable + sage: MesonEditable() + Feature('meson_editable') + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.meson_editable import MesonEditable + sage: MesonEditable() is MesonEditable() + True + """ + Feature.__init__(self, 'meson_editable') + + def _is_present(self): + r""" + Test whether Meson editable install is used. + + EXAMPLES:: + + sage: from sage.features.meson_editable import MesonEditable + sage: MesonEditable()._is_present() # random + FeatureTestResult('meson_editable', True) + """ + import sage + result = type(sage.__loader__).__module__ == '_sagemath_editable_loader' + return FeatureTestResult(self, result) + + +def all_features(): + return [MesonEditable()] diff --git a/src/sage/misc/package_dir.py b/src/sage/misc/package_dir.py index 6b6ca12803f..b9742a6d1c2 100644 --- a/src/sage/misc/package_dir.py +++ b/src/sage/misc/package_dir.py @@ -261,6 +261,7 @@ def is_package_or_sage_namespace_package_dir(path, *, distribution_filter=None): :mod:`sage.cpython` is an ordinary package:: + sage: # optional - !meson_editable sage: from sage.misc.package_dir import is_package_or_sage_namespace_package_dir sage: directory = sage.cpython.__path__[0]; directory '.../sage/cpython' @@ -270,24 +271,48 @@ def is_package_or_sage_namespace_package_dir(path, *, distribution_filter=None): :mod:`sage.libs.mpfr` only has an ``__init__.pxd`` file, but we consider it a package directory for consistency with Cython:: + sage: # optional - !meson_editable sage: directory = os.path.join(sage.libs.__path__[0], 'mpfr'); directory '.../sage/libs/mpfr' - sage: is_package_or_sage_namespace_package_dir(directory) # known bug (seen in build.yml) + sage: is_package_or_sage_namespace_package_dir(directory) True :mod:`sage` is designated to become an implicit namespace package:: + sage: # optional - !meson_editable sage: directory = sage.__path__[0]; directory '.../sage' - sage: is_package_or_sage_namespace_package_dir(directory) # known bug (seen in build.yml) + sage: is_package_or_sage_namespace_package_dir(directory) True Not a package:: + sage: # optional - !meson_editable sage: directory = os.path.join(sage.symbolic.__path__[0], 'ginac'); directory # needs sage.symbolic '.../sage/symbolic/ginac' sage: is_package_or_sage_namespace_package_dir(directory) # needs sage.symbolic False + + TESTS:: + + sage: # optional - meson_editable + sage: from sage.misc.package_dir import is_package_or_sage_namespace_package_dir + sage: directory = os.path.dirname(sage.cpython.__file__); directory + '.../sage/cpython' + sage: is_package_or_sage_namespace_package_dir(directory) + True + + sage: # optional - meson_editable + sage: directory = os.path.join(os.path.dirname(sage.libs.__file__), 'mpfr'); directory + '.../sage/libs/mpfr' + sage: is_package_or_sage_namespace_package_dir(directory) + True + + sage: # optional - meson_editable, sage.symbolic + sage: directory = os.path.join(os.path.dirname(sage.symbolic.__file__), 'ginac'); directory + '.../sage/symbolic/ginac' + sage: is_package_or_sage_namespace_package_dir(directory) + False """ if os.path.exists(os.path.join(path, '__init__.py')): # ordinary package return True @@ -345,8 +370,15 @@ def walk_packages(path=None, prefix='', onerror=None): EXAMPLES:: + sage: # optional - !meson_editable sage: sorted(sage.misc.package_dir.walk_packages(sage.misc.__path__)) # a namespace package [..., ModuleInfo(module_finder=FileFinder('.../sage/misc'), name='package_dir', ispkg=False), ...] + + TESTS:: + + sage: # optional - meson_editable + sage: sorted(sage.misc.package_dir.walk_packages(sage.misc.__path__)) + [..., ModuleInfo(module_finder=<...MesonpyPathFinder object...>, name='package_dir', ispkg=False), ...] """ # Adapted from https://github.com/python/cpython/blob/3.11/Lib/pkgutil.py From b1606b58cb19ad2fcee596ee02f7cf0501828937 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Wed, 12 Feb 2025 11:11:10 +0700 Subject: [PATCH 12/13] Workaround for test-new failure --- src/sage/misc/package_dir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/misc/package_dir.py b/src/sage/misc/package_dir.py index 6358bc1d265..16364fb1483 100644 --- a/src/sage/misc/package_dir.py +++ b/src/sage/misc/package_dir.py @@ -274,7 +274,7 @@ def is_package_or_sage_namespace_package_dir(path, *, distribution_filter=None): sage: # optional - !meson_editable sage: directory = os.path.join(sage.libs.__path__[0], 'mpfr'); directory '.../sage/libs/mpfr' - sage: is_package_or_sage_namespace_package_dir(directory) + sage: is_package_or_sage_namespace_package_dir(directory) # known bug (seen in build.yml) True :mod:`sage` is designated to become an implicit namespace package:: From b14e2e92cdc1c3c437c2359a4b3e434dd6c86486 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 3 Mar 2025 22:27:45 +0700 Subject: [PATCH 13/13] Only run 2/4 combinations in pull_request --- .github/workflows/ci-meson.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/ci-meson.yml b/.github/workflows/ci-meson.yml index 91cf277296c..0b3f6401bc2 100644 --- a/.github/workflows/ci-meson.yml +++ b/.github/workflows/ci-meson.yml @@ -25,6 +25,19 @@ jobs: os: [ubuntu] python: ['3.11', '3.12'] editable: [false, true] + is-pull-request: + - ${{ github.event_name == 'pull_request' }} + # trick from https://github.com/orgs/community/discussions/26253#discussioncomment-6745038 + # in PR, only run the combinations listed below; otherwise, run all combinations + exclude: + - is-pull-request: true + include: + - os: ubuntu + python: 3.11 + editable: false + - os: ubuntu + python: 3.12 + editable: true steps: - uses: actions/checkout@v4