Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: osrf/osrf_pycommon
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: eloquent
Choose a base ref
...
head repository: osrf/osrf_pycommon
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Able to merge. These branches can be automatically merged.
Loading
35 changes: 35 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: osrf_pycommon-ci

on:
push:
branches: [master]
pull_request:

jobs:
build:
strategy:
matrix:
os: [macos-latest, ubuntu-22.04, windows-latest]
python: ['3.8', '3.9', '3.10', '3.11', '3.12']
include:
- os: ubuntu-20.04
python: '3.6'
name: osrf_pycommon tests
runs-on: ${{matrix.os}}

steps:
- uses: actions/checkout@v4
- name: Set up Python ${{matrix.python}}
uses: actions/setup-python@v5
with:
python-version: ${{matrix.python}}
- name: Install dependencies
run: |
python -m pip install -U -e .[test] pytest-cov
- name: Run tests
run: |
python -m pytest tests --cov=osrf_pycommon
- name: Upload coverage
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
12 changes: 0 additions & 12 deletions .travis.yml

This file was deleted.

128 changes: 128 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,131 @@
2.1.6 (2025-03-25)
------------------
* Merge pull request `#103 <https://github.com/osrf/osrf_pycommon/issues/103>`_ from christophebedard/christophebedard/fix-typo-on-each-verb
* Contributors: Christophe Bedard

2.1.5 (2024-12-18)
------------------
* Align stdeb dependencies with setup.py (`#101 <https://github.com/osrf/osrf_pycommon/issues/101>`_)
Follow-up to 4b2f3a8e4969f33dced1dc2db2296230e7a55b1d
* Add '+upstream' suffix to published deb version (`#102 <https://github.com/osrf/osrf_pycommon/issues/102>`_)
Using a debian version suffix which falls late alphabetically appears to
give our packages preference by apt. If a user enables a repository
which distributes packages created by OSRF or ROS, it is likely that
they wish to use these packages instead of the ones packaged by their
platform.
* Upload coverage results to codecov (`#100 <https://github.com/osrf/osrf_pycommon/issues/100>`_)
* Update ci.yaml (`#96 <https://github.com/osrf/osrf_pycommon/issues/96>`_)
fix node.js <20 deprecation
Co-authored-by: Scott K Logan <logans@cottsay.net>
* Updated python version (`#97 <https://github.com/osrf/osrf_pycommon/issues/97>`_)
Python version 3.7 is no longer supported as of June 27, 2023
Co-authored-by: Scott K Logan <logans@cottsay.net>
* Resolve outstanding resource warnings when running tests (`#99 <https://github.com/osrf/osrf_pycommon/issues/99>`_)
* Update deb platforms for release (`#95 <https://github.com/osrf/osrf_pycommon/issues/95>`_)
Added:
* Ubuntu Noble (24.04 LTS pre-release)
* Debian Trixie (testing)
Dropped:
* Debian Bullseye (oldstable)
Retained:
* Debian Bookworm (stable)
* Ubuntu Focal (20.04 LTS)
* Ubuntu Jammy (22.04 LTS)
* Remove CODEOWNERS. (`#98 <https://github.com/osrf/osrf_pycommon/issues/98>`_)
It is out of date and no longer serving its intended purpose.
* Contributors: Chris Lalancette, Scott K Logan, Steven! Ragnarök, mosfet80

2.1.4 (2023-08-21)
------------------
* Catch all of the spurious warnings from get_event_loop. (`#94 <https://github.com/osrf/osrf_pycommon/issues/94>`_)
* Contributors: Chris Lalancette

2.1.3 (2023-07-11)
------------------
* Add bookworm as a python3 target (`#91 <https://github.com/osrf/osrf_pycommon/issues/91>`_)
* Suppress warning for specifically handled behavior (`#87 <https://github.com/osrf/osrf_pycommon/issues/87>`_)
* Update supported platforms (`#93 <https://github.com/osrf/osrf_pycommon/issues/93>`_)
* Add GitHub Actions CI workflow (`#88 <https://github.com/osrf/osrf_pycommon/issues/88>`_)
* Contributors: Scott K Logan, Tully Foote

2.1.2 (2023-02-14)
------------------
* [master] Update maintainers - 2022-11-07 (`#89 <https://github.com/osrf/osrf_pycommon/issues/89>`_)
* Contributors: Audrow Nash

2.1.1 (2022-11-07)
------------------
* Declare test dependencies in [test] extra (`#86 <https://github.com/osrf/osrf_pycommon/issues/86>`_)
* Contributors: Scott K Logan

