_check_module.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. """
  2. Utility for locating a module (or package's __main__.py) with a given name
  3. and verifying it contains the PYTHON_ARGCOMPLETE_OK marker.
  4. The module name should be specified in a form usable with `python -m`.
  5. Intended to be invoked by argcomplete's global completion function.
  6. """
  7. import os
  8. import sys
  9. import tokenize
  10. try:
  11. from importlib.util import find_spec # type:ignore
  12. except ImportError:
  13. import typing as t
  14. from collections import namedtuple
  15. from imp import find_module # type:ignore
  16. ModuleSpec = namedtuple("ModuleSpec", ["origin", "has_location", "submodule_search_locations"])
  17. def find_spec( # type:ignore
  18. name: str,
  19. package: t.Optional[str] = None,
  20. ) -> t.Optional[ModuleSpec]:
  21. """Minimal implementation as required by `find`."""
  22. try:
  23. f, path, _ = find_module(name)
  24. except ImportError:
  25. return None
  26. has_location = path is not None
  27. if f is None:
  28. return ModuleSpec(None, has_location, [path])
  29. f.close()
  30. return ModuleSpec(path, has_location, None)
  31. class ArgcompleteMarkerNotFound(RuntimeError):
  32. pass
  33. def find(name, return_package=False):
  34. names = name.split(".")
  35. spec = find_spec(names[0])
  36. if spec is None:
  37. raise ArgcompleteMarkerNotFound('no module named "{}"'.format(names[0]))
  38. if not spec.has_location:
  39. raise ArgcompleteMarkerNotFound("cannot locate file")
  40. if spec.submodule_search_locations is None:
  41. if len(names) != 1:
  42. raise ArgcompleteMarkerNotFound("{} is not a package".format(names[0]))
  43. return spec.origin
  44. if len(spec.submodule_search_locations) != 1:
  45. raise ArgcompleteMarkerNotFound("expecting one search location")
  46. path = os.path.join(spec.submodule_search_locations[0], *names[1:])
  47. if os.path.isdir(path):
  48. filename = "__main__.py"
  49. if return_package:
  50. filename = "__init__.py"
  51. return os.path.join(path, filename)
  52. else:
  53. return path + ".py"
  54. def main():
  55. try:
  56. name = sys.argv[1]
  57. except IndexError:
  58. raise ArgcompleteMarkerNotFound("missing argument on the command line")
  59. filename = find(name)
  60. try:
  61. fp = tokenize.open(filename)
  62. except OSError:
  63. raise ArgcompleteMarkerNotFound("cannot open file")
  64. with fp:
  65. head = fp.read(1024)
  66. if "PYTHON_ARGCOMPLETE_OK" not in head:
  67. raise ArgcompleteMarkerNotFound("marker not found")
  68. if __name__ == "__main__":
  69. try:
  70. main()
  71. except ArgcompleteMarkerNotFound as e:
  72. sys.exit(str(e))