smelly.py 2.1 KB

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