<commit_before># -*- coding: utf-8 -*-
'''
Module for the management of MacOS systems that use launchd/launchctl

.. important::
    If you feel that Salt should be using this module to manage services on a
    minion, and it is using a different module (or gives an error similar to
    *'service.start' is not available*), see :ref:`here
    <module-provider-override>`.

:depends:   - plistlib Python module
'''
from __future__ import absolute_import

# Import python libs
import os
import plistlib

# Import salt libs
import salt.utils
import salt.utils.decorators as decorators
import salt.ext.six as six

# Define the module's virtual name
__virtualname__ = 'service'


def __virtual__():
    '''
    Only work on MacOS
    '''
    if __grains__['os'] == 'MacOS':
        return __virtualname__
    return False


def _launchd_paths():
    '''
    Paths where launchd services can be found
    '''
    return [
        '/Library/LaunchAgents',
        '/Library/LaunchDaemons',
        '/System/Library/LaunchAgents',
        '/System/Library/LaunchDaemons',
    ]


@decorators.memoize
def _available_services():
    '''
    Return a dictionary of all available services on the system
    '''
    available_services = dict()
    for launch_dir in _launchd_paths():
        for root, dirs, files in os.walk(launch_dir):
            for filename in files:
                file_path = os.path.join(root, filename)
                # Follow symbolic links of files in _launchd_paths
                true_path = os.path.realpath(file_path)
                # ignore broken symlinks
                if not os.path.exists(true_path):
                    continue

                try:
                    # This assumes most of the plist files will be already in XML format
                    with salt.utils.fopen(file_path):
                        plist = plistlib.readPlist(true_path)

                except Exception:
                    # If plistlib is unable to read the file we'll need to use
                    # the system provided plutil program to do the conversion
                    cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'.format(true_path)
                    plist_xml = __salt__['cmd.run_all'](cmd, python_shell=False)['stdout']
                    if six.PY2:
                        plist = plistlib.readPlistFromString(plist_xml)
                    else:
                        plist = plistlib.readPlistFromBytes(salt.utils.to_bytes(plist_xml))

                available_services[plist.Label.lower()] = {
                    'filename': filename,
                    'file_path': true_path,
                    'plist': plist,
                }

    return available_services


def _service_by_name(name):
    '''
    Return the service info for a service by label, filename or path
    '''
    services = _available_services()
    name = name.lower()

    if name in services:
        # Match on label
        return services[name]

    for service in six.itervalues(services):
        if service['file_path'].lower() == name:<commit_msg>Ignore plist files without Label key<commit_after># -*- coding: utf-8 -*-
'''
Module for the management of MacOS systems that use launchd/launchctl

.. important::
    If you feel that Salt should be using this module to manage services on a
    minion, and it is using a different module (or gives an error similar to
    *'service.start' is not available*), see :ref:`here
    <module-provider-override>`.

:depends:   - plistlib Python module
'''
from __future__ import absolute_import

# Import python libs
import os
import plistlib

# Import salt libs
import salt.utils
import salt.utils.decorators as decorators
import salt.ext.six as six

# Define the module's virtual name
__virtualname__ = 'service'


def __virtual__():
    '''
    Only work on MacOS
    '''
    if __grains__['os'] == 'MacOS':
        return __virtualname__
    return False


def _launchd_paths():
    '''
    Paths where launchd services can be found
    '''
    return [
        '/Library/LaunchAgents',
        '/Library/LaunchDaemons',
        '/System/Library/LaunchAgents',
        '/System/Library/LaunchDaemons',
    ]


@decorators.memoize
def _available_services():
    '''
    Return a dictionary of all available services on the system
    '''
    available_services = dict()
    for launch_dir in _launchd_paths():
        for root, dirs, files in os.walk(launch_dir):
            for filename in files:
                file_path = os.path.join(root, filename)
                # Follow symbolic links of files in _launchd_paths
                true_path = os.path.realpath(file_path)
                # ignore broken symlinks
                if not os.path.exists(true_path):
                    continue

                try:
                    # This assumes most of the plist files will be already in XML format
                    with salt.utils.fopen(file_path):
                        plist = plistlib.readPlist(true_path)

                except Exception:
                    # If plistlib is unable to read the file we'll need to use
                    # the system provided plutil program to do the conversion
                    cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'.format(true_path)
                    plist_xml = __salt__['cmd.run_all'](cmd, python_shell=False)['stdout']
                    if six.PY2:
                        plist = plistlib.readPlistFromString(plist_xml)
                    else:
                        plist = plistlib.readPlistFromBytes(salt.utils.to_bytes(plist_xml))

                try:
                    available_services[plist.Label.lower()] = {
                        'filename': filename,
                        'file_path': true_path,
                        'plist': plist,
                    }
                except AttributeError:
                    # As of MacOS 10.12 there might be plist files without Label key
                    # in the searched directories. As these files do not represent 
                    # services, thay are not added to the list.
                    pass

    return available_services


def _service_by_name(name):
    '''
    Return the service info for a service by label, filename or path
    '''
    services = _available_services()
    name = name.lower()

    if name in services:
        # Match on label
        return services[name]

    for service in six.itervalues(services):
        if service['file_path'].lower() == name:
{'fraction_non_alphanumeric': 0.08332054626361823, 'fraction_numerical': 0.0018413380389749885, 'mean_word_length': 1.9111210361768647, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1429635', 'n_tokens_mistral': 1802, 'n_tokens_neox': 1686, 'n_words': 558}