diff --git a/.gitignore b/.gitignore index 4d48f6b90eacb9e5732069c757d4138f445d2d9a..54aa8c4f91839ac3dbbe48675baad6916e0d92e3 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ pyporter.egg-info .pytest_cache/ .mypy_cache/ *.spec +rpmbuild/ diff --git a/pyporter/__init__.py b/pyporter/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c621ababec7ef07fdfa6ceacb5ccee33882192b5 100644 --- a/pyporter/__init__.py +++ b/pyporter/__init__.py @@ -0,0 +1 @@ +from .utils import refine_requires, transform_module_name diff --git a/pyporter/pyporter.py b/pyporter/pyporter.py index 6de600c71a8ec3afbcb6e5240082615536a78516..ec7629a5324a4965ef37871d32fa8864f49e7f40 100755 --- a/pyporter/pyporter.py +++ b/pyporter/pyporter.py @@ -24,7 +24,6 @@ import json import logging import os import platform -import re import subprocess import sys import urllib @@ -34,6 +33,8 @@ from pathlib import Path from retry import retry_call +from pyporter.utils import refine_requires, transform_module_name + logger = logging.getLogger() handler = logging.StreamHandler(sys.stdout) @@ -214,12 +215,14 @@ class PyPorter: """ rs = self.__json["info"]["requires_dist"] if rs is None: - return + return [] + all_requires = [] for r in rs: idx = r.find(";") if idx != -1: r = r[:idx] - print("Requires:\t" + transform_module_name(r)) + all_requires.append(transform_module_name(r)) + return all_requires def __get_buildarch(self): """ @@ -327,35 +330,6 @@ class PyPorter: json.dump(self.__json, f) -def transform_module_name(n): - """ - return module name with version restriction. - Any string with '.' or '/' is considered file, and will be ignored - Modules start with python- will be changed to python3- for consistency. - """ - ns = re.split("[()]", n) - ver_constrain = [] - ns[0] = ns[0].strip() - if ns[0].startswith("python-"): - ns[0] = ns[0].replace("python-", "python3-") - else: - ns[0] = "python3-" + ns[0] - if ns[0].find("/") != -1 or ns[0].find(".") != -1: - return "" - return ns[0] - - -def refine_requires(req): - """ - return only requires without ';' (thus no extra) - """ - ra = req.split(";", 1) - # - # Do not add requires which has ;, which is often has very complicated precondition - # TODO: need more parsing of the denpency after ; - return transform_module_name(ra[0]) - - def download_source(porter, tgtpath): """ download source file from url, and save it to target path @@ -430,13 +404,6 @@ def package_installed(pkg): return False -def dependencies_ready(req_list): - """ - TODO: do not need to do dependency check here, do it in pyporter_run - """ - return "" - - def build_package(specfile): """ build rpm package with rpmbuild @@ -477,11 +444,6 @@ def build_rpm(porter, rootpath): porter.get_spec_name() + ".spec") req_list = build_spec(porter, specfile) - ret = dependencies_ready(req_list) - if ret != "": - logger.error( - f"{ret} can not be installed automatically, Please handle it") - return ret download_source(porter, os.path.join(buildroot, "SOURCES")) @@ -510,7 +472,8 @@ def build_spec(porter, output): print(source_tag_template.format(pkg_source=porter.get_source_url())) porter.get_buildarch() print("") - porter.get_requires() + for r in porter.get_requires(): + print("Requires:\t" + r) print("") print("%description") print(porter.get_description()) diff --git a/pyporter/utils.py b/pyporter/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..0fa2346fd026adc436d170413595a50a8aabc784 --- /dev/null +++ b/pyporter/utils.py @@ -0,0 +1,51 @@ +import re + +# def transform_module_name(s: str) -> str: +# """ +# return module name with version restriction. +# Any string with '.' or '/' is considered file, and will be ignored +# Modules start with python- will be changed to python3- for consistency. +# """ +# ns = re.split("[()]", s) +# ver_constrain = [] +# ns[0] = ns[0].strip() +# if ns[0].startswith("python-"): +# ns[0] = ns[0].replace("python-", "python3-") +# else: +# ns[0] = "python3-" + ns[0] + +# # Process version constraints +# if len(ns) > 1: +# ver_constrain.append(ns[1]) + +# if len(ver_constrain) > 0: +# return f"({ns[0]} {ver_constrain[0]})" +# else: +# return ns[0] + + +def transform_module_name(input_str): + # Extracting the module name from the input string + module_name = re.match(r"([a-zA-Z0-9_-]+)", input_str).group(1) + version_names = input_str[len(module_name):] + # Extracting the version constraint from the input string + version_constraint = version_names.split(",") + package_name = "python3-" + module_name + if len(version_constraint) > 1: + constraints_string = " with ".join([ + f"{package_name}{constraint}" for constraint in version_constraint + ]) + result_string = f"({constraints_string})" + else: + result_string = f"({package_name}{version_constraint[0]})" + + return result_string + + +def refine_requires(req: str) -> str: + """ + return only requires without ';' (thus no extra) + """ + ra = req.split(";", 1) + # Do not add requires which has ;, which is often has very complicated precondition + return transform_module_name(ra[0]) diff --git a/tests/TestPyprojectToml.json b/tests/TestPyprojectToml.json new file mode 100644 index 0000000000000000000000000000000000000000..8122cba4390c6458e80e1162a53cf5c7b3da920a --- /dev/null +++ b/tests/TestPyprojectToml.json @@ -0,0 +1,108 @@ +{ + "info": { + "author": "", + "author_email": "Andrey Petrov ", + "bugtrack_url": null, + "classifiers": [ + "Environment :: Web Environment", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Software Development :: Libraries" + ], + "description": "

