Added examples of problems using Coverage.py with macros.
This commit is contained in:
parent
7cd0afcdb9
commit
adb9e26750
|
|
@ -0,0 +1,9 @@
|
||||||
|
[run]
|
||||||
|
source =
|
||||||
|
.
|
||||||
|
|
||||||
|
[report]
|
||||||
|
exclude_lines =
|
||||||
|
pragma: no cover
|
||||||
|
# raise RuntimeError
|
||||||
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/make
|
||||||
|
#PY_COV=python3-coverage
|
||||||
|
PY_COV=$(HOME)/.local/bin/coverage3
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(PY_COV) erase
|
||||||
|
$(PY_COV) run -a --timid ./try_mymacros.py
|
||||||
|
$(PY_COV) report
|
||||||
|
$(PY_COV) html
|
||||||
|
x-www-browser htmlcov/index.html
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Coverage and macropy
|
||||||
|
|
||||||
|
When using the `with` macro block [Coverage.py](https://coverage.readthedocs.io/en/coverage-5.1/) fails to detect some sentences.
|
||||||
|
|
||||||
|
Here is a small example to show it. The `mymacros.py` contains a macro that was reduced to *do nothing*.
|
||||||
|
|
||||||
|
Running `make` you can see how 3 lines in `application.py` are marked as uncovered.
|
||||||
|
|
||||||
|
I commented these lines in the code with `# <--- Not covered?`
|
||||||
|
|
||||||
|
The coverage version used is the Debian stable one:
|
||||||
|
|
||||||
|
```
|
||||||
|
Coverage.py, version 4.5.2 with C extension
|
||||||
|
Documentation at https://coverage.readthedocs.io
|
||||||
|
```
|
||||||
|
|
||||||
|
I also tried the code from GitHub (installed on my user:
|
||||||
|
|
||||||
|
```
|
||||||
|
Coverage.py, version 5.1.1a0 with C extension
|
||||||
|
Full documentation is at https://coverage.readthedocs.io/en/coverage-5.1.1a0
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
from mymacros import macros, document # noqa: F401
|
||||||
|
|
||||||
|
with document: # <--- Not covered?
|
||||||
|
# comentario a
|
||||||
|
a = "5.1"
|
||||||
|
""" docu a """
|
||||||
|
b = False
|
||||||
|
""" docu b """
|
||||||
|
c = 3
|
||||||
|
""" docu c """ # <--- Not covered?
|
||||||
|
|
||||||
|
|
||||||
|
class d(object):
|
||||||
|
def __init__(self):
|
||||||
|
with document: # <--- Not covered?
|
||||||
|
self.at1 = 4.5
|
||||||
|
""" documenting d.at1 """
|
||||||
|
|
||||||
|
|
||||||
|
# print("a = "+str(a)+" # "+_help_a) # noqa: F821
|
||||||
|
# print("b = "+str(b)+" # "+_help_b) # noqa: F821
|
||||||
|
# print("c = "+str(c)+" # "+_help_c) # noqa: F821
|
||||||
|
# e = d()
|
||||||
|
# print("e.at1 = "+str(e.at1)+" # "+e._help_at1) # noqa: F821
|
||||||
|
print("a = "+str(a))
|
||||||
|
print("b = "+str(b))
|
||||||
|
print("c = "+str(c))
|
||||||
|
e = d()
|
||||||
|
print("e.at1 = "+str(e.at1))
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
from macropy.core.macros import Macros
|
||||||
|
from ast import (Assign, Name, Attribute, Expr, Num, Str, NameConstant, Load, Store)
|
||||||
|
|
||||||
|
macros = Macros()
|
||||||
|
|
||||||
|
|
||||||
|
@macros.block
|
||||||
|
def document(tree, **kw):
|
||||||
|
# Simplify it just to show the problem isn't related to the content of the macro
|
||||||
|
return tree
|
||||||
|
""" This macro takes literal strings and converts them into:
|
||||||
|
_help_ID = type_hint+STRING
|
||||||
|
where:
|
||||||
|
ID is the first target of the last assignment.
|
||||||
|
type_hint is the assigned type and default value (only works for a few types)
|
||||||
|
STRING is the literal string """
|
||||||
|
for n in range(len(tree)):
|
||||||
|
s = tree[n]
|
||||||
|
if not n:
|
||||||
|
prev = s
|
||||||
|
continue
|
||||||
|
# The whole sentence is a string?
|
||||||
|
if (isinstance(s, Expr) and isinstance(s.value, Str) and
|
||||||
|
# and the previous is an assign
|
||||||
|
isinstance(prev, Assign)): # noqa: E128
|
||||||
|
# Apply it to the first target
|
||||||
|
target = prev.targets[0]
|
||||||
|
value = prev.value
|
||||||
|
# Extract its name
|
||||||
|
# variables and attributes are supported
|
||||||
|
if isinstance(target, Name):
|
||||||
|
name = target.id
|
||||||
|
is_attr = False
|
||||||
|
elif isinstance(target, Attribute):
|
||||||
|
name = target.attr
|
||||||
|
is_attr = True
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
# Remove starting underscore
|
||||||
|
if name[0] == '_':
|
||||||
|
name = name[1:]
|
||||||
|
# Create a _help_ID
|
||||||
|
doc_id = '_help_'+name
|
||||||
|
# Create the type hint for numbers, strings and booleans
|
||||||
|
type_hint = ''
|
||||||
|
if isinstance(value, Num):
|
||||||
|
type_hint = '[number={}]'.format(value.n)
|
||||||
|
elif isinstance(value, Str):
|
||||||
|
type_hint = "[string='{}']".format(value.s)
|
||||||
|
elif isinstance(value, NameConstant) and isinstance(value.value, bool):
|
||||||
|
type_hint = '[boolean={}]'.format(str(value.value).lower())
|
||||||
|
# Transform the string into an assign for _help_ID
|
||||||
|
if is_attr:
|
||||||
|
target = Attribute(value=Name(id='self', ctx=Load()), attr=doc_id, ctx=Store())
|
||||||
|
else:
|
||||||
|
target = Name(id=doc_id, ctx=Store())
|
||||||
|
tree[n] = Assign(targets=[target], value=Str(s=type_hint+s.value.s))
|
||||||
|
prev = s
|
||||||
|
# Return the modified AST
|
||||||
|
return tree
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
import macropy.activate # noqa: F401
|
||||||
|
import application # noqa: F401
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
[run]
|
||||||
|
source =
|
||||||
|
.
|
||||||
|
|
||||||
|
[report]
|
||||||
|
exclude_lines =
|
||||||
|
pragma: no cover
|
||||||
|
# raise RuntimeError
|
||||||
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/make
|
||||||
|
#PY_COV=python3-coverage
|
||||||
|
PY_COV=$(HOME)/.local/bin/coverage3
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(PY_COV) erase
|
||||||
|
$(PY_COV) run -a --timid ./try_mymacros.py
|
||||||
|
$(PY_COV) report
|
||||||
|
$(PY_COV) html
|
||||||
|
x-www-browser htmlcov/index.html
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Coverage and mcpy
|
||||||
|
|
||||||
|
When using the `with` macro block [Coverage.py](https://coverage.readthedocs.io/en/coverage-5.1/) fails to detect some sentences.
|
||||||
|
|
||||||
|
Here is a small example to show it. The `mymacros.py` contains a macro that was reduced to *do nothing*.
|
||||||
|
|
||||||
|
Running `make` you can see how 3 lines in `application.py` are marked as uncovered.
|
||||||
|
|
||||||
|
I commented these lines in the code with `# <--- Not covered?`
|
||||||
|
|
||||||
|
The coverage version used is the Debian stable one:
|
||||||
|
|
||||||
|
```
|
||||||
|
Coverage.py, version 4.5.2 with C extension
|
||||||
|
Documentation at https://coverage.readthedocs.io
|
||||||
|
```
|
||||||
|
|
||||||
|
I also tried the code from GitHub (installed on my user:
|
||||||
|
|
||||||
|
```
|
||||||
|
Coverage.py, version 5.1.1a0 with C extension
|
||||||
|
Full documentation is at https://coverage.readthedocs.io/en/coverage-5.1.1a0
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
from mymacros import macros, document # noqa: F401
|
||||||
|
|
||||||
|
with document: # <--- Not covered?
|
||||||
|
# comentario a
|
||||||
|
a = "5.1"
|
||||||
|
""" docu a """
|
||||||
|
b = False
|
||||||
|
""" docu b """
|
||||||
|
c = 3
|
||||||
|
""" docu c """ # <--- Not covered?
|
||||||
|
|
||||||
|
|
||||||
|
class d(object):
|
||||||
|
def __init__(self):
|
||||||
|
with document:
|
||||||
|
self.at1 = 4.5
|
||||||
|
""" documenting d.at1 """ # <--- Not covered?
|
||||||
|
|
||||||
|
|
||||||
|
# print("a = "+str(a)+" # "+_help_a) # noqa: F821
|
||||||
|
# print("b = "+str(b)+" # "+_help_b) # noqa: F821
|
||||||
|
# print("c = "+str(c)+" # "+_help_c) # noqa: F821
|
||||||
|
# e = d()
|
||||||
|
# print("e.at1 = "+str(e.at1)+" # "+e._help_at1) # noqa: F821
|
||||||
|
print("a = "+str(a))
|
||||||
|
print("b = "+str(b))
|
||||||
|
print("c = "+str(c))
|
||||||
|
e = d()
|
||||||
|
print("e.at1 = "+str(e.at1))
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
from ast import (Assign, Name, Attribute, Expr, Num, Str, NameConstant, Load, Store)
|
||||||
|
|
||||||
|
|
||||||
|
def document(tree, **kw):
|
||||||
|
""" This macro takes literal strings and converts them into:
|
||||||
|
_help_ID = type_hint+STRING
|
||||||
|
where:
|
||||||
|
ID is the first target of the last assignment.
|
||||||
|
type_hint is the assigned type and default value (only works for a few types)
|
||||||
|
STRING is the literal string """
|
||||||
|
# Simplify it just to show the problem isn't related to the content of the macro
|
||||||
|
return tree
|
||||||
|
for n in range(len(tree)):
|
||||||
|
s = tree[n]
|
||||||
|
if not n:
|
||||||
|
prev = s
|
||||||
|
continue
|
||||||
|
# The whole sentence is a string?
|
||||||
|
if (isinstance(s, Expr) and isinstance(s.value, Str) and
|
||||||
|
# and the previous is an assign
|
||||||
|
isinstance(prev, Assign)): # noqa: E128
|
||||||
|
# Apply it to the first target
|
||||||
|
target = prev.targets[0]
|
||||||
|
value = prev.value
|
||||||
|
# Extract its name
|
||||||
|
# variables and attributes are supported
|
||||||
|
if isinstance(target, Name):
|
||||||
|
name = target.id
|
||||||
|
is_attr = False
|
||||||
|
elif isinstance(target, Attribute):
|
||||||
|
name = target.attr
|
||||||
|
is_attr = True
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
# Remove starting underscore
|
||||||
|
if name[0] == '_':
|
||||||
|
name = name[1:]
|
||||||
|
# Create a _help_ID
|
||||||
|
doc_id = '_help_'+name
|
||||||
|
# Create the type hint for numbers, strings and booleans
|
||||||
|
type_hint = ''
|
||||||
|
if isinstance(value, Num):
|
||||||
|
type_hint = '[number={}]'.format(value.n)
|
||||||
|
elif isinstance(value, Str):
|
||||||
|
type_hint = "[string='{}']".format(value.s)
|
||||||
|
elif isinstance(value, NameConstant) and isinstance(value.value, bool):
|
||||||
|
type_hint = '[boolean={}]'.format(str(value.value).lower())
|
||||||
|
# Transform the string into an assign for _help_ID
|
||||||
|
if is_attr:
|
||||||
|
target = Attribute(value=Name(id='self', ctx=Load()), attr=doc_id, ctx=Store())
|
||||||
|
else:
|
||||||
|
target = Name(id=doc_id, ctx=Store())
|
||||||
|
tree[n] = Assign(targets=[target], value=Str(s=type_hint+s.value.s))
|
||||||
|
prev = s
|
||||||
|
# Return the modified AST
|
||||||
|
return tree
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
import mcpy.activate # noqa: F401
|
||||||
|
import application # noqa: F401
|
||||||
Loading…
Reference in New Issue