util: accept PathLike where relevant

Move the PathType type alias from west.manifest to west.util and use
it there to type-annotate individual functions as accepting either str
or an os.PathLike.

Change the source code as needed to make this true. Delete a stale
comment mentioning the long-gone bootstrapper while at it.

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
Martí Bolívar 2020-06-29 16:41:13 -07:00 committed by Marti Bolivar
parent e637e2ac83
commit f6331aba4f
2 changed files with 30 additions and 27 deletions

View File

@ -25,6 +25,7 @@ import pykwalify.core
import yaml
from west import util
from west.util import PathType
import west.configuration as cfg
#
@ -61,13 +62,6 @@ SCHEMA_VERSION = '0.7'
# Type aliases
# What we would eventually like to accept for paths. Using more
# pathlib.Path internally would let us do comparisons with == and
# support better hashing, which will make life easier on Windows.
# However, right now, paths are 'str' in a variety of places, and we
# are using west.util.canon_path(). See #273 on GitHub.
PathType = Union[str, os.PathLike]
# The value of a west-commands as passed around during manifest
# resolution. It can become a list due to resolving imports, even
# though it's just a str in each individual file right now.

View File

@ -9,8 +9,21 @@ import os
import pathlib
import shlex
import textwrap
from typing import List, Optional, Union
def canon_path(path):
# What we would eventually like to accept for paths everywhere in
# west's APIs. However, right now, paths are 'str' in a variety of
# places, and we are using west.util.canon_path() to create strings we
# can compare with ==. See #273 on GitHub.
#
# Here, os.PathLike objects should return str from their __fspath__
# methods, not bytes. We could try to do something like the approach
# taken in https://github.com/python/mypy/issues/5264 to annotate that
# as os.PathLike[str] if TYPE_CHECKING and plain os.PathLike
# otherwise, but it doesn't seem worth it.
PathType = Union[str, os.PathLike]
def canon_path(path: str) -> str:
'''Returns a canonical version of the path.
This is currently ``os.path.normcase(os.path.abspath(path))``. The
@ -22,30 +35,28 @@ def canon_path(path):
'''
return os.path.normcase(os.path.abspath(path))
def escapes_directory(path, directory):
def escapes_directory(path: PathType, directory: PathType) -> bool:
'''Returns True if `path` escapes parent directory `directory`.
:param path: path to check is inside `directory`
:param directory: parent directory to check
Verifies `path` is inside of `directory`, after computing
normalized real paths for both.'''
# It turns out not to be easy to implement this without using
# pathlib.
p = pathlib.Path(os.path.normcase(os.path.realpath(path)))
d = pathlib.Path(os.path.normcase(os.path.realpath(directory)))
Verifies `path` is inside of `directory`, after resolving
both.'''
path_resolved = pathlib.Path(path).resolve()
dir_resolved = pathlib.Path(directory).resolve()
try:
p.relative_to(d)
path_resolved.relative_to(dir_resolved)
ret = False
except ValueError:
ret = True
return ret
def quote_sh_list(cmd):
def quote_sh_list(cmd: List[str]) -> str:
'''Transform a command from list into shell string form.'''
return ' '.join(shlex.quote(s) for s in cmd)
def wrap(text, indent):
def wrap(text: str, indent: str) -> List[str]:
'''Convenience routine for wrapping text to a consistent indent.'''
return textwrap.wrap(text, initial_indent=indent,
subsequent_indent=indent)
@ -53,7 +64,7 @@ def wrap(text, indent):
class WestNotFound(RuntimeError):
'''Neither the current directory nor any parent has a west workspace.'''
def west_dir(start=None):
def west_dir(start: Optional[PathType] = None) -> str:
'''Returns the absolute path of the workspace's .west directory.
Starts the search from the start directory, and goes to its
@ -64,21 +75,19 @@ def west_dir(start=None):
'''
return os.path.join(west_topdir(start), '.west')
def west_topdir(start=None, fall_back=True):
def west_topdir(start: Optional[PathType] = None,
fall_back: bool = True) -> str:
'''
Like west_dir(), but returns the path to the parent directory of the .west/
directory instead, where project repositories are stored
'''
# If you change this function, make sure to update the bootstrap
# script's find_west_topdir().
cur_dir = start or os.getcwd()
cur_dir = pathlib.Path(start or os.getcwd())
while True:
if os.path.isdir(os.path.join(cur_dir, '.west')):
return cur_dir
if (cur_dir / '.west').is_dir():
return os.fspath(cur_dir)
parent_dir = os.path.dirname(cur_dir)
parent_dir = cur_dir.parent
if cur_dir == parent_dir:
# At the root. Should we fall back?
if fall_back and os.environ.get('ZEPHYR_BASE'):