summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/general/plugins/modules/pip_package_info.py
blob: 6aea178cec83963cd5a1f78e3f255ebc57829b36 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

# started out with AWX's scan_packages module

from __future__ import absolute_import, division, print_function
__metaclass__ = type

DOCUMENTATION = '''
module: pip_package_info
short_description: Pip package information
description:
  - Return information about installed pip packages
extends_documentation_fragment:
  - community.general.attributes
  - community.general.attributes.info_module
options:
  clients:
    description:
      - A list of the pip executables that will be used to get the packages.
        They can be supplied with the full path or just the executable name, for example V(pip3.7).
    default: ['pip']
    required: false
    type: list
    elements: path
requirements:
  - The requested pip executables must be installed on the target.
author:
  - Matthew Jones (@matburt)
  - Brian Coca (@bcoca)
  - Adam Miller (@maxamillion)
'''

EXAMPLES = '''
- name: Just get the list from default pip
  community.general.pip_package_info:

- name: Get the facts for default pip, pip2 and pip3.6
  community.general.pip_package_info:
    clients: ['pip', 'pip2', 'pip3.6']

- name: Get from specific paths (virtualenvs?)
  community.general.pip_package_info:
    clients: '/home/me/projec42/python/pip3.5'
'''

RETURN = '''
packages:
  description: a dictionary of installed package data
  returned: always
  type: dict
  contains:
    python:
      description: A dictionary with each pip client which then contains a list of dicts with python package information
      returned: always
      type: dict
      sample:
        "packages": {
            "pip": {
                "Babel": [
                    {
                        "name": "Babel",
                        "source": "pip",
                        "version": "2.6.0"
                    }
                ],
                "Flask": [
                    {
                        "name": "Flask",
                        "source": "pip",
                        "version": "1.0.2"
                    }
                ],
                "Flask-SQLAlchemy": [
                    {
                        "name": "Flask-SQLAlchemy",
                        "source": "pip",
                        "version": "2.3.2"
                    }
                ],
                "Jinja2": [
                    {
                        "name": "Jinja2",
                        "source": "pip",
                        "version": "2.10"
                    }
                ],
            },
        }
'''
import json
import os

from ansible.module_utils.common.text.converters import to_text
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.facts.packages import CLIMgr


class PIP(CLIMgr):

    def __init__(self, pip, module):

        self.CLI = pip
        self.module = module

    def list_installed(self):
        rc, out, err = self.module.run_command([self._cli, 'list', '-l', '--format=json'])
        if rc != 0:
            raise Exception("Unable to list packages rc=%s : %s" % (rc, err))
        return json.loads(out)

    def get_package_details(self, package):
        package['source'] = self.CLI
        return package


def main():

    # start work
    module = AnsibleModule(
        argument_spec=dict(
            clients=dict(type='list', elements='path', default=['pip']),
        ),
        supports_check_mode=True)
    packages = {}
    results = {'packages': {}}
    clients = module.params['clients']

    found = 0
    for pip in clients:

        if not os.path.basename(pip).startswith('pip'):
            module.warn('Skipping invalid pip client: %s' % (pip))
            continue
        try:
            pip_mgr = PIP(pip, module)
            if pip_mgr.is_available():
                found += 1
                packages[pip] = pip_mgr.get_packages()
        except Exception as e:
            module.warn('Failed to retrieve packages with %s: %s' % (pip, to_text(e)))
            continue

    if found == 0:
        module.fail_json(msg='Unable to use any of the supplied pip clients: %s' % clients)

    # return info
    results['packages'] = packages
    module.exit_json(**results)


if __name__ == '__main__':
    main()