The latest versions of protobuf do not reveal empty message fields with dir(). This uses the documented way of determining all of a message's fields and so is compatible with past and future versions of protobuf.
# get all parameter names (typically underscore case) and corresponding
# type names (typically camel case), which contain the layer names
# (note that not all parameters correspond to layers, but we'll ignore that)
- param_names = [s for s in dir(layer) if s.endswith('_param')]
+ param_names = [f.name for f in layer.DESCRIPTOR.fields if f.name.endswith('_param')]
param_type_names = [type(getattr(layer, s)).__name__ for s in param_names]
# strip the final '_param' or 'Parameter'
param_names = [s[:-len('_param')] for s in param_names]