【问题标题】:Python Refactor this function to reduce its Cognitive Complexity from 19 to the 15 allowedPython 重构此函数以将其认知复杂度从 19 降低到允许的 15
【发布时间】:2018-06-08 08:11:47
【问题描述】:

我从 sonarlint 看到这条消息,并试图弄清楚如何降低此功能的认知复杂性。提前感谢任何帮助。

import os
import json
import click
import hcl

cfn = [".json", ".template", ".yaml", ".yml"]
tf  = ["tf"]

def file_handler(dir):
    for root, dirs, files in os.walk(dir):
        for file in files:
            if file.endswith(tuple(cfn)):
                with open(os.path.join(root, file), 'r') as fin:
                    try:
                        file = fin.read()
                        if "AWSTemplateFormatVersion" in file:
                            data = json.dumps(file)
                            print(data)

                    except ValueError as e:
                        raise SystemExit(e)

            elif file.endswith(tuple(tf)):
                with open(os.path.join(root, file), 'r') as file:
                    try:
                        obj  = hcl.load(file)
                        data = json.dumps(obj)
                        print(data)
                    except ValueError as e:
                        raise SystemExit(e)
    return data

【问题讨论】:

  • 您使用什么 metrix 工具来获取结果数字?
  • 提取函数、为文件类型创建处理程序查找、统一错误处理、删除重复等。
  • 你最好在Code Review发帖

标签: python python-2.7 sonarlint


【解决方案1】:

提取函数以产生文件路径:

def _file_paths(directory):
    for root, dirs, filenames in os.walk(directory):
        for filename in filenames:
            file_path = os.path.join(root, filename)
            if os.path.isfile(file_path):
                yield file_path

统一异常处理:

from contextlib import contextmanager

@contextmanager
def _translate_error(from_error, to_error):
    try:
        yield
    except from_error as error:
        raise to_error(error)

提取函数来处理文件类型:

def _handle_cfn(file_object):
    file_contents = file_object.read()
    if "AWSTemplateFormatVersion" in file_contents:
        data = json.dumps(file_contents)
        print(data)


def _handle_tf(file_object):
    obj  = hcl.load(file_oject)
    data = json.dumps(obj)
    print(data)

当文件扩展名不匹配时创建一个空处理程序:

def _null_handler(file_object):
    pass

将文件扩展名映射到处理程序:

_extension_handlers = {'.json': _handle_cfn,
            '.template': _handle_cfn,
            '.yaml': _handle_cfn,
            '.yml': _handle_cfn,
            '.tf': _handle_tf}

把它放在一起:

import os
import json
import click
import hcl

def file_handler(dir):
    for file_path in _file_paths(dir):
        base, extension = os.path.splitext(file_path)
        handler = _extension_handlers.get(extension, _null_handler)
        with open(file_path) as file_object:
            with _translate_error(ValueError, SystemExit):
                handler(file_object)

您可以进一步提取处理每个文件的函数:

def _handle_file(file_path):
    base, extension = os.path.splitext(file_path)
    handler = _extension_handlers.get(extension, _null_handler)
    with open(file_path) as file_object:
        with _translate_error(ValueError, SystemExit):
            handler(file_object)

那么你的主要功能是:

def file_handler(dir):
    for file_path in _file_paths(dir):
        _handle_file(file_path)

【讨论】:

  • 我推荐倒数第二个file_handler(不基于map); map 用于函数式编程,不应用于副作用(在 Python 2 上不必要地创建无用的临时 lists,并且在没有额外更改的情况下根本无法在 Python 3 上工作)。
  • @ShadowRanger 同意,将不加解释地删除,因为它超出了需要。
  • 这行得通。 def _file_paths(directory): for root, dirs, filenames in os.walk(directory): for filenames in filenames: path = os.path.join(root, filename) if os.path.isfile(path): yield path
【解决方案2】:

您应该考虑使用glob 进行递归文件查找,尤其是当您知道要查找的文件扩展名时:

import glob
import json
import os

import click
import hcl

def file_handler(dir):
    for extension in cfn:
        # eg: /path/to/dir/**/*.json
        glob_search = os.path.join(dir, "**/*{}".format(extension))  
        filenames = glob.glob(glob_search, recursive=True)

        for filename in filenames:
            with open(filename, 'r') as fin:
                try:
                    file = fin.read()
                    if "AWSTemplateFormatVersion" in file:
                        data = json.dumps(file)
                        print(data)

                except ValueError as e:
                    raise SystemExit(e)

    for extension in tf:
        # eg: /path/to/dir/**/*tf
        glob_search = os.path.join(dir, "**/*{}".format(extension))
        filenames = glob.glob(glob_search, recursive=True)

        for filename in filenames:
            with open(filename, 'r') as file:
                try:
                    obj = hcl.load(file)
                    data = json.dumps(obj)
                    print(data)
                except ValueError as e:
                    raise SystemExit(e)

仅供参考:关于使用 glob (Use a Glob() to find files recursively in Python?) 的问题

【讨论】:

    【解决方案3】:

    您可以通过使用两个生成器表达式按扩展名过滤文件来删除一级缩进:

    def file_handler(dir):
        for root, dirs, files in os.walk(dir):
            cfn_files = (file for file in files if file.endswith(tuple(cfn)))
            tf_files = (file for file in files if file.endswith(tuple(tf)))
            for file in cfn_files:
                with open(os.path.join(root, file), 'r') as fin:
                    try:
                        file = fin.read()
                        if "AWSTemplateFormatVersion" in file:
                            data = json.dumps(file)
                            print(data)
                    except ValueError as e:
                        raise SystemExit(e)
            for file in tf_files:
                with open(os.path.join(root, file), 'r') as file:
                    try:
                        obj  = hcl.load(file)
                        data = json.dumps(obj)
                        print(data)
                    except ValueError as e:
                        raise SystemExit(e)
        return data
    

    【讨论】:

      猜你喜欢
      • 2021-03-14
      • 2021-09-11
      • 2021-07-01
      • 2020-11-02
      • 2021-10-31
      • 1970-01-01
      • 2019-03-08
      • 1970-01-01
      • 2018-09-19
      相关资源
      最近更新 更多