\n\n![urllib3](https://github.com/urllib3/urllib3/raw/main/docs/_static/banner_github.svg)\n\n

\n\n

\n \"PyPI\n \"Python\n \"Join\n \"Coverage\n \"Build\n \"Documentation
\n \"OpenSSF\n \"SLSA\n \"CII\n

\n\nurllib3 is a powerful, *user-friendly* HTTP client for Python. Much of the\nPython ecosystem already uses urllib3 and you should too.\nurllib3 brings many critical features that are missing from the Python\nstandard libraries:\n\n- Thread safety.\n- Connection pooling.\n- Client-side SSL/TLS verification.\n- File uploads with multipart encoding.\n- Helpers for retrying requests and dealing with HTTP redirects.\n- Support for gzip, deflate, brotli, and zstd encoding.\n- Proxy support for HTTP and SOCKS.\n- 100% test coverage.\n\nurllib3 is powerful and easy to use:\n\n```python3\n>>> import urllib3\n>>> resp = urllib3.request(\"GET\", \"http://httpbin.org/robots.txt\")\n>>> resp.status\n200\n>>> resp.data\nb\"User-agent: *\\nDisallow: /deny\\n\"\n```\n\n## Installing\n\nurllib3 can be installed with [pip](https://pip.pypa.io):\n\n```bash\n$ python -m pip install urllib3\n```\n\nAlternatively, you can grab the latest source code from [GitHub](https://github.com/urllib3/urllib3):\n\n```bash\n$ git clone https://github.com/urllib3/urllib3.git\n$ cd urllib3\n$ pip install .\n```\n\n\n## Documentation\n\nurllib3 has usage and reference documentation at [urllib3.readthedocs.io](https://urllib3.readthedocs.io).\n\n\n## Community\n\nurllib3 has a [community Discord channel](https://discord.gg/urllib3) for asking questions and\ncollaborating with other contributors. Drop by and say hello šŸ‘‹\n\n\n## Contributing\n\nurllib3 happily accepts contributions. Please see our\n[contributing documentation](https://urllib3.readthedocs.io/en/latest/contributing.html)\nfor some tips on getting started.\n\n\n## Security Disclosures\n\nTo report a security vulnerability, please use the\n[Tidelift security contact](https://tidelift.com/security).\nTidelift will coordinate the fix and disclosure with maintainers.\n\n\n## Maintainers\n\n- [@sethmlarson](https://github.com/sethmlarson) (Seth M. Larson)\n- [@pquentin](https://github.com/pquentin) (Quentin Pradet)\n- [@theacodes](https://github.com/theacodes) (Thea Flowers)\n- [@haikuginger](https://github.com/haikuginger) (Jess Shapiro)\n- [@lukasa](https://github.com/lukasa) (Cory Benfield)\n- [@sigmavirus24](https://github.com/sigmavirus24) (Ian Stapleton Cordasco)\n- [@shazow](https://github.com/shazow) (Andrey Petrov)\n\nšŸ‘‹\n\n\n## Sponsorship\n\nIf your company benefits from this library, please consider [sponsoring its\ndevelopment](https://urllib3.readthedocs.io/en/latest/sponsors.html).\n\n\n## For Enterprise\n\nProfessional support for urllib3 is available as part of the [Tidelift\nSubscription][1]. Tidelift gives software development teams a single source for\npurchasing and maintaining their software, with professional grade assurances\nfrom the experts who know it best, while seamlessly integrating with existing\ntools.\n\n[1]: https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=readme\n", + "description_content_type": "text/markdown", + "docs_url": null, + "download_url": "", + "downloads": { + "last_day": -1, + "last_month": -1, + "last_week": -1 + }, + "home_page": "", + "keywords": "filepost,http,httplib,https,pooling,ssl,threadsafe,urllib", + "license": "", + "maintainer": "", + "maintainer_email": "Seth Michael Larson , Quentin Pradet ", + "name": "urllib3", + "package_url": "https://pypi.org/project/urllib3/", + "platform": null, + "project_url": "https://pypi.org/project/urllib3/", + "project_urls": { + "Changelog": "https://github.com/urllib3/urllib3/blob/main/CHANGES.rst", + "Code": "https://github.com/urllib3/urllib3", + "Documentation": "https://urllib3.readthedocs.io", + "Issue tracker": "https://github.com/urllib3/urllib3/issues" + }, + "release_url": "https://pypi.org/project/urllib3/2.0.3/", + "requires_dist": [ + "brotli>=1.0.9; platform_python_implementation == 'CPython' and extra == 'brotli'", + "certifi; extra == 'secure'", + "urllib3-secure-extra; extra == 'secure'", + "pysocks<2.0,>=1.5.6; extra == 'socks'" + ], + "requires_python": ">=3.7", + "summary": "HTTP library with thread-safe connection pooling, file post, and more.", + "version": "2.0.3", + "yanked": false, + "yanked_reason": null + }, + "last_serial": 18406816, + "urls": [ + { + "comment_text": "", + "digests": { + "blake2b_256": "8a03ad9306a50d05c166e3456fe810f33cee2b8b2a7a6818ec5d4908c4ec6b36", + "md5": "8a9ecd2fb1059fdc16a1c36a1139b546", + "sha256": "48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1" + }, + "downloads": -1, + "filename": "urllib3-2.0.3-py3-none-any.whl", + "has_sig": false, + "md5_digest": "8a9ecd2fb1059fdc16a1c36a1139b546", + "packagetype": "bdist_wheel", + "python_version": "py3", + "requires_python": ">=3.7", + "size": 123613, + "upload_time": "2023-06-07T10:59:09", + "upload_time_iso_8601": "2023-06-07T10:59:09.651459Z", + "url": "https://files.pythonhosted.org/packages/8a/03/ad9306a50d05c166e3456fe810f33cee2b8b2a7a6818ec5d4908c4ec6b36/urllib3-2.0.3-py3-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "blake2b_256": "d6af3b4cfedd46b3addab52e84a71ab26518272c23c77116de3c61ead54af903", + "md5": "39c1af575eac4938c33e9bdf4549c1dd", + "sha256": "bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825" + }, + "downloads": -1, + "filename": "urllib3-2.0.3.tar.gz", + "has_sig": false, + "md5_digest": "39c1af575eac4938c33e9bdf4549c1dd", + "packagetype": "sdist", + "python_version": "source", + "requires_python": ">=3.7", + "size": 280489, + "upload_time": "2023-06-07T10:59:13", + "upload_time_iso_8601": "2023-06-07T10:59:13.325611Z", + "url": "https://files.pythonhosted.org/packages/d6/af/3b4cfedd46b3addab52e84a71ab26518272c23c77116de3c61ead54af903/urllib3-2.0.3.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "vulnerabilities": [] +} diff --git a/tests/test_get_pyproject_toml.py b/tests/test_get_pyproject_toml.py new file mode 100644 index 0000000000000000000000000000000000000000..60dc5bb499c98a55a8543aafb71603f03138a083 --- /dev/null +++ b/tests/test_get_pyproject_toml.py @@ -0,0 +1,47 @@ +import os +import unittest +from unittest.mock import MagicMock, patch + +from pyporter.pyporter import porter_creator + +SRC_URL = "https://files.pythonhosted.org/packages/d6/af/"\ + "3b4cfedd46b3addab52e84a71ab26518272c23c77116de3c61ead54af903/urllib3-2.0.3.tar.gz" # noqa + + +class TestPyprojectToml(unittest.TestCase): + + def setUp(self): + self.f = open(os.path.join('tests', __class__.__name__ + '.json')) + self.data = self.f.read().encode() + + @patch('urllib.request.urlopen') + def test_pyprojecttoml(self, m): + cm = MagicMock() + cm.read.return_value = self.data + cm.__enter__.return_value = cm + m.return_value = cm + + args = MagicMock() + args.configure_mock(type="python") + args.pkg = "urllib3" + args.pkgversion = "2.0.3" + args.arch = None + args.mirror = "" + + p = porter_creator(args) + self.assertEqual("https://pypi.org/project/urllib3/", p.get_home()) + self.assertEqual(p.get_version(), "2.0.3") + self.assertEqual(p.get_source_url(), SRC_URL) + self.assertEqual(p.get_license(), "MIT License") + self.assertEqual(p.get_requires(), [ + "(python3-brotli>=1.0.9)", "(python3-certifi)", + "(python3-urllib3-secure-extra)", + "(python3-pysocks<2.0 with python3-pysocks>=1.5.6)" + ]) + + def tearDown(self): + self.f.close() + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_transform_module_name.py b/tests/test_transform_module_name.py new file mode 100644 index 0000000000000000000000000000000000000000..765946e8904096111d7a7d3a0e2b702aa3622849 --- /dev/null +++ b/tests/test_transform_module_name.py @@ -0,0 +1,19 @@ +import unittest + +from pyporter.utils import transform_module_name + + +class TestTransofrmModuleName(unittest.TestCase): + + def test_transform_module_name(self): + input_str = "brotli>=1.0.9" + expected_output = "(python3-brotli>=1.0.9)" + self.assertEqual(transform_module_name(input_str), expected_output) + + input_str = "pysocks<2.0,>=1.5.6" + expected_output = "(python3-pysocks<2.0 with python3-pysocks>=1.5.6)" + self.assertEqual(transform_module_name(input_str), expected_output) + + +if __name__ == '__main__': + unittest.main()