2.1.0 (2022-05-10)
------------------

2.0.2 (2022-04-08)
------------------
* Fix an importlib_metadata warning with Python 3.10. (`#84 <https://github.com/osrf/osrf_pycommon/issues/84>`_)
* Contributors: Chris Lalancette

2.0.1 (2022-02-14)
------------------
* Don't release 2.x / master on Debian Buster. (`#83 <https://github.com/osrf/osrf_pycommon/issues/83>`_)
Debian Buster is on Python 3.7: https://packages.debian.org/buster/python3
* Stop using mock in favor of unittest.mock. (`#74 <https://github.com/osrf/osrf_pycommon/issues/74>`_)
Mock has been deprecated since Python 3.3; see
https://pypi.org/project/mock/ . The recommended replacement
is unittest.mock, which seems to be a drop-in replacement.
Co-authored-by: William Woodall <william@osrfoundation.org>
* Fix dependencies (`#81 <https://github.com/osrf/osrf_pycommon/issues/81>`_)
* Remove obsolete setuptools from install_requires
Now that pkg_resources are no longer used, there is no need to depend
on setuptools at runtime.
* Fix version-conditional dependency on importlib-metadata
Use version markers to depend on importlib-metadata correctly. Explicit
conditions mean that wheels built with setup.py will either have the dep
or not depending on what Python version they're built with, rather than
what version they're installed on.
* fix whitespace and date in changelog heading
* Contributors: Chris Lalancette, Michał Górny, Steven! Ragnarök, William Woodall

2.0.0 (2022-02-01)
------------------
* Replace the use of ``pkg_resources`` with the more modern ``importlib-metadata``. (`#66 <https://github.com/osrf/osrf_pycommon/issues/66>`_)
* Note this means that from now on you can only release on >= Ubuntu focal as that was when ``python3-importlib-metadata`` was introduced.
* Used the ``1.0.x`` branch if you need an ealier version that still uses ``pkg_resources``.
Co-authored-by: William Woodall <william@osrfoundation.org>
* Contributors: Chris Lalancette

1.0.1 (2022-01-20)
------------------
* Update release distributions. (`#78 <https://github.com/osrf/osrf_pycommon/issues/78>`_)
* Contributors: Steven! Ragnarök

1.0.0 (2021-01-25)
------------------
* Added missing conflict rules in stdeb.cfg.
* Removed Python 2 support.
* Contributors: Chris Lalancette, Timon Engelke

0.2.1 (2021-01-25)
------------------
* Fix osrf.py_common.process_utils.get_loop() implementation (`#70 <https://github.com/osrf/osrf_pycommon/issues/70>`_)
* Contributors: Michel Hidalgo

0.2.0 (2020-12-07)
------------------
* Python 2/3 version conflict (`#69 <https://github.com/osrf/osrf_pycommon/issues/69>`_)
* remove jessie because we no longer support 3.4 (`#67 <https://github.com/osrf/osrf_pycommon/issues/67>`_)
* Remove deprecated use of asyncio.coroutine decorator. (`#64 <https://github.com/osrf/osrf_pycommon/issues/64>`_)
* Fix the __str_\_ method for windows terminal_color. (`#65 <https://github.com/osrf/osrf_pycommon/issues/65>`_)
* Contributors: Chris Lalancette, Jochen Sprickerhof, William Woodall

0.1.10 (2020-05-08)
-------------------
* fixed simple deprecation warnings (issue `#61 <https://github.com/osrf/osrf_pycommon/issues/61>`_) (`#63 <https://github.com/osrf/osrf_pycommon/issues/63>`_)
* Also run tests with Python 3.7 and 3.8 (`#60 <https://github.com/osrf/osrf_pycommon/issues/60>`_)
* Remove old py2 platforms, add Suite3 option with Ubuntu Focal (`#58 <https://github.com/osrf/osrf_pycommon/issues/58>`_)
* Contributors: Shane Loretz, Zahi Kakish

0.1.9 (2019-10-10 12:55:00 -0800)
---------------------------------
* install resource marker file for package (`#56 <https://github.com/osrf/osrf_pycommon/pull/56>`_)
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
osrf_pycommon
=============

Commonly needed Python modules, used by Python software developed at OSRF
Commonly needed Python modules, used by Python software developed at OSRF.

Branches
========

If you are releasing (using ``stdeb`` or on the ROS buildfarm) for any Ubuntu < ``focal``, or for any OS that doesn't have a key for ``python3-importlib-metadata``, then you need to use the ``1.0.x`` branch, or the latest ``1.<something>`` branch, because starting with ``2.0.0``, that dependency will be required.

