findpaths.py 4.9 KB

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