Skip to content

Commit 1ad308a

Browse files
authored
Getting rid of the dependency in ruamel.yaml (#418)
1 parent a6cd813 commit 1ad308a

13 files changed

+121
-101
lines changed

nca/CoreDS/Peer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def canonical_form(self):
128128

129129
labels = sorted(self.labels.items())
130130
for label in labels:
131-
ret += ',(' + label[0] + ',' + label[1] + ')'
131+
ret += f',({label[0]},{label[1]})'
132132

133133
return ret
134134

nca/FileScanners/DirScanner.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ class DirScanner(GenericTreeScanner, HelmScanner):
1515
A class for reading yaml files from a file system path
1616
"""
1717

18-
def __init__(self, fs_path, rt_load=False):
19-
GenericTreeScanner.__init__(self, rt_load)
18+
def __init__(self, fs_path, fast_load=False):
19+
GenericTreeScanner.__init__(self, fast_load)
2020
HelmScanner.__init__(self)
2121
self.fs_path = fs_path
2222

nca/FileScanners/GenericTreeScanner.py

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55

66
import os
77
import abc
8-
from dataclasses import dataclass
9-
from ruamel.yaml import YAML, error
108
from sys import stderr
9+
from dataclasses import dataclass
10+
import yaml
1111

1212

1313
@dataclass
@@ -19,15 +19,68 @@ class YamlFile:
1919
path: str
2020

2121

22+
class ObjectWithLocation:
23+
line_number = 0
24+
column_number = 0
25+
26+
27+
class YamlDict(dict, ObjectWithLocation):
28+
pass
29+
30+
31+
class YamlList(list, ObjectWithLocation):
32+
pass
33+
34+
35+
def to_yaml_objects(yaml_node):
36+
if isinstance(yaml_node, yaml.SequenceNode):
37+
res = YamlList()
38+
res.line_number = yaml_node.start_mark.line
39+
res.column_number = yaml_node.start_mark.column
40+
for obj in yaml_node.value:
41+
res.append(to_yaml_objects(obj))
42+
return res
43+
if isinstance(yaml_node, yaml.MappingNode):
44+
res = YamlDict()
45+
res.line_number = yaml_node.start_mark.line + 1
46+
res.column_number = yaml_node.start_mark.column + 1
47+
for obj in yaml_node.value:
48+
res[obj[0].value] = to_yaml_objects(obj[1])
49+
return res
50+
51+
# Node is a ScalarNode. First check if it can be interpreted as an int (e.g., port number)
52+
try:
53+
int_val = int(yaml_node.value)
54+
return int_val
55+
except ValueError:
56+
pass
57+
58+
if not yaml_node.style:
59+
# Now check if it is Boolean
60+
if yaml_node.value == 'true':
61+
return True
62+
if yaml_node.value == 'false':
63+
return False
64+
# check if it's the null value
65+
if yaml_node.value in ['', 'null']:
66+
return None
67+
68+
return yaml_node.value
69+
70+
71+
def convert_documents(documents):
72+
return [to_yaml_objects(document) for document in documents]
73+
74+
2275
class GenericTreeScanner(abc.ABC):
2376
"""
2477
A base class for reading yaml files
2578
"""
26-
def __init__(self, rt_load=False):
79+
def __init__(self, fast_load=False):
2780
"""
28-
:param bool rt_load: if True, load yaml with RoundTripLoader
81+
:param bool fast_load: if True, load yaml faster, without saving objects location in file
2982
"""
30-
self.rt_load = rt_load
83+
self.fast_load = fast_load
3184

3285
@abc.abstractmethod
3386
def get_yamls(self):
@@ -49,14 +102,17 @@ def _yield_yaml_file(self, path, stream):
49102
:param str path: the path of the file
50103
:param stream: an IO-Text stream or Union of the file contents, depends on the scanner's type
51104
"""
52-
yaml = YAML(typ="rt") if self.rt_load else YAML(typ="safe")
53105
try:
54-
yield YamlFile(yaml.load_all(stream), path)
55-
except error.MarkedYAMLError as parse_error:
56-
print(f'{parse_error.problem_mark.name}:{parse_error.problem_mark.line}:{parse_error.problem_mark.column}:',
106+
if self.fast_load:
107+
documents = yaml.load_all(stream, Loader=yaml.CSafeLoader)
108+
else:
109+
documents = convert_documents(yaml.compose_all(stream, Loader=yaml.CSafeLoader))
110+
yield YamlFile(documents, path)
111+
except yaml.MarkedYAMLError as parse_error:
112+
print(f'{parse_error.problem_mark.name}:{parse_error.problem_mark.line+1}:{parse_error.problem_mark.column+1}:',
57113
'Parse Error:', parse_error.problem, file=stderr)
58114
return
59-
except error.YAMLError:
115+
except yaml.YAMLError:
60116
print('Bad yaml file:', path, file=stderr)
61117
return
62118

@@ -68,15 +124,15 @@ def _yield_yaml_file(self, path, stream):
68124
class TreeScannerFactory:
69125

70126
@staticmethod
71-
def get_scanner(entry, rt_load=False):
127+
def get_scanner(entry, fast_load=False):
72128
"""
73129
factory method to determine what scanner to build
74130
:param str entry: the entry (path/url) to be scanned
75-
:param bool rt_load: if True, load yaml with RoundTripLoader
131+
:param bool fast_load: if True, load yaml faster, without saving objects location in file
76132
"""
77133
if entry.startswith(('https://github', GitScanner.raw_github_content_prefix)):
78-
return GitScanner(entry, rt_load)
134+
return GitScanner(entry, fast_load)
79135
elif os.path.isfile(entry) or os.path.isdir(entry) or (entry.endswith('**') and os.path.isdir(entry[:-2])):
80-
return DirScanner(entry, rt_load)
136+
return DirScanner(entry, fast_load)
81137
else:
82138
return None

nca/FileScanners/GitScanner.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ class GitScanner(GenericTreeScanner):
1616
"""
1717
raw_github_content_prefix = 'https://raw.githubusercontent'
1818

19-
def __init__(self, url, rt_load=False):
20-
GenericTreeScanner.__init__(self, rt_load)
19+
def __init__(self, url, fast_load=False):
20+
GenericTreeScanner.__init__(self, fast_load)
2121
self.url = url
2222
if url.startswith(self.raw_github_content_prefix):
2323
if not self.is_yaml_file(url):

nca/NetworkConfig/PoliciesFinder.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
# SPDX-License-Identifier: Apache2.0
44
#
55

6-
from ruamel.yaml import YAML
76
from collections import deque
7+
import yaml
88
from nca.Utils.CmdlineRunner import CmdlineRunner
99
from nca.Resources.NetworkPolicy import NetworkPolicy
1010
from nca.Parsers.K8sPolicyYamlParser import K8sPolicyYamlParser
@@ -122,8 +122,7 @@ def _add_policies_to_parse_queue(self, policy_list, file_name):
122122
self.parse_yaml_code_for_policy(policy, file_name)
123123

124124
def _add_policies(self, doc, file_name):
125-
yaml1 = YAML()
126-
code = yaml1.load_all(doc)
125+
code = yaml.load_all(doc, Loader=yaml.CSafeLoader)
127126
for policy_list in code:
128127
if isinstance(policy_list, dict):
129128
self._add_policies_to_parse_queue(policy_list.get('items', []), file_name)

nca/NetworkConfig/ResourcesHandler.py

Lines changed: 22 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,13 @@
55
import copy
66
import os
77
from enum import Enum
8-
from sys import stderr
9-
from ruamel.yaml import error
108
from nca.FileScanners.GenericTreeScanner import TreeScannerFactory
119
from nca.Utils.CmdlineRunner import CmdlineRunner
10+
from nca.Utils.NcaLogger import NcaLogger
1211
from .NetworkConfig import NetworkConfig
1312
from .PoliciesFinder import PoliciesFinder
1413
from .TopologyObjectsFinder import PodsFinder, NamespacesFinder, ServicesFinder
1514
from .PeerContainer import PeerContainer
16-
from nca.Utils.NcaLogger import NcaLogger
1715

1816

1917
class ResourceType(Enum):
@@ -370,23 +368,15 @@ def parse_livesim_yamls(path):
370368
pods_finder = PodsFinder()
371369
ns_finder = NamespacesFinder()
372370
labels_found = {}
373-
try:
374-
for res_code in yaml_file.data:
375-
ns_finder.parse_yaml_code_for_ns(res_code)
376-
pods_finder.namespaces_finder = ns_finder
377-
pods_finder.add_eps_from_yaml(res_code)
378-
for item in ns_finder.namespaces.values():
379-
labels_found.update(item.labels)
380-
for item in pods_finder.peer_set:
381-
labels_found.update(item.labels)
382-
results.update({yaml_file.path: labels_found})
383-
384-
except error.MarkedYAMLError as prs_err:
385-
print(
386-
f'{prs_err.problem_mark.name}:{prs_err.problem_mark.line}:{prs_err.problem_mark.column}:',
387-
'Parse Error:', prs_err.problem, file=stderr)
388-
except UnicodeDecodeError as decode_err:
389-
print(f'Parse Error: Failed to decode {yaml_file.path}. error:\n{decode_err.reason}')
371+
for res_code in yaml_file.data:
372+
ns_finder.parse_yaml_code_for_ns(res_code)
373+
pods_finder.namespaces_finder = ns_finder
374+
pods_finder.add_eps_from_yaml(res_code)
375+
for item in ns_finder.namespaces.values():
376+
labels_found.update(item.labels)
377+
for item in pods_finder.peer_set:
378+
labels_found.update(item.labels)
379+
results.update({yaml_file.path: labels_found})
390380

391381
return results
392382

@@ -406,32 +396,24 @@ def _parse_resources_path(self, resource_list, resource_flags):
406396
elif resource_item == 'istio':
407397
self._handle_istio_inputs(resource_flags)
408398
else:
409-
rt_load = True if ResourceType.Policies in resource_flags else False
410-
resource_scanner = TreeScannerFactory.get_scanner(resource_item, rt_load=rt_load)
399+
fast_load = ResourceType.Policies not in resource_flags
400+
resource_scanner = TreeScannerFactory.get_scanner(resource_item, fast_load=fast_load)
411401
if resource_scanner is None:
412402
continue
413403
yaml_files = resource_scanner.get_yamls()
414404
if not yaml_files:
415405
continue
416406
for yaml_file in yaml_files:
417-
try:
418-
for res_code in yaml_file.data:
419-
if ResourceType.Namespaces in resource_flags:
420-
self.ns_finder.parse_yaml_code_for_ns(res_code)
421-
if ResourceType.Pods in resource_flags:
422-
self.pods_finder.namespaces_finder = self.ns_finder
423-
self.pods_finder.add_eps_from_yaml(res_code)
424-
self.services_finder.namespaces_finder = self.ns_finder
425-
self.services_finder.parse_yaml_code_for_service(res_code, yaml_file)
426-
if ResourceType.Policies in resource_flags:
427-
self.policies_finder.parse_yaml_code_for_policy(res_code, yaml_file.path)
428-
429-
except error.MarkedYAMLError as prs_err:
430-
print(
431-
f'{prs_err.problem_mark.name}:{prs_err.problem_mark.line}:{prs_err.problem_mark.column}:',
432-
'Parse Error:', prs_err.problem, file=stderr)
433-
except UnicodeDecodeError as decode_err:
434-
print(f'Parse Error: Failed to decode {yaml_file.path}. error:\n{decode_err.reason}')
407+
for res_code in yaml_file.data:
408+
if ResourceType.Namespaces in resource_flags:
409+
self.ns_finder.parse_yaml_code_for_ns(res_code)
410+
if ResourceType.Pods in resource_flags:
411+
self.pods_finder.namespaces_finder = self.ns_finder
412+
self.pods_finder.add_eps_from_yaml(res_code)
413+
self.services_finder.namespaces_finder = self.ns_finder
414+
self.services_finder.parse_yaml_code_for_service(res_code, yaml_file)
415+
if ResourceType.Policies in resource_flags:
416+
self.policies_finder.parse_yaml_code_for_policy(res_code, yaml_file.path)
435417

436418
self.policies_finder.parse_policies_in_parse_queue()
437419

nca/NetworkConfig/TopologyObjectsFinder.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ def __init__(self):
3131
def load_peer_from_calico_resource(self):
3232
for peer_type in ['wep', 'hep', 'networkset', 'globalnetworkset']:
3333
peer_code = yaml.load(CmdlineRunner.get_calico_resources(peer_type),
34-
Loader=yaml.SafeLoader)
34+
Loader=yaml.CSafeLoader)
3535
self.add_eps_from_yaml(peer_code)
3636

3737
def load_peer_from_k8s_live_cluster(self):
38-
peer_code = yaml.load(CmdlineRunner.get_k8s_resources('pod'), Loader=yaml.SafeLoader)
38+
peer_code = yaml.load(CmdlineRunner.get_k8s_resources('pod'), Loader=yaml.CSafeLoader)
3939
self.add_eps_from_yaml(peer_code)
4040

4141
def add_eps_from_yaml(self, yaml_obj, kind_override=None):
@@ -262,7 +262,7 @@ def parse_yaml_code_for_ns(self, res_code):
262262

263263
def load_ns_from_live_cluster(self):
264264
yaml_file = CmdlineRunner.get_k8s_resources('namespace')
265-
ns_code = yaml.load(yaml_file, Loader=yaml.SafeLoader)
265+
ns_code = yaml.load(yaml_file, Loader=yaml.CSafeLoader)
266266
self.set_namespaces(ns_code)
267267

268268
def set_namespaces(self, ns_list):
@@ -326,7 +326,7 @@ def load_services_from_live_cluster(self):
326326
:return: The list of parsed services in K8sService format
327327
"""
328328
yaml_file = CmdlineRunner.get_k8s_resources('service')
329-
srv_resources = yaml.load(yaml_file, Loader=yaml.SafeLoader)
329+
srv_resources = yaml.load(yaml_file, Loader=yaml.CSafeLoader)
330330
if not isinstance(srv_resources, dict):
331331
return
332332
parser = K8sServiceYamlParser('k8s')

nca/Parsers/CalicoPolicyYamlParser.py

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
#
55

66
import re
7-
from ruamel.yaml import comments
87
from nca.CoreDS.ProtocolNameResolver import ProtocolNameResolver
98
from nca.CoreDS.Peer import PeerSet, IpBlock
109
from nca.CoreDS.PortSet import PortSet
@@ -258,20 +257,6 @@ def _parse_port(self, port, array):
258257
res_port_set.add_port(port)
259258
return res_port_set
260259

261-
@staticmethod
262-
def _get_value_as_str(dict_to_use, key):
263-
"""
264-
Safely getting string values that contain '!'.
265-
Calico uses '!' as negation operator in selectors, while YAML uses it to declare tags
266-
:param dict dict_to_use: The dictionary to retrieve the value from
267-
:param str key: The key for which the value should be retrieved
268-
:return: The proper string value
269-
"""
270-
val = dict_to_use.get(key)
271-
if isinstance(val, comments.TaggedScalar): # negation operator '!' is used to declare tags in YAML
272-
val = val.tag.value
273-
return val
274-
275260
def _get_rule_peers(self, entity_rule):
276261
"""
277262
Parse the peer-specifying parts of the source/destination parts of a rule
@@ -291,9 +276,9 @@ def _get_rule_peers(self, entity_rule):
291276
for cidr in not_nets:
292277
rule_ips -= IpBlock(cidr)
293278

294-
ns_selector = self._get_value_as_str(entity_rule, 'namespaceSelector')
295-
pod_selector = self._get_value_as_str(entity_rule, 'selector')
296-
not_pod_selector = self._get_value_as_str(entity_rule, 'notSelector')
279+
ns_selector = entity_rule.get('namespaceSelector')
280+
pod_selector = entity_rule.get('selector')
281+
not_pod_selector = entity_rule.get('notSelector')
297282
if ns_selector:
298283
rule_peers = self._parse_label_selector(ns_selector, entity_rule, namespace_selector=True)
299284
elif pod_selector:

nca/Parsers/GenericYamlParser.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
#
55

66
from sys import stderr
7-
from ruamel.yaml import comments
87
from enum import Enum
98
from nca.CoreDS.DimensionsManager import DimensionsManager
109
from nca.CoreDS.TcpLikeProperties import TcpLikeProperties
1110
from nca.CoreDS.MethodSet import MethodSet
1211
from nca.CoreDS.ConnectionSet import ConnectionSet
1312
from nca.CoreDS.PortSet import PortSet
14-
from nca.Utils.NcaLogger import NcaLogger
1513
from nca.CoreDS.Peer import IpBlock
14+
from nca.Utils.NcaLogger import NcaLogger
15+
from nca.FileScanners.GenericTreeScanner import ObjectWithLocation
1616

1717

1818
class GenericYamlParser:
@@ -52,8 +52,8 @@ def syntax_error(self, msg, obj=None):
5252
:param obj: optionally, a CommentedBase object with context
5353
:return: None
5454
"""
55-
if isinstance(obj, comments.CommentedBase):
56-
raise SyntaxError(msg, (self.yaml_file_name, obj.lc.line, obj.lc.col, '')) from None
55+
if isinstance(obj, ObjectWithLocation):
56+
raise SyntaxError(msg, (self.yaml_file_name, obj.line_number, obj.column_number, '')) from None
5757
raise SyntaxError(msg) from None
5858

5959
def warning(self, msg, obj=None):
@@ -64,8 +64,8 @@ def warning(self, msg, obj=None):
6464
:return: None
6565
"""
6666
print_msg = 'Warning: ' + msg
67-
if isinstance(obj, comments.CommentedBase):
68-
print_msg = f'{self.yaml_file_name}:{obj.lc.line}:{obj.lc.col}: {print_msg}'
67+
if isinstance(obj, ObjectWithLocation):
68+
print_msg = f'{self.yaml_file_name}:{obj.line_number}:{obj.column_number}: {print_msg}'
6969

7070
NcaLogger().log_message(print_msg, file=stderr)
7171
self.warning_msgs.append(msg)

0 commit comments

Comments
 (0)