Skip to content

Commit

Permalink
Merge pull request #49 from rdkcentral/development/alt-in-notifications
Browse files Browse the repository at this point in the history
[JsonGen] Support alt in notifications
  • Loading branch information
pwielders authored Oct 24, 2023
2 parents d3a017f + 0e628b3 commit 91eaf16
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 80 deletions.
98 changes: 80 additions & 18 deletions JsonGenerator/source/documentation_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ def bold(string):
def italics(string):
return "*%s*" % string

def link(string):
return "[%s](#%s)" % (string.split(".", 1)[1].replace("_", " "), string)
def link(string, caption=None):
return "[%s](#%s)" % (caption if caption else string.split(".", 1)[1].replace("_", " "), string)

def weblink(string, link):
return "[%s](%s)" % (string,link)
Expand Down Expand Up @@ -204,12 +204,30 @@ def ExampleObj(name, obj, root=False):

return json_data

def MethodDump(method, props, classname, section, is_notification=False, is_property=False, include=None):
def MethodDump(method, props, classname, section, header, is_notification=False, is_property=False, include=None):
method = (method.rsplit(".", 1)[1] if "." in method else method)
type = "property" if is_property else "event" if is_notification else "method"

log.Info("Emitting documentation for %s '%s'..." % (type, method))

element = ["property", "properties"] if is_property else ["method", "methods"]
sen1 = " This %s is **deprecated** and may be removed in the future. It is not recommended for use in new implementations."
sen2 = " This %s is **obsolete**. It is not recommended for use in new implementations."
main_status = sen2 if props.get("obsolete") else sen1 if props.get("deprecated") else ""
alt_status = ""
is_alt = "alt" in props

if is_notification:
element = ["notification", "events"]
if is_alt:
alt_status = sen2 if props.get("altisobsolete") else sen1 if props.get("altisdeprecated") else ""
else:
if is_alt:
alt_status = sen2 if interface[element[1]][props["alt"]].get("obsolete") else sen1 if interface[element[1]][props["alt"]].get("deprecated") else ""

orig_method = method
method = props["alt"] if main_status and not alt_status else method

MdHeader(method, 2, type, section)

