also check class methods

This commit is contained in:
Mike McLean 2024-04-01 08:50:37 -04:00 committed by Tomas Kopecek
parent 7f98bceb6b
commit db4c6de1b7

View file

@ -78,8 +78,14 @@ def read_api():
if '__future__' in _type: if '__future__' in _type:
continue continue
vinfo['type'] = str(type(value)) vinfo['type'] = str(type(value))
if isinstance(value, types.FunctionType): if inspect.isclass(value):
vinfo['is_class'] = True
vinfo.update(dump_class(value))
elif inspect.isfunction(value):
vinfo['is_function'] = True
vinfo.update(dump_func(value)) vinfo.update(dump_func(value))
elif inspect.ismodule(value):
vinfo['is_module'] = True
info[name] = vinfo info[name] = vinfo
# hub rpc calls (no plugins) # hub rpc calls (no plugins)
@ -94,6 +100,10 @@ def read_api():
def dump_func(func): def dump_func(func):
info = OrderedDict() info = OrderedDict()
if inspect.isbuiltin(func):
info['is_builtin'] = True
if inspect.isgeneratorfunction(func):
info['is_generator_function'] = True
sig = inspect.signature(func) sig = inspect.signature(func)
info['desc'] = '(%s)' % ', '.join([str(x) for x in sig.parameters.values()]) info['desc'] = '(%s)' % ', '.join([str(x) for x in sig.parameters.values()])
args = [] args = []
@ -116,6 +126,22 @@ def dump_func(func):
return info return info
def dump_class(cls):
members = OrderedDict()
names = [n for n in vars(cls) if not n.startswith('_')]
names.sort()
for name in names:
value = getattr(cls, name)
vinfo = OrderedDict()
_type = str(type(value))
vinfo['type'] = str(type(value))
if inspect.isfunction(value):
vinfo['is_function'] = True
vinfo.update(dump_func(value))
members[name] = vinfo
return {'members': members}
def compare(old, new): def compare(old, new):
top_keys = {'version', 'lib', 'rpc'} top_keys = {'version', 'lib', 'rpc'}
if set(old) != top_keys: if set(old) != top_keys:
@ -178,7 +204,35 @@ def compare_mod_global(mod, name, old, new):
# this prevents further comparison # this prevents further comparison
return return
desc = f'{mod}.{name}' desc = f'{mod}.{name}'
if 'args' in old: if old.get('is_function'):
compare_function(desc, old, new)
elif old.get('is_class'):
compare_class(desc, old, new)
def compare_class(cls, old, new):
names1 = set(old['members'])
names2 = set(new['members'])
added = names2 - names1
dropped = names1 - names2
both = names1.intersection(names2)
for name in sorted(added):
warn(f'Added class variable: {cls}.{name}')
for name in sorted(dropped):
# TODO figure out a way to distinguish deprecations
error(f'Dropped class variable: {cls}.{name}')
for name in sorted(both):
compare_class_var(cls, name, old['members'][name], new['members'][name])
def compare_class_var(cls, name, old, new):
if old['type'] != new['type']:
error(f'{cls}.{name} changed type: {old["type"]} -> {new["type"]}')
# this prevents further comparison
return
desc = f'{cls}.{name}'
if old.get('is_function'):
compare_function(desc, old, new) compare_function(desc, old, new)