Skip to content

Move regular expression compilation to the top of the module in create_from_yaml #1381

Closed
@abhiabhi94

Description

@abhiabhi94

I was going through the codebase and was trying to understand it. I landed up at:

def create_from_yaml_single_item(
k8s_client, yml_object, verbose=False, **kwargs):
group, _, version = yml_object["apiVersion"].partition("/")
if version == "":
version = group
group = "core"
# Take care for the case e.g. api_type is "apiextensions.k8s.io"
# Only replace the last instance
group = "".join(group.rsplit(".k8s.io", 1))
# convert group name from DNS subdomain format to
# python class name convention
group = "".join(word.capitalize() for word in group.split('.'))
fcn_to_call = "{0}{1}Api".format(group, version.capitalize())
k8s_api = getattr(client, fcn_to_call)(k8s_client)
# Replace CamelCased action_type into snake_case
kind = yml_object["kind"]
kind = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', kind)
kind = re.sub('([a-z0-9])([A-Z])', r'\1_\2', kind).lower()
# Expect the user to create namespaced objects more often
if hasattr(k8s_api, "create_namespaced_{0}".format(kind)):
# Decide which namespace we are going to put the object in,
# if any
if "namespace" in yml_object["metadata"]:
namespace = yml_object["metadata"]["namespace"]
kwargs['namespace'] = namespace
resp = getattr(k8s_api, "create_namespaced_{0}".format(kind))(
body=yml_object, **kwargs)
else:
kwargs.pop('namespace', None)
resp = getattr(k8s_api, "create_{0}".format(kind))(
body=yml_object, **kwargs)
if verbose:
msg = "{0} created.".format(kind)
if hasattr(resp, 'status'):
msg += " status='{0}'".format(str(resp.status))
print(msg)
return resp

I think we can refactor this function by breaking down it into a couple of nested functions to reduce cognitive complexity and maybe distribute responsibilities.

Furthermore, I have this suggestion and would love to contribute if such a change is acceptable.

upper_followed_by_lower_re = re.compile('(.)([A-Z][a-z]+)')
lower_or_num_followed_by_upper_re = re.compile('([a-z0-9])([A-Z])')

def create_from_yaml_single_item(
        k8s_client, yml_object, verbose=False, **kwargs):

    def get_version_and_group(_api_version):
        _group, _, _version = _api_version.partition("/")
        if _version == "":
            _version = group
            _group = "core"
        return _version, _group

    def clean_group(_group):
        # Take care for the case e.g. api_type is "apiextensions.k8s.io"
        # Only replace the last instance
        _group = "".join(_group.rsplit(".k8s.io", 1))
        # convert group name from DNS subdomain format to
        # python class name convention
        return "".join(word.capitalize() for word in _group.split('.'))

    def clean_kind(_kind):
        # Replace CamelCased action_type into snake_case
        _kind = upper_followed_by_lower_re.sub(r'\1_\2', _kind)
        return lower_or_num_followed_by_upper_re.sub(r'\1_\2', _kind).lower()

    def get_response(_yml_obj, _k8s_api, _kind, **_kwargs):

        def get_namespaced_response(__yml_obj, __k8s_api, __kind, **__kwargs):
            __namespace = __yml_obj["metadata"]["namespace"]
            __kwargs['namespace'] = __namespace
            return getattr(__k8s_api, "create_namespaced_{0}".format(_kind_))(
                body=__yml_obj, **__kwargs)

        def get_non_namespaced_response(__yml_obj, __k8s_apis, __kind, **__kwargs):
            __kwargs.pop('namespace', None)
            return getattr(__k8s_api, "create_{0}".format(__kind))(
                body=__yml_obj, **__kwargs)

        # Expect the user to create namespaced objects more often
        if hasattr(_k8s_api, "create_namespaced_{0}"._format(kind)):
            # Decide which namespace we are going to put the object in,
            # if any
            return get_namespaced_response(_yml_obj, _k8s_api, _kind, **_kwargs)
        return get_non_namespaced_response(_yml_obj, _k8s_api, _kind, **_kwargs)


    def display_message(_kind, _resp, *, _verbose):
        if _verbose:
            _msg = "{0} created.".format(_kind)
            if hasattr(resp, 'status'):
                _msg += " status='{0}'".format(str(_resp.status))
            print(_msg)


    version, group = get_version_and_group(yml_object['apiVersion'])
    group = clean_group(group)

    fcn_to_call = "{0}{1}Api".format(group, version.capitalize())
    k8s_api = getattr(client, fcn_to_call)(k8s_client)
    kind = clean_kind(yml_object['kind'])
    resp = get_response(yml_object, k8s_api, kind, **kwargs)
    display_message(kind, resp, _verbose=verbose)
    return resp

Regarding the code:

  • Prefixed _ in nested function variables so that they are there are lesser chances of facing scoping issues.
  • Move the regular expression pattern to the top of the module as it can hopefully reduce execution time.

I believe a couple of other functions too can be broken down but just wanted to confirm if this is acceptable.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions