pax_global_header00006660000000000000000000000064140433027570014517gustar00rootroot0000000000000052 comment=9c7c7dfd690589df89e3d67e721fc7e12cf5c7a7 genpy-0.6.16/000077500000000000000000000000001404330275700127335ustar00rootroot00000000000000genpy-0.6.16/.gitignore000066400000000000000000000000331404330275700147170ustar00rootroot00000000000000*.pyc ._* *~ src/genpy/msg genpy-0.6.16/CHANGELOG.rst000066400000000000000000000202541404330275700147570ustar00rootroot00000000000000^^^^^^^^^^^^^^^^^^^^^^^^^^^ Changelog for package genpy ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 0.6.16 (2021-05-01) ------------------- * Fix runtime incompatibility with messages generated with 0.6.14 (`#139 `_) * Contributors: Shane Loretz 0.6.15 (2021-04-12) ------------------- * Update maintainers (`#135 `_) * Check for Python 3 before looking up codec (`#134 `_) * Add check for float32 and float64 to check_type (`#131 `_) * Contributors: Dirk Thomas, Martin Günther, Shane Loretz 0.6.14 (2020-08-10) ------------------- * create Struct objects to save memory (`#129 `_) 0.6.13 (2020-07-20) ------------------- * don't raise exceptions on unicode decode error (`#127 `_) 0.6.12 (2020-05-28) ------------------- * fix check_type for uint8[] to accept bytes (`#123 `_) 0.6.11 (2020-05-12) ------------------- * support non-int integral time parameters (`#121 `_) 0.6.10 (2020-01-24) ------------------- * various code cleanup (`#117 `_) * update logic for newer PyYAML output for dump() (`#116 `_) * small optimization in dynamic.py (`#109 `_) * use setuptools instead of distutils (`#115 `_) * bump CMake version to avoid CMP0048 warning (`#114 `_) * serialization: always set _x var for correct exception msg (`#113 `_) * sort generated imports to make them reproducible (`#111 `_) * make the generated "struct" constructs reproducible (`#110 `_) 0.6.9 (2019-10-03) ------------------ * fix Python 3 buffer handling (`#106 `_) * Python 3 compatibility (`#104 `_) * fix usage of undefined variables in exception (`#105 `_) * convert map iterator to list (`#103 `_) * fix negative limit check for signed ints (`#102 `_) * failing test case for UTF-8 encoded characters in comments (`#95 `_) 0.6.8 (2019-03-04) ------------------ * check size of fixed sized arrays when serializing (`#92 `_) * allow returning derived types in overloaded operators (`#100 `_) * reload() was move into importlib in Python 3 (`#98 `_) * fix _convert_getattr for handling uint8[] message fields in Python 3 (`#96 `_) 0.6.7 (2017-10-26) ------------------ * use errno to detect existing dir (`#89 `_) * fix typo (`#84 `_) 0.6.6 (2017-07-27) ------------------ * add escaping for strings which is valid in YAML (`#79 `_) * fix inefficient canon method in rostime (`#77 `_) 0.6.5 (2017-03-06) ------------------ * expose spec for dynamically generated messages (`#75 `_) 0.6.4 (2017-02-27) ------------------ * fix default value for non-primitive fixed-size arrays, regression of 0.6.1 (`#74 `) 0.6.3 (2016-10-24) ------------------ * fix Python 3 regressions (`#71 `_) * use Python safe subfields for nested types (`#69 `_) 0.6.2 (2016-09-03) ------------------ * fix regression regarding lazy init introduced in 0.6.1 (`#67 `_) 0.6.1 (2016-09-02) ------------------ * lazy init struct (`#65 `_) * fix default value of lists to not expand to N items in the generated code (`#64 `_) * simpler and more canonical hash (`#55 `_) * various improvements to the time and duration classes (`#63 `_) 0.6.0 (2016-04-21) ------------------ * change semantic of integer division for duration (`#59 `_) 0.5.9 (2016-04-19) ------------------ * warn about using floor division of durations (`#58 `_) * allow durations to be divided by other durations (`#48 `_) * avoid adding newline in msg_generator (`#47 `_, `#51 `_) 0.5.8 (2016-03-09) ------------------ * right align nsec fields of timestamps (`#45 `_) * fix order of imports in generated init files deterministic (`#44 `_) * fix exception handling code using undefined variable (`#42 `_) * add test for expected exception when serializing wrong type 0.5.7 (2015-11-09) ------------------ * add line about encoding to generated Python files (`#41 `_) 0.5.6 (2015-10-12) ------------------ * fix handling of dynamic message classes with names containing other message classes as substrings (`#40 `_) 0.5.5 (2015-09-19) ------------------ * fix handling of dynamic message classes with the same name (`#37 `_) * fix Duration.abs() when sec is zero (`#35 `_) 0.5.4 (2014-12-22) ------------------ * add support for fixed-width floating-point and integer array values (`ros/ros_comm#400 `_) * add missing test dependency on yaml 0.5.3 (2014-06-02) ------------------ * make TVal more similar to generated messages for introspection (`ros/std_msgs#6 `_) 0.5.2 (2014-05-08) ------------------ * fix usage of load_manifest() introduced in 0.5.1 (`#28 `_) 0.5.1 (2014-05-07) ------------------ * resolve message classes from dry packages (`ros/ros_comm#293 `_) * add architecture_independent flag in package.xml (`#27 `_) 0.5.0 (2014-02-25) ------------------ * use catkin_install_python() to install Python scripts (`#25 `_) 0.4.15 (2014-01-07) ------------------- * python 3 compatibility (`#22 `_) * use PYTHON_EXECUTABLE when invoking scripts for better Windows support (`#23 `_) * improve exception message when message type does not match (`#21 `_) 0.4.14 (2013-08-21) ------------------- * make genpy relocatable (`ros/catkin#490 `_) * enable int/long values for list of time/duration (`#13 `_) * fix issue with time/duration message fields (without std_msgs prefix) when used as array (`ros/ros_comm#252 `_) * fix Time() for seconds being of type long on 32-bit systems (fix `#15 `_) * fix passing keys to _fill_message_args (`#20 `_) 0.4.13 (2013-07-03) ------------------- * check for CATKIN_ENABLE_TESTING to enable configure without tests 0.4.12 (2013-06-18) ------------------- * fix deserialize bytes in Python3 (`#10 `_) 0.4.11 (2013-03-08) ------------------- * fix handling spaces in folder names (`ros/catkin#375 `_) 0.4.10 (2012-12-21) ------------------- * first public release for Groovy genpy-0.6.16/CMakeLists.txt000066400000000000000000000014251404330275700154750ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0.2) project(genpy) find_package(catkin REQUIRED COMPONENTS genmsg) catkin_package( CATKIN_DEPENDS genmsg CFG_EXTRAS genpy-extras.cmake ) add_subdirectory(scripts) file(WRITE ${CATKIN_DEVEL_PREFIX}/${GENMSG_LANGS_DESTINATION}/genpy "Python") install(FILES ${CATKIN_DEVEL_PREFIX}/${GENMSG_LANGS_DESTINATION}/genpy DESTINATION ${GENMSG_LANGS_DESTINATION}) catkin_python_setup() if(CATKIN_ENABLE_TESTING) assert(CATKIN_ENV) add_custom_target(generate_test_messages COMMAND "${CATKIN_ENV}" "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/test/msg/generate_test_messages.py") if(TARGET tests) add_dependencies(tests generate_test_messages) endif() catkin_add_nosetests(test DEPENDENCIES generate_test_messages) endif() genpy-0.6.16/README.md000066400000000000000000000006561404330275700142210ustar00rootroot00000000000000# genpy The Python ROS message and service generator. ## Examples for generating messages with dependencies ```console ./scripts/genmsg_py.py -p std_msgs -Istd_msgs:`rospack find std_msgs`/msg -o gen `rospack find std_msgs`/msg/String.msg ./scripts/genmsg_py.py -p geometry_msgs -Istd_msgs:`rospack find std_msgs`/msg -Igeometry_msgs:`rospack find geometry_msgs`/msg -o gen `rospack find geometry_msgs`/msg/PoseStamped.msg ``` genpy-0.6.16/cmake/000077500000000000000000000000001404330275700140135ustar00rootroot00000000000000genpy-0.6.16/cmake/genpy-extras.cmake.em000066400000000000000000000060251404330275700200460ustar00rootroot00000000000000@[if DEVELSPACE]@ # location of scripts in develspace set(GENPY_BIN_DIR "@(CMAKE_CURRENT_SOURCE_DIR)/scripts") @[else]@ # location of scripts in installspace set(GENPY_BIN_DIR "${genpy_DIR}/../../../@(CATKIN_PACKAGE_BIN_DESTINATION)") @[end if]@ set(GENMSG_PY_BIN ${GENPY_BIN_DIR}/genmsg_py.py) set(GENSRV_PY_BIN ${GENPY_BIN_DIR}/gensrv_py.py) # Generate .msg->.h for py # The generated .h files should be added ALL_GEN_OUTPUT_FILES_py macro(_generate_msg_py ARG_PKG ARG_MSG ARG_IFLAGS ARG_MSG_DEPS ARG_GEN_OUTPUT_DIR) #Append msg to output dir set(GEN_OUTPUT_DIR "${ARG_GEN_OUTPUT_DIR}/msg") file(MAKE_DIRECTORY ${GEN_OUTPUT_DIR}) #Create input and output filenames get_filename_component(MSG_SHORT_NAME ${ARG_MSG} NAME_WE) set(MSG_GENERATED_NAME _${MSG_SHORT_NAME}.py) set(GEN_OUTPUT_FILE ${GEN_OUTPUT_DIR}/${MSG_GENERATED_NAME}) add_custom_command(OUTPUT ${GEN_OUTPUT_FILE} DEPENDS ${GENMSG_PY_BIN} ${ARG_MSG} ${ARG_MSG_DEPS} COMMAND ${CATKIN_ENV} ${PYTHON_EXECUTABLE} ${GENMSG_PY_BIN} ${ARG_MSG} ${ARG_IFLAGS} -p ${ARG_PKG} -o ${GEN_OUTPUT_DIR} COMMENT "Generating Python from MSG ${ARG_PKG}/${MSG_SHORT_NAME}" ) list(APPEND ALL_GEN_OUTPUT_FILES_py ${GEN_OUTPUT_FILE}) endmacro() #todo, these macros are practically equal. Check for input file extension instead macro(_generate_srv_py ARG_PKG ARG_SRV ARG_IFLAGS ARG_MSG_DEPS ARG_GEN_OUTPUT_DIR) #Append msg to output dir set(GEN_OUTPUT_DIR "${ARG_GEN_OUTPUT_DIR}/srv") file(MAKE_DIRECTORY ${GEN_OUTPUT_DIR}) #Create input and output filenames get_filename_component(SRV_SHORT_NAME ${ARG_SRV} NAME_WE) set(SRV_GENERATED_NAME _${SRV_SHORT_NAME}.py) set(GEN_OUTPUT_FILE ${GEN_OUTPUT_DIR}/${SRV_GENERATED_NAME}) add_custom_command(OUTPUT ${GEN_OUTPUT_FILE} DEPENDS ${GENSRV_PY_BIN} ${ARG_SRV} ${ARG_MSG_DEPS} COMMAND ${CATKIN_ENV} ${PYTHON_EXECUTABLE} ${GENSRV_PY_BIN} ${ARG_SRV} ${ARG_IFLAGS} -p ${ARG_PKG} -o ${GEN_OUTPUT_DIR} COMMENT "Generating Python code from SRV ${ARG_PKG}/${SRV_SHORT_NAME}" ) list(APPEND ALL_GEN_OUTPUT_FILES_py ${GEN_OUTPUT_FILE}) endmacro() macro(_generate_module_py ARG_PKG ARG_GEN_OUTPUT_DIR ARG_GENERATED_FILES) # generate empty __init__ to make parent folder of msg/srv a python module if(NOT EXISTS ${ARG_GEN_OUTPUT_DIR}/__init__.py) file(WRITE ${ARG_GEN_OUTPUT_DIR}/__init__.py "") endif() #Append msg to output dir foreach(type "msg" "srv") set(GEN_OUTPUT_DIR "${ARG_GEN_OUTPUT_DIR}/${type}") set(GEN_OUTPUT_FILE ${GEN_OUTPUT_DIR}/__init__.py) if(IS_DIRECTORY ${GEN_OUTPUT_DIR}) add_custom_command(OUTPUT ${GEN_OUTPUT_FILE} DEPENDS ${GENMSG_PY_BIN} ${ARG_GENERATED_FILES} COMMAND ${CATKIN_ENV} ${PYTHON_EXECUTABLE} ${GENMSG_PY_BIN} -o ${GEN_OUTPUT_DIR} --initpy COMMENT "Generating Python ${type} __init__.py for ${ARG_PKG}") list(APPEND ALL_GEN_OUTPUT_FILES_py ${GEN_OUTPUT_FILE}) endif() endforeach() endmacro() if(NOT EXISTS @(PROJECT_NAME)_SOURCE_DIR) set(genpy_INSTALL_DIR ${PYTHON_INSTALL_DIR}) endif() genpy-0.6.16/doc/000077500000000000000000000000001404330275700135005ustar00rootroot00000000000000genpy-0.6.16/doc/Makefile000066400000000000000000000107521404330275700151450ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/genpy.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/genpy.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/genpy" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/genpy" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." genpy-0.6.16/doc/conf.py000066400000000000000000000157621404330275700150120ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # genpy documentation build configuration file, created by # sphinx-quickstart on Thu Oct 6 14:10:15 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'genpy' copyright = u'2011, Ken Conley' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.1.0' # The full version, including alpha/beta/rc tags. release = '0.1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'genpydoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'genpy.tex', u'genpy Documentation', u'Ken Conley', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'genpy', u'genpy Documentation', [u'Ken Conley'], 1) ] # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} genpy-0.6.16/doc/index.rst000066400000000000000000000006421404330275700153430ustar00rootroot00000000000000.. genpy documentation master file, created by sphinx-quickstart on Thu Oct 6 14:10:15 2011. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to genpy's documentation! ================================= Contents: .. toctree:: :maxdepth: 2 Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` genpy-0.6.16/package.xml000066400000000000000000000027621404330275700150570ustar00rootroot00000000000000 genpy 0.6.16 Python ROS message and service generators. Mabel Zhang Shane Loretz BSD http://wiki.ros.org/genpy https://github.com/ros/genpy/issues https://github.com/ros/genpy Ken Conley Troy Straszheim Morten Kjaergaard Dirk Thomas genmsg catkin python-setuptools python3-setuptools python-yaml python3-yaml python-numpy python3-numpy py genpy-0.6.16/rosdoc.yaml000066400000000000000000000000531404330275700151060ustar00rootroot00000000000000 - builder: sphinx sphinx_root_dir: doc genpy-0.6.16/scripts/000077500000000000000000000000001404330275700144225ustar00rootroot00000000000000genpy-0.6.16/scripts/CMakeLists.txt000066400000000000000000000001551404330275700171630ustar00rootroot00000000000000catkin_install_python( PROGRAMS genmsg_py.py gensrv_py.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) genpy-0.6.16/scripts/genmsg_py.py000077500000000000000000000037161404330275700167760ustar00rootroot00000000000000#!/usr/bin/env python # Software License Agreement (BSD License) # # Copyright (c) 2008, Willow Garage, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # * Neither the name of Willow Garage, Inc. nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # Revision $Id: genmsg_py.py 9002 2010-04-09 01:08:47Z kwc $ """ ROS message source code generation for Python. Converts ROS .msg files in a package into Python source code implementations. """ import sys import genpy.generator import genpy.genpy_main if __name__ == '__main__': genpy.genpy_main.genmain(sys.argv, 'genmsg_py.py', genpy.generator.MsgGenerator()) genpy-0.6.16/scripts/gensrv_py.py000077500000000000000000000036151404330275700170200ustar00rootroot00000000000000#!/usr/bin/env python # Software License Agreement (BSD License) # # Copyright (c) 2008, Willow Garage, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # * Neither the name of Willow Garage, Inc. nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. """ ROS service source code generation for Python. Converts ROS .srv files into Python source code implementations. """ import os import sys import genpy.generator import genpy.genpy_main if __name__ == "__main__": genpy.genpy_main.genmain(sys.argv, 'gensrv_py.py', genpy.generator.SrvGenerator()) genpy-0.6.16/setup.py000066400000000000000000000003251404330275700144450ustar00rootroot00000000000000from catkin_pkg.python_setup import generate_distutils_setup from setuptools import setup d = generate_distutils_setup( packages=['genpy'], package_dir={'': 'src'}, requires=['genmsg'] ) setup(**d) genpy-0.6.16/src/000077500000000000000000000000001404330275700135225ustar00rootroot00000000000000genpy-0.6.16/src/genpy/000077500000000000000000000000001404330275700146445ustar00rootroot00000000000000genpy-0.6.16/src/genpy/__init__.py000066400000000000000000000035111404330275700167550ustar00rootroot00000000000000# Software License Agreement (BSD License) # # Copyright (c) 2011, Willow Garage, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # * Neither the name of Willow Garage, Inc. nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. from . rostime import Time, Duration, TVal from . message import Message, SerializationError, DeserializationError, MessageException, struct_I __all__ = [ 'Time', 'Duration', 'TVal', 'Message', 'SerializationError', 'DeserializationError', 'MessageException', 'struct_I'] genpy-0.6.16/src/genpy/base.py000066400000000000000000000050251404330275700161320ustar00rootroot00000000000000# Software License Agreement (BSD License) # # Copyright (c) 2008, Willow Garage, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # * Neither the name of Willow Garage, Inc. nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. ################################################################################ # Primitive type handling for ROS builtin types SIMPLE_TYPES_DICT = { # see python module struct 'int8': 'b', 'uint8': 'B', # Python 2.6 adds in '?' for C99 _Bool, which appears equivalent to an uint8, # thus, we use uint8 'bool': 'B', 'int16': 'h', 'uint16': 'H', 'int32': 'i', 'uint32': 'I', 'int64': 'q', 'uint64': 'Q', 'float32': 'f', 'float64': 'd', # deprecated 'char': 'B', # unsigned 'byte': 'b', # signed } # Simple types are primitives with fixed-serialization length SIMPLE_TYPES = list(SIMPLE_TYPES_DICT.keys()) # py3k def is_simple(type_): """ Check if a type is a 'simple' type. :returns: ``True`` if type is a 'simple' type, i.e. is of fixed/known serialization length. This is effectively all primitive types except for string, ``bool`` """ return type_ in SIMPLE_TYPES genpy-0.6.16/src/genpy/dynamic.py000066400000000000000000000200221404330275700166360ustar00rootroot00000000000000# Software License Agreement (BSD License) # # Copyright (c) 2008, Willow Garage, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # * Neither the name of Willow Garage, Inc. nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. """Dynamic generation of deserializer.""" from __future__ import print_function try: from cStringIO import StringIO # Python 2.x except ImportError: from io import StringIO # Python 3.x import atexit import os import re import shutil import sys import tempfile import genmsg import genmsg.msg_loader from genmsg import MsgContext, MsgGenerationException from . generator import msg_generator def _generate_dynamic_specs(msg_context, specs, dep_msg): """ Dynamically generate message specificition. :param dep_msg: text of dependent .msg definition, ``str`` :returns: type name, message spec, ``str, MsgSpec`` :raises: MsgGenerationException If dep_msg is improperly formatted """ line1 = dep_msg.find('\n') msg_line = dep_msg[:line1] if not msg_line.startswith('MSG: '): raise MsgGenerationException("invalid input to generate_dynamic: dependent type is missing 'MSG:' type declaration header") dep_type = msg_line[5:].strip() dep_pkg, dep_base_type = genmsg.package_resource_name(dep_type) dep_spec = genmsg.msg_loader.load_msg_from_string(msg_context, dep_msg[line1+1:], dep_type) return dep_type, dep_spec def _gen_dyn_name(pkg, base_type): """ Modify pkg/base_type name so that it can safely co-exist with statically generated files. :returns: name to use for pkg/base_type for dynamically generated message class. @rtype: str """ return '_%s__%s' % (pkg, base_type) def _gen_dyn_modify_references(py_text, current_type, types): """ Modify the generated code to rewrite names such that the code can safely co-exist with messages of the same name. :param py_text: genmsg_py-generated Python source code, ``str`` :returns: updated text, ``str`` """ for t in types: pkg, base_type = genmsg.package_resource_name(t) gen_name = _gen_dyn_name(pkg, base_type) # Several things we have to rewrite: # - remove any import statements py_text = py_text.replace('import %s.msg' % pkg, '') # - rewrite any references to class if '%s.msg.%s' % (pkg, base_type) in py_text: # only call expensive re.sub if the class name is in the string py_text = re.sub(r'(? 1: new_pattern = new_pattern + str(count) + prev else: new_pattern = new_pattern + prev prev = c count = 1 if count > 1: new_pattern = new_pattern + str(count) + c else: new_pattern = new_pattern + prev return new_pattern def serialize(expr): """ Return code to write expression to buffer. :param expr str: string python expression that is evaluated for serialization :returns str: python call to write value returned by expr to serialization buffer """ return 'buff.write(%s)' % expr # int32 is very common due to length serialization, so it is special cased def int32_pack(var): """ Pack int32. :param var: variable name, ``str`` :returns: struct packing code for an int32 """ return serialize('_struct_I.pack(%s)' % var) # int32 is very common due to length serialization, so it is special cased def int32_unpack(var, buff): """ Unpack int32. :param var: variable name, ``str`` :returns: struct unpacking code for an int32 """ return '(%s,) = _struct_I.unpack(%s)' % (var, buff) # NOTE: '<' = little endian def pack(pattern, vars_): """ Create struct.pack call for when pattern is a string pattern. :param pattern: pattern for pack, ``str`` :param vars_: name of variables to pack, ``str`` """ # - store pattern in context pattern = reduce_pattern(pattern) add_pattern(pattern) return serialize('_get_struct_%s().pack(%s)' % (pattern, vars_)) def pack2(pattern, vars_): """ Create struct.pack call for when pattern is the name of a variable. :param pattern: name of variable storing string pattern, ``struct`` :param vars_: name of variables to pack, ``str`` """ return serialize('struct.Struct(%s).pack(%s)' % (pattern, vars_)) def unpack(var, pattern, buff): """ Create struct.unpack call for when pattern is a string pattern. :param var: name of variable to unpack, ``str`` :param pattern: pattern for pack, ``str`` :param buff: buffer to unpack from, ``str`` """ # - store pattern in context pattern = reduce_pattern(pattern) add_pattern(pattern) return var + ' = _get_struct_%s().unpack(%s)' % (pattern, buff) def unpack2(var, pattern, buff): """ Create struct.unpack call for when pattern refers to variable. :param var: variable the stores the result of unpack call, ``str`` :param pattern: name of variable that unpack will read from, ``str`` :param buff: buffer that the unpack reads from, ``StringIO`` """ return '%s = struct.unpack(%s, %s)' % (var, pattern, buff) def unpack3(var, struct_var, buff): """ Create an unpack call on the ``struct.Struct`` object with the name ``struct_var``. :param var: variable the stores the result of unpack call, ``str`` :param str struct_var: name of the struct variable used to unpack ``buff`` :param buff: buffer that the unpack reads from, ``StringIO`` """ return '%s = %s.unpack(%s)' % (var, struct_var, buff) genpy-0.6.16/src/genpy/generator.py000066400000000000000000001224551404330275700172150ustar00rootroot00000000000000# Software License Agreement (BSD License) # # Copyright (c) 2008, Willow Garage, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # * Neither the name of Willow Garage, Inc. nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. """ Library for Python message generation. The structure of the serialization descends several levels of serializers: - msg_generator: generator for an individual msg file - serialize_fn_generator: generator for msg.serialize() - serializer_generator - field-type-specific serializers raise MsgGenerationException("unknown file extension: %s"%f) - deserialize_fn_generator: generator for msg.deserialize() - serializer_generator - field-type-specific serializers """ from __future__ import print_function import errno import keyword import os import struct import sys import traceback import genmsg import genmsg.gentools import genmsg.msg_loader import genmsg.msgs from genmsg import InvalidMsgSpec from genmsg import MsgContext from genmsg import MsgGenerationException from genmsg import MsgSpec from genmsg.base import log from . base import SIMPLE_TYPES # noqa: F401 from . base import is_simple from . generate_numpy import NUMPY_DTYPE from . generate_numpy import pack_numpy from . generate_numpy import unpack_numpy from . generate_struct import clear_patterns from . generate_struct import compute_struct_pattern from . generate_struct import get_patterns from . generate_struct import int32_pack from . generate_struct import int32_unpack from . generate_struct import pack from . generate_struct import pack2 from . generate_struct import reduce_pattern from . generate_struct import unpack from . generate_struct import unpack2 from . generate_struct import unpack3 # indent width INDENT = ' ' def get_registered_ex(msg_context, type_): """ Wrap unknown types with a MsgGenerationException. Wrapper for get_registered. :param type_: ROS message type, ``str`` """ try: return msg_context.get_registered(type_) except Exception: raise MsgGenerationException('Unknown type [%s]. Please check that the manifest.xml correctly declares dependencies.' % type_) ################################################################################ # Special type handling for ROS builtin types that are not primitives class Special: def __init__(self, constructor, post_deserialize, import_str): # noqa: D205, D400 """ :param constructor: expression to instantiate new type instance for deserialization, ``str`` :param post_Deserialize: format string for expression to evaluate on type instance after deserialization is complete., ``str`` variable name will be passed in as the single argument to format string. :param import_str: import to include if type is present, ``str`` """ self.constructor = constructor self.post_deserialize = post_deserialize self.import_str = import_str def get_post_deserialize(self, varname): # noqa: D205, D400, D401 """ :returns: Post-deserialization code to executed (unindented) or ``None`` if no post-deserialization is required, ``str`` """ if self.post_deserialize: return self.post_deserialize % varname else: return None _SPECIAL_TYPES = { genmsg.HEADER: Special('std_msgs.msg._Header.Header()', None, 'import std_msgs.msg'), genmsg.TIME: Special('genpy.Time()', '%s.canon()', 'import genpy'), genmsg.DURATION: Special('genpy.Duration()', '%s.canon()', 'import genpy'), } def is_special(type_): # noqa: D200, D400, D401 """ :returns: ``True` if *type_* is a special type (i.e. builtin represented as a class instead of a primitive), ``bool`` """ return type_ in _SPECIAL_TYPES def get_special(type_): # noqa: D200, D400, D401 """ :returns: special type handler for *type_* or ``None``, ``Special`` """ return _SPECIAL_TYPES.get(type_, None) ################################################################################ # utilities # #671 def default_value(msg_context, field_type, default_package): """ Compute default value for field_type. :param default_package: default package, ``str`` :param field_type: ROS .msg field type, ``str`` :returns: default value encoded in Python string representation, ``str`` """ if field_type in [ 'byte', 'int8', 'int16', 'int32', 'int64', 'char', 'uint8', 'uint16', 'uint32', 'uint64', ]: return '0' elif field_type in ['float32', 'float64']: return '0.' elif field_type == 'string': return "''" elif field_type == 'bool': return 'False' elif field_type.endswith(']'): # array type base_type, is_array, array_len = genmsg.msgs.parse_type(field_type) if base_type in ['char', 'uint8']: # strings, char[], and uint8s are all optimized to be strings if array_len is not None: return r"b'\0'*%s" % array_len else: return "b''" elif array_len is None: # var-length return '[]' else: # fixed-length def_val = default_value(msg_context, base_type, default_package) if base_type in [ 'byte', 'int8', 'int16', 'int32', 'int64', 'uint16', 'uint32', 'uint64', 'float32', 'float64', 'string', 'bool' ]: # fill primitive values return '[' + def_val + '] * ' + str(array_len) else: # fill values with distinct instances def_val = default_value(msg_context, base_type, default_package) return '[' + def_val + ' for _ in range(' + str(array_len) + ')]' else: return compute_constructor(msg_context, default_package, field_type) def flatten(msg_context, msg): """ Flatten the msg spec so that embedded message fields become direct references. The resulting MsgSpec isn't a true/legal. :class:`MsgSpec` and should only be used for serializer generation. :param msg: MsgSpec to flatten :returns: flattened MsgSpec message """ new_types = [] new_names = [] for t, n in zip(msg.types, msg.names): # Parse type to make sure we don't flatten an array msg_type, is_array, _ = genmsg.msgs.parse_type(t) # flatten embedded types - note: bug #59 if not is_array and msg_context.is_registered(t): msg_spec = flatten(msg_context, msg_context.get_registered(t)) new_types.extend(msg_spec.types) for n2 in msg_spec.names: new_names.append(n+'.'+n2) else: # I'm not sure if it's a performance win to flatten fixed-length arrays # as you get n __getitems__ method calls vs. a single *array call new_types.append(t) new_names.append(n) return MsgSpec(new_types, new_names, msg.constants, msg.text, msg.full_name) def make_python_safe(spec): """ Remap field/constant names in spec to avoid collision with Python reserved words. :param spec: msg spec to map to new, python-safe field names, ``MsgSpec`` :returns: python-safe message specification, ``MsgSpec`` """ new_c = [genmsg.Constant(c.type, _remap_reserved(c.name), c.val, c.val_text) for c in spec.constants] return MsgSpec(spec.types, [_remap_reserved(n) for n in spec.names], new_c, spec.text, spec.full_name) def _remap_reserved(field_name): """ Map field_name to a python-safe representation, if necessary. :param field_name: msg field name, ``str`` :returns: remapped name, ``str`` """ # include 'self' as well because we are within a class instance idx = field_name.rfind('.') if idx > 0: prefix = field_name[:idx+1] sub_field_name = field_name[idx+1:] else: prefix = '' sub_field_name = field_name if sub_field_name in keyword.kwlist + ['self']: sub_field_name = sub_field_name + '_' return prefix + sub_field_name ################################################################################ # (de)serialization routines def compute_post_deserialize(type_, varname): """ Compute post-deserialization code for type_, if necessary. :returns: code to execute post-deserialization (unindented), or None if not necessary. ``str`` """ s = get_special(type_) if s is not None: return s.get_post_deserialize(varname) def compute_constructor(msg_context, package, type_): """ Compute python constructor expression for specified message type implementation. :param package str: package that type is being imported into. Used to resolve type_ if package is not specified. ``str`` :param type_: message type, ``str`` """ if is_special(type_): return get_special(type_).constructor elif genmsg.msgs.bare_msg_type(type_) != type_: # array or other weird type return None else: base_pkg, base_type_ = compute_pkg_type(package, type_) if not msg_context.is_registered('%s/%s' % (base_pkg, base_type_)): return None else: return '%s.msg.%s()' % (base_pkg, base_type_) def compute_pkg_type(package, type_): # noqa: D205, D400 """ :param package: package that type is being imported into, ``str`` :param type: message type (package resource name), ``str`` :returns: python package and type name, ``(str, str)`` """ splits = type_.split(genmsg.SEP) if len(splits) == 1: return package, splits[0] elif len(splits) == 2: return tuple(splits) else: raise MsgGenerationException('illegal message type: %s' % type_) def compute_import(msg_context, package, type_): """ Compute python import statement for specified message type implementation. :param package: package that type is being imported into, ``str`` :param type_: message type (package resource name), ``str`` :returns: list of import statements (no newline) required to use type_ from package, ``[str]`` """ # orig_base_type is the unresolved type orig_base_type = genmsg.msgs.bare_msg_type(type_) # strip array-suffix # resolve orig_base_type based on the current package context. # base_type is the resolved type stripped of any package name. # pkg is the actual package of type_. pkg, base_type = compute_pkg_type(package, orig_base_type) full_msg_type = '%s/%s' % (pkg, base_type) # compute fully-qualified type # important: have to do is_builtin check first. We do this check # against the unresolved type builtins/specials are never # relative. This requires some special handling for Header, which has # two names (Header and std_msgs/Header). if genmsg.msgs.is_builtin(orig_base_type) or \ genmsg.msgs.is_header_type(orig_base_type): # of the builtin types, only special types require import # handling. we switch to base_type as special types do not # include package names. if is_special(base_type): retval = [get_special(base_type).import_str] else: retval = [] elif not msg_context.is_registered(full_msg_type): retval = [] else: retval = ['import %s.msg' % pkg] iter_types = get_registered_ex(msg_context, full_msg_type).types for t in iter_types: assert t != full_msg_type, 'msg [%s] has circular self-dependencies' % (full_msg_type) full_sub_type = '%s/%s' % (package, t) log('compute_import', full_msg_type, package, t) sub = compute_import(msg_context, package, t) retval.extend([x for x in sub if x not in retval]) return retval def compute_full_text_escaped(msg_context, spec): """ Escape the result of genmsg.compute_full_text. The resulting text is escaped to be safe for Python's triple-quote string quoting. :param get_deps_dict: dictionary returned by load_dependencies call, ``dict`` :returns: concatenated text for msg/srv file and embedded msg/srv types. Text will be escaped for triple-quote, ``str`` """ msg_definition = genmsg.compute_full_text(msg_context, spec) msg_definition = msg_definition.replace('"""', r'\"\"\"') return msg_definition ################################################################################ # (De)serialization generators _serial_context = '' _context_stack = [] _counter = 0 def next_var(): # we could optimize this by reusing vars once the context is popped global _counter _counter += 1 return '_v%s' % _counter def reset_var(): global _counter _counter = 0 def push_context(context): """ Push new variable context onto context stack. The context stack manages field-reference context for serialization, e.g. 'self.foo' vs. 'self.bar.foo' vs. 'var.foo' """ global _serial_context, _context_stack _context_stack.append(_serial_context) _serial_context = context def pop_context(): """ Pop variable context from context stack. The context stack manages field-reference context for serialization, e.g. 'self.foo' vs. 'self.bar.foo' vs. 'var.foo' """ global _serial_context _serial_context = _context_stack.pop() # These are the workhorses of the message generation. The generators # are implemented as iterators, where each iteration value is a line # of Python code. The generators will invoke underlying generators, # using the context stack to manage any changes in variable-naming, so # that code can be reused as much as possible. def len_serializer_generator(var, is_string, serialize): # noqa: D401 """ Generator for array-length serialization (32-bit, little-endian unsigned integer). :param var: variable name, ``str`` :param is_string: if True, variable is a string type, ``bool`` :param serialize bool: if True, generate code for serialization. Other, generate code for deserialization, ``bool`` """ if serialize: yield 'length = len(%s)' % var # NOTE: it's more difficult to save a call to struct.pack with # the array length as we are already using *array_val to pass # into struct.pack as *args. Although it's possible that # Python is not optimizing it, it is potentially worse for # performance to attempt to combine if not is_string: yield int32_pack('length') else: yield 'start = end' yield 'end += 4' yield int32_unpack('length', 'str[start:end]') # 4 = struct.calcsize(' 1 and _serial_context.endswith('.'): yield '_x = '+_serial_context[:-1] vars_ = '_x.' + (', _x.').join(spec.names[start:end]) elif serialize and end - start == 1 and _serial_context.endswith('.'): yield '_x = %s%s' % (_serial_context, spec.names[start]) vars_ = '_x' else: vars_ = _serial_context + (', '+_serial_context).join(spec.names[start:end]) pattern = compute_struct_pattern(spec.types[start:end]) if serialize: yield pack(pattern, vars_) else: yield 'start = end' yield 'end += %s' % struct.calcsize('<%s' % reduce_pattern(pattern)) yield unpack('(%s,)' % vars_, pattern, 'str[start:end]') # convert uint8 to bool. this doesn't add much value as Python # equality test on a field will return that True == 1, but I # want to be consistent with bool bool_vars = [(f, t) for f, t in zip(spec.names[start:end], spec.types[start:end]) if t == 'bool'] for f, t in bool_vars: # TODO: could optimize this as well var = _serial_context+f yield '%s = bool(%s)' % (var, var) def serializer_generator(msg_context, spec, serialize, is_numpy): # noqa: D401 """ Generator that yields un-indented python code for (de)serializing MsgSpec. The code this yields is meant to be included in a class method and cannot be used standalone. serialize_fn_generator and deserialize_fn_generator wrap method to provide appropriate class field initializations. :param serialize: if True, yield serialization code. Otherwise, yield deserialization code. ``bool`` :param is_numpy: if True, generate serializer code for numpy datatypes instead of Python lists. ``bool`` """ # Break spec into chunks of simple (primitives) vs. complex (arrays, etc...) # Simple types are batch serialized using the python struct module. # Complex types are individually serialized if spec is None: raise MsgGenerationException('spec is none') names, types = spec.names, spec.types if serialize and not len(names): # Empty yield 'pass' return _max_chunk = 255 # iterate through types. whenever we encounter a non-simple type, # yield serializer for any simple types we've encountered until # then, then yield the complex type serializer curr = 0 for (i, full_type) in enumerate(types): if not is_simple(full_type): if i != curr: # yield chunk of simples for _start in range(curr, i, _max_chunk): _end = min(_start + _max_chunk, i) for y in simple_serializer_generator(msg_context, spec, _start, _end, serialize): yield y curr = i+1 for y in complex_serializer_generator(msg_context, spec.package, full_type, names[i], serialize, is_numpy): yield y if curr < len(types): # yield rest of simples for _start in range(curr, len(types), _max_chunk): _end = min(_start + _max_chunk, len(types)) for y in simple_serializer_generator(msg_context, spec, _start, _end, serialize): yield y def serialize_fn_generator(msg_context, spec, is_numpy=False): # noqa: D401 """ Generator for body of serialize() function. :param is_numpy: if True, generate serializer code for numpy datatypes instead of Python lists, ``bool`` """ # method-var context ######### yield 'try:' push_context('self.') # NOTE: we flatten the spec for optimal serialization # #3741: make sure to have sub-messages python safe flattened = make_python_safe(flatten(msg_context, spec)) for y in serializer_generator(msg_context, flattened, True, is_numpy): yield ' '+y pop_context() yield "except struct.error as se: self._check_types(struct.error(\"%s: '%s' when writing '%s'\" % (type(se), str(se), str(locals().get('_x', self)))))" yield "except TypeError as te: self._check_types(ValueError(\"%s: '%s' when writing '%s'\" % (type(te), str(te), str(locals().get('_x', self)))))" # done w/ method-var context # def deserialize_fn_generator(msg_context, spec, is_numpy=False): # noqa: D401 """ Generator for body of deserialize() function. :param is_numpy: if True, generate serializer code for numpy datatypes instead of Python lists, ``bool`` """ yield 'if python3:' yield INDENT+'codecs.lookup_error("rosmsg").msg_type = self._type' yield 'try:' package = spec.package # Instantiate embedded type classes for type_, name in spec.fields(): if msg_context.is_registered(type_): yield ' if self.%s is None:' % name yield ' self.%s = %s' % (name, compute_constructor(msg_context, package, type_)) yield ' end = 0' # initialize var # method-var context ######### push_context('self.') # NOTE: we flatten the spec for optimal serialization # #3741: make sure to have sub-messages python safe flattened = make_python_safe(flatten(msg_context, spec)) for y in serializer_generator(msg_context, flattened, False, is_numpy): yield ' '+y pop_context() # done w/ method-var context # # generate post-deserialization code for type_, name in spec.fields(): code = compute_post_deserialize(type_, 'self.%s' % name) if code: yield ' %s' % code yield ' return self' yield 'except struct.error as e:' yield ' raise genpy.DeserializationError(e) # most likely buffer underfill' def msg_generator(msg_context, spec, search_path): """ Python code generator for .msg files. Generates a Python from a :class:`genmsg.MsgSpec`. :param spec: parsed .msg :class:`genmsg.MsgSpec` instance :param search_path: dictionary mapping message namespaces to a directory locations """ # #2990: have to compute md5sum before any calls to make_python_safe # generate dependencies dictionary. omit files calculation as we # rely on in-memory MsgSpecs instead so that we can generate code # for older versions of msg files try: genmsg.msg_loader.load_depends(msg_context, spec, search_path) except InvalidMsgSpec as e: raise MsgGenerationException('Cannot generate .msg for %s/%s: %s' % (spec.package, spec.short_name, str(e))) md5sum = genmsg.compute_md5(msg_context, spec) # remap spec names to be Python-safe spec = make_python_safe(spec) spec_names = spec.names # #1807 : this will be much cleaner when msggenerator library is # rewritten to not use globals clear_patterns() yield '# This Python file uses the following encoding: utf-8' yield '"""autogenerated by genpy from %s.msg. Do not edit."""' % spec.full_name yield 'import codecs' yield 'import sys' yield 'python3 = True if sys.hexversion > 0x03000000 else False' yield 'import genpy\nimport struct\n' import_strs = [] for t in spec.types: import_strs.extend(compute_import(msg_context, spec.package, t)) import_strs = sorted(set(import_strs)) for i in import_strs: if i: yield i yield '' fulltype = spec.full_name name = spec.short_name # Yield data class first, e.g. Point2D yield 'class %s(genpy.Message):' % spec.short_name yield ' _md5sum = "%s"' % (md5sum) yield ' _type = "%s"' % (fulltype) yield ' _has_header = %s # flag to mark the presence of a Header object' % spec.has_header() full_text = compute_full_text_escaped(msg_context, spec) # escape trailing double-quote, unless already escaped, before wrapping in """ if full_text.endswith('"') and not full_text.endswith(r'\"'): full_text = full_text[:-1] + r'\"' yield ' _full_text = """%s"""' % full_text if spec.constants: yield ' # Pseudo-constants' for c in spec.constants: if c.type == 'string': val = c.val if '"' in val and "'" in val: # crude escaping of \ and " escaped = c.val.replace('\\', '\\\\') escaped = escaped.replace('\"', '\\"') yield ' %s = "%s"' % (c.name, escaped) elif '"' in val: # use raw encoding for prettiness yield " %s = r'%s'" % (c.name, val) elif "'" in val: # use raw encoding for prettiness yield ' %s = r"%s"' % (c.name, val) else: yield " %s = '%s'" % (c.name, val) else: yield ' %s = %s' % (c.name, c.val) yield '' if len(spec_names): yield " __slots__ = ['"+"','".join(spec_names)+"']" yield " _slot_types = ['"+"','".join(spec.types)+"']" else: yield ' __slots__ = []' yield ' _slot_types = []' yield """ def __init__(self, *args, **kwds): \"\"\" Constructor. Any message fields that are implicitly/explicitly set to None will be assigned a default value. The recommend use is keyword arguments as this is more robust to future message changes. You cannot mix in-order arguments and keyword arguments. The available fields are: %s :param args: complete set of field values, in .msg order :param kwds: use keyword arguments corresponding to message field names to set specific fields. \"\"\" if args or kwds: super(%s, self).__init__(*args, **kwds)""" % (','.join(spec_names), name) if len(spec_names): yield ' # message fields cannot be None, assign default values for those that are' for (t, s) in zip(spec.types, spec_names): yield ' if self.%s is None:' % s yield ' self.%s = %s' % (s, default_value(msg_context, t, spec.package)) if len(spec_names) > 0: yield ' else:' for (t, s) in zip(spec.types, spec_names): yield ' self.%s = %s' % (s, default_value(msg_context, t, spec.package)) yield """ def _get_types(self): \"\"\" internal API method \"\"\" return self._slot_types def serialize(self, buff): \"\"\" serialize message into buffer :param buff: buffer, ``StringIO`` \"\"\"""" for y in serialize_fn_generator(msg_context, spec): yield ' ' + y yield """ def deserialize(self, str): \"\"\" unpack serialized message in str into this message instance :param str: byte array of serialized message, ``str`` \"\"\"""" for y in deserialize_fn_generator(msg_context, spec): yield ' ' + y yield '' yield """ def serialize_numpy(self, buff, numpy): \"\"\" serialize message with numpy array types into buffer :param buff: buffer, ``StringIO`` :param numpy: numpy python module \"\"\"""" for y in serialize_fn_generator(msg_context, spec, is_numpy=True): yield ' ' + y yield """ def deserialize_numpy(self, str, numpy): \"\"\" unpack serialized message in str into this message instance using numpy for array types :param str: byte array of serialized message, ``str`` :param numpy: numpy python module \"\"\"""" for y in deserialize_fn_generator(msg_context, spec, is_numpy=True): yield ' ' + y yield '' # #1807 : this will be much cleaner when msggenerator library is # rewritten to not use globals yield '_struct_I = genpy.struct_I' yield 'def _get_struct_I():' yield ' global _struct_I' yield ' return _struct_I' patterns = get_patterns() for p in sorted(set(patterns)): # I patterns are already optimized if p == 'I': continue var_name = '_struct_%s' % (p.replace('<', '')) yield '%s = None' % var_name yield 'def _get%s():' % var_name yield ' global %s' % var_name yield ' if %s is None:' % var_name yield ' %s = struct.Struct("<%s")' % (var_name, p) yield ' return %s' % var_name clear_patterns() def srv_generator(msg_context, spec, search_path): for mspec in (spec.request, spec.response): for l in msg_generator(msg_context, mspec, search_path): yield l name = spec.short_name req, resp = ['%s%s' % (name, suff) for suff in ['Request', 'Response']] fulltype = spec.full_name genmsg.msg_loader.load_depends(msg_context, spec, search_path) md5 = genmsg.compute_md5(msg_context, spec) yield 'class %s(object):' % name yield " _type = '%s'" % fulltype yield " _md5sum = '%s'" % md5 yield ' _request_class = %s' % req yield ' _response_class = %s' % resp def _module_name(type_name): # noqa: D205, D400 """ :param type_name str: Name of message type sans package, e.g. 'String' :returns str: name of python module for auto-generated code """ return '_'+type_name def compute_resource_name(filename, ext): """ Convert resource filename to ROS resource name. :param filename str: path to .msg/.srv file :returns str: name of ROS resource """ return os.path.basename(filename)[:-len(ext)] def compute_outfile_name(outdir, infile_name, ext): # noqa: D205, D400 """ :param outdir str: path to directory that files are generated to :returns str: output file path based on input file name and output directory """ # Use leading _ so that module name does not collide with message name. It also # makes it more clear that the .py file should not be imported directly return os.path.join(outdir, _module_name(compute_resource_name(infile_name, ext))+'.py') class Generator(object): def __init__(self, what, ext, spec_loader_fn, generator_fn): self.what = what self.ext = ext self.spec_loader_fn = spec_loader_fn self.generator_fn = generator_fn def generate(self, msg_context, full_type, f, outdir, search_path): try: # you can't just check first... race condition os.makedirs(outdir) except OSError as e: if e.errno != errno.EEXIST: raise # generate message files for request/response spec = self.spec_loader_fn(msg_context, f, full_type) outfile = compute_outfile_name(outdir, os.path.basename(f), self.ext) with open(outfile, 'w') as f: for l in self.generator_fn(msg_context, spec, search_path): f.write(l+'\n') return outfile def generate_messages(self, package, package_files, outdir, search_path): # noqa: D200, D400, D401 """ :returns: return code, ``int`` """ if not genmsg.is_legal_resource_base_name(package): raise MsgGenerationException("\nERROR: package name '%s' is illegal and cannot be used in message generation.\nPlease see http://ros.org/wiki/Names" % (package)) # package/src/package/msg for messages, packages/src/package/srv for services msg_context = MsgContext.create_default() retcode = 0 for f in package_files: try: f = os.path.abspath(f) infile_name = os.path.basename(f) full_type = genmsg.gentools.compute_full_type_name(package, infile_name) self.generate(msg_context, full_type, f, outdir, search_path) # actual generation except Exception as e: if not isinstance(e, MsgGenerationException) and not isinstance(e, genmsg.msgs.InvalidMsgSpec): traceback.print_exc() print("\nERROR: Unable to generate %s for package '%s': while processing '%s': %s\n" % (self.what, package, f, e), file=sys.stderr) retcode = 1 # flag error return retcode class SrvGenerator(Generator): def __init__(self): super(SrvGenerator, self).__init__('services', genmsg.EXT_SRV, genmsg.msg_loader.load_srv_from_file, srv_generator) class MsgGenerator(Generator): """ Generate Python message code for all messages in a package. See genutil.Generator. In order to generator code for a single .msg file, see msg_generator. """ def __init__(self): super(MsgGenerator, self).__init__('messages', genmsg.EXT_MSG, genmsg.msg_loader.load_msg_from_file, msg_generator) genpy-0.6.16/src/genpy/genpy_main.py000066400000000000000000000067561404330275700173620ustar00rootroot00000000000000# Software License Agreement (BSD License) # # Copyright (c) 2008, Willow Garage, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # * Neither the name of Willow Garage, Inc. nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. from __future__ import print_function import os import sys import traceback from optparse import OptionParser import genmsg import genmsg.command_line from genmsg import MsgGenerationException from . generate_initpy import write_modules def usage(progname): print('%(progname)s file(s)' % vars()) def genmain(argv, progname, gen): parser = OptionParser('%s file' % (progname)) parser.add_option('--initpy', dest='initpy', action='store_true', default=False) parser.add_option('-p', dest='package') parser.add_option('-o', dest='outdir') parser.add_option('-I', dest='includepath', action='append') options, args = parser.parse_args(argv) try: if options.initpy: if options.outdir: retcode = write_modules(options.outdir) else: parser.error('Missing args') else: if len(args) < 2: parser.error('please specify args') if not os.path.exists(options.outdir): # This script can be run multiple times in parallel. We # don't mind if the makedirs call fails because somebody # else snuck in and created the directory before us. try: os.makedirs(options.outdir) except OSError: if not os.path.exists(options.outdir): raise search_path = genmsg.command_line.includepath_to_dict(options.includepath) retcode = gen.generate_messages(options.package, args[1:], options.outdir, search_path) except genmsg.InvalidMsgSpec as e: print('ERROR: ', e, file=sys.stderr) retcode = 1 except MsgGenerationException as e: print('ERROR: ', e, file=sys.stderr) retcode = 2 except Exception as e: traceback.print_exc() print('ERROR: ', e) retcode = 3 sys.exit(retcode or 0) genpy-0.6.16/src/genpy/message.py000066400000000000000000000713431404330275700166520ustar00rootroot00000000000000# Software License Agreement (BSD License) # # Copyright (c) 2008, Willow Garage, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # * Neither the name of Willow Garage, Inc. nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. """ Support library for Python autogenerated message files. This defines the Message base class used by genpy as well as support libraries for type checking and retrieving message classes by type name. """ import codecs import itertools import math import struct import sys import genmsg import yaml from .base import is_simple from .rostime import Duration from .rostime import TVal from .rostime import Time try: reload # Python 2 except NameError: # Python 3 from importlib import reload if sys.version > '3': long = int try: import numpy as np _valid_float_types = [float, int, long, np.float32, np.float64, np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64] except ImportError: _valid_float_types = [float, int, long] # common struct pattern singletons for msgs to use. Although this # would better placed in a generator-specific module, we don't want to # add another import to messages (which incurs higher import cost) struct_I = struct.Struct('iter(str)`` :returns: string (YAML) representation of message, ``str`` """ type_ = type(val) if type_ in (int, long, float) and fixed_numeric_width is not None: if type_ is float: num_str = ('%.' + str(fixed_numeric_width) + 'f') % val return num_str[:max(num_str.find('.'), fixed_numeric_width)] else: return ('%' + str(fixed_numeric_width) + 'd') % val elif type_ in (int, long, float, bool): return str(val) elif isstring(val): if not val: return "''" # escape strings for use in yaml file using yaml dump with default style to avoid trailing "...\n" return yaml.dump(val, default_style='"').rstrip('\n') elif isinstance(val, TVal): if time_offset is not None and isinstance(val, Time): val = val-time_offset if fixed_numeric_width is not None: format_str = '%' + str(fixed_numeric_width) + 'd' sec_str = '\n%ssecs: ' % indent + (format_str % val.secs) nsec_str = '\n%snsecs: ' % indent + (format_str % val.nsecs) return sec_str + nsec_str else: return '\n%ssecs: %s\n%snsecs: %9d' % (indent, val.secs, indent, val.nsecs) elif type_ in (list, tuple): if len(val) == 0: return '[]' val0 = val[0] if type(val0) in (int, float) and fixed_numeric_width is not None: list_str = '[' + ''.join(strify_message(v, indent, time_offset, current_time, field_filter, fixed_numeric_width) + ', ' for v in val).rstrip(', ') + ']' return list_str elif isstring(val0): # escape list of strings for use in yaml file using yaml dump yaml_str = yaml.dump(val).rstrip('\n') # earlier versions of PyYAML return: ['', '']\n # newer versions of PyYaml return: - ''\n- ''\n assert yaml_str[0] in ('[', '-') if yaml_str[0] == '[': return yaml_str return '\n' + '\n'.join(indent + line for line in yaml_str.splitlines()) elif type(val0) in (int, float, bool): return str(list(val)) else: pref = indent + '- ' indent = indent + ' ' return '\n'+'\n'.join([pref+strify_message(v, indent, time_offset, current_time, field_filter, fixed_numeric_width) for v in val]) elif isinstance(val, Message): # allow caller to select which fields of message are strified if field_filter is not None: fields = list(field_filter(val)) else: fields = val.__slots__ p = '%s%%s: %%s' % (indent) ni = ' '+indent if sys.hexversion > 0x03000000: # Python3 vals = '\n'.join([ p % (f, strify_message(_convert_getattr(val, f, t), ni, time_offset, current_time, field_filter, fixed_numeric_width)) for f, t in zip(val.__slots__, val._slot_types) if f in fields]) else: # Python2 vals = '\n'.join([ p % (f, strify_message(_convert_getattr(val, f, t), ni, time_offset, current_time, field_filter, fixed_numeric_width)) for f, t in itertools.izip(val.__slots__, val._slot_types) if f in fields]) if indent: return '\n'+vals else: return vals else: return str(val) # punt def _convert_getattr(val, f, t): """ Convert atttribute types on the fly, if necessary. This is mainly to convert uint8[] fields back to an array type. """ attr = getattr(val, f) if isstring(attr) and 'uint8[' in t: return [ord(x) for x in attr] elif isinstance(attr, bytes) and 'uint8[' in t: return list(attr) else: return attr # check_type mildly violates some abstraction boundaries between .msg # representation and the python Message representation. The # alternative is to have the message generator map .msg types to # python types beforehand, but that would make it harder to do # width/signed checks. _widths = { 'byte': 8, 'char': 8, 'int8': 8, 'uint8': 8, 'int16': 16, 'uint16': 16, 'int32': 32, 'uint32': 32, 'int64': 64, 'uint64': 64, } def check_type(field_name, field_type, field_val): """ Dynamic type checker that maps ROS .msg types to python types and verifies the python value. check_type() is not designed to be fast and is targeted at error diagnosis. This type checker is not designed to run fast and is meant only for error diagnosis. :param field_name: ROS .msg field name, ``str`` :param field_type: ROS .msg field type, ``str`` :param field_val: field value, ``Any`` :raises: :exc:`SerializationError` If typecheck fails """ if is_simple(field_type): # check sign and width if field_type in ['byte', 'int8', 'int16', 'int32', 'int64']: if type(field_val) not in [long, int]: raise SerializationError('field %s must be an integer type' % field_name) maxval = int(math.pow(2, _widths[field_type]-1)) if field_val >= maxval or field_val < -maxval: raise SerializationError('field %s exceeds specified width [%s]' % (field_name, field_type)) elif field_type in ['char', 'uint8', 'uint16', 'uint32', 'uint64']: if type(field_val) not in [long, int] or field_val < 0: raise SerializationError('field %s must be unsigned integer type' % field_name) maxval = int(math.pow(2, _widths[field_type])) if field_val >= maxval: raise SerializationError('field %s exceeds specified width [%s]' % (field_name, field_type)) elif field_type in ['float32', 'float64']: if type(field_val) not in _valid_float_types: raise SerializationError('field %s must be float type' % field_name) elif field_type == 'bool': if field_val not in [True, False, 0, 1]: raise SerializationError('field %s is not a bool' % (field_name)) elif field_type == 'string': if sys.hexversion > 0x03000000: if type(field_val) == str: try: field_val.encode('ascii') except UnicodeEncodeError: raise SerializationError('field %s is a non-ascii string' % field_name) elif not type(field_val) == bytes: raise SerializationError('field %s must be of type bytes or an ascii string' % field_name) else: if type(field_val) == unicode: # noqa: F821 raise SerializationError('field %s is a unicode string instead of an ascii string' % field_name) elif not isstring(field_val): raise SerializationError('field %s must be of type str' % field_name) elif field_type == 'time': if not isinstance(field_val, Time): raise SerializationError('field %s must be of type Time' % field_name) elif field_type == 'duration': if not isinstance(field_val, Duration): raise SerializationError('field %s must be of type Duration' % field_name) elif field_type.endswith(']'): # array type # use index to generate error if '[' not present base_type = field_type[:field_type.index('[')] if type(field_val) in (bytes, str): if base_type not in ['char', 'uint8']: raise SerializationError('field %s must be a list or tuple type. Only uint8[] can be a string' % field_name) else: # It's a string so its already in byte format and we # don't need to check the individual bytes in the # string. return if not type(field_val) in [list, tuple]: raise SerializationError('field %s must be a list or tuple type' % field_name) for v in field_val: check_type(field_name + '[]', base_type, v) else: if isinstance(field_val, Message): # roslib/Header is the old location of Header. We check it for backwards compat if field_val._type in ['std_msgs/Header', 'roslib/Header']: if field_type not in ['Header', 'std_msgs/Header', 'roslib/Header']: raise SerializationError('field %s must be a Header instead of a %s' % (field_name, field_val._type)) elif field_val._type != field_type: raise SerializationError('field %s must be of type %s instead of %s' % (field_name, field_type, field_val._type)) for n, t in zip(field_val.__slots__, field_val._get_types()): check_type('%s.%s' % (field_name, n), t, getattr(field_val, n)) else: raise SerializationError('field %s must be of type [%s]' % (field_name, field_type)) # TODO: dynamically load message class and do instance compare class Message(object): """Base class of Message data classes auto-generated from msg files.""" # slots is explicitly both for data representation and # performance. Higher-level code assumes that there is a 1-to-1 # mapping between __slots__ and message fields. In terms of # performance, explicitly settings slots eliminates dictionary for # new-style object. __slots__ = ['_connection_header'] def __init__(self, *args, **kwds): """ Create a new Message instance. There are multiple ways of initializing Message instances, either using a 1-to-1 correspondence between constructor arguments and message fields (*args), or using Python "keyword" arguments (**kwds) to initialize named field and leave the rest with default values. """ if args and kwds: raise TypeError('Message constructor may only use args OR keywords, not both') if args: if len(args) != len(self.__slots__): raise TypeError('Invalid number of arguments, args should be %s' % str(self.__slots__) + ' args are' + str(args)) for i, k in enumerate(self.__slots__): setattr(self, k, args[i]) else: # validate kwds for k, v in kwds.items(): if k not in self.__slots__: raise AttributeError('%s is not an attribute of %s' % (k, self.__class__.__name__)) # iterate through slots so all fields are initialized. # this is important so that subclasses don't reference an # uninitialized field and raise an AttributeError. for k in self.__slots__: if k in kwds: setattr(self, k, kwds[k]) else: setattr(self, k, None) def __getstate__(self): """Support for Python pickling.""" return [getattr(self, x) for x in self.__slots__] def __setstate__(self, state): """Support for Python pickling.""" for x, val in zip(self.__slots__, state): setattr(self, x, val) def _get_types(self): raise Exception('must be overriden') def _check_types(self, exc=None): """ Perform dynamic type-checking of Message fields. This is performance intensive and is meant for post-error diagnosis :param exc: underlying exception that gave cause for type check, ``Exception`` :raises: exc:`genpy.SerializationError` If typecheck fails """ for n, t in zip(self.__slots__, self._get_types()): check_type(n, t, getattr(self, n)) if exc: # if exc is set and check_type could not diagnose, raise wrapped error raise SerializationError(str(exc)) def serialize(self, buff): """ Serialize data into buffer. :param buff: buffer, ``StringIO`` """ pass def deserialize(self, str_): """ Deserialize data in str into this instance. :param str_: serialized data, ``str`` """ pass def __repr__(self): return strify_message(self) def __str__(self): return strify_message(self) # TODO: unit test def __eq__(self, other): if not isinstance(other, self.__class__): return False for f in self.__slots__: try: v1 = getattr(self, f) v2 = getattr(other, f) if type(v1) in (list, tuple) and type(v2) in (list, tuple): # we treat tuples and lists as equivalent if tuple(v1) != tuple(v2): return False elif not v1 == v2: return False except AttributeError: return False return True def __ne__(self, other): return not self == other def get_printable_message_args(msg, buff=None, prefix=''): """ Get string representation of msg arguments. :param msg: msg message to fill, ``Message`` :param prefix: field name prefix (for verbose printing), ``str`` :returns: printable representation of msg args, ``str`` """ try: from cStringIO import StringIO # Python 2.x except ImportError: from io import StringIO # Python 3.x if buff is None: buff = StringIO() for f in msg.__slots__: if isinstance(getattr(msg, f), Message): get_printable_message_args(getattr(msg, f), buff=buff, prefix=(prefix+f+'.')) else: buff.write(prefix+f+' ') return buff.getvalue().rstrip() def _fill_val(msg, f, v, keys, prefix): """ Subroutine of L{_fill_message_args()}. Sets a particular field on a message :param f: field name, ``str`` :param v: field value :param keys: keys to use as substitute values for messages and timestamps, ``dict`` :raises: exc:`MessageException` """ if f not in msg.__slots__: raise MessageException('No field name [%s%s]' % (prefix, f)) def_val = getattr(msg, f) if isinstance(def_val, Message) or isinstance(def_val, TVal): # check for substitution key, e.g. 'now' if type(v) == str: if v in keys: setattr(msg, f, keys[v]) else: raise MessageException('No key named [%s]' % (v)) elif isinstance(def_val, TVal) and type(v) in (int, long): # special case to handle time value represented as a single number # TODO: this is a lossy conversion if isinstance(def_val, Time): setattr(msg, f, Time.from_sec(v/1e9)) elif isinstance(def_val, Duration): setattr(msg, f, Duration.from_sec(v/1e9)) else: raise MessageException('Cannot create time values of type [%s]' % (type(def_val))) else: _fill_message_args(def_val, v, keys, prefix=(prefix+f+'.')) elif type(def_val) == list: if not type(v) in [list, tuple]: raise MessageException('Field [%s%s] must be a list or tuple instead of: %s' % (prefix, f, type(v).__name__)) # determine base_type of field by looking at _slot_types idx = msg.__slots__.index(f) t = msg._slot_types[idx] base_type, is_array, length = genmsg.msgs.parse_type(t) # - for primitives, we just directly set (we don't # type-check. we rely on serialization type checker) if base_type in genmsg.msgs.PRIMITIVE_TYPES: # 3785 if length is not None and len(v) != length: raise MessageException('Field [%s%s] has incorrect number of elements: %s != %s' % (prefix, f, len(v), length)) setattr(msg, f, v) # - for complex types, we have to iteratively append to def_val else: # 3785 if length is not None and len(v) != length: raise MessageException('Field [%s%s] has incorrect number of elements: %s != %s' % (prefix, f, len(v), length)) list_msg_class = get_message_class(base_type) if list_msg_class is None: raise MessageException('Cannot instantiate messages for field [%s%s] : cannot load class %s' % (prefix, f, base_type)) del def_val[:] for el in v: inner_msg = list_msg_class() if isinstance(inner_msg, TVal) and type(el) in (int, long): # special case to handle time value represented as a single number # TODO: this is a lossy conversion if isinstance(inner_msg, Time): inner_msg = Time.from_sec(el/1e9) elif isinstance(inner_msg, Duration): inner_msg = Duration.from_sec(el/1e9) else: raise MessageException('Cannot create time values of type [%s]' % (type(inner_msg))) else: _fill_message_args(inner_msg, el, keys, prefix) def_val.append(inner_msg) else: setattr(msg, f, v) def _fill_message_args(msg, msg_args, keys, prefix=''): """ Populate message with specified args. :param msg: message to fill, ``Message`` :param msg_args: list of arguments to set fields to, ``[args]`` :param keys: keys to use as substitute values for messages and timestamps. ``dict`` :param prefix: field name prefix (for verbose printing), ``str`` :returns: unused/leftover message arguments. ``[args]`` :raise :exc:`MessageException` If not enough message arguments to fill message :raises: :exc:`ValueError` If msg or msg_args is not of correct type """ if not isinstance(msg, (Message, TVal)): raise ValueError('msg must be a Message instance: %s' % msg) if type(msg_args) == dict: # print "DICT ARGS", msg_args # print "ACTIVE SLOTS",msg.__slots__ for f, v in msg_args.items(): # assume that an empty key is actually an empty string if v is None: v = '' _fill_val(msg, f, v, keys, prefix) elif type(msg_args) == list: # print "LIST ARGS", msg_args # print "ACTIVE SLOTS",msg.__slots__ if len(msg_args) > len(msg.__slots__): raise MessageException('Too many arguments:\n * Given: %s\n * Expected: %s' % (msg_args, msg.__slots__)) elif len(msg_args) < len(msg.__slots__): raise MessageException('Not enough arguments:\n * Given: %s\n * Expected: %s' % (msg_args, msg.__slots__)) for f, v in zip(msg.__slots__, msg_args): _fill_val(msg, f, v, keys, prefix) else: raise ValueError('invalid msg_args type: %s' % str(msg_args)) def fill_message_args(msg, msg_args, keys={}): """ Populate message with specified args. Args are assumed to be a list of arguments from a command-line YAML parser. See http://www.ros.org/wiki/ROS/YAMLCommandLine for specification on how messages are filled. fill_message_args also takes in an optional 'keys' dictionary which contain substitute values for message and time types. These values must be of the correct instance type, i.e. a Message, Time, or Duration. In a string key is encountered with these types, the value from the keys dictionary will be used instead. This is mainly used to provide values for the 'now' timestamp. :param msg: message to fill, ``Message`` :param msg_args: list of arguments to set fields to, or If None, msg_args will be made an empty list., ``[args]`` :param keys: keys to use as substitute values for messages and timestamps, ``dict`` :raises: :exc:`MessageException` If not enough/too many message arguments to fill message """ # a list of arguments is similar to python's # *args, whereas dictionaries are like **kwds. # empty messages serialize as a None, which we make equivalent to # an empty message if msg_args is None: msg_args = [] # msg_args is always a list, due to the fact it is parsed from a # command-line argument list. We have to special-case handle a # list with a single dictionary, which has precedence over the # general list representation. We offer this precedence as there # is no other way to do kwd assignments into the outer message. if len(msg_args) == 1 and type(msg_args[0]) == dict: # according to spec, if we only get one msg_arg and it's a dictionary, we # use it directly _fill_message_args(msg, msg_args[0], keys, '') else: _fill_message_args(msg, msg_args, keys, '') def _get_message_or_service_class(type_str, message_type, reload_on_error=False): """ Retrieve message/service class instances. Used by get_message_class and get_service_class. :param type_str: 'msg' or 'srv', ``str`` :param message_type: type name of message/service, ``str`` :returns: Message/Service for message/service type or None, ``class`` :raises: :exc:`ValueError` If message_type is invalidly specified """ if message_type == 'time': return Time if message_type == 'duration': return Duration # parse package and local type name for import package, base_type = genmsg.package_resource_name(message_type) if not package: if base_type == 'Header': package = 'std_msgs' else: raise ValueError('message type is missing package name: %s' % str(message_type)) pypkg = val = None try: # import the package pypkg = __import__('%s.%s' % (package, type_str)) except ImportError: # try importing from dry package if available try: from roslib import load_manifest from rospkg import ResourceNotFound try: load_manifest(package) try: pypkg = __import__('%s.%s' % (package, type_str)) except ImportError: pass except ResourceNotFound: pass except ImportError: pass if pypkg: try: val = getattr(getattr(pypkg, type_str), base_type) except AttributeError: pass # this logic is mainly to support rosh, so that a user doesn't # have to exit a shell just because a message wasn't built yet if val is None and reload_on_error: try: if pypkg: reload(pypkg) val = getattr(getattr(pypkg, type_str), base_type) except Exception: val = None return val # cache for get_message_class _message_class_cache = {} def get_message_class(message_type, reload_on_error=False): """ Get the message class. NOTE: this function maintains a local cache of results to improve performance. :param message_type: type name of message, ``str`` :param reload_on_error: (optional). Attempt to reload the Python module if unable to load message the first time. Defaults to False. This is necessary if messages are built after the first load. :returns: Message class for message/service type, ``Message class`` :raises :exc:`ValueError`: if message_type is invalidly specified """ if message_type in _message_class_cache: return _message_class_cache[message_type] cls = _get_message_or_service_class('msg', message_type, reload_on_error=reload_on_error) if cls: _message_class_cache[message_type] = cls return cls # cache for get_service_class _service_class_cache = {} def get_service_class(service_type, reload_on_error=False): """ Get the service class. NOTE: this function maintains a local cache of results to improve performance. :param service_type: type name of service, ``str`` :param reload_on_error: (optional). Attempt to reload the Python module if unable to load message the first time. Defaults to False. This is necessary if messages are built after the first load. :returns: Service class for service type, ``Service class`` :raises :exc:`Exception` If service_type is invalidly specified """ if service_type in _service_class_cache: return _service_class_cache[service_type] cls = _get_message_or_service_class('srv', service_type, reload_on_error=reload_on_error) _service_class_cache[service_type] = cls return cls genpy-0.6.16/src/genpy/rostime.py000066400000000000000000000347571404330275700167200ustar00rootroot00000000000000# Software License Agreement (BSD License) # # Copyright (c) 2008, Willow Garage, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # * Neither the name of Willow Garage, Inc. nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. """ROS Time representation, including Duration.""" from __future__ import division import numbers def _canon(secs, nsecs): # canonical form: nsecs is always positive, nsecs < 1 second secs_over = nsecs // 1000000000 secs += secs_over nsecs -= secs_over * 1000000000 return secs, nsecs class TVal(object): """ Base class of :class:`Time` and :class:`Duration` representations. Representation is secs+nanoseconds since epoch. """ __slots__ = ['secs', 'nsecs'] # mimic same API as messages when being introspected _slot_types = ['int32', 'int32'] def __init__(self, secs=0, nsecs=0): # noqa: D205, D400 """ :param secs: seconds. If secs is a float, then nsecs must not be set or 0, larger seconds will be of type long on 32-bit systems, ``int/long/float`` :param nsecs: nanoseconds, ``int`` """ if not isinstance(secs, numbers.Integral): # float secs constructor if nsecs != 0: raise ValueError('if secs is a float, nsecs cannot be set') float_secs = secs secs = int(float_secs) nsecs = int((float_secs - secs) * 1000000000) else: secs = int(secs) nsecs = int(nsecs) self.secs, self.nsecs = _canon(secs, nsecs) @classmethod def from_sec(cls, float_secs): """ Create new TVal instance using time.time() value (float seconds). :param float_secs: time value in time.time() format, ``float`` :returns: :class:`TVal` instance for specified time """ secs = int(float_secs) nsecs = int((float_secs - secs) * 1000000000) return cls(secs, nsecs) def is_zero(self): # noqa: D200, D400, D401 """ :returns: ``True`` if time is zero (secs and nsecs are zero), ``bool`` """ return self.secs == 0 and self.nsecs == 0 def set(self, secs, nsecs): """ Set time using separate secs and nsecs values. :param secs: seconds since epoch, ``int`` :param nsecs: nanoseconds since seconds, ``int`` """ self.secs = secs self.nsecs = nsecs def canon(self): """ Canonicalize the field representation in this instance. Should only be used when manually setting secs/nsecs slot values, as in deserialization. """ self.secs, self.nsecs = _canon(self.secs, self.nsecs) def to_sec(self): # noqa: D200, D400, D401 """ :returns: time as float seconds (same as time.time() representation), ``float`` """ return float(self.secs) + float(self.nsecs) / 1e9 def to_nsec(self): # noqa: D200, D400, D401 """ :returns: time as nanoseconds, ``long`` """ return self.secs * int(1e9) + self.nsecs def __hash__(self): """ Time values are hashable. Time values with identical fields have the same hash. """ return hash((self.secs, self.nsecs)) def __str__(self): return str(self.to_nsec()) def __repr__(self): return 'genpy.TVal[%d]' % self.to_nsec() def __nonzero__(self): """Return if time value is not zero.""" return self.secs != 0 or self.nsecs != 0 __bool__ = __nonzero__ def __lt__(self, other): """< test for time values.""" try: return self.__cmp__(other) < 0 except TypeError: return NotImplemented def __le__(self, other): """<= test for time values.""" try: return self.__cmp__(other) <= 0 except TypeError: return NotImplemented def __gt__(self, other): """> test for time values.""" try: return self.__cmp__(other) > 0 except TypeError: return NotImplemented def __ge__(self, other): """>= test for time values.""" try: return self.__cmp__(other) >= 0 except TypeError: return NotImplemented def __ne__(self, other): return not self.__eq__(other) def __cmp__(self, other): if not isinstance(other, TVal): raise TypeError('Cannot compare to non-TVal') a = self.to_nsec() b = other.to_nsec() return (a > b) - (a < b) def __eq__(self, other): if not isinstance(other, TVal): return False return self.to_nsec() == other.to_nsec() class Time(TVal): """ Time contains the ROS-wide 'time' primitive representation. It consists of two integers: seconds since epoch and nanoseconds since seconds. Time instances are mutable. """ __slots__ = ['secs', 'nsecs'] def __init__(self, secs=0, nsecs=0): """ Construct time where secs and nsecs are integers. You may prefer to use the static L{from_sec()} factory method instead. :param secs: seconds since epoch, ``int`` :param nsecs: nanoseconds since seconds (since epoch), ``int`` """ super(Time, self).__init__(secs, nsecs) if self.secs < 0: raise TypeError('time values must be positive') def __getstate__(self): """Support for Python pickling.""" return [self.secs, self.nsecs] def __setstate__(self, state): """Support for Python pickling.""" self.secs, self.nsecs = state def to_time(self): """ Get Time in time.time() format. alias of L{to_sec()}. :returns: time in floating point secs (time.time() format), ``float`` """ return self.to_sec() def __hash__(self): return super(Time, self).__hash__() def __repr__(self): return 'genpy.Time[%d]' % self.to_nsec() def __add__(self, other): """ Add duration to this time. :param other: :class:`Duration` """ if not isinstance(other, Duration): return NotImplemented return self.__class__(self.secs + other.secs, self.nsecs + other.nsecs) __radd__ = __add__ def __sub__(self, other): """ Subtract time or duration from this time. :param other: :class:`Duration`/:class:`Time` :returns: :class:`Duration` if other is a :class:`Time`, :class:`Time` if other is a :class:`Duration` """ if isinstance(other, Time): return Duration(self.secs - other.secs, self.nsecs - other.nsecs) elif isinstance(other, Duration): return self.__class__(self.secs - other.secs, self.nsecs - other.nsecs) else: return NotImplemented def __cmp__(self, other): """ Compare to another time. :param other: :class:`Time` """ if not isinstance(other, Time): raise TypeError('cannot compare to non-Time') a = self.to_nsec() b = other.to_nsec() return (a > b) - (a < b) def __eq__(self, other): """ Equal test for Time. Comparison assumes that both time instances are in canonical representation; only compares fields. :param other: :class:`Time` """ if not isinstance(other, Time): return False return self.secs == other.secs and self.nsecs == other.nsecs class Duration(TVal): """ Duration represents the ROS 'duration' primitive. It consists of two integers: seconds and nanoseconds. The Duration class allows you to add and subtract Duration instances, including adding and subtracting from :class:`Time` instances. """ __slots__ = ['secs', 'nsecs'] def __init__(self, secs=0, nsecs=0): """ Create new Duration instance. secs and nsecs are integers and correspond to the ROS 'duration' primitive. :param secs: seconds, ``int`` :param nsecs: nanoseconds, ``int`` """ super(Duration, self).__init__(secs, nsecs) def __getstate__(self): """Support for Python pickling.""" return [self.secs, self.nsecs] def __setstate__(self, state): """Support for Python pickling.""" self.secs, self.nsecs = state def __hash__(self): return super(Duration, self).__hash__() def __repr__(self): return 'genpy.Duration[%d]' % self.to_nsec() def __neg__(self): # noqa: D400, D401 """:returns: Negative value of this :class:`Duration`""" return self.__class__(-self.secs, -self.nsecs) def __abs__(self): """ Absolute value of this duration. :returns: positive :class:`Duration` """ if self.secs >= 0: return self return self.__neg__() def __add__(self, other): """ Add duration to this duration, or this duration to a time, creating a new time value as a result. :param other: duration or time, ``Duration``/``Time`` :returns: :class:`Duration` if other is a :class:`Duration` instance, :class:`Time` if other is a :class:`Time` """ if isinstance(other, Duration): return self.__class__(self.secs+other.secs, self.nsecs+other.nsecs) else: return NotImplemented __radd__ = __add__ def __sub__(self, other): """ Subtract duration from this duration, returning a new duration. :param other: duration :returns: :class:`Duration` """ if not isinstance(other, Duration): return NotImplemented return self.__class__(self.secs-other.secs, self.nsecs-other.nsecs) def __mul__(self, val): """ Multiply this duration by an integer or float. :param val: multiplication factor, ``int/float`` :returns: :class:`Duration` multiplied by val """ if isinstance(val, numbers.Integral): return self.__class__(self.secs * val, self.nsecs * val) elif isinstance(val, numbers.Real): return self.__class__.from_sec(self.to_sec() * val) else: return NotImplemented __rmul__ = __mul__ def __floordiv__(self, val): """ Floor divide this duration by an integer or float. :param val: division factor ``int/float``, or :class:`Duration` to divide by :returns: :class:`Duration` divided by val - a :class:`Duration` if divided by a number, or a number if divided by a duration """ if isinstance(val, numbers.Integral) or isinstance(val, numbers.Real): return self.__class__.from_sec(self.to_sec() // val) elif isinstance(val, Duration): return int(self.to_sec() // val.to_sec()) else: return NotImplemented def __div__(self, val): """ Divide this duration by an integer or float. :param val: division factor ``int/float``, or :class:`Duration` to divide by :returns: :class:`Duration` divided by val - a :class:`Duration` if divided by a number, or a number if divided by a duration """ if isinstance(val, numbers.Integral) or isinstance(val, numbers.Real): return self.__class__.from_sec(self.to_sec() / val) elif isinstance(val, Duration): return self.to_sec() / val.to_sec() else: return NotImplemented def __truediv__(self, val): """ Divide this duration by an integer or float. :param val: division factor ``int/float``, or :class:`Duration` to divide by :returns: :class:`Duration` divided by val - a :class:`Duration` if divided by a number, or a number if divided by a duration """ if isinstance(val, numbers.Real): return self.__class__.from_sec(self.to_sec() / val) elif isinstance(val, Duration): return self.to_sec() / val.to_sec() else: return NotImplemented def __mod__(self, val): """ Find the remainder when dividing this Duration by another Duration. :returns: :class:`Duration` The remaining time after the division """ if isinstance(val, Duration): return self.__class__.from_sec(self.to_sec() % val.to_sec()) else: return NotImplemented def __divmod__(self, val): """ Implement the builtin divmod for a pair of Durations. :returns: ``int`` The floored result of the division :returns: :class:`Duration` The remaining time after the division """ if isinstance(val, Duration): quotient, remainder = divmod(self.to_sec(), val.to_sec()) return int(quotient), self.__class__.from_sec(remainder) else: return NotImplemented def __cmp__(self, other): if not isinstance(other, Duration): raise TypeError('Cannot compare to non-Duration') a = self.to_nsec() b = other.to_nsec() return (a > b) - (a < b) def __eq__(self, other): if not isinstance(other, Duration): return False return self.secs == other.secs and self.nsecs == other.nsecs genpy-0.6.16/test/000077500000000000000000000000001404330275700137125ustar00rootroot00000000000000genpy-0.6.16/test/__init__.py000066400000000000000000000000001404330275700160110ustar00rootroot00000000000000genpy-0.6.16/test/files/000077500000000000000000000000001404330275700150145ustar00rootroot00000000000000genpy-0.6.16/test/files/array/000077500000000000000000000000001404330275700161325ustar00rootroot00000000000000genpy-0.6.16/test/files/array/Object.msg000066400000000000000000000000131404330275700200420ustar00rootroot00000000000000int32 data genpy-0.6.16/test/files/array/ObjectArray.msg000066400000000000000000000000171404330275700210450ustar00rootroot00000000000000Object[] array genpy-0.6.16/test/files/array/bool_fixed_deser.txt000066400000000000000000000001411404330275700221630ustar00rootroot00000000000000start = end end += 3 data = _get_struct_3B().unpack(str[start:end]) data = list(map(bool, data)) genpy-0.6.16/test/files/array/bool_fixed_ser.txt000066400000000000000000000000511404330275700216520ustar00rootroot00000000000000buff.write(_get_struct_3B().pack(*data)) genpy-0.6.16/test/files/array/bool_varlen_deser.txt000066400000000000000000000003141404330275700223550ustar00rootroot00000000000000start = end end += 4 (length,) = _struct_I.unpack(str[start:end]) pattern = '<%sB'%length start = end s = struct.Struct(pattern) end += s.size data = s.unpack(str[start:end]) data = list(map(bool, data)) genpy-0.6.16/test/files/array/bool_varlen_ser.txt000066400000000000000000000001751404330275700220510ustar00rootroot00000000000000length = len(data) buff.write(_struct_I.pack(length)) pattern = '<%sB'%length buff.write(struct.Struct(pattern).pack(*data)) genpy-0.6.16/test/files/array/int16_fixed_deser.txt000066400000000000000000000001061404330275700221720ustar00rootroot00000000000000start = end end += 20 data = _get_struct_10h().unpack(str[start:end]) genpy-0.6.16/test/files/array/int16_fixed_deser_np.txt000066400000000000000000000001331404330275700226670ustar00rootroot00000000000000start = end end += 20 data = numpy.frombuffer(str[start:end], dtype=numpy.int16, count=10) genpy-0.6.16/test/files/array/int16_fixed_ser.txt000066400000000000000000000000521404330275700216610ustar00rootroot00000000000000buff.write(_get_struct_10h().pack(*data)) genpy-0.6.16/test/files/array/int16_fixed_ser_np.txt000066400000000000000000000000341404330275700223560ustar00rootroot00000000000000buff.write(data.tostring()) genpy-0.6.16/test/files/array/int16_varlen_deser.txt000066400000000000000000000002571404330275700223710ustar00rootroot00000000000000start = end end += 4 (length,) = _struct_I.unpack(str[start:end]) pattern = '<%sh'%length start = end s = struct.Struct(pattern) end += s.size data = s.unpack(str[start:end]) genpy-0.6.16/test/files/array/int16_varlen_deser_np.txt000066400000000000000000000003301404330275700230560ustar00rootroot00000000000000start = end end += 4 (length,) = _struct_I.unpack(str[start:end]) pattern = '<%sh'%length start = end s = struct.Struct(pattern) end += s.size data = numpy.frombuffer(str[start:end], dtype=numpy.int16, count=length) genpy-0.6.16/test/files/array/int16_varlen_ser.txt000066400000000000000000000001751404330275700220570ustar00rootroot00000000000000length = len(data) buff.write(_struct_I.pack(length)) pattern = '<%sh'%length buff.write(struct.Struct(pattern).pack(*data)) genpy-0.6.16/test/files/array/int16_varlen_ser_np.txt000066400000000000000000000001521404330275700225470ustar00rootroot00000000000000length = len(data) buff.write(_struct_I.pack(length)) pattern = '<%sh'%length buff.write(data.tostring()) genpy-0.6.16/test/files/array/object_fixed_deser.txt000066400000000000000000000002371404330275700225040ustar00rootroot00000000000000data = [] for i in range(0, 3): val0 = foo.msg.Object() start = end end += 4 (val0.data,) = _get_struct_i().unpack(str[start:end]) data.append(val0) genpy-0.6.16/test/files/array/object_fixed_ser.txt000066400000000000000000000003131404330275700221660ustar00rootroot00000000000000if len(data) != 3: self._check_types(ValueError("Expecting %s items but found %s when writing '%s'" % (3, len(data), 'data'))) for val0 in data: _x = val0.data buff.write(_get_struct_i().pack(_x)) genpy-0.6.16/test/files/array/object_varlen_deser.txt000066400000000000000000000003461404330275700226750ustar00rootroot00000000000000start = end end += 4 (length,) = _struct_I.unpack(str[start:end]) data = [] for i in range(0, length): val0 = foo.msg.Object() start = end end += 4 (val0.data,) = _get_struct_i().unpack(str[start:end]) data.append(val0) genpy-0.6.16/test/files/array/object_varlen_ser.txt000066400000000000000000000002001404330275700223510ustar00rootroot00000000000000length = len(data) buff.write(_struct_I.pack(length)) for val0 in data: _x = val0.data buff.write(_get_struct_i().pack(_x)) genpy-0.6.16/test/files/array/object_varlen_ser_full.txt000066400000000000000000000006621404330275700234070ustar00rootroot00000000000000try: length = len(self.array) buff.write(_struct_I.pack(length)) for val1 in self.array: _x = val1.data buff.write(_get_struct_i().pack(_x)) except struct.error as se: self._check_types(struct.error("%s: '%s' when writing '%s'" % (type(se), str(se), str(locals().get('_x', self))))) except TypeError as te: self._check_types(ValueError("%s: '%s' when writing '%s'" % (type(te), str(te), str(locals().get('_x', self))))) genpy-0.6.16/test/files/array/string_fixed_deser.txt000066400000000000000000000003761404330275700225500ustar00rootroot00000000000000data = [] for i in range(0, 2): start = end end += 4 (length,) = _struct_I.unpack(str[start:end]) start = end end += length if python3: val0 = str[start:end].decode('utf-8', 'rosmsg') else: val0 = str[start:end] data.append(val0) genpy-0.6.16/test/files/array/string_fixed_ser.txt000066400000000000000000000005051404330275700222310ustar00rootroot00000000000000if len(data) != 2: self._check_types(ValueError("Expecting %s items but found %s when writing '%s'" % (2, len(data), 'data'))) for val0 in data: length = len(val0) if python3 or type(val0) == unicode: val0 = val0.encode('utf-8') length = len(val0) buff.write(struct.Struct(' 0x03000000: # Python3 self.assertEqual('{1}', strify_message({1})) else: self.assertEqual('set([1])', strify_message({1})) def test_strify_yaml(self): def roundtrip(m): yaml_text = strify_message(m) print(yaml_text) loaded = yaml.load(yaml_text) print('loaded', loaded) new_inst = m.__class__() if loaded is not None: fill_message_args(new_inst, [loaded]) else: fill_message_args(new_inst, []) return new_inst # test YAML roundtrip. strify_message doesn't promise this # yet, but want to use it in this way in some demo toolchains from genpy.message import Message, strify_message, fill_message_args class M1(Message): __slots__ = [] _slot_types = [] def __init__(self): pass self.assertEqual(M1(), roundtrip(M1())) class M2(Message): __slots__ = ['str', 'int', 'float', 'bool', 'list', 'str_list'] _slot_types = ['string', 'int32', 'float32', 'bool', 'int32[]', 'string[]'] def __init__(self, str_=None, int_=None, float_=None, bool_=None, list_=None, str_list_=None): self.str = str_ self.int = int_ self.float = float_ self.bool = bool_ self.list = list_ self.str_list = str_list_ # test with empty string and empty list val = M2('foo\nbar', -1, 0., False, [], ['', '']) self.assertEqual(val, roundtrip(val)) multiline_str = """ foo bar foo # bar foo ' bar foo " bar """ # test with strings and list of strings that need escaping val = M2(multiline_str, 123456789101112, 5678., True, [1, 2, 3], [multiline_str, 'foo \n # bar\n', '"foo \' bar"', '"foo" \" # \' \" bar']) self.assertEqual(val, roundtrip(val)) class M3(Message): __slots__ = ['m2'] _slot_types = ['test_roslib/M2'] def __init__(self, m2=None): self.m2 = m2 or M2() # test with nested complex message val = M3(val) self.assertEqual(val, roundtrip(val)) def test_check_type(self): # check_type() currently does not do harder checks like # type-checking class types. as soon as it does, it will need # test to validate this. from genpy.message import check_type, SerializationError from genpy import Time, Duration valids = [ ('byte', 1), ('byte', -1), ('string', ''), ('string', 'a string of text'), ('int32[]', []), ('int32[]', [1, 2, 3, 4]), ('time', Time()), ('time', Time.from_sec(1.0)), ('time', Time(10000)), ('time', Time(1000, -100)), ('duration', Duration()), ('duration', Duration()), ('duration', Duration(100)), ('duration', Duration(-100, -100)), ] for t, v in valids: try: check_type('n', t, v) except Exception as e: traceback.print_exc() raise Exception('failure type[%s] value[%s]: %s' % (t, v, str(e))) invalids = [ ('byte', 129), ('byte', -129), ('byte', 'byte'), ('byte', 1.0), ('string', 1), ('uint32', -1), ('int8', 112312), ('int8', -112312), ('uint8', -1), ('uint8', 112312), ('int32', '1'), ('int32', 1.), ('int32[]', 1), ('int32[]', [1., 2.]), ('int32[]', [1, 2.]), ('duration', 1), ('time', 1), ] for t, v in invalids: try: check_type('n', t, v) self.fail('check_type[%s, %s] should have failed' % (t, v)) except SerializationError: pass def test_serialize_exception_msg(self): from genpy.message import fill_message_args from genpy.msg import TestStringFloat try: from cStringIO import StringIO except ImportError: from io import BytesIO as StringIO buff = StringIO() m = TestStringFloat() fill_message_args(m, ['foo', 1.0]) self.assertEqual(m.data, 'foo') self.assertEqual(m.float, 1.0) # should serialize fine with float m.serialize(buff) # setting a string instead should fail with correct message m.float = '1.0' try: m.serialize(buff) assert False, 'This should have raised a genpy.SerializationError' except genpy.SerializationError as e: self.assertEqual(str(e), "field float must be float type") except Exception: assert False, 'This should have raised a genpy.SerializationError instead' @unittest.skipIf(sys.hexversion < 0x03000000, "Python 3 only test") def test_deserialize_unicode_error(self): from genpy.msg import TestString, TestMsgArray m = TestString() buff = b'\x00\x00\x00\x04\xF0\x9F\x92\xA9' self.assertEqual(m.deserialize(buff).data, b'\xF0\x9F\x92\xA9'.decode()) m = TestString() buff = b'\x00\x00\x00\x04\x41\xff\xfe\x42' with self.assertLogs('rosout', level='ERROR') as cm: self.assertEqual(m.deserialize(buff).data, 'A\ufffd\ufffdB') self.assertEqual(m.deserialize(buff).data, 'A\ufffd\ufffdB') self.assertEqual(len(cm.output), 1) self.assertIn("Characters replaced when decoding message genpy/TestString (will print only once)", cm.output[0]) m = TestMsgArray() buff = b'\x00\x00\x00\x00\x00\x00\x00\x04\x41\xff\xfe\x42' with self.assertLogs('rosout', level='ERROR') as cm: self.assertEqual(m.deserialize(buff).fixed_strings[0].data, 'A\ufffd\ufffdB') self.assertEqual(m.deserialize(buff).fixed_strings[0].data, 'A\ufffd\ufffdB') self.assertEqual(len(cm.output), 1) self.assertIn("Characters replaced when decoding message genpy/TestMsgArray (will print only once)", cm.output[0]) genpy-0.6.16/test/test_genpy_python_safe.py000066400000000000000000000020011404330275700210350ustar00rootroot00000000000000import traceback import unittest class MessageTest(unittest.TestCase): def test_python_safe_message(self): # simple test for make_python_safe try: # this import will fail, if the data field "from" is not properly properly renames, since "from" is a reserved keyword in Python from genpy.msg import TestPythonSafe # noqa: F401 except Exception: # assert False, "should have raised" self.fail("failed to import message type 'TestPythonSafe':\n%s" % (traceback.format_exc())) def test_python_safe_message_with_subfields(self): # regression test for https://github.com/ros/genpy/issues/68 try: # this import will fail, if the make_python_safe function is not properly used on all subfields of the message from genpy.msg import TestPythonSafeSubfields # noqa: F401 except Exception: self.fail("failed to import message type 'TestPythonSafeSubfields':\n%s" % (traceback.format_exc())) genpy-0.6.16/test/test_genpy_rostime.py000066400000000000000000000375771404330275700202320ustar00rootroot00000000000000# Software License Agreement (BSD License) # # Copyright (c) 2009, Willow Garage, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # * Neither the name of Willow Garage, Inc. nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. import unittest import warnings class RostimeTest(unittest.TestCase): def test_TVal(self, TVal=None, test_neg=True): import genpy.rostime if TVal is None: # cannot set as default arg because --cov option reloads the module and causes # spurious failure TVal = genpy.rostime.TVal # test constructor # - test zero v = TVal() self.assert_(repr(v)) self.assert_(str(v)) self.assertEquals(0, v.secs) self.assertEquals(0, v.nsecs) self.failIf(v) # test __zero__ self.assert_(v.is_zero()) self.assertEquals('0', str(v)) self.assertEquals(0, v.to_nsec()) self.assertEquals(0, v.to_sec()) self.assertEquals(v, v) self.assertEquals(v, TVal()) self.assertEquals(v, TVal(0)) self.assertEquals(v, TVal(0, 0)) self.assertEquals(v.__hash__(), TVal(0, 0).__hash__()) self.assert_(v != TVal(0,1)) self.assert_(v >= TVal()) self.assert_(v <= TVal()) self.assert_(v < TVal(0,1)) self.assert_(TVal(0,1) > v) v.set(0, 0) self.assertEquals(0, v.secs) self.assertEquals(0, v.nsecs) v.set(1, 0) self.assertEquals(1, v.secs) self.assertEquals(0, v.nsecs) v.set(0, 1) self.assertEquals(0, v.secs) self.assertEquals(1, v.nsecs) # - set does _not_ canonicalize v.set(0, 1000000000) self.assertEquals(0, v.secs) self.assertEquals(1000000000, v.nsecs) v.canon() self.assertEquals(1, v.secs) self.assertEquals(0, v.nsecs) # - test seconds v = TVal(1) self.assertEquals(1, v.secs) self.assertEquals(0, v.nsecs) self.assert_(v) # test __zero__ self.failIf(v.is_zero()) self.assertEquals('1000000000', str(v)) self.assertEquals(1000000000, v.to_nsec()) self.assertEquals(v, v) self.assertEquals(v, TVal(1)) self.assertEquals(v, TVal(1, 0)) self.assertEquals(v, TVal(0,1000000000)) self.assertEquals(v.__hash__(), TVal(0,1000000000).__hash__()) self.assertNotEquals(v, TVal(0, 0)) self.assertNotEquals(v.__hash__(), TVal(0, 0).__hash__()) self.assertEquals(NotImplemented, v.__ge__(0)) class Foo(object): pass self.assertEquals(NotImplemented, v.__gt__(Foo())) self.assertEquals(NotImplemented, v.__ge__(Foo())) self.assertEquals(NotImplemented, v.__le__(Foo())) self.assertEquals(NotImplemented, v.__lt__(Foo())) self.failIf(v.__eq__(Foo())) self.assert_(v.__ne__(Foo())) self.assert_(v >= TVal()) self.assert_(v <= TVal(1)) self.assert_(v <= TVal(1,0)) self.assert_(v <= TVal(2,0)) self.assert_(v < TVal(2)) self.assert_(v < TVal(1,1)) self.assert_(TVal(1,1) > v) self.assert_(TVal(2) > v) # - test ns v = TVal(0, 1) self.assertEquals(0, v.secs) self.assertEquals(1, v.nsecs) self.assert_(v) # test __zero__ self.failIf(v.is_zero()) self.assertEquals('1', str(v)) self.assertEquals(1, v.to_nsec()) self.assertEquals(v, v) self.assertEquals(v, TVal(0,1)) self.assertNotEquals(v, TVal(0, 0)) self.assert_(v >= TVal()) self.assert_(v <= TVal(1)) self.assert_(v <= TVal(0,1)) self.assert_(v <= TVal(2,0)) self.assert_(v < TVal(0,2)) self.assert_(v < TVal(1)) self.assert_(TVal(1) > v) self.assert_(TVal(0,2) > v) # - test canon v = TVal(1, 1000000000) self.assertEquals(2, v.secs) self.assertEquals(0, v.nsecs) self.assertEquals(2, v.to_sec()) self.assertEquals(2000000000, v.to_nsec()) v = TVal(1, 1000000001) self.assertEquals(2, v.secs) self.assertEquals(1, v.nsecs) self.assertEquals(2.000000001, v.to_sec()) self.assertEquals(2000000001, v.to_nsec()) v = TVal(1, -1000000000) self.assertEquals(0, v.secs) self.assertEquals(0, v.nsecs) v = TVal(1, -999999999) self.assertEquals(0, v.secs) self.assertEquals(1, v.nsecs) self.assertEquals(0.000000001, v.to_sec()) self.assertEquals(1, v.to_nsec()) if test_neg: v = TVal(-1, -1000000000) self.assertEquals(-2, v.secs) self.assertEquals(0, v.nsecs) self.assertEquals(-2, v.to_sec()) self.assertEquals(-2000000000, v.to_nsec()) v = TVal(-2, 1000000000) self.assertEquals(-1, v.secs) self.assertEquals(0, v.nsecs) self.assertEquals(-1, v.to_sec()) self.assertEquals(-1000000000, v.to_nsec()) # test some more hashes self.assertEquals(TVal(1).__hash__(), TVal(1).__hash__()) self.assertEquals(TVal(1,1).__hash__(), TVal(1,1).__hash__()) self.assertNotEquals(TVal(1).__hash__(), TVal(2).__hash__()) self.assertNotEquals(TVal(1,1).__hash__(), TVal(1,2).__hash__()) self.assertNotEquals(TVal(1,1).__hash__(), TVal(2,1).__hash__()) def test_Time(self): from genpy.rostime import Time, Duration self.test_TVal(TVal=Time, test_neg=False) # #1600 Duration > Time should fail failed = False try: v = Duration.from_sec(0.1) > Time.from_sec(0.5) failed = True except: pass self.failIf(failed, "should have failed to compare") try: v = Time.from_sec(0.4) > Duration.from_sec(0.1) failed = True except: pass self.failIf(failed, "should have failed to compare") # TODO: sub # neg time fails try: Time(-1) failed = True except: pass self.failIf(failed, "negative time not allowed") try: Time(1, -1000000001) failed = True except: pass self.failIf(failed, "negative time not allowed") # test Time.now() is within 10 seconds of actual time (really generous) import time t = time.time() v = Time.from_sec(t) self.assertEquals(v.to_sec(), t) # test from_sec() self.assertEquals(Time.from_sec(0), Time()) self.assertEquals(Time.from_sec(1.), Time(1)) self.assertEquals(Time.from_sec(v.to_sec()), v) self.assertEquals(v.from_sec(v.to_sec()), v) # test to_time() self.assertEquals(v.to_sec(), v.to_time()) # test addition # - time + time fails try: v = Time(1,0) + Time(1, 0) failed = True except: pass self.failIf(failed, "Time + Time must fail") # - time + duration v = Time(1,0) + Duration(1, 0) self.assertEquals(Time(2, 0), v) v = Duration(1, 0) + Time(1,0) self.assertEquals(Time(2, 0), v) v = Time(1,1) + Duration(1, 1) self.assertEquals(Time(2, 2), v) v = Duration(1, 1) + Time(1,1) self.assertEquals(Time(2, 2), v) v = Time(1) + Duration(0, 1000000000) self.assertEquals(Time(2), v) v = Duration(1) + Time(0, 1000000000) self.assertEquals(Time(2), v) v = Time(100, 100) + Duration(300) self.assertEquals(Time(400, 100), v) v = Duration(300) + Time(100, 100) self.assertEquals(Time(400, 100), v) v = Time(100, 100) + Duration(300, 300) self.assertEquals(Time(400, 400), v) v = Duration(300, 300) + Time(100, 100) self.assertEquals(Time(400, 400), v) v = Time(100, 100) + Duration(300, -101) self.assertEquals(Time(399, 999999999), v) v = Duration(300, -101) + Time(100, 100) self.assertEquals(Time(399, 999999999), v) # test subtraction try: v = Time(1,0) - 1 failed = True except: pass self.failIf(failed, "Time - non Duration must fail") class Foob(object): pass try: v = Time(1,0) - Foob() failed = True except: pass self.failIf(failed, "Time - non TVal must fail") # - Time - Duration v = Time(1,0) - Duration(1, 0) self.assertEquals(Time(), v) v = Time(1,1) - Duration(-1, -1) self.assertEquals(Time(2, 2), v) v = Time(1) - Duration(0, 1000000000) self.assertEquals(Time(), v) v = Time(2) - Duration(0, 1000000000) self.assertEquals(Time(1), v) v = Time(400, 100) - Duration(300) self.assertEquals(Time(100, 100), v) v = Time(100, 100) - Duration(0, 101) self.assertEquals(Time(99, 999999999), v) # - Time - Time = Duration v = Time(100, 100) - Time(100, 100) self.assertEquals(Duration(), v) v = Time(100, 100) - Time(100) self.assertEquals(Duration(0,100), v) v = Time(100) - Time(200) self.assertEquals(Duration(-100), v) # Time (float secs) vs. Time(int, int) self.assertEquals(Time.from_sec(0.5), Time(0.5)) t = Time(0.5) self.assert_(type(t.secs) == int) self.assertEquals(0, t.secs) self.assertEquals(500000000, t.nsecs) try: Time(0.5, 0.5) self.fail("should have thrown value error") except ValueError: pass def test_Duration(self): from genpy.rostime import Time, Duration self.test_TVal(TVal=Duration, test_neg=True) # test from_sec v = Duration(1000) self.assertEquals(v, Duration.from_sec(v.to_sec())) self.assertEquals(v, v.from_sec(v.to_sec())) v = Duration(0,1000) self.assertEquals(v, Duration.from_sec(v.to_sec())) self.assertEquals(v, v.from_sec(v.to_sec())) # test neg v = -Duration(1, -1) self.assertEquals(-1, v.secs) self.assertEquals(1, v.nsecs) v = -Duration(-1, -1) self.assertEquals(1, v.secs) self.assertEquals(1, v.nsecs) v = -Duration(-1, 1) self.assertEquals(0, v.secs) self.assertEquals(999999999, v.nsecs) # test addition self.assertEquals(Duration(1,0) + Time(1, 0), Time(2, 0)) failed = False try: v = Duration(1,0) + 1 failed = True except: pass self.failIf(failed, "Duration + int must fail") v = Duration(1,0) + Duration(1, 0) self.assertEquals(2, v.secs) self.assertEquals(0, v.nsecs) self.assertEquals(Duration(2, 0), v) v = Duration(-1,-1) + Duration(1, 1) self.assertEquals(0, v.secs) self.assertEquals(0, v.nsecs) self.assertEquals(Duration(), v) v = Duration(1) + Duration(0, 1000000000) self.assertEquals(2, v.secs) self.assertEquals(0, v.nsecs) self.assertEquals(Duration(2), v) v = Duration(100, 100) + Duration(300) self.assertEquals(Duration(400, 100), v) v = Duration(100, 100) + Duration(300, 300) self.assertEquals(Duration(400, 400), v) v = Duration(100, 100) + Duration(300, -101) self.assertEquals(Duration(399, 999999999), v) # test subtraction try: v = Duration(1,0) - 1 failed = True except: pass self.failIf(failed, "Duration - non duration must fail") try: v = Duration(1, 0) - Time(1,0) failed = True except: pass self.failIf(failed, "Duration - Time must fail") v = Duration(1,0) - Duration(1, 0) self.assertEquals(Duration(), v) v = Duration(-1,-1) - Duration(1, 1) self.assertEquals(Duration(-3, 999999998), v) v = Duration(1) - Duration(0, 1000000000) self.assertEquals(Duration(), v) v = Duration(2) - Duration(0, 1000000000) self.assertEquals(Duration(1), v) v = Duration(100, 100) - Duration(300) self.assertEquals(Duration(-200, 100), v) v = Duration(100, 100) - Duration(300, 101) self.assertEquals(Duration(-201, 999999999), v) # test abs self.assertEquals(abs(Duration()), Duration()) self.assertEquals(abs(Duration(1)), Duration(1)) self.assertEquals(abs(Duration(-1)), Duration(1)) self.assertEquals(abs(Duration(0,-1)), Duration(0,1)) self.assertEquals(abs(Duration(-1,-1)), Duration(1,1)) self.assertEquals(abs(Duration(0,1)), Duration(0,1)) # Duration (float secs) vs. Duration(int, int) self.assertEquals(Duration.from_sec(0.5), Duration(0.5)) t = Duration(0.5) self.assert_(type(t.secs) == int) self.assertEquals(0, t.secs) self.assertEquals(500000000, t.nsecs) try: Duration(0.5, 0.5) self.fail("should have thrown value error") except ValueError: pass # Test mul self.assertEquals(Duration(4), Duration(2) * 2) self.assertEquals(Duration(4), Duration(2) * 2.) self.assertEquals(Duration(4), 2 * Duration(2)) self.assertEquals(Duration(4), 2. * Duration(2)) self.assertEquals(Duration(10), Duration(4) * 2.5) self.assertEquals(Duration(4, 8), Duration(2, 4) * 2) v = Duration(4, 8) - (Duration(2, 4) * 2.) self.assert_(abs(v.to_nsec()) < 100) v = Duration(5, 10) - (Duration(2, 4) * 2.5) self.assert_(abs(v.to_nsec()) < 100) # Test div self.assertEquals(Duration(4), Duration(8) / 2) self.assertEquals(Duration(4), Duration(8) / 2.) with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') self.assertEqual(Duration(4), Duration(8) // 2) self.assertEqual(Duration(4), Duration(8) // 2.) self.assertEqual(Duration(4), Duration(9) // 2) self.assertEqual(Duration(4), Duration(9) // 2.) self.assertEqual(len(w), 0) self.assertEquals(Duration(4, 2), Duration(8, 4) / 2) v = Duration(4, 2) - (Duration(8, 4) / 2.) self.assert_(abs(v.to_nsec()) < 100) with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') self.assertEqual(Duration(4, 0), Duration(8, 4) // 2) self.assertEqual(Duration(4, 0), Duration(9, 5) // 2) v = Duration(4, 2) - (Duration(9, 5) // 2.) self.assertTrue(abs(v.to_nsec()) < 100) self.assertEqual(len(w), 0) genpy-0.6.16/test/test_genpy_rostime_truediv.py000066400000000000000000000051611404330275700217540ustar00rootroot00000000000000# Software License Agreement (BSD License) # # Copyright (c) 2009, Willow Garage, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # * Neither the name of Willow Garage, Inc. nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. from __future__ import division import unittest class RostimeTruedivTest(unittest.TestCase): def test_Duration(self): from genpy.rostime import Duration # See #3667 as well as PEP 238 d = Duration(13, 500000000) to_sec = d.to_sec() self.assertEqual(Duration(to_sec / 2.), d/2) # Test div self.assertEqual(Duration(4), Duration(8) / 2) self.assertEqual(Duration(4), Duration(8) / 2.) self.assertEqual(Duration(4), Duration(8) // 2) self.assertEqual(Duration(4), Duration(8) // 2.) self.assertEqual(Duration(4), Duration(9) // 2) self.assertEqual(Duration(4), Duration(9) // 2.) self.assertEqual(Duration(4, 2), Duration(8, 4) / 2) v = Duration(4, 2) - (Duration(8, 4) / 2.) self.assertTrue(abs(v.to_nsec()) < 100) self.assertEqual(Duration(4, 0), Duration(8, 4) // 2) self.assertEqual(Duration(4, 0), Duration(9, 5) // 2) v = Duration(4, 2) - (Duration(9, 5) // 2.) self.assertTrue(abs(v.to_nsec()) < 100)