Skip to content

Commit 2964c29

Browse files
authored
Merge pull request #30 from cclauss/patch-2
Update README.rst to recommend pipx run or install
2 parents 4de8785 + 335a39f commit 2964c29

File tree

2 files changed

+56
-62
lines changed

2 files changed

+56
-62
lines changed

.github/workflows/test.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ jobs:
1313
matrix:
1414
python-version: ["3.6", "3.7.7", "3.8", "3.9", "3.10", "3.11"]
1515
os: [ubuntu-20.04, macos-latest, windows-latest]
16+
exclude: # Apple Silicon ARM64 does not support Python < v3.8
17+
- python-version: "3.6"
18+
os: macos-latest
19+
- python-version: "3.7.7"
20+
os: macos-latest
21+
include: # So run those legacy versions on Intel CPUs
22+
- python-version: "3.6"
23+
os: macos-13
24+
- python-version: "3.7.7"
25+
os: macos-13
1626

1727
steps:
1828
- uses: actions/checkout@v4

README.rst

Lines changed: 46 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Overview
1212

1313
This package provides a Python script and pytest plugin to help convert Nose-based tests into pytest-based
1414
tests. Specifically, the script transforms ``nose.tools.assert_*`` function calls into raw assert statements,
15-
while preserving format of original arguments as much as possible. For example, the script:
15+
while preserving the format of original arguments as much as possible. For example, the script:
1616

1717
.. code-block:: python
1818
@@ -31,35 +31,36 @@ transformed because there is no raw assert statement equivalent, or the equivale
3131
maintain. They are provided as functions in the pytest namespace via pytest's plugin system.
3232

3333

34-
Installation
35-
-------------
36-
37-
From a command shell run ::
38-
39-
pip install nose2pytest
40-
41-
This puts an executable file in ``<python-root>/Scripts`` with *python-root* being the root folder of the
42-
Python installation from which ``pip`` was run.
43-
44-
4534
Running
4635
------------
4736

48-
From a command shell, ::
37+
For a one-time conversion use the shell command ::
4938

50-
nose2pytest path/to/dir/with/python_files
39+
pipx run --python 3.11 nose2pytest path/to/dir/with/python_files
5140
5241
This will find all ``.py`` files in the folder tree starting at ``path/to/dir/with/python_files`` and
5342
overwrite the original (assuming most users will be running this on a version-controlled code base, this is
5443
almost always what would be most convenient). Type ``nose2pytest -h`` for other options, such as ``-v``.
5544

5645

46+
Installation
47+
-------------
48+
49+
For doing multiple conversions use the shell command ::
50+
51+
pipx install --python 3.11 nose2pytest
52+
53+
For each conversion use the shell command ::
54+
55+
nose2pytest path/to/dir/with/python_files
56+
57+
5758
Motivation
5859
------------
5960

6061
I have used Nose for years and it is a great tool. However, to get good test failure diagnostics with Nose you
6162
ought to use the ``assert_*()`` functions from ``nose.tools``. Although they provide very good diagnostics, they
62-
are not as convenient to use as raw assertions, since you have to decide before hand what type of assertion you
63+
are not as convenient to use as raw assertions, since you have to decide beforehand what type of assertion you
6364
are going to write: an identity comparison to None, a truth check, a falseness check, an identity comparison to another
6465
object, etc. Just being able to write a raw assertion, and still get good diagnostics on failure as done by
6566
pytest, is really nice. This is a main reason for using pytest for me. Another reason is the design of fixtures
@@ -72,7 +73,7 @@ manually, to get the same test coverage and results. A few gotchas:
7273
- test classes that have ``__init__`` will be ignored, those will have to be moved (usually, into class's
7374
``setup_class()``)
7475
- the ``setup.cfg`` may have to be edited since test discovery rules are slightly more strict with pytest
75-
- the order of tests may be different, but in general that should not matter
76+
- the order of tests may be different, but in general, that should not matter
7677
- all test modules are imported up-front, so some test modules may need adjustment such as moving some
7778
code from the top of the test module into its ``setup_module()``
7879

@@ -83,7 +84,7 @@ uses ``nose.tools.assert_*`` functions, yet with pytest you can use raw assertio
8384
these two approaches should a developer use? If you modify existing tests, should new assertions use raw assert?
8485
Should the remaining test method, test class, or test module be updated? A test module can contain hundreds of
8586
calls to ``nose.tools.assert_*`` functions, is a developer to manually go through each one to convert it? Painful and
86-
error prone, in general not feasible to do manually.
87+
error-prone, in general not feasible to do manually.
8788

8889
This is why I developed nose2pytest: I wanted to migrate my pypubsub project's test suite from Nose to pytest,
8990
but also have only pytest as a dependency, and have one obvious way to write assertions in the test suite.
@@ -92,14 +93,9 @@ but also have only pytest as a dependency, and have one obvious way to write ass
9293
Requirements
9394
-------------
9495

