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:
parent
e637e2ac83
commit
f6331aba4f
|
@ -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.
|
||||
|
|
|
@ -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'):
|
||||
|
|
Loading…
Reference in New Issue