If you are using Python 2, then you should use the ``python2`` branch.
10 changes: 5 additions & 5 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -5,8 +5,8 @@
Things like ansi terminal coloring, capturing colored output from programs using subprocess, or even a simple logging system which provides some nice functionality over the built-in Python logging system.

The functionality provided here should be generic enough to be reused in arbitrary scenarios and should avoid bringing in dependencies which are not part of the standard Python library.
Where possible Windows and Linux/OS X should be supported, and where it cannot it should be gracefully degrading.
Code should be pure Python as well as Python 2 and Python 3 bilingual.
Where possible Windows, Linux, and macOS should be supported, and where it cannot it should be gracefully degrading.
Code should be pure Python 3.

Contents:

@@ -64,13 +64,13 @@ That will "uninstall" the hooks into the ``PYTHONPATH`` which point to your sour
Testing
-------

In order to run the tests you will need to install `nosetests <https://nose.readthedocs.org/>`_, `flake8 <https://flake8.readthedocs.org/>`_, and `Mock <http://www.voidspace.org.uk/python/mock/>`_.
In order to run the tests you will need to install `flake8 <https://flake8.readthedocs.org/>`_.

Once you have installed those, then run ``nosetest`` in the root of the ``osrf_pycommon`` source directory:
Once you have installed those, then run ``unittest``:

.. code-block:: bash
$ nosetests
$ python3 -m unittest discover -v tests
Building the Documentation
--------------------------
12 changes: 7 additions & 5 deletions docs/process_utils.rst
Original file line number Diff line number Diff line change
@@ -12,14 +12,14 @@ These are the main sections of this module:
Asynchronous Process Utilities
------------------------------

There is a function and class which can be used together with your custom `Tollius <http://trollius.readthedocs.org/>`_ or `asyncio <https://docs.python.org/3/library/asyncio.html>`_ run loop.
There is a function and class which can be used together with your custom `asyncio <https://docs.python.org/3/library/asyncio.html>`_ run loop.

The :py:func:`osrf_pycommon.process_utils.async_execute_process` function is a `coroutine <https://docs.python.org/3/library/asyncio-task.html#coroutines>`_ which allows you to run a process and get the output back bit by bit in real-time, either with stdout and stderr separated or combined.
This function also allows you to emulate the terminal using a pty simply by toggling a flag in the parameters.

Along side this coroutine is a `Protocol <https://docs.python.org/3/library/asyncio-protocol.html#protocols>`_ class, :py:class:`osrf_pycommon.process_utils.AsyncSubprocessProtocol`, from which you can inherit in order to customize how the yielded output is handled.

Because this coroutine is built on the ``trollius``/``asyncio`` framework's subprocess functions, it is portable and should behave the same on all major OS's. (including on Windows where an IOCP implementation is used)
Because this coroutine is built on the ``asyncio`` framework's subprocess functions, it is portable and should behave the same on all major OS's. (including on Windows where an IOCP implementation is used)

.. autofunction:: osrf_pycommon.process_utils.async_execute_process

@@ -33,9 +33,11 @@ In addtion to these functions, there is a utility function for getting the corre
Treatment of File Descriptors
-----------------------------

Unlike ``subprocess.Popen``, all of the ``process_utils`` functions behave the same way on Python versions 2.7 through 3.4, and they do not close `inheritable <https://docs.python.org/3.4/library/os.html#fd-inheritance>`. file descriptors before starting subprocesses. This is equivalent to passing ``close_fds=False`` to ``subprocess.Popen`` on all Python versions.
Like Python 3.4's ``subprocess.Popen`` (and newer versions), all of the ``process_utils`` functions do not close `inheritable <https://docs.python.org/3.4/library/os.html#fd-inheritance>` file descriptors before starting subprocesses.
This is equivalent to passing ``close_fds=False`` to ``subprocess.Popen`` on all Python versions.

In Python 3.2, the ``subprocess.Popen`` default for the ``close_fds`` option changed from ``False`` to ``True`` so that file descriptors opened by the parent process were closed before spawning the child process. In Python 3.4, `PEP 0446 <https://www.python.org/dev/peps/pep-0446/>`_ additionally made it so even when ``close_fds=False`` file descriptors which are `non-inheritable <https://docs.python.org/3.4/library/os.html#fd-inheritance>`_ are still closed before spawning the subprocess.
For historical context, in Python 3.2, the ``subprocess.Popen`` default for the ``close_fds`` option changed from ``False`` to ``True`` so that file descriptors opened by the parent process were closed before spawning the child process.
In Python 3.4, `PEP 0446 <https://www.python.org/dev/peps/pep-0446/>`_ additionally made it so even when ``close_fds=False`` file descriptors which are `non-inheritable <https://docs.python.org/3.4/library/os.html#fd-inheritance>`_ are still closed before spawning the subprocess.

