1#!/usr/bin/env python 2# Script checking that all symbols exported by libpython start with Py or _Py 3 4import subprocess 5import sys 6import sysconfig 7 8 9def get_exported_symbols(): 10 LIBRARY = sysconfig.get_config_var('LIBRARY') 11 if not LIBRARY: 12 raise Exception("failed to get LIBRARY") 13 14 args = ('nm', '-p', LIBRARY) 15 print("+ %s" % ' '.join(args)) 16 proc = subprocess.run(args, stdout=subprocess.PIPE, universal_newlines=True) 17 if proc.returncode: 18 sys.stdout.write(proc.stdout) 19 sys.exit(proc.returncode) 20 21 stdout = proc.stdout.rstrip() 22 if not stdout: 23 raise Exception("command output is empty") 24 return stdout 25 26 27def get_smelly_symbols(stdout): 28 symbols = [] 29 ignored_symtypes = set() 30 31 allowed_prefixes = ('Py', '_Py') 32 if sys.platform == 'darwin': 33 allowed_prefixes += ('__Py',) 34 35 for line in stdout.splitlines(): 36 # Split line '0000000000001b80 D PyTextIOWrapper_Type' 37 if not line: 38 continue 39 40 parts = line.split(maxsplit=2) 41 if len(parts) < 3: 42 continue 43 44 symtype = parts[1].strip() 45 # Ignore private symbols. 46 # 47 # If lowercase, the symbol is usually local; if uppercase, the symbol 48 # is global (external). There are however a few lowercase symbols that 49 # are shown for special global symbols ("u", "v" and "w"). 50 if symtype.islower() and symtype not in "uvw": 51 ignored_symtypes.add(symtype) 52 continue 53 54 symbol = parts[-1] 55 if symbol.startswith(allowed_prefixes): 56 continue 57 symbol = '%s (type: %s)' % (symbol, symtype) 58 symbols.append(symbol) 59 60 if ignored_symtypes: 61 print("Ignored symbol types: %s" % ', '.join(sorted(ignored_symtypes))) 62 print() 63 return symbols 64 65 66def main(): 67 nm_output = get_exported_symbols() 68 symbols = get_smelly_symbols(nm_output) 69 70 if not symbols: 71 print("OK: no smelly symbol found") 72 sys.exit(0) 73 74 symbols.sort() 75 for symbol in symbols: 76 print("Smelly symbol: %s" % symbol) 77 print() 78 print("ERROR: Found %s smelly symbols!" % len(symbols)) 79 sys.exit(1) 80 81 82if __name__ == "__main__": 83 main() 84