| #!/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() |