If you want to be able to pass file descriptors to subprocesses in Python 3.4 or higher, you will need to make sure they are `inheritable <https://docs.python.org/3.4/library/os.html#fd-inheritance>`.

@@ -47,7 +49,7 @@ For synchronous execution and output capture of subprocess, there are two functi
- :py:func:`osrf_pycommon.process_utils.execute_process`
- :py:func:`osrf_pycommon.process_utils.execute_process_split`

These functions are not yet using the ``trollius``/``asyncio`` framework as a back-end and therefore on Windows will not stream the data from the subprocess as it does on Unix machines.
These functions are not yet using the ``asyncio`` framework as a back-end and therefore on Windows will not stream the data from the subprocess as it does on Unix machines.
Instead data will not be yielded until the subprocess is finished and all output is buffered (the normal warnings about long running programs with lots of output apply).

The streaming of output does not work on Windows because on Windows the :py:func:`select.select` method only works on sockets and not file-like objects which are used with subprocess pipes.
44 changes: 38 additions & 6 deletions osrf_pycommon/cli_utils/verb_pattern.py
Original file line number Diff line number Diff line change
@@ -14,9 +14,13 @@

"""API for implementing commands and verbs which used the verb pattern."""

import sys
import inspect

import pkg_resources
try:
import importlib.metadata as importlib_metadata
except ModuleNotFoundError:
import importlib_metadata


def call_prepare_arguments(func, parser, sysargs=None):
@@ -41,18 +45,36 @@ def call_prepare_arguments(func, parser, sysargs=None):
func_args = [parser]
# If the provided function takes two arguments and args were given
# also give the args to the function
arguments, _, _, defaults = inspect.getargspec(func)

# Remove the following if condition and keep else condition once Xenial is
# dropped
if sys.version_info[0] < 3:
arguments, _, _, defaults = inspect.getargspec(func)

else:
arguments, _, _, defaults, _, _, _ = inspect.getfullargspec(func)

if arguments[0] == 'self':
del arguments[0]
if defaults:
arguments = arguments[:-len(defaults)]
if len(arguments) not in [1, 2]:
# Remove the following if condition once Xenial is dropped
if sys.version_info[0] < 3:
raise ValueError("Given function '{0}' must have one or two "
"parameters (excluding self), but got '{1}' "
"parameters: '{2}'"
.format(func.__name__,
len(arguments),
', '.join(inspect.getargspec(func)[0])))

raise ValueError("Given function '{0}' must have one or two "
"parameters (excluding self), but got '{1}' "
"parameters: '{2}'"
.format(func.__name__,
len(arguments),
', '.join(inspect.getargspec(func)[0])))
', '.join(inspect.getfullargspec(func)[0])))

if len(arguments) == 2:
func_args.append(sysargs or [])
return func(*func_args) or parser
@@ -87,7 +109,7 @@ def create_subparsers(parser, cmd_name, verbs, group, sysargs, title=None):
subparser = parser.add_subparsers(
title=title or '{0} command'.format(cmd_name),
metavar=metavar,
description='Call `{0} {1} -h` for help on a each verb.'.format(
description='Call `{0} {1} -h` for help on each verb.'.format(
cmd_name, metavar),
dest='verb'
)
@@ -130,7 +152,12 @@ def list_verbs(group):
:rtype: list of str
"""
verbs = []
for entry_point in pkg_resources.iter_entry_points(group=group):
entry_points = importlib_metadata.entry_points()
if hasattr(entry_points, 'select'):
groups = entry_points.select(group=group)
else:
groups = entry_points.get(group, [])
for entry_point in groups:
verbs.append(entry_point.name)
return verbs

@@ -143,7 +170,12 @@ def load_verb_description(verb_name, group):
:returns: verb description
:rtype: dict
"""
for entry_point in pkg_resources.iter_entry_points(group=group):
entry_points = importlib_metadata.entry_points()
if hasattr(entry_points, 'select'):
groups = entry_points.select(group=group)
else:
groups = entry_points.get(group, [])
for entry_point in groups:
if entry_point.name == verb_name:
return entry_point.load()

Loading