forked from ThreatConnect-Inc/tcex-util
-
Notifications
You must be signed in to change notification settings - Fork 0
/
code_operation.py
112 lines (96 loc) · 3.95 KB
/
code_operation.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
"""TcEx Framework Module"""
# standard library
import ast
import logging
import os
import re
# third-party
import black
import isort
from black.report import NothingChanged
from .render.render import Render
# get logger
_logger = logging.getLogger(__name__.split('.', maxsplit=1)[0])
class CodeOperation:
"""TcEx FrameWork Code Operations"""
@staticmethod
def find_line_in_code(
needle: str | re.Pattern,
code: str,
trigger_start: re.Pattern | str | None = None,
trigger_stop: re.Pattern | str | None = None,
) -> str | None:
"""Return matching line of code in a class definition.
Args:
needle: The string to search for.
code: The contents of the Python file to search.
trigger_start: The regex pattern to use to trigger the search.
trigger_stop: The regex pattern to use to stop the search.
"""
magnet_on = not trigger_start
for line in ast.unparse(ast.parse(code)).split('\n'):
if line.lstrip()[:1] not in ("'", '"'):
# Find class before looking for needle
if trigger_start is not None and re.match(trigger_start, line):
magnet_on = True
continue
# find need now that class definition is found
if magnet_on is True and re.match(needle, line):
line = line.strip()
return line
# break if needle not found before next class definition
if trigger_stop is not None and re.match(trigger_stop, line) and magnet_on is True:
break
return None
@staticmethod
def find_line_number(
needle: str,
contents: str,
trigger_start: re.Pattern | str | None = None,
trigger_stop: re.Pattern | str | None = None,
) -> int | None:
"""Return matching line of code in a class definition.
Args:
needle: The string to search for.
contents: The contents (haystack) to search
trigger_start: The regex pattern to use to trigger the search.
trigger_stop: The regex pattern to use to stop the search.
"""
magnet_on = not trigger_start
for line_number, line in enumerate(contents.split('\n'), start=1):
if line.strip():
# set magnet_on to True if trigger_start is found
if trigger_start is not None and re.match(trigger_start, line):
magnet_on = True
continue
# find needle now that trigger is found
if magnet_on is True and re.match(needle, line):
return line_number
# break if trigger_stop is defined and found
if trigger_stop is not None and re.match(trigger_stop, line) and magnet_on is True:
break
return None
@staticmethod
def format_code(_code):
"""Return formatted code."""
# run black formatter on code
mode = black.FileMode(line_length=100, string_normalization=False)
try:
_code = black.format_file_contents(_code, fast=False, mode=mode)
except ValueError as ex:
_logger.exception('Formatting of code with black failed.')
Render.panel.failure(f'Formatting of code with black failed {ex}.')
except NothingChanged:
pass
# run isort on code
try:
isort_args = (
{'settings_file': 'pyproject.toml'} if os.path.isfile('pyproject.toml') else {}
)
isort_config = isort.Config(**isort_args) # type: ignore
_code = isort.code(_code, config=isort_config)
except Exception as ex:
_logger.exception('Formatting of code with isort failed.')
# _logger.debug(_code)
Render.panel.failure(f'Formatting of code with isort failed {ex}.')
return _code