95-
I expect nose2pytest script to run with Python >= 3.4, to correctly convert Python test suite >= 2.7, on any
96-
OS supported by a version of python that has lib2to3 compatible with Python 3.4's lib2to3. I expect it to
97-
succeed even with quite old versions of Nose (even prior to 1.0 which came out ca. 2010), and with the new
98-
Nose2 test driver.
99-
100-
Note however that I have run the script only with Python 3.4, to convert Python 3.4 test suites based on
101-
Nose 1.3.7 on Windows 7 Pro 64. If you have successfully used nose2pytest with other combinations, please
102-
kindly let me know (via github).
96+
I expect nose2pytest script to run with supported versions of CPython <= v3.11, on any OS supported by a version of
97+
Python that has lib2to3 compatible with fissix. I expect it to succeed even with quite old versions of Nose (even
98+
prior to 1.0 which came out ca. 2010) and with the new Nose2 test driver.
10399

104100
The pytest package namespace will be extended with ``assert_`` functions that are not converted by the script
105101
only if, err, you have pytest installed!
@@ -207,7 +203,7 @@ Limitations
207203

208204
- The script does not convert ``nose.tools.assert_`` import statements as there are too many possibilities.
209205
Should ``from nose.tools import ...`` be changed to ``from pytest import ...``, and the implemented
210-
conversions removed? Should an ``import pytest`` statement be added, and if so, where? If it is added after
206+
conversions be removed? Should an ``import pytest`` statement be added, and if so, where? If it is added after
211207
the line that had the ``nose.tools`` import, is the previous line really needed? Indeed the ``assert_``
212208
functions added in the ``pytest`` namespace could be accessed via ``pytest.assert_``, in which case the
213209
script should prepend ``pytest.`` and remove the ``from nose.tools import ...`` entirely. Too many options,
@@ -229,30 +225,17 @@ Limitations
229225
import bogo.assert_true
230226
bogo.assert_true(...) # should this one be converted?
231227
232-
The possiblities are endless so supporting this would require such a large amount of time that I
228+
The possibilities are endless so supporting this would require such a large amount of time that I
233229
do not have. As with other limitations in this section
234230

