package_hash: remove all unassigned strings, not just docstrings
Some packages use top-level unassigned strings instead of comments, either just after a docstring on in the body somewhere else. Ignore those strings becasue they have no effect on package behavior. - [x] adjust RemoveDocstrings to remove all free-standing strings. - [x] move tests for util/package_hash.py to test/util/package_hash.py Co-authored-by: Danny McClanahan <1305167+cosmicexplorer@users.noreply.github.com>
This commit is contained in:
parent
572fbf4f49
commit
8880a00862
@ -3,18 +3,22 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
|
||||||
|
import ast
|
||||||
|
|
||||||
|
import spack.paths
|
||||||
|
import spack.util.package_hash as ph
|
||||||
from spack.spec import Spec
|
from spack.spec import Spec
|
||||||
from spack.util.package_hash import package_content, package_hash
|
from spack.util.unparse import unparse
|
||||||
|
|
||||||
|
|
||||||
def test_hash(tmpdir, mock_packages, config):
|
def test_hash(tmpdir, mock_packages, config):
|
||||||
package_hash("hash-test1@1.2")
|
ph.package_hash("hash-test1@1.2")
|
||||||
|
|
||||||
|
|
||||||
def test_different_variants(tmpdir, mock_packages, config):
|
def test_different_variants(tmpdir, mock_packages, config):
|
||||||
spec1 = Spec("hash-test1@1.2 +variantx")
|
spec1 = Spec("hash-test1@1.2 +variantx")
|
||||||
spec2 = Spec("hash-test1@1.2 +varianty")
|
spec2 = Spec("hash-test1@1.2 +varianty")
|
||||||
assert package_hash(spec1) == package_hash(spec2)
|
assert ph.package_hash(spec1) == ph.package_hash(spec2)
|
||||||
|
|
||||||
|
|
||||||
def test_all_same_but_name(tmpdir, mock_packages, config):
|
def test_all_same_but_name(tmpdir, mock_packages, config):
|
||||||
@ -55,11 +59,67 @@ def test_all_same_but_install(tmpdir, mock_packages, config):
|
|||||||
|
|
||||||
|
|
||||||
def compare_sans_name(eq, spec1, spec2):
|
def compare_sans_name(eq, spec1, spec2):
|
||||||
content1 = package_content(spec1)
|
content1 = ph.package_content(spec1)
|
||||||
content1 = content1.replace(spec1.package.__class__.__name__, '')
|
content1 = content1.replace(spec1.package.__class__.__name__, '')
|
||||||
content2 = package_content(spec2)
|
content2 = ph.package_content(spec2)
|
||||||
content2 = content2.replace(spec2.package.__class__.__name__, '')
|
content2 = content2.replace(spec2.package.__class__.__name__, '')
|
||||||
if eq:
|
if eq:
|
||||||
assert content1 == content2
|
assert content1 == content2
|
||||||
else:
|
else:
|
||||||
assert content1 != content2
|
assert content1 != content2
|
||||||
|
|
||||||
|
|
||||||
|
many_strings = '''\
|
||||||
|
"""ONE"""
|
||||||
|
"""TWO"""
|
||||||
|
|
||||||
|
var = "THREE" # make sure this is not removed
|
||||||
|
|
||||||
|
"FOUR"
|
||||||
|
|
||||||
|
class ManyDocstrings:
|
||||||
|
"""FIVE"""
|
||||||
|
"""SIX"""
|
||||||
|
|
||||||
|
x = "SEVEN"
|
||||||
|
|
||||||
|
def method1():
|
||||||
|
"""EIGHT"""
|
||||||
|
|
||||||
|
print("NINE")
|
||||||
|
|
||||||
|
"TEN"
|
||||||
|
for i in range(10):
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
def method2():
|
||||||
|
"""ELEVEN"""
|
||||||
|
return "TWELVE"
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_docstrings():
|
||||||
|
tree = ast.parse(many_strings)
|
||||||
|
tree = ph.RemoveDocstrings().visit(tree)
|
||||||
|
|
||||||
|
unparsed = unparse(tree)
|
||||||
|
|
||||||
|
# make sure the methods are preserved
|
||||||
|
assert "method1" in unparsed
|
||||||
|
assert "method2" in unparsed
|
||||||
|
|
||||||
|
# all of these are unassigned and should be removed
|
||||||
|
assert "ONE" not in unparsed
|
||||||
|
assert "TWO" not in unparsed
|
||||||
|
assert "FOUR" not in unparsed
|
||||||
|
assert "FIVE" not in unparsed
|
||||||
|
assert "SIX" not in unparsed
|
||||||
|
assert "EIGHT" not in unparsed
|
||||||
|
assert "TEN" not in unparsed
|
||||||
|
assert "ELEVEN" not in unparsed
|
||||||
|
|
||||||
|
# these are used in legitimate expressions
|
||||||
|
assert "THREE" in unparsed
|
||||||
|
assert "SEVEN" in unparsed
|
||||||
|
assert "NINE" in unparsed
|
||||||
|
assert "TWELVE" in unparsed
|
@ -15,12 +15,20 @@
|
|||||||
|
|
||||||
|
|
||||||
class RemoveDocstrings(ast.NodeTransformer):
|
class RemoveDocstrings(ast.NodeTransformer):
|
||||||
"""Transformer that removes docstrings from a Python AST."""
|
"""Transformer that removes docstrings from a Python AST.
|
||||||
|
|
||||||
|
This removes *all* strings that aren't on the RHS of an assignment statement from
|
||||||
|
the body of functions, classes, and modules -- even if they're not directly after
|
||||||
|
the declaration.
|
||||||
|
|
||||||
|
"""
|
||||||
def remove_docstring(self, node):
|
def remove_docstring(self, node):
|
||||||
|
def unused_string(node):
|
||||||
|
"""Criteria for unassigned body strings."""
|
||||||
|
return isinstance(node, ast.Expr) and isinstance(node.value, ast.Str)
|
||||||
|
|
||||||
if node.body:
|
if node.body:
|
||||||
if isinstance(node.body[0], ast.Expr) and \
|
node.body = [child for child in node.body if not unused_string(child)]
|
||||||
isinstance(node.body[0].value, ast.Str):
|
|
||||||
node.body.pop(0)
|
|
||||||
|
|
||||||
self.generic_visit(node)
|
self.generic_visit(node)
|
||||||
return node
|
return node
|
||||||
|
Loading…
Reference in New Issue
Block a user