diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9a4501ee..2cc1a534 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,6 +12,28 @@ jobs: uses: ros-infrastructure/ci/.github/workflows/pytest.yaml@main with: matrix-filter: del(.matrix.os[] | select(contains("windows"))) + + # Dedicated job for testing with empy < 4 + pytest-empy-legacy: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.11', '3.12', '3.13'] + name: Test empy < 4 compatibility (Python ${{ matrix.python-version }}) + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies with empy < 4 + run: | + python -m pip install --upgrade pip + pip install 'empy<4' + pip install -e .[test] + - name: Run tests + run: | + python -m pytest test/ -v yamllint: runs-on: ubuntu-latest steps: diff --git a/bloom/generators/debian/generator.py b/bloom/generators/debian/generator.py index c1abe493..4439e814 100644 --- a/bloom/generators/debian/generator.py +++ b/bloom/generators/debian/generator.py @@ -78,6 +78,8 @@ from bloom.logging import is_debug from bloom.logging import warning +from bloom.util import expand_template_em + from bloom.commands.git.patch.common import get_patch_config from bloom.commands.git.patch.common import set_patch_config @@ -102,23 +104,6 @@ debug(traceback.format_exc()) error("rosdistro was not detected, please install it.", exit=True) -try: - import em -except ImportError: - debug(traceback.format_exc()) - error("empy was not detected, please install it.", exit=True) - -# Fix unicode bug in empy -# This should be removed once upstream empy is fixed -# See: https://github.com/ros-infrastructure/bloom/issues/196 -try: - em.str = unicode - em.Stream.write_old = em.Stream.write - em.Stream.write = lambda self, data: em.Stream.write_old(self, data.encode('utf8')) -except NameError: - pass -# End fix - # Drop the first log prefix for this command enable_drop_first_log_prefix(True) @@ -151,10 +136,7 @@ def __place_template_folder(group, src, dst, gbp=False): debug("Not overwriting existing file '{0}'".format(template_dst)) else: with io.open(template_dst, 'w', encoding='utf-8') as f: - if not isinstance(template, str): - template = template.decode('utf-8') - # Python 2 API needs a `unicode` not a utf-8 string. - elif sys.version_info.major == 2: + if isinstance(template, bytes): template = template.decode('utf-8') f.write(template) shutil.copystat(template_abs_path, template_dst) @@ -483,17 +465,11 @@ def generate_substitutions_from_package( data['Licenses'] = licenses def convertToUnicode(obj): - if sys.version_info.major == 2: - if isinstance(obj, str): - return unicode(obj.decode('utf8')) - elif isinstance(obj, unicode): - return obj - else: - if isinstance(obj, bytes): - return str(obj.decode('utf8')) - elif isinstance(obj, str): - return obj - if isinstance(obj, list): + if isinstance(obj, bytes): + return str(obj.decode('utf8')) + elif isinstance(obj, str): + return obj + elif isinstance(obj, list): for i, val in enumerate(obj): obj[i] = convertToUnicode(val) return obj @@ -534,7 +510,7 @@ def __process_template_folder(path, subs): info("Expanding '{0}' -> '{1}'".format( os.path.relpath(item), os.path.relpath(template_path))) - result = em.expand(template, **subs) + result = expand_template_em(template, subs) # Don't write an empty file if len(result) == 0 and \ os.path.basename(template_path) in ['copyright']: @@ -542,8 +518,6 @@ def __process_template_folder(path, subs): continue # Write the result with io.open(template_path, 'w', encoding='utf-8') as f: - if sys.version_info.major == 2: - result = result.decode('utf-8') f.write(result) # Copy the permissions shutil.copymode(item, template_path) diff --git a/bloom/generators/rpm/generator.py b/bloom/generators/rpm/generator.py index 66c8f3fe..2d8bc62d 100644 --- a/bloom/generators/rpm/generator.py +++ b/bloom/generators/rpm/generator.py @@ -81,6 +81,7 @@ from bloom.util import code from bloom.util import execute_command +from bloom.util import expand_template_em from bloom.util import maybe_continue try: @@ -89,12 +90,6 @@ debug(traceback.format_exc()) error("rosdistro was not detected, please install it.", exit=True) -try: - import em -except ImportError: - debug(traceback.format_exc()) - error("empy was not detected, please install it.", exit=True) - # Drop the first log prefix for this command enable_drop_first_log_prefix(True) @@ -325,17 +320,11 @@ def generate_substitutions_from_package( summarize_dependency_mapping(data, depends, build_depends, resolved_deps) def convertToUnicode(obj): - if sys.version_info.major == 2: - if isinstance(obj, str): - return unicode(obj.decode('utf8')) - elif isinstance(obj, unicode): - return obj - else: - if isinstance(obj, bytes): - return str(obj.decode('utf8')) - elif isinstance(obj, str): - return obj - if isinstance(obj, list): + if isinstance(obj, bytes): + return str(obj.decode('utf8')) + elif isinstance(obj, str): + return obj + elif isinstance(obj, list): for i, val in enumerate(obj): obj[i] = convertToUnicode(val) return obj @@ -378,11 +367,9 @@ def __process_template_folder(path, subs): info("Expanding '{0}' -> '{1}'".format( os.path.relpath(item), os.path.relpath(template_path))) - result = em.expand(template, **subs) + result = expand_template_em(template, subs) # Write the result with io.open(template_path, 'w', encoding='utf-8') as f: - if sys.version_info.major == 2: - result = result.decode('utf-8') f.write(result) # Copy the permissions shutil.copymode(item, template_path) diff --git a/bloom/util.py b/bloom/util.py index 7ef46093..a39c84a5 100755 --- a/bloom/util.py +++ b/bloom/util.py @@ -84,6 +84,25 @@ to_unicode = str +def expand_template_em(template, subs): + """ + Compatibility function for EmPy 3 and 4. + EmPy 3: em.expand(template, **kwargs) + EmPy 4: em.expand(template, locals=dict) + """ + try: + import em + except ImportError: + error("empy was not detected, please install it.", exit=True) + + try: + # Try EmPy 4 API first + return em.expand(template, locals=subs) + except (TypeError, NameError): + # Fall back to EmPy 3 API + return em.expand(template, **subs) + + def flush_stdin(): try: from termios import tcflush, TCIFLUSH diff --git a/setup.py b/setup.py index 476911b0..f07c3b9c 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -import sys from setuptools import find_packages, setup @@ -20,7 +19,7 @@ install_requires=[ 'catkin_pkg >= 0.4.3', 'setuptools', - 'empy < 4', + 'empy', 'packaging', 'python-dateutil', 'PyYAML', diff --git a/test/unit_tests/test_generators/test_debian/test_generator.py b/test/unit_tests/test_generators/test_debian/test_generator.py index d0eb2723..466fcfb0 100644 --- a/test/unit_tests/test_generators/test_debian/test_generator.py +++ b/test/unit_tests/test_generators/test_debian/test_generator.py @@ -2,9 +2,9 @@ from ....utils.common import redirected_stdio -from bloom.generators.debian.generator import em from bloom.generators.debian.generator import get_changelogs from bloom.generators.debian.generator import format_description +from bloom.util import expand_template_em from catkin_pkg.packages import find_packages @@ -24,7 +24,7 @@ def test_unicode_templating(): assert 'bad_changelog_pkg' in packages chlogs = get_changelogs(packages['bad_changelog_pkg']) template = "@(changelog)" - em.expand(template, {'changelog': chlogs[0][2]}) + expand_template_em(template, {'changelog': chlogs[0][2]}) def test_format_description():