Skip to content

Commit

Permalink
Added yaml launch frontend implementation
Browse files Browse the repository at this point in the history
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
  • Loading branch information
ivanpauno committed May 10, 2019
1 parent 58d958d commit b28736f
Show file tree
Hide file tree
Showing 10 changed files with 349 additions and 0 deletions.
23 changes: 23 additions & 0 deletions launch_yaml/launch_yaml/__init__.py
@@ -0,0 +1,23 @@
# Copyright 2019 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Main entry point for the `launch_xml` package."""

from .entity import Entity
from .parser import Parser

__all__ = [
'Entity',
'Parser',
]
103 changes: 103 additions & 0 deletions launch_yaml/launch_yaml/entity.py
@@ -0,0 +1,103 @@
# Copyright 2019 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Module for YAML Entity class."""

from typing import List
from typing import Optional
from typing import Text
from typing import Tuple
from typing import Union

from launch_frontend import Entity as BaseEntity
from launch_frontend.type_utils import check_type


class Entity(BaseEntity):
"""Single item in the intermediate YAML front_end representation."""

def __init__(
self,
element: dict,
*,
type_name: Optional[Text] = None,
parent: 'Entity' = None
) -> Text:
"""Construnctor."""
if type_name is None:
if len(element) != 1:
raise RuntimeError('Expected a len 1 dictionary')
self.__type_name = list(element.keys())[0]
self.__element = element[self.__type_name]
else:
self.__type_name = type_name
self.__element = element
self.__parent = parent

@property
def type_name(self) -> Text:
"""Get Entity type."""
return self.__type_name

@property
def parent(self) -> Optional['Entity']:
"""Get Entity parent."""
return self.__parent

@property
def children(self) -> List['Entity']:
"""Get the Entity's children."""
if not isinstance(self.__element, list):
raise TypeError('Expected a list, got {}'.format(type(self.element)))
return [Entity(child) for child in self.__element]

def get_attr(
self,
name: Text,
*,
types: Union[Text, Tuple[Text]] = 'str',
optional: bool = False
) -> Optional[Union[
Text,
int,
float,
List[Text],
List[int],
List[float],
List['Entity']
]]:
"""Access an attribute of the entity."""
if name not in self.__element:
if not optional:
raise AttributeError(
'Can not find attribute {} in Entity {}'.format(
name, self.type_name))
else:
return None
data = self.__element[name]
if types == 'list[Entity]':
if isinstance(data, list) and isinstance(data[0], dict):
return [Entity(child, type_name=name) for child in data]
raise TypeError(
'Attribute {} of Entity {} expected to be a list of dictionaries.'.format(
name, self.type_name
)
)
if not check_type(data, types):
raise TypeError(
'Attribute {} of Entity {} expected to be of type {}, got {}'.format(
name, self.type_name, types, type(data)
)
)
return data
39 changes: 39 additions & 0 deletions launch_yaml/launch_yaml/parser.py
@@ -0,0 +1,39 @@
# Copyright 2019 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Module for YAML Parser class."""

import io
from typing import Union

import launch_frontend

import yaml

from .entity import Entity


class Parser(launch_frontend.Parser):
"""YAML parser implementation."""

@classmethod
def load(
cls,
stream: Union[str, io.TextIOBase],
) -> (Entity, 'Parser'):
"""Load a YAML launch file."""
if isinstance(stream, str):
stream = open(stream, 'r')
"""Return entity loaded with markup file."""
return (Entity(yaml.safe_load(stream)), cls)
21 changes: 21 additions & 0 deletions launch_yaml/package.xml
@@ -0,0 +1,21 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>launch_yaml</name>
<version>0.7.3</version>
<description>The ROS launch YAML frontend.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
<license>Apache License 2.0</license>

<depend>launch</depend>
<depend>launch_frontend</depend>

<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>

<export>
<build_type>ament_python</build_type>
</export>
</package>
34 changes: 34 additions & 0 deletions launch_yaml/setup.py
@@ -0,0 +1,34 @@
from setuptools import find_packages
from setuptools import setup

setup(
name='launch_yaml',
version='0.7.3',
packages=find_packages(exclude=['test']),
install_requires=['setuptools'],
zip_safe=True,
author='Ivan Paunovic',
author_email='ivanpauno@ekumenlabs.com',
maintainer='Ivan Paunovic',
maintainer_email='ivanpauno@ekumenlabs.com',
url='https://github.com/ros2/launch',
download_url='https://github.com/ros2/launch/releases',
keywords=['ROS'],
classifiers=[
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Topic :: Software Development',
],
description='YAML `launch` front-end extension.',
long_description=(
'This package provides YAML parsing ability to `launch-frontend` package.'
),
license='Apache License, Version 2.0',
tests_require=['pytest'],
entry_points={
'launch_frontend.parser': [
'yaml = launch_yaml:Parser',
],
}
)
12 changes: 12 additions & 0 deletions launch_yaml/test/launch_yaml/executable.yaml
@@ -0,0 +1,12 @@
launch:
- executable:
cmd: ls
cwd: '/'
name: my_ls
args: -l -a -s
shell: true
output: log
launch_prefix: $(env LAUNCH_PREFIX)
env:
- name: var
value: '1'
48 changes: 48 additions & 0 deletions launch_yaml/test/launch_yaml/test_executable.py
@@ -0,0 +1,48 @@
# Copyright 2019 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Test parsing an executable action."""

from pathlib import Path

from launch import LaunchService

from launch_frontend import Parser


def test_executable():
"""Parse executable yaml example."""
root_entity, parser = Parser.load(str(Path(__file__).parent / 'executable.yaml'))
ld = parser.parse_description(root_entity)
executable = ld.entities[0]
cmd = [i[0].perform(None) for i in executable.cmd]
assert(
cmd == ['ls', '-l', '-a', '-s'])
assert(executable.cwd[0].perform(None) == '/')
assert(executable.name[0].perform(None) == 'my_ls')
assert(executable.shell is True)
assert(executable.output == 'log')
key, value = executable.additional_env[0]
key = key[0].perform(None)
value = value[0].perform(None)
assert(key == 'var')
assert(value == '1')
# assert(executable.prefix[0].perform(None) == 'time')
ls = LaunchService()
ls.include_launch_description(ld)
assert(0 == ls.run())


if __name__ == '__main__':
test_executable()
23 changes: 23 additions & 0 deletions launch_yaml/test/test_copyright.py
@@ -0,0 +1,23 @@
# Copyright 2017 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ament_copyright.main import main
import pytest


@pytest.mark.copyright
@pytest.mark.linter
def test_copyright():
rc = main(argv=['.', 'test'])
assert rc == 0, 'Found errors'
23 changes: 23 additions & 0 deletions launch_yaml/test/test_flake8.py
@@ -0,0 +1,23 @@
# Copyright 2017 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ament_flake8.main import main
import pytest


@pytest.mark.flake8
@pytest.mark.linter
def test_flake8():
rc = main(argv=[])
assert rc == 0, 'Found errors'
23 changes: 23 additions & 0 deletions launch_yaml/test/test_pep257.py
@@ -0,0 +1,23 @@
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ament_pep257.main import main
import pytest


@pytest.mark.linter
@pytest.mark.pep257
def test_pep257():
rc = main(argv=[])
assert rc == 0, 'Found code style errors / warnings'

0 comments on commit b28736f

Please sign in to comment.