Skip to content

Commit

Permalink
Merge 894af92 into d299c9e
Browse files Browse the repository at this point in the history
  • Loading branch information
petr-muller committed May 9, 2018
2 parents d299c9e + 894af92 commit e97b46c
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 16 deletions.
10 changes: 7 additions & 3 deletions helpers/helpers.sh
Expand Up @@ -4,6 +4,10 @@ example() {
pyff tests/examples/$1*.old tests/examples/$1*.new
}

exdiff() {
vimdiff tests/examples/$1*.old tests/examples/$1*.new
}

example_quotes() {
pyff --highlight-names quotes tests/examples/$1*.old tests/examples/$1*.new
}
Expand All @@ -15,9 +19,9 @@ ft() {

st() {
cat helpers/strict-setup.cfg > setup.cfg
python setup.py test
helpers/clitest --prefix '# ' --diff-options '-u --color=always' tests/examples/*.new
mypy pyff
python setup.py test &&
helpers/clitest --prefix '# ' --diff-options '-u --color=always' tests/examples/*.new &&
mypy pyff
}

cov() {
Expand Down
10 changes: 8 additions & 2 deletions pyff/pyff.py
Expand Up @@ -6,7 +6,7 @@
from itertools import zip_longest

import pyff.pyfference as pf
from pyff.summary import ClassSummary, LocalBaseClass, ImportedBaseClass
from pyff.summary import ClassSummary, LocalBaseClass, ImportedBaseClass, FunctionSummary

class ExternalNamesExtractor(NodeVisitor):
"""Collects information about imported name usage in function"""
Expand Down Expand Up @@ -157,7 +157,13 @@ def _pyff_functions(first_ast: Module, second_ast: Module) -> Optional[pf.Functi
if difference:
differences[function] = difference

return pf.FunctionsPyfference(changed=differences) if differences else None
new_names = second_walker.names - first_walker.names
new_functions = {FunctionSummary(name) for name in new_names}

if differences or new_functions:
return pf.FunctionsPyfference(changed=differences, new=new_functions)

return None


def _pyff_modules(first_ast: Module, second_ast: Module) -> Optional[pf.ModulePyfference]:
Expand Down
16 changes: 10 additions & 6 deletions pyff/pyfference.py
Expand Up @@ -2,7 +2,7 @@

from collections import namedtuple
from typing import Tuple, List, Dict, Iterable, Set, Optional
from pyff.summary import ClassSummary
from pyff.summary import ClassSummary, FunctionSummary
from pyff.kitchensink import HL_OPEN, HL_CLOSE

Change = namedtuple("Change", ["old", "new"])
Expand Down Expand Up @@ -65,14 +65,18 @@ def __len__(self) -> int:

class FunctionsPyfference: # pylint: disable=too-few-public-methods
"""Holds differences between top-level functions in a module"""
def __init__(self, changed: Dict[str, FunctionPyfference]) -> None:
self.changed = changed
def __init__(self, new: Set[FunctionSummary], changed: Dict[str, FunctionPyfference]) -> None:
self.changed: Dict[str, FunctionPyfference] = changed
self.new: Set[FunctionSummary] = new

def __str__(self) -> str:
return "\n".join([str(change) for change in self.changed.values()])
changed = "\n".join([str(change) for change in self.changed.values()])
new = "\n".join([f"New {f}" for f in sorted([str(name) for name in self.new])])

return "\n".join([changeset for changeset in (new, changed) if changeset])

def __len__(self) -> int:
return len(self.changed)
return len(self.changed) + len(self.new)

class ClassesPyfference: # pylint: disable=too-few-public-methods

Expand All @@ -81,7 +85,7 @@ def __init__(self, new: Set[ClassSummary]) -> None:
self.new: Set[ClassSummary] = new

def __str__(self):
return "\n".join([f"New {cls}" for cls in self.new])
return "\n".join([f"New {cls}" for cls in sorted([str(cls) for cls in self.new])])

def __len__(self) -> int:
return len(self.new)
Expand Down
8 changes: 8 additions & 0 deletions pyff/summary.py
Expand Up @@ -43,3 +43,11 @@ def __str__(self) -> str:
return f"{class_part} derived from {str(self.baseclasses[0])} {method_part}"

raise Exception("Multiple inheritance not yet implemented")

class FunctionSummary(): # pylint: disable=too-few-public-methods
"""Contains summary information about a function"""
def __init__(self, name: str) -> None:
self.name = name

def __str__(self):
return f"function {HL_OPEN}{self.name}{HL_CLOSE}"
2 changes: 2 additions & 0 deletions tests/examples/03.new
Expand Up @@ -8,6 +8,8 @@
# New imported names 'Game' from new package 'vtes.game'
# New imported names 'GameStore', 'load_store' from new package 'vtes.store'
# New class 'ParsePlayerAction' derived from imported 'Action' with 0 public methods
# New function 'add_command'
# New function 'games_command'
# Function 'main' changed implementation, newly uses external names 'Path'

"""Entry point for the `vtes` command"""
Expand Down
14 changes: 14 additions & 0 deletions tests/unit/test_modules.py
Expand Up @@ -6,6 +6,14 @@
def func():
pass"""

NEW_FUNCTION_MODULE = """import sys
def func():
pass
def Funktion():
pass
"""

IMPORT_MODULE = """import sys
from os import path
def func():
Expand Down Expand Up @@ -58,6 +66,12 @@ def test_module_with_new_class():
assert len(difference) == 1
assert str(difference) == "New class ``Klass'' with 1 public methods"

def test_module_with_new_function():
difference = pyff_module(TRIVIAL_MODULE, NEW_FUNCTION_MODULE)
assert difference is not None
assert len(difference) == 1
assert str(difference) == "New function ``Funktion''"

def test_module_with_inherited_classes(): # pylint: disable=invalid-name
difference = pyff_module(TRIVIAL_MODULE, EXTERNAL_INHERITANCE_CLASS_MODULE)
assert difference is not None
Expand Down
18 changes: 14 additions & 4 deletions tests/unit/test_pyfference.py
Expand Up @@ -46,8 +46,8 @@ def test_module_with_from_imports():
assert str(mpyff) == "New imported names ``getenv'', ``path'' from new package ``os''"

def test_new_classes():
cpyff = pf.ClassesPyfference(new=["NewClass", "NewClass2"])
assert cpyff.new == ["NewClass", "NewClass2"]
cpyff = pf.ClassesPyfference(new={"NewClass", "NewClass2"})
assert cpyff.new == {"NewClass", "NewClass2"}
assert str(cpyff) == "New NewClass\nNew NewClass2"
assert len(cpyff) == 2

Expand All @@ -58,16 +58,26 @@ def test_module_with_new_classes():
assert len(mpyff) == 2
assert str(mpyff) == "New NewClass\nNew NewClass2"

def test_function_new():
fpyff = pf.FunctionsPyfference(new=("NewFunktion", "AnotherNewFunktion"), changed={})
assert fpyff.new == ("NewFunktion", "AnotherNewFunktion")
assert str(fpyff) == "New AnotherNewFunktion\nNew NewFunktion"
assert len(fpyff) == 2
mpyff = pf.ModulePyfference(functions=fpyff)
assert mpyff.functions is not None
assert len(mpyff) == 2
assert str(mpyff) == "New AnotherNewFunktion\nNew NewFunktion"

def test_functions_changed():
fpyff = pf.FunctionPyfference(name='func', implementation=True)
fspyff = pf.FunctionsPyfference(changed={'func': fpyff})
fspyff = pf.FunctionsPyfference(changed={'func': fpyff}, new=set())
assert fspyff.changed is not None
assert str(fspyff) == "Function ``func'' changed implementation"
assert len(fspyff) == 1

def test_module_with_changed_functions(): # pylint: disable=invalid-name
fpyff = pf.FunctionPyfference(name='func', implementation=True)
fspyff = pf.FunctionsPyfference(changed={'func': fpyff})
fspyff = pf.FunctionsPyfference(changed={'func': fpyff}, new=set())
mpyff = pf.ModulePyfference(functions=fspyff)
assert mpyff.functions is not None
assert len(mpyff) == 1
Expand Down
7 changes: 6 additions & 1 deletion tests/unit/test_summary.py
@@ -1,7 +1,7 @@
# pylint: disable=missing-docstring

from pytest import raises
from pyff.summary import ClassSummary, LocalBaseClass, ImportedBaseClass
from pyff.summary import ClassSummary, LocalBaseClass, ImportedBaseClass, FunctionSummary

def test_class_summary():
cls = ClassSummary("classname", methods=5, private=2)
Expand Down Expand Up @@ -30,3 +30,8 @@ def test_multiple_inherited_summary():
baseclasses=[LocalBaseClass("C1"), LocalBaseClass("C2")])
with raises(Exception):
str(local)

def test_function_summary():
function = FunctionSummary('funktion')
assert function.name == 'funktion'
assert str(function) == "function ``funktion''"

0 comments on commit e97b46c

Please sign in to comment.