pytest-dev / pytest

The pytest framework makes it easy to write small tests, yet scales to support complex functional testing
https://pytest.org
MIT License
12.04k stars 2.67k forks source link

Offer a way to handle conflicting options #12915

Open DieTapete opened 1 day ago

DieTapete commented 1 day ago

What's the problem this feature will solve?

When a user installs multiple plugins it is often the case that there are conflicting options. Currently there is no way to handle those conflicts but to uninstall one of the conflicting plugins. Also when adding custom options the user is forced to rename the option.

Describe the solution you'd like

There should be a way to either define a resolution for a conflict or to remove one of the conflicting options.

For example in the following case I remove a conflicting option from the playwright plugin before I add my own:

def pytest_addoption(parser):
    pw_options = parser.getgroup("playwright", "Playwright")
    # either rename the options
    pw_options.renameoption('--device', '--machine')
    # or remove it
    pw_options.removeoption('--device')
    parser.addoption(
        "--device",
        action="store",
        choices=devices
    )

Alternatively there could be a function handling conflicts, a crude approach would look like this

def resolve_options(option1, option2):
    if option1.group is None:
        return option1
    return option2

def pytest_addoption(parser):
    parser.conflict_handler = resolve_options
RonnyPfannschmidt commented 1 day ago

The namespace where the values land are currently the same

Additionally options having different names based on the set of installed plugins would be a ux disaster

A mechanism for namespaced long names plus conflict averse lookup is necessary

The-Compiler commented 1 day ago

I don't think "it is often the case" is really accurate here, given how rarely this comes up.

In any case, I don't think pytest can easily solve this. The public-facing option values is not the only thing that conflicts, but as @RonnyPfannschmidt hints at, receiving options is also a shared namespace. If you remove/rename the option from Playwright, what's going to happen when Playwright reads pytestconfig.option.device? And how could that possibly differ from what happens if you yourself read pytestconfig.option.device instead?

Without fundamental changes on how plugins register and receive options, this can only be fixed in cooperation with the affected plugin(s):

I agree something like you propose would be very confusing, but for it to be possible in the first place, pytest would need something like a parser.add_plugin_option("playwright", "--device") and pytestconfig.plugin_option.playwright.device. Only then it could (perhaps optionally / on request) expose that as --playwright-device instead of just --device - but this only works if there is a per-plugin namespace to read it.

RonnyPfannschmidt commented 1 day ago

I started some experiments for typesetting alike/ I believe registring types that act as namespaces,+ declarations is the most viable way