Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
avocado
提交
c790bd20
A
avocado
项目概览
openeuler
/
avocado
通知
0
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
A
avocado
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
c790bd20
编写于
9月 07, 2018
作者:
C
Cleber Rosa
浏览文件
操作
浏览文件
下载
差异文件
Merge remote-tracking branch 'ldoktor/recursive-rework2.d'
Signed-off-by:
N
Cleber Rosa
<
crosa@redhat.com
>
上级
118a37e1
fd10ed83
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
241 addition
and
150 deletion
+241
-150
avocado/core/safeloader.py
avocado/core/safeloader.py
+166
-150
selftests/.data/loader_instrumented/dont_crash.py
selftests/.data/loader_instrumented/dont_crash.py
+60
-0
selftests/unit/test_loader.py
selftests/unit/test_loader.py
+15
-0
未找到文件。
avocado/core/safeloader.py
浏览文件 @
c790bd20
...
...
@@ -26,6 +26,54 @@ import sys
from
..utils
import
data_structures
class
AvocadoModule
(
object
):
"""
Representation of a module that might contain avocado.Test tests
"""
__slots__
=
'path'
,
'test_import'
,
'mod_import'
,
'mod'
def
__init__
(
self
,
path
,
test_import
=
False
,
mod_import
=
False
):
self
.
path
=
path
self
.
test_import
=
test_import
self
.
mod_import
=
mod_import
if
os
.
path
.
isdir
(
path
):
self
.
path
=
os
.
path
.
join
(
path
,
"__init__.py"
)
else
:
self
.
path
=
path
with
open
(
self
.
path
)
as
source_file
:
self
.
mod
=
ast
.
parse
(
source_file
.
read
(),
self
.
path
)
def
iter_classes
(
self
):
"""
Iter through classes and keep track of imported avocado statements
"""
for
statement
in
self
.
mod
.
body
:
# Looking for a 'from avocado import Test'
if
(
isinstance
(
statement
,
ast
.
ImportFrom
)
and
statement
.
module
==
'avocado'
):
for
name
in
statement
.
names
:
if
name
.
name
==
'Test'
:
if
name
.
asname
is
not
None
:
self
.
test_import
=
name
.
asname
else
:
self
.
test_import
=
name
.
name
break
# Looking for a 'import avocado'
elif
isinstance
(
statement
,
ast
.
Import
):
for
name
in
statement
.
names
:
if
name
.
name
==
'avocado'
:
if
name
.
asname
is
not
None
:
self
.
mod_import
=
name
.
nasname
else
:
self
.
mod_import
=
name
.
name
# Looking for a 'class Anything(anything):'
elif
isinstance
(
statement
,
ast
.
ClassDef
):
yield
statement
def
modules_imported_as
(
module
):
"""
Returns a mapping of imported module names whether using aliases or not
...
...
@@ -193,166 +241,134 @@ def find_avocado_tests(path, class_name=None):
force-disabled.
:rtype: tuple
"""
# If only the Test class was imported from the avocado namespace
test_import
=
False
# The name used, in case of 'from avocado import Test as AvocadoTest'
test_import_name
=
None
# If the "avocado" module itself was imported
mod_import
=
False
# The name used, in case of 'import avocado as avocadolib'
mod_import_name
=
None
module
=
AvocadoModule
(
path
)
# The resulting test classes
result
=
collections
.
OrderedDict
()
disabled
=
set
()
if
os
.
path
.
isdir
(
path
):
path
=
os
.
path
.
join
(
path
,
"__init__.py"
)
with
open
(
path
)
as
source_file
:
mod
=
ast
.
parse
(
source_file
.
read
(),
path
)
for
statement
in
mod
.
body
:
# Looking for a 'from avocado import Test'
if
(
isinstance
(
statement
,
ast
.
ImportFrom
)
and
statement
.
module
==
'avocado'
):
for
name
in
statement
.
names
:
if
name
.
name
==
'Test'
:
test_import
=
True
if
name
.
asname
is
not
None
:
test_import_name
=
name
.
asname
else
:
test_import_name
=
name
.
name
break
# Looking for a 'import avocado'
elif
isinstance
(
statement
,
ast
.
Import
):
for
name
in
statement
.
names
:
if
name
.
name
==
'avocado'
:
mod_import
=
True
if
name
.
asname
is
not
None
:
mod_import_name
=
name
.
nasname
else
:
mod_import_name
=
name
.
name
# Looking for a 'class Anything(anything):'
elif
isinstance
(
statement
,
ast
.
ClassDef
):
# class_name will exist only under recursion. In that
# case, we will only process the class if it has the
# expected class_name.
if
class_name
is
not
None
and
class_name
!=
statement
.
name
:
continue
docstring
=
ast
.
get_docstring
(
statement
)
# Looking for a class that has in the docstring either
# ":avocado: enable" or ":avocado: disable
has_disable
=
check_docstring_directive
(
docstring
,
'disable'
)
if
(
has_disable
and
class_name
is
None
):
disabled
.
add
(
statement
.
name
)
continue
cl_tags
=
get_docstring_directives_tags
(
docstring
)
has_enable
=
check_docstring_directive
(
docstring
,
'enable'
)
if
(
has_enable
and
class_name
is
None
):
info
=
get_methods_info
(
statement
.
body
,
cl_tags
)
result
[
statement
.
name
]
=
info
continue
# Looking for the 'recursive' docstring or a 'class_name'
# (meaning we are under recursion)
has_recurse
=
check_docstring_directive
(
docstring
,
'recursive'
)
if
(
has_recurse
or
class_name
is
not
None
):
info
=
get_methods_info
(
statement
.
body
,
cl_tags
)
result
[
statement
.
name
]
=
info
# Getting the list of parents of the current class
parents
=
statement
.
bases
# Searching the parents in the same module
for
parent
in
parents
[:]:
for
klass
in
module
.
iter_classes
():
# class_name will exist only under recursion. In that
# case, we will only process the class if it has the
# expected class_name.
if
class_name
is
not
None
and
class_name
!=
klass
.
name
:
continue
docstring
=
ast
.
get_docstring
(
klass
)
# Looking for a class that has in the docstring either
# ":avocado: enable" or ":avocado: disable
has_disable
=
check_docstring_directive
(
docstring
,
'disable'
)
if
(
has_disable
and
class_name
is
None
):
disabled
.
add
(
klass
.
name
)
continue
cl_tags
=
get_docstring_directives_tags
(
docstring
)
has_enable
=
check_docstring_directive
(
docstring
,
'enable'
)
if
(
has_enable
and
class_name
is
None
):
info
=
get_methods_info
(
klass
.
body
,
cl_tags
)
result
[
klass
.
name
]
=
info
continue
# Looking for the 'recursive' docstring or a 'class_name'
# (meaning we are under recursion)
has_recurse
=
check_docstring_directive
(
docstring
,
'recursive'
)
if
(
has_recurse
or
class_name
is
not
None
):
info
=
get_methods_info
(
klass
.
body
,
cl_tags
)
result
[
klass
.
name
]
=
info
# Getting the list of parents of the current class
parents
=
klass
.
bases
# Searching the parents in the same module
for
parent
in
parents
[:]:
# Looking for a 'class FooTest(Parent)'
if
not
isinstance
(
parent
,
ast
.
Name
):
# 'class FooTest(bar.Bar)' not supported withing
# a module
continue
parent_class
=
parent
.
id
res
,
dis
=
find_avocado_tests
(
path
,
parent_class
)
if
res
:
parents
.
remove
(
parent
)
for
cls
in
res
:
info
.
extend
(
res
[
cls
])
disabled
.
update
(
dis
)
# If there are parents left to be discovered, they
# might be in a different module.
for
parent
in
parents
:
if
isinstance
(
parent
,
ast
.
Attribute
):
# Looking for a 'class FooTest(module.Parent)'
if
isinstance
(
parent
,
ast
.
Attribute
):
parent_class
=
parent
.
attr
parent_module
=
parent
.
value
.
id
parent_class
=
parent
.
attr
else
:
# Looking for a 'class FooTest(Parent)'
else
:
parent_class
=
parent
.
id
res
,
dis
=
find_avocado_tests
(
path
,
parent_class
)
if
res
:
parents
.
remove
(
parent
)
for
cls
in
res
:
info
.
extend
(
res
[
cls
])
disabled
.
update
(
dis
)
# If there are parents left to be discovered, they
# might be in a different module.
for
parent
in
parents
:
if
isinstance
(
parent
,
ast
.
Attribute
):
# Looking for a 'class FooTest(module.Parent)'
parent_module
=
parent
.
value
.
id
parent_class
=
parent
.
attr
else
:
# Looking for a 'class FooTest(Parent)'
parent_module
=
None
parent_class
=
parent
.
id
for
node
in
mod
.
body
:
reference
=
None
# Looking for 'from parent import class'
if
isinstance
(
node
,
ast
.
ImportFrom
):
reference
=
parent_class
# Looking for 'import parent'
elif
isinstance
(
node
,
ast
.
Import
):
reference
=
parent_module
if
reference
is
None
:
continue
for
artifact
in
node
.
names
:
# Looking for a class alias
# ('from parent import class as alias')
if
artifact
.
asname
is
not
None
:
parent_class
=
reference
=
artifact
.
name
# If the parent class or the parent module
# is found in the imports, discover the
# parent module path and find the parent
# class there
if
artifact
.
name
==
reference
:
modules_paths
=
[
os
.
path
.
dirname
(
path
)]
modules_paths
.
extend
(
sys
.
path
)
if
parent_module
is
None
:
parent_module
=
node
.
module
_
,
ppath
,
_
=
imp
.
find_module
(
parent_module
,
modules_paths
)
res
,
dis
=
find_avocado_tests
(
ppath
,
parent_class
)
if
res
:
for
cls
in
res
:
info
.
extend
(
res
[
cls
])
disabled
.
update
(
dis
)
parent_module
=
None
parent_class
=
parent
.
id
for
node
in
module
.
mod
.
body
:
reference
=
None
# Looking for 'from parent import class'
if
isinstance
(
node
,
ast
.
ImportFrom
):
reference
=
parent_class
# Looking for 'import parent'
elif
isinstance
(
node
,
ast
.
Import
):
reference
=
parent_module
if
reference
is
None
:
continue
for
artifact
in
node
.
names
:
# Looking for a class alias
# ('from parent import class as alias')
if
artifact
.
asname
is
not
None
:
parent_class
=
reference
=
artifact
.
name
# If the parent class or the parent module
# is found in the imports, discover the
# parent module path and find the parent
# class there
if
artifact
.
name
==
reference
:
modules_paths
=
[
os
.
path
.
dirname
(
path
)]
modules_paths
.
extend
(
sys
.
path
)
if
parent_module
is
None
:
parent_module
=
node
.
module
_
,
ppath
,
_
=
imp
.
find_module
(
parent_module
,
modules_paths
)
res
,
dis
=
find_avocado_tests
(
ppath
,
parent_class
)
if
res
:
for
cls
in
res
:
info
.
extend
(
res
[
cls
])
disabled
.
update
(
dis
)
continue
# Looking for a 'class FooTest(Test):'
if
module
.
test_import
:
base_ids
=
[
base
.
id
for
base
in
klass
.
bases
if
isinstance
(
base
,
ast
.
Name
)]
# Looking for a 'class FooTest(Test):'
if
module
.
test_import
in
base_ids
:
info
=
get_methods_info
(
klass
.
body
,
cl_tags
)
result
[
klass
.
name
]
=
info
continue
if
test_import
:
base_ids
=
[
base
.
id
for
base
in
statement
.
bases
if
hasattr
(
base
,
'id'
)]
# Looking for a 'class FooTest(Test):'
if
test_import_name
in
base_ids
:
info
=
get_methods_info
(
statement
.
body
,
# Looking for a 'class FooTest(avocado.Test):'
if
module
.
mod_import
:
for
base
in
klass
.
bases
:
if
not
isinstance
(
base
,
ast
.
Attribute
):
# Check only 'module.Class' bases
continue
cls_module
=
base
.
value
.
id
cls_name
=
base
.
attr
if
cls_module
==
module
.
mod_import
and
cls_name
==
'Test'
:
info
=
get_methods_info
(
klass
.
body
,
cl_tags
)
result
[
statement
.
name
]
=
info
result
[
klass
.
name
]
=
info
continue
# Looking for a 'class FooTest(avocado.Test):'
if
mod_import
:
for
base
in
statement
.
bases
:
module
=
base
.
value
.
id
klass
=
base
.
attr
if
module
==
mod_import_name
and
klass
==
'Test'
:
info
=
get_methods_info
(
statement
.
body
,
cl_tags
)
result
[
statement
.
name
]
=
info
continue
return
result
,
disabled
selftests/.data/loader_instrumented/dont_crash.py
0 → 100644
浏览文件 @
c790bd20
# Having 2 imports forces both paths
import
avocado
# Should not be discovered as "Test" import did not happened yet
class
DontCrash0
(
Test
):
def
test
(
self
):
pass
from
avocado
import
Test
# on "import avocado" this requires some skipping
class
DontCrash1
(
object
):
pass
# This one should be discovered no matter how other
# classes break
class
DiscoverMe
(
avocado
.
Test
):
def
test
(
self
):
pass
# The same as "DontCrash1" only this one should be discovered
class
DiscoverMe2
(
object
,
avocado
.
Test
,
main
):
# pylint: disable=E0240,E0602
def
test
(
self
):
pass
# The same as "DontCrash1" only this one should be discovered
class
DiscoverMe3
(
object
,
Test
,
main
):
# pylint: disable=E0240,E0602
def
test
(
self
):
pass
class
DontCrash2p
(
object
):
class
Bar
(
avocado
.
Test
):
def
test
(
self
):
pass
# Only top-level-namespace classes are allowed for
# in-module-class definitions
class
DontCrash2
(
DontCrash2p
.
Bar
):
""":avocado: recursive"""
# Class DiscoverMe4p is defined after this one
class
DiscoverMe4
(
DiscoverMe4p
):
# pylint: disable=E0601
""":avocado: recursive"""
class
DiscoverMe4p
(
object
):
def
test
(
self
):
pass
dont_crash3_on_broken_syntax
# pylint: disable=E0602,W0104
selftests/unit/test_loader.py
浏览文件 @
c790bd20
...
...
@@ -479,6 +479,21 @@ class LoaderTest(unittest.TestCase):
"test_dir"
:
os
.
path
.
dirname
(
python_unittest
.
path
)})]
self
.
assertEqual
(
tests
,
exp
)
def
test_mod_import_and_classes
(
self
):
path
=
os
.
path
.
join
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
__file__
)),
'.data'
,
'loader_instrumented'
,
'dont_crash.py'
)
tests
=
self
.
loader
.
discover
(
path
)
exps
=
[(
'DiscoverMe'
,
'selftests/.data/loader_instrumented/dont_crash.py:DiscoverMe.test'
),
(
'DiscoverMe2'
,
'selftests/.data/loader_instrumented/dont_crash.py:DiscoverMe2.test'
),
(
'DiscoverMe3'
,
'selftests/.data/loader_instrumented/dont_crash.py:DiscoverMe3.test'
),
(
'DiscoverMe4'
,
'selftests/.data/loader_instrumented/dont_crash.py:DiscoverMe4.test'
)]
for
exp
,
tst
in
zip
(
exps
,
tests
):
# Test class
self
.
assertEqual
(
tst
[
0
],
exp
[
0
])
# Test name (path)
# py2 reports relpath, py3 abspath
self
.
assertEqual
(
os
.
path
.
abspath
(
tst
[
1
][
'name'
]),
os
.
path
.
abspath
(
exp
[
1
]))
def
tearDown
(
self
):
shutil
.
rmtree
(
self
.
tmpdir
)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录