Skip to content

Commit

Permalink
feat(command): add --delete option for unused command (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
TheCrab13 authored Mar 23, 2024
1 parent 15cb108 commit c83851f
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 15 deletions.
2 changes: 1 addition & 1 deletion docs/source/commands/promote.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ The command will do not perform any file type check since the input files are fe
from an existing symbol store.

By default, ``pdbstore promote`` command will generate a default comment for the transaction
by using comment from specified transaction and appending `\ : Promote from <Input Symbol Store Path>`.
by using initial transaction comment and by appending `\ : Promote from <Input Symbol Store Path>`.
If a comment is specified through ``-c/--comment`` command-line option, the specified comment
will be used as it is.
3 changes: 2 additions & 1 deletion docs/source/commands/unused.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pdbstore unused
.. code-block:: text
$ pdbstore unused -h
usage: pdbstore unused [-s DIRECTORY] [-C PATH] [-S NAME] [-L PATH]
usage: pdbstore unused [-s DIRECTORY] [-d] [-C PATH] [-S NAME] [-L PATH]
[-V [LEVEL]] [-f NAME] [-h] [DATE]
Find all files not used since a specific date
Expand All @@ -18,6 +18,7 @@ pdbstore unused
-s DIRECTORY, --store-dir DIRECTORY
Local root directory for the symbol store. [env var:
PDBSTORE_STORAGE_DIR]
-d --delete Delete automatically all unused files
-C PATH, --config-file PATH
Configuration file to use. Can be used multiple times.
[env var: PDBSTORE_CFG]
Expand Down
29 changes: 27 additions & 2 deletions pdbstore/cli/commands/unused.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from pdbstore.cli.command import pdbstore_command, PDBStoreArgumentParser
from pdbstore.exceptions import CommandLineError, PDBAbortExecution, PDBStoreException
from pdbstore.io.output import cli_out_write, PDBStoreOutput
from pdbstore.store import OpStatus, Store, Summary, TransactionType
from pdbstore.typing import Any
from pdbstore.store import OpStatus, Store, Summary, Transaction, TransactionType
from pdbstore.typing import Any, Dict, List


def unused_text_formatter(summary: Summary) -> None:
Expand Down Expand Up @@ -85,6 +85,14 @@ def unused(parser: PDBStoreArgumentParser, *args: Any) -> Any:
help="""Date given YYYY-MM-DD format.""",
)

parser.add_argument(
"-d",
"--delete",
dest="delete",
action="store_true",
help="""Delete automatically all unused files.""",
)

add_global_arguments(parser)

opts = parser.parse_args(*args)
Expand Down Expand Up @@ -113,6 +121,8 @@ def unused(parser: PDBStoreArgumentParser, *args: Any) -> Any:
# Check for each file is present to the specified store or not.
summary = Summary(None, OpStatus.SUCCESS, TransactionType.UNUSED)

obselete_transactions: List[Transaction] = []
deletion_dict: Dict[str, int] = {}
for transaction, entry in store.iterator(lambda x: not x.is_deleted()):
try:
output.verbose(f"checking {entry.rel_path} ...")
Expand All @@ -122,11 +132,26 @@ def unused(parser: PDBStoreArgumentParser, *args: Any) -> Any:
dct = summary.add_file(entry.rel_path, OpStatus.SUCCESS)
dct["date"] = time.strftime("%Y-%m-%d", time.localtime(file_stat.st_atime))
dct["transaction_id"] = transaction.id
if opts.delete:
try:
dir_path: Path = store.rootdir / entry.file_name / entry.file_hash
dir_path.rmdir()
except OSError:
pass
count = deletion_dict.get(transaction.id, 0) + 1
deletion_dict[transaction.id] = count
if count == transaction.count:
# All files associated to the transaction have been deleted,
# so we can delete the transaction
obselete_transactions.append(transaction)
except PDBStoreException as exp: # pragma: no cover
summary.add_file(util.path_to_str(entry.rel_path), OpStatus.FAILED, "ex:" + str(exp))
except Exception as exc: # pylint: disable=broad-except # pragma: no cover
summary.add_file(util.path_to_str(entry.rel_path), OpStatus.FAILED, str(exc))
output.error(exc)
output.error("unexpected error when checking {file_path} file usage")

