本文由 大侠(AhcaoZhu)原创,转载请声明。
链接: https://blog.csdn.net/Ahcao2008

一图看懂 pycodestyle 模块:一个检查Python代码是否符合PEP8风格约定的工具,资料整理+笔记(大全)-LMLPHP

一图看懂 pycodestyle 模块:一个检查Python代码是否符合PEP8风格约定的工具,资料整理+笔记(大全)

🧊摘要

🧊模块图

一图看懂 pycodestyle 模块:一个检查Python代码是否符合PEP8风格约定的工具,资料整理+笔记(大全)-LMLPHP

pycodestyle
	◆bisect
	◆configparser
	◆inspect
	◆io
	◆keyword
	◆os
	◆re
	◆sys
	◆time
	◆tokenize
	◆warnings

🧊类关系图

一图看懂 pycodestyle 模块:一个检查Python代码是否符合PEP8风格约定的工具,资料整理+笔记(大全)-LMLPHP

◆object
	pycodestyle.BaseReport
		pycodestyle.FileReport
		pycodestyle.StandardReport
			pycodestyle.DiffReport
	pycodestyle.Checker
	pycodestyle.StyleGuide

🧊模块全展开

☘️【pycodestyle】

根据PEP 8,检查Python源代码格式。
要了解用法和选项列表,请尝试以下操作:
    $ python pycodestyle.py -h
