Source code for sobe.main
"""Command-line interface entry point: input validation and output to user."""
import argparse
import datetime
import functools
import importlib.metadata
import pathlib
import warnings
import urllib3.exceptions
from sobe.aws import AWS
from sobe.config import MustEditConfig, load_config
write = functools.partial(print, flush=True, end="")
print = functools.partial(print, flush=True) # type: ignore
warnings.filterwarnings("ignore", category=urllib3.exceptions.InsecureRequestWarning)
[docs]
def main() -> None:
try:
config = load_config()
except MustEditConfig as err:
print("Created config file at the path below. You must edit it before use.")
print(err.path)
raise SystemExit(1) from err
args = parse_args()
aws = AWS(config.aws)
if args.policy:
print(aws.generate_needed_permissions())
return
if args.list:
files = aws.list(args.prefix)
if not files:
print(f"No files under {config.url}{args.prefix}")
return
for name in files:
print(f"{config.url}{args.prefix}{name}")
return
for path in args.paths:
write(f"{config.url}{args.prefix}{args.remote_name or path.name} ...")
if args.delete:
existed = aws.delete(args.prefix, path.name)
print("deleted." if existed else "didn't exist.")
else:
aws.upload(args.prefix, path, args.remote_name, content_type=args.content_type)
print("ok.")
if args.invalidate:
write("Clearing cache...")
for _ in aws.invalidate_cache():
write(".")
print("complete.")
[docs]
def parse_args(argv=None) -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Upload files to your AWS drop box.")
parser.add_argument("--version", action="version", version=f"sobe {get_version()}")
parser.add_argument("-y", "--year", type=str, help="set remote directory (usually a year)")
parser.add_argument("-t", "--content-type", type=str, help="override detected MIME type for uploaded files")
parser.add_argument("-l", "--list", action="store_true", help="list all files in the year")
parser.add_argument("-d", "--delete", action="store_true", help="delete instead of upload")
parser.add_argument("-i", "--invalidate", action="store_true", help="invalidate CloudFront cache")
parser.add_argument("-p", "--policy", action="store_true", help="generate IAM policy requirements and exit")
parser.add_argument("-r", "--remote-name", type=str, help="upload a single file with a different remote name")
parser.add_argument("files", nargs="*", help="Source files.")
args = parser.parse_args(argv)
num_arg_types = sum(map(bool, args.__dict__.values()))
if num_arg_types == 0:
parser.print_help()
raise SystemExit(0)
if args.policy:
if num_arg_types != 1:
parser.error("--policy cannot be used with other arguments")
return args
if args.year is None:
args.year = str(datetime.date.today().year)
elif not (args.files or args.list):
parser.error("--year requires files or --list to be specified")
args.prefix = args.year if args.year == "" or args.year.endswith("/") else f"{args.year}/"
if args.content_type or args.remote_name:
if args.delete or args.list:
parser.error("Arguments like --content-type and --remote-name are only valid for uploads")
if not args.files:
parser.error("You must specify files to be uploaded")
elif args.remote_name and len(args.files) > 1:
parser.error("--remote-name can only be used when uploading a single file")
elif args.list:
if args.delete:
parser.error("--list and --delete cannot be used at the same time")
if args.files:
parser.error("--list does not support file filtering yet")
elif args.delete:
if not args.files:
parser.error("--delete requires files to be specified")
args.paths = [pathlib.Path(p) for p in args.files]
if not (args.delete or args.list):
missing = [p for p in args.paths if not p.exists()]
if missing:
print("The following files do not exist:")
for p in missing:
print(f" {p}")
raise SystemExit(1)
return args
[docs]
def get_version() -> str:
"""Get the current version of the sobe package."""
try:
return importlib.metadata.version("sobe")
except importlib.metadata.PackageNotFoundError: # pragma: no cover
return "unknown"