blob: 43d091654d2dc84e499e1950cc3a0303879d4d33 [file] [log] [blame]
#!/usr/bin/env python
# Script checking that all symbols exported by libpython start with Py or _Py
import subprocess
import sys
import sysconfig
def get_exported_symbols():
LIBRARY = sysconfig.get_config_var('LIBRARY')
if not LIBRARY:
raise Exception("failed to get LIBRARY")
args = ('nm', '-p', LIBRARY)
print("+ %s" % ' '.join(args))
proc = subprocess.run(args, stdout=subprocess.PIPE, universal_newlines=True)
if proc.returncode:
sys.stdout.write(proc.stdout)
sys.exit(proc.returncode)
stdout = proc.stdout.rstrip()
if not stdout:
raise Exception("command output is empty")
return stdout
def get_smelly_symbols(stdout):
symbols = []
ignored_symtypes = set()
allowed_prefixes = ('Py', '_Py')
if sys.platform == 'darwin':
allowed_prefixes += ('__Py',)
for line in stdout.splitlines():
# Split line '0000000000001b80 D PyTextIOWrapper_Type'
if not line:
continue
parts = line.split(maxsplit=2)
if len(parts) < 3:
continue
symtype = parts[1].strip()
# Ignore private symbols.
#
# If lowercase, the symbol is usually local; if uppercase, the symbol
# is global (external). There are however a few lowercase symbols that
# are shown for special global symbols ("u", "v" and "w").
if symtype.islower() and symtype not in "uvw":
ignored_symtypes.add(symtype)
continue
symbol = parts[-1]
if symbol.startswith(allowed_prefixes):
continue
symbol = '%s (type: %s)' % (symbol, symtype)
symbols.append(symbol)
if ignored_symtypes:
print("Ignored symbol types: %s" % ', '.join(sorted(ignored_symtypes)))
print()
return symbols
def main():
nm_output = get_exported_symbols()
symbols = get_smelly_symbols(nm_output)
if not symbols:
print("OK: no smelly symbol found")
sys.exit(0)
symbols.sort()
for symbol in symbols:
print("Smelly symbol: %s" % symbol)
print()
print("ERROR: Found %s smelly symbols!" % len(symbols))
sys.exit(1)
if __name__ == "__main__":
main()