blob: 96e8dc21e0e4541af7247b8745dd028c9a361481 [file] [log] [blame]
Matteo Scandolod2044a42017-08-07 16:08:28 -07001# Copyright 2017-present Open Networking Foundation
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15
Zack Williams045b63d2019-01-22 16:30:57 -070016from __future__ import print_function
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -070017import pdb
Sapan Bhatiac4f803f2017-04-21 11:50:39 +020018import re
Scott Baker391f5d82018-10-02 16:34:41 -070019from inflect import engine as inflect_engine_class
20
21inflect_engine = inflect_engine_class()
Sapan Bhatia7886e122017-05-17 11:19:39 +020022
Zack Williams045b63d2019-01-22 16:30:57 -070023
Sapan Bhatiaf7934b52017-06-12 05:04:23 -070024class FieldNotFound(Exception):
25 def __init__(self, message):
26 super(FieldNotFound, self).__init__(message)
27
Zack Williams045b63d2019-01-22 16:30:57 -070028
Sapan Bhatiad4567592017-07-24 17:26:26 -040029def xproto_debug(**kwargs):
Zack Williams045b63d2019-01-22 16:30:57 -070030 print(kwargs)
Sapan Bhatiad4567592017-07-24 17:26:26 -040031 pdb.set_trace()
32
Zack Williams045b63d2019-01-22 16:30:57 -070033
Sapan Bhatia943dad52017-05-19 18:41:01 +020034def xproto_unquote(s):
35 return unquote(s)
36
Zack Williams045b63d2019-01-22 16:30:57 -070037
Sapan Bhatia49b54ae2017-05-19 17:11:32 +020038def unquote(s):
Zack Williams045b63d2019-01-22 16:30:57 -070039 if s.startswith('"') and s.endswith('"'):
Sapan Bhatia49b54ae2017-05-19 17:11:32 +020040 return s[1:-1]
Matteo Scandolo292cc2a2017-07-31 19:02:12 -070041 else:
42 return s
Sapan Bhatia49b54ae2017-05-19 17:11:32 +020043
Zack Williams045b63d2019-01-22 16:30:57 -070044
Sapan Bhatia49b54ae2017-05-19 17:11:32 +020045def xproto_singularize(field):
46 try:
47 # The user has set a singular, as an exception that cannot be handled automatically
Zack Williams045b63d2019-01-22 16:30:57 -070048 singular = field["options"]["singular"]
Sapan Bhatia49b54ae2017-05-19 17:11:32 +020049 singular = unquote(singular)
50 except KeyError:
Zack Williams045b63d2019-01-22 16:30:57 -070051 singular = inflect_engine.singular_noun(field["name"])
Scott Bakera1b089a2018-10-05 09:59:17 -070052 if singular is False:
53 # singular_noun returns False on a noun it can't singularize
54 singular = field["name"]
Sapan Bhatia49b54ae2017-05-19 17:11:32 +020055
56 return singular
57
Zack Williams045b63d2019-01-22 16:30:57 -070058
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +020059def xproto_singularize_pluralize(field):
60 try:
61 # The user has set a plural, as an exception that cannot be handled automatically
Zack Williams045b63d2019-01-22 16:30:57 -070062 plural = field["options"]["plural"]
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +020063 plural = unquote(plural)
64 except KeyError:
Zack Williams045b63d2019-01-22 16:30:57 -070065 singular = inflect_engine.singular_noun(field["name"])
Scott Bakera1b089a2018-10-05 09:59:17 -070066 if singular is False:
67 # singular_noun returns False on a noun it can't singularize
68 singular = field["name"]
69
70 plural = inflect_engine.plural_noun(singular)
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +020071
72 return plural
73
Zack Williams045b63d2019-01-22 16:30:57 -070074
Sapan Bhatia7886e122017-05-17 11:19:39 +020075def xproto_pluralize(field):
76 try:
77 # The user has set a plural, as an exception that cannot be handled automatically
Zack Williams045b63d2019-01-22 16:30:57 -070078 plural = field["options"]["plural"]
Sapan Bhatia49b54ae2017-05-19 17:11:32 +020079 plural = unquote(plural)
Sapan Bhatia7886e122017-05-17 11:19:39 +020080 except KeyError:
Zack Williams045b63d2019-01-22 16:30:57 -070081 plural = inflect_engine.plural_noun(field["name"])
Sapan Bhatia7886e122017-05-17 11:19:39 +020082
83 return plural
Sapan Bhatiac4f803f2017-04-21 11:50:39 +020084
Zack Williams045b63d2019-01-22 16:30:57 -070085
86def xproto_base_def(model_name, base, suffix="", suffix_list=[]):
87 if model_name == "XOSBase":
88 return "(models.Model, PlModelMixIn)"
89 elif not base:
90 return ""
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -070091 else:
Zack Williams045b63d2019-01-22 16:30:57 -070092 int_base = [i["name"] + suffix for i in base if i["name"] in suffix_list]
93 ext_base = [i["name"] for i in base if i["name"] not in suffix_list]
94 return "(" + ",".join(int_base + ext_base) + ")"
95
Sapan Bhatiaff1b8fa2017-04-10 19:44:38 -070096
Sapan Bhatia504cc972017-04-27 01:56:28 +020097def xproto_first_non_empty(lst):
98 for l in lst:
Zack Williams045b63d2019-01-22 16:30:57 -070099 if l:
100 return l
101
Sapan Bhatia504cc972017-04-27 01:56:28 +0200102
Sapan Bhatia943dad52017-05-19 18:41:01 +0200103def xproto_api_type(field):
104 try:
Zack Williams045b63d2019-01-22 16:30:57 -0700105 if unquote(field["options"]["content_type"]) == "date":
106 return "double"
Sapan Bhatia943dad52017-05-19 18:41:01 +0200107 except KeyError:
108 pass
109
Zack Williams045b63d2019-01-22 16:30:57 -0700110 return field["type"]
Sapan Bhatia943dad52017-05-19 18:41:01 +0200111
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200112
113def xproto_base_name(n):
114 # Hack - Refactor NetworkParameter* to make this go away
Zack Williams045b63d2019-01-22 16:30:57 -0700115 if n.startswith("NetworkParameter"):
116 return "_"
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200117
Zack Williams045b63d2019-01-22 16:30:57 -0700118 expr = r"^[A-Z]+[a-z]*"
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200119
120 try:
121 match = re.findall(expr, n)[0]
Zack Williams045b63d2019-01-22 16:30:57 -0700122 except BaseException:
123 return "_"
Sapan Bhatiac4f803f2017-04-21 11:50:39 +0200124
125 return match
Sapan Bhatiaae9645c2017-05-05 15:35:54 +0200126
Zack Williams045b63d2019-01-22 16:30:57 -0700127
Sapan Bhatia943dad52017-05-19 18:41:01 +0200128def xproto_base_fields(m, table):
129 fields = []
130
Zack Williams045b63d2019-01-22 16:30:57 -0700131 for b in m["bases"]:
132 option1 = b["fqn"]
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200133 try:
Zack Williams045b63d2019-01-22 16:30:57 -0700134 option2 = m["package"] + "." + b["name"]
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200135 except TypeError:
136 option2 = option1
Sapan Bhatia943dad52017-05-19 18:41:01 +0200137
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200138 accessor = None
Zack Williams045b63d2019-01-22 16:30:57 -0700139 if option1 in table:
140 accessor = option1
141 elif option2 in table:
142 accessor = option2
Sapan Bhatia3cfdf632017-06-08 05:14:03 +0200143
144 if accessor:
145 base_fields = xproto_base_fields(table[accessor], table)
146
Zack Williams045b63d2019-01-22 16:30:57 -0700147 model_fields = [x.copy() for x in table[accessor]["fields"]]
Scott Bakerc237f882018-09-28 14:12:47 -0700148 for field in model_fields:
149 field["accessor"] = accessor
150
Sapan Bhatia943dad52017-05-19 18:41:01 +0200151 fields.extend(base_fields)
152 fields.extend(model_fields)
153
Zack Williams045b63d2019-01-22 16:30:57 -0700154 if "no_sync" in m["options"] and m["options"]["no_sync"]:
155 fields = [
156 f
157 for f in fields
158 if f["name"] != "backend_status" and f["name"] != "backend_code"
159 ]
Matteo Scandolo39b4a272017-11-17 11:09:21 -0800160
Zack Williams045b63d2019-01-22 16:30:57 -0700161 if "no_policy" in m["options"] and m["options"]["no_policy"]:
162 fields = [
163 f
164 for f in fields
165 if f["name"] != "policy_status" and f["name"] != "policy_code"
166 ]
Matteo Scandolo39b4a272017-11-17 11:09:21 -0800167
Sapan Bhatia943dad52017-05-19 18:41:01 +0200168 return fields
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200169
Zack Williams045b63d2019-01-22 16:30:57 -0700170
Scott Bakerc237f882018-09-28 14:12:47 -0700171def xproto_fields(m, table):
172 """ Generate the full list of models for the xproto message `m` including fields from the classes it inherits.
173
174 Inserts the special field "id" at the very beginning.
175
176 Each time we descend a new level of inheritance, increment the offset field numbers by 100. The base
177 class's fields will be numbered from 1-99, the first descendant will be number 100-199, the second
178 descdendant numbered from 200-299, and so on. This assumes any particular model as at most 100
179 fields.
180 """
181
182 model_fields = [x.copy() for x in m["fields"]]
183 for field in model_fields:
184 field["accessor"] = m["fqn"]
185
186 fields = xproto_base_fields(m, table) + model_fields
187
188 # The "id" field is a special field. Every model has one. Put it up front and pretend it's part of the
189
Scott Baker1f7791d2018-10-04 13:21:20 -0700190 if not fields:
Zack Williams045b63d2019-01-22 16:30:57 -0700191 raise Exception(
192 "Model %s has no fields. Check for missing base class." % m["name"]
193 )
Scott Baker1f7791d2018-10-04 13:21:20 -0700194
Zack Williams045b63d2019-01-22 16:30:57 -0700195 id_field = {
196 "type": "int32",
197 "name": "id",
198 "options": {},
199 "id": "1",
200 "accessor": fields[0]["accessor"],
201 }
Scott Bakerc237f882018-09-28 14:12:47 -0700202
203 fields = [id_field] + fields
204
205 # Walk through the list of fields. They will be in depth-first search order from the base model forward. Each time
206 # the model changes, offset the protobuf field numbers by 100.
207 offset = 0
208 last_accessor = fields[0]["accessor"]
209 for field in fields:
Zack Williams045b63d2019-01-22 16:30:57 -0700210 if field["accessor"] != last_accessor:
Scott Bakerc237f882018-09-28 14:12:47 -0700211 last_accessor = field["accessor"]
212 offset += 100
213 field_id = int(field["id"])
214 if (field_id < 1) or (field_id >= 100):
Zack Williams045b63d2019-01-22 16:30:57 -0700215 raise Exception(
216 "Only field numbers from 1 to 99 are permitted, field %s in model %s"
217 % (field["name"], field["accessor"])
218 )
Scott Bakerc237f882018-09-28 14:12:47 -0700219 field["id"] = int(field["id"]) + offset
220
221 # Check for duplicates
222 fields_by_number = {}
223 for field in fields:
224 id = field["id"]
225 dup = fields_by_number.get(id)
226 if dup:
Zack Williams045b63d2019-01-22 16:30:57 -0700227 raise Exception(
228 "Field %s has duplicate number %d with field %s in model %s"
229 % (field["name"], id, dup["name"], field["accessor"])
230 )
Scott Bakerc237f882018-09-28 14:12:47 -0700231 fields_by_number[id] = field
232
233 return fields
234
Zack Williams045b63d2019-01-22 16:30:57 -0700235
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200236def xproto_base_rlinks(m, table):
237 links = []
238
Zack Williams045b63d2019-01-22 16:30:57 -0700239 for base in m["bases"]:
240 b = base["name"]
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200241 if b in table:
242 base_rlinks = xproto_base_rlinks(table[b], table)
243
Zack Williams045b63d2019-01-22 16:30:57 -0700244 model_rlinks = [x.copy() for x in table[b]["rlinks"]]
Scott Bakerd87c02a2018-10-29 16:24:29 -0700245 for link in model_rlinks:
246 link["accessor"] = b
247
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200248 links.extend(base_rlinks)
249 links.extend(model_rlinks)
250
251 return links
252
Zack Williams045b63d2019-01-22 16:30:57 -0700253
Scott Bakerc237f882018-09-28 14:12:47 -0700254def xproto_rlinks(m, table):
255 """ Return the reverse links for the xproto message `m`.
256
Scott Bakerd87c02a2018-10-29 16:24:29 -0700257 If the link includes a reverse_id, then it will be used for the protobuf field id. Each level of inheritance
258 will add an offset of 100 to the supplied reverse_id.
259
260 If there is no reverse_id, then one will automatically be allocated started at id 1900. It is encouraged that
261 all links include reverse_ids, so that field identifiers are deterministic across all protobuf messages.
Scott Bakerc237f882018-09-28 14:12:47 -0700262 """
263
Scott Bakerd87c02a2018-10-29 16:24:29 -0700264 model_rlinks = [x.copy() for x in m["rlinks"]]
265 for link in model_rlinks:
266 link["accessor"] = m["fqn"]
267
268 links = xproto_base_rlinks(m, table) + model_rlinks
Scott Bakerc237f882018-09-28 14:12:47 -0700269
Zack Williams045b63d2019-01-22 16:30:57 -0700270 links = [
271 x for x in links if ("+" not in x["src_port"]) and ("+" not in x["dst_port"])
272 ]
Scott Bakerc237f882018-09-28 14:12:47 -0700273
Scott Bakerd87c02a2018-10-29 16:24:29 -0700274 if links:
275 last_accessor = links[0]["accessor"]
276 offset = 0
277 index = 1900
278 for link in links:
Zack Williams045b63d2019-01-22 16:30:57 -0700279 if link["accessor"] != last_accessor:
Scott Bakerd87c02a2018-10-29 16:24:29 -0700280 last_accessor = link["accessor"]
281 offset += 100
Scott Bakerc237f882018-09-28 14:12:47 -0700282
Scott Bakerd87c02a2018-10-29 16:24:29 -0700283 if link["reverse_id"]:
284 # Statically numbered reverse links. Use the id that the developer supplied, adding the offset based on
285 # inheritance depth.
286 link["id"] = int(link["reverse_id"]) + offset
287 else:
288 # Automatically numbered reverse links. These will eventually go away.
289 link["id"] = index
290 index += 1
291
292 # check for duplicates
Zack Williams045b63d2019-01-22 16:30:57 -0700293 links_by_number = {}
Scott Bakerd87c02a2018-10-29 16:24:29 -0700294 for link in links:
295 id = link["id"]
Zack Williams045b63d2019-01-22 16:30:57 -0700296 dup = links_by_number.get(id)
Scott Bakerd87c02a2018-10-29 16:24:29 -0700297 if dup:
Zack Williams045b63d2019-01-22 16:30:57 -0700298 raise Exception(
299 "Field %s has duplicate number %d in model %s with reverse field %s"
300 % (link["src_port"], id, m["name"], dup["src_port"])
301 )
Scott Bakerd87c02a2018-10-29 16:24:29 -0700302 links_by_number[id] = link
Scott Bakerc237f882018-09-28 14:12:47 -0700303
304 return links
305
306
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200307def xproto_base_links(m, table):
308 links = []
309
Zack Williams045b63d2019-01-22 16:30:57 -0700310 for base in m["bases"]:
311 b = base["name"]
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200312 if b in table:
313 base_links = xproto_base_links(table[b], table)
314
Zack Williams045b63d2019-01-22 16:30:57 -0700315 model_links = table[b]["links"]
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200316 links.extend(base_links)
317 links.extend(model_links)
318 return links
319
Zack Williams045b63d2019-01-22 16:30:57 -0700320
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200321def xproto_string_type(xptags):
322 try:
Zack Williams045b63d2019-01-22 16:30:57 -0700323 max_length = eval(xptags["max_length"])
324 except BaseException:
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200325 max_length = 1024
326
Zack Williams045b63d2019-01-22 16:30:57 -0700327 if "varchar" not in xptags:
328 return "string"
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200329 else:
Zack Williams045b63d2019-01-22 16:30:57 -0700330 return "text"
331
Sapan Bhatiad022aeb2017-06-07 15:49:55 +0200332
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700333def xproto_tuplify(nested_list_or_set):
Zack Williams045b63d2019-01-22 16:30:57 -0700334 if not isinstance(nested_list_or_set, list) and not isinstance(
335 nested_list_or_set, set
336 ):
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700337 return nested_list_or_set
338 else:
339 return tuple([xproto_tuplify(i) for i in nested_list_or_set])
340
Zack Williams045b63d2019-01-22 16:30:57 -0700341
342def xproto_field_graph_components(fields, model, tag="unique_with"):
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700343 def find_components(graph):
344 pending = set(graph.keys())
345 components = []
346
347 while pending:
Zack Williams045b63d2019-01-22 16:30:57 -0700348 front = {pending.pop()}
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700349 component = set()
350
351 while front:
352 node = front.pop()
353 neighbours = graph[node]
Matteo Scandoloa17e6e42018-05-25 10:28:25 -0700354 neighbours -= component # These we have already visited
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700355 front |= neighbours
356
Matteo Scandoloa17e6e42018-05-25 10:28:25 -0700357 pending -= neighbours
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700358 component |= neighbours
Zack Williams045b63d2019-01-22 16:30:57 -0700359
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700360 components.append(component)
361
362 return components
363
364 field_graph = {}
Zack Williams045b63d2019-01-22 16:30:57 -0700365 field_names = {f["name"] for f in fields}
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700366
367 for f in fields:
368 try:
Zack Williams045b63d2019-01-22 16:30:57 -0700369 tagged_str = unquote(f["options"][tag])
370 tagged_fields = tagged_str.split(",")
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700371
372 for uf in tagged_fields:
373 if uf not in field_names:
Zack Williams045b63d2019-01-22 16:30:57 -0700374 raise FieldNotFound(
375 'Field "%s" not found in model "%s", referenced from field "%s" by option "%s"'
376 % (uf, model["name"], f["name"], tag)
377 )
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700378
Zack Williams045b63d2019-01-22 16:30:57 -0700379 field_graph.setdefault(f["name"], set()).add(uf)
380 field_graph.setdefault(uf, set()).add(f["name"])
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700381 except KeyError:
382 pass
383
Matteo Scandoloa17e6e42018-05-25 10:28:25 -0700384 return find_components(field_graph)
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700385
Zack Williams045b63d2019-01-22 16:30:57 -0700386
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200387def xproto_api_opts(field):
388 options = []
Zack Williams045b63d2019-01-22 16:30:57 -0700389 if "max_length" in field["options"] and field["type"] == "string":
390 options.append("(val).maxLength = %s" % field["options"]["max_length"])
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700391
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200392 try:
Zack Williams045b63d2019-01-22 16:30:57 -0700393 if field["options"]["null"] == "False":
394 options.append("(val).nonNull = true")
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200395 except KeyError:
396 pass
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700397
Zack Williams045b63d2019-01-22 16:30:57 -0700398 if "link" in field and "model" in field["options"]:
399 options.append('(foreignKey).modelName = "%s"' % field["options"]["model"])
Scott Bakerc4156c32017-12-08 10:58:21 -0800400 if ("options" in field) and ("port" in field["options"]):
Zack Williams045b63d2019-01-22 16:30:57 -0700401 options.append(
402 '(foreignKey).reverseFieldName = "%s"' % field["options"]["port"]
403 )
Sapan Bhatiaf7934b52017-06-12 05:04:23 -0700404
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200405 if options:
Zack Williams045b63d2019-01-22 16:30:57 -0700406 options_str = "[" + ", ".join(options) + "]"
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200407 else:
Zack Williams045b63d2019-01-22 16:30:57 -0700408 options_str = ""
Sapan Bhatiacb35e7f2017-05-24 12:17:28 +0200409
410 return options_str
Matteo Scandolo67654fa2017-06-09 09:33:17 -0700411
Zack Williams045b63d2019-01-22 16:30:57 -0700412
Matteo Scandolo431781c2017-09-06 15:33:07 -0700413def xproto_type_to_swagger_type(f):
414 try:
Zack Williams045b63d2019-01-22 16:30:57 -0700415 content_type = f["options"]["content_type"]
Matteo Scandolo431781c2017-09-06 15:33:07 -0700416 content_type = eval(content_type)
Zack Williams045b63d2019-01-22 16:30:57 -0700417 except BaseException:
Matteo Scandolo431781c2017-09-06 15:33:07 -0700418 content_type = None
419 pass
420
Zack Williams045b63d2019-01-22 16:30:57 -0700421 if "choices" in f["options"]:
422 return "string"
423 elif content_type == "date":
424 return "string"
425 elif f["type"] == "bool":
426 return "boolean"
427 elif f["type"] == "string":
428 return "string"
429 elif f["type"] in ["int", "uint32", "int32"] or "link" in f:
430 return "integer"
431 elif f["type"] in ["double", "float"]:
432 return "string"
433
Matteo Scandolo431781c2017-09-06 15:33:07 -0700434
435def xproto_field_to_swagger_enum(f):
Zack Williams045b63d2019-01-22 16:30:57 -0700436 if "choices" in f["options"]:
Matteo Scandolo431781c2017-09-06 15:33:07 -0700437 list = []
438
Zack Williams045b63d2019-01-22 16:30:57 -0700439 for c in eval(xproto_unquote(f["options"]["choices"])):
Matteo Scandolo431781c2017-09-06 15:33:07 -0700440 list.append(c[0])
441
442 return list
443 else:
Sapan Bhatiabfb233a2018-02-09 14:53:09 -0800444 return False
Scott Bakera33ccb02018-01-26 13:03:28 -0800445
Zack Williams045b63d2019-01-22 16:30:57 -0700446
Scott Bakera33ccb02018-01-26 13:03:28 -0800447def xproto_is_true(x):
448 # TODO: Audit xproto and make specification of trueness more uniform
Zack Williams045b63d2019-01-22 16:30:57 -0700449 if x is True or (x == "True") or (x == '"True"'):
Scott Bakera33ccb02018-01-26 13:03:28 -0800450 return True
451 return False