# Delete all required obselete transactions
for transaction in obselete_transactions:
store.delete_transaction(transaction.id)
return summary
4 changes: 2 additions & 2 deletions pdbstore/store/entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ def commit(
"""Commit transaction entry by storing the required filse into the symbol store.
If ``store`` is `None`, this function will consider as a standard transaction entry,
else this function will promote the files referenced by this
:class:`TransactionEntry <TransactionEntry>` object and stored them in ``store``
else this function will promote the files referenced by
:class:`this entry object <TransactionEntry>` and stored them in ``store``
as a new entry.
:param force: True to overwrite any existing file from the store, else False.
Expand Down
14 changes: 7 additions & 7 deletions pdbstore/store/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from pdbstore.store.transaction import Transaction
from pdbstore.store.transaction_type import TransactionType
from pdbstore.store.transactions import Transactions
from pdbstore.typing import Callable, Generator, List, Optional, PathLike, Tuple
from pdbstore.typing import Callable, Generator, List, Optional, PathLike, Tuple, Union

__all__ = ["Store"]

Expand Down Expand Up @@ -106,7 +106,7 @@ def new_transaction(
)

def find_transaction(
self, transaction_id: int, transaction_type: Optional[TransactionType] = None
self, transaction_id: Union[str, int], transaction_type: Optional[TransactionType] = None
) -> Transaction:
"""Find an existing transaction given by its id
Expand All @@ -120,8 +120,8 @@ def find_transaction(
a different transaction type.
:WriteFileError: An error occurs when updating global file.
"""
trans_id = f"{transaction_id:010d}"
PDBStoreOutput().debug("Finding ID ... {trans_id}")
trans_id = f"{int(transaction_id):010d}"
PDBStoreOutput().debug(f"Finding ID ... {trans_id}")
transaction = self.transactions.find(trans_id)
if not transaction:
raise exceptions.TransactionNotFoundError(transaction_id)
Expand All @@ -133,7 +133,7 @@ def find_transaction(
)
return transaction

def delete_transaction(self, transaction_id: int, dry_run: bool = False) -> Summary:
def delete_transaction(self, transaction_id: Union[str, int], dry_run: bool = False) -> Summary:
"""Delete an existing transaction given by its id
:param transaction_id: The transaction id to be deleted.
Expand Down Expand Up @@ -171,8 +171,8 @@ def commit(
"""Commit a transaction on the disk.
If ``store`` is `None`, this function will consider as a standard transaction,
else this function will promote the files referenced by ``transaction``
:class:`Transaction <pdbstore.store.transaction.Transaction>` object and stored
else this function will promote the files referenced by
:class:`transaction <pdbstore.store.transaction.Transaction>` object and stored
in ``store`` as a new transaction from this
:class:`Store <pdbstore.store.store.Store>` object.
Expand Down
5 changes: 3 additions & 2 deletions pdbstore/store/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,9 @@ def commit(
"""Save the transaction on the disk.
If ``store`` is `None`, this function will consider as a standard transaction,
else this function will promote the files referenced by this :class:`Transaction` object
and stored in ``store`` as a new transaction from its associated
else this function will promote the files referenced by
:class:`this transaction object<Transaction>` object and stored in ``store``
as a new transaction from its associated
:class:`Store <pdbstore.store.store.Store>` object.
:param transaction_id: The transaction ID
Expand Down
8 changes: 8 additions & 0 deletions pdbstore/store/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ def __init__(self, store: "Store"): # type: ignore[name-defined] # noqa: F821
self.store: "Store" = store # type: ignore[name-defined] # noqa: F821
self._transactions: Dict[str, Transaction] = {}

@property
def count(self) -> int:
"""Retrieve the total number of transactions
:return: The total number of registered transactions
"""
return len(self._transactions)

def _server_file_exists(self) -> bool:
"""Determine whether the server file exists or not
Expand Down
29 changes: 29 additions & 0 deletions tests/cli/test_unused.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from pdbstore import cli
from pdbstore.cli.exit_codes import ERROR_UNEXPECTED, SUCCESS
from pdbstore.store import Store


@pytest.mark.parametrize(
Expand Down Expand Up @@ -79,12 +80,40 @@ def test_complete(capsys, tmp_store_dir, test_data_native_dir):
assert "" != out
assert "" == err

# New file into the store
assert cli.cli.main(["add", "-Vquiet"] + argv) == SUCCESS

# Test through direct command-line with --delete option
with mock.patch(
"sys.argv",
[
"pdbstore",
"unused",
]
+ argv[0:2]
+ [tomorrow, "--delete"],
):
assert cli.cli.main() == SUCCESS
out, err = capsys.readouterr()
assert "" != out
assert "" == err
assert Store(tmp_store_dir).transactions.count == 0

# New file into the store
assert cli.cli.main(["add", "-Vquiet"] + argv) == SUCCESS

# Test with direct call to main function
assert cli.cli.main(["unused"] + argv[0:2] + [tomorrow]) == SUCCESS
out, err = capsys.readouterr()
assert "" != out
assert "" == err

# Test with direct call to main function with --delete option
assert cli.cli.main(["unused"] + argv[0:2] + [tomorrow, "--delete"]) == SUCCESS
out, err = capsys.readouterr()
assert "" != out
assert "" == err


@pytest.mark.parametrize(
"formatter",
Expand Down

0 comments on commit c83851f

Please sign in to comment.