-
Notifications
You must be signed in to change notification settings - Fork 580
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
bears/general: Add OutdatedDependencyBear
Closes #2445
- Loading branch information
Showing
4 changed files
with
135 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
from coalib.bears.LocalBear import LocalBear | ||
from coalib.results.Result import Result | ||
from dependency_management.requirements.PipRequirement import PipRequirement | ||
from distutils.version import LooseVersion | ||
from sarge import run, Capture | ||
|
||
|
||
class OutdatedDependencyBear(LocalBear): | ||
LANGUAGES = {'All'} | ||
REQUIREMENTS = {PipRequirement('pip-tools', '3.8.0')} | ||
AUTHORS = {'The coala developers'} | ||
AUTHORS_EMAILS = {'coala-devel@googlegroups.com'} | ||
LICENSE = 'AGPL-3.0' | ||
|
||
def run(self, filename, file, requirement_type: str,): | ||
""" | ||
Checks for the outdated dependencies in a project. | ||
:param requirement_type: | ||
One of the requirement types supported by coala's package manager. | ||
:param requirements_file: | ||
Requirements file can be specified to look for the requirements. | ||
""" | ||
requirement_types = ['pip'] | ||
|
||
if requirement_type not in requirement_types: | ||
raise ValueError('Currently the bear only supports {} as ' | ||
'requirement_type.' | ||
.format(', '.join( | ||
_type for _type in requirement_types))) | ||
|
||
message = ('The requirement {} with version {} is not ' | ||
'pinned to its latest version {}.') | ||
|
||
out = run('pip-compile -n --allow-unsafe {}'.format(filename), | ||
stdout=Capture()) | ||
|
||
data = [line for line in out.stdout.text.splitlines() | ||
if '#' not in line and line] | ||
|
||
for requiremenent in data: | ||
package, version = requiremenent.split('==') | ||
pip_requirement = PipRequirement(package) | ||
latest_ver = pip_requirement.get_latest_version() | ||
line_number = [num for num, line in enumerate(file, 1) | ||
if package in line.lower()] | ||
|
||
if LooseVersion(version) < LooseVersion(latest_ver): | ||
yield Result.from_values(origin=self, | ||
message=message.format( | ||
package, | ||
version, | ||
latest_ver), | ||
file=filename, | ||
line=line_number[0], | ||
end_line=line_number[0], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import unittest.mock | ||
import sarge | ||
from queue import Queue | ||
|
||
from bears.general.OutdatedDependencyBear import OutdatedDependencyBear | ||
from coalib.testing.LocalBearTestHelper import LocalBearTestHelper | ||
from coalib.results.Result import Result | ||
from coalib.settings.Section import Section | ||
from coalib.settings.Setting import Setting | ||
|
||
|
||
test_file = """ | ||
foo==1.0 | ||
bar==2.0 | ||
""" | ||
|
||
|
||
class OutdatedDependencyBearTest(LocalBearTestHelper): | ||
|
||
def setUp(self): | ||
self.section = Section('') | ||
self.uut = OutdatedDependencyBear(self.section, Queue()) | ||
|
||
@unittest.mock.patch('bears.general.OutdatedDependencyBear.' | ||
'PipRequirement.get_latest_version') | ||
def test_pip_outdated_requirement(self, _mock): | ||
self.section.append(Setting('requirement_type', 'pip')) | ||
_mock.return_value = '3.0' | ||
with unittest.mock.patch('bears.general.OutdatedDependencyBear.' | ||
'run') as mock: | ||
patched = unittest.mock.Mock(spec=sarge.Pipeline) | ||
patched.stdout = unittest.mock.Mock(spec=sarge.Capture) | ||
patched.stdout.text = 'foo==1.0\nbar==2.0' | ||
mock.return_value = patched | ||
message = ('The requirement {} with version {} is not ' | ||
'pinned to its latest version 3.0.') | ||
self.check_results(self.uut, | ||
test_file.splitlines(True), | ||
[Result.from_values( | ||
origin='OutdatedDependencyBear', | ||
message=message.format('foo', '1.0'), | ||
file='default', | ||
line=2, end_line=2, | ||
), | ||
Result.from_values( | ||
origin='OutdatedDependencyBear', | ||
message=message.format('bar', '2.0'), | ||
file='default', | ||
line=3, end_line=3, | ||
)], | ||
filename='default', | ||
) | ||
|
||
@unittest.mock.patch('bears.general.OutdatedDependencyBear.' | ||
'PipRequirement.get_latest_version') | ||
def test_pip_latest_requirement(self, _mock): | ||
self.section.append(Setting('requirement_type', 'pip')) | ||
_mock.return_value = '1.0' | ||
with unittest.mock.patch('bears.general.OutdatedDependencyBear.' | ||
'run') as mock: | ||
patched = unittest.mock.Mock(spec=sarge.Pipeline) | ||
patched.stdout = unittest.mock.Mock(spec=sarge.Capture) | ||
patched.stdout.text = 'foo==1.0' | ||
mock.return_value = patched | ||
self.check_results(self.uut, | ||
[test_file.splitlines()[0]], | ||
[], | ||
filename='default') | ||
|
||
def test_requirement_type_value_error(self): | ||
self.section.append(Setting('requirement_type', 'blabla')) | ||
error = ('ValueError: Currently the bear only supports pip as ' | ||
'requirement_type.') | ||
with self.assertRaisesRegex(AssertionError, error): | ||
self.check_validity(self.uut, [], filename='default') |