From fa91e6190e7874e1950fe51333aeeec5c6ca79c6 Mon Sep 17 00:00:00 2001 From: Corey Hickey Date: Wed, 28 Jul 2021 15:58:49 -0700 Subject: [PATCH] fix inconsistent naming between packages and dependencies This works around: https://github.com/pypa/setuptools/issues/2522 For example, when building a module which depends on 'backports_abc', fpm will currently translate that into a dependency on 'backports-abc'. When building the 'backports_abc' module, fpm will currently retain the original name. Thus, it is not possible to use fpm to meet some dependencies. Underscores should be allowed in package names, but the most important thing is to be internally consistent. Monkey-patch pkg_resources.safe_name() in order to allow underscores (default for fpm) or disallow underscores, according to user specification. If and when pkg_resources fixes safe_name(), then the behavior of fpm will remain the same. Note that this change matches both methods of loading requirements: * for setup.py, setuptools internally calls pkg_resources.safe_name() * for requirements.txt, pkg_resources uses its own safe_name() --- lib/fpm/package/pyfpm/get_metadata.py | 7 +++++-- lib/fpm/package/python.rb | 6 ++++++ lib/fpm/package/python_patch.py | 23 +++++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/lib/fpm/package/pyfpm/get_metadata.py b/lib/fpm/package/pyfpm/get_metadata.py index 1237e27423..fc3244f6ee 100644 --- a/lib/fpm/package/pyfpm/get_metadata.py +++ b/lib/fpm/package/pyfpm/get_metadata.py @@ -32,13 +32,16 @@ def u(s): class get_metadata(Command): description = "get package metadata" user_options = [ + ('allow-underscores', 'a', 'allow underscores in package names/deps'), ('load-requirements-txt', 'l', "load dependencies from requirements.txt"), ("output=", "o", "output destination for metadata json") ] - boolean_options = ['load-requirements-txt'] + boolean_options = ['allow-underscores', 'load-requirements-txt'] def initialize_options(self): + # allow-underscores is handled in python_patch.py; this is ignored here + self.allow_underscores = False self.load_requirements_txt = False self.cwd = None self.output = None @@ -68,7 +71,7 @@ def process_dep(self, dep): def run(self): data = { - "name": self.distribution.get_name(), + "name": pkg_resources.safe_name(self.distribution.get_name()), "version": self.distribution.get_version(), "author": u("%s <%s>") % ( u(self.distribution.get_author()), diff --git a/lib/fpm/package/python.rb b/lib/fpm/package/python.rb index d4cf8fb708..3e9dba3716 100644 --- a/lib/fpm/package/python.rb +++ b/lib/fpm/package/python.rb @@ -19,6 +19,8 @@ # class FPM::Package::Python < FPM::Package # Flags '--foo' will be accessable as attributes[:python_foo] + option "--allow-underscores", :flag, "Should underscores be allowed in " \ + " package names and dependencies?", :default => true option "--bin", "PYTHON_EXECUTABLE", "The path to the python executable you wish to run.", :default => "python" option "--easyinstall", "EASYINSTALL_EXECUTABLE", @@ -224,6 +226,10 @@ def load_package_info(setup_py) setup_cmd = "env PYTHONPATH=#{pylib}:$PYTHONPATH #{attributes[:python_bin]} " \ "#{pylib}/python_patch.py --command-packages=pyfpm get_metadata --output=#{tmp}" + if attributes[:python_allow_underscores?] + setup_cmd += " --allow-underscores" + end + if attributes[:python_obey_requirements_txt?] setup_cmd += " --load-requirements-txt" end diff --git a/lib/fpm/package/python_patch.py b/lib/fpm/package/python_patch.py index a8f3e39013..134ea05389 100755 --- a/lib/fpm/package/python_patch.py +++ b/lib/fpm/package/python_patch.py @@ -1,4 +1,27 @@ +import pkg_resources +import re +import sys from setuptools.dist import Distribution + + +def gen_safe_name(allow_underscores): + if allow_underscores: + regex = '[^A-Za-z0-9_.]+' + else: + # This matches the original verion in pkg_resources. + regex = '[^A-Za-z0-9.]+' + + def safe_name(name): + return re.sub(regex, '-', name) + + return safe_name + + +# Use simple argument parsing--older python lacks argparse and optparse has no +# good way to ignore unrecognized options (which need to be passed through). +pkg_resources.safe_name = gen_safe_name('--allow-underscores' in sys.argv) + + try: # Many older modules include a setup.py that uses distutils.core, which # does not support the install_requires attribute. Monkey-patch