mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-08 14:18:26 +02:00
More work on file transmission
This commit is contained in:
@@ -70,13 +70,13 @@ def iter_file_metadata(file_specs: Iterable[Tuple[str, str]]) -> Iterator[Union[
|
|||||||
|
|
||||||
def make_ftc(path: str, spec_id: str, sr: Optional[os.stat_result] = None) -> FileTransmissionCommand:
|
def make_ftc(path: str, spec_id: str, sr: Optional[os.stat_result] = None) -> FileTransmissionCommand:
|
||||||
if sr is None:
|
if sr is None:
|
||||||
sr = os.stat(path)
|
sr = os.stat(path, follow_symlinks=False)
|
||||||
if stat.S_ISREG(sr.st_mode):
|
if stat.S_ISLNK(sr.st_mode):
|
||||||
ftype = FileType.regular
|
|
||||||
elif stat.S_ISLNK(sr.st_mode):
|
|
||||||
ftype = FileType.symlink
|
ftype = FileType.symlink
|
||||||
elif stat.S_ISDIR(sr.st_mode):
|
elif stat.S_ISDIR(sr.st_mode):
|
||||||
ftype = FileType.directory
|
ftype = FileType.directory
|
||||||
|
elif stat.S_ISREG(sr.st_mode):
|
||||||
|
ftype = FileType.regular
|
||||||
else:
|
else:
|
||||||
raise ValueError('Not an appropriate file type')
|
raise ValueError('Not an appropriate file type')
|
||||||
return FileTransmissionCommand(
|
return FileTransmissionCommand(
|
||||||
@@ -91,7 +91,7 @@ def iter_file_metadata(file_specs: Iterable[Tuple[str, str]]) -> Iterator[Union[
|
|||||||
if not os.path.isabs(path):
|
if not os.path.isabs(path):
|
||||||
path = abspath(path, use_home=True)
|
path = abspath(path, use_home=True)
|
||||||
try:
|
try:
|
||||||
sr = os.stat(path)
|
sr = os.stat(path, follow_symlinks=False)
|
||||||
read_ok = os.access(path, os.R_OK)
|
read_ok = os.access(path, os.R_OK)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
errname = errno.errorcode.get(err.errno, 'EFAIL')
|
errname = errno.errorcode.get(err.errno, 'EFAIL')
|
||||||
@@ -129,7 +129,7 @@ def iter_file_metadata(file_specs: Iterable[Tuple[str, str]]) -> Iterator[Union[
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
s = os.stat(dest)
|
s = os.stat(dest, follow_symlinks=False)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@@ -533,7 +533,7 @@ class ActiveSend:
|
|||||||
|
|
||||||
def __init__(self, request_id: str, quiet: int, bypass: str, num_of_args: int) -> None:
|
def __init__(self, request_id: str, quiet: int, bypass: str, num_of_args: int) -> None:
|
||||||
self.id = request_id
|
self.id = request_id
|
||||||
self.args_remaining = num_of_args
|
self.expected_num_of_args = num_of_args
|
||||||
self.bypass_ok: Optional[bool] = None
|
self.bypass_ok: Optional[bool] = None
|
||||||
self.accepted = False
|
self.accepted = False
|
||||||
if bypass:
|
if bypass:
|
||||||
@@ -547,7 +547,7 @@ class ActiveSend:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def spec_complete(self) -> bool:
|
def spec_complete(self) -> bool:
|
||||||
return self.args_remaining < 1
|
return self.expected_num_of_args <= len(self.file_specs)
|
||||||
|
|
||||||
def add_file_spec(self, cmd: FileTransmissionCommand) -> None:
|
def add_file_spec(self, cmd: FileTransmissionCommand) -> None:
|
||||||
if len(self.file_specs) > 8192 or self.spec_complete:
|
if len(self.file_specs) > 8192 or self.spec_complete:
|
||||||
@@ -559,7 +559,7 @@ class ActiveSend:
|
|||||||
return monotonic() - self.last_activity_at > (60 * EXPIRE_TIME)
|
return monotonic() - self.last_activity_at > (60 * EXPIRE_TIME)
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
raise NotImplementedError('TODO: Implement this')
|
pass # TODO: Implement this
|
||||||
|
|
||||||
|
|
||||||
class FileTransmission:
|
class FileTransmission:
|
||||||
|
|||||||
@@ -86,9 +86,9 @@ class TestFileTransmission(BaseTest):
|
|||||||
b = tuple(f(r) for r in b if r.get('status') != 'PROGRESS')
|
b = tuple(f(r) for r in b if r.get('status') != 'PROGRESS')
|
||||||
self.ae(a, b)
|
self.ae(a, b)
|
||||||
|
|
||||||
def assertResponses(self, ft, **kw):
|
def assertResponses(self, ft, limit=1024, **kw):
|
||||||
self.responses.append(response(**kw))
|
self.responses.append(response(**kw))
|
||||||
self.cr(ft.test_responses, self.responses)
|
self.cr(ft.test_responses[:limit], self.responses[:limit])
|
||||||
|
|
||||||
def assertPathEqual(self, a, b):
|
def assertPathEqual(self, a, b):
|
||||||
a = os.path.abspath(os.path.realpath(a))
|
a = os.path.abspath(os.path.realpath(a))
|
||||||
@@ -132,6 +132,58 @@ class TestFileTransmission(BaseTest):
|
|||||||
# test size of delta
|
# test size of delta
|
||||||
patch(a_path, a_path, c_path, max_delta_len=256)
|
patch(a_path, a_path, c_path, max_delta_len=256)
|
||||||
|
|
||||||
|
def test_file_get(self):
|
||||||
|
# send refusal
|
||||||
|
for quiet in (0, 1, 2):
|
||||||
|
ft = FileTransmission(allow=False)
|
||||||
|
ft.handle_serialized_command(serialized_cmd(action='receive', id='x', quiet=quiet))
|
||||||
|
self.cr(ft.test_responses, [] if quiet == 2 else [response(id='x', status='EPERM:User refused the transfer')])
|
||||||
|
self.assertFalse(ft.active_sends)
|
||||||
|
# reading metadata for specs
|
||||||
|
cwd = os.path.join(self.tdir, 'cwd')
|
||||||
|
home = os.path.join(self.tdir, 'home')
|
||||||
|
os.mkdir(cwd), os.mkdir(home)
|
||||||
|
with set_paths(cwd=cwd, home=home):
|
||||||
|
ft = FileTransmission()
|
||||||
|
self.responses = []
|
||||||
|
ft.handle_serialized_command(serialized_cmd(action='receive', size=1))
|
||||||
|
self.assertResponses(ft, status='OK')
|
||||||
|
ft.handle_serialized_command(serialized_cmd(action='file', file_id='missing', data=b'XXX'))
|
||||||
|
self.responses.append(response(status='ENOENT:Failed to read spec', file_id='missing'))
|
||||||
|
self.assertResponses(ft, status='OK')
|
||||||
|
ft = FileTransmission()
|
||||||
|
self.responses = []
|
||||||
|
ft.handle_serialized_command(serialized_cmd(action='receive', size=2))
|
||||||
|
self.assertResponses(ft, status='OK')
|
||||||
|
with open(os.path.join(home, 'a'), 'w') as f:
|
||||||
|
f.write('a')
|
||||||
|
os.mkdir(f.name + 'd')
|
||||||
|
with open(os.path.join(f.name + 'd', 'b'), 'w') as f2:
|
||||||
|
f2.write('bbb')
|
||||||
|
os.symlink(f.name, f.name + 'd/s')
|
||||||
|
os.link(f.name, f.name + 'd/h')
|
||||||
|
os.symlink('XXX', f.name + 'd/q')
|
||||||
|
ft.handle_serialized_command(serialized_cmd(action='file', file_id='a', data=b'a'))
|
||||||
|
ft.handle_serialized_command(serialized_cmd(action='file', file_id='b', data=b'ad'))
|
||||||
|
files = {r['name']: r for r in ft.test_responses if r['action'] == 'file'}
|
||||||
|
self.ae(len(files), 6)
|
||||||
|
q = files[f.name]
|
||||||
|
tgt = q['status'].encode('ascii')
|
||||||
|
self.ae(q['size'], 1), self.assertNotIn('ftype', q)
|
||||||
|
q = files[f.name + 'd']
|
||||||
|
self.ae(q['ftype'], 'directory')
|
||||||
|
q = files[f.name + 'd/b']
|
||||||
|
self.ae(q['size'], 3)
|
||||||
|
q = files[f.name + 'd/s']
|
||||||
|
self.ae(q['ftype'], 'symlink')
|
||||||
|
self.ae(q['data'], tgt)
|
||||||
|
q = files[f.name + 'd/h']
|
||||||
|
self.ae(q['ftype'], 'link')
|
||||||
|
self.ae(q['data'], tgt)
|
||||||
|
q = files[f.name + 'd/q']
|
||||||
|
self.ae(q['ftype'], 'symlink')
|
||||||
|
self.assertNotIn('data', q)
|
||||||
|
|
||||||
def test_file_put(self):
|
def test_file_put(self):
|
||||||
# send refusal
|
# send refusal
|
||||||
for quiet in (0, 1, 2):
|
for quiet in (0, 1, 2):
|
||||||
|
|||||||
Reference in New Issue
Block a user