Error generating documentation in latest 3.2 release on py 2.7

Hi,

we generate Robot Documentation (html, xml) for our own library using the api. It used to work, but with the change to 3.2.1 we get an issue.

We are still on Python 2.7 (I know… but we have customers still there)

When we try to generate the docs we get this error (with a traceback I added temporary to the logs)

15:35:28,929 INFO  - Try --help for usage information.
15:35:28,929 INFO  - Building library 'Mock.py' failed: **AttributeError: 'NoneType' object has no attribute 'decode'**
15:35:28,929 INFO  - Traceback (most recent call last):
15:35:28,929 INFO  -   File "C:\Python27\Lib\site-packages\robot\libdocpkg\builder.py", line 43, in LibraryDocumentation
15:35:28,929 INFO  -     libdoc = builder.build(library_or_resource)
15:35:28,929 INFO  -   File "C:\Python27\Lib\site-packages\robot\libdocpkg\robotbuilder.py", line 41, in build
15:35:28,929 INFO  -     libdoc.keywords = KeywordDocBuilder().build_keywords(lib)
15:35:28,929 INFO  -   File "C:\Python27\Lib\site-packages\robot\libdocpkg\robotbuilder.py", line 103, in build_keywords
15:35:28,929 INFO  -     return [self.build_keyword(kw) for kw in lib.handlers]
15:35:28,929 INFO  -   File "C:\Python27\Lib\site-packages\robot\libdocpkg\robotbuilder.py", line 111, in build_keyword
15:35:28,929 INFO  -     source=kw.source,
15:35:28,929 INFO  -   File "C:\Python27\Lib\site-packages\robot\running\handlers.py", line 153, in source
15:35:28,929 INFO  -     return normpath(inspect.getsourcefile(unwrap(handler)))
15:35:28,929 INFO  -   File "C:\Python27\Lib\site-packages\robot\utils\robotpath.py", line 75, in normpath
15:35:28,929 INFO  -     path = system_decode(path)
15:35:28,929 INFO  -   File "C:\Python27\Lib\site-packages\robot\utils\encoding.py", line 104, in system_decode
15:35:28,929 INFO  -     return string.decode(_SYSTEM_ENCODING)
15:35:28,929 INFO  - AttributeError: 'NoneType' object has no attribute 'decode'

Going through the robot code, I found the place where it gets into problem: (robot.running.handlers.py)

class _PythonHandler(_RunnableHandler):

    def __init__(self, library, handler_name, handler_method):
        _RunnableHandler.__init__(self, library, handler_name, handler_method,
                                    getdoc(handler_method))

    def _parse_arguments(self, handler_method):
        return PythonArgumentParser().parse(handler_method, self.longname)

    @property
    def source(self):
        handler = self.current_handler()
        try:
            return normpath(inspect.getsourcefile(unwrap(handler)))
        except TypeError:   # **changing this to Exception:  "solves" our problem**
            return self.library.source

This is sort of, the behavior as it used to be in 3.1.2. Any tips how to handle this the best? Shall I report a bug?

/Richard

Hi Richard,

Is it possible to provide the Mock.py or a minimum reproducable code snippet?
This is typically the best to analyze the issue.

Hi René,

I will check, should be possible!

/Richard

Hi René,

while checking what to provide (a reduced example) I found out what makes the problem…

We are using the decorator module to decorate some of our keyword methods. This makes the keyword documentation (the parameters) correct even if your decorator applies changes to them.
As it seems the inspect.getsourcefile does not like decorated functions using the decorator module. It took me a while to understand what was the problem behind this…

I will investigate if we can upgrade to a later version of this library, which hopefully solves the issue.

/Richard

I thought the latest release 4.4.2 of Decorator would fix the problem, but it is not the case.

I will have to patch the robot source “robot\running\handlers.py” for now. Will see if it can be solved with Decorator somehow.

Here is a small example using the decorator module:

from __future__ import print_function
from decorator import decorator

@decorator
def handleexception(fn, *args, **kwargs):
    return fn(*args, **kwargs)

class KeywordLibrary(object):

    @handleexception
    def MyFunction(self, firstParameter, secondParameter):
        print("%s called"%(__name__) )

When I generate the documentation on the command line:

C:\temp\DecoratorTest>python -m robot.libdoc KeywordLibrary KeywordLibrary.html
Building library 'KeywordLibrary' failed: AttributeError: 'NoneType' object has no attribute 'decode'

Try --help for usage information.

I have to add, the problem only occurs on python 2.

The reason for using the decorator module is the function signature in the documentation is properly generated.

An example using decorator or not: (My Function is decorated using a simple function and the documentation shows the signature of the decorator instead)

Keyword Arguments Documentation
My Decorated Function firstParameter, secondParameter
My Function *args, **kwargs

Ok. This is going over my mind.
Why do you need these decorators in the first place?

Hello René,

well the reason for decorators in python, is it is an easy way to add common functionality to functions/methods/(and keywords!) without touching the original implementation.

Using the simple python way, does not correctly keep the function signature as can be seen above. Here the decorator module comes handy, as a commonly used module.

for this exact use-case I have a decorator I call HandleException

class ExpectErrorException(Exception):
     pass

@decorator
def HandleException(fn, *args, **kwargs):
    continueOnFailure = kwargs.pop('continueOnFailure',False) in ALLTRUE
    expectError = kwargs.pop('expectError',False) in ALLTRUE
    try:
        returnCode = fn(*args, **kwargs)
        if expectError:
            raise ExpectErrorException("expectError turned on, and keyword did not fail")
        return returnCode

    except Exception as exception:
        if continueOnFailure:
            exception.ROBOT_CONTINUE_ON_FAILURE = continueOnFailure
            exception.ROBOT_EXIT_ON_FAILURE = not continueOnFailure

        if expectError and not isinstance(exception, ExpectErrorException):
            print(exception)
            return exception

        raise

This means that all keywords having this decorator understands the extra parameters continueOnFailure and expectError to easy control what I expect from the keyword call flow.

And for handlers.py I did this change:

class _PythonHandler(_RunnableHandler):

    ...

    @property
    def source(self):
        handler = self.current_handler()
        try:
            return normpath(inspect.getsourcefile(unwrap(handler)) or self.library.source) # also handle None
        except TypeError:
            return self.library.source
    ...

This means if inspect.getsourcefile returns None (which seems to be a common problem searching the net) the OR kicks in.

:joy:

Ok yes that makes more sense!
Iwas thinking about:

@decorator
def handleexception(fn, *args, **kwargs):
    return fn(*args, **kwargs)

Why use this…

But of course your decorator is a logical cool usage!

Now I understand your question :grinning:

What do you think, how should I best report this. I guess it should not be any problem changing it as I propose.

I would create an issue at robotframework project in Github.
Then Pekka could give you an advice.

Done! Thanks!

And now something completely different, why is my posts above “flagged by the community” as SPAM?

1 Like

Hi Richard,
That was a “Spam” filter of this Discourse Forum.
It detected that you did some posts with a link to the same domain. (the decorator page)
And this was flagged.

I didn´t know about this feature and get a message.
i reactivated your posts. Dont worry this was just a mistake by the detection!

Sorry!
Regards

Hi René,

The github ticket is already closed by Pekka. Thanks for your support.

/Richard

1 Like