Skip to content

Commit 45d9753

Browse files
authored
pip refactoring: implementation of installed and version (#606)
1 parent f677e54 commit 45d9753

File tree

4 files changed

+118
-23
lines changed

4 files changed

+118
-23
lines changed

doc/source/modules.rst

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ host
5858

5959
:class:`testinfra.modules.package.Package` class
6060

61+
.. attribute:: pip
62+
63+
:class:`testinfra.modules.pip.Pip` class
64+
6165
.. attribute:: pip_package
6266

6367
:class:`testinfra.modules.pip.PipPackage` class
@@ -182,11 +186,17 @@ Package
182186
:members:
183187

184188

189+
Pip
190+
~~~~~~~~~~
191+
192+
.. autoclass:: testinfra.modules.pip.Pip
193+
:members:
194+
195+
185196
PipPackage
186197
~~~~~~~~~~
187198

188199
.. autoclass:: testinfra.modules.pip.PipPackage
189-
:members:
190200

191201

192202
Podman

test/test_modules.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from ipaddress import IPv4Address
2222
from ipaddress import IPv6Address
2323

24+
2425
from testinfra.modules.socket import parse_socketspec
2526

2627
all_images = pytest.mark.testinfra_hosts(
@@ -515,13 +516,40 @@ def test_command_execution(host):
515516

516517

517518
def test_pip_package(host):
518-
assert host.pip_package.get_packages()["pip"]["version"] == "18.1"
519-
pytest = host.pip_package.get_packages(pip_path="/v/bin/pip")["pytest"]
520-
assert pytest["version"].startswith("2.")
521-
outdated = host.pip_package.get_outdated_packages(pip_path="/v/bin/pip")["pytest"]
522-
assert outdated["current"] == pytest["version"]
519+
with pytest.warns(DeprecationWarning):
520+
assert host.pip_package.get_packages()["pip"]["version"] == "18.1"
521+
pytest_package = host.pip_package.get_packages(pip_path="/v/bin/pip")["pytest"]
522+
assert pytest_package["version"].startswith("2.")
523+
with pytest.warns(DeprecationWarning):
524+
outdated = host.pip_package.get_outdated_packages(pip_path="/v/bin/pip")[
525+
"pytest"
526+
]
527+
assert outdated["current"] == pytest_package["version"]
528+
assert int(outdated["latest"].split(".")[0]) > 2
529+
with pytest.warns(DeprecationWarning):
530+
assert host.pip_package.check().succeeded
531+
532+
533+
def test_pip(host):
534+
# get_packages
535+
assert host.pip.get_packages()["pip"]["version"] == "18.1"
536+
pytest_package = host.pip.get_packages(pip_path="/v/bin/pip")["pytest"]
537+
assert pytest_package["version"].startswith("2.")
538+
# outdated
539+
outdated = host.pip.get_outdated_packages(pip_path="/v/bin/pip")["pytest"]
540+
assert outdated["current"] == pytest_package["version"]
523541
assert int(outdated["latest"].split(".")[0]) > 2
524-
assert host.pip_package.check().succeeded
542+
# check
543+
assert host.pip.check().succeeded
544+
# is_installed
545+
assert host.pip("pip").is_installed
546+
assert not host.pip("does_not_exist").is_installed
547+
pytest_package = host.pip("pytest", pip_path="/v/bin/pip")
548+
assert pytest_package.is_installed
549+
# version
550+
assert host.pip("pip").version == "18.1"
551+
assert pytest_package.version.startswith("2.")
552+
assert host.pip("does_not_exist").version == ""
525553

526554

527555
def test_environment_home(host):

testinfra/modules/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"iptables": "iptables:Iptables",
2626
"mount_point": "mountpoint:MountPoint",
2727
"package": "package:Package",
28+
"pip": "pip:Pip",
2829
"pip_package": "pip:PipPackage",
2930
"process": "process:Process",
3031
"puppet_resource": "puppet:PuppetResource",

testinfra/modules/pip.py

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212

1313
import json
1414
import re
15+
import warnings
1516

16-
from testinfra.modules.base import InstanceModule
17+
from testinfra.modules.base import Module
1718

1819

1920
def _re_match(line, regexp):
@@ -23,10 +24,38 @@ def _re_match(line, regexp):
2324
return match.groups()
2425

2526

26-
class PipPackage(InstanceModule):
27-
"""Test pip packages status and version"""
27+
class Pip(Module):
28+
"""Test pip package manager and packages"""
2829

29-
def check(self, pip_path="pip"):
30+
def __init__(self, name, pip_path="pip"):
31+
self.name = name
32+
self.pip_path = pip_path
33+
super().__init__()
34+
35+
@property
36+
def is_installed(self):
37+
"""Test if the package is installed
38+
39+
>>> host.package("pip").is_installed
40+
True
41+
"""
42+
return self.run_test("%s show %s", self.pip_path, self.name).rc == 0
43+
44+
@property
45+
def version(self):
46+
"""Return package version as returned by pip
47+
48+
>>> host.package("pip").version
49+
'18.1'
50+
"""
51+
return self.check_output(
52+
"%s show %s | grep Version: | cut -d' ' -f2",
53+
self.pip_path,
54+
self.name,
55+
)
56+
57+
@classmethod
58+
def check(cls, pip_path="pip"):
3059
"""Verify installed packages have compatible dependencies.
3160
3261
>>> cmd = host.pip_package.check()
@@ -41,20 +70,18 @@ def check(self, pip_path="pip"):
4170
.. _pip check: https://pip.pypa.io/en/stable/reference/pip_check/
4271
.. _9.0.0: https://pip.pypa.io/en/stable/news/#id526
4372
"""
44-
cmd = "{} check".format(pip_path)
45-
return self.run_expect([0, 1], cmd)
73+
return cls.run_expect([0, 1], "%s check", pip_path)
4674

47-
def get_packages(self, pip_path="pip"):
75+
@classmethod
76+
def get_packages(cls, pip_path="pip"):
4877
"""Get all installed packages and versions returned by `pip list`:
4978
5079
>>> host.pip_package.get_packages(pip_path='~/venv/website/bin/pip')
5180
{'Django': {'version': '1.10.2'},
5281
'mywebsite': {'version': '1.0a3', 'path': '/srv/website'},
5382
'psycopg2': {'version': '2.6.2'}}
5483
"""
55-
out = self.run_expect(
56-
[0, 2], "{0} list --no-index --format=json".format(pip_path)
57-
)
84+
out = cls.run_expect([0, 2], "%s list --no-index --format=json", pip_path)
5885
pkgs = {}
5986
if out.rc == 0:
6087
for pkg in json.loads(out.stdout):
@@ -63,9 +90,7 @@ def get_packages(self, pip_path="pip"):
6390
else:
6491
# pip < 9
6592
output_re = re.compile(r"^(.+) \((.+)\)$")
66-
for line in self.check_output(
67-
"{0} list --no-index".format(pip_path)
68-
).splitlines():
93+
for line in cls.check_output("%s list --no-index", pip_path).splitlines():
6994
if line.startswith("Warning: "):
7095
# Warning: cannot find svn location for ...
7196
continue
@@ -77,14 +102,15 @@ def get_packages(self, pip_path="pip"):
77102
pkgs[name] = {"version": version}
78103
return pkgs
79104

80-
def get_outdated_packages(self, pip_path="pip"):
105+
@classmethod
106+
def get_outdated_packages(cls, pip_path="pip"):
81107
"""Get all outdated packages with current and latest version
82108
83109
>>> host.pip_package.get_outdated_packages(
84110
... pip_path='~/venv/website/bin/pip')
85111
{'Django': {'current': '1.10.2', 'latest': '1.10.3'}}
86112
"""
87-
out = self.run_expect([0, 2], "{0} list -o --format=json".format(pip_path))
113+
out = cls.run_expect([0, 2], "%s list -o --format=json", pip_path)
88114
pkgs = {}
89115
if out.rc == 0:
90116
for pkg in json.loads(out.stdout):
@@ -100,11 +126,41 @@ def get_outdated_packages(self, pip_path="pip"):
100126
re.compile(r"^(.+?) \((.+)\) - Latest: (.+) .*$"),
101127
re.compile(r"^(.+?) \(Current: (.+) Latest: (.+) .*$"),
102128
]
103-
for line in self.check_output("{0} list -o".format(pip_path)).splitlines():
129+
for line in cls.check_output("%s list -o", pip_path).splitlines():
104130
if line.startswith("Warning: "):
105131
# Warning: cannot find svn location for ...
106132
continue
107133
output_re = regexpes[1] if "Current:" in line else regexpes[0]
108134
name, current, latest = _re_match(line, output_re)
109135
pkgs[name] = {"current": current, "latest": latest}
110136
return pkgs
137+
138+
139+
class PipPackage(Pip):
140+
""".. deprecated:: 6.2
141+
142+
Use :class:`~testinfra.modules.pip.Pip` instead.
143+
"""
144+
145+
@staticmethod
146+
def _deprecated():
147+
"""Raise a `DeprecationWarning`"""
148+
warnings.warn(
149+
"Calling host.pip_package is deprecated, call host.pip instead",
150+
DeprecationWarning,
151+
)
152+
153+
@classmethod
154+
def check(cls, pip_path="pip"):
155+
PipPackage._deprecated()
156+
return super().check(pip_path=pip_path)
157+
158+
@classmethod
159+
def get_packages(cls, pip_path="pip"):
160+
PipPackage._deprecated()
161+
return super().get_packages(pip_path=pip_path)
162+
163+
@classmethod
164+
def get_outdated_packages(cls, pip_path="pip"):
165+
PipPackage._deprecated()
166+
return super().get_outdated_packages(pip_path=pip_path)

0 commit comments

Comments
 (0)