Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add return type to Libdoc specs and HTML output #3017

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

Closed
robinmackaij opened this issue Nov 30, 2018 · 45 comments
Closed

Add return type to Libdoc specs and HTML output #3017

robinmackaij opened this issue Nov 30, 2018 · 45 comments
Assignees
Milestone

Comments

@robinmackaij
Copy link
Contributor

With the support of type-hints Python signatures can give information on the type of returned values of a method. This could be used to improve the documentation generated by libdoc.

def my_func(foo: str, count: int) -> str: will give the following in the Argument column:
foo: str, count: int
This could be expanded to include the type of the returned value:
foo: str, count: int -> str (following the Python notation)
The header Arguments could be updated to Signature to reflect this additional information.

Things to consider (from Slack conversation):

  • Take into account user keywords that may have used [Return] setting
  • How to handle def(*, named_arg: str) -> int for forced named arguments methods. The resulting signature in the documentation (*, named_arg: str -> int) may confuse users that are not familiar with the new syntax to force named arguments. Note that the * cannot be omitted from the method signature since this would mean the method is no longer named-only.
  • How to handle types in a signature that are based on the typing module (typing.Union, etc.). Right now, they come up as typing.Union in the documentation, while the inclusion of the standard module (typing.) does not really give the user much information. Also, the more complex typing constructions (Optional, nested structures, etc.) could lead to very long and unusable documentation entries.
@mawentao119
Copy link

mawentao119 commented Apr 4, 2019

I usually deal with arguments in function. Such as "myvar = arg[1].encode('utf-8')" or other convertions .
If libdoc can generate [return] arguments, that will be more helpful to other members of the team.

@pekkaklarck
Copy link
Member

Showing return type information in Libdoc output would certainly be useful. The biggest question is how to show that information. Options include:

  • Add new column. A drawback is that it takes some precious horizontal space with all keywords and most of the keywords don't have any return info to show. Could avoid this issue partly by not showing the column at all if its fully empty (we do that with keyword tags).

  • Show the info in the same column with arguments. There's lot of free space there at least with keywords that have longer documentation. The column could then be renamed to Signature but still needed to thing a bit how to actually show arguments and return values there.

I think the latter solution is better but this can still be discussed. Anyone interested to implement this? Shouldn't be overly complicated and I'd be happy to help and review pull requests.

@pekkaklarck pekkaklarck added good first issue Good for newcomers help wanted Extra help appreciated labels Aug 15, 2019
@anandaprakash1979
Copy link

Hi @pekkaklarck I am interested in implementing this request.
Can I work on this?

@pekkaklarck
Copy link
Member

