Skip to content

run: Support system signal as a coverage report dump trigger. #1998

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

ark-g
Copy link

@ark-g ark-g commented Jul 12, 2025

Implement a feature that allows users to generate a coverage report by sending a signal. This is particularly useful for obtaining coverage reports from continuously running Python scripts, such as server implementations or monitoring utilities.
Currently supported only two signals SIGUSR1 and SIGUSR2.

Copy link
Owner

@nedbat nedbat left a comment

Choose a reason for hiding this comment

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

A few small changes. I'll adjust the English description once this merges.

@@ -227,7 +229,15 @@ class Opts:
"", "--version", action="store_true",
help="Display version information and exit.",
)

dump_signal = optparse.make_option(
'', '--dump_signal', action='store', metavar='DUMP_SIGNAL',
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
'', '--dump_signal', action='store', metavar='DUMP_SIGNAL',
'', '--dump-signal', action='store', metavar='DUMP_SIGNAL',

Options use hyphens, not underscores.

@@ -227,7 +229,15 @@ class Opts:
"", "--version", action="store_true",
help="Display version information and exit.",
)

dump_signal = optparse.make_option(
Copy link
Owner

Choose a reason for hiding this comment

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

Overall, I think this should be called "save signal" instead of "dump signal" throughout.

Copy link
Owner

Choose a reason for hiding this comment

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

These make_option definitions are in rough alphabetical order, so this should be further up.

@@ -525,6 +536,7 @@ def get_prog_name(self) -> str:
Opts.parallel_mode,
Opts.source,
Opts.timid,
Opts.dump_signal,
Copy link
Owner

Choose a reason for hiding this comment

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

Keep these items alphabetized.

@nedbat
Copy link
Owner

nedbat commented Jul 12, 2025

Thanks for this! I want to have tests for this feature. test_process.py has some tests you could use as models, or I could give you some suggestions.

 * Set options in alphabetical order
 * Rename "dump-signal" to "save-signal"

Signed-off-by: Arkady Gilinsky <[email protected]>
@ark-g
Copy link
Author

ark-g commented Jul 12, 2025

Thanks for this! I want to have tests for this feature. test_process.py has some tests you could use as models, or I could give you some suggestions.

Hi,
First, I want to thank you for your great work and support for the Python coverage utility.
I don't see any option to create a test within the current environment. From what I understand, the test for the new feature should send a signal during the execution of the target script, but I haven't found this capability in the existing test examples or infrastructure. I would appreciate your suggestions on how to implement the test for the new feature.

Thanks.

@nedbat
Copy link
Owner

nedbat commented Jul 12, 2025

Thanks, what are your thoughts on a test? Let me know how far you want to take this, I can take it over when you want.

@ark-g
Copy link
Author

ark-g commented Jul 12, 2025

I think that test should run some script with 2 stages.

    def test_save_signal(self) -> None:
        test_file = "dummy_hello.py"
        self.make_file(test_file, """\
            import time
            for i in range(10):
              time.sleep(1)
              if i < 5:
                print(f'{i:02d} Hello from the main script!')
              else:
                print(f'0x{i:02x} Hello X!')
            print('Done and goodbye')
            """)
        dummy_prc = self.run_command(f"coverage run --signal-save USR1 {test_file}")
        <Send signal first time>
        <Make sure that report reflects expected coverage>
        <sleep 5 secs>
       <Send signal second time>
       <Make sure that report reflects expected coverage>
       <Sleep 6 secs>
       <Make sure that report reflects 100% coverage>

But for the above I need to run script in background and to communicate with the running script dynamically (to monitor it's output).
Current infrastructure doesn't support such an ability, AFAIU.
Let me know, please, how do you see this test ?

@nedbat
Copy link
Owner

nedbat commented Jul 12, 2025

The test would have to be something like that, but I don't want it to take 10 seconds. I think it would be enough to have two cases in the test: in both, start an infinite loop process with coverage run --save-signal=USR1. In both cases, we use kill -s KILL to end the process. In one case, that's all we do, and we see no .coverage file is created. In the other case, we use kill -s USR1 before the KILL, and we see that a .coverage file is created.

@nedbat
Copy link
Owner

nedbat commented Jul 12, 2025

BTW, we also need to do the right thing on Windows. I think it's best to add "not on Windows" to the option help, and print an error if it's used on Windows. The test should check this also.

@ark-g
Copy link
Author

ark-g commented Jul 12, 2025

The test would have to be something like that, but I don't want it to take 10 seconds. I think it would be enough to have two cases in the test: in both, start an infinite loop process with coverage run --save-signal=USR1. In both cases, we use kill -s KILL to end the process. In one case, that's all we do, and we see no .coverage file is created. In the other case, we use kill -s USR1 before the KILL, and we see that a .coverage file is created.

Do you have an example how to initiate infinite loop process in existing test infrastructure or I should use Subprocess module in the test ?

@ark-g
Copy link
Author

ark-g commented Jul 13, 2025

The test would have to be something like that, but I don't want it to take 10 seconds. I think it would be enough to have two cases in the test: in both, start an infinite loop process with coverage run --save-signal=USR1. In both cases, we use kill -s KILL to end the process. In one case, that's all we do, and we see no .coverage file is created. In the other case, we use kill -s USR1 before the KILL, and we see that a .coverage file is created.

Do you have an example how to initiate infinite loop process in existing test infrastructure or I should use Subprocess module in the test ?

I do not see much more purpose on the second test (with only KILL signal), but as you wish ... ;)

@nedbat
Copy link
Owner

nedbat commented Jul 13, 2025

Did you see this pull request? ark-g#1 It has some other changes for the code in cmdline.py. I wanted the test without the signal to show that the signal is needed to get the data file.

@ark-g
Copy link
Author

ark-g commented Jul 14, 2025

Did you see this pull request? ark-g#1 It has some other changes for the code in cmdline.py. I wanted the test without the signal to show that the signal is needed to get the data file.

I think that the first test is also demonstrating this fact, since the final coverage report doesn't include the line that is sending KILL signal, but does include the line with USR1 signal.

Do you think that I can merge this PR ?

@nedbat
Copy link
Owner

nedbat commented Jul 15, 2025

I want you to take the changes I gave you in the pull request in your repo.

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.

2 participants