@@ -12,41 +12,26 @@ For development, we recommend you use :mod:`venv` for virtual environments and
12
12
as well as the ``pytest `` package itself.
13
13
This ensures your code and dependencies are isolated from your system Python installation.
14
14
15
- Next, place a ``pyproject.toml `` file in the root of your package:
15
+ Create a ``pyproject.toml `` file in the root of your repository as described in
16
+ :doc: `packaging:tutorials/packaging-projects `.
17
+ The first few lines should look like this:
16
18
17
19
.. code-block :: toml
18
20
19
21
[build-system]
20
- requires = ["setuptools >= 42"]
21
- build-backend = "setuptools.build_meta"
22
-
23
- and a ``setup.cfg `` file containing your package's metadata with the following minimum content:
24
-
25
- .. code-block :: ini
22
+ requires = ["hatchling"]
23
+ build-backend = "hatchling.build"
26
24
27
25
[metadata]
28
- name = PACKAGENAME
29
-
30
- [options]
31
- packages = find:
26
+ name = "PACKAGENAME"
32
27
33
28
where ``PACKAGENAME `` is the name of your package.
34
29
35
- .. note ::
36
-
37
- If your pip version is older than ``21.3 ``, you'll also need a ``setup.py `` file:
38
-
39
- .. code-block :: python
40
-
41
- from setuptools import setup
42
-
43
- setup()
44
-
45
30
You can then install your package in "editable" mode by running from the same directory:
46
31
47
32
.. code-block :: bash
48
33
49
- pip install -e .
34
+ pip install -e .
50
35
51
36
which lets you change your source code (both tests and application) and rerun tests at will.
52
37
@@ -89,11 +74,11 @@ to keep tests separate from actual application code (often a good idea):
89
74
.. code-block :: text
90
75
91
76
pyproject.toml
92
- setup.cfg
93
- mypkg/
94
- __init__.py
95
- app.py
96
- view.py
77
+ src/
78
+ mypkg/
79
+ __init__.py
80
+ app.py
81
+ view.py
97
82
tests/
98
83
test_app.py
99
84
test_view.py
@@ -112,72 +97,28 @@ This has the following benefits:
112
97
See :ref: `pytest vs python -m pytest ` for more information about the difference between calling ``pytest `` and
113
98
``python -m pytest ``.
114
99
115
- Note that this scheme has a drawback if you are using ``prepend `` :ref: `import mode <import-modes >`
116
- (which is the default): your test files must have **unique names **, because
117
- ``pytest `` will import them as *top-level * modules since there are no packages
118
- to derive a full package name from. In other words, the test files in the example above will
119
- be imported as ``test_app `` and ``test_view `` top-level modules by adding ``tests/ `` to
120
- ``sys.path ``.
100
+ For new projects, we recommend to use ``importlib `` :ref: `import mode <import-modes >`
101
+ (see which-import-mode _ for a detailed explanation).
102
+ To this end, add the following to your ``pyproject.toml ``:
121
103
122
- If you need to have test modules with the same name, you might add ``__init__.py `` files to your
123
- ``tests `` folder and subfolders, changing them to packages:
124
-
125
- .. code-block :: text
104
+ .. code-block :: toml
126
105
127
- pyproject.toml
128
- setup.cfg
129
- mypkg/
130
- ...
131
- tests/
132
- __init__.py
133
- foo/
134
- __init__.py
135
- test_view.py
136
- bar/
137
- __init__.py
138
- test_view.py
106
+ [tool.pytest.ini_options]
107
+ addopts = [
108
+ "--import-mode=importlib",
109
+ ]
139
110
140
- Now pytest will load the modules as ``tests.foo.test_view `` and ``tests.bar.test_view ``, allowing
141
- you to have modules with the same name. But now this introduces a subtle problem: in order to load
142
- the test modules from the ``tests `` directory, pytest prepends the root of the repository to
143
- ``sys.path ``, which adds the side-effect that now ``mypkg `` is also importable.
111
+ .. _src-layout :
144
112
145
- This is problematic if you are using a tool like `tox `_ to test your package in a virtual environment,
146
- because you want to test the *installed * version of your package, not the local code from the repository.
113
+ Generally, but especially if you use the default import mode ``prepend ``,
114
+ it is **strongly ** suggested to use a ``src `` layout.
115
+ Here, your application root package resides in a sub-directory of your root,
116
+ i.e. ``src/mypkg/ `` instead of ``mypkg ``.
147
117
148
- .. _`src-layout` :
118
+ This layout prevents a lot of common pitfalls and has many benefits,
119
+ which are better explained in this excellent `blog post `_ by Ionel Cristian Mărieș.
149
120
150
- In this situation, it is **strongly ** suggested to use a ``src `` layout where application root package resides in a
151
- sub-directory of your root:
152
-
153
- .. code-block :: text
154
-
155
- pyproject.toml
156
- setup.cfg
157
- src/
158
- mypkg/
159
- __init__.py
160
- app.py
161
- view.py
162
- tests/
163
- __init__.py
164
- foo/
165
- __init__.py
166
- test_view.py
167
- bar/
168
- __init__.py
169
- test_view.py
170
-
171
-
172
- This layout prevents a lot of common pitfalls and has many benefits, which are better explained in this excellent
173
- `blog post by Ionel Cristian Mărieș <https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure >`_.
174
-
175
- .. note ::
176
- The ``--import-mode=importlib `` option (see :ref: `import-modes `) does not have
177
- any of the drawbacks above because ``sys.path `` is not changed when importing
178
- test modules, so users that run into this issue are strongly encouraged to try it.
179
-
180
- The ``src `` directory layout is still strongly recommended however.
121
+ .. _blog post : https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure>
181
122
182
123
183
124
Tests as part of application code
@@ -190,8 +131,7 @@ want to distribute them along with your application:
190
131
.. code-block :: text
191
132
192
133
pyproject.toml
193
- setup.cfg
194
- mypkg/
134
+ [src/]mypkg/
195
135
__init__.py
196
136
app.py
197
137
view.py
@@ -253,6 +193,56 @@ Note that this layout also works in conjunction with the ``src`` layout mentione
253
193
much less surprising.
254
194
255
195
196
+ .. _which-import-mode :
197
+
198
+ Choosing an import mode
199
+ ^^^^^^^^^^^^^^^^^^^^^^^
200
+
201
+ For historical reasons, pytest defaults to the ``prepend `` :ref: `import mode <import-modes >`
202
+ instead of the ``importlib `` import mode we recommend for new projects.
203
+ The reason lies in the way the ``prepend `` mode works:
204
+
205
+ Since there are no packages to derive a full package name from,
206
+ ``pytest `` will import your test files as *top-level * modules.
207
+ The test files in the first example (:ref: `src layout <src-layout >`) would be imported as
208
+ ``test_app `` and ``test_view `` top-level modules by adding ``tests/ `` to ``sys.path ``.
209
+
210
+ This results in a drawback compared to the import mode ``importlib ``:
211
+ your test files must have **unique names **.
212
+
213
+ If you need to have test modules with the same name,
214
+ as a workaround you might add ``__init__.py `` files to your ``tests `` folder and subfolders,
215
+ changing them to packages:
216
+
217
+ .. code-block :: text
218
+
219
+ pyproject.toml
220
+ mypkg/
221
+ ...
222
+ tests/
223
+ __init__.py
224
+ foo/
225
+ __init__.py
226
+ test_view.py
227
+ bar/
228
+ __init__.py
229
+ test_view.py
230
+
231
+ Now pytest will load the modules as ``tests.foo.test_view `` and ``tests.bar.test_view ``,
232
+ allowing you to have modules with the same name.
233
+ But now this introduces a subtle problem:
234
+ in order to load the test modules from the ``tests `` directory,
235
+ pytest prepends the root of the repository to ``sys.path ``,
236
+ which adds the side-effect that now ``mypkg `` is also importable.
237
+
238
+ This is problematic if you are using a tool like tox _ to test your package in a virtual environment,
239
+ because you want to test the *installed * version of your package,
240
+ not the local code from the repository.
241
+
242
+ The ``importlib `` import mode does not have any of the drawbacks above,
243
+ because ``sys.path `` is not changed when importing test modules.
244
+
245
+
256
246
.. _`buildout` : http://www.buildout.org/en/latest/
257
247
258
248
.. _`use tox` :
0 commit comments