• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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