I'm currently trying to get the first and last line numbers of all test methods in a python file.
For example:
The file test_lift.py tests the file lift.py.
test_lift.py
1 import random
2
3 from src.lift import Lift
4 from flaky import flaky
5
6
7 def test_upwards():
8 tester = Lift()
9 tester.upwards()
10 assert 1 == tester.curr_floor
11
12 def test_downwards():
13 tester = Lift()
14 tester.curr_floor = 1
15 tester.downwards()
16 assert 0 == tester.curr_floor
17 tester.downwards()
18 assert 0 == tester.curr_floor
19
...
I now want to get the first and last line number for each test method in test_lift.py, in this case for example:
test_upwards, 7, 10
test_downwards, 12, 18
I've already tried using the conftest.py, but without any success. Maybe I'm overlooking something? The solution doesn't necessarily have to be in python. If anybody knows how to solve this problem by parsing the files I'm fine with that.
Or, without any additional modules (but some Python internals):
>>> def thing():
... a = 5
... b = a + 4
... return a + b * 2
...
>>> thing.__code__.co_firstlineno # self-explanatory
1
>>> list(thing.__code__.co_lnotab)
[0, 1, 4, 1, 8, 1]
>>> thing.__code__.co_firstlineno + sum(line_increment for i, line_increment in enumerate(thing.__code__.co_lnotab) if i % 2)
4
So there you have it: the function spans from line 1 (thing.__code__.co_firstlineno
) to line 4.
The dis
module proves this (the numbers in the first column are line numbers):
>>> import dis
>>> dis.dis(thing)
2 0 LOAD_CONST 1 (5)
2 STORE_FAST 0 (a)
3 4 LOAD_FAST 0 (a)
6 LOAD_CONST 2 (4)
8 BINARY_ADD
10 STORE_FAST 1 (b)
4 12 LOAD_FAST 0 (a)
14 LOAD_FAST 1 (b)
16 LOAD_CONST 3 (2)
18 BINARY_MULTIPLY
20 BINARY_ADD
22 RETURN_VALUE
Notice how the last number is 4 - that's the number the last line of the function.
More about the structure of co_lnotab
can be found here and here.
def span_of_function(func):
start = func.__code__.co_firstlineno
end = start + sum(line_increment for i, line_increment in enumerate(func.__code__.co_lnotab) if i % 2)
return start, end
def hello(name):
print(f'Hello, {name}!')
return 5
print(span_of_function(span_of_function))
print(span_of_function(hello))
Output:
$ python3 test.py
(1, 5)
(7, 9)