Changeset View
Changeset View
Standalone View
Standalone View
swh/deposit/cli/client.py
# Copyright (C) 2017-2020 The Software Heritage developers | # Copyright (C) 2017-2020 The Software Heritage developers | ||||
# See the AUTHORS file at the top-level directory of this distribution | # See the AUTHORS file at the top-level directory of this distribution | ||||
# License: GNU General Public License version 3, or any later version | # License: GNU General Public License version 3, or any later version | ||||
# See top-level LICENSE file for more information | # See top-level LICENSE file for more information | ||||
from __future__ import annotations | from __future__ import annotations | ||||
from datetime import datetime, timezone | from datetime import datetime, timezone | ||||
import logging | import logging | ||||
# WARNING: do not import unnecessary things here to keep cli startup time under | # WARNING: do not import unnecessary things here to keep cli startup time under | ||||
# control | # control | ||||
import os | import os | ||||
import sys | import sys | ||||
from typing import TYPE_CHECKING, Any, Collection, Dict, List, Optional | from typing import TYPE_CHECKING, Any, Collection, Dict, List, Optional | ||||
import warnings | |||||
import click | import click | ||||
from swh.deposit.cli import deposit | from swh.deposit.cli import deposit | ||||
logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||
▲ Show 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | |||||
def client_command_parse_input( | def client_command_parse_input( | ||||
client, | client, | ||||
username: str, | username: str, | ||||
archive: Optional[str], | archive: Optional[str], | ||||
metadata: Optional[str], | metadata: Optional[str], | ||||
collection: Optional[str], | collection: Optional[str], | ||||
archive_deposit: bool, | |||||
metadata_deposit: bool, | |||||
slug: Optional[str], | slug: Optional[str], | ||||
partial: bool, | partial: bool, | ||||
deposit_id: Optional[int], | deposit_id: Optional[int], | ||||
swhid: Optional[str], | swhid: Optional[str], | ||||
replace: bool, | replace: bool, | ||||
url: str, | url: str, | ||||
name: Optional[str], | name: Optional[str], | ||||
authors: List[str], | authors: List[str], | ||||
Show All 23 Lines | """Parse the client subcommand options and make sure the combination | ||||
errors are already dealt with by the underlying api client. | errors are already dealt with by the underlying api client. | ||||
Raises: | Raises: | ||||
InputError explaining the user input related issue | InputError explaining the user input related issue | ||||
MaintenanceError explaining the api status | MaintenanceError explaining the api status | ||||
Returns: | Returns: | ||||
dict with the following keys: | dict with the following keys: | ||||
'archive': the software archive to deposit | "archive": the software archive to deposit | ||||
'username': username | "username": username | ||||
'metadata': the metadata file to deposit | "metadata": the metadata file to deposit | ||||
'collection: the user's collection under which to put the deposit | "collection": the user's collection under which to put the deposit | ||||
'slug': the slug or external id identifying the deposit to make | "slug": the slug or external id identifying the deposit to make | ||||
'partial': if the deposit is partial or not | "in_progress": if the deposit is partial or not | ||||
'client': instantiated class | "url": deposit's server main entry point | ||||
'url': deposit's server main entry point | "deposit_id": optional deposit identifier | ||||
'deposit_type': deposit's type (binary, multipart, metadata) | "swhid": optional deposit swhid | ||||
'deposit_id': optional deposit identifier | "replace": whether the given deposit is to be replaced or not | ||||
vlorentz: you missed the quotes in these three lines | |||||
'swhid': optional deposit swhid | |||||
""" | """ | ||||
if archive_deposit and metadata_deposit: | |||||
# too many flags use, remove redundant ones (-> multipart deposit) | |||||
archive_deposit = False | |||||
metadata_deposit = False | |||||
if not slug: # generate one as this is mandatory | if not slug: # generate one as this is mandatory | ||||
slug = generate_slug() | slug = generate_slug() | ||||
if not metadata: | if not metadata: | ||||
if metadata_deposit: | |||||
raise InputError( | |||||
"Metadata deposit must be provided for metadata " | |||||
"deposit, either a filepath with --metadata or --name and --author" | |||||
) | |||||
if name and authors: | if name and authors: | ||||
metadata_path = os.path.join(temp_dir, "metadata.xml") | metadata_path = os.path.join(temp_dir, "metadata.xml") | ||||
logging.debug("Temporary file: %s", metadata_path) | logging.debug("Temporary file: %s", metadata_path) | ||||
metadata_xml = generate_metadata(username, name, slug, authors) | metadata_xml = generate_metadata(username, name, slug, authors) | ||||
logging.debug("Metadata xml generated: %s", metadata_xml) | logging.debug("Metadata xml generated: %s", metadata_xml) | ||||
with open(metadata_path, "w") as f: | with open(metadata_path, "w") as f: | ||||
f.write(metadata_xml) | f.write(metadata_xml) | ||||
metadata = metadata_path | metadata = metadata_path | ||||
elif not archive_deposit and not partial and not deposit_id: | elif archive is not None and not partial and not deposit_id: | ||||
# If we meet all the following conditions: | # If we meet all the following conditions: | ||||
# * this is not an archive-only deposit request | # * this is not an archive-only deposit request | ||||
# * it is not part of a multipart deposit (either create/update | # * it is not part of a multipart deposit (either create/update | ||||
# or finish) | # or finish) | ||||
# * it misses either name or authors | # * it misses either name or authors | ||||
raise InputError( | raise InputError( | ||||
"For metadata deposit request, either a metadata file with " | "For metadata deposit request, either a metadata file with " | ||||
"--metadata or both --author and --name must be provided. " | "--metadata or both --author and --name must be provided. " | ||||
"If this is an archive deposit request, none is required." | |||||
) | ) | ||||
elif name or authors: | elif name or authors: | ||||
# If we are generating metadata, then all mandatory metadata | # If we are generating metadata, then all mandatory metadata | ||||
# must be present | # must be present | ||||
raise InputError( | raise InputError( | ||||
"For metadata deposit request, either a metadata file with " | "For metadata deposit request, either a metadata file with " | ||||
"--metadata or both --author and --name must be provided." | "--metadata or both --author and --name must be provided." | ||||
) | ) | ||||
else: | else: | ||||
# TODO: this is a multipart deposit, we might want to check that | # TODO: this is a multipart deposit, we might want to check that | ||||
# metadata are deposited at some point | # metadata are deposited at some point | ||||
pass | pass | ||||
elif name or authors: | elif name or authors: | ||||
raise InputError( | raise InputError( | ||||
"Using --metadata flag is incompatible with both " | "Using --metadata flag is incompatible with both " | ||||
"--author and --name (Those are used to generate one metadata file)." | "--author and --name (Those are used to generate one metadata file)." | ||||
) | ) | ||||
if metadata_deposit: | if not archive and not metadata: | ||||
archive = None | |||||
if archive_deposit: | |||||
metadata = None | |||||
if not archive and not metadata and partial: | |||||
Done Inline ActionsBTW @ardumont I did not understand this test. Why the partial ? What's the meaning of a non-partial deposit with no archive and no metadata? Is it somehow to support the "closing" of a complete but still marked in-progress deposit? (which currently does not work but for other reasons). douardda: BTW @ardumont I did not understand this test. Why the `partial` ?
What's the meaning of a non… | |||||
Not Done Inline ActionsI don't remember. But your 2nd paragraph is what immediately cames to as an answer to the question ardumont: I don't remember.
But your 2nd paragraph is what immediately cames to as an answer to the… | |||||
raise InputError( | raise InputError( | ||||
"Please provide an actionable command. See --help for more information" | "Please provide an actionable command. See --help for more information" | ||||
) | ) | ||||
if replace and not deposit_id: | if replace and not deposit_id: | ||||
raise InputError("To update an existing deposit, you must provide its id") | raise InputError("To update an existing deposit, you must provide its id") | ||||
if not collection: | if not collection: | ||||
Show All 34 Lines | @click.option( | ||||
help=( | help=( | ||||
"(Optional) Path to xml metadata file. If not provided, " | "(Optional) Path to xml metadata file. If not provided, " | ||||
"this will use a file named <archive>.metadata.xml" | "this will use a file named <archive>.metadata.xml" | ||||
), | ), | ||||
) # noqa | ) # noqa | ||||
@click.option( | @click.option( | ||||
"--archive-deposit/--no-archive-deposit", | "--archive-deposit/--no-archive-deposit", | ||||
default=False, | default=False, | ||||
help="(Optional) Software archive only deposit", | help="Deprecated (ignored)", | ||||
) | ) | ||||
@click.option( | @click.option( | ||||
"--metadata-deposit/--no-metadata-deposit", | "--metadata-deposit/--no-metadata-deposit", | ||||
default=False, | default=False, | ||||
help="(Optional) Metadata only deposit", | help="Deprecated (ignored)", | ||||
) | ) | ||||
@click.option( | @click.option( | ||||
"--collection", | "--collection", | ||||
help="(Optional) User's collection. If not provided, this will be fetched.", | help="(Optional) User's collection. If not provided, this will be fetched.", | ||||
) # noqa | ) # noqa | ||||
@click.option( | @click.option( | ||||
"--slug", | "--slug", | ||||
help=( | help=( | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | @click.option( | ||||
type=click.Choice(["logging", "yaml", "json"]), | type=click.Choice(["logging", "yaml", "json"]), | ||||
help="Output format results.", | help="Output format results.", | ||||
) | ) | ||||
@click.pass_context | @click.pass_context | ||||
def upload( | def upload( | ||||
ctx, | ctx, | ||||
username: str, | username: str, | ||||
password: str, | password: str, | ||||
archive: Optional[str] = None, | archive: Optional[str], | ||||
metadata: Optional[str] = None, | metadata: Optional[str], | ||||
archive_deposit: bool = False, | archive_deposit: bool, | ||||
metadata_deposit: bool = False, | metadata_deposit: bool, | ||||
collection: Optional[str] = None, | collection: Optional[str], | ||||
slug: Optional[str] = None, | slug: Optional[str], | ||||
partial: bool = False, | partial: bool, | ||||
deposit_id: Optional[int] = None, | deposit_id: Optional[int], | ||||
swhid: Optional[str] = None, | swhid: Optional[str], | ||||
replace: bool = False, | replace: bool, | ||||
url: str = "https://deposit.softwareheritage.org", | url: str, | ||||
verbose: bool = False, | verbose: bool, | ||||
name: Optional[str] = None, | name: Optional[str], | ||||
author: List[str] = [], | author: List[str], | ||||
output_format: Optional[str] = None, | output_format: Optional[str], | ||||
): | ): | ||||
"""Software Heritage Public Deposit Client | """Software Heritage Public Deposit Client | ||||
Create/Update deposit through the command line. | Create/Update deposit through the command line. | ||||
More documentation can be found at | More documentation can be found at | ||||
https://docs.softwareheritage.org/devel/swh-deposit/getting-started.html. | https://docs.softwareheritage.org/devel/swh-deposit/getting-started.html. | ||||
""" | """ | ||||
import tempfile | import tempfile | ||||
from swh.deposit.client import MaintenanceError, PublicApiDepositClient | from swh.deposit.client import MaintenanceError, PublicApiDepositClient | ||||
if archive_deposit or metadata_deposit: | |||||
warnings.warn( | |||||
'"archive_deposit" and "metadata_deposit" option arguments are ' | |||||
"deprecated and have no effect; simply do not provide the archive " | |||||
"for a metadata-only deposit, and do not provide a metadata for a" | |||||
"archive-only deposit.", | |||||
DeprecationWarning, | |||||
) | |||||
url = _url(url) | url = _url(url) | ||||
client = PublicApiDepositClient(url=url, auth=(username, password)) | client = PublicApiDepositClient(url=url, auth=(username, password)) | ||||
with tempfile.TemporaryDirectory() as temp_dir: | with tempfile.TemporaryDirectory() as temp_dir: | ||||
try: | try: | ||||
logger.debug("Parsing cli options") | logger.debug("Parsing cli options") | ||||
config = client_command_parse_input( | config = client_command_parse_input( | ||||
client, | client, | ||||
username, | username, | ||||
archive, | archive, | ||||
metadata, | metadata, | ||||
collection, | collection, | ||||
archive_deposit, | |||||
metadata_deposit, | |||||
slug, | slug, | ||||
partial, | partial, | ||||
deposit_id, | deposit_id, | ||||
swhid, | swhid, | ||||
replace, | replace, | ||||
url, | url, | ||||
name, | name, | ||||
author, | author, | ||||
▲ Show 20 Lines • Show All 81 Lines • Show Last 20 Lines |
you missed the quotes in these three lines