Skip to content

Commit 82ab6c0

Browse files
committed
DOC: document how to use installed data files with editable installs
1 parent 09e18fc commit 82ab6c0

File tree

8 files changed

+106
-0
lines changed

8 files changed

+106
-0
lines changed

docs/how-to-guides/editable-installs.rst

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,52 @@ An alternative build directory can be specified using the
108108
:option:`build-dir` config setting.
109109

110110

111+
Data files
112+
----------
113+
114+
It is relatively common to install data files needed at runtime
115+
alongside the package's Python code or extension modules. For a Python
116+
package named ``package`` this would look like this:
117+
118+
.. TODO the :force: option should be removed once the pygments meson lexer is
119+
updated to fix https://github.com/pygments/pygments/issues/2918
120+
121+
.. literalinclude:: ../../tests/packages/install-data/meson.build
122+
:language: meson
123+
:force:
124+
:lines: 5-
125+
126+
In most circumstances, these files can be accessed deriving their
127+
filesystem path from the filesystem path of the Python module next to
128+
them via the ``__file__`` special variable. For example, within the
129+
package ``__init__.py``:
130+
131+
.. code-block:: python
132+
133+
import pathlib
134+
135+
data = pathlib.Path(__file__).parent.joinpath('data.txt').read_text()
136+
uuid = pathlib.Path(__file__).parent.joinpath('uuid.txt').read_text() # WRONG!
137+
138+
However, this does not work when modules are not loaded from a package
139+
installed in the Python library path in the filesystem but with a
140+
special module loader, as used to implement editable installs in
141+
``meson-python``. In the example above, the second read would fail
142+
when the package is installed in editable mode. For this reason, data
143+
files need to be accessed using :mod:`importlib.resources`. The code
144+
above should be replaced with:
145+
146+
.. literalinclude:: ../../tests/packages/install-data/__init__.py
147+
:lines: 5-
148+
149+
:mod:`importlib.resources` implements a virtual filesystem that allows
150+
to access individual files as if they were in their install location.
151+
However, there is no way to expose this file structure outside of the
152+
python runtime. In the example above, it is not possible to make the
153+
``data.txt`` and ``uuid.txt`` files appear in the same fileystem
154+
directory.
155+
156+
111157
.. _how-to-guides-editable-installs-verbose:
112158

113159
Verbose mode

pyproject.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ changelog = 'https://mesonbuild.com/meson-python/changelog.html'
5151
[dependency-groups]
5252
test = [
5353
'build',
54+
'importlib_resources >= 5.10; python_version < "3.12"',
5455
'pytest >= 6.0',
5556
'pytest-cov[toml]',
5657
'pytest-mock',
@@ -109,6 +110,11 @@ known-first-party = [
109110
'mesonpy',
110111
]
111112

113+
[tool.ruff.lint.per-file-ignores]
114+
# this file is included literally in the documentation and the double
115+
# empty lines forced by the import sorting style look odd there
116+
'tests/packages/isntall-data/__init__.py' = ['I001']
117+
112118

113119
[tool.coverage.run]
114120
disable_warnings = [
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# SPDX-FileCopyrightText: 2025 The meson-python developers
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import sys
6+
7+
if sys.version_info < (3, 12):
8+
import importlib_resources as resources
9+
else:
10+
from importlib import resources
11+
12+
data = resources.files().joinpath('data.txt').read_text()
13+
uuid = resources.files().joinpath('uuid.txt').read_text()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DATA
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# SPDX-FileCopyrightText: 2025 The meson-python developers
2+
#
3+
# SPDX-License-Identifier: MIT
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# SPDX-FileCopyrightText: 2025 The meson-python developers
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
project('package', version: '1.0.0')
6+
7+
py = import('python').find_installation()
8+
9+
py.install_sources(
10+
'__init__.py',
11+
subdir: 'package',
12+
)
13+
14+
install_data(
15+
'data.txt',
16+
install_dir: py.get_install_dir() / 'package',
17+
)
18+
19+
custom_target(
20+
output: 'uuid.txt',
21+
command: [py, '-m', 'uuid'],
22+
capture: true,
23+
install: true,
24+
install_dir: py.get_install_dir() / 'package',
25+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# SPDX-FileCopyrightText: 2023 The meson-python developers
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
[build-system]
6+
build-backend = 'mesonpy'
7+
requires = ['meson-python']

tests/test_editable.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,3 +344,8 @@ def test_editable_rebuild_error(package_purelib_and_platlib, tmp_path, verbose):
344344
del sys.meta_path[0]
345345
sys.modules.pop('pure', None)
346346
path.write_text(code)
347+
348+
349+
def test_install_data(venv, editable_install_data, tmp_path):
350+
venv.pip('install', os.fspath(editable_install_data))
351+
venv.python('-c', 'import package')

0 commit comments

Comments
 (0)