pluggy icon indicating copy to clipboard operation
pluggy copied to clipboard

Have a unified way of showing info about installed plugins

Open obestwalter opened this issue 8 years ago • 14 comments

When implementing https://github.com/tox-dev/tox/issues/544 a discussion arose about what it should look like when you output version information about the tool.

https://github.com/tox-dev/tox/pull/628#discussion_r138037385

So I leave that suggestion here, that pluggy could provide a higher level API to output version information and maybe with different levels of exactness/verbosity (e.g. full paths, vs. just names and versions).

obestwalter avatar Sep 11 '17 15:09 obestwalter

Huh, yeah I guess we don't have anything for that yet.

@RonnyPfannschmidt @obestwalter what were you thinking?

How bout

version, path = PluginManager.get_info(plugin_name)

goodboy avatar Sep 12 '17 03:09 goodboy

get_info is pretty much nonsaying, its not an acceptable api

RonnyPfannschmidt avatar Sep 12 '17 07:09 RonnyPfannschmidt

I think we should figure out what in general a command line tool using pluggy should output when whatever --version is invoked.

tox current version (no pluggy information at all):

2.7.0 imported from /home/oliver/.virtualenvs/ubng/lib/python3.6/site-packages/tox/__init__.py

tox dev version (fetching pluggy info via):

https://github.com/tox-dev/tox/blob/aa05d0fd625322bad1fb451ef2e192523061d831/tox/config.py#L263-L272

tox --version
2.8.2.dev32 imported from /home/oliver/work/tox/tox/tox/__init__.py
registered plugins:
    tox-durations-0.1 at /home/oliver/.virtualenvs/tox/lib/python3.6/site-packages/tox_durations.py
    detox-0.11 at /home/oliver/.virtualenvs/tox/lib/python3.6/site-packages/detox/tox_proclimit.py

for pytest:

This is pytest version 3.1.0, imported from /home/oliver/.virtualenvs/ubng/lib/python3.6/site-packages/pytest.py
setuptools registered plugins:
  pytest-xdist-1.16.0 at /home/oliver/.virtualenvs/ubng/lib/python3.6/site-packages/xdist/plugin.py
  pytest-xdist-1.16.0 at /home/oliver/.virtualenvs/ubng/lib/python3.6/site-packages/xdist/boxed.py
  pytest-xdist-1.16.0 at /home/oliver/.virtualenvs/ubng/lib/python3.6/site-packages/xdist/looponfail.py
  pytest-cov-2.5.1 at /home/oliver/.virtualenvs/ubng/lib/python3.6/site-packages/pytest_cov/plugin.py

pytest also outputs the version info to stderr, which I find strange, but that is another topic.

Pluggy should offer a function that returns a string representation of all relevant information about what is installed through it and how (and maybe with different verbosity levels, if that makes sense). I do not know too much about pluggy yet, so I don't know what information is there and how it should be presented.

I am quite happy though with that comes back from list_plugin_distinfo() and having the full paths helps with debugging things, but maybe if it is always the same path it is enough to display it once for all setuptools installed plugins.

I also think that the info should say that those pugins are installed via pluggy (and then maybe more concrete info about the mechanism, e.g. setuptools).

obestwalter avatar Sep 12 '17 08:09 obestwalter

So, basically not much different of what we now construct from list_plugin_distinfo() but standardized with all the pugyy insider knowledge right from the source, so as an hypothetic example:

plugins registered via pluggy 0.8.7:
    - registered through setuptools entry point <path to wherever file modules are installed from>
        - super-plugin 1.2.3
        - other-plugin -3.4.a1
    - registered from outer space:
        - space-balls 2

obestwalter avatar Sep 12 '17 09:09 obestwalter

for good measure - here is what devpi does:

devpi --version
devpi-client 2.7.0

current devpi server: https://devpi.net/
    devpi-findlinks 2.0.0
    devpi-server 4.3.1rc1
    devpi-web 3.2.1rc1

obestwalter avatar Sep 12 '17 10:09 obestwalter

I still think it would be good to consolidate this functionality into pluggy instead of everybody baking their own thing.

obestwalter avatar Apr 17 '18 12:04 obestwalter

the main question is api for example, a setuptools plugin cant tell pluggy if a plugin object added is part of it or not

until then i believe its fine just to have a helper that lists names and versions for setuptools plugins off a pluginmanager

RonnyPfannschmidt avatar Apr 17 '18 15:04 RonnyPfannschmidt

