|
9 | 9 | import multiprocessing |
10 | 10 | import pathlib |
11 | 11 | import shutil |
| 12 | +import os |
12 | 13 | import subprocess |
13 | 14 | import sys |
14 | 15 | import typing |
15 | 16 | import uuid |
16 | 17 |
|
17 | | -__version__ = '1.0.1' |
| 18 | +__version__ = '1.0.2' |
18 | 19 |
|
19 | 20 | DEFAULT_THREAD_COUNT = min(multiprocessing.cpu_count(), 64) |
20 | 21 |
|
@@ -524,26 +525,49 @@ def create_logger(name: str, log_level: int = logging.INFO) -> logging.Logger: |
524 | 525 |
|
525 | 526 | def get_uuid() -> str: |
526 | 527 | """Returns a unique string identifier.""" |
527 | | - return f'vclust-{str(uuid.uuid4().hex)[:16]}' |
| 528 | + return f'vclust-{str(uuid.uuid4().hex)[:10]}' |
528 | 529 |
|
529 | 530 |
|
530 | 531 | def validate_binary(bin_path: pathlib.Path) -> pathlib.Path: |
531 | | - """Verifies if a binary file exists and is executable. |
| 532 | + """Validates the existence and executability of a binary file. |
| 533 | +
|
| 534 | + This function checks if the provided path points to an existing binary file |
| 535 | + and if it is executable. It also attempts to run the binary to ensure it |
| 536 | + operates without errors. |
532 | 537 |
|
533 | 538 | Args: |
534 | | - bin_path (Path): Path to the executable binary file. |
| 539 | + bin_path: |
| 540 | + The path to the executable binary file. |
535 | 541 |
|
536 | 542 | Returns: |
537 | | - Path to the binary file. |
| 543 | + pathlib.Path: The resolved path to the binary file. |
538 | 544 |
|
539 | 545 | Raises: |
540 | | - SystemExit: If the binary file is not found or not executable. |
541 | | -
|
| 546 | + SystemExit: If the binary file does not exist, is not executable, or |
| 547 | + if running the binary encounters an error. |
542 | 548 | """ |
| 549 | + bin_path = bin_path.resolve() |
| 550 | + |
543 | 551 | if not bin_path.exists(): |
544 | | - exit(f'error: executable not found: {bin_path.resolve()}') |
545 | | - if not shutil.which(bin_path): |
546 | | - exit(f'error: file not executable: {bin_path.resolve()}') |
| 552 | + exit(f'error: Executable not found: {bin_path}') |
| 553 | + |
| 554 | + if not bin_path.is_file() or not os.access(bin_path, os.X_OK): |
| 555 | + exit(f'error: Binary file not executable: {bin_path}') |
| 556 | + |
| 557 | + try: |
| 558 | + process = subprocess.run( |
| 559 | + [str(bin_path)], |
| 560 | + stdout=subprocess.DEVNULL, |
| 561 | + stderr=subprocess.PIPE, |
| 562 | + text=True, |
| 563 | + check=True |
| 564 | + ) |
| 565 | + except subprocess.CalledProcessError as e: |
| 566 | + exit(f'error: Running {bin_path} failed with message: {e.stderr}') |
| 567 | + except OSError as e: |
| 568 | + exit(f'error: OSError in {bin_path} - {e}') |
| 569 | + except Exception as e: |
| 570 | + exit(f'error: Unexpected error in binary {bin_path} - {e}') |
547 | 571 | return bin_path |
548 | 572 |
|
549 | 573 |
|
@@ -601,23 +625,45 @@ def run( |
601 | 625 | verbose: bool, |
602 | 626 | logger: logging.Logger |
603 | 627 | ) -> subprocess.CompletedProcess: |
604 | | - """Runs a given command as a subprocess and handle logging. |
| 628 | + """Executes a given command as a subprocess and handles logging. |
| 629 | +
|
| 630 | + This function runs the specified command, logs the execution details, |
| 631 | + and manages errors. If verbose mode is enabled, the command's standard |
| 632 | + error output is not suppressed. Otherwise, the standard error is piped |
| 633 | + and logged in case of failure. |
| 634 | +
|
| 635 | + Args: |
| 636 | + cmd: |
| 637 | + The command to run as a list of strings. |
| 638 | + verbose: |
| 639 | + Flag indicating whether to run the command in verbose mode. |
| 640 | + logger: |
| 641 | + The logger instance for logging information and errors. |
605 | 642 |
|
606 | 643 | Returns: |
607 | | - subprocess.CompletedProcess: Completed process information. |
| 644 | + subprocess.CompletedProcess: The completed process information. |
608 | 645 |
|
| 646 | + Raises: |
| 647 | + SystemExit: If the command fails to execute or an error occurs. |
609 | 648 | """ |
610 | 649 | logger.info(f'Running: {" ".join(cmd)}') |
611 | | - process = subprocess.run( |
612 | | - cmd, |
613 | | - stdout=subprocess.DEVNULL, |
614 | | - stderr=None if verbose else subprocess.PIPE, |
615 | | - text=True, |
616 | | - ) |
617 | | - if process.returncode: |
618 | | - logger.error(f'While running: {" ".join(process.args)}') |
619 | | - logger.error(f'Error message: {process.stderr}') |
| 650 | + try: |
| 651 | + process = subprocess.run( |
| 652 | + cmd, |
| 653 | + stdout=subprocess.DEVNULL, |
| 654 | + stderr=None if verbose else subprocess.PIPE, |
| 655 | + text=True, |
| 656 | + check=True |
| 657 | + ) |
| 658 | + except subprocess.CalledProcessError as e: |
| 659 | + logger.error(f'Process {" ".join(cmd)} failed with message: {e.stderr}') |
| 660 | + exit(1) |
| 661 | + except OSError as e: |
| 662 | + logger.error(f'OSError: {" ".join(cmd)} failed with message: {e}') |
620 | 663 | exit(1) |
| 664 | + except Exception as e: |
| 665 | + logger.error(f'Unexpected: {" ".join(cmd)} failed with message: {e}') |
| 666 | + exit(1) |
621 | 667 | logger.info(f'Done') |
622 | 668 | return process |
623 | 669 |
|
|
0 commit comments