Skip to content

Commit f59bd11

Browse files
authored
Add touch to CLI (#205)
1 parent 0e7b273 commit f59bd11

File tree

2 files changed

+103
-1
lines changed

2 files changed

+103
-1
lines changed

python/hdfs_native/cli.py

+74-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from argparse import ArgumentParser, Namespace
99
from collections import defaultdict
1010
from concurrent.futures import ThreadPoolExecutor, as_completed
11-
from datetime import datetime
11+
from datetime import datetime, timezone
1212
from typing import Dict, List, Optional, Sequence, Tuple, Union
1313
from urllib.parse import urlparse
1414

@@ -470,6 +470,40 @@ def rmdir(args: Namespace):
470470
client.delete(path)
471471

472472

473+
def touch(args: Namespace):
474+
for url in args.path:
475+
client = _client_for_url(url)
476+
for path in _glob_path(client, _path_for_url(url)):
477+
if args.access_time and args.modification_time:
478+
raise ValueError(
479+
"--access-time and --modification-time cannot both be passed"
480+
)
481+
timestamp = None
482+
if args.timestamp:
483+
timestamp = datetime.strptime(args.timestamp, r"%Y%m%d:%H%M%S")
484+
485+
try:
486+
status = client.get_file_info(path)
487+
if timestamp is None:
488+
timestamp = datetime.now(timezone.utc)
489+
except FileNotFoundError:
490+
if args.only_change:
491+
return
492+
493+
client.create(path).close()
494+
status = client.get_file_info(path)
495+
496+
if timestamp:
497+
access_time = int(timestamp.timestamp() * 1000)
498+
modification_time = int(timestamp.timestamp() * 1000)
499+
if args.access_time:
500+
modification_time = status.modification_time
501+
if args.modification_time:
502+
access_time = status.access_time
503+
504+
client.set_times(path, modification_time, access_time)
505+
506+
473507
def main(in_args: Optional[Sequence[str]] = None):
474508
parser = ArgumentParser(
475509
description="""Command line utility for interacting with HDFS using hdfs-native.
@@ -740,6 +774,45 @@ def main(in_args: Optional[Sequence[str]] = None):
740774
)
741775
rmdir_parser.set_defaults(func=rmdir)
742776

777+
touch_parser = subparsers.add_parser(
778+
"touch",
779+
help="Updates the access and modification times of a file or creates it if it doesn't exist",
780+
description="""Updates the access and modification times of the file specified by the <path> to
781+
the current time. If the file does not exist, then a zero length file is created
782+
at <path> with current time as the timestamp of that <path>.""",
783+
)
784+
touch_parser.add_argument(
785+
"-a",
786+
"--access-time",
787+
action="store_true",
788+
default=False,
789+
help="Only change the access time",
790+
)
791+
touch_parser.add_argument(
792+
"-m",
793+
"--modification-time",
794+
action="store_true",
795+
default=False,
796+
help="Only change the modification time",
797+
)
798+
touch_parser.add_argument(
799+
"-t",
800+
"--timestamp",
801+
help="Use specified timestamp instead of current time in the format yyyyMMdd:HHmmss",
802+
)
803+
touch_parser.add_argument(
804+
"-c",
805+
"--only-change",
806+
action="store_true",
807+
default=False,
808+
help="Don't create the file if it doesn't exist",
809+
)
810+
touch_parser.add_argument(
811+
"path",
812+
nargs="+",
813+
)
814+
touch_parser.set_defaults(func=touch)
815+
743816
args = parser.parse_args(in_args)
744817
args.func(args)
745818

python/tests/test_cli.py

+29
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import os
55
import re
66
import stat
7+
from datetime import datetime
78
from tempfile import TemporaryDirectory
89
from typing import Callable, Iterator, List, Literal, Optional, Tuple, overload
910

@@ -393,3 +394,31 @@ def test_rmdir(client: Client):
393394
pytest.fail("Directory was not removed")
394395
except FileNotFoundError:
395396
pass
397+
398+
399+
def test_touch(client: Client):
400+
cli_main(["touch", "/testfile"])
401+
client.get_file_info("/testfile")
402+
403+
cli_main(["touch", "-c", "/testfile2"])
404+
try:
405+
client.get_file_info("/testfile2")
406+
pytest.fail("File should not have been created")
407+
except FileNotFoundError:
408+
pass
409+
410+
cli_main(["touch", "-a", "/testfile"])
411+
status = client.get_file_info("/testfile")
412+
assert status.access_time > status.modification_time
413+
414+
cli_main(["touch", "-m", "/testfile"])
415+
status = client.get_file_info("/testfile")
416+
assert status.modification_time > status.access_time
417+
418+
cli_main(["touch", "-t", "20240101:000000", "/testfile"])
419+
timestamp = int(
420+
datetime.strptime("20240101:000000", r"%Y%m%d:%H%M%S").timestamp() * 1000
421+
)
422+
status = client.get_file_info("/testfile")
423+
assert status.modification_time == timestamp
424+
assert status.access_time == timestamp

0 commit comments

Comments
 (0)