FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
vscode-python/pythonFiles/testing_tools/adapter/pytest.py at master · raphaelpe/vscode-python · GitHub
raphaelpe
/
vscode-python
Public
forked from
microsoft/vscode-python
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Pull requests
0
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Pull requests
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
vscode-python
/
pythonFiles
/
testing_tools
/
adapter
/
pytest.py
Copy path
More file actions
More file actions
Latest commit
History
History
History
403 lines (346 loc) · 13.1 KB
Breadcrumbs
vscode-python
/
pythonFiles
/
testing_tools
/
adapter
/
pytest.py
Copy path
File metadata and controls
403 lines (346 loc) · 13.1 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
from
__future__
import
absolute_import
import
os
.
path
import
pytest
from
.
import
util
from
.
errors
import
UnsupportedCommandError
from
.
info
import
TestInfo
,
TestPath
,
ParentInfo
def
add_cli_subparser
(
cmd
,
name
,
parent
):
"""Add a new subparser to the given parent and add args to it."""
parser
=
parent
.
add_parser
(
name
)
if
cmd
==
'discover'
:
# For now we don't have any tool-specific CLI options to add.
pass
else
:
raise
UnsupportedCommandError
(
cmd
)
return
parser
def
discover
(
pytestargs
=
None
,
hidestdio
=
False
,
_pytest_main
=
pytest
.
main
,
_plugin
=
None
,
**
_ignored
):
"""Return the results of test discovery."""
if
_plugin
is
None
:
_plugin
=
TestCollector
()
pytestargs
=
_adjust_pytest_args
(
pytestargs
)
# We use this helper rather than "-pno:terminal" due to possible
# platform-dependent issues.
with
util
.
hide_stdio
()
if
hidestdio
else
util
.
noop_cm
():
ec
=
_pytest_main
(
pytestargs
, [
_plugin
])
if
ec
!=
0
:
raise
Exception
(
'pytest discovery failed (exit code {})'
.
format
(
ec
))
if
not
_plugin
.
_started
:
raise
Exception
(
'pytest discovery did not start'
)
return
(
_plugin
.
_tests
.
parents
,
#[p._replace(
# id=p.id.lstrip('.' + os.path.sep),
# parentid=p.parentid.lstrip('.' + os.path.sep),
# )
# for p in _plugin._tests.parents],
list
(
_plugin
.
_tests
),
)
def
_adjust_pytest_args
(
pytestargs
):
pytestargs
=
list
(
pytestargs
)
if
pytestargs
else
[]
# Duplicate entries should be okay.
pytestargs
.
insert
(
0
,
'--collect-only'
)
# TODO: pull in code from:
# src/client/unittests/pytest/services/discoveryService.ts
# src/client/unittests/pytest/services/argsService.ts
return
pytestargs
class
TestCollector
(
object
):
"""This is a pytest plugin that collects the discovered tests."""
NORMCASE
=
staticmethod
(
os
.
path
.
normcase
)
PATHSEP
=
os
.
path
.
sep
def
__init__
(
self
,
tests
=
None
):
if
tests
is
None
:
tests
=
DiscoveredTests
()
self
.
_tests
=
tests
self
.
_started
=
False
# Relevant plugin hooks:
# https://docs.pytest.org/en/latest/reference.html#collection-hooks
def
pytest_collection_modifyitems
(
self
,
session
,
config
,
items
):
self
.
_started
=
True
self
.
_tests
.
reset
()
for
item
in
items
:
test
,
suiteids
=
_parse_item
(
item
,
self
.
NORMCASE
,
self
.
PATHSEP
)
self
.
_tests
.
add_test
(
test
,
suiteids
)
# This hook is not specified in the docs, so we also provide
# the "modifyitems" hook just in case.
def
pytest_collection_finish
(
self
,
session
):
self
.
_started
=
True
try
:
items
=
session
.
items
except
AttributeError
:
# TODO: Is there an alternative?
return
self
.
_tests
.
reset
()
for
item
in
items
:
test
,
suiteids
=
_parse_item
(
item
,
self
.
NORMCASE
,
self
.
PATHSEP
)
self
.
_tests
.
add_test
(
test
,
suiteids
)
class
DiscoveredTests
(
object
):
def
__init__
(
self
):
self
.
reset
()
def
__len__
(
self
):
return
len
(
self
.
_tests
)
def
__getitem__
(
self
,
index
):
return
self
.
_tests
[
index
]
@
property
def
parents
(
self
):
return
sorted
(
self
.
_parents
.
values
(),
key
=
lambda
v
: (
v
.
root
or
v
.
name
,
v
.
id
))
def
reset
(
self
):
self
.
_parents
=
{}
self
.
_tests
=
[]
def
add_test
(
self
,
test
,
suiteids
):
parentid
=
self
.
_ensure_parent
(
test
.
path
,
test
.
parentid
,
suiteids
)
test
=
test
.
_replace
(
parentid
=
parentid
)
if
not
test
.
id
.
startswith
(
'.'
+
os
.
path
.
sep
):
test
=
test
.
_replace
(
id
=
os
.
path
.
join
(
'.'
,
test
.
id
))
self
.
_tests
.
append
(
test
)
def
_ensure_parent
(
self
,
path
,
parentid
,
suiteids
):
if
not
parentid
.
startswith
(
'.'
+
os
.
path
.
sep
):
parentid
=
os
.
path
.
join
(
'.'
,
parentid
)
fileid
=
self
.
_ensure_file
(
path
.
root
,
path
.
relfile
)
rootdir
=
path
.
root
if
not
path
.
func
:
return
parentid
fullsuite
,
_
,
funcname
=
path
.
func
.
rpartition
(
'.'
)
suiteid
=
self
.
_ensure_suites
(
fullsuite
,
rootdir
,
fileid
,
suiteids
)
parent
=
suiteid
if
suiteid
else
fileid
if
path
.
sub
:
if
(
rootdir
,
parentid
)
not
in
self
.
_parents
:
funcinfo
=
ParentInfo
(
parentid
,
'function'
,
funcname
,
rootdir
,
parent
)
self
.
_parents
[(
rootdir
,
parentid
)]
=
funcinfo
elif
parent
!=
parentid
:
# TODO: What to do?
raise
NotImplementedError
return
parentid
def
_ensure_file
(
self
,
rootdir
,
relfile
):
if
(
rootdir
,
'.'
)
not
in
self
.
_parents
:
self
.
_parents
[(
rootdir
,
'.'
)]
=
ParentInfo
(
'.'
,
'folder'
,
rootdir
)
if
relfile
.
startswith
(
'.'
+
os
.
path
.
sep
):
fileid
=
relfile
else
:
fileid
=
relfile
=
os
.
path
.
join
(
'.'
,
relfile
)
if
(
rootdir
,
fileid
)
not
in
self
.
_parents
:
folderid
,
filebase
=
os
.
path
.
split
(
fileid
)
fileinfo
=
ParentInfo
(
fileid
,
'file'
,
filebase
,
rootdir
,
folderid
)
self
.
_parents
[(
rootdir
,
fileid
)]
=
fileinfo
while
folderid
!=
'.'
and
(
rootdir
,
folderid
)
not
in
self
.
_parents
:
parentid
,
name
=
os
.
path
.
split
(
folderid
)
folderinfo
=
ParentInfo
(
folderid
,
'folder'
,
name
,
rootdir
,
parentid
)
self
.
_parents
[(
rootdir
,
folderid
)]
=
folderinfo
folderid
=
parentid
return
relfile
def
_ensure_suites
(
self
,
fullsuite
,
rootdir
,
fileid
,
suiteids
):
if
not
fullsuite
:
if
suiteids
:
# TODO: What to do?
raise
NotImplementedError
return
None
if
len
(
suiteids
)
!=
fullsuite
.
count
(
'.'
)
+
1
:
# TODO: What to do?
raise
NotImplementedError
suiteid
=
suiteids
.
pop
()
if
not
suiteid
.
startswith
(
'.'
+
os
.
path
.
sep
):
suiteid
=
os
.
path
.
join
(
'.'
,
suiteid
)
final
=
suiteid
while
'.'
in
fullsuite
and
(
rootdir
,
suiteid
)
not
in
self
.
_parents
:
parentid
=
suiteids
.
pop
()
if
not
parentid
.
startswith
(
'.'
+
os
.
path
.
sep
):
parentid
=
os
.
path
.
join
(
'.'
,
parentid
)
fullsuite
,
_
,
name
=
fullsuite
.
rpartition
(
'.'
)
suiteinfo
=
ParentInfo
(
suiteid
,
'suite'
,
name
,
rootdir
,
parentid
)
self
.
_parents
[(
rootdir
,
suiteid
)]
=
suiteinfo
suiteid
=
parentid
else
:
name
=
fullsuite
suiteinfo
=
ParentInfo
(
suiteid
,
'suite'
,
name
,
rootdir
,
fileid
)
if
(
rootdir
,
suiteid
)
not
in
self
.
_parents
:
self
.
_parents
[(
rootdir
,
suiteid
)]
=
suiteinfo
return
final
def
_parse_item
(
item
,
_normcase
,
_pathsep
):
"""
(pytest.Collector)
pytest.Session
pytest.Package
pytest.Module
pytest.Class
(pytest.File)
(pytest.Item)
pytest.Function
"""
#_debug_item(item, showsummary=True)
kind
,
_
=
_get_item_kind
(
item
)
# Figure out the func, suites, and subs.
(
fileid
,
suiteids
,
suites
,
funcid
,
basename
,
parameterized
)
=
_parse_node_id
(
item
.
nodeid
,
kind
)
if
kind
==
'function'
:
funcname
=
basename
if
funcid
and
item
.
function
.
__name__
!=
funcname
:
# TODO: What to do?
raise
NotImplementedError
if
suites
:
testfunc
=
'.'
.
join
(
suites
)
+
'.'
+
funcname
else
:
testfunc
=
funcname
elif
kind
==
'doctest'
:
testfunc
=
None
funcname
=
None
# Figure out the file.
fspath
=
str
(
item
.
fspath
)
if
not
fspath
.
endswith
(
_pathsep
+
fileid
):
raise
NotImplementedError
filename
=
fspath
[
-
len
(
fileid
):]
testroot
=
str
(
item
.
fspath
)[:
-
len
(
fileid
)].
rstrip
(
_pathsep
)
if
_pathsep
in
filename
:
relfile
=
filename
else
:
relfile
=
'.'
+
_pathsep
+
filename
srcfile
,
lineno
,
fullname
=
item
.
location
if
srcfile
!=
fileid
:
# pytest supports discovery of tests imported from other
# modules. This is reflected by a different filename
# in item.location.
if
_normcase
(
fileid
)
==
_normcase
(
srcfile
):
srcfile
=
fileid
else
:
srcfile
=
relfile
location
=
'{}:{}'
.
format
(
srcfile
,
lineno
)
if
kind
==
'function'
:
if
testfunc
and
fullname
!=
testfunc
+
parameterized
:
print
(
fullname
,
testfunc
)
# TODO: What to do?
raise
NotImplementedError
elif
kind
==
'doctest'
:
if
testfunc
and
fullname
!=
testfunc
+
parameterized
:
print
(
fullname
,
testfunc
)
# TODO: What to do?
raise
NotImplementedError
# Sort out the parent.
if
parameterized
:
parentid
=
funcid
elif
suites
:
parentid
=
suiteids
[
-
1
]
else
:
parentid
=
fileid
# Sort out markers.
# See: https://docs.pytest.org/en/latest/reference.html#marks
markers
=
set
()
for
marker
in
item
.
own_markers
:
if
marker
.
name
==
'parameterize'
:
# We've already covered these.
continue
elif
marker
.
name
==
'skip'
:
markers
.
add
(
'skip'
)
elif
marker
.
name
==
'skipif'
:
markers
.
add
(
'skip-if'
)
elif
marker
.
name
==
'xfail'
:
markers
.
add
(
'expected-failure'
)
# TODO: Support other markers?
test
=
TestInfo
(
id
=
item
.
nodeid
,
name
=
item
.
name
,
path
=
TestPath
(
root
=
testroot
,
relfile
=
relfile
,
func
=
testfunc
,
sub
=
[
parameterized
]
if
parameterized
else
None
,
),
source
=
location
,
markers
=
sorted
(
markers
)
if
markers
else
None
,
parentid
=
parentid
,
)
return
test
,
suiteids
def
_parse_node_id
(
nodeid
,
kind
=
'function'
):
if
kind
==
'doctest'
:
try
:
parentid
,
name
=
nodeid
.
split
(
'::'
)
except
ValueError
:
# TODO: Unexpected! What to do?
raise
NotImplementedError
funcid
=
None
parameterized
=
''
else
:
parameterized
=
''
if
nodeid
.
endswith
(
']'
):
funcid
,
sep
,
parameterized
=
nodeid
.
partition
(
'['
)
if
not
sep
:
# TODO: Unexpected! What to do?
raise
NotImplementedError
parameterized
=
sep
+
parameterized
else
:
funcid
=
nodeid
parentid
,
_
,
name
=
funcid
.
rpartition
(
'::'
)
if
not
name
:
# TODO: What to do? We expect at least a filename and a function
raise
NotImplementedError
suites
=
[]
suiteids
=
[]
while
'::'
in
parentid
:
suiteids
.
insert
(
0
,
parentid
)
parentid
,
_
,
suitename
=
parentid
.
rpartition
(
'::'
)
suites
.
insert
(
0
,
suitename
)
fileid
=
parentid
return
fileid
,
suiteids
,
suites
,
funcid
,
name
,
parameterized
def
_get_item_kind
(
item
):
"""Return (kind, isunittest) for the given item."""
try
:
itemtype
=
item
.
kind
except
AttributeError
:
itemtype
=
item
.
__class__
.
__name__
if
itemtype
==
'DoctestItem'
:
return
'doctest'
,
False
elif
itemtype
==
'Function'
:
return
'function'
,
False
elif
itemtype
==
'TestCaseFunction'
:
return
'function'
,
True
elif
item
.
hasattr
(
'function'
):
return
'function'
,
False
else
:
return
None
,
False
#############################
# useful for debugging
def
_debug_item
(
item
,
showsummary
=
False
):
item
.
_debugging
=
True
try
:
# TODO: Make a PytestTest class to wrap the item?
summary
=
{
'id'
:
item
.
nodeid
,
'kind'
:
_get_item_kind
(
item
),
'class'
:
item
.
__class__
.
__name__
,
'name'
:
item
.
name
,
'fspath'
:
item
.
fspath
,
'location'
:
item
.
location
,
'func'
:
getattr
(
item
,
'function'
,
None
),
'markers'
:
item
.
own_markers
,
#'markers': list(item.iter_markers()),
'props'
:
item
.
user_properties
,
'attrnames'
:
dir
(
item
),
}
finally
:
item
.
_debugging
=
False
if
showsummary
:
print
(
item
.
nodeid
)
for
key
in
(
'kind'
,
'class'
,
'name'
,
'fspath'
,
'location'
,
'func'
,
'markers'
,
'props'
):
print
(
' {:12} {}'
.
format
(
key
,
summary
[
key
]))
print
()
return
summary
def
_group_attr_names
(
attrnames
):
grouped
=
{
'dunder'
: [
n
for
n
in
attrnames
if
n
.
startswith
(
'__'
)
and
n
.
endswith
(
'__'
)],
'private'
: [
n
for
n
in
attrnames
if
n
.
startswith
(
'_'
)],
'constants'
: [
n
for
n
in
attrnames
if
n
.
isupper
()],
'classes'
: [
n
for
n
in
attrnames
if
n
==
n
.
capitalize
()
and
not
n
.
isupper
()],
'vars'
: [
n
for
n
in
attrnames
if
n
.
islower
()],
}
grouped
[
'other'
]
=
[
n
for
n
in
attrnames
if
n
not
in
grouped
[
'dunder'
]
and
n
not
in
grouped
[
'private'
]
and
n
not
in
grouped
[
'constants'
]
and
n
not
in
grouped
[
'classes'
]
and
n
not
in
grouped
[
'vars'
]
]
return
grouped
Back
|
FazBrowse Home
|
New Git URL