Sorry @anandaprakash1979 for missing your comment. Are you still interested in looking at this issue? RF 4.0 will contain also other LIbdoc HTML (e.g. #3586) and return value could be added at the same time.

We could also think should return value info be added to Libdocs XML outputs (libspec). Do you @fabioz see that as a useful enhancement? If yes, we should submit a separate issue about it.

@pekkaklarck pekkaklarck added this to the v4.0 milestone Aug 21, 2020
@pekkaklarck
Copy link
Member

One thing to decide is how to handle user keywords that use the [Return] setting to specify the return value. That is the actual value, not type, so it's different information than what function annotations provide. Showing it would probably be useful anyway.

@Snooz82
Copy link
Member

Snooz82 commented Sep 15, 2020

@pekkaklarck

For robot keywords it would be interesting that these keywords could return anything.
So i would propose that the Return Type of robot keywords that has [Return] or Return From Keyword ... is Any

So, for us (imbus) as user of the xml output, we would really be interested into getting an information if a keyword does return or does not return anything.
What is returned is not the most important issue. If this information is there, fine. If not, also ok.

i think also for Libdoc it would be a nice information.

@JockeJarre
Copy link
Contributor

JockeJarre commented Nov 20, 2020

As discussed on Slack, would be nice if the @Keyword decorator allow setting the return type.

the proposed syntax:

@Keyword(types={'return': int})

It's compatible with how they are represented in func.annotations and would make implementation really easy. (Pekka)
There are also thoughts of having support for Unions as return type. Like Union[str, int]

Here is the whole slack thread https://robotframework.slack.com/archives/C0K0240NL/p1605819214419400?thread_ts=1604056554.147300&cid=C0K0240NL

@pekkaklarck pekkaklarck removed this from the v4.0 milestone Nov 30, 2020
@pekkaklarck
Copy link
Member

We want RF 4.0 out soon and decided not to include this issue in it. If someone is really interested in it an provides a PR, this can still be considered, but most likely RF 4.1 is a better target. The biggest open question is the difference with return info we get from library keywords (type info) and from user keywords (actual value, often as a variable).

@kollonqv
Copy link

Hi @pekkaklarck, I'm new here but I'd be interested to work on this.

@pekkaklarck
Copy link
Member

pekkaklarck commented May 20, 2021

Sounds good. There are few related things to be done related to this:

  1. Make it possible to add return type using @keyword as noted in the comment above by @JockeJarre. This is pretty easy and should probably get its own issue.
  2. Actually add return type information into Libdoc HTML output. That's what this issue is about and without it the above task doesn't give too much benefits. Adding the info to Libdoc output itself probably isn't too hard, but we need to agree on where/how it is shown.
  3. Add return information into Libdoc XML and JSON spec files. This is also an integral part of this issue.
  4. Decide how to handle return info we get from user keywords using [Return]. It's different info than type info we get from type hints and probably should be shown somehow differently. There also are other ways to return info (e.g. Return From Keyword) and supporting them all isn't easy. Finally, I think we should actually add separate RETURN statement and deprecate [Return] in the future. Due to these issues I think we should forget about user keywords now and concentrate only on library keywords with "real" type info. We can later add user keyword info in the future if needed. One option being that we add possibility to specify user keyword return type somehow.

@kollonqv
Copy link

Sounds good. There are few related things to be done related to this:

  1. Make it possible to add return type using @keyword as noted in the comment above by @JockeJarre. This is pretty easy and should probably get its own issue.
  2. Actually add return type information into Libdoc HTML output. That's what this issue is about and without it the above task doesn't give too much benefits. Adding the info to Libdoc output itself probably isn't too hard, but we need to agree on where/how it is shown.

Thanks, I've been looking at adding the return type to the Libdoc output regardless of the output format and it does not seem to be too difficult. Wouldn't it make more sense to show the expected return type (if it exists) regardless of the chosen Libdoc output format? Why only HTML?

@pekkaklarck
Copy link
Member

The comment only had HTML initially because I accidentally posted it too early....

@kollonqv
Copy link

kollonqv commented May 21, 2021

There's some WIP implementation now in https://github.com/kollonqv/robotframework/commit/d2439b072b2720612e23bcc304ddac765824d4f5

We now show the type hint for return information for all Libdoc outputs.

HTML:

image

show:

test_libdoc
===========
Scope:    GLOBAL

Documentation for library test_libdoc.

Test Return Type
----------------
Signatures:  [foo: str, count: int, return: str]

However, the current WIP implementation processes the return type hint as an argument. This leads to the return type hint being described as an arg in the machine readable formatted outputs like XML, JSON and LIBSPEC:

i.e. XML:

<keywords>
  <kw name="Test Return Type" lineno="25">
    <signatures repr="foo: str, count: int, return: str">
      <arg kind="POSITIONAL_OR_NAMED" required="true" repr="foo: str">
        <name>foo</name>
        <type>str</type>
      </arg>
      <arg kind="POSITIONAL_OR_NAMED" required="true" repr="count: int">
        <name>count</name>
        <type>int</type>
      </arg>
      <arg kind="RETURN_TYPE" required="false" repr="return: str">
        <name>return</name>
        <type>str</type>
      </arg>
    </signatures>
    <doc/>
    <shortdoc/>
  </kw>
</keywords>

The current WIP approach definitely needs to be changed as type hint is not an argument.
This leads to the following question; should we add some new field for the return type hint in the machine readable formats, i.e. <return_type> and what should it be called? On which level do we want it to be in? Thoughts?

@JockeJarre
Copy link
Contributor

JockeJarre commented May 22, 2021 via email

@kollonqv
Copy link

kollonqv commented May 22, 2021

Hi @JockeJarre,

Sorry but it’s not ready yet, I’ve only tested this with annotation only as in the original description of the issue. There’s still work to do and I don’t know how it behaves with @Keyword, I’ve not looked into that yet

@JockeJarre
Copy link
Contributor

JockeJarre commented May 22, 2021 via email

@Snooz82
Copy link
Member

Snooz82 commented May 22, 2021

Hi,

i think we should not change again the XML structure.
If the xml has to be changed, the return type should be an extra node and also in the html it should be separated from arguments.

I know that return type is the last element in the type hints, in python but that representation makes imho no sense.

@kollonqv
Copy link

kollonqv commented May 24, 2021

@JockeJarre I checked and it seems to behave ok with the @Keyword as well so you can give it a spin if you want.
Please find the latest in https://github.com/kollonqv/robotframework/commit/5bd9470e5608fbf32fc14b88f7778cecdfd463db

I updated the HTML to separate the return type from the arguments and left the xml's untouched for now. I was wondering if it would be clearer to separate the return type under it's own title under Arguments if it exists or do we want to have them all under Signatures title and just add an empty line between the arguments and the return?
image

from robot.api.deco import keyword

def test_return_type_annotation(foo: str, count: int) -> str:
    return foo

@keyword(types={'foo': str, 'count': int, 'return': str})
def test_return_type_decorator(foo, count):
    return foo

@keyword(types={'foo': str, 'count': int})
def test_return_type_no_return_type(foo, count):
    return foo

@JockeJarre
Copy link
Contributor

JockeJarre commented May 24, 2021 via email

@kollonqv
Copy link

kollonqv commented May 25, 2021

I need to do some fixes to the implementation and libdoc acceptance tests to make them all pass. I'll open up a PR on this once ready

@Snooz82
Copy link
Member

Snooz82 commented May 25, 2021

@kollonqv

have you read my comment about the XML structure?

i really think that it is absolutely not the correct way to add the return type to the arguments section or change the format again, for such a small "feature/reason".

you can add a new node/field to xml and json without creating new incompatibility.

cheers
René

@Snooz82
Copy link
Member

Snooz82 commented Jan 24, 2023

I think it is not that easy to detect if a python method/function has no return statement in it.
Therefore i would just concentrate on the annotations/hints.

and therefore i think it is something different between "not giving a return type hint" and "a hint to None"

@Snooz82
Copy link
Member

Snooz82 commented Jan 24, 2023

So in general we have imho two states in the libdoc:

a) return type defined (None, str, Union[int, float], etc)
b) no return type given or known.

@JirkaVrbka
Copy link

I already did something like it. It is possible to detect via inspect. The code needs to be enumerated, sure, but I think is it better to go through that instead of just giving up.

Here I would like to stress, that even RobotFramework libraries are not (currently) typed, so without walking through the code, this change will be useless

@Snooz82
Copy link
Member

Snooz82 commented Jan 24, 2023

@JirkaVrbka if you have a good proposal that:

  • does not need any external dependancies
  • works with Python 3.6 and newer

Then i think it would be very nice.

@JirkaVrbka
Copy link

JirkaVrbka commented Jan 24, 2023

"does not need any external dependancies"

Is inspect (part of python standard libraries) internal dependency? If it is, I can do that

@pekkaklarck pekkaklarck modified the milestones: v6.2, v7.0 Jun 2, 2023
@pekkaklarck
Copy link
Member

In my opinion it's enough to get the return type annotation and not look at the keyword code. Users who are interested to get return type listed can very easily add the needed annotation. That's the same as with type hints in general.

Using just the annotation ought to make implementation pretty simple. I believe the execution side model objects that Libdoc uses could already get the return type, then it needed to be added to Libdoc's model, and finally written to specs and shown in HTML.

@pekkaklarck
Copy link
Member

We are just starting RF 7 planning. Is there someone interested to provide a PR for this in RF 7 timeframe (latest in November) or should we move this further?

@Snooz82
Copy link
Member

Snooz82 commented Sep 4, 2023

@pekkaklarck maybe i find time

@xfoggi
Copy link

xfoggi commented Oct 4, 2023

@pekkaklarck / @Snooz82 how do you feel about this one? could it be squeezed into RF 7? :)

@pekkaklarck
Copy link
Member

I doubt I have time to look at this myself in RF 7 timeframe, there are more important issues for me and only a limited amount of time, but I can review a PR if someone is interested to implement this. The first step would be agreeing on what exactly we want to include. I believe in RF 7 it would be enough to just look at Python annotations and forget about user keywords altogether.

@pekkaklarck pekkaklarck self-assigned this Nov 15, 2023
pekkaklarck added a commit that referenced this issue Nov 15, 2023
The core part of #3017. The final task is adding the information to
the HTML outputs.
@pekkaklarck pekkaklarck added effort: medium and removed help wanted Extra help appreciated labels Nov 16, 2023
@pekkaklarck
Copy link
Member

@Snooz82 was able to convince me that this is an important functionality in RF 7.0. I implemented the core part of this functionality and now the return type is in Libdoc spec files. The remaining task is to show the type also in the Libdoc HTML outputs similarly as argument types. @Snooz82 himself promised to look at that.

@pekkaklarck pekkaklarck changed the title Add return type to libdoc output Add return type to Libdoc specs and HTML output Nov 16, 2023
pekkaklarck added a commit that referenced this issue Nov 16, 2023
Explicit tests for specifying the return type with dynamic libraries
and with the `@keyword` decorator. Part of #3017.
@pekkaklarck
Copy link
Member

pekkaklarck commented Nov 16, 2023

There are three ways to specify the return type:

  1. With normal functions and methods, it is easiest to use -> TheType annotation.
  2. When using the @keyword decorator, it is possible to add 'return': TheType into the types dictionary. The above approach is generally recommended, though.
  3. With dynamic libraries, the return type can be added as 'return': TheType to the type dictionary returned by get_keyword_types.

We don't use inspection for trying to find the return type from Python keywords or from user keywords. With the former it is very easy for users to specify the type themselves and with the latter we couldn't get the type reliably anyway. Adding type hint support for user keywords is a topic for a separate issue.

@pekkaklarck
Copy link
Member

@Snooz82 added the return type information to Libdoc's HTML output and you can see how it works with the latest Browser library documentation. This enhancement will be part of the forthcoming RF 7.0 alpha 2 and then it's easier to test this also with other libraries. If you have enhancement ideas or other comments, let us know either here or on Slack.

@pekkaklarck pekkaklarck added alpha 2 acknowledge To be acknowledged in release notes labels Nov 22, 2023
@JirkaVrbka
Copy link

JirkaVrbka commented Jul 23, 2024

We don't use inspection for trying to find the return type from Python keywords or from user keywords. With the former it is very easy for users to specify the type themselves and with the latter we couldn't get the type reliably anyway.

Since Robot Framework itself does not have those documented (and thus LibDoc gives "returnType": null to keywords like Convert To Binary) I played with the code a little and found a solution to at least give information that a keyword actually returns anything (and giving 'Any' as return type) or not.

In file argumentparser.py, we can change function _get_types as follows:

 def _get_types(self, method):
        # If types are set using the `@keyword` decorator, use them. Including when
        # types are explicitly disabled with `@keyword(types=None)`. Otherwise get
        # type hints.
        if isclass(method):
            method = method.__init__
        types = getattr(method, 'robot_types', ())
        if types or types is None:
            return types
        try:
            hints = get_type_hints(method)

            if 'return' not in hints:
                has_return = True if re.search(r' *return (?!None).+', inspect.getsource(method)) else False
                if has_return:
                    hints['return'] = typing.Any

            return hints
        except Exception:  # Can raise pretty much anything
            # Not all functions have `__annotations__`.
            # https://github.com/robotframework/robotframework/issues/4059
            return getattr(method, '__annotations__', {})

Is it something that LibDoc could benefit from? What do you think @pekkaklarck ? If yes, I can create a PR from this POC.

(Tested on BuiltIn and SeleniumLibrary)

@pekkaklarck
Copy link
Member

I don't think it's worth the effort to try to parse return information from the source code. It would require something more than using re to get any reliable information. More importantly, this would slow down inspecting keyword information and thus could only be done with Libdoc, not when running tests. All this makes the enhancement pretty complicated compared to just adding return type information to keywords themselves. PRs adding such info to standard libraries would be appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests