mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-21 22:31:48 +01:00
msggen: Add an optional patch
This patch annotates the fields with a new `optional` attribute which determines whether the field should be considered an inferred optional due to being added or deprecated.
This commit is contained in:
parent
5df469cfca
commit
392cacac81
4 changed files with 1219 additions and 25 deletions
1120
.msggen.json
1120
.msggen.json
File diff suppressed because it is too large
Load diff
|
@ -8,6 +8,9 @@ from msggen.gen.rust import RustGenerator
|
|||
from msggen.gen.generator import GeneratorChain
|
||||
from msggen.utils import load_jsonrpc_service
|
||||
import logging
|
||||
from msggen.patch import VersionAnnotationPatch, OptionalPatch
|
||||
from msggen.checks import VersioningCheck
|
||||
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
|
@ -51,7 +54,6 @@ def load_msggen_meta():
|
|||
meta = json.load(open('.msggen.json', 'r'))
|
||||
return meta
|
||||
|
||||
from msggen.patch import VersionAnnotationPatch
|
||||
|
||||
def write_msggen_meta(meta):
|
||||
pid = os.getpid()
|
||||
|
@ -69,6 +71,7 @@ def run(rootdir: Path):
|
|||
|
||||
p = VersionAnnotationPatch(meta=meta)
|
||||
p.apply(service)
|
||||
OptionalPatch().apply(service)
|
||||
|
||||
generator_chain = GeneratorChain()
|
||||
|
||||
|
|
|
@ -27,11 +27,17 @@ class FieldName:
|
|||
|
||||
|
||||
class Field:
|
||||
def __init__(self, path, description):
|
||||
def __init__(
|
||||
self,
|
||||
path,
|
||||
description,
|
||||
added=None,
|
||||
deprecated=None
|
||||
):
|
||||
self.path = path
|
||||
self.description = description
|
||||
self.deprecated = None
|
||||
self.added = None
|
||||
self.added = added
|
||||
self.deprecated = deprecated
|
||||
self.required = False
|
||||
|
||||
@property
|
||||
|
@ -93,8 +99,22 @@ class Method:
|
|||
|
||||
|
||||
class CompositeField(Field):
|
||||
def __init__(self, typename, fields, path, description):
|
||||
Field.__init__(self, path, description)
|
||||
def __init__(
|
||||
self,
|
||||
typename,
|
||||
fields,
|
||||
path,
|
||||
description,
|
||||
added,
|
||||
deprecated
|
||||
):
|
||||
Field.__init__(
|
||||
self,
|
||||
path,
|
||||
description,
|
||||
added=added,
|
||||
deprecated=deprecated
|
||||
)
|
||||
self.typename = typename
|
||||
self.fields = fields
|
||||
|
||||
|
@ -131,6 +151,8 @@ class CompositeField(Field):
|
|||
field = None
|
||||
desc = ftype["description"] if "description" in ftype else ""
|
||||
fpath = f"{path}.{fname}"
|
||||
added = ftype.get('added', None)
|
||||
deprecated = ftype.get('deprecated', None)
|
||||
|
||||
if fpath in overrides:
|
||||
field = copy(overrides[fpath])
|
||||
|
@ -160,7 +182,7 @@ class CompositeField(Field):
|
|||
field = ArrayField.from_js(fpath, ftype)
|
||||
|
||||
elif ftype["type"] in PrimitiveField.types:
|
||||
field = PrimitiveField(ftype["type"], fpath, desc)
|
||||
field = PrimitiveField(ftype["type"], fpath, desc, added=added, deprecated=deprecated)
|
||||
|
||||
else:
|
||||
logger.warning(
|
||||
|
@ -174,7 +196,7 @@ class CompositeField(Field):
|
|||
logger.debug(field)
|
||||
|
||||
return CompositeField(
|
||||
typename, fields, path, js["description"] if "description" in js else ""
|
||||
typename, fields, path, js["description"] if "description" in js else "", added=js.get('added', None), deprecated=js.get('deprecated', None)
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -196,8 +218,8 @@ class EnumVariant(Field):
|
|||
|
||||
|
||||
class EnumField(Field):
|
||||
def __init__(self, typename, values, path, description):
|
||||
Field.__init__(self, path, description)
|
||||
def __init__(self, typename, values, path, description, added, deprecated):
|
||||
Field.__init__(self, path, description, added=added, deprecated=deprecated)
|
||||
self.typename = typename
|
||||
self.values = values
|
||||
self.variants = [EnumVariant(v) for v in self.values]
|
||||
|
@ -211,6 +233,8 @@ class EnumField(Field):
|
|||
values=filter(lambda i: i is not None, js["enum"]),
|
||||
path=path,
|
||||
description=js["description"] if "description" in js else "",
|
||||
added=js.get('added', None),
|
||||
deprecated=js.get('deprecated', None),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -225,8 +249,8 @@ class UnionField(Field):
|
|||
and a `oneof` in protobuf.
|
||||
|
||||
"""
|
||||
def __init__(self, path, description, variants):
|
||||
Field.__init__(self, path, description)
|
||||
def __init__(self, path, description, variants, added, deprecated):
|
||||
Field.__init__(self, path, description, added=added, deprecated=deprecated)
|
||||
self.variants = variants
|
||||
self.typename = path2type(path)
|
||||
|
||||
|
@ -282,8 +306,8 @@ class PrimitiveField(Field):
|
|||
"hash",
|
||||
]
|
||||
|
||||
def __init__(self, typename, path, description):
|
||||
Field.__init__(self, path, description)
|
||||
def __init__(self, typename, path, description, added, deprecated):
|
||||
Field.__init__(self, path, description, added=added, deprecated=deprecated)
|
||||
self.typename = typename
|
||||
|
||||
def __str__(self):
|
||||
|
@ -291,8 +315,8 @@ class PrimitiveField(Field):
|
|||
|
||||
|
||||
class ArrayField(Field):
|
||||
def __init__(self, itemtype, dims, path, description):
|
||||
Field.__init__(self, path, description)
|
||||
def __init__(self, itemtype, dims, path, description, added, deprecated):
|
||||
Field.__init__(self, path, description, added=added, deprecated=deprecated)
|
||||
self.itemtype = itemtype
|
||||
self.dims = dims
|
||||
self.path = path
|
||||
|
@ -322,11 +346,13 @@ class ArrayField(Field):
|
|||
child_js["type"],
|
||||
path,
|
||||
child_js.get("description", ""),
|
||||
added=child_js.get("added", None),
|
||||
deprecated=child_js.get("deprecated", None),
|
||||
)
|
||||
|
||||
logger.debug(f"Array path={path} dims={dims}, type={itemtype}")
|
||||
return ArrayField(
|
||||
itemtype, dims=dims, path=path, description=js.get("description", "")
|
||||
itemtype, dims=dims, path=path, description=js.get("description", ""), added=js.get('added', None), deprecated=js.get('deprecated', None)
|
||||
)
|
||||
|
||||
|
||||
|
@ -340,14 +366,16 @@ class Command:
|
|||
return f"Command[name={self.name}, fields=[{fieldnames}]]"
|
||||
|
||||
|
||||
InvoiceLabelField = PrimitiveField("string", None, None)
|
||||
DatastoreKeyField = ArrayField(itemtype=PrimitiveField("string", None, None), dims=1, path=None, description=None)
|
||||
InvoiceExposeprivatechannelsField = PrimitiveField("boolean", None, None)
|
||||
PayExclude = ArrayField(itemtype=PrimitiveField("string", None, None), dims=1, path=None, description=None)
|
||||
InvoiceLabelField = PrimitiveField("string", None, None, added=None, deprecated=None)
|
||||
DatastoreKeyField = ArrayField(itemtype=PrimitiveField("string", None, None, added=None, deprecated=None), dims=1, path=None, description=None, added=None, deprecated=None)
|
||||
InvoiceExposeprivatechannelsField = PrimitiveField("boolean", None, None, added=None, deprecated=None)
|
||||
PayExclude = ArrayField(itemtype=PrimitiveField("string", None, None, added=None, deprecated=None), dims=1, path=None, description=None, added=None, deprecated=None)
|
||||
RoutehintListField = PrimitiveField(
|
||||
"RoutehintList",
|
||||
None,
|
||||
None
|
||||
None,
|
||||
added=None,
|
||||
deprecated=None
|
||||
)
|
||||
|
||||
# TlvStreams are special, they don't have preset dict-keys, rather
|
||||
|
@ -356,7 +384,9 @@ RoutehintListField = PrimitiveField(
|
|||
TlvStreamField = PrimitiveField(
|
||||
"TlvStream",
|
||||
None,
|
||||
None
|
||||
None,
|
||||
added=None,
|
||||
deprecated=None
|
||||
)
|
||||
|
||||
# Override fields with manually managed types, fieldpath -> field mapping
|
||||
|
|
|
@ -82,3 +82,48 @@ class VersionAnnotationPatch(Patch):
|
|||
'deprecated': f.deprecated,
|
||||
}
|
||||
|
||||
|
||||
class OptionalPatch(Patch):
|
||||
"""Annotates fields with `.optional`
|
||||
|
||||
Optional fields are either non-required fields, or fields that
|
||||
were not required in prior versions. This latter case covers the
|
||||
deprecation and addition for schema evolution
|
||||
"""
|
||||
|
||||
versions = [
|
||||
'pre-v0.10.1', # Dummy versions collecting all fields that predate the versioning.
|
||||
'v0.10.1',
|
||||
'v0.10.2',
|
||||
'v0.11.0',
|
||||
'v0.12.0',
|
||||
'v0.12.1',
|
||||
'v22.11',
|
||||
'v23.02',
|
||||
'v23.05',
|
||||
]
|
||||
# Oldest supported versions. Bump this if you no longer want to
|
||||
# support older versions, and you want to make required fields
|
||||
# more stringent.
|
||||
supported = 'v0.12.0'
|
||||
|
||||
def visit(self, f: model.Field) -> None:
|
||||
if f.added not in self.versions:
|
||||
raise ValueError(f"Version {f.added} in unknown, please add it to {__file__}")
|
||||
if f.deprecated and f.deprecated not in self.versions:
|
||||
raise ValueError(f"Version {f.deprecated} in unknown, please add it to {__file__}")
|
||||
|
||||
idx = (
|
||||
self.versions.index(self.supported),
|
||||
len(self.versions) - 1,
|
||||
)
|
||||
# Default to false, and then overwrite it if required.
|
||||
f.optional = False
|
||||
if not f.required:
|
||||
f.optional = True
|
||||
|
||||
if self.versions.index(f.added) > idx[0]:
|
||||
f.optional = True
|
||||
|
||||
if f.deprecated and self.versions.index(f.deprecated) < idx[1]:
|
||||
f.optional = True
|
||||
|
|
Loading…
Add table
Reference in a new issue