diff options
| author | Julien Stephan <jstephan@baylibre.com> | 2023-12-04 16:59:32 +0100 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2023-12-06 22:55:49 +0000 |
| commit | 85a2a6f68af304ace1d767e3e8367627cea898f6 (patch) | |
| tree | 8e20b0b68250cf3a8cc0e69d3036d5f87b92fbe9 /scripts/lib/recipetool/create_buildsys_python.py | |
| parent | 6c06fb0a4395bd98918cbcdb16e93e094e79226e (diff) | |
| download | poky-85a2a6f68af304ace1d767e3e8367627cea898f6.tar.gz | |
recipetool: create_buildsys_python: add pypi support
Today, we can use devtool/recipetool to create recipes for python projects
using the github url or the direct release tarball of the project, but the
create_buildsys_python plugin doesn't support the pypi class, since we cannot
know from the extracted source if the package is available on pypi or not.
By implementing the new optional process_url callback, we can detect
that the url is a pypi one (i.e 'https://pypi.org/project/<package>')
and retrieve the release tarball location.
Also detect if the url points to a release tarball hosted on
"files.pythonhosted.iorg" (i.e https://files.pythonhosted.org/packages/...)
In both cases, adds the pypi class, remove 'S' and 'SRC_URIxxx'
variables from the created recipe as they will be handled by the pypi class
and add the PYPI_PACKAGE variable
This helps to produce cleaner recipes when package is hosted on pypi.
If the url points to a github url or a release tarball not coming from
"files.pythonhosted.org", the created recipe is the same as before.
One can also use the newly added "--no-pypi" switch to NOT inherit
from pypi class on matching url, to keep legacy behaviour.
To create a recipe for a pypi package, one can now use one of the
new following syntax (using recipetool create / devtool add):
* recipetool create https://pypi.org/project/<package>
* recipetool create https://pypi.org/project/<package>/<version>
* recipetool create https://pypi.org/project/<package> --version <version>
or the old syntax:
* recipetool create https://files.pythonhosted.org/packages/<...>
(From OE-Core rev: 097a43846cd99a7d74d004efc57f583ce78970a4)
Signed-off-by: Julien Stephan <jstephan@baylibre.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts/lib/recipetool/create_buildsys_python.py')
| -rw-r--r-- | scripts/lib/recipetool/create_buildsys_python.py | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/scripts/lib/recipetool/create_buildsys_python.py b/scripts/lib/recipetool/create_buildsys_python.py index b620e3271b..5e07222ece 100644 --- a/scripts/lib/recipetool/create_buildsys_python.py +++ b/scripts/lib/recipetool/create_buildsys_python.py | |||
| @@ -18,7 +18,11 @@ import os | |||
| 18 | import re | 18 | import re |
| 19 | import sys | 19 | import sys |
| 20 | import subprocess | 20 | import subprocess |
| 21 | import json | ||
| 22 | import urllib.request | ||
| 21 | from recipetool.create import RecipeHandler | 23 | from recipetool.create import RecipeHandler |
| 24 | from urllib.parse import urldefrag | ||
| 25 | from recipetool.create import determine_from_url | ||
| 22 | 26 | ||
| 23 | logger = logging.getLogger('recipetool') | 27 | logger = logging.getLogger('recipetool') |
| 24 | 28 | ||
| @@ -111,6 +115,74 @@ class PythonRecipeHandler(RecipeHandler): | |||
| 111 | def __init__(self): | 115 | def __init__(self): |
| 112 | pass | 116 | pass |
| 113 | 117 | ||
| 118 | def process_url(self, args, classes, handled, extravalues): | ||
| 119 | """ | ||
| 120 | Convert any pypi url https://pypi.org/project/<package>/<version> into https://files.pythonhosted.org/packages/source/... | ||
| 121 | which corresponds to the archive location, and add pypi class | ||
| 122 | """ | ||
| 123 | |||
| 124 | if 'url' in handled: | ||
| 125 | return None | ||
| 126 | |||
| 127 | fetch_uri = None | ||
| 128 | source = args.source | ||
| 129 | required_version = args.version if args.version else None | ||
| 130 | match = re.match(r'https?://pypi.org/project/([^/]+)(?:/([^/]+))?/?$', urldefrag(source)[0]) | ||
| 131 | if match: | ||
| 132 | package = match.group(1) | ||
| 133 | version = match.group(2) if match.group(2) else required_version | ||
| 134 | |||
| 135 | json_url = f"https://pypi.org/pypi/%s/json" % package | ||
| 136 | response = urllib.request.urlopen(json_url) | ||
| 137 | if response.status == 200: | ||
| 138 | data = json.loads(response.read()) | ||
| 139 | if not version: | ||
| 140 | # grab latest version | ||
| 141 | version = data["info"]["version"] | ||
| 142 | pypi_package = data["info"]["name"] | ||
| 143 | for release in reversed(data["releases"][version]): | ||
| 144 | if release["packagetype"] == "sdist": | ||
| 145 | fetch_uri = release["url"] | ||
| 146 | break | ||
| 147 | else: | ||
| 148 | logger.warning("Cannot handle pypi url %s: cannot fetch package information using %s", source, json_url) | ||
| 149 | return None | ||
| 150 | else: | ||
| 151 | match = re.match(r'^https?://files.pythonhosted.org/packages.*/(.*)-.*$', source) | ||
| 152 | if match: | ||
| 153 | fetch_uri = source | ||
| 154 | pypi_package = match.group(1) | ||
| 155 | _, version = determine_from_url(fetch_uri) | ||
| 156 | |||
| 157 | if match and not args.no_pypi: | ||
| 158 | if required_version and version != required_version: | ||
| 159 | raise Exception("Version specified using --version/-V (%s) and version specified in the url (%s) do not match" % (required_version, version)) | ||
| 160 | # This is optionnal if BPN looks like "python-<pypi_package>" or "python3-<pypi_package>" (see pypi.bbclass) | ||
| 161 | # but at this point we cannot know because because user can specify the output name of the recipe on the command line | ||
| 162 | extravalues["PYPI_PACKAGE"] = pypi_package | ||
| 163 | # If the tarball extension is not 'tar.gz' (default value in pypi.bblcass) whe should set PYPI_PACKAGE_EXT in the recipe | ||
| 164 | pypi_package_ext = re.match(r'.*%s-%s\.(.*)$' % (pypi_package, version), fetch_uri) | ||
| 165 | if pypi_package_ext: | ||
| 166 | pypi_package_ext = pypi_package_ext.group(1) | ||
| 167 | if pypi_package_ext != "tar.gz": | ||
| 168 | extravalues["PYPI_PACKAGE_EXT"] = pypi_package_ext | ||
| 169 | |||
| 170 | # Pypi class will handle S and SRC_URIxxx variables, so remove them | ||
| 171 | # TODO: allow oe.recipeutils.patch_recipe_lines() to accept regexp so we can simplify the following to: | ||
| 172 | # extravalues['SRC_URI(?:\[.*?\])?'] = None | ||
| 173 | extravalues['S'] = None | ||
| 174 | extravalues['SRC_URI'] = None | ||
| 175 | extravalues['SRC_URI[md5sum]'] = None | ||
| 176 | extravalues['SRC_URI[sha1sum]'] = None | ||
| 177 | extravalues['SRC_URI[sha256sum]'] = None | ||
| 178 | extravalues['SRC_URI[sha384sum]'] = None | ||
| 179 | extravalues['SRC_URI[sha512sum]'] = None | ||
| 180 | |||
| 181 | classes.append('pypi') | ||
| 182 | |||
| 183 | handled.append('url') | ||
| 184 | return fetch_uri | ||
| 185 | |||
| 114 | def handle_classifier_license(self, classifiers, existing_licenses=""): | 186 | def handle_classifier_license(self, classifiers, existing_licenses=""): |
| 115 | 187 | ||
| 116 | licenses = [] | 188 | licenses = [] |
