findpaths.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. # -*- coding: utf-8 -*-
  2. import os
  3. import py
  4. from .exceptions import UsageError
  5. from _pytest.outcomes import fail
  6. def exists(path, ignore=EnvironmentError):
  7. try:
  8. return path.check()
  9. except ignore:
  10. return False
  11. def getcfg(args, config=None):
  12. """
  13. Search the list of arguments for a valid ini-file for pytest,
  14. and return a tuple of (rootdir, inifile, cfg-dict).
  15. note: config is optional and used only to issue warnings explicitly (#2891).
  16. """
  17. from _pytest.deprecated import CFG_PYTEST_SECTION
  18. inibasenames = ["pytest.ini", "tox.ini", "setup.cfg"]
  19. args = [x for x in args if not str(x).startswith("-")]
  20. if not args:
  21. args = [py.path.local()]
  22. for arg in args:
  23. arg = py.path.local(arg)
  24. for base in arg.parts(reverse=True):
  25. for inibasename in inibasenames:
  26. p = base.join(inibasename)
  27. if exists(p):
  28. try:
  29. iniconfig = py.iniconfig.IniConfig(p)
  30. except py.iniconfig.ParseError as exc:
  31. raise UsageError(str(exc))
  32. if (
  33. inibasename == "setup.cfg"
  34. and "tool:pytest" in iniconfig.sections
  35. ):
  36. return base, p, iniconfig["tool:pytest"]
  37. elif "pytest" in iniconfig.sections:
  38. if inibasename == "setup.cfg" and config is not None:
  39. fail(
  40. CFG_PYTEST_SECTION.format(filename=inibasename),
  41. pytrace=False,
  42. )
  43. return base, p, iniconfig["pytest"]
  44. elif inibasename == "pytest.ini":
  45. # allowed to be empty
  46. return base, p, {}
  47. return None, None, None
  48. def get_common_ancestor(paths):
  49. common_ancestor = None
  50. for path in paths:
  51. if not path.exists():
  52. continue
  53. if common_ancestor is None:
  54. common_ancestor = path
  55. else:
  56. if path.relto(common_ancestor) or path == common_ancestor:
  57. continue
  58. elif common_ancestor.relto(path):
  59. common_ancestor = path
  60. else:
  61. shared = path.common(common_ancestor)
  62. if shared is not None:
  63. common_ancestor = shared
  64. if common_ancestor is None:
  65. common_ancestor = py.path.local()
  66. elif common_ancestor.isfile():
  67. common_ancestor = common_ancestor.dirpath()
  68. return common_ancestor
  69. def get_dirs_from_args(args):
  70. def is_option(x):
  71. return str(x).startswith("-")
  72. def get_file_part_from_node_id(x):
  73. return str(x).split("::")[0]
  74. def get_dir_from_path(path):
  75. if path.isdir():
  76. return path
  77. return py.path.local(path.dirname)
  78. # These look like paths but may not exist
  79. possible_paths = (
  80. py.path.local(get_file_part_from_node_id(arg))
  81. for arg in args
  82. if not is_option(arg)
  83. )
  84. return [get_dir_from_path(path) for path in possible_paths if path.exists()]
  85. def determine_setup(inifile, args, rootdir_cmd_arg=None, config=None):
  86. dirs = get_dirs_from_args(args)
  87. if inifile:
  88. iniconfig = py.iniconfig.IniConfig(inifile)
  89. is_cfg_file = str(inifile).endswith(".cfg")
  90. sections = ["tool:pytest", "pytest"] if is_cfg_file else ["pytest"]
  91. for section in sections:
  92. try:
  93. inicfg = iniconfig[section]
  94. if is_cfg_file and section == "pytest" and config is not None:
  95. from _pytest.deprecated import CFG_PYTEST_SECTION
  96. fail(
  97. CFG_PYTEST_SECTION.format(filename=str(inifile)), pytrace=False
  98. )
  99. break
  100. except KeyError:
  101. inicfg = None
  102. if rootdir_cmd_arg is None:
  103. rootdir = get_common_ancestor(dirs)
  104. else:
  105. ancestor = get_common_ancestor(dirs)
  106. rootdir, inifile, inicfg = getcfg([ancestor], config=config)
  107. if rootdir is None and rootdir_cmd_arg is None:
  108. for possible_rootdir in ancestor.parts(reverse=True):
  109. if possible_rootdir.join("setup.py").exists():
  110. rootdir = possible_rootdir
  111. break
  112. else:
  113. if dirs != [ancestor]:
  114. rootdir, inifile, inicfg = getcfg(dirs, config=config)
  115. if rootdir is None:
  116. if config is not None:
  117. cwd = config.invocation_dir
  118. else:
  119. cwd = py.path.local()
  120. rootdir = get_common_ancestor([cwd, ancestor])
  121. is_fs_root = os.path.splitdrive(str(rootdir))[1] == "/"
  122. if is_fs_root:
  123. rootdir = ancestor
  124. if rootdir_cmd_arg:
  125. rootdir = py.path.local(os.path.expandvars(rootdir_cmd_arg))
  126. if not rootdir.isdir():
  127. raise UsageError(
  128. "Directory '{}' not found. Check your '--rootdir' option.".format(
  129. rootdir
  130. )
  131. )
  132. return rootdir, inifile, inicfg or {}