def safe_exists(path: Path) -> bool: # This can throw on paths that contain characters unrepresentable at the OS level, # or with invalid syntax on Windows (https://bugs.python.org/issue35306) try: return path.exists() except OSError: return False
def resolve_collection_argument( invocation_dir: py.path.local, arg: str, *, as_pypath: bool = False) -> Tuple[py.path.local, List[str]]: """Parse path arguments optionally containing selection parts and return (fspath, names). Command-line arguments can point to files and/or directories, and optionally contain parts for specific tests selection, for example: "pkg/tests/test_foo.py::TestClass::test_foo" This function ensures the path exists, and returns a tuple: (py.path.path("/full/path/to/pkg/tests/test_foo.py"), ["TestClass", "test_foo"]) When as_pypath is True, expects that the command-line argument actually contains module paths instead of file-system paths: "pkg.tests.test_foo::TestClass::test_foo" In which case we search sys.path for a matching module, and then return the *path* to the found module. If the path doesn't exist, raise UsageError. If the path is a directory and selection parts are present, raise UsageError. """ strpath, *parts = str(arg).split("::") if as_pypath: strpath = search_pypath(strpath) fspath = Path(str(invocation_dir), strpath) fspath = absolutepath(fspath) if not fspath.exists(): msg = ("module or package not found: {arg} (missing __init__.py?)" if as_pypath else "file or directory not found: {arg}") raise UsageError(msg.format(arg=arg)) if parts and fspath.is_dir(): msg = ("package argument cannot contain :: selection parts: {arg}" if as_pypath else "directory argument cannot contain :: selection parts: {arg}") raise UsageError(msg.format(arg=arg)) return py.path.local(str(fspath)), parts