Skip to content

Commit

Permalink
bears/general: Add OutdatedDependencyBear
Browse files Browse the repository at this point in the history
Closes coala#2445
  • Loading branch information
bkhanale committed Aug 18, 2019
1 parent cbe1841 commit f67eab6
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 0 deletions.
1 change: 1 addition & 0 deletions bear-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ munkres3~=1.0
mypy==0.590
nbformat~=4.1
nltk~=3.2
pip-tools~=3.8.0
proselint~=0.7.0
pycodestyle~=2.2
pydocstyle~=2.0
Expand Down
2 changes: 2 additions & 0 deletions bear-requirements.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ pip_requirements:
version: ~=4.1
nltk:
version: ~=3.2
pip-tools:
version: ~=3.8.0
proselint:
version: ~=0.7.0
pycodestyle:
Expand Down
57 changes: 57 additions & 0 deletions bears/general/OutdatedDependencyBear.py
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 = {'[email protected]'}
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],
)
75 changes: 75 additions & 0 deletions tests/general/OutdatedDependencyBearTest.py
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')

0 comments on commit f67eab6

Please sign in to comment.