这个程序及其回归测试套件在这里:[参见](https://github.com/pycqa/pycodestyle)
错误和警告组:
    E errors
    W warnings
    100 indentation
    200 whitespace
    300 blank lines
    400 imports
    500 line length
    600 deprecation
    700 statements
    900 syntax error

🔵统计

🔵常量

🌿int

🌿str

🌿tuple

🌿list

🌿dict

🔵模块

🌿15 bisect

🌿16 configparser

🌿17 inspect

🌿18 io

🌿19 keyword

🌿20 os

🌿21 re

🌿22 sys

🌿23 time

🌿24 tokenize

🌿25 warnings

🔵函数

🌿26 fnmatch(name, pat)

测试FILENAME是否与PATTERN匹配。
    模式是Unix shell风格:
    *       匹配一切
    ?       匹配任何单个字符
    [seq]   匹配seq中的任何字符
    [!seq]  匹配任何不在seq中的字符
    FILENAME中的初始周期没有特殊的含义。
    如果操作系统需要,FILENAME和PATTERN首先都是大小写规范化的。
    如果你不想这样,使用fnmatchcase(FILENAME, PATTERN)。

🌿27 lru_cache(maxsize=128, typed=False)

最近最少使用的缓存装饰器。
    如果“*maxsize*”设置为“None”,则不启用LRU特性,缓存可以不受约束地增长。
    如果*typed*为True,不同类型的参数将被单独缓存。
    例如,f(3.0)和f(3)将被视为具有不同结果的不同调用。
    缓存函数的参数必须是可哈希的。使用 f.cache_info() 查看命名tuple的缓存统计信息(hits, misses, maxsize, currsize)。
    使用f.cache_Clear()清除缓存和统计信息。
    使用 f.__wrapped__ 访问底层函数。[参见:](http://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU))

🌿28 _get_parameters(function)

🌿29 register_check(check, codes=None)

注册一个新的检查对象。

🌿30 tabs_or_spaces(physical_line, indent_char)

不要混合制表符和空格。
Python最流行的缩进方式是只使用空格。
第二种最流行的方式是只使用标签。
混合使用制表符和空格缩进的代码应该转换为只使用空格。
当使用-t选项调用Python命令行解释器时,它会对非法混合制表符和空格的代码发出警告。
当使用-tt时,这些警告变成错误。这些选项是强烈推荐的!
    Okay: if a == 0:\n    a = 1\n    b = 1
    E101: if a == 0:\n        a = 1\n\tb = 1

🌿31 tabs_obsolete(physical_line)

在新项目中,强烈建议只使用空格而不是制表符。
    Okay: 如果为真:\n返回
    W191: 如果为真:\n\t 返回

🌿32 trailing_whitespace(physical_line)

尾随空格是多余的。
    返回的警告会根据行本身是否为空而有所不同,以便对那些想要缩进空行的人进行更容易的过滤。
    Okay: spam(1)\n#
    W291: spam(1) \n#
    W293: class Foo(object):\n    \n    bang = 12

🌿33 trailing_blank_lines(physical_line, lines, line_number, total_lines)

尾随空行是多余的。
    Okay: spam(1)
    W391: spam(1)\n
    然而,最后一行应该以新行结束(警告W292)。

🌿34 maximum_line_length(physical_line, max_line_length, multiline, line_number, noqa)

将所有行限制为最多79个字符。
仍然有许多设备被限制在80个字符行;另外,将Windows限制在80个字符使得多个Windows并行使用成为可能。
这些设备上的默认包装看起来很丑。
因此,请将所有行限制为最多79个字符。
对于长文本块(文档字符串或注释),建议将长度限制为72个字符。报告错误E501。

🌿35 _is_one_liner(logical_line, indent_level, lines, line_number)

🌿36 blank_lines(logical_line, blank_lines, indent_level, line_number, blank_before, previous_logical, previous_unindented_logical_line, previous_indent_level, lines)

用两个空行分隔顶级函数和类定义。
    类内的方法定义由单个空行分隔。可
    以使用额外的空行(少量地)来分隔相关函数组。
    在一堆相关的一行程序(例如一组虚拟实现)之间可以省略空行。
    在函数中尽量使用空行来表示逻辑部分。
    Okay: def a():\n    pass\n\n\ndef b():\n    pass
    Okay: def a():\n    pass\n\n\nasync def b():\n    pass
    Okay: def a():\n    pass\n\n\n# Foo\n# Bar\n\ndef b():\n    pass
    Okay: default = 1\nfoo = 1
    Okay: classify = 1\nfoo = 1

    E301: class Foo:\n    b = 0\n    def bar():\n        pass
    E302: def a():\n    pass\n\ndef b(n):\n    pass
    E302: def a():\n    pass\n\nasync def b(n):\n    pass
    E303: def a():\n    pass\n\n\n\ndef b(n):\n    pass
    E303: def a():\n\n\n\n    pass
    E304: @decorator\n\ndef a():\n    pass
    E305: def a():\n    pass\na()
    E306: def a():\n    def b():\n        pass\n    def c():\n        pass

🌿37 extraneous_whitespace(logical_line)

避免多余的空白。
    在这些情况下避免使用多余的空格:
        -立即在圆括号,括号或大括号内。
        —紧接在逗号、分号或冒号前。
    Okay: spam(ham[1], {eggs: 2})
    E201: spam( ham[1], {eggs: 2})
    E201: spam(ham[ 1], {eggs: 2})
    E201: spam(ham[1], { eggs: 2})
    E202: spam(ham[1], {eggs: 2} )
    E202: spam(ham[1 ], {eggs: 2})
    E202: spam(ham[1], {eggs: 2 })

    E203: if x == 4: print x, y; x, y = y , x
    E203: if x == 4: print x, y ; x, y = y, x
    E203: if x == 4 : print x, y; x, y = y, x

🌿38 whitespace_around_keywords(logical_line)

避免在关键字周围使用多余的空白。
    Okay: True and False
    E271: True and  False
    E272: True  and False
    E273: True and\tFalse
    E274: True\tand False

🌿39 missing_whitespace_after_keyword(logical_line, tokens)

关键字后面应该有空格。
    Okay: from foo import (bar, baz)
    E275: from foo import(bar, baz)
    E275: from importable.module import(bar, baz)
    E275: if(foo): bar

🌿40 missing_whitespace(logical_line)

每个逗号、分号或冒号后面都应该有空格。
    Okay: [a, b]
    Okay: (3,)
    Okay: a[3,] = 1
    Okay: a[1:4]
    Okay: a[:4]
    Okay: a[1:]
    Okay: a[1:4:2]
    E231: ['a','b']
    E231: foo(bar,baz)
    E231: [{'a':'b'}]

🌿41 indentation(logical_line, previous_logical, indent_char, indent_level, previous_indent_level, indent_size)

每个缩进级别使用缩进大小(PEP8规定为4)空格。
    对于你不想弄乱的旧代码,你可以继续使用8空格制表符。
    Okay: a = 1
    Okay: if a == 0:\n    a = 1
    E111:   a = 1
    E114:   # a = 1

    Okay: for item in items:\n    pass
    E112: for item in items:\npass
    E115: for item in items:\n# Hi\n    pass

    Okay: a = 1\nb = 2
    E113: a = 1\n    b = 2
    E116: a = 1\n    # b = 2

🌿42 continued_indentation(logical_line, tokens, indent_level, hang_closing, indent_char, indent_size, noqa, verbose)

延续行缩进。
    延续行应该使用Python隐式的圆括号、方括号和大括号内的行连接垂直对齐换行元素,或者使用悬挂缩进。
    当使用悬挂缩进时,应该考虑以下几点:
        -第一行上不应该有参数,并且
        -应该使用进一步的缩进来清楚地区分自己是一个延续行。
    Okay: a = (\n)
    E123: a = (\n    )

    Okay: a = (\n    42)
    E121: a = (\n   42)
    E122: a = (\n42)
    E123: a = (\n    42\n    )
    E124: a = (24,\n     42\n)
    E125: if (\n    b):\n    pass
    E126: a = (\n        42)
    E127: a = (24,\n      42)
    E128: a = (24,\n    42)
    E129: if (a or\n    b):\n    pass
    E131: a = (\n    42\n 24)

🌿43 whitespace_before_parameters(logical_line, tokens)

避免多余的空白。
    在以下情况下避免多余的空白:
        -在函数调用的参数列表开始的圆括号之前。
        -在开始索引或切片的左括号之前。
    Okay: spam(1)
    E211: spam (1)

    Okay: dict['key'] = list[index]
    E211: dict ['key'] = list[index]
    E211: dict['key'] = list [index]

🌿44 whitespace_around_operator(logical_line)

避免在操作符周围使用多余的空白。
    Okay: a = 12 + 3
    E221: a = 4  + 5
    E222: a = 4 +  5
    E223: a = 4\t+ 5
    E224: a = 4 +\t5

🌿45 missing_whitespace_around_operator(logical_line, tokens)

在操作符的两侧使用一个空格。
    -总是在这些二元操作符的两边用一个空格括起来:
        赋值(=),增广赋值(+=,-=等),比较(=,<,>,!=,<=,>=,in, not in, is, is not),布尔值(and, or, not)。
    —如果使用不同优先级的操作符,请考虑在优先级最低的操作符周围添加空白。

    Okay: i = i + 1
    Okay: submitted += 1
    Okay: x = x * 2 - 1
    Okay: hypot2 = x * x + y * y
    Okay: c = (a + b) * (a - b)
    Okay: foo(bar, key='word', *args, **kwargs)
    Okay: alpha[:-i]

    E225: i=i+1
    E225: submitted +=1
    E225: x = x /2 - 1
    E225: z = x **y
    E225: z = 1and 1
    E226: c = (a+b) * (a-b)
    E226: hypot2 = x*x + y*y
    E227: c = a|b
    E228: msg = fmt%(errno, errmsg)

🌿46 whitespace_around_comma(logical_line)

避免逗号或冒号后面出现多余的空白。
    注意:这些检查在默认情况下是禁用的
    Okay: a = (1, 2)
    E241: a = (1,  2)
    E242: a = (1,\t2)

🌿47 whitespace_around_named_parameter_equals(logical_line, tokens)

不要在函数参数中的'='符号周围使用空格。
当用于指示关键字参数或默认形参值时,不要在'='符号周围使用空格,除非使用类型注释。
    Okay: def complex(real, imag=0.0):
    Okay: return magic(r=real, i=imag)
    Okay: boolean(a == b)
    Okay: boolean(a != b)
    Okay: boolean(a <= b)
    Okay: boolean(a >= b)
    Okay: def foo(arg: int = 42):
    Okay: async def foo(arg: int = 42):

    E251: def complex(real, imag = 0.0):
    E251: return magic(r = real, i = imag)
    E252: def complex(real, image: float=0.0):

🌿48 whitespace_before_comment(logical_line, tokens)

将内联注释分开至少两个空格。
    内联注释是与语句在同一行的注释。
    内联注释与语句之间应该至少间隔两个空格。它们应该以#和一个空格开头。
    块注释的每一行都以#和一个或多个空格开头,因为注释中可以有缩进的文本。
    Okay: x = x + 1  # Increment x
    Okay: x = x + 1    # Increment x
    Okay: # Block comments:
    Okay: #  - Block comment list
    Okay: #  - Block comment list
    E261: x = x + 1 # Increment x
    E262: x = x + 1  #Increment x
    E262: x = x + 1  #  Increment x
    E262: x = x + 1  #  Increment x
    E265: #Block comment
    E266: ### Block comment

🌿49 imports_on_separate_lines(logical_line)

将导入放到单独的行上。
    Okay: import os\nimport sys
    E401: import sys, os

    Okay: from subprocess import Popen, PIPE
    Okay: from myclas import MyClass
    Okay: from foo.bar.yourclass import YourClass
    Okay: import myclass
    Okay: import foo.bar.yourclass

🌿50 module_imports_on_top_of_file(logical_line, indent_level, checker_state, noqa)

将导入放在文件的顶部。
    总是把导入放在文件的顶部,就在模块注释和文档字符串之后,模块全局变量和常量之前。
    Okay: import os
    Okay: # this is a comment\nimport os
    Okay: '''this is a module docstring'''\nimport os
    Okay: r'''this is a module docstring'''\nimport os
    Okay:
    try:\n\timport x\nexcept ImportError:\n\tpass\nelse:\n\tpass\nimport y
    Okay:
    try:\n\timport x\nexcept ImportError:\n\tpass\nfinally:\n\tpass\nimport y
    E402: a=1\nimport os
    E402: 'One string'\n"Two string"\nimport os
    E402: a=1\nfrom sys import x

    Okay: if x:\n    import os

🌿51 compound_statements(logical_line)

通常不鼓励使用复合语句(在同一行)。
    虽然有时将if/for/ While语句与一个小体放在同一行是可以的,但对于多子句语句永远不要这样做。
    也要避免折叠这么长的线!始终使用def语句,而不是将lambda表达式直接绑定到名称的赋值语句。

    Okay: if foo == 'blah':\n    do_blah_thing()
    Okay: do_one()
    Okay: do_two()
    Okay: do_three()

    E701: if foo == 'blah': do_blah_thing()
    E701: for x in lst: total += x
    E701: while t < 10: t = delay()
    E701: if foo == 'blah': do_blah_thing()
    E701: else: do_non_blah_thing()
    E701: try: something()
    E701: finally: cleanup()
    E701: if foo == 'blah': one(); two(); three()
    E702: do_one(); do_two(); do_three()
    E703: do_four();  # useless semicolon
    E704: def f(x): return 2*x
    E731: f = lambda x: 2*x

🌿52 explicit_line_join(logical_line, tokens)

避免括号之间显式的行连接。
    换行的首选方法是在括号、方括号和大括号内使用Python隐含的行延续。
    通过将表达式括在括号中,可以将长行分隔成多行。
    这些应该优先于使用反斜杠进行行延续。
    E502: aaa = [123, \\n       123]
    E502: aaa = ("bbb " \\n       "ccc")

    Okay: aaa = [123,\n       123]
    Okay: aaa = ("bbb "\n       "ccc")
    Okay: aaa = "bbb " \\n    "ccc"
    Okay: aaa = 123  # \\

🌿53 _is_binary_operator(token_type, text)

🌿54 _break_around_binary_operators(tokens)

私人功能,以减少重复。
    这就把 :func:`break_before_binary_operator` 和 :func:`break_after_binary_operator` 的共享细节分解出来了。

🌿55 break_before_binary_operator(logical_line, tokens)

避免在二进制操作符之前中断。
    绕过二进制运算符的最佳位置是在运算符之后,而不是在运算符之前。
    W503: (width == 0\n + height == 0)
    W503: (width == 0\n and height == 0)
    W503: var = (1\n       & ~2)
    W503: var = (1\n       / -2)
    W503: var = (1\n       + -1\n       + -2)

    Okay: foo(\n    -x)
    Okay: foo(x\n    [])
    Okay: x = '''\n''' + ''
    Okay: foo(x,\n    -y)
    Okay: foo(x,  # comment\n    -y)

🌿56 break_after_binary_operator(logical_line, tokens)

避免在二进制运算符之后中断。
    绕过二进制运算符的最佳位置是在运算符之前,而不是之后。
    W504: (width == 0 +\n height == 0)
    W504: (width == 0 and\n height == 0)
    W504: var = (1 &\n       ~2)

    Okay: foo(\n    -x)
    Okay: foo(x\n    [])
    Okay: x = '''\n''' + ''
    Okay: x = '' + '''\n'''
    Okay: foo(x,\n    -y)
    Okay: foo(x,  # comment\n    -y)
    下面应该是W504,但是unary_context在这方面很棘手
    Okay: var = (1 /\n       -2)
    Okay: var = (1 +\n       -1 +\n       -2)

🌿57 comparison_to_singleton(logical_line, noqa)

与单例的比较应该使用“is”或“is not”。
    与None这样的单例比较应该总是使用"is"或"is not",而不是相等操作符。
    Okay: if arg is not None:
    E711: if arg != None:
    E711: if None == arg:
    E712: if arg == True:
    E712: if False == arg:
    另外,当你真正的意思是如果x不是None时,注意不要写if x
    -例如,当测试默认为None的变量或参数是否被设置为其他值时。另一个值的类型(例如容器)在布尔上下文中可能为false !

🌿58 comparison_negative(logical_line)

否定比较应该使用“not in”和“is not”。
    Okay: if x not in y:\n    pass
    Okay: assert (X in Y or X is Z)
    Okay: if not (X in Y):\n    pass
    Okay: zz = x is not y
    E713: Z = not X in Y
    E713: if not X.B in Y:\n    pass
    E714: if not X is Y:\n    pass
    E714: Z = not X.B is Y

🌿59 comparison_type(logical_line, noqa)

对象类型比较应该始终使用isinstance()。
    不要直接比较类型。
    Okay: if isinstance(obj, int):
    E721: if type(obj) is type(1):
    当检查一个对象是否是字符串时,请记住它也可能是unicode字符串!
    在Python 2.3中,str和unicode有一个共同的基类basestring,所以你可以这样做:
    Okay: if isinstance(obj, basestring):
    Okay: if type(a1) is type(b1):

🌿60 bare_except(logical_line, noqa)

在捕获异常时,尽可能提及特定的异常。
    Okay: except Exception:
    Okay: except BaseException:
    E722: except:

🌿61 ambiguous_identifier(logical_line, tokens)

永远不要使用字符“l”、“O”或“I”作为变量名。
    在某些字体中,这些字符与数字1和0无法区分。当你想用“l”的时候,就用“L”代替。
    Okay: L = 0
    Okay: o = 123
    Okay: i = 42
    E741: l = 0
    E741: O = 123
    E741: I = 42
    变量可以在其他几个上下文中绑定,包括类和函数定义、lambda函数、“全局”和“非局部”语句、异常处理程序以及“with”和“for”语句。

    Okay: except AttributeError as o:
    Okay: with lock as L:
    Okay: foo(l=12)
    Okay: foo(l=I)
    Okay: for a in foo(l=12):
    Okay: lambda arg: arg * l
    Okay: lambda a=l[I:5]: None
    Okay: lambda x=a.I: None
    Okay: if l >= 12:
    E741: except AttributeError as O:
    E741: with lock as l:
    E741: global I
    E741: nonlocal l
    E741: def foo(l):
    E741: def foo(l=12):
    E741: l = foo(l=12)
    E741: for l in range(10):
    E741: [l for l in lines if l]
    E741: lambda l: None
    E741: lambda a=x[1:5], l: None
    E741: lambda **l:
    E741: def f(**l):
    E742: class I(object):
    E743: def l(x):

🌿62 python_3000_invalid_escape_sequence(logical_line, tokens, noqa)

无效转义序列在Python 3.6中已弃用。
    Okay: regex = r'\.png$'
    W605: regex = '\.png$'

🌿63 python_3000_async_await_keywords(logical_line, tokens)

从Python 3.7开始,'async'和'await'是保留关键字。
    W606: async = 42
    W606: await = 42
    Okay: async def read(db):
    data = await db.fetch('SELECT ...')

🌿64 maximum_doc_length(logical_line, max_doc_length, noqa, tokens)

将所有文档行限制为最多72个字符。
    对于长文本块(文档字符串或注释),建议将长度限制为72个字符。
    报告警告W505

🌿65 readlines(filename)

阅读源代码。

🌿66 stdin_get_value()

从stdin中读取值。

🌿67 expand_indent(line)

返回缩进量。
    制表符将展开到下一个8的倍数。
    >>> expand_indent('    ')
    4
    >>> expand_indent('\t')
    8
    >>> expand_indent('       \t')
    8
    >>> expand_indent('        \t')
    16

🌿68 mute_string(text)

将内容替换为'xxx'以防止语法匹配。
    >>> mute_string('"abc"')
    '"xxx"'
    >>> mute_string("'''abc'''")
    "'''xxx'''"
    >>> mute_string("r'abc'")
    "r'xxx'"

🌿69 parse_udiff(diff, patterns=None, parent=‘.’)

返回匹配行的字典。

🌿70 normalize_paths(value, parent=‘.’)

解析以逗号分隔的路径列表。返回一个绝对路径列表。

🌿71 filename_match(filename, patterns, default=True)

检查patterns是否包含与filename匹配的模式。
    如果patterns未指定,则总是返回True。

🌿72 update_counts(s, counts)

对于计数中的字符,将s中的字符每次出现的次数加1

🌿73 _is_eol_token(token)

🌿74 get_parser(prog=‘pycodestyle’, version=‘2.10.0’)

为程序创建解析器。

🌿75 read_config(options, args, arglist, parser)

读取和解析配置。
    如果在命令行中使用“--config”选项指定了配置文件,那么只有它被用于配置。
    否则,用户配置(~/.config/pycodestyle)和当前目录或以上目录中的任何本地配置将使用ConfigParser的read方法合并在一起(按此顺序)。

🌿76 process_options(arglist=None, parse_argv=False, config_file=None, parser=None, verbose=None)

通过参数列表或命令行参数传递的进程选项。
    传入``config_file``参数允许其他工具,如flake8指定自己的选项以pycodestyle进行处理。

🌿77 _parse_multi_options(options, split_token=‘,’)

拆开,剥去,丢弃空的。
    把下面的句子变成:
    A,
    B,
    into ["A", "B"]

🌿78 _main()

解析选项并在Python源代码上运行检查。

🔵类

🌿79 optparse.OptionParser

解析器的命令行选项。

🌿80 pycodestyle.Checker

加载Python源文件,标记它,检查编码风格。

method

从令牌构建逻辑行。
对输入文件运行所有检查。
构建文件的AST并运行所有AST检查。
从令牌构建一行,并在其上运行所有逻辑检查。
在原始输入行上运行所有物理检查。
标记文件,运行物理行检查并生成标记。
为特定的检查器插件准备自定义状态。
如果合适的话,检查当前的物理线路。
从输入缓冲区中获取下一行。
检查语法是否有效。
运行检查插件。

🌿81 pycodestyle.BaseReport

收集检查结果。

data

method

根据选项报告错误。
返回错误和警告的总数。
返回该文件的错误和警告计数。
获取以该前缀开头的消息代码的统计信息。
    prefix='' 匹配所有错误和警告前缀
    prefix='E' 匹配所有错误前缀
    prefix='W' 匹配所有警告前缀
    prefix='E4' 匹配所有与导入有关的错误
标记一条新的逻辑线。
给新文件发信号。
打印基准数据。
打印总体统计信息(错误和警告的数量)。
启动计时器。
停止计时器。

🌿82 pycodestyle.FileReport

收集检查结果并打印文件名。

data

🌿83 pycodestyle.StandardReport

收集并打印检查结果。

method

根据选项报告错误。
打印结果并返回该文件的总计数。
给新文件发信号。

🌿84 pycodestyle.DiffReport

仅收集和打印更改行的结果。

method

🌿85 pycodestyle.StyleGuide

初始化带有少量选项的PEP-8实例。

method

对路径执行所有检查。
检查是否应该排除该文件。
    检查它的选项'options.exclude'是否包含一个模式匹配的文件名。
得到这个类别的所有检查。
    查找所有全局可见的函数,其中第一个参数名称以参数名称开头,并且包含选定的测试。
检查是否应该忽略错误代码。
    如果'options.select'包含错误码的前缀,返回False。
    否则,if 'options.ignore'包含错误代码的前缀,返回True。
初始化报表实例。
检查此目录和所有子目录中的所有文件。
对Python源文件运行所有检查。

🔵私有或局部

🔵剩余

☘️【bisect】

☘️【configparser】

☘️【inspect】

☘️【io】

☘️【keyword】

☘️【os】

☘️【re】

☘️【sys】

☘️【time】

☘️【tokenize】

☘️【warnings】

05-28 13:08