readonly = False
Expand All @@ -228,17 +246,26 @@ def MethodDump(method, props, classname, section, is_notification=False, is_prop
MdParagraph(text)

if is_property:
if "readonly" in props and props["readonly"] == True:
if "readonly" in props and props["readonly"]:
MdParagraph("> This property is **read-only**.")
readonly = True
elif "writeonly" in props and props["writeonly"] == True:
elif "writeonly" in props and props["writeonly"]:
writeonly = True
MdParagraph("> This property is **write-only**.")

if "deprecated" in props:
MdParagraph("> This API is **deprecated** and may be removed in the future. It is no longer recommended for use in new implementations.")
elif "obsolete" in props:
MdParagraph("> This API is **obsolete**. It is no longer recommended for use in new implementations.")
if alt_status == main_status:
if main_status:
MdParagraph("> %s" % (main_status % element[0]))
if is_alt:
MdParagraph("> ``%s`` is an alternative name for this %s." % (props.get("alt"), element[0]))
else:
if main_status and not alt_status:
MdParagraph("> ``%s`` is an alternative name for this %s.%s" % (orig_method, element[0], main_status % "name"))
elif alt_status and not main_status:
MdParagraph("> ``%s`` is an alternative name for this %s.%s" % (props.get("alt"), element[0], alt_status % "name"))
else:
MdParagraph("> %s." % main_status % element[0])
MdParagraph("> ``%s`` is an alternative name for this %s.%s" % (props.get("alt"), element[0], alt_status % "name"))

if "description" in props:
MdHeader("Description", 3)
Expand Down Expand Up @@ -268,7 +295,7 @@ def MethodDump(method, props, classname, section, is_notification=False, is_prop
if "name" not in props["index"] or "example" not in props["index"]:
raise DocumentationError("'%s': index field requires 'name' and 'example' properties" % method)

extra_paragraph = "> The *%s* argument shall be passed as the index to the property, e.g. *%s.1.%s@%s*.%s" % (
extra_paragraph = "> The *%s* argument shall be passed as the index to the property, e.g. ``%s.1.%s@%s``.%s" % (
props["index"]["name"].lower(), classname, method, props["index"]["example"],
(" " + props["index"]["description"]) if "description" in props["index"] else "")

Expand All @@ -283,7 +310,7 @@ def MethodDump(method, props, classname, section, is_notification=False, is_prop
ParamTable("params", props["params"])
else:
if is_notification:
MdParagraph("This event carries no parameters.")
MdParagraph("This notification carries no parameters.")
else:
MdParagraph("This method takes no parameters.")

Expand Down Expand Up @@ -355,6 +382,8 @@ def MethodDump(method, props, classname, section, is_notification=False, is_prop
object_pairs_hook=OrderedDict), indent=4)
MdCode(jsonResponse, "json")

return props["alt"] if is_alt and not is_notification else ""

commons = dict()
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), config.GLOBAL_DEFINITIONS)) as f:
commons = json.load(f)
Expand Down Expand Up @@ -700,6 +729,7 @@ def SectionDump(section_name, section, header, description=None, description2=No
def InterfaceDump(interface, section, header):
head = False
emitted = False

if section in interface:
for method, contents in interface[section].items():
if contents and method not in skip_list:
Expand All @@ -713,20 +743,20 @@ def InterfaceDump(interface, section, header):
access = ""

if "readonly" in contents and contents["readonly"] == True:
access = "RO"
access = "read-only"
elif "writeonly" in contents and contents["writeonly"] == True:
access = "WO"
access = "write-only"

if access:
access = " <sup>%s</sup>" % access
access = " (%s)" % access

tags = ""

if "obsolete" in contents and contents["obsolete"]:
tags += "<sup>obsolete</sup> "
tags += " <sup>obsolete</sup> "

if "deprecated" in contents and contents["deprecated"]:
tags += "<sup>deprecated</sup> "
tags += " <sup>deprecated</sup> "

descr = ""

Expand All @@ -738,9 +768,38 @@ def InterfaceDump(interface, section, header):

if "i.e" in descr:
descr = descr[0:descr.index("i.e") - 1]

descr = descr.split(".", 1)[0] if "." in descr else descr

MdRow([tags + link(header + "." + (method.rsplit(".", 1)[1] if "." in method else method)) + access, descr])
ln = header + "." + (method.rsplit(".", 1)[1] if "." in method else method)
line = link(ln) + tags

if "alt" in contents and contents["alt"]:
alt_tags = ""

if not event:
if interface[section][contents["alt"]].get("obsolete"):
alt_tags += " <sup>obsolete</sup> "

if interface[section][contents["alt"]].get("deprecated"):
alt_tags += " <sup>deprecated</sup> "
else:
if contents.get("altisobsolete"):
alt_tags += " <sup>obsolete</sup> "
if contents.get("altisdeprecated"):
alt_tags += " <sup>deprecated</sup> "

if not alt_tags and tags:
line = link(header + "." + contents["alt"]) + alt_tags
line += " / " + link(header + "." + contents["alt"], method.rsplit(".", 1)[1] if "." in method else method) + tags
else:
line += " / " + link(ln, contents["alt"]) + alt_tags

skip_list.append(contents["alt"])

line += access

MdRow([line, descr])
emitted = True

skip_list.append(method)
Expand Down Expand Up @@ -770,7 +829,10 @@ def InterfaceDump(interface, section, header):
if section in interface:
for method, props in interface[section].items():
if props and method not in skip_list:
MethodDump(method, props, plugin_class, section_name, event, prop)
to_skip = MethodDump(method, props, plugin_class, section_name, header, event, prop)

if to_skip:
skip_list.append(to_skip)

skip_list.append(method)

Expand Down
87 changes: 73 additions & 14 deletions JsonGenerator/source/header_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,14 +423,38 @@ def BuildResult(vars, is_property=False):
else:
prefix = ""

method_name = method.name
method_name_lower = method_name.lower()

if method.retval.meta.text == method_name_lower:
log.WarnLine(method, "%s': changed function name is same as original name ('%s')" % (method.name, method.retval.meta.text))

# Copy over @text tag to the other method of a property
if method.retval.meta.text and method.retval.meta.is_property:
for mm in face.obj.methods:
if mm != method and mm.name == method.name:
mm.retval.meta.text = method.retval.meta.text
break

method_name = method.retval.meta.text if method.retval.meta.text else method.name
method_name_lower = method_name.lower()

if method.retval.meta.alt == method_name_lower:
log.WarnLine(method, "%s': alternative name is same as original name" % method.name)
if method.parent.is_json:
if method.retval.meta.alt == method_name_lower:
log.WarnLine(method, "%s': alternative name is same as original name ('%s')" % (method.name, method.retval.meta.text))

if method.retval.meta.text == method_name_lower:
log.WarnLine(method, "%s': changed function name is same as original name" % method.name)
for mm in methods:
if mm == prefix + method_name_lower:
raise CppParseError(method, "JSON-RPC name clash detected ('%s')" % (prefix + method_name_lower))
if method.retval.meta.alt and (mm == prefix + method.retval.meta.alt):
raise CppParseError(method, "JSON-RPC name clash detected ('%s' alternative)" % (prefix + method_name_lower))

for mm in properties:
if properties[mm]["@originalname"] != method.name:
if mm == prefix + method_name_lower:
raise CppParseError(method, "JSON-RPC name clash detected ('%s')x" % (prefix + method_name_lower))
if method.retval.meta.alt and (mm == prefix + method.retval.meta.alt):
raise CppParseError(method, "JSON-RPC name clash detected ('%s' alternative)" % (prefix + method_name_lower))

for e in event_params:
exists = any(x.obj.type == e.type.type for x in event_interfaces)
Expand All @@ -439,15 +463,18 @@ def BuildResult(vars, is_property=False):
event_interfaces.add(CppInterface.Interface(ResolveTypedef(e).type, 0, file))

obj = None
property_second_method = False

if method.retval.meta.is_property or (prefix + method_name_lower) in properties:
try:
obj = properties[prefix + method_name_lower]
property_second_method = True
except:
obj = OrderedDict()
properties[prefix + method_name_lower] = obj

obj["@originalname"] = method.name
method.retval.meta.is_property = True

indexed_property = (len(method.vars) == 2 and method.vars[0].meta.is_index)

Expand Down Expand Up @@ -552,11 +579,6 @@ def BuildResult(vars, is_property=False):

if obj["params"] == None or obj["params"]["type"] == "null":
raise CppParseError(method.vars[value], "property setter method must have one input parameter")

if method.retval.meta.alt:
properties[prefix + method.retval.meta.alt] = copy.deepcopy(obj)
properties[prefix + method.retval.meta.alt]["deprecated"] = True

else:
raise CppParseError(method, "property method must have one parameter")

Expand All @@ -579,9 +601,6 @@ def BuildResult(vars, is_property=False):
obj["result"] = BuildResult(method.vars)
methods[prefix + method_name_lower] = obj

if method.retval.meta.alt:
methods[prefix + method.retval.meta.alt] = copy.deepcopy(obj)
methods[prefix + method.retval.meta.alt]["deprecated"] = True
else:
raise CppParseError(method, "method return type must be uint32_t (error code), i.e. pass other return values by a reference")

Expand Down Expand Up @@ -609,6 +628,32 @@ def BuildResult(vars, is_property=False):
if errors:
obj["errors"] = errors

upd = properties if method.retval.meta.is_property else methods

if method.retval.meta.alt:
idx = prefix + method.retval.meta.alt
upd[idx] = copy.deepcopy(obj)
upd[idx]["alt"] = prefix + method_name_lower
obj["alt"] = idx

if "deprecated" in upd[idx]:
del upd[idx]["deprecated"]
if "obsolete" in upd[idx]:
del upd[idx]["obsolete"]

if method.retval.meta.alt_is_deprecated:
upd[idx]["deprecated"] = True
elif method.retval.meta.alt_is_obsolete:
upd[idx]["obsolete"] = True

elif "alt" in obj:
o = upd[obj["alt"]]
if "readonly" in o and "readonly" not in obj:
del o["readonly"]
if "writeonly" in o and "writeonly" not in obj:
del o["writeonly"]


for f in event_interfaces:
rpc_format = _EvaluateRpcFormat(f.obj)

Expand Down Expand Up @@ -669,8 +714,22 @@ def BuildResult(vars, is_property=False):
if params:
obj["params"] = params

method_name = method.retval.meta.text if method.retval.meta.text else method.name
events[prefix + method_name.lower()] = obj
method_name = (method.retval.meta.text if method.retval.meta.text else method.name).lower()

for mm in events:
if mm == prefix + method_name:
raise CppParseError(method, "JSON-RPC name clash detected ('%s')" % (prefix + method_name))
if method.retval.meta.alt and (mm == prefix + method.retval.meta.alt):
raise CppParseError(method, "JSON-RPC name clash detected ('%s' alternative)" % (prefix + method_name))
if events[mm].get("alt") == method_name:
raise CppParseError(method, "JSON-RPC name clash detected ('%s' with '%s' alternative)" % ((prefix + method_name), mm))

if method.retval.meta.alt:
obj["alt"] = method.retval.meta.alt
obj["altisdeprecated"] = method.retval.meta.alt_is_deprecated
obj["altisobsolete"] = method.retval.meta.alt_is_obsolete

events[prefix + method_name] = obj

if methods:
schema["methods"] = methods
Expand Down
11 changes: 11 additions & 0 deletions JsonGenerator/source/json_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,7 @@ def __init__(self, name, parent, schema, included=None, property=False):

JsonObject.__init__(self, name, parent, method_schema, included=included)

self.alternative = None
self.summary = schema.get("summary")
self.deprecated = schema.get("deprecated")
self.obsolete = schema.get("obsolete")
Expand Down Expand Up @@ -744,6 +745,16 @@ def __init__(self, name, parent, schema, included=None):
self.sendif_type = JsonItem("id", self, schema["id"]) if "id" in schema else None
self.is_status_listener = schema.get("statuslistener")

if "alt" in schema:
self.alternative = schema.get("alt")

if not self.alternative.islower():
log.Warn("'%s' (alternative): mixedCase identifiers are supported, however all-lowercase names are recommended" % self.alternative)
elif "_" in self.alternative:
log.Warn("'%s' (alternative): snake_case identifiers are supported, however flatcase names are recommended" % self.alternative)
else:
self.alternative = None

self.endpoint_name = (config.IMPL_EVENT_PREFIX + self.json_name)

for param in self.params.properties:
Expand Down
Loading

0 comments on commit 91eaf16

Please sign in to comment.