summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-cli/src/meshbay_cli/client/upload_plan.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-cli/src/meshbay_cli/client/upload_plan.py')
-rw-r--r--packages/meshbay-cli/src/meshbay_cli/client/upload_plan.py33
1 files changed, 28 insertions, 5 deletions
diff --git a/packages/meshbay-cli/src/meshbay_cli/client/upload_plan.py b/packages/meshbay-cli/src/meshbay_cli/client/upload_plan.py
index 070e686..b4ec781 100644
--- a/packages/meshbay-cli/src/meshbay_cli/client/upload_plan.py
+++ b/packages/meshbay-cli/src/meshbay_cli/client/upload_plan.py
@@ -12,7 +12,7 @@ import stat
from dataclasses import dataclass
from pathlib import Path
-from meshbay_common.paths import fold
+from meshbay_common.paths import fold, long_path
from ..errors import USAGE, CliError
from ..output import safe
@@ -36,7 +36,7 @@ def plan_upload(sources: list[str], recursive: bool):
for src in sources:
path = Path(src).expanduser()
try:
- st = path.stat()
+ st = os.stat(long_path(path))
except OSError as e:
problems.append(f"{src}: {e.strerror}")
continue
@@ -51,18 +51,18 @@ def plan_upload(sources: list[str], recursive: bool):
elif stat.S_ISDIR(st.st_mode):
if not recursive:
raise CliError(USAGE, f"{safe(src)} is a folder; use -R.")
- for here, subdirs, names in os.walk(path):
+ for here, subdirs, names in _walk(path):
rel = [top] + list(Path(here).relative_to(path).parts)
dirs.append(rel)
for d in list(subdirs):
- if (Path(here) / d).is_symlink():
+ if os.path.islink(long_path(Path(here) / d)):
subdirs.remove(d)
problems.append(f"{Path(here) / d} is a link to a folder; not "
"followed")
for n in names:
p = Path(here) / n
try:
- st_n = p.stat()
+ st_n = os.stat(long_path(p))
except OSError as e:
problems.append(f"{p}: {e.strerror}")
continue
@@ -74,6 +74,29 @@ def plan_upload(sources: list[str], recursive: bool):
return dirs, files, problems
+def _walk(top: Path):
+ """`os.walk` for a tree that may go past Windows' MAX_PATH: each folder is
+ listed under its long form (`long_path`, which only prefixes a long path, so
+ a short top alone would not do) and the paths given back stay plain."""
+ stack = [top]
+ while stack:
+ here = stack.pop()
+ try:
+ with os.scandir(long_path(here)) as it:
+ entries = list(it)
+ except OSError:
+ continue
+ subdirs, names = [], []
+ for e in entries:
+ try:
+ is_dir = e.is_dir()
+ except OSError:
+ is_dir = False
+ (subdirs if is_dir else names).append(e.name)
+ yield here, subdirs, names # the caller prunes and orders subdirs
+ stack.extend(here / d for d in reversed(subdirs))
+
+
def taken_names(tree: Tree, folder: str, names: list[str]) -> set[str]:
"""The names among `names` already in `folder`, compared without case."""
there = [i.name for i in tree.children(folder, include_unavailable=True)]