Skip to content

Commit 62d4f8b

Browse files
dnicolodirgommers
authored andcommitted
DOC: document how to use installed data files with editable installs
1 parent 73b7019 commit 62d4f8b

File tree

8 files changed

+110
-0
lines changed

8 files changed

+110
-0
lines changed

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

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,61 @@ 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+
.. warning::
157+
158+
The :mod:`importlib.resources` appeared in Python 3.7 but it did not work
159+
correctly for this use until Python 3.10. The `importlib-resources`_
160+
backport version 5.10 or later can be used if support for earlier Python
161+
versions is desired.
162+
163+
.. _importlib-resources: https://importlib-resources.readthedocs.io/en/latest/index.html
164+
165+
111166
.. _how-to-guides-editable-installs-verbose:
112167

113168
Verbose mode

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ known-first-party = [
109109
'mesonpy',
110110
]
111111

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

113118
[tool.coverage.run]
114119
disable_warnings = [
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-FileCopyrightText: 2025 The meson-python developers
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import importlib.resources
6+
7+
data = importlib.resources.files(__package__).joinpath('data.txt').read_text()
8+
uuid = importlib.resources.files(__package__).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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,3 +344,9 @@ 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+
@pytest.mark.skipif(sys.version_info < (3, 10), reason='importlib.resources is unusable on Python 3.9')
350+
def test_install_data(venv, editable_install_data, tmp_path):
351+
venv.pip('install', os.fspath(editable_install_data))
352+
venv.python('-c', 'import package')

0 commit comments

Comments
 (0)