Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmake ctest support #6

Merged
merged 2 commits into from
Dec 19, 2023
Merged

cmake ctest support #6

merged 2 commits into from
Dec 19, 2023

Conversation

PhilipDeegan
Copy link
Owner

@PhilipDeegan PhilipDeegan commented Nov 26, 2023

Summary by CodeRabbit

  • New Features

    • Introduced a feature to display a list of available modules in the package.
    • Added new functionality for configuring and building C++ projects using CMake.
    • Implemented a sleep function that can be triggered via command-line arguments.
    • Created a new ValDict class extending the functionality of the built-in dict.
    • Established a new logger setup for application logging.
  • Refactor

    • Removed several functions from phlop.app related to system operations and process management.
    • Replaced the RunTimer class with an updated version.
    • Enhanced the runtime statistics management and test case functionalities with new classes and methods.
  • Bug Fixes

    • Fixed the checkout function in phlop.app/git.py by removing an unnecessary parameter.
    • Updated version function in phlop.app/perf.py.
    • Adjusted pushd function and added a write_to_file function in phlop/os.py.
    • Modified the decode_bytes function in phlop/string.py.
  • Documentation

    • Updated inline documentation to reflect changes in modules and functionalities.
  • Style

    • Adjusted code styling to comply with new .pylintrc configuration.
  • Chores

    • Updated the package version from "0.0.6" to "0.0.7".

Copy link

coderabbitai bot commented Nov 26, 2023

Walkthrough

The phlop package underwent a version update with changes across various modules. Some functions were removed, while others were added or modified, with a focus on enhancing capabilities related to CMake project handling, logging, file operations, and test case management. New classes and data structures were introduced, and there was a shift towards more specialized functionality, such as the ValDict class and RunTimer class. Additionally, the package now provides a clearer overview of its modules through added print statements in __main__.py files.

Changes

Files Change Summary
phlop/app/.../__init__.py, phlop/proc.py Removed several functions; modified and added new ones.
phlop/app/cmake.py, phlop/app/sleep.py, phlop/run/test_cases.py Added new scripts and classes for CMake and testing.
phlop/app/git.py, phlop/app/perf.py, phlop/string.py Modified existing functions.
phlop/dict.py, phlop/logger.py, phlop/os.py, phlop/procs/runtimer.py, phlop/run/stats_man.py, phlop/sys.py Introduced new classes and functions.
phlop/.../__main__.py Added blocks to print available modules.
setup.py Updated version and .pylintrc configuration.

🐇✨
In the land of code where the phlop bunnies hop,
They tweaked and they twirled, never willing to stop.
New features they've spun in the soft light of moon,
With a version new sprung, they shall see you all soon!
🌟📦

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on X ?


Tips

Chat with CodeRabbit Bot (@coderabbitai)

  • You can reply to a review comment made by CodeRabbit.
  • You can tag CodeRabbit on specific lines of code or files in the PR by tagging @coderabbitai in a comment.
  • You can tag @coderabbitai in a PR comment and ask one-off questions about the PR and the codebase. Use quoted replies to pass the context for follow-up questions.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 16

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 837bb1c and 882198f.
Files selected for processing (13)
  • phlop/app/init.py (1 hunks)
  • phlop/app/cmake.py (1 hunks)
  • phlop/app/git.py (1 hunks)
  • phlop/app/perf.py (1 hunks)
  • phlop/os.py (2 hunks)
  • phlop/proc.py (3 hunks)
  • phlop/reflection.py (1 hunks)
  • phlop/run/test_cases.py (2 hunks)
  • phlop/string.py (1 hunks)
  • phlop/testing/parallel_processor.py (3 hunks)
  • phlop/testing/test_cases.py (1 hunks)
  • sh/lint.sh (1 hunks)
  • tests/all_concurrent.py (1 hunks)
Additional comments: 30
phlop/app/__init__.py (1)
  • 1-1: The hunk shows an empty file, which aligns with the summary stating that several functions have been removed. Ensure that the removal of these functions is intentional and that their responsibilities are covered elsewhere in the codebase.
phlop/app/cmake.py (5)
  • 8-9: The version function is empty and has no implementation. Please verify if this is intentional or if the implementation is missing.

  • 20-22: The config function correctly uses the make_config_str function to construct the command and then runs it. Ensure that the extra parameter is handled safely as mentioned in the previous comment.

  • 25-26: The build function correctly chooses between "ninja" and "make" based on the use_ninja parameter.

  • 70-87: The list_tests function correctly constructs the command, runs it, and parses the output into CTest_test instances. Ensure that the build_dir parameter is properly sanitized to prevent command injection vulnerabilities.

  • 90-94: The test_cmd function correctly generates a test command based on the provided test name and verbosity.

phlop/app/git.py (1)
  • 29-32: The checkout function has been updated to remove the recreate logic and now only creates a branch if it does not exist and create is True. Ensure that all parts of the codebase that depended on the recreate functionality have been updated to handle this change.
phlop/os.py (2)
  • 3-3: The import of Path from pathlib is correctly added to support the new write_to_file function.

  • 24-25: The addition of a check for the existence of new_cwd before changing the directory is a good practice for error handling and robustness.

phlop/reflection.py (5)
  • 3-3: The addition of the logging module is appropriate for the new error logging functionality.

  • 9-9: The addition of the fail_on_import_error parameter allows for better control over the function's behavior in case of import errors, enhancing the robustness of the function.

  • 27-30: The exception handling for ModuleNotFoundError is well-implemented, providing a choice between raising the exception or logging an error based on the fail_on_import_error flag.

  • 30-30: Logging the error message when a ModuleNotFoundError occurs is a good practice for debugging and understanding why a module failed to import.

  • 16-32: The modification of sys.path is correctly handled within a try block, and the original path is restored in the finally block, which is good practice to avoid side effects on the module search path.

phlop/run/test_cases.py (3)
  • 14-21: The addition of the --cmake flag and --filter option to the argument parser is correctly implemented. Ensure that the help descriptions are added for these new arguments for better user guidance.

  • 40-45: The function get_test_cases is correctly refactored to handle the new --cmake flag. Ensure that the pp.load_cmake_tests function is implemented and correctly handles the loading of CMake tests.

  • 51-53: The refactored call to pp.process correctly passes the n_cores and print_only arguments. Ensure that the pp.process function signature is updated to accept these named parameters.

phlop/testing/parallel_processor.py (4)
  • 12-30: The CallableTest class has been refactored to use a test_case object, which is a good approach for encapsulating test case data. Ensure that all instances where CallableTest is instantiated have been updated to pass a test_case object.

  • 45-48: The print_tests function correctly accesses the cmd attribute from the test object. This change aligns with the new structure of test cases.

  • 51-55: The early return in the process function when there are no tests to run is a good practice to avoid unnecessary processing.

  • 103-110: The waiter function correctly handles the test case execution status and prints the command and status. It also properly manages the core availability and increments the finished counter for each batch. This is a good use of resources and provides clear feedback on test execution.

phlop/testing/test_cases.py (8)
  • 14-19: The TestCase dataclass is well-defined with fields for command, environment, working directory, and log file path. This should facilitate the representation of test cases.

  • 22-26: The TestBatch class is a simple encapsulation for a batch of test cases, which should be useful for handling multiple test cases together.

  • 28-39: The DefaultTestCaseExtractor class returns a list with a single TestCase instance. Ensure this aligns with the intended use case, as it may need to handle multiple test cases if a single CTest test can correspond to more than one TestCase.

  • 64-68: The EXTRACTORS list is correctly populated with instances of the test case extractor classes. This should allow for easy extension with additional extractors in the future.

  • 71-72: The python3_default_test_cmd function is a concise way to generate a default test command for Python 3 unit tests.

  • 75-90: The load_test_cases_in function correctly loads test cases from classes and allows for an optional test command function and log file path. This should provide flexibility in test case loading.

  • 93-105: The load_test_cases_from_cmake function modifies the PYTHONPATH to include the test's working directory, which is important for Python tests to locate necessary modules. This seems to be a well-thought-out approach.

  • 112-133: The load_cmake_tests function correctly loads CMake tests as a batch and uses the EXTRACTORS list to process each test. This should allow for flexible and extensible test case extraction.

tests/all_concurrent.py (1)
  • 10-26: The changes to the import statement and function calls are consistent with the refactoring described in the pull request summary. Ensure that the pp.TestBatch class and the pp.load_test_cases_in function are correctly defined in the parallel_processor module. The code is clean and follows best practices.

phlop/app/perf.py Outdated Show resolved Hide resolved
@@ -7,8 +7,13 @@ set -e

PY_FILES=$(find . -name "*.py")
Copy link

@coderabbitai coderabbitai bot Nov 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PY_FILES variable is no longer necessary since black and isort are applied to directories and not individual files. Consider removing this variable to clean up the script.

- PY_FILES=$(find . -name "*.py")

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
PY_FILES=$(find . -name "*.py")

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

autoflake is per file

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PhilipDeegan You're correct, autoflake does require the file list for its operation. The PY_FILES variable should be retained for that purpose. Thank you for pointing that out.


If you found the review helpful, we would love if you give us a shout-out on X (twitter).

Comment on lines 7 to 19

PY_FILES=$(find . -name "*.py")

python3 -m black phlop tests
pylint --errors-only phlop tests
isort phlop tests

for FILE in ${PY_FILES[@]}; do
python3 -m black "$FILE"

autoflake -i "$FILE"
isort "$FILE"


done
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes to the linting script are appropriate and align with the summary provided. The script now applies black, pylint, and isort to the entire directories, which is more efficient than the previous approach of processing individual files. However, the PY_FILES variable is now redundant.

phlop/proc.py Outdated Show resolved Hide resolved
phlop/proc.py Outdated Show resolved Hide resolved
phlop/testing/test_cases.py Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 9

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 837bb1c and 3765fa3.
Files selected for processing (14)
  • ISSUES.md (1 hunks)
  • phlop/app/init.py (1 hunks)
  • phlop/app/cmake.py (1 hunks)
  • phlop/app/git.py (1 hunks)
  • phlop/app/perf.py (1 hunks)
  • phlop/os.py (2 hunks)
  • phlop/proc.py (3 hunks)
  • phlop/reflection.py (1 hunks)
  • phlop/run/test_cases.py (2 hunks)
  • phlop/string.py (1 hunks)
  • phlop/testing/parallel_processor.py (3 hunks)
  • phlop/testing/test_cases.py (1 hunks)
  • sh/lint.sh (1 hunks)
  • tests/all_concurrent.py (1 hunks)
Additional comments: 29
ISSUES.md (1)
  • 5-6: The information provided in ISSUES.md is accurate and relevant for developers working with Python 3.8. It's important to note that the end of life for Python 3.8 is mentioned as October 2024, which is correct based on the knowledge cutoff date.
phlop/app/__init__.py (1)
  • 1-1: The hunk shows an empty file, which suggests that all previous content has been removed. If this is intentional and all functionality has been refactored into other modules, ensure that there are no dangling references to the removed functions in the rest of the codebase.
phlop/app/cmake.py (3)
  • 42-42: Skip commenting on the working_dir field default value as this issue has been previously raised.

  • 70-87: The implementation of the list_tests function to parse JSON and create CTest_test instances looks correct.

  • 90-94: The implementation of the test_cmd function to generate a test command based on the provided test name and verbosity looks correct.

phlop/app/git.py (4)
  • 26-32: The checkout function has been simplified to remove the recreate logic. Ensure that all calls to this function have been updated to remove the recreate argument if it was previously used.

  • 26-32: The checkout function's logic is correct, but it's important to ensure that the create parameter is being used correctly in all calls to this function since the recreate functionality has been removed.

  • 26-32: The checkout function now directly checks if the branch exists and creates it if specified. This change simplifies the function and removes potentially dangerous logic that could delete and recreate branches.

  • 26-32:

phlop/app/perf.py (1)
  • 12-12: The code will raise an AttributeError because lists do not have a split method. The string should be split by spaces first and then each part should be split by ".".
-    return [int(digit) for digit in proc.split(" ").split(".")]
+    return [int(digit) for part in proc.split(" ") for digit in part.split(".") if digit.isdigit()]
phlop/proc.py (3)
  • 34-35: The file descriptors for stdout and stderr are opened but not explicitly closed, which can lead to resource leaks. Consider using a context manager to ensure that files are properly closed.

  • 56-62: The run method only catches subprocess.CalledProcessError. Consider catching a more general exception (e.g., Exception) to handle unexpected errors that may occur during the subprocess execution.

  • 124-125: The binary_exists_on_path function raises a ValueError with a message that seems to be a placeholder. Ensure that this is intentional and consider implementing a proper check or removing the placeholder if it's not needed.

phlop/reflection.py (4)
  • 3-3: The addition of the logging module is appropriate for the new logging functionality introduced in the classes_in_file function.

  • 9-9: The addition of the fail_on_import_error parameter to the classes_in_file function allows for better control over the function's behavior in case of import errors. Ensure that all calls to this function have been updated to handle the new parameter.

  • 27-30: The exception handling for ModuleNotFoundError with an option to log the error and continue is a good addition. Ensure that the logging level is set appropriately elsewhere in the application to capture these error messages.

  • 16-32: The modification and restoration of sys.path are handled correctly within a try-finally block, ensuring that the original path is restored even if an exception occurs.

phlop/run/test_cases.py (4)
  • 1-2: The new imports of multiprocessing and os are used in the code, which is good for modularity and maintainability.

  • 14-18: The addition of the --cmake flag and --filter option to the argument parser is consistent with the summary indicating support for CMake and test filtering.

  • 40-45: The get_test_cases function has been correctly updated to handle the --cmake flag, which aligns with the summary's mention of CMake support.

  • 51-52: The refactoring to use pp.process and pp.load_test_cases_in is consistent with the changes in the parallel_processor module structure.

phlop/testing/parallel_processor.py (4)
  • 13-28: The changes to the CallableTest class constructor and the __call__ method to use test_case attributes are consistent with the summary and seem logically correct.

  • 45-48: The update to the print_tests function to print test.cmd is consistent with the summary and seems logically correct.

  • 51-55: The early return in the process function when there are no tests to run is a good addition for performance, as it avoids unnecessary processing.

  • 103-107: The changes to the waiter function to access cmd attribute from proc.test_case and the handling of test completion status are consistent with the summary and seem logically correct.

phlop/testing/test_cases.py (1)
  • 1-133: The changes in the file seem to be correctly implemented with no apparent issues in the logic or structure of the code. The use of dataclasses, context managers, and default parameters are all good practices in Python.
sh/lint.sh (2)
  • 8-8: The PY_FILES variable is no longer necessary and should be removed to clean up the script.

  • 14-19: The loop correctly applies autoflake to individual files, which is consistent with the summary provided.

tests/all_concurrent.py (1)
  • 10-26: The changes to the import statements and function calls are consistent with the new module structure and usage of the parallel_processor module. The use of the alias pp is clear and the updated function calls reflect this change. The refactoring to use pp.TestBatch in the process function call is also consistent with the summary provided.

phlop/string.py Outdated Show resolved Hide resolved
phlop/os.py Outdated Show resolved Hide resolved
phlop/os.py Outdated Show resolved Hide resolved
Comment on lines 1 to 30
import time
import unittest
from multiprocessing import Process, Queue, cpu_count

from phlop.proc import ProcessNonZeroExitCode, run
from phlop.testing.test_cases import *


class TestCaseFailure(Exception):
...


def test_cmd(clazz, test_id, cores):
return f"python3 -m {clazz.__module__} {clazz.__name__}.{test_id}"


class TestBatch:
def __init__(self, tests, cores=1):
self.tests = tests
self.cores = cores


def load_test_cases_in(classes, cores=1, test_cmd_fn=None):
test_cmd_fn = test_cmd_fn if test_cmd_fn else test_cmd

tests, loader = [], unittest.TestLoader()
for test_class in classes:
for suite in loader.loadTestsFromTestCase(test_class):
tests += [test_cmd_fn(type(suite), suite._testMethodName, cores)]

return TestBatch(tests, cores)


class CallableTest:
def __init__(self, batch_index, cmd):
def __init__(self, batch_index, test_case):
self.batch_index = batch_index
self.cmd = cmd
self.test_case = test_case
self.run = None

def __call__(self, **kwargs):
self.run = run(
self.cmd.split(),
self.test_case.cmd.split(),
shell=False,
capture_output=True,
check=True,
print_cmd=False,
env=self.test_case.env,
working_dir=self.test_case.working_dir,
log_file_path=self.test_case.log_file_path,
)
if self.run.exitcode != 0:
print(self.run.stderr)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This review was outside the patches, and no patch overlapping with it was found. Original lines [31-31]

Typo in the CoreCount class attribute name.

-        self.proces = []
+        self.procs = []

phlop/proc.py Outdated Show resolved Hide resolved
phlop/run/test_cases.py Outdated Show resolved Hide resolved
Comment on lines 26 to 32
return True


def checkout(branch, create=False, recreate=False):
if recreate:
delete_branch(branch)
create = True

def checkout(branch, create=False):
if create and not branch_exists(branch):
run(f"git checkout -b {branch}", check=True)
else:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using git rev-parse --verify instead of git show-branch for checking branch existence, as it is more commonly used and might be more performant.

def branch_exists(branch):
    try:
-       run(f"git show-branch {branch}", check=True)
+       run(f"git rev-parse --verify {branch}", check=True)
    except subprocess.CalledProcessError as e:
        return False  # exit failure means branch does not exist
    return True

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
return True
def checkout(branch, create=False, recreate=False):
if recreate:
delete_branch(branch)
create = True
def checkout(branch, create=False):
if create and not branch_exists(branch):
run(f"git checkout -b {branch}", check=True)
else:
return True
def branch_exists(branch):
try:
run(f"git rev-parse --verify {branch}", check=True)
except subprocess.CalledProcessError as e:
return False # exit failure means branch does not exist
return True
def checkout(branch, create=False):
if create and not branch_exists(branch):
run(f"git checkout -b {branch}", check=True)
else:

Comment on lines +12 to +17
def make_config_str(path, cxx_flags=None, use_ninja=False, use_ccache=False, extra=""):
cxx_flags = "" if cxx_flags is None else f'-DCMAKE_CXX_FLAGS="{cxx_flags}"'
ccache = "" if use_ccache is False else "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache"
ninja = "" if not use_ninja else "-G Ninja"

return f"cmake {path} {cxx_flags} {ninja} {ccache} {extra}"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The extra parameter is directly interpolated into the command string. Ensure that any input passed to this parameter is properly sanitized to prevent command injection vulnerabilities.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 3

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 837bb1c and e22592d.
Files selected for processing (14)
  • ISSUES.md (1 hunks)
  • phlop/app/init.py (1 hunks)
  • phlop/app/cmake.py (1 hunks)
  • phlop/app/git.py (1 hunks)
  • phlop/app/perf.py (1 hunks)
  • phlop/os.py (2 hunks)
  • phlop/proc.py (3 hunks)
  • phlop/reflection.py (1 hunks)
  • phlop/run/test_cases.py (2 hunks)
  • phlop/string.py (1 hunks)
  • phlop/testing/parallel_processor.py (3 hunks)
  • phlop/testing/test_cases.py (1 hunks)
  • sh/lint.sh (1 hunks)
  • tests/all_concurrent.py (1 hunks)
Files skipped from review due to trivial changes (1)
  • sh/lint.sh
Additional comments: 37
ISSUES.md (1)
  • 5-6: The information provided is accurate and relevant for developers working with Python 3.8. It's important to ensure that the codebase does not use features that are not supported in Python 3.8 until the project drops support for this version.
phlop/app/__init__.py (1)
  • 1-1: The hunk is empty, indicating that there may have been a removal of code. Ensure that any code that depended on the removed functions has been updated or refactored accordingly.
phlop/app/cmake.py (3)
  • 20-21: The extra parameter is not being passed to make_config_str from the config function. This should be corrected to ensure that the extra parameter is used when generating the command string.
- cmd = make_config_str(path, cxx_flags, use_ninja, extra)
+ cmd = make_config_str(path, cxx_flags, use_ninja, use_ccache, extra)
  • 42-42: The working_dir field has a default factory that returns None, which may lead to type inconsistencies. Consider changing the default to an empty string or handling None values appropriately where working_dir is used.

  • 12-17: The extra parameter is directly interpolated into the command string. Ensure that any input passed to this parameter is properly sanitized to prevent command injection vulnerabilities.

phlop/app/git.py (2)
  • 26-32: The checkout function has been correctly updated to remove the recreate logic, and now it only handles branch creation if the create flag is set and the branch does not exist. Ensure that all calls to this function have been updated to remove any recreate parameter that may have been passed previously.

  • 26-26: Skip generating a comment about the branch_exists function as the previous comment is still valid.

phlop/app/perf.py (2)
  • 9-12: The change in the return statement assumes that the version number is always at the end of the output. Verify that this assumption holds true for all versions of perf that the application supports. If the output format of perf -v is not consistent across versions, this could lead to incorrect parsing of the version number.

  • 6-15: The rest of the code in the provided file appears to be unchanged and is not part of the review.

phlop/os.py (4)
  • 22-22:

  • 35-39:

  • 24-25: The addition of a check for the existence of new_cwd before changing the directory is a good practice for error handling and ensures that the function fails early if the directory does not exist.

  • 37-39: The addition of creating parent directories if they do not exist before writing to the file is a useful feature that enhances the robustness of the write_to_file function.

phlop/proc.py (4)
  • 17-17: The env parameter should not be initialized with a mutable default argument (empty dict). Use None and then initialize it inside the method.
-        env: dict = {},  # dict[str, str] # eventually
+        env: dict = None,  # dict[str, str] # eventually
+        if env is None:
+            env = {}
  • 34-35: The file descriptors for stdout and stderr are opened but not explicitly closed, which can lead to resource leaks. Consider using a context manager to ensure that files are properly closed.
-                    stdout=open(f"{log_file_path}.stdout", "w"),
-                    stderr=open(f"{log_file_path}.stderr", "w"),
+                    stdout=open(f"{log_file_path}.stdout", "w").__enter__(),
+                    stderr=open(f"{log_file_path}.stderr", "w").__enter__(),
  • 56-62: The run method only catches subprocess.CalledProcessError. Consider catching a more general exception (e.g., Exception) to handle unexpected errors that may occur during the subprocess execution.

  • 124-125: The binary_exists_on_path function raises a ValueError with a message that seems to be a placeholder. Ensure that this is intentional and consider implementing a proper check or removing the placeholder if it's not needed.

phlop/reflection.py (4)
  • 3-3: The addition of the logging module is appropriate for the new error logging functionality.

  • 9-9: Ensure that all calls to classes_in_file throughout the codebase are updated to handle the new fail_on_import_error parameter.

  • 27-30: The error handling and logging for ModuleNotFoundError are implemented correctly. Verify that the logging level is appropriately set elsewhere in the application to capture this error message.

  • 16-32: The use of a try-finally block to ensure sys.path is restored to its original state is good practice and prevents potential side effects on the module import system.

phlop/run/test_cases.py (5)
  • 1-2: The new imports multiprocessing and os are used appropriately in the file.

  • 14-21: The argument parser has been updated with new arguments and defaults. Ensure that all new arguments are documented and used correctly throughout the codebase.

  • 27-36: The verify_cli_args function has been updated to handle the --dir argument and check for directory existence. Ensure that the changes are compatible with the rest of the codebase and that the error messages are clear and informative.

  • 40-45: The get_test_cases function has been updated to handle CMake tests. Ensure that the new functionality is tested and that the function integrates well with the existing testing framework.

  • 48-53: The main function has been updated to incorporate the new argument verification and test case retrieval logic. Ensure that the changes are consistent with the expected program flow and that the new features are properly integrated.

phlop/string.py (1)
  • 1-6: The function now handles strings and bytes correctly and raises a ValueError for unknown types. However, consider if ignoring errors during decoding is the desired behavior, as it may lead to data loss or unexpected results.
phlop/testing/parallel_processor.py (1)
  • 12-30: The changes to the CallableTest class to store test_case instead of cmd and the corresponding updates to the __call__ method are consistent with the summary and appear to be correctly implemented.
phlop/testing/test_cases.py (8)
  • 14-19: The TestCase dataclass is well-defined and includes default factories for optional fields.

  • 22-26: The TestBatch class is a straightforward container for test cases and the number of cores, which is good for batch processing.

  • 28-38: The DefaultTestCaseExtractor class correctly creates a TestCase instance, but ensure that the log file path concatenation handles edge cases, such as when _LOG_DIR is the root directory.

  • 64-68: The EXTRACTORS list is correctly populated with instances of the test case extractor classes.

  • 71-72: The python3_default_test_cmd function is a simple and effective way to generate a default test command for Python 3 test cases.

  • 75-90: The load_test_cases_in function is well-implemented, providing a way to load test cases from classes and allowing for a custom test command function.

  • 93-105: The load_test_cases_from_cmake function correctly sets up the environment and loads test cases from a CMake test, using the classes_in_file function to find test classes.

  • 108-129: The load_cmake_tests function is implemented to load CMake tests and extract test cases using the defined extractors. It returns a TestBatch which is good for batch processing.

tests/all_concurrent.py (1)
  • 10-26: The changes to the import statements and function calls are consistent with the summary provided. The use of the pp alias for parallel_processor is clear and the updated directory path for locating test classes is correct.

Comment on lines 103 to +107
fail += proc.run.exitcode
if fail_fast and fail > 0:
raise TestCaseFailure("Some tests have failed")
print(proc.cmd, f"{status} in {proc.run.run_time:.2f} seconds")
print(
proc.test_case.cmd, f"{status} in {proc.run.run_time:.2f} seconds"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The waiter function should be updated to access the cmd attribute from the test_case attribute of the proc object, as the direct cmd attribute on proc no longer exists.

-                    proc.test_case.cmd, f"{status} in {proc.run.run_time:.2f} seconds"
+                    proc.test_case.test_case.cmd, f"{status} in {proc.run.run_time:.2f} seconds"

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
fail += proc.run.exitcode
if fail_fast and fail > 0:
raise TestCaseFailure("Some tests have failed")
print(proc.cmd, f"{status} in {proc.run.run_time:.2f} seconds")
print(
proc.test_case.cmd, f"{status} in {proc.run.run_time:.2f} seconds"
fail += proc.run.exitcode
if fail_fast and fail > 0:
raise TestCaseFailure("Some tests have failed")
print(
proc.test_case.test_case.cmd, f"{status} in {proc.run.run_time:.2f} seconds"

Repository owner deleted a comment from coderabbitai bot Dec 9, 2023
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 5

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between e22592d and 654d151.
Files ignored due to filter (1)
  • pyproject.toml
Files selected for processing (7)
  • phlop/main.py (1 hunks)
  • phlop/app/main.py (1 hunks)
  • phlop/os.py (2 hunks)
  • phlop/run/main.py (1 hunks)
  • phlop/run/test_cases.py (1 hunks)
  • phlop/testing/main.py (1 hunks)
  • setup.py (1 hunks)
Files skipped from review due to trivial changes (3)
  • phlop/app/main.py
  • phlop/run/main.py
  • setup.py
Additional comments: 7
phlop/__main__.py (1)
  • 1-14: The list of modules printed by _STR_ should be verified to ensure it is up-to-date with the modules currently available in the project, especially after the refactor mentioned in the PR objectives and AI-generated overview.
Verification successful

The list of modules in _STR_ within phlop/__main__.py is consistent with the current structure of the phlop directory. There are no new top-level modules missing from the list, and the submodules within phlop.app, phlop.run, and phlop.testing do not need to be listed separately. The list is up-to-date with the modules available in the project.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# List all Python modules in the phlop directory to verify against the _STR_ variable.
fd --extension py --base-directory phlop

Length of output: 295

phlop/os.py (2)
  • 2-3: The addition of contextlib and Path imports aligns with the new functionality introduced in the pushd and write_to_file functions.

  • 22-23: The added directory existence check in the pushd function is a good practice to prevent runtime errors. Ensure that the error message is clear and actionable for the user.

phlop/run/test_cases.py (4)
  • 10-34: The cli_args_parser function has been updated with new arguments and default values, which aligns with the PR objectives. Ensure that these new arguments are correctly utilized in the rest of the codebase.

  • 40-40: The conversion of cli_args.cores to an integer is redundant since the --cores argument is already defined as an integer type in the argument parser. This should be removed to avoid unnecessary code.

-    cli_args.cores = int(cli_args.cores)
  • 51-56: The get_test_cases function has been introduced with conditional logic based on the cmake flag. This change should be cross-verified with the rest of the codebase to ensure that it integrates properly and that the cmake flag is being handled as expected.

  • 59-70: The main function has been refactored to utilize the new argument parsing and processing functions. Ensure that the error handling is appropriate and that the parsed arguments are correctly handled throughout the function.

phlop/testing/__main__.py Outdated Show resolved Hide resolved
phlop/testing/__main__.py Outdated Show resolved Hide resolved
phlop/run/test_cases.py Outdated Show resolved Hide resolved
phlop/os.py Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between e22592d and cd0a6d0.
Files ignored due to filter (1)
  • pyproject.toml
Files selected for processing (7)
  • phlop/main.py (1 hunks)
  • phlop/app/main.py (1 hunks)
  • phlop/os.py (2 hunks)
  • phlop/run/main.py (1 hunks)
  • phlop/run/test_cases.py (1 hunks)
  • phlop/testing/main.py (1 hunks)
  • setup.py (1 hunks)
Files skipped from review due to trivial changes (3)
  • phlop/main.py
  • phlop/app/main.py
  • setup.py
Files skipped from review as they are similar to previous changes (4)
  • phlop/os.py
  • phlop/run/main.py
  • phlop/run/test_cases.py
  • phlop/testing/main.py

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 14

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between e22592d and 5306dd0.
Files ignored due to filter (1)
  • pyproject.toml
Files selected for processing (10)
  • phlop/main.py (1 hunks)
  • phlop/app/main.py (1 hunks)
  • phlop/app/cmake.py (1 hunks)
  • phlop/dict.py (1 hunks)
  • phlop/os.py (2 hunks)
  • phlop/run/main.py (1 hunks)
  • phlop/run/test_cases.py (1 hunks)
  • phlop/testing/main.py (1 hunks)
  • phlop/testing/test_cases.py (1 hunks)
  • setup.py (1 hunks)
Files skipped from review due to trivial changes (3)
  • phlop/main.py
  • phlop/app/main.py
  • setup.py
Files skipped from review as they are similar to previous changes (3)
  • phlop/os.py
  • phlop/run/main.py
  • phlop/testing/main.py
Additional comments: 2
phlop/app/cmake.py (1)
  • 8-9: The version function is empty. Please verify if this is intentional or if the implementation is missing.
phlop/run/test_cases.py (1)
  • 12-12: The function parse_cli_args has been renamed to cli_args_parser. Verify that all references to the old function name have been updated across the codebase.
Verification successful

The search for the old function name parse_cli_args across the codebase did not yield any results. This suggests that there are no remaining references to the old function name, which aligns with the changes made in the pull request.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Search for the old function name usage across the codebase. Expecting no results.
rg --type python 'parse_cli_args\('

Length of output: 68



Script:

#!/bin/bash
# Search for the old function name usage across the codebase without specifying the file type.
rg 'parse_cli_args\('

Length of output: 23

Comment on lines +8 to +12
class ValDict(dict):
def __init__(self, **d):
for k, v in d.items():
object.__setattr__(self, k, v)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ValDict class sets dictionary keys as attributes, which could lead to conflicts if keys overlap with dict method names. Consider adding a check to prevent setting attributes that conflict with dict methods or built-in attributes.


The ValDict class does not handle updates to the dictionary after initialization. Consider implementing __setitem__ and __delitem__ to synchronize changes to the dictionary with the instance attributes.


The ValDict class does not override the __getattr__ method, which means accessing non-existent attributes that correspond to dictionary keys will not return the dictionary value. Consider implementing __getattr__ to provide consistent access to dictionary items as attributes.

Comment on lines +9 to +12
def __init__(self, **d):
for k, v in d.items():
object.__setattr__(self, k, v)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The __init__ method lacks error handling for invalid key names that cannot be set as attributes. Consider adding validation for key names to ensure they are valid attribute identifiers.

phlop/run/test_cases.py Outdated Show resolved Hide resolved
Comment on lines +34 to +44
working_dir=ctest_test.working_dir,
log_file_path=_LOG_DIR
/ ".phlop"
/ f"{Path(ctest_test.working_dir).relative_to(_LOG_DIR)}",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential issue with path operations.

-                log_file_path=_LOG_DIR
-                / ".phlop"
-                / f"{Path(ctest_test.working_dir).relative_to(_LOG_DIR)}",

Ensure that ctest_test.working_dir is always a subdirectory of _LOG_DIR to avoid ValueError from Path.relative_to(). If not, consider adding error handling or validation.


Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
working_dir=ctest_test.working_dir,
log_file_path=_LOG_DIR
/ ".phlop"
/ f"{Path(ctest_test.working_dir).relative_to(_LOG_DIR)}",
working_dir=ctest_test.working_dir,

Comment on lines +42 to +60
class GoogleTestCaseExtractor:
def __call__(self, ctest_test):
...
# not configured, assumed fast per file
# print("GoogleTestCaseExtractor")
# exec binary with `--gtest_list_tests` and see if it doesn't fail
# p = run(
# ctest_test.cmd + " --gtest_list_tests --gtest_output=json:gtest.json",
# working_dir=ctest_test.working_dir,
# )
# print(p.stdout)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete implementation or dead code.

-        ...
-        # not configured, assumed fast per file
-        # print("GoogleTestCaseExtractor")
-        # exec binary with `--gtest_list_tests` and see if it doesn't fail
-        # p = run(
-        #     ctest_test.cmd + " --gtest_list_tests --gtest_output=json:gtest.json",
-        #     working_dir=ctest_test.working_dir,
-        # )
-        # print(p.stdout)

Clarify the status of GoogleTestCaseExtractor. If this is not intended for use, consider removing the commented-out code to avoid confusion.


Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
class GoogleTestCaseExtractor:
def __call__(self, ctest_test):
...
# not configured, assumed fast per file
# print("GoogleTestCaseExtractor")
# exec binary with `--gtest_list_tests` and see if it doesn't fail
# p = run(
# ctest_test.cmd + " --gtest_list_tests --gtest_output=json:gtest.json",
# working_dir=ctest_test.working_dir,
# )
# print(p.stdout)
class GoogleTestCaseExtractor:
def __call__(self, ctest_test):

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 2

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between e22592d and 3741fc3.
Files ignored due to filter (1)
  • pyproject.toml
Files selected for processing (10)
  • phlop/main.py (1 hunks)
  • phlop/app/main.py (1 hunks)
  • phlop/app/cmake.py (1 hunks)
  • phlop/dict.py (1 hunks)
  • phlop/os.py (2 hunks)
  • phlop/run/main.py (1 hunks)
  • phlop/run/test_cases.py (1 hunks)
  • phlop/testing/main.py (1 hunks)
  • phlop/testing/test_cases.py (1 hunks)
  • setup.py (1 hunks)
Files skipped from review due to trivial changes (6)
  • phlop/main.py
  • phlop/app/main.py
  • phlop/app/cmake.py
  • phlop/dict.py
  • phlop/run/main.py
  • setup.py
Files skipped from review as they are similar to previous changes (2)
  • phlop/testing/main.py
  • phlop/testing/test_cases.py
Additional comments: 6
phlop/os.py (2)
  • 22-23: The addition of a directory existence check in the pushd function is a good practice to prevent runtime errors. Ensure that the function's documentation is updated to reflect this new behavior.

  • 3-3: The import of Path from pathlib is appropriate for the file operations performed in the write_to_file function.

phlop/run/test_cases.py (4)
  • 11-11: The function parse_cli_args has been renamed to cli_args_parser. Verify that all references to this function have been updated accordingly.
Verification successful

The shell script results indicate that the old function name parse_cli_args is no longer present in any Python files within the codebase. The new function name cli_args_parser is found in phlop/run/test_cases.py, which is consistent with the changes described in the PR. There are no references to the old function name, suggesting that the necessary updates to function calls have been made.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Search for the old function name to ensure it's no longer used.
rg --type python 'parse_cli_args'
# Search for the new function name to ensure it's being used correctly.
rg --type python 'cli_args_parser'

Length of output: 130



Script:

#!/bin/bash
# Search for the old function name to ensure it's no longer used.
rg 'parse_cli_args' --glob '*.py'
# Search for the new function name to ensure it's being used correctly.
rg 'cli_args_parser' --glob '*.py'

Length of output: 170

* 48-59: The `get_test_cases` function has been updated to handle CMake test cases when the `cmake` flag is set. Ensure that the `pp.load_cmake_tests` and `pp.TestBatch` functions are implemented correctly and handle the new arguments.
Verification successful

The rg (ripgrep) search has successfully located the load_cmake_tests function and the TestBatch class within a file named phlop/testing/test_cases.py. This confirms the existence of both entities in the codebase, which aligns with the changes mentioned in the PR review.

Based on this information, it can be concluded that the get_test_cases function in phlop/run/test_cases.py is likely to be correctly implemented, as it references the load_cmake_tests function and the TestBatch class which do exist in the codebase.

Given this confirmation, the original approval with a verification request can be upheld, and no further action is needed regarding the implementation of these entities.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify the implementation of pp.load_cmake_tests function.
ast-grep --lang python --pattern $'def load_cmake_tests($_, $_, $_)'
# Verify the implementation of pp.TestBatch class.
ast-grep --lang python --pattern $'class TestBatch { $$$ }'

Length of output: 921



Script:

#!/bin/bash
# Simplify the pattern for the load_cmake_tests function to increase the chance of a match.
ast-grep --lang python --pattern $'def load_cmake_tests($_, $_, $_)'

# Correct the pattern syntax for the TestBatch class to match the class definition.
ast-grep --lang python --pattern $'class TestBatch:'

Length of output: 468



Script:

#!/bin/bash
# Use ripgrep to search for the load_cmake_tests function and TestBatch class within Python files.
rg --type py 'def load_cmake_tests'
rg --type py 'class TestBatch'

Length of output: 220

* 62-82: The `main` function has been refactored to use the new `cli_args_parser` and `verify_cli_args` functions. Ensure that the refactoring does not introduce any issues with how arguments are parsed and processed.
Verification successful

The output from the rg command provides the context needed to verify the usage of cli_args_parser and verify_cli_args within the main function of the phlop/run/test_cases.py file. The code snippet shows that cli_args_parser is defined and returns a parser object. In the main function, this parser object is used to parse the command-line arguments, and the resulting cli_args is then passed to verify_cli_args for further verification.

The sequence of operations in the main function is as follows:

  1. parser = cli_args_parser() initializes the argument parser.
  2. cli_args = verify_cli_args(parser.parse_args()) parses the command-line arguments and then verifies them.

This sequence matches the intended refactoring described in the review comment and the AI-generated summary of changes. There are no apparent issues with how arguments are parsed and processed based on the provided code snippet.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify the correct usage of cli_args_parser and verify_cli_args within the main function.
ast-grep --lang python --pattern $'def main() {
  $$$
  cli_args_parser($_)
  $$$
  verify_cli_args($_)
  $$$
}'

Length of output: 458



Script:

#!/bin/bash
# Refine the pattern to correctly match the usage of cli_args_parser and verify_cli_args within the main function.
ast-grep --lang python --pattern $'def main() {
  $_ = cli_args_parser()
  $$$
  $_ = verify_cli_args($_.parse_args())
  $$$
}'

Length of output: 473



Script:

#!/bin/bash
# Use ripgrep to search for the usage of cli_args_parser and verify_cli_args within the phlop/run/test_cases.py file.
rg --context 5 'cli_args_parser|verify_cli_args' phlop/run/test_cases.py

Length of output: 1052

* 80-82: Catching all exceptions is not recommended as it can obscure the actual error. Consider catching specific exceptions to provide more detailed error information and handling.

Comment on lines +33 to +47
def write_to_file(file, contents, mode="w", skip_if_empty=True):
if contents or not skip_if_empty:
try:
Path(file).parent.mkdir(parents=True, exist_ok=True)
with open(file, mode) as f:
f.write(contents)
except IOError as e:
raise RuntimeError(f"Failed to write to file {file}: {e}")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The write_to_file function should be documented to explain its parameters and behavior, especially the skip_if_empty flag. Additionally, consider adding a check for the validity of the mode parameter to ensure it's a valid file opening mode.

+    """
+    Writes contents to a file.
+
+    Parameters:
+    - file: The file path where contents will be written.
+    - contents: The content to write to the file.
+    - mode: The mode in which the file is opened, e.g., 'w' for write.
+    - skip_if_empty: If True, writing is skipped when contents are empty.
+
+    Raises:
+    - RuntimeError: If the file cannot be written.
+    """

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def write_to_file(file, contents, mode="w", skip_if_empty=True):
if contents or not skip_if_empty:
try:
Path(file).parent.mkdir(parents=True, exist_ok=True)
with open(file, mode) as f:
f.write(contents)
except IOError as e:
raise RuntimeError(f"Failed to write to file {file}: {e}")
def write_to_file(file, contents, mode="w", skip_if_empty=True):
"""
Writes contents to a file.
Parameters:
- file: The file path where contents will be written.
- contents: The content to write to the file.
- mode: The mode in which the file is opened, e.g., 'w' for write.
- skip_if_empty: If True, writing is skipped when contents are empty.
Raises:
- RuntimeError: If the file cannot be written.
"""
if contents or not skip_if_empty:
try:
Path(file).parent.mkdir(parents=True, exist_ok=True)
with open(file, mode) as f:
f.write(contents)
except IOError as e:
raise RuntimeError(f"Failed to write to file {file}: {e}")

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 5

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between e22592d and 7ab09ba.
Files ignored due to filter (1)
  • pyproject.toml
Files selected for processing (12)
  • phlop/main.py (1 hunks)
  • phlop/app/main.py (1 hunks)
  • phlop/app/cmake.py (1 hunks)
  • phlop/dict.py (1 hunks)
  • phlop/logger.py (1 hunks)
  • phlop/os.py (2 hunks)
  • phlop/reflection.py (1 hunks)
  • phlop/run/main.py (1 hunks)
  • phlop/run/test_cases.py (1 hunks)
  • phlop/testing/main.py (1 hunks)
  • phlop/testing/test_cases.py (1 hunks)
  • setup.py (1 hunks)
Files skipped from review due to trivial changes (5)
  • phlop/main.py
  • phlop/app/main.py
  • phlop/dict.py
  • phlop/run/main.py
  • setup.py
Files skipped from review as they are similar to previous changes (4)
  • phlop/app/cmake.py
  • phlop/os.py
  • phlop/testing/main.py
  • phlop/testing/test_cases.py
Additional comments: 6
phlop/logger.py (1)
  • 1-10: The changes in phlop/logger.py do not appear to be directly related to the PR's stated objectives of enhancing CMake and CTest integration. Ensure that these changes are intentional and relevant to the PR.
phlop/reflection.py (2)
  • 9-9: Ensure that documentation and tests are updated to reflect the new fail_on_import_error parameter in the classes_in_file function.

  • 27-30: The new error handling behavior based on the fail_on_import_error flag should be clearly communicated to users. Additionally, ensure that logging is set up appropriately to capture the error messages when exceptions are not raised.

phlop/run/test_cases.py (3)
  • 20-42: The refactoring and addition of new command-line arguments in cli_args_parser appear to be correctly implemented with appropriate help descriptions.

  • 57-68: The implementation of conditional logic in get_test_cases based on the cmake flag is consistent with the PR's objectives to handle CMake test cases.

  • 80-82: Catching SystemExit exceptions separately from other exceptions is a good practice, as it allows for different handling of normal exit scenarios versus unexpected exceptions.

Comment on lines 16 to 38
old_path = sys.path
try:
sys.path += [os.getcwd()]
for name, cls in inspect.getmembers(
importlib.import_module(module), inspect.isclass
):
should_add = subclasses_only == None or any(
[issubclass(cls, sub) for sub in subclasses_only]
)
if should_add:
classes += [cls]
except (ValueError, ModuleNotFoundError) as e:
if fail_on_import_error:
raise e
logging.error(f"Skipping on error: {e} in module {module}")
finally:
sys.path = old_path
Copy link

@coderabbitai coderabbitai bot Dec 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The modification of sys.path is a global operation that could have unintended side effects. It's correctly reverted in the finally block, but consider using a context manager to ensure sys.path is restored even if an unexpected exception occurs.

from contextlib import contextmanager

@contextmanager
def extend_sys_path(path):
    old_path = sys.path[:]
    sys.path.append(path)
    try:
        yield
    finally:
        sys.path = old_path

# Usage within the function
with extend_sys_path(os.getcwd()):
    # ... import module and get classes ...

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PhilipDeegan, I'm glad you found the suggestion helpful! 😊

(\(\ 
( -.-)
o_(")(")

If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

)
if should_add:
classes += [cls]
old_path = sys.path
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assignment old_path = sys.path should be changed to old_path = sys.path[:] to ensure that a copy of the original sys.path is made. This prevents the finally block from restoring sys.path to a modified state if it's changed elsewhere after the assignment.

- old_path = sys.path
+ old_path = sys.path[:]

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
old_path = sys.path
old_path = sys.path[:]


import logging

logging.basicConfig(level=logging.DEBUG)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logging level is set to DEBUG globally, which could expose sensitive information in production. Consider making the logging level configurable.

- logging.basicConfig(level=logging.DEBUG)
+ logging_level = os.getenv('LOGGING_LEVEL', 'WARNING').upper()
+ logging.basicConfig(level=getattr(logging, logging_level))

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
logging.basicConfig(level=logging.DEBUG)
logging_level = os.getenv('LOGGING_LEVEL', 'WARNING').upper()
logging.basicConfig(level=getattr(logging, logging_level))

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 21

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between e22592d and 90e5704.
Files ignored due to filter (1)
  • pyproject.toml
Files selected for processing (20)
  • .pylintrc (1 hunks)
  • phlop/main.py (1 hunks)
  • phlop/app/main.py (1 hunks)
  • phlop/app/cmake.py (1 hunks)
  • phlop/app/sleep.py (1 hunks)
  • phlop/app/stats_man.py (1 hunks)
  • phlop/dict.py (1 hunks)
  • phlop/logger.py (1 hunks)
  • phlop/os.py (2 hunks)
  • phlop/proc.py (3 hunks)
  • phlop/procs/init.py (1 hunks)
  • phlop/procs/main.py (1 hunks)
  • phlop/procs/runtimer.py (1 hunks)
  • phlop/reflection.py (1 hunks)
  • phlop/run/main.py (1 hunks)
  • phlop/run/test_cases.py (1 hunks)
  • phlop/testing/main.py (1 hunks)
  • phlop/testing/parallel_processor.py (4 hunks)
  • phlop/testing/test_cases.py (1 hunks)
  • setup.py (1 hunks)
Files skipped from review due to trivial changes (6)
  • phlop/main.py
  • phlop/app/sleep.py
  • phlop/logger.py
  • phlop/procs/main.py
  • phlop/run/main.py
  • setup.py
Files skipped from review as they are similar to previous changes (5)
  • phlop/app/main.py
  • phlop/dict.py
  • phlop/os.py
  • phlop/reflection.py
  • phlop/testing/main.py
Additional comments: 17
.pylintrc (1)
  • 1-2: Please confirm that ignoring type checks for the ValDict class is intentional and that the class's design justifies this exclusion from type checking.
phlop/app/stats_man.py (10)
  • 23-28: The ProcessCaptureInfo data class is a good addition for encapsulating process capture information.

  • 31-46: Refactoring argument parsing into the cli_args_parser function improves modularity and readability.

  • 49-51: The verify_cli_args function is a good addition for separating the concern of argument verification.

  • 82-90: Using a multiprocessing Queue in RuntimeStatsManager for inter-process communication is a good enhancement.

  • 73-79: Updating the capture_now function to collect additional process statistics is a valuable enhancement.

  • 130-143: Reorganizing the main function to use the new argument parsing and process management logic is a good practice.

  • 122-127: The addition of the print_summary function to display process statistics is a good enhancement for debugging and monitoring.

  • 69-71: The bytes_as_mb utility function is a simple and correct implementation for converting bytes to megabytes.

  • 65-66: The signal_handler function is a standard implementation for handling signals in Python.

  • 54-62: The check_pid function is a standard and correct implementation for checking the existence of a Unix PID.

phlop/proc.py (2)
  • 22-26: The run_raw function is a new addition and seems to be implemented correctly. It provides more control over subprocesses by allowing the redirection of input/output streams based on the quiet parameter.

  • 1-10: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [8-19]

Verify that the run function has been updated to accept a list of strings as arguments and a new quiet parameter, as these changes are not visible in the provided hunk.

phlop/procs/__init__.py (1)
  • 1-1: The hunk for phlop/procs/__init__.py is empty, indicating no changes were made. Please confirm if this is intentional or if there was an oversight in the commit.
phlop/run/test_cases.py (3)
  • 20-42: The cli_args_parser function has been updated with new arguments and help descriptions. Ensure that the help descriptions are clear and match the intended functionality of the arguments.

  • 57-68: The get_test_cases function has been refactored to include conditional logic based on the cmake flag. Verify that the logic correctly handles the different scenarios and that the function returns the expected values.

  • 71-85: The main function has been refactored to utilize the new argument parsing and processing functions. Ensure that the new functions are being called correctly and that the overall flow of the main function is maintained.

Comment on lines +20 to +28
def config(path, cxx_flags=None, use_ninja=False, use_ccache=False, extra=""):
cmd = make_config_str(path, cxx_flags, use_ninja, extra)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use_ccache parameter is not being passed to make_config_str from the config function. This should be corrected to ensure that the use_ccache parameter is used when generating the command string.

- cmd = make_config_str(path, cxx_flags, use_ninja, extra)
+ cmd = make_config_str(path, cxx_flags, use_ninja, use_ccache, extra)

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def config(path, cxx_flags=None, use_ninja=False, use_ccache=False, extra=""):
cmd = make_config_str(path, cxx_flags, use_ninja, extra)
def config(path, cxx_flags=None, use_ninja=False, use_ccache=False, extra=""):
cmd = make_config_str(path, cxx_flags, use_ninja, use_ccache, extra)

Comment on lines +96 to +104
if __name__ == "__main__":
list_tests("build")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider allowing the build directory to be specified as a command-line argument to the script for greater flexibility.

Comment on lines +42 to +61
class GoogleTestCaseExtractor:
def __call__(self, ctest_test):
...
# not configured, assumed fast per file
# print("GoogleTestCaseExtractor")
# exec binary with `--gtest_list_tests` and see if it doesn't fail
# p = run(
# ctest_test.cmd + " --gtest_list_tests --gtest_output=json:gtest.json",
# working_dir=ctest_test.working_dir,
# )
# print(p.stdout)

return None
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GoogleTestCaseExtractor class contains commented-out code and returns None. If this is intentional and the class is not yet implemented, consider removing the commented-out code to avoid confusion, or provide an explanation for its presence.

Comment on lines +57 to +68
class PythonUnitTestCaseExtractor:
def __call__(self, ctest_test):
if "python3" in ctest_test.cmd: # hacky
return load_test_cases_from_cmake(ctest_test)
return None
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PythonUnitTestCaseExtractor still uses a simple string check to determine if a test is a Python unit test. Consider implementing a more robust method for identifying Python unit tests to avoid false positives or negatives.

cmd = test_cmd_fn(type(suite), suite._testMethodName)
tests += [
TestCase(
cmd=f"{test_cmd_pre} {cmd} {test_cmd_post}".strip(),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The command construction for test cases still uses string concatenation. Consider using a list and join to construct the command to avoid issues with extra spaces or improper formatting.

- cmd=f"{test_cmd_pre} {cmd} {test_cmd_post}".strip(),
+ cmd=" ".join(filter(None, [test_cmd_pre, cmd, test_cmd_post])),

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
cmd=f"{test_cmd_pre} {cmd} {test_cmd_post}".strip(),
cmd=" ".join(filter(None, [test_cmd_pre, cmd, test_cmd_post])),

Comment on lines 23 to 32
start = time.time()

self.run_time = time.time() - start
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initial calculation of run_time is unnecessary and does not reflect the actual run time of the command. This line can be removed.

- self.run_time = time.time() - start

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
start = time.time()
self.run_time = time.time() - start
start = time.time()

Comment on lines +73 to +82
def out(self, ignore_exit_code=False):
if not ignore_exit_code and self.exitcode > 0:
raise RuntimeError(f"phlop.RunTimer error: {self.stderr}")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The out method raises a generic RuntimeError. Consider creating a custom exception class for better error handling and clarity.

env: dict = {}, # dict[str, str] # eventually
working_dir=None,
log_file_path=None,
**kwargs,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The kwargs and ekwargs are used to pass additional parameters to subprocess.run, but there is no validation or documentation on what can be passed. Consider adding validation or documentation for these parameters.

Comment on lines +67 to +78
if working_dir:
with pushd(working_dir):
run()
else:
run()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pushd context manager is used to change the working directory, but there is no check to ensure that the directory exists or is accessible. Consider adding such a check to prevent runtime errors.


from phlop.os import pushd, write_to_file
from phlop.procs.runtimer import RunTimer
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RunTimer class is imported but according to the summary, it has been removed. Please ensure that the run function is updated accordingly to not use a removed class.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 18

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between e22592d and ee5dbe0.
Files ignored due to filter (1)
  • pyproject.toml
Files selected for processing (25)
  • .pylintrc (1 hunks)
  • phlop/init.py (1 hunks)
  • phlop/main.py (1 hunks)
  • phlop/app/init.py (1 hunks)
  • phlop/app/main.py (1 hunks)
  • phlop/app/cmake.py (1 hunks)
  • phlop/app/git.py (2 hunks)
  • phlop/app/perf.py (2 hunks)
  • phlop/app/sleep.py (1 hunks)
  • phlop/dict.py (1 hunks)
  • phlop/logger.py (1 hunks)
  • phlop/os.py (2 hunks)
  • phlop/proc.py (3 hunks)
  • phlop/procs/init.py (1 hunks)
  • phlop/procs/main.py (1 hunks)
  • phlop/procs/runtimer.py (1 hunks)
  • phlop/reflection.py (1 hunks)
  • phlop/run/main.py (1 hunks)
  • phlop/run/stats_man.py (1 hunks)
  • phlop/run/test_cases.py (1 hunks)
  • phlop/string.py (1 hunks)
  • phlop/testing/main.py (1 hunks)
  • phlop/testing/parallel_processor.py (4 hunks)
  • phlop/testing/test_cases.py (1 hunks)
  • setup.py (1 hunks)
Files skipped from review due to trivial changes (7)
  • .pylintrc
  • phlop/init.py
  • phlop/main.py
  • phlop/app/main.py
  • phlop/app/sleep.py
  • phlop/procs/init.py
  • setup.py
Files skipped from review as they are similar to previous changes (8)
  • phlop/dict.py
  • phlop/logger.py
  • phlop/os.py
  • phlop/proc.py
  • phlop/procs/main.py
  • phlop/reflection.py
  • phlop/run/main.py
  • phlop/testing/main.py
Additional comments: 19
phlop/app/git.py (2)
  • 36-39: The checkout function has been correctly updated to remove the recreate parameter, and the logic has been adjusted to only consider the create parameter.

  • 33-33: The previous suggestion to use git rev-parse --verify instead of git show-branch for checking branch existence has not been implemented.

phlop/app/perf.py (3)
  • 1-6: The addition of empty lines at the beginning of the file seems to be a formatting change and does not affect the functionality.

  • 16-16: Verify that the run function returns an object that has an out method and that this method provides the expected output format for further processing.

  • 19-19: Ensure that the output of the perf -v command is consistent with the parsing logic, specifically that the version number is the last element in the output after splitting by space and then by period.

phlop/procs/runtimer.py (2)
  • 74-78: Using the pushd context manager to change the working directory when provided is a good practice.

  • 70-72: Logging the output to files when log_file_path is provided is a good practice.

phlop/run/stats_man.py (1)
  • 38-53: The cli_args_parser function defines command-line arguments. Ensure that the new arguments introduced are consistent with the PR's objectives and that they are documented and tested.
phlop/run/test_cases.py (1)
  • 22-22: The function parse_cli_args has been renamed to cli_args_parser. Ensure that all references to this function are updated throughout the codebase to reflect the new name.
phlop/testing/parallel_processor.py (4)
  • 19-37: The refactoring of the CallableTest class to accept a test_case object instead of a command string aligns with the PR's objectives to improve CMake and CTest integration.

  • 58-62: The early return in the process function when there are no tests to run is a good performance optimization.

  • 85-91: The simplification of the args parameter in the Process constructor improves code readability.

  • 107-112: The update in the waiter function to print test case information using the test_case attribute is consistent with the refactoring of the CallableTest class.

phlop/testing/test_cases.py (6)
  • 49-61: The previous comment about the GoogleTestCaseExtractor class still applies. The class contains commented-out code and returns None, indicating it is not yet implemented or is a placeholder.

  • 64-68: The previous comment about the PythonUnitTestCaseExtractor class still applies. The class uses a simple string check to determine if a test is a Python unit test, which could lead to false positives or negatives.

  • 93-93: The previous comment about the command construction for test cases still applies. Consider using a list and join to construct the command to avoid issues with extra spaces or improper formatting.

  • 104-105: The previous comment about the load_test_cases_from_cmake function still applies. Ensure that changes to PYTHONPATH are scoped and do not affect other parts of the system or subsequent tests.

  • 134-138: The previous comment about handling the case where no extractors return a result, which could lead to an empty TestBatch, still applies. Consider adding a check after the loop and handling the scenario where test_cases remains empty.

  • 41-44: The previous comment about potential issues with path operations in the DefaultTestCaseExtractor class still applies. Ensure that ctest_test.working_dir is always a subdirectory of _LOG_DIR to avoid ValueError from Path.relative_to().

Comment on lines +27 to +29
def config(path, cxx_flags=None, use_ninja=False, use_ccache=False, extra=""):
cmd = make_config_str(path, cxx_flags, use_ninja, extra)
run(cmd, capture_output=False)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use_ccache parameter is not being passed to make_config_str from the config function. This should be corrected to ensure that the use_ccache parameter is used when generating the command string.

- cmd = make_config_str(path, cxx_flags, use_ninja, extra)
+ cmd = make_config_str(path, cxx_flags, use_ninja, use_ccache, extra)

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def config(path, cxx_flags=None, use_ninja=False, use_ccache=False, extra=""):
cmd = make_config_str(path, cxx_flags, use_ninja, extra)
run(cmd, capture_output=False)
def config(path, cxx_flags=None, use_ninja=False, use_ccache=False, extra=""):
cmd = make_config_str(path, cxx_flags, use_ninja, use_ccache, extra)
run(cmd, capture_output=False)

properties: list = field(default_factory=lambda: [{}]) # list[dict] # eventually

env: dict = field(default_factory=lambda: {}) # dict[str, str] # eventually
working_dir: str = field(default_factory=lambda: None)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The working_dir field still has a default factory that returns None, which may lead to type inconsistencies. Consider changing the default to an empty string or handling None values appropriately where working_dir is used.

Comment on lines +76 to +87
def list_tests(build_dir=None):
cmd = "".join(
[
s
for s in [
"ctest ",
f"--test-dir {build_dir} " if build_dir else "",
"--show-only=json-v1",
]
if s
]
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The command string in list_tests can be constructed more cleanly using a list comprehension and the join method.

- cmd = "".join(
-     [
-         s
-         for s in [
-             "ctest ",
-             f"--test-dir {build_dir} " if build_dir else "",
-             "--show-only=json-v1",
-         ]
-         if s
-     ]
- )
+ cmd = " ".join(filter(None, ["ctest", f"--test-dir {build_dir}" if build_dir else "", "--show-only=json-v1"]))

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def list_tests(build_dir=None):
cmd = "".join(
[
s
for s in [
"ctest ",
f"--test-dir {build_dir} " if build_dir else "",
"--show-only=json-v1",
]
if s
]
)
def list_tests(build_dir=None):
cmd = " ".join(filter(None, ["ctest", f"--test-dir {build_dir}" if build_dir else "", "--show-only=json-v1"]))

Comment on lines +15 to +17
def version():
pass

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version function is currently a placeholder with no implementation. Please ensure that the implementation is complete or provide a TODO comment if it's intended to be implemented later.

@@ -63,13 +52,14 @@ def runner(runnable, queue):
def print_tests(batches):
for batch in batches:
for test in batch.tests:
print(test)
print(test.cmd)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The print_tests function should be updated to access the cmd attribute from the test_case attribute of the test object, as the direct cmd attribute on test no longer exists.

-            print(test.cmd)
+            print(test.test_case.cmd)

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
print(test.cmd)
print(test.test_case.cmd)

Comment on lines 56 to 61
def verify_cli_args(cli_args):
cli_args.interval = int(cli_args.interval)
return cli_args
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verify_cli_args function converts the interval argument to an integer without validation. Consider adding error handling for non-integer inputs to prevent runtime exceptions.

def verify_cli_args(cli_args):
+   try:
        cli_args.interval = int(cli_args.interval)
+   except ValueError:
+       raise ValueError("Interval must be an integer")
    return cli_args

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def verify_cli_args(cli_args):
cli_args.interval = int(cli_args.interval)
return cli_args
def verify_cli_args(cli_args):
try:
cli_args.interval = int(cli_args.interval)
except ValueError:
raise ValueError("Interval must be an integer")
return cli_args

Comment on lines +89 to +100
class RuntimeStatsManager:
def __init__(self, pid, interval=_default_interval):
self.pid = pid
self.interval = interval
self.pqueue = Queue()
self.data = {}
self.p = Process(target=RuntimeStatsManager._run, args=(self,))
self.p.start()
self.data = self.pqueue.get()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RuntimeStatsManager class starts a process in its constructor. Starting a process during object initialization can be risky and might lead to resources not being released properly if an exception occurs. Consider using a factory method or a context manager to handle process lifecycle.

Comment on lines 112 to 122
@staticmethod
def _run(self):
data = ProcessCaptureInfo()
signal.signal(signal.SIGINT, signal_handler)
while check_pid(self.pid):
try:
capture_now(self.pid, data)
except psutil.AccessDenied:
break
time.sleep(self.interval)
self.pqueue.put(data)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _run method is a static method but is called with self as an argument. This is unconventional and could lead to confusion. Consider refactoring to use instance methods or class methods with appropriate arguments.

- @staticmethod
- def _run(self):
+ def _run(self):
    data = ProcessCaptureInfo()
    signal.signal(signal.SIGINT, signal_handler)
    while check_pid(self.pid):
        try:
            capture_now(self.pid, data)
        except psutil.AccessDenied:
            break
        time.sleep(self.interval)
    self.pqueue.put(data)

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
@staticmethod
def _run(self):
data = ProcessCaptureInfo()
signal.signal(signal.SIGINT, signal_handler)
while check_pid(self.pid):
try:
capture_now(self.pid, data)
except psutil.AccessDenied:
break
time.sleep(self.interval)
self.pqueue.put(data)
def _run(self):
data = ProcessCaptureInfo()
signal.signal(signal.SIGINT, signal_handler)
while check_pid(self.pid):
try:
capture_now(self.pid, data)
except psutil.AccessDenied:
break
time.sleep(self.interval)
self.pqueue.put(data)



def verify_cli_args(cli_args):
if cli_args.cores == "a" or cli_args.cores == "all":
cli_args.cores = cpu_count()
cli_args.cores = multiprocessing.cpu_count()
cli_args.cores = int(cli_args.cores)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conversion of cli_args.cores to an integer is redundant since the --cores argument is already defined as an integer type in the argument parser. This line should be removed.

- cli_args.cores = int(cli_args.cores)

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
cli_args.cores = int(cli_args.cores)

Comment on lines +85 to +87
except:
e = sys.exc_info()[0]
print(f"Error: Unknown Error {e}")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The catch-all exception handler is still present, which can obscure the actual error. Consider catching specific exceptions to provide more detailed error information and handling.

-    except:
-        e = sys.exc_info()[0]
-        print(f"Error: Unknown Error {e}")
+    except Exception as e:
+        print(f"Error: {e.__class__.__name__}: {e}")

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
except:
e = sys.exc_info()[0]
print(f"Error: Unknown Error {e}")
except Exception as e:
print(f"Error: {e.__class__.__name__}: {e}")

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 12

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between e22592d and 09db8d2.
Files ignored due to filter (1)
  • pyproject.toml
Files selected for processing (26)
  • .pylintrc (1 hunks)
  • phlop/init.py (1 hunks)
  • phlop/main.py (1 hunks)
  • phlop/app/init.py (1 hunks)
  • phlop/app/main.py (1 hunks)
  • phlop/app/cmake.py (1 hunks)
  • phlop/app/git.py (2 hunks)
  • phlop/app/perf.py (2 hunks)
  • phlop/app/sleep.py (1 hunks)
  • phlop/dict.py (1 hunks)
  • phlop/logger.py (1 hunks)
  • phlop/os.py (2 hunks)
  • phlop/proc.py (3 hunks)
  • phlop/procs/init.py (1 hunks)
  • phlop/procs/main.py (1 hunks)
  • phlop/procs/runtimer.py (1 hunks)
  • phlop/reflection.py (1 hunks)
  • phlop/run/main.py (1 hunks)
  • phlop/run/stats_man.py (1 hunks)
  • phlop/run/test_cases.py (1 hunks)
  • phlop/string.py (1 hunks)
  • phlop/sys.py (1 hunks)
  • phlop/testing/main.py (1 hunks)
  • phlop/testing/parallel_processor.py (5 hunks)
  • phlop/testing/test_cases.py (1 hunks)
  • setup.py (1 hunks)
Files skipped from review due to trivial changes (9)
  • .pylintrc
  • phlop/init.py
  • phlop/main.py
  • phlop/app/sleep.py
  • phlop/logger.py
  • phlop/procs/init.py
  • phlop/procs/main.py
  • phlop/run/main.py
  • setup.py
Files skipped from review as they are similar to previous changes (11)
  • phlop/app/main.py
  • phlop/app/cmake.py
  • phlop/app/git.py
  • phlop/app/perf.py
  • phlop/dict.py
  • phlop/os.py
  • phlop/procs/runtimer.py
  • phlop/reflection.py
  • phlop/string.py
  • phlop/testing/main.py
  • phlop/testing/parallel_processor.py
Additional comments: 7
phlop/proc.py (2)
  • 10-10: The RunTimer class has been imported from phlop.procs.runtimer but the usage in the run function needs to be verified to ensure it aligns with the new implementation.
Verification successful

The RunTimer class has been successfully located in the phlop/procs/runtimer.py file, and its usage in phlop/proc.py has been confirmed. The initial concerns have been addressed, and the changes appear to be correctly implemented.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the new RunTimer class is compatible with its usage in the run function.
ast-grep --lang python --pattern $'class RunTimer {
  $$$
  __init__($_, $_, $_, $_, $$$) {
    $$$
  }
}'

Length of output: 452



Script:

#!/bin/bash
# Verify the existence and constructor signature of the RunTimer class in its new location.
ast-grep --lang python --pattern $'class RunTimer {
  $$$
  __init__($$$) {
    $$$
  }
}'

Length of output: 436


Script:

#!/bin/bash
# Verify the usage of RunTimer in the run function within phlop/proc.py.
ast-grep --lang python --pattern $'def run($$$) {
  $$$
  RunTimer($$$)
  $$$
}'

Length of output: 426



Script:

#!/bin/bash
# Search for the RunTimer class in the phlop/procs/runtimer.py file.
rg 'class RunTimer' -- phlop/procs/runtimer.py

Length of output: 64


Script:

#!/bin/bash
# Search for the usage of RunTimer in the phlop/proc.py file.
rg 'RunTimer' -- phlop/proc.py

Length of output: 93

* 28-32: The `run_raw` function has been added to run subprocesses with more control over input/output streams. This is a significant addition and appears to be implemented correctly.
phlop/run/test_cases.py (4)
  • 22-45:
    The cli_args_parser function has been updated with new argument options and default values, which aligns with the PR objectives to enhance CMake and CTest support.

  • 51-51:
    The previous comment regarding the redundant conversion of cli_args.cores to an integer still stands, as the --cores argument is already defined as an integer type in the argument parser.

  • 59-70:
    The get_test_cases function has been correctly updated to include conditional logic for handling CMake tests based on the cmake flag.

  • 73-87:
    The refactoring of the main function to use the new argument parsing and processing functions is consistent with the PR objectives. The previous comment regarding the catch-all exception handler still applies.

phlop/testing/test_cases.py (1)
  • 26-26: Ensure that the log_file_path is being used correctly and safely throughout the code, especially in the context of file operations and potential security implications.

Comment on lines +1 to +5
#
#
#
#
#
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hunk indicates that the entire content of phlop/app/__init__.py has been removed, leaving only comments or whitespace. This is a substantial change that could potentially break any part of the codebase that depends on the removed functions. It is crucial to ensure that all references to the removed functions are either updated or removed, and that any necessary functionality is reimplemented elsewhere if needed.

Comment on lines +52 to +55
if not Path(cli_args.dir).exists():
raise RuntimeError(
"phlop.run.test_cases error: directory provided does not exist"
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider enhancing the error message by including the directory path that does not exist.

-            "phlop.run.test_cases error: directory provided does not exist"
+            f"phlop.run.test_cases error: directory '{cli_args.dir}' provided does not exist"

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
if not Path(cli_args.dir).exists():
raise RuntimeError(
"phlop.run.test_cases error: directory provided does not exist"
)
if not Path(cli_args.dir).exists():
raise RuntimeError(
f"phlop.run.test_cases error: directory '{cli_args.dir}' provided does not exist"
)

Comment on lines +12 to +21
@contextmanager
def extend_sys_path(paths):
if isinstance(paths, str):
paths = [paths]
old_path = sys.path[:]
sys.path.extend(paths)
try:
yield
finally:
sys.path = old_path
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a docstring to the extend_sys_path function to explain its purpose, usage, and the parameters it accepts. This will improve code readability and maintainability.

@contextmanager
+ def extend_sys_path(paths):
+     """
+     Temporarily add directories to the system path.
+     
+     This context manager extends the system path with the given paths
+     for the duration of the context. The original system path is restored
+     afterwards.
+     
+     Args:
+         paths (str or list): A path or list of paths to add to the system path.
+     """
    if isinstance(paths, str):
        paths = [paths]
    old_path = sys.path[:]
    sys.path.extend(paths)
    try:
        yield
    finally:
        sys.path = old_path

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
@contextmanager
def extend_sys_path(paths):
if isinstance(paths, str):
paths = [paths]
old_path = sys.path[:]
sys.path.extend(paths)
try:
yield
finally:
sys.path = old_path
@contextmanager
def extend_sys_path(paths):
"""
Temporarily add directories to the system path.
This context manager extends the system path with the given paths
for the duration of the context. The original system path is restored
afterwards.
Args:
paths (str or list): A path or list of paths to add to the system path.
"""
if isinstance(paths, str):
paths = [paths]
old_path = sys.path[:]
sys.path.extend(paths)
try:
yield
finally:
sys.path = old_path

Comment on lines +29 to +33
class TestBatch:
def __init__(self, tests, cores=1):
self.tests = tests
self.cores = cores

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TestBatch class currently does not have any methods to process the tests or manage concurrency based on the cores attribute. Please clarify if this is an incomplete implementation or if additional code is planned to utilize these attributes.

Comment on lines +24 to +25
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider making the logging level configurable or setting a less verbose default level for production use.

- logging.basicConfig(level=logging.DEBUG)
+ logging.basicConfig(level=logging.INFO)  # Or make this configurable

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO) # Or make this configurable
logger = logging.getLogger(__name__)

Comment on lines +102 to +105
def __del__(self):
if check_pid(self.p.pid):
os.kill(self.p.pid, signal.SIGINT)
self.join()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the destructor method __del__ to terminate the process is unreliable. Consider using a context manager or explicit cleanup method.

Comment on lines +107 to +113
def join(self):
if not self.pid:
return
while self.p.exitcode is None and check_pid(self.p.pid):
time.sleep(1)
self.pid = 0
return self
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The join method sets self.pid to 0 after waiting for the process to exit. Consider making the method idempotent or documenting this behavior.

Comment on lines +132 to +141
def print_summary(statsman):
print("summary:")
print("\tCPU avg:", np.average(statsman.data.cpu_load))
print("\tCPU max:", np.max(statsman.data.cpu_load))

print("\tFDS avg:", np.average(statsman.data.fds))
print("\tFDS max:", np.max(statsman.data.fds))

print("\tMEM avg:", np.average(statsman.data.mem_usage))
print("\tMEM max:", np.max(statsman.data.mem_usage))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that the statsman.data lists are not empty before calling numpy functions to avoid ValueError.

def print_summary(statsman):
+   if statsman.data.cpu_load:
        print("CPU avg:", np.average(statsman.data.cpu_load))
        print("CPU max:", np.max(statsman.data.cpu_load))
    # Repeat checks for fds and mem_usage

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def print_summary(statsman):
print("summary:")
print("\tCPU avg:", np.average(statsman.data.cpu_load))
print("\tCPU max:", np.max(statsman.data.cpu_load))
print("\tFDS avg:", np.average(statsman.data.fds))
print("\tFDS max:", np.max(statsman.data.fds))
print("\tMEM avg:", np.average(statsman.data.mem_usage))
print("\tMEM max:", np.max(statsman.data.mem_usage))
def print_summary(statsman):
print("summary:")
if statsman.data.cpu_load:
print("\tCPU avg:", np.average(statsman.data.cpu_load))
print("\tCPU max:", np.max(statsman.data.cpu_load))
if statsman.data.fds:
print("\tFDS avg:", np.average(statsman.data.fds))
print("\tFDS max:", np.max(statsman.data.fds))
if statsman.data.mem_usage:
print("\tMEM avg:", np.average(statsman.data.mem_usage))
print("\tMEM max:", np.max(statsman.data.mem_usage))

Comment on lines +144 to +157
def main():
parser = cli_args_parser()
cli_args = verify_cli_args(parser.parse_args())
try:
proc = run_raw(cli_args.remaining, quiet=cli_args.quiet)
statsman = RuntimeStatsManager(proc.pid, cli_args.interval).join()
print_summary(statsman)
except (Exception, SystemExit) as e:
logger.exception(e)
parser.print_help()
except:
e = sys.exc_info()[0]
print(f"Error: Unknown Error {e}")

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main function catches a generic exception and prints an unknown error message. Consider logging the full exception details.

- print(f"Error: Unknown Error {e}")
+ logger.exception("An unexpected error occurred", exc_info=e)

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def main():
parser = cli_args_parser()
cli_args = verify_cli_args(parser.parse_args())
try:
proc = run_raw(cli_args.remaining, quiet=cli_args.quiet)
statsman = RuntimeStatsManager(proc.pid, cli_args.interval).join()
print_summary(statsman)
except (Exception, SystemExit) as e:
logger.exception(e)
parser.print_help()
except:
e = sys.exc_info()[0]
print(f"Error: Unknown Error {e}")
def main():
parser = cli_args_parser()
cli_args = verify_cli_args(parser.parse_args())
try:
proc = run_raw(cli_args.remaining, quiet=cli_args.quiet)
statsman = RuntimeStatsManager(proc.pid, cli_args.interval).join()
print_summary(statsman)
except (Exception, SystemExit) as e:
logger.exception(e)
parser.print_help()
except:
e = sys.exc_info()[0]
logger.exception("An unexpected error occurred", exc_info=e)

Comment on lines 66 to +67
raise ValueError("do better")
return run(f"which {bin}").returncode == 0
return run(f"which {bin}").exitcode == 0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The binary_exists_on_path function still raises a ValueError with a non-descriptive message. This should be replaced with a proper check or a more informative error message.

- raise ValueError("do better")
+ # Implement the actual check for binary existence or provide a descriptive error message

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
raise ValueError("do better")
return run(f"which {bin}").returncode == 0
return run(f"which {bin}").exitcode == 0
# Implement the actual check for binary existence or provide a descriptive error message
return run(f"which {bin}").exitcode == 0

@PhilipDeegan PhilipDeegan merged commit 6467c50 into master Dec 19, 2023
4 checks passed
@PhilipDeegan PhilipDeegan deleted the cmake branch December 19, 2023 17:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant