mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-22 12:50:22 -07:00
Cleanups to the common.sys_info API
* Move get_all_subclasses out of sys_info as it is unrelated to system information. * get_all_subclasses now returns a set() instead of a list. * Don't port get_platform to sys_info as it is deprecated. Code using the common API should just use platform.system() directly. * Rename load_platform_subclass() to get_platform_subclass and do not instantiate the rturned class. * Test the compat shims in module_utils/basic.py separately from the new API in module_utils/common/sys_info.py and module_utils/common/_utils.py
This commit is contained in:
parent
79dc9a75c3
commit
5844c8c7f0
7 changed files with 284 additions and 47 deletions
|
@ -165,11 +165,9 @@ from ansible.module_utils.common.file import (
|
|||
get_flags_from_attributes,
|
||||
)
|
||||
from ansible.module_utils.common.sys_info import (
|
||||
get_platform,
|
||||
get_distribution,
|
||||
get_distribution_version,
|
||||
load_platform_subclass,
|
||||
get_all_subclasses,
|
||||
get_platform_subclass,
|
||||
)
|
||||
from ansible.module_utils.pycompat24 import get_exception, literal_eval
|
||||
from ansible.module_utils.six import (
|
||||
|
@ -184,6 +182,7 @@ from ansible.module_utils.six import (
|
|||
)
|
||||
from ansible.module_utils.six.moves import map, reduce, shlex_quote
|
||||
from ansible.module_utils._text import to_native, to_bytes, to_text
|
||||
from ansible.module_utils.common._utils import get_all_subclasses as _get_all_subclasses
|
||||
from ansible.module_utils.parsing.convert_bool import BOOLEANS, BOOLEANS_FALSE, BOOLEANS_TRUE, boolean
|
||||
|
||||
|
||||
|
@ -276,6 +275,42 @@ if not _PY_MIN:
|
|||
sys.exit(1)
|
||||
|
||||
|
||||
#
|
||||
# Deprecated functions
|
||||
#
|
||||
|
||||
def get_platform():
|
||||
'''
|
||||
**Deprecated** Use :py:func:`platform.system` directly.
|
||||
|
||||
:returns: Name of the platform the module is running on in a native string
|
||||
|
||||
Returns a native string that labels the platform ("Linux", "Solaris", etc). Currently, this is
|
||||
the result of calling :py:func:`platform.system`.
|
||||
'''
|
||||
return platform.system()
|
||||
|
||||
# End deprecated functions
|
||||
|
||||
|
||||
#
|
||||
# Compat shims
|
||||
#
|
||||
|
||||
def load_platform_subclass(cls, *args, **kwargs):
|
||||
"""**Deprecated**: Use ansible.module_utils.common.sys_info.get_platform_subclass instead"""
|
||||
platform_cls = get_platform_subclass(cls)
|
||||
return super(cls, platform_cls).__new__(platform_cls)
|
||||
|
||||
|
||||
def get_all_subclasses(cls):
|
||||
"""**Deprecated**: Use ansible.module_utils.common._utils.get_all_subclasses instead"""
|
||||
return list(_get_all_subclasses(cls))
|
||||
|
||||
|
||||
# End compat shims
|
||||
|
||||
|
||||
def json_dict_unicode_to_bytes(d, encoding='utf-8', errors='surrogate_or_strict'):
|
||||
''' Recursively convert dict keys and values to byte str
|
||||
|
||||
|
|
40
lib/ansible/module_utils/common/_utils.py
Normal file
40
lib/ansible/module_utils/common/_utils.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
# Copyright (c) 2018, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
"""
|
||||
Modules in _utils are waiting to find a better home. If you need to use them, be prepared for them
|
||||
to move to a different location in the future.
|
||||
"""
|
||||
|
||||
|
||||
def get_all_subclasses(cls):
|
||||
'''
|
||||
Recursively search and find all subclasses of a given class
|
||||
|
||||
:arg cls: A python class
|
||||
:rtype: set
|
||||
:returns: The set of python classes which are the subclasses of `cls`.
|
||||
|
||||
In python, you can use a class's :py:meth:`__subclasses__` method to determine what subclasses
|
||||
of a class exist. However, `__subclasses__` only goes one level deep. This function searches
|
||||
each child class's `__subclasses__` method to find all of the descendent classes. It then
|
||||
returns an iterable of the descendent classes.
|
||||
'''
|
||||
# Retrieve direct subclasses
|
||||
subclasses = set(cls.__subclasses__())
|
||||
to_visit = list(subclasses)
|
||||
# Then visit all subclasses
|
||||
while to_visit:
|
||||
for sc in to_visit:
|
||||
# The current class is now visited, so remove it from list
|
||||
to_visit.remove(sc)
|
||||
# Appending all subclasses to visit and keep a reference of available class
|
||||
for ssc in sc.__subclasses__():
|
||||
if ssc not in subclasses:
|
||||
to_visit.append(ssc)
|
||||
subclasses.add(ssc)
|
||||
return subclasses
|
|
@ -9,21 +9,22 @@ import os
|
|||
import platform
|
||||
|
||||
from ansible.module_utils import distro
|
||||
from ansible.module_utils.common._utils import get_all_subclasses
|
||||
|
||||
|
||||
# Backwards compat. New code should just use platform.system()
|
||||
def get_platform():
|
||||
'''
|
||||
:rtype: NativeString
|
||||
:returns: Name of the platform the module is running on
|
||||
'''
|
||||
return platform.system()
|
||||
__all__ = ('get_distribution', 'get_distribution_version', 'get_platform_subclass')
|
||||
|
||||
|
||||
def get_distribution():
|
||||
'''
|
||||
Return the name of the distribution the module is running on
|
||||
|
||||
:rtype: NativeString or None
|
||||
:returns: Name of the distribution the module is running on
|
||||
|
||||
This function attempts to determine what Linux distribution the code is running on and return
|
||||
a string representing that value. If the distribution cannot be determined, it returns
|
||||
``OtherLinux``. If not run on Linux it returns None.
|
||||
'''
|
||||
distribution = None
|
||||
|
||||
|
@ -42,9 +43,11 @@ def get_distribution():
|
|||
|
||||
def get_distribution_version():
|
||||
'''
|
||||
Get the version of the Linux distribution the code is running on
|
||||
|
||||
:rtype: NativeString or None
|
||||
:returns: A string representation of the version of the distribution. None if this is not run
|
||||
on a Linux machine
|
||||
:returns: A string representation of the version of the distribution. If it cannot determine
|
||||
the version, it returns empty string. If this is not run on a Linux machine it returns None
|
||||
'''
|
||||
version = None
|
||||
if platform.system() == 'Linux':
|
||||
|
@ -82,33 +85,36 @@ def get_distribution_codename():
|
|||
return codename
|
||||
|
||||
|
||||
def get_all_subclasses(cls):
|
||||
def get_platform_subclass(cls):
|
||||
'''
|
||||
used by modules like Hardware or Network fact classes to recursively retrieve all
|
||||
subclasses of a given class not only the direct sub classes.
|
||||
'''
|
||||
# Retrieve direct subclasses
|
||||
subclasses = cls.__subclasses__()
|
||||
to_visit = list(subclasses)
|
||||
# Then visit all subclasses
|
||||
while to_visit:
|
||||
for sc in to_visit:
|
||||
# The current class is now visited, so remove it from list
|
||||
to_visit.remove(sc)
|
||||
# Appending all subclasses to visit and keep a reference of available class
|
||||
for ssc in sc.__subclasses__():
|
||||
subclasses.append(ssc)
|
||||
to_visit.append(ssc)
|
||||
return subclasses
|
||||
Finds a subclass implementing desired functionality on the platform the code is running on
|
||||
|
||||
:arg cls: Class to find an appropriate subclass for
|
||||
:returns: A class that implements the functionality on this platform
|
||||
|
||||
def load_platform_subclass(cls, *args, **kwargs):
|
||||
'''
|
||||
used by modules like User to have different implementations based on detected platform. See User
|
||||
module for an example.
|
||||
Some Ansible modules have different implementations depending on the platform they run on. This
|
||||
function is used to select between the various implementations and choose one. You can look at
|
||||
the implementation of the Ansible :ref:`User module<user_module>` module for an example of how to use this.
|
||||
|
||||
This function replaces ``basic.load_platform_subclass()``. When you port code, you need to
|
||||
change the callers to be explicit about instantiating the class. For instance, code in the
|
||||
Ansible User module changed from::
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Old
|
||||
class User:
|
||||
def __new__(cls, args, kwargs):
|
||||
return load_platform_subclass(User, args, kwargs)
|
||||
|
||||
# New
|
||||
class User:
|
||||
def __new__(cls, args, kwargs):
|
||||
new_cls = get_platform_subclass(User)
|
||||
return super(cls, new_cls).__new__(new_cls, args, kwargs)
|
||||
'''
|
||||
|
||||
this_platform = get_platform()
|
||||
this_platform = platform.system()
|
||||
distribution = get_distribution()
|
||||
subclass = None
|
||||
|
||||
|
@ -124,4 +130,4 @@ def load_platform_subclass(cls, *args, **kwargs):
|
|||
if subclass is None:
|
||||
subclass = cls
|
||||
|
||||
return super(cls, subclass).__new__(subclass)
|
||||
return subclass
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue