Skip to content

Automatic argument conversion based on type information passed to @keyword decorator #2947

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
pekkaklarck opened this issue Aug 29, 2018 · 5 comments

Comments

@pekkaklarck
Copy link
Member

pekkaklarck commented Aug 29, 2018

We have lately been working with automatic argument type conversion based on function annotations (#2890) and argument default values (#2932). This is very useful, but function annotations work only with Python 3 and it doesn't make sense to add default values to all arguments. Having a generic and Python version independent type conversion would be handy, and because the actual type conversion functionality is already implemented we'd only need a way to specify types.

My proposal is to support specifying types using the @keyword decorator by passing types as a dictionary mapping argument names to types:

@keyword(types={'arg1': int, 'arg3': bool})
def example(arg1, arg2, arg3):
    # ...

Additional design decisions related to this proposal:

  • Type information is be stored to the function object as robot_types attribute (consistent with the existing robot_name and robot_tags) that can be also set otherwise.
  • This explicit Robot specific type information has priority over type information got from annotations or default values.
  • It is not necessarily to specify types for all arguments. The above example doesn't specify type for arg2.
  • Like with function annotations, it is possible to specify types for *varargs and **kws.
  • It is an error to specify a type for a non-existing arguments.

UPDATE: Decided to allow specifying types also as a list to make simple cases less verbose. See the comment below for details.

@pekkaklarck
Copy link
Member Author

The example in the description would be equivalent to this example using Python 3 function annotations:

def example(arg1: int, arg2, arg3: bool):
    # ...

@pekkaklarck
Copy link
Member Author

One design decision to be made is how to handle specified types that don't match types we support. Here are some possibilities:

  1. Make all unrecognized values always an error.
  2. Make values that are not types an error. For example, int and str would be fine but 2 and 'foo' would cause an error.
  3. Ignore non-type values altogether. Only use types we recognize for conversion. Show all types in Libdoc outputs.
  4. Store all values and show them in Libdoc outputs. Only use values we recognize for conversion.

This is related to handling function annotations (#2890) that we don't recognize as supported types. Initially that design worked so that all annotations were shown in Libdoc outputs but only types we recognized were used for conversion (same as 4. above). That was later changed to throw all non-type annotations away, use values we recognize for conversion, and show all type annotation in Libdoc outputs (same as 3.). Because function annotations can be used also for other purposes than specifying types (see PEP-3107), we cannot make unrecognized annotations an error. With explicit types specified via the @keyword decorator that could be done, but I'm not sure is that a good idea.

I'm currently thinking we should preserve all types (and annotations) and show them in Libdoc outputs, but naturally only use types we recognize for conversion. This would allow using type information (and annotations) for documentation purposes as illustrated by PEP-3107 and would also be forward compatible with possible new types we support in the future. The only drawback I see is that someone could specify an incorrect type and it wouldn't be reported. I don't think that's very likely and the problem ought to be easy to see if any tests are created for the keyword.

pekkaklarck added a commit that referenced this issue Aug 30, 2018
Initial implementation. Error handling missing.
pekkaklarck added a commit that referenced this issue Aug 30, 2018
This includes values that aren't even types. Naturally only types we
recognize can be used for type conversion.

Affects #2890 and #2947.
@pekkaklarck
Copy link
Member Author

Decided to implement this so that all given type information is preserved and shown by Libdoc. Obviously only types we recognize can be used for conversion.

@pekkaklarck
Copy link
Member Author

Also decided to support giving arguments using a list like

@keyword(types=[int, bool])
def example(arg1, arg2):
    # ...

In simple cases this is less verbose than using a dictionary like

@keyword(types={'arg1': int, 'arg2': bool})
def example(arg1, arg2):
    # ...

Using a list with more arguments than there are types will cause an error, but having less types is fine and None as a type means no type. For example, this is equivalent to the example in the original description:

@keyword(types=[int, None, bool])
def example(arg1, arg2, arg3):
    # ...

pekkaklarck added a commit that referenced this issue Aug 30, 2018
- Non-types don't cause error (#2890 and #2947)
- Invalid type info passed via @Keyword causes explicit errors (#2947)
pekkaklarck added a commit that referenced this issue Aug 31, 2018
Types set via @Keyword decorator (#2947) override types set via
annotations (#2890). This includs empty types ({} or []) but not
None which is the default set by @Keyword.
pekkaklarck added a commit that referenced this issue Aug 31, 2018
Return value type/annotation isn't used for anything yet, but may be
used in the future by Libdoc. Testing that specifying the value doesn't
cause errors. Related to #2890 and #2947.
pekkaklarck added a commit that referenced this issue Aug 31, 2018
- Old "enum" module is supported along with newer "enum34" and
  the standard module in Python 3.4+. Old code didn't work with
  the old module.
- Tests requiring enum are tagged with "require-enum".
- Enum requirement is documented in atest/README.rst and "enum34"
  was conditionally (< 3.0) added to atest/requirements.txt.
- At the same time made lxml installation in atest/requirement.txt
  conditional (CPython and PyPy).

Related to type conversion issues #2890 and #2947.
pekkaklarck added a commit that referenced this issue Sep 13, 2018
- collections.abc.Sequence is now mapped directly to list, not to list
  or tuple.
- collections.abc.Iterable is not supported anymore.
- Explicit tests for Integral, Real and ByteString.
- Test cleanup.
pekkaklarck added a commit that referenced this issue Nov 4, 2018
Makes it easier to disable type conversion altogether, including based
on default values, when it is not desired. For example, eases fixing
regression in Telnet caused by automatic conversion when `None` is a
default value.

Change is related to issue #2947 (specifying types using the
`@keyword` decorator) and to issue #2932 (getting types from default
values).
@pekkaklarck
Copy link
Member Author

Changed functionality so that using @keyword(types=None) disables argument conversion altogether, including getting type information based on default values.

pekkaklarck added a commit that referenced this issue Nov 20, 2018
This isn't likely to be too useful when specifying types using annotations
(#2890) or @Keyword (#2947), but will be useful with dynamic libraries
(#2068) when the library is a proxy getting information elsewhere.
pekkaklarck added a commit that referenced this issue Nov 20, 2018
Also changed handling types specified as a list (typically via
`@keyword` decorator, #2947) so that any falsy value can be used as a
marker telling argument has no type. This allows remote API to use `''`
instead of `None` that's not generally supported by XML-RPC.

Fixes #3008.
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

1 participant