Victor Stinner | 87d332d | 2017-10-24 01:29:53 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Script checking that all symbols exported by libpython start with Py or _Py |
| 3 | |
| 4 | import subprocess |
| 5 | import sys |
| 6 | import sysconfig |
| 7 | |
| 8 | |
| 9 | def 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 | |
| 27 | def get_smelly_symbols(stdout): |
| 28 | symbols = [] |
| 29 | ignored_symtypes = set() |
Antoine Pitrou | d7687eb | 2018-02-27 21:40:37 +0100 | [diff] [blame] | 30 | |
| 31 | allowed_prefixes = ('Py', '_Py') |
| 32 | if sys.platform == 'darwin': |
| 33 | allowed_prefixes += ('__Py',) |
| 34 | |
Victor Stinner | 87d332d | 2017-10-24 01:29:53 -0700 | [diff] [blame] | 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] |
Antoine Pitrou | d7687eb | 2018-02-27 21:40:37 +0100 | [diff] [blame] | 55 | if symbol.startswith(allowed_prefixes): |
Victor Stinner | 87d332d | 2017-10-24 01:29:53 -0700 | [diff] [blame] | 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 | |
| 66 | def 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 | |
| 82 | if __name__ == "__main__": |
| 83 | main() |