235231
- Nose functions that can be used as context managers can obviously not be converted to raw assertions.
236232
However, there is currently no way of preventing nose2pytest from converting Nose functions used this way.
237233
You will have to manually fix.
238-
239-
- The lib2to3 package that nose2pytest relies on assumes python 2.7 syntax as input. The only issue that
240-
this has caused so far on code base of 20k lines of python 3.4 *test* code (i.e. the source code does not
241-
matter, as none of the test code, such as import statements, is actually run) are keywords like ``exec``
242-
and ``print``, which in Python 2.x were statements, whereas they are functions in Python 3.x. This means
243-
that in Python 3.x, a method can be named ``exec()`` or ``print()``, whereas this would lead to a syntax
244-
error in Python 2.7. Some libraries that do not support 2.x take advantage of this (like PyQt5). Any
245-
occurrence of these two keywords as methods in your test code will cause the script to fail converting
246-
anything.
247-
248-
The work around is, luckily, simple: do a global search-replace of ``\.exec\(`` for ``.exec__(`` in your
249-
test folder, run nose2pytest, then reverse the search-replace (do a global search-replace of ``\.exec__\(``
250-
for ``.exec(``).
251-
234+
252235
- ``@raises``: this decorator can be replaced via the regular expression ``@raises\((.*)\)`` to
253236
``@pytest.mark.xfail(raises=$1)``,
254237
but I prefer instead to convert such decorated test functions to use ``pytest.raises`` in the test function body.
255-
Indeed, it is easy to forget the decorator, and add code after the line that raises, but this code will never
238+
Indeed, it is easy to forget the decorator and add code after the line that raises, but this code will never
256239
be run and you won't know. Using the ``pytest.raises(...)`` is better than ``xfail(raise=...)``.
257240

258241
- Nose2pytest does not have a means of determining if an assertion function is inside a lambda expression, so
@@ -281,16 +264,17 @@ should be able to run both a unittest2pytest converter, then the nose2pytest con
281264
Solution Notes
282265
---------------
283266

284-
I don't think this script would have been possible without lib2to3, certainly not with the same functionality since
285-
lib2to3, due to its purpose, preserves newlines, spaces and comments. The documentation for lib2to3 is very
286-
minimal, so I was lucky to find http://python3porting.com/fixers.html.
267+
I don't think this script would have been possible without lib2to3/fissix, certainly not with the same
268+
functionality since lib2to3/fissix, due to their purpose, preserves newlines, spaces and comments. The
269+
documentation for lib2to3/fissix is very minimal, so I was lucky to
270+
find http://python3porting.com/fixers.html.
287271

288-
Other than figuring out lib2to3 package so I could harness its
289-
capabilities, some aspects of code transformations still turned out to be tricky, as warned by Regobro in the
290-
last paragraph of his `Extending 2to3 <http://python3porting.com/fixers.html>`_ page.
272+
Other than figuring out lib2to3/fissix package so I could harness its capabilities, some aspects of code
273+
transformations still turned out to be tricky, as warned by Regobro in the last paragraph of his
274+
`Extending 2to3 <http://python3porting.com/fixers.html>`_ page.
291275

292276
- Multi-line arguments: Python accepts multi-line expressions when they are surrounded by parentheses, brackets
293-
or braces, but not otherwise. For example converting:
277+
or braces, but not otherwise. For example, converting:
294278

295279
.. code-block:: python
296280
@@ -313,7 +297,7 @@ last paragraph of his `Extending 2to3 <http://python3porting.com/fixers.html>`_
313297
314298
So nose2pytest checks each argument expression (such as ``long_a +\n long_b``) to see if it has
315299
newlines that would cause an invalid syntax, and if so, wraps them in parentheses. However, it is also important
316-
for readability of raw assertions that parentheses only be present if necessary. In other words:
300+
for the readability of raw assertions that parentheses only be present if necessary. In other words:
317301

318302
.. code-block:: python
319303
@@ -342,10 +326,10 @@ last paragraph of his `Extending 2to3 <http://python3porting.com/fixers.html>`_
342326
343327
So nose2pytest only tries to limit the addition of external parentheses to code that really needs it.
344328

345-
- Operator precedence: Python assigns a precedence to each operator; operators that are on the same level
329+
- Operator precedence: Python assigns precedence to each operator; operators that are on the same level
346330
of precedence (like the comparison operators ==, >=, !=, etc) are executed in sequence. This poses a problem
347331
for two-argument assertion functions. Example: translating ``assert_equal(a != b, a <= c)`` to
348-
``assert a != b == a <= c`` is incorrect, it must be converted to ``assert (a != b) == (a <= c)``. However
332+
``assert a != b == a <= c`` is incorrect, it must be converted to ``assert (a != b) == (a <= c)``. However,
349333
wrapping every argument in parentheses all the time does not produce easy-to-read assertions:
350334
``assert_equal(a, b < c)`` should convert to ``assert a == (b < c)``, not ``assert (a) == (b < c)``.
351335

@@ -358,20 +342,20 @@ last paragraph of his `Extending 2to3 <http://python3porting.com/fixers.html>`_
358342
Contributing
359343
------------
360344

361-
Patches and extensions are welcome. Please fork, branch, then submit PR. Nose2pytest uses `lib2to3.pytree`,
345+
Patches and extensions are welcome. Please fork, branch, and then submit PR. Nose2pytest uses `lib2to3.pytree`,
362346
in particular the Leaf and Node classes. There are a few particularly challenging aspects to transforming
363347
nose test expressions to equivalent pytest expressions:
364348

365349
#. Finding expressions that match a pattern: If the code you want to transform does not already match one
366-
of the uses cases in script.py, you will have to determine the lib2to3 pattern expression
350+
of the uses cases in script.py, you will have to determine the lib2to3/fissix pattern expression
367351
that describes it (this is similar to regular expressions, but for AST representation of code,
368352
instead of text strings). Various expression patterns already exist near the top of
369353
nose2pytest/script.py. This is largely trial and error as there is (as of this writing) no good
370354
documentation.
371-
#. Inserting the sub-expressions extracted by lib2to3 in step 1 into the target "expression template". For
372-
example to convert `assert_none(a)` to `assert a is None`, the `a` sub-expression extracted via the lib2to3
373-
pattern must be inserted into the correct "placeholder" node of the target expression. If step 1 was
374-
necessary, then step 2 like involves creating a new class that derives from `FixAssertBase`.
355+
#. Inserting the sub-expressions extracted by lib2to3/fissix in step 1 into the target "expression template".
356+
For example to convert `assert_none(a)` to `assert a is None`, the `a` sub-expression extracted via the
357+
lib2to3/fissix pattern must be inserted into the correct "placeholder" node of the target expression. If
358+
step 1 was necessary, then step 2 like involves creating a new class that derives from `FixAssertBase`.
375359
#. Parentheses and priority of operators: sometimes, it is necessary to add parentheses around an extracted
376360
subexpression to protect it against higher-priority operators. For example, in `assert_none(a)` the `a`
377361
could be an arbitrary Python expression, such as `var1 and var2`. The meaning of `assert_none(var1 and var2)`
@@ -431,10 +415,10 @@ Maintenance
431415

432416
- Clone or fork the git repo, create a branch
433417
- Install `pytest` and `nose` on your system: `python -m pip install pytest nose`
434-
- In root folder, run `pytest`
418+
- In the root folder, run `pytest`
435419
- Once all tests pass, install tox on your system: on Ubuntu, `python -m pip install tox`
436420
- Run tox: `tox`
437-
- Add a python version if latest python is not in `tox.ini`
421+
- Add a python version if the latest Python is not in `tox.ini`
438422

439423
Notes for Ubuntu:
440424
- My experience today installing python 3.5 to 3.11 on Ubuntu 18 was surprisingly not smooth. I had to use these
@@ -448,8 +432,8 @@ Notes for Ubuntu:
448432
- note however that once the correct tox installed,
449433

450434

451-
Acknowledgements
452-
----------------
435+
Acknowledgments
436+
---------------
453437

454438
Thanks to (AFAICT) Lennart Regebro for having written http://python3porting.com/fixers.html#find-pattern, and
455439
to those who answered

0 commit comments

Comments
 (0)