Can you explain that problem further? I don't get it. From the perspective of a maintainer of a project using pluggy I would like to have a convenience method to give me a human readable version of the installed plugins. What I did in tox was simply:

def get_version_info(pm):
    out = ["%s imported from %s" % (tox.__version__, tox.__file__)]
    plugin_dist_info = pm.list_plugin_distinfo()
    if plugin_dist_info:
        out.append('registered plugins:')
        for mod, egg_info in plugin_dist_info:
            source = getattr(mod, '__file__', repr(mod))
            out.append("    %s-%s at %s" % (
                egg_info.project_name, egg_info.version, source))
    return '\n'.join(out)

This is running happily for quite a while now and I haven't seen any problems with it yet:

I am pretty sure that if we put our heads together we can come up with a bit more generalized version of that that catches some corner cases I might not have foreseen (which is another reason, why I would rather have a proper wrapper directly from pluggy rather then digging around in the guts).

Regarding API and naming things I could think of several ways: e.g. adding a human_readable parameter to list_plugin_distinfo that is False by default and if passed True gives back basically what I am putting together manually as a string.

Or make that parameter more versatile by passing in a string that asks for the info in one of several formats.

Or add a method display_plugin_distinfo that yields the human readable version. I don't really care TBH as long as it is documented :)

obestwalter avatar Apr 17 '18 16:04 obestwalter

pytest: https://github.com/pytest-dev/pytest/blob/master/_pytest/helpconfig.py#L154

def getpluginversioninfo(config):
    lines = []
    plugininfo = config.pluginmanager.list_plugin_distinfo()
    if plugininfo:
        lines.append("setuptools registered plugins:")
        for plugin, dist in plugininfo:
            loc = getattr(plugin, '__file__', repr(plugin))
            content = "%s-%s at %s" % (dist.project_name, dist.version, loc)
            lines.append("  " + content)
	return lines

obestwalter avatar Apr 17 '18 18:04 obestwalter

looks like pytest even grew two ways of doing the same thing already? I did not look to closely but here it is: https://github.com/pytest-dev/pytest/blob/master/_pytest/terminal.py#L455

https://github.com/pytest-dev/pytest/blob/master/_pytest/terminal.py#L736

def _plugin_nameversions(plugininfo):
    values = []
    for plugin, dist in plugininfo:
        # gets us name and version!
        name = '{dist.project_name}-{dist.version}'.format(dist=dist)
        # questionable convenience, but it keeps things short
        if name.startswith("pytest-"):
            name = name[7:]
        # we decided to print python package names
        # they can have more than one plugin
        if name not in values:
            values.append(name)
return values

obestwalter avatar Apr 17 '18 18:04 obestwalter

devpi server does save the generated output in a dict and sorts the stuff (good idea), but not fundamentally different: https://github.com/devpi/devpi/blob/master/server/devpi_server/main.py#L309

         threadlog.info("Found plugin %s-%s (%s)." % (
                distinfo.project_name, distinfo.version, distinfo.location))
            key = (distinfo.project_name, distinfo.version)
            if key not in version_info:
                version_info.append(key)
        version_info.sort()
pyramid_config.registry['devpi_version_info'] = version_info

obestwalter avatar Apr 17 '18 18:04 obestwalter

i beleive an api getting a sorted by name list of unique setuptools plugins might be a nice point - it should be a helper function not part of the pluginmanager

RonnyPfannschmidt avatar Apr 18 '18 08:04 RonnyPfannschmidt

it should be a helper function not part of the pluginmanager

I think it should be part of the plugin manager, because pluginmanager.list_plugin_distinfo() is providing the same knowledge but in a different format. My ideal solution would actually be to enhance the function itself to provide the same knowledge in a human digestible format. I would have to feed the helper function the output of the function anyway, so this is just shoving data around unnecessarily. to clarify: as a programmer using pluggy I would find this a bit unwieldy for the job at hand:

pluggy.human_readable_version_info(pluginmanager.list_plugin_distinfo()

I would prefer something like this:

pluginmanager.list_plugin_distinfo(format='human_readable')

obestwalter avatar Apr 18 '18 12:04 obestwalter

@obestwalter depending on circumstances human readable is pain text, formatteddifferently, maybe html, maybe terminal encoded ^^ plus different implementation may choose to behave different in different circumstances (like list distributions vs list actual plugins) - there is a lot of choice possible and its all presentation logic thats completely unrelated to actual plugin management - as such i beleive its best experimented with and implemented with as an external consumer of pluginmanager, not as a part of it

RonnyPfannschmidt avatar Apr 18 '18 20:04 RonnyPfannschmidt