From 6f79f0a37139c0e6beff467cafafdfc57a908bd9 Mon Sep 17 00:00:00 2001 From: u Date: Mon, 13 Jan 2025 09:50:31 +0300 Subject: [PATCH 1/3] Add generating from archive option, fix exceptions, caused by multiline content in rpm spec files, remove shebangs, fix missing provides_extra field --- py2pack/__init__.py | 105 +++++++++-- py2pack/templates/fedora.spec | 70 +++---- py2pack/templates/mageia.spec | 8 +- py2pack/templates/opensuse-legacy.spec | 8 +- py2pack/templates/opensuse.spec | 8 +- test/examples/poetry-fedora-augmented.spec | 168 +++++++++++++++++ test/examples/poetry-mageia.spec | 32 ++++ test/examples/poetry-opensuse-augmented.spec | 80 +------- test/examples/poetry-opensuse-legacy.spec | 159 ++++++++++++++++ test/examples/poetry-opensuse.dsc | 8 + test/examples/poetry-opensuse.spec | 163 +++++++++++++++++ test/examples/py2pack-fedora-augmented.spec | 173 +++++++++++++++--- test/examples/py2pack-mageia.spec | 2 +- test/examples/py2pack-opensuse-augmented.spec | 163 ++++++++++++++--- test/examples/py2pack-opensuse-legacy.spec | 2 +- test/examples/py2pack-opensuse.spec | 2 +- .../sampleproject-fedora-augmented.spec | 96 +++++----- test/examples/sampleproject-mageia.spec | 2 +- .../sampleproject-opensuse-augmented.spec | 84 ++++----- .../sampleproject-opensuse-legacy.spec | 2 +- test/examples/sampleproject-opensuse.spec | 2 +- test/test_template.py | 34 +++- 22 files changed, 1063 insertions(+), 308 deletions(-) create mode 100644 test/examples/poetry-fedora-augmented.spec create mode 100644 test/examples/poetry-mageia.spec create mode 100644 test/examples/poetry-opensuse-legacy.spec create mode 100644 test/examples/poetry-opensuse.dsc create mode 100644 test/examples/poetry-opensuse.spec diff --git a/py2pack/__init__.py b/py2pack/__init__.py index 37d39a8..7eaec12 100755 --- a/py2pack/__init__.py +++ b/py2pack/__init__.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# # Copyright (c) 2013, Sascha Peilicke # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -40,6 +37,22 @@ get_metadata) from email import parser +import tarfile +import zipfile +from packaging.requirements import Requirement +from os.path import basename +from io import StringIO + + +try: + import distro + DEFAULT_TEMPLATE = { + 'fedora': 'fedora.spec', + 'debian': 'opensuse.dsc', + 'mageia': 'mageia.spec' + }.get(distro.id(), 'opensuse.spec') +except ModuleNotFoundError: + DEFAULT_TEMPLATE = 'opensuse.spec' def replace_string(output_string, replaces): @@ -68,8 +81,14 @@ def pypi_json(project, release=None): def pypi_text_file(pkg_info_path): - with open(pkg_info_path, 'r') as pkg_info_file: - pkg_info_lines = parser.Parser().parse(pkg_info_file) + pkg_info_file = open(pkg_info_path, 'r') + text = pypi_text_stream(pkg_info_file) + pkg_info_file.close() + return text + + +def pypi_text_stream(pkg_info_stream): + pkg_info_lines = parser.Parser().parse(pkg_info_stream) pkg_info_dict = {} for key, value in pkg_info_lines.items(): key = key.lower().replace('-', '_') @@ -85,8 +104,14 @@ def pypi_text_file(pkg_info_path): def pypi_json_file(file_path): - with open(file_path, 'r') as json_file: - js = json.load(json_file) + json_file = open(file_path, 'r') + js = pypi_json_stream(json_file) + json_file.close() + return js + + +def pypi_json_stream(json_stream): + js = json.load(json_stream) if 'info' not in js: js = {'info': js} if 'urls' not in js: @@ -94,6 +119,29 @@ def pypi_json_file(file_path): return js +def pypi_archive_file_tar(file_path): + with tarfile.open(file_path, 'r') as archive: + for member in archive.getmembers(): + if os.path.basename(member.name) == 'PKG-INFO': + return pypi_text_stream(StringIO(archive.extractfile(member).read().decode())) + raise KeyError('PKG-INFO not found on archive '+file_path) + + +def pypi_archive_file_zip(file_path): + with zipfile.ZipFile(file_path, 'r') as archive: + for member in archive.namelist(): + if os.path.basename(member) == 'PKG-INFO': + return pypi_text_stream(StringIO(archive.open(member).read().decode())) + raise KeyError('PKG-INFO not found on archive '+file_path) + + +def pypi_archive_file(file_path): + try: + return pypi_archive_file_tar(file_path) + except tarfile.ReadError: + return pypi_archive_file_zip(file_path) + + def _get_template_dirs(): """existing directories where to search for jinja2 templates. The order is important. The first found template from the first found dir wins!""" @@ -111,6 +159,22 @@ def _get_template_dirs(): ]) +def fix_data(data): + extra_from_req = re.compile(r'''\bextra\s+==\s+["']([^"']+)["']''') + extras = [] + data_info = data["info"] + requires_dist = data_info.get("requires_dist", []) or [] + provides_extra = data_info.get("provides_extra", []) or [] + for required_dist in requires_dist: + req = Requirement(required_dist) + if found := extra_from_req.search(str(req.marker)): + extras.append(found.group(1)) + provides_extra = list(sorted(set([*extras, *provides_extra]))) + data_info["requires_dist"] = requires_dist + data_info["provides_extra"] = provides_extra + data_info["classifiers"] = (data_info.get("classifiers", []) or []) + + def list_packages(args=None): """query the "Simple API" of PYPI for all packages and print them.""" print('listing all PyPI packages...') @@ -407,6 +471,12 @@ def generate(args): _normalize_license(data) + for field in ['summary', 'license', 'home_page', 'source_url', 'summary_no_ending_dot']: + if field not in data: + continue + # remove line breaks to avoid multiline rpm spec file + data[field + '_singleline'] = str(data[field]).replace('\n', ' ') + env = _prepare_template_env(_get_template_dirs()) template = env.get_template(args.template) result = template.render(data).encode('utf-8') # render template and encode properly @@ -425,23 +495,28 @@ def fetch_local_data(args): localfile = os.path.join(f'{args.name}.egg-info', 'PKG-INFO') if os.path.isfile(localfile): try: - data = pypi_json_file(localfile) - except json.decoder.JSONDecodeError: - data = pypi_text_file(localfile) + data = pypi_archive_file(localfile) + except Exception: + try: + data = pypi_json_file(localfile) + except json.decoder.JSONDecodeError: + data = pypi_text_file(localfile) args.fetched_data = data args.version = args.fetched_data['info']['version'] - return - fetch_data(args) + fix_data(data) + else: + fetch_data(args) def fetch_data(args): - args.fetched_data = pypi_json(args.name, args.version) - urls = args.fetched_data.get('urls', []) + data = args.fetched_data = pypi_json(args.name, args.version) + urls = data.get('urls', []) if len(urls) == 0: print(f"unable to find a suitable release for {args.name}!") sys.exit(1) else: args.version = args.fetched_data['info']['version'] # return current release number + fix_data(data) def newest_download_url(args): @@ -502,7 +577,7 @@ def main(): parser_generate.add_argument('--source-glob', help='source glob template') parser_generate.add_argument('--local', action='store_true', help='build from local package') parser_generate.add_argument('--localfile', default='', help='path to the local PKG-INFO or json metadata') - parser_generate.add_argument('-t', '--template', choices=file_template_list(), default='opensuse.spec', help='file template') + parser_generate.add_argument('-t', '--template', choices=file_template_list(), default=DEFAULT_TEMPLATE, help='file template') parser_generate.add_argument('-f', '--filename', help='spec filename (optional)') # TODO (toabctl): remove this is a later release parser_generate.add_argument( diff --git a/py2pack/templates/fedora.spec b/py2pack/templates/fedora.spec index 611cbe8..8ce5875 100644 --- a/py2pack/templates/fedora.spec +++ b/py2pack/templates/fedora.spec @@ -3,61 +3,31 @@ Name: python-%{pypi_name} Version: {{ version }} Release: %autorelease -Summary: {{ summary }} +# Fill in the actual package summary to submit package to Fedora +Summary: {{ summary_singleline }} # Check if the automatically generated License and its spelling is correct for Fedora # https://docs.fedoraproject.org/en-US/packaging-guidelines/LicensingGuidelines/ -License: {{ license }} -URL: {{ home_page }} -Source: {{ source_url|replace(version, '%{version}') }} +License: {{ license_singleline }} +URL: {{ home_page_singleline }} +Source: {{ source_url_singleline|replace(version, '%{version}') }} BuildRequires: pyproject-rpm-macros -BuildRequires: python-devel -%if %{undefined python_module} -%define python_module() python3dist(%1) -%endif +BuildRequires: python3-devel -{%- set build_requires_plus_pip = ((build_requires if build_requires and build_requires is not none else []) + - ['pip']) %} -{%- for req in build_requires_plus_pip |sort %} -BuildRequires: %{python_module {{ req }}} -{%- endfor %} -{%- if (install_requires and install_requires is not none) or (tests_require and tests_require is not none) %} -# SECTION test requirements -%if %{with test} -{%- if install_requires and install_requires is not none %} -{%- for req in install_requires|reject("in",build_requires)|sort %} -BuildRequires: %{python_module {{ req }}} -{%- endfor %} -{%- endif %} -{%- if tests_require and tests_require is not none %} -{%- for req in tests_require|sort|reject("in",build_requires|sort) %} -BuildRequires: %{python_module {{ req }}} -{%- endfor %} -{%- endif %} -%endif -# /SECTION -{%- endif %} {%- if source_url.endswith('.zip') %} BuildRequires: unzip {%- endif %} BuildRequires: fdupes -{%- if install_requires and install_requires is not none %} -{%- for req in install_requires|sort %} -Requires: %{python_module {{ req }}} -{%- endfor %} -{%- endif %} -{%- if extras_require and extras_require is not none %} -{%- for reqlist in extras_require.values() %} -{%- for req in reqlist %} -Suggests: %{python_module {{ req }}} -{%- endfor %} -{%- endfor %} -{%- endif %} {%- if not has_ext_modules %} BuildArch: noarch {%- endif %} +{%- if provides_extra and provides_extra is not none %} +{%- set provides_extra_comma_separated_list = ','.join(provides_extra) %} +{%- set have_provides_extra = 1 %} +{%- endif %} + # Fill in the actual package description to submit package to Fedora %global _description %{expand: {{ description }}} @@ -69,22 +39,27 @@ Summary: %{summary} %description -n %{python_name} %_description +{%- if have_provides_extra %} +# For official Fedora packages, review which extras should be actually packaged +# See: https://docs.fedoraproject.org/en-US/packaging-guidelines/Python/#Extras +%pyproject_extras_subpkg -n %{python_name} {{ provides_extra_comma_separated_list }} +{%- endif %} %prep %autosetup -p1 -n %{pypi_name}-%{version} + +%generate_buildrequires +# Keep only those extras which you actually want to package or use during tests +%pyproject_buildrequires {% if have_provides_extra %}-x {{ provides_extra_comma_separated_list }}{% endif %} + + %build %pyproject_wheel %install %pyproject_install -{%- set scripts_or_console_scripts = ( - (scripts|map('basename')|list if scripts and scripts is not none else []) + - (console_scripts if console_scripts and console_scripts is not none else [])) %} -#{%- for script in scripts_or_console_scripts %} -#%python_clone -a %{buildroot}%{_bindir}/{{ script }} -#{%- endfor %} # For official Fedora packages, including files with '*' +auto is not allowed # Replace it with a list of relevant Python modules/globs and list extra files in %%files %pyproject_save_files '*' +auto @@ -93,6 +68,7 @@ Summary: %{summary} {%- if testsuite or test_suite %} %if %{with test} %check +%pyproject_check_import {%- if has_ext_modules %} %pytest_arch {%- else %} diff --git a/py2pack/templates/mageia.spec b/py2pack/templates/mageia.spec index 5c2140d..52cf170 100644 --- a/py2pack/templates/mageia.spec +++ b/py2pack/templates/mageia.spec @@ -3,11 +3,11 @@ Name: python-%{mod_name} Version: {{ version }} Release: %mkrel 1 -Url: {{ home_page }} -Summary: {{ summary }} -License: {{ license }} +Url: {{ home_page_singleline }} +Summary: {{ summary_singleline }} +License: {{ license_singleline }} Group: Development/Python -Source: {{ source_url|replace(version, '%{version}') }} +Source: {{ source_url_singleline|replace(version, '%{version}') }} BuildRoot: %{_tmppath}/%{name}-%{version}-buildroot BuildRequires: python-devel {%- for req in requires %} diff --git a/py2pack/templates/opensuse-legacy.spec b/py2pack/templates/opensuse-legacy.spec index 349e50d..30b3858 100644 --- a/py2pack/templates/opensuse-legacy.spec +++ b/py2pack/templates/opensuse-legacy.spec @@ -18,10 +18,10 @@ Name: python-{{ name }} Version: {{ version }} Release: 0 -Summary: {{ summary_no_ending_dot|default(summary, true) }} -License: {{ license }} -URL: {{ home_page }} -Source: {{ source_url|replace(version, '%{version}') }} +Summary: {{ summary_no_ending_dot_singleline|default(summary, true) }} +License: {{ license_singleline }} +URL: {{ home_page_singleline }} +Source: {{ source_url_singleline|replace(version, '%{version}') }} BuildRequires: python-setuptools {%- if install_requires and install_requires is not none %} {%- for req in install_requires|sort %} diff --git a/py2pack/templates/opensuse.spec b/py2pack/templates/opensuse.spec index 0d7286a..62d953f 100644 --- a/py2pack/templates/opensuse.spec +++ b/py2pack/templates/opensuse.spec @@ -19,10 +19,10 @@ Name: python-{{ name }} Version: {{ version }} Release: 0 -Summary: {{ summary_no_ending_dot|default(summary, true) }} -License: {{ license }} -URL: {{ home_page }} -Source: {{ source_url|replace(version, '%{version}') }} +Summary: {{ summary_no_ending_dot_singleline|default(summary, true) }} +License: {{ license_singleline }} +URL: {{ home_page_singleline }} +Source: {{ source_url_singleline|replace(version, '%{version}') }} BuildRequires: python-rpm-macros {%- set build_requires_plus_pip = ((build_requires if build_requires and build_requires is not none else []) + ['pip']) %} diff --git a/test/examples/poetry-fedora-augmented.spec b/test/examples/poetry-fedora-augmented.spec new file mode 100644 index 0000000..c38d98d --- /dev/null +++ b/test/examples/poetry-fedora-augmented.spec @@ -0,0 +1,168 @@ +__USER__%define pypi_name poetry +%define python_name python3-%{pypi_name} +Name: python-%{pypi_name} +Version: 1.5.1 +Release: %autorelease +# Fill in the actual package summary to submit package to Fedora +Summary: Python dependency management and packaging made easy. + +# Check if the automatically generated License and its spelling is correct for Fedora +# https://docs.fedoraproject.org/en-US/packaging-guidelines/LicensingGuidelines/ +License: MIT +URL: https://python-poetry.org/ +Source: https://files.pythonhosted.org/packages/source/p/poetry/poetry-%{version}.tar.gz + +BuildRequires: pyproject-rpm-macros +BuildRequires: python3-devel +BuildRequires: fdupes +BuildArch: noarch + +# Fill in the actual package description to submit package to Fedora +%global _description %{expand: +# Poetry: Python packaging and dependency management made easy + +[![Stable Version](https://img.shields.io/pypi/v/poetry?label=stable)][PyPI Releases] +[![Pre-release Version](https://img.shields.io/github/v/release/python-poetry/poetry?label=pre-release&include_prereleases&sort=semver)][PyPI Releases] +[![Python Versions](https://img.shields.io/pypi/pyversions/poetry)][PyPI] +[![Download Stats](https://img.shields.io/pypi/dm/poetry)](https://pypistats.org/packages/poetry) +[![Discord](https://img.shields.io/discord/487711540787675139?logo=discord)][Discord] + +Poetry helps you declare, manage and install dependencies of Python projects, +ensuring you have the right stack everywhere. + +![Poetry Install](https://raw.githubusercontent.com/python-poetry/poetry/master/assets/install.gif) + +Poetry replaces `setup.py`, `requirements.txt`, `setup.cfg`, `MANIFEST.in` and `Pipfile` with a simple `pyproject.toml` +based project format. + +```toml +[tool.poetry] +name = "my-package" +version = "0.1.0" +description = "The description of the package" + +license = "MIT" + +authors = [ + "Sébastien Eustace " +] + +repository = "https://github.com/python-poetry/poetry" +homepage = "https://python-poetry.org" + +# README file(s) are used as the package description +readme = ["README.md", "LICENSE"] + +# Keywords (translated to tags on the package index) +keywords = ["packaging", "poetry"] + +[tool.poetry.dependencies] +# Compatible Python versions +python = ">=3.8" +# Standard dependency with semver constraints +aiohttp = "^3.8.1" +# Dependency with extras +requests = { version = "^2.28", extras = ["security"] } +# Version-specific dependencies with prereleases allowed +tomli = { version = "^2.0.1", python = "<3.11", allow-prereleases = true } +# Git dependencies +cleo = { git = "https://github.com/python-poetry/cleo.git", branch = "master" } +# Optional dependencies (installed by extras) +pendulum = { version = "^2.1.2", optional = true } + +# Dependency groups are supported for organizing your dependencies +[tool.poetry.group.dev.dependencies] +pytest = "^7.1.2" +pytest-cov = "^3.0" + +# ...and can be installed only when explicitly requested +[tool.poetry.group.docs] +optional = true +[tool.poetry.group.docs.dependencies] +Sphinx = "^5.1.1" + +# Python-style entrypoints and scripts are easily expressed +[tool.poetry.scripts] +my-script = "my_package:main" +``` + +## Installation + +Poetry supports multiple installation methods, including a simple script found at [install.python-poetry.org]. For full +installation instructions, including advanced usage of the script, alternate install methods, and CI best practices, see +the full [installation documentation]. + +## Documentation + +[Documentation] for the current version of Poetry (as well as the development branch and recently out of support +versions) is available from the [official website]. + +## Contribute + +Poetry is a large, complex project always in need of contributors. For those new to the project, a list of +[suggested issues] to work on in Poetry and poetry-core is available. The full [contributing documentation] also +provides helpful guidance. + +## Resources + +* [Releases][PyPI Releases] +* [Official Website] +* [Documentation] +* [Issue Tracker] +* [Discord] + + [PyPI]: https://pypi.org/project/poetry/ + [PyPI Releases]: https://pypi.org/project/poetry/#history + [Official Website]: https://python-poetry.org + [Documentation]: https://python-poetry.org/docs/ + [Issue Tracker]: https://github.com/python-poetry/poetry/issues + [Suggested Issues]: https://github.com/python-poetry/poetry/contribute + [Contributing Documentation]: https://python-poetry.org/docs/contributing + [Discord]: https://discord.com/invite/awxPgve + [install.python-poetry.org]: https://install.python-poetry.org + [Installation Documentation]: https://python-poetry.org/docs/#installation + +## Related Projects + +* [poetry-core](https://github.com/python-poetry/poetry-core): PEP 517 build-system for Poetry projects, and +dependency-free core functionality of the Poetry frontend +* [poetry-plugin-export](https://github.com/python-poetry/poetry-plugin-export): Export Poetry projects/lock files to +foreign formats like requirements.txt +* [poetry-plugin-bundle](https://github.com/python-poetry/poetry-plugin-bundle): Install Poetry projects/lock files to +external formats like virtual environments +* [install.python-poetry.org](https://github.com/python-poetry/install.python-poetry.org): The official Poetry +installation script +* [website](https://github.com/python-poetry/website): The official Poetry website and blog +} + +%description %_description + +%package -n %{python_name} +Summary: %{summary} + +%description -n %{python_name} %_description + +%prep +%autosetup -p1 -n %{pypi_name}-%{version} + + +%generate_buildrequires +# Keep only those extras which you actually want to package or use during tests +%pyproject_buildrequires + + +%build +%pyproject_wheel + + +%install +%pyproject_install +# For official Fedora packages, including files with '*' +auto is not allowed +# Replace it with a list of relevant Python modules/globs and list extra files in %%files +%pyproject_save_files '*' +auto + +%files -n %{python_name} -f %{pyproject_files} + +%changelog +%autochangelog + diff --git a/test/examples/poetry-mageia.spec b/test/examples/poetry-mageia.spec new file mode 100644 index 0000000..a952300 --- /dev/null +++ b/test/examples/poetry-mageia.spec @@ -0,0 +1,32 @@ +__USER__%define mod_name poetry + +Name: python-%{mod_name} +Version: 1.5.1 +Release: %mkrel 1 +Url: https://python-poetry.org/ +Summary: Python dependency management and packaging made easy. +License: MIT +Group: Development/Python +Source: https://files.pythonhosted.org/packages/source/p/poetry/poetry-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-buildroot +BuildRequires: python-devel + +%description +Python dependency management and packaging made easy. + + +%prep +%setup -q -n %{mod_name}-%{version} + +%build +%{__python} setup.py build + +%install +%{__python} setup.py install --prefix=%{_prefix} --root=%{buildroot} + +%clean +rm -rf %{buildroot} + +%files -f +%defattr(-,root,root) +%{python_sitelib}/* diff --git a/test/examples/poetry-opensuse-augmented.spec b/test/examples/poetry-opensuse-augmented.spec index 8f12f0b..4586145 100644 --- a/test/examples/poetry-opensuse-augmented.spec +++ b/test/examples/poetry-opensuse-augmented.spec @@ -1,7 +1,7 @@ -# +__USER__# # spec file for package python-poetry # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) __YEAR__ SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -25,69 +25,7 @@ URL: https://python-poetry.org/ Source: https://files.pythonhosted.org/packages/source/p/poetry/poetry-%{version}.tar.gz BuildRequires: python-rpm-macros BuildRequires: %{python_module pip} -BuildRequires: %{python_module poetry-core >= 1.5.0} -# SECTION test requirements -BuildRequires: %{python_module build >= 0.10.0} -BuildRequires: %{python_module cachecontrol >= 0.12.9} -BuildRequires: %{python_module cleo >= 2.0.0} -BuildRequires: %{python_module crashtest >= 0.4.1} -BuildRequires: %{python_module dulwich >= 0.21.2} -BuildRequires: %{python_module filelock >= 3.8.0} -BuildRequires: %{python_module html5lib >= 1.0} -BuildRequires: %{python_module installer >= 0.7.0} -BuildRequires: %{python_module jsonschema >= 4.10.0} -BuildRequires: %{python_module keyring >= 23.9.0} -BuildRequires: %{python_module lockfile >= 0.12.2} -BuildRequires: %{python_module packaging >= 20.4} -BuildRequires: %{python_module pexpect >= 4.7.0} -BuildRequires: %{python_module pkginfo >= 1.9.4} -BuildRequires: %{python_module platformdirs >= 3.0.0} -BuildRequires: %{python_module poetry-core == 1.6.1} -BuildRequires: %{python_module poetry-plugin-export >= 1.4.0} -BuildRequires: %{python_module pyproject-hooks >= 1.0.0} -BuildRequires: %{python_module requests >= 2.18} -BuildRequires: %{python_module requests-toolbelt >= 0.9.1} -BuildRequires: %{python_module shellingham >= 1.5} -BuildRequires: %{python_module tomlkit >= 0.11.4} -BuildRequires: %{python_module trove-classifiers >= 2022.5.19} -BuildRequires: %{python_module urllib3 >= 1.26.0} -BuildRequires: %{python_module virtualenv >= 20.22.0} -BuildRequires: %{python_module cachy} -BuildRequires: %{python_module deepdiff} -BuildRequires: %{python_module httpretty} -BuildRequires: %{python_module pytest} -BuildRequires: %{python_module pytest-cov} -BuildRequires: %{python_module pytest-mock} -BuildRequires: %{python_module pytest-randomly} -BuildRequires: %{python_module pytest-xdist} -BuildRequires: %{python_module zipp} -# /SECTION BuildRequires: fdupes -Requires: python-build >= 0.10.0 -Requires: python-cachecontrol >= 0.12.9 -Requires: python-cleo >= 2.0.0 -Requires: python-crashtest >= 0.4.1 -Requires: python-dulwich >= 0.21.2 -Requires: python-filelock >= 3.8.0 -Requires: python-html5lib >= 1.0 -Requires: python-installer >= 0.7.0 -Requires: python-jsonschema >= 4.10.0 -Requires: python-keyring >= 23.9.0 -Requires: python-lockfile >= 0.12.2 -Requires: python-packaging >= 20.4 -Requires: python-pexpect >= 4.7.0 -Requires: python-pkginfo >= 1.9.4 -Requires: python-platformdirs >= 3.0.0 -Requires: python-poetry-core == 1.6.1 -Requires: python-poetry-plugin-export >= 1.4.0 -Requires: python-pyproject-hooks >= 1.0.0 -Requires: python-requests >= 2.18 -Requires: python-requests-toolbelt >= 0.9.1 -Requires: python-shellingham >= 1.5 -Requires: python-tomlkit >= 0.11.4 -Requires: python-trove-classifiers >= 2022.5.19 -Requires: python-urllib3 >= 1.26.0 -Requires: python-virtualenv >= 20.22.0 BuildArch: noarch %python_subpackages @@ -208,7 +146,6 @@ installation script * [website](https://github.com/python-poetry/website): The official Poetry website and blog - %prep %autosetup -p1 -n poetry-%{version} @@ -217,22 +154,9 @@ installation script %install %pyproject_install -%python_clone -a %{buildroot}%{_bindir}/poetry %python_expand %fdupes %{buildroot}%{$python_sitelib} -%check -CHOOSE: %pytest OR %pyunittest -v OR CUSTOM - -%post -%python_install_alternative poetry - -%postun -%python_uninstall_alternative poetry - %files %{python_files} -%doc README.md -%license LICENSE -%python_alternative %{_bindir}/poetry %{python_sitelib}/poetry %{python_sitelib}/poetry-%{version}.dist-info diff --git a/test/examples/poetry-opensuse-legacy.spec b/test/examples/poetry-opensuse-legacy.spec new file mode 100644 index 0000000..6e69341 --- /dev/null +++ b/test/examples/poetry-opensuse-legacy.spec @@ -0,0 +1,159 @@ +__USER__# +# spec file for package python-poetry +# +# Copyright (c) __YEAR__ SUSE LINUX GmbH, Nuernberg, Germany. +# +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + +# Please submit bugfixes or comments via https://bugs.opensuse.org/ + + +Name: python-poetry +Version: 1.5.1 +Release: 0 +Summary: Python dependency management and packaging made easy +License: MIT +URL: https://python-poetry.org/ +Source: https://files.pythonhosted.org/packages/source/p/poetry/poetry-%{version}.tar.gz +BuildRequires: python-setuptools +BuildRoot: %{_tmppath}/%{name}-%{version}-build +BuildArch: noarch + +%description +# Poetry: Python packaging and dependency management made easy + +[![Stable Version](https://img.shields.io/pypi/v/poetry?label=stable)][PyPI Releases] +[![Pre-release Version](https://img.shields.io/github/v/release/python-poetry/poetry?label=pre-release&include_prereleases&sort=semver)][PyPI Releases] +[![Python Versions](https://img.shields.io/pypi/pyversions/poetry)][PyPI] +[![Download Stats](https://img.shields.io/pypi/dm/poetry)](https://pypistats.org/packages/poetry) +[![Discord](https://img.shields.io/discord/487711540787675139?logo=discord)][Discord] + +Poetry helps you declare, manage and install dependencies of Python projects, +ensuring you have the right stack everywhere. + +![Poetry Install](https://raw.githubusercontent.com/python-poetry/poetry/master/assets/install.gif) + +Poetry replaces `setup.py`, `requirements.txt`, `setup.cfg`, `MANIFEST.in` and `Pipfile` with a simple `pyproject.toml` +based project format. + +```toml +[tool.poetry] +name = "my-package" +version = "0.1.0" +description = "The description of the package" + +license = "MIT" + +authors = [ + "Sébastien Eustace " +] + +repository = "https://github.com/python-poetry/poetry" +homepage = "https://python-poetry.org" + +# README file(s) are used as the package description +readme = ["README.md", "LICENSE"] + +# Keywords (translated to tags on the package index) +keywords = ["packaging", "poetry"] + +[tool.poetry.dependencies] +# Compatible Python versions +python = ">=3.8" +# Standard dependency with semver constraints +aiohttp = "^3.8.1" +# Dependency with extras +requests = { version = "^2.28", extras = ["security"] } +# Version-specific dependencies with prereleases allowed +tomli = { version = "^2.0.1", python = "<3.11", allow-prereleases = true } +# Git dependencies +cleo = { git = "https://github.com/python-poetry/cleo.git", branch = "master" } +# Optional dependencies (installed by extras) +pendulum = { version = "^2.1.2", optional = true } + +# Dependency groups are supported for organizing your dependencies +[tool.poetry.group.dev.dependencies] +pytest = "^7.1.2" +pytest-cov = "^3.0" + +# ...and can be installed only when explicitly requested +[tool.poetry.group.docs] +optional = true +[tool.poetry.group.docs.dependencies] +Sphinx = "^5.1.1" + +# Python-style entrypoints and scripts are easily expressed +[tool.poetry.scripts] +my-script = "my_package:main" +``` + +## Installation + +Poetry supports multiple installation methods, including a simple script found at [install.python-poetry.org]. For full +installation instructions, including advanced usage of the script, alternate install methods, and CI best practices, see +the full [installation documentation]. + +## Documentation + +[Documentation] for the current version of Poetry (as well as the development branch and recently out of support +versions) is available from the [official website]. + +## Contribute + +Poetry is a large, complex project always in need of contributors. For those new to the project, a list of +[suggested issues] to work on in Poetry and poetry-core is available. The full [contributing documentation] also +provides helpful guidance. + +## Resources + +* [Releases][PyPI Releases] +* [Official Website] +* [Documentation] +* [Issue Tracker] +* [Discord] + + [PyPI]: https://pypi.org/project/poetry/ + [PyPI Releases]: https://pypi.org/project/poetry/#history + [Official Website]: https://python-poetry.org + [Documentation]: https://python-poetry.org/docs/ + [Issue Tracker]: https://github.com/python-poetry/poetry/issues + [Suggested Issues]: https://github.com/python-poetry/poetry/contribute + [Contributing Documentation]: https://python-poetry.org/docs/contributing + [Discord]: https://discord.com/invite/awxPgve + [install.python-poetry.org]: https://install.python-poetry.org + [Installation Documentation]: https://python-poetry.org/docs/#installation + +## Related Projects + +* [poetry-core](https://github.com/python-poetry/poetry-core): PEP 517 build-system for Poetry projects, and +dependency-free core functionality of the Poetry frontend +* [poetry-plugin-export](https://github.com/python-poetry/poetry-plugin-export): Export Poetry projects/lock files to +foreign formats like requirements.txt +* [poetry-plugin-bundle](https://github.com/python-poetry/poetry-plugin-bundle): Install Poetry projects/lock files to +external formats like virtual environments +* [install.python-poetry.org](https://github.com/python-poetry/install.python-poetry.org): The official Poetry +installation script +* [website](https://github.com/python-poetry/website): The official Poetry website and blog + + +%prep +%autosetup -p1 -n poetry-%{version} + +%build +python setup.py build + +%install +python setup.py install --prefix=%{_prefix} --root=%{buildroot} + +%files +%defattr(-,root,root,-) +%{python_sitelib}/* + +%changelog diff --git a/test/examples/poetry-opensuse.dsc b/test/examples/poetry-opensuse.dsc new file mode 100644 index 0000000..68e4287 --- /dev/null +++ b/test/examples/poetry-opensuse.dsc @@ -0,0 +1,8 @@ +Format: 1.0 +Source: poetry +Version: 1.5.1 +Binary: python-poetry +Maintainer: __USER__ +Architecture: any +Standards-Version: 3.7.1 +Build-Depends: debhelper (>= 4.0.0), python-dev diff --git a/test/examples/poetry-opensuse.spec b/test/examples/poetry-opensuse.spec new file mode 100644 index 0000000..4586145 --- /dev/null +++ b/test/examples/poetry-opensuse.spec @@ -0,0 +1,163 @@ +__USER__# +# spec file for package python-poetry +# +# Copyright (c) __YEAR__ SUSE LLC +# +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + +# Please submit bugfixes or comments via https://bugs.opensuse.org/ +# + + +Name: python-poetry +Version: 1.5.1 +Release: 0 +Summary: Python dependency management and packaging made easy +License: MIT +URL: https://python-poetry.org/ +Source: https://files.pythonhosted.org/packages/source/p/poetry/poetry-%{version}.tar.gz +BuildRequires: python-rpm-macros +BuildRequires: %{python_module pip} +BuildRequires: fdupes +BuildArch: noarch +%python_subpackages + +%description +# Poetry: Python packaging and dependency management made easy + +[![Stable Version](https://img.shields.io/pypi/v/poetry?label=stable)][PyPI Releases] +[![Pre-release Version](https://img.shields.io/github/v/release/python-poetry/poetry?label=pre-release&include_prereleases&sort=semver)][PyPI Releases] +[![Python Versions](https://img.shields.io/pypi/pyversions/poetry)][PyPI] +[![Download Stats](https://img.shields.io/pypi/dm/poetry)](https://pypistats.org/packages/poetry) +[![Discord](https://img.shields.io/discord/487711540787675139?logo=discord)][Discord] + +Poetry helps you declare, manage and install dependencies of Python projects, +ensuring you have the right stack everywhere. + +![Poetry Install](https://raw.githubusercontent.com/python-poetry/poetry/master/assets/install.gif) + +Poetry replaces `setup.py`, `requirements.txt`, `setup.cfg`, `MANIFEST.in` and `Pipfile` with a simple `pyproject.toml` +based project format. + +```toml +[tool.poetry] +name = "my-package" +version = "0.1.0" +description = "The description of the package" + +license = "MIT" + +authors = [ + "Sébastien Eustace " +] + +repository = "https://github.com/python-poetry/poetry" +homepage = "https://python-poetry.org" + +# README file(s) are used as the package description +readme = ["README.md", "LICENSE"] + +# Keywords (translated to tags on the package index) +keywords = ["packaging", "poetry"] + +[tool.poetry.dependencies] +# Compatible Python versions +python = ">=3.8" +# Standard dependency with semver constraints +aiohttp = "^3.8.1" +# Dependency with extras +requests = { version = "^2.28", extras = ["security"] } +# Version-specific dependencies with prereleases allowed +tomli = { version = "^2.0.1", python = "<3.11", allow-prereleases = true } +# Git dependencies +cleo = { git = "https://github.com/python-poetry/cleo.git", branch = "master" } +# Optional dependencies (installed by extras) +pendulum = { version = "^2.1.2", optional = true } + +# Dependency groups are supported for organizing your dependencies +[tool.poetry.group.dev.dependencies] +pytest = "^7.1.2" +pytest-cov = "^3.0" + +# ...and can be installed only when explicitly requested +[tool.poetry.group.docs] +optional = true +[tool.poetry.group.docs.dependencies] +Sphinx = "^5.1.1" + +# Python-style entrypoints and scripts are easily expressed +[tool.poetry.scripts] +my-script = "my_package:main" +``` + +## Installation + +Poetry supports multiple installation methods, including a simple script found at [install.python-poetry.org]. For full +installation instructions, including advanced usage of the script, alternate install methods, and CI best practices, see +the full [installation documentation]. + +## Documentation + +[Documentation] for the current version of Poetry (as well as the development branch and recently out of support +versions) is available from the [official website]. + +## Contribute + +Poetry is a large, complex project always in need of contributors. For those new to the project, a list of +[suggested issues] to work on in Poetry and poetry-core is available. The full [contributing documentation] also +provides helpful guidance. + +## Resources + +* [Releases][PyPI Releases] +* [Official Website] +* [Documentation] +* [Issue Tracker] +* [Discord] + + [PyPI]: https://pypi.org/project/poetry/ + [PyPI Releases]: https://pypi.org/project/poetry/#history + [Official Website]: https://python-poetry.org + [Documentation]: https://python-poetry.org/docs/ + [Issue Tracker]: https://github.com/python-poetry/poetry/issues + [Suggested Issues]: https://github.com/python-poetry/poetry/contribute + [Contributing Documentation]: https://python-poetry.org/docs/contributing + [Discord]: https://discord.com/invite/awxPgve + [install.python-poetry.org]: https://install.python-poetry.org + [Installation Documentation]: https://python-poetry.org/docs/#installation + +## Related Projects + +* [poetry-core](https://github.com/python-poetry/poetry-core): PEP 517 build-system for Poetry projects, and +dependency-free core functionality of the Poetry frontend +* [poetry-plugin-export](https://github.com/python-poetry/poetry-plugin-export): Export Poetry projects/lock files to +foreign formats like requirements.txt +* [poetry-plugin-bundle](https://github.com/python-poetry/poetry-plugin-bundle): Install Poetry projects/lock files to +external formats like virtual environments +* [install.python-poetry.org](https://github.com/python-poetry/install.python-poetry.org): The official Poetry +installation script +* [website](https://github.com/python-poetry/website): The official Poetry website and blog + + +%prep +%autosetup -p1 -n poetry-%{version} + +%build +%pyproject_wheel + +%install +%pyproject_install +%python_expand %fdupes %{buildroot}%{$python_sitelib} + +%files %{python_files} +%{python_sitelib}/poetry +%{python_sitelib}/poetry-%{version}.dist-info + +%changelog diff --git a/test/examples/py2pack-fedora-augmented.spec b/test/examples/py2pack-fedora-augmented.spec index edec47d..e493944 100644 --- a/test/examples/py2pack-fedora-augmented.spec +++ b/test/examples/py2pack-fedora-augmented.spec @@ -1,8 +1,9 @@ -%define pypi_name py2pack +__USER__%define pypi_name py2pack %define python_name python3-%{pypi_name} Name: python-%{pypi_name} Version: 0.8.5 Release: %autorelease +# Fill in the actual package summary to submit package to Fedora Summary: Generate distribution packages from PyPI # Check if the automatically generated License and its spelling is correct for Fedora @@ -12,32 +13,146 @@ URL: http://github.com/openSUSE/py2pack Source: https://files.pythonhosted.org/packages/source/p/py2pack/py2pack-%{version}.tar.gz BuildRequires: pyproject-rpm-macros -BuildRequires: python-devel -%if %{undefined python_module} -%define python_module() python3dist(%1) -%endif -BuildRequires: %{python_module pbr >= 1.8} -BuildRequires: %{python_module pip} -BuildRequires: %{python_module setuptools} -BuildRequires: %{python_module wheel} -# SECTION test requirements -%if %{with test} -BuildRequires: %{python_module Jinja2} -BuildRequires: %{python_module metaextract} -BuildRequires: %{python_module six} -%endif -# /SECTION +BuildRequires: python3-devel BuildRequires: fdupes -Requires: %{python_module Jinja2} -Requires: %{python_module metaextract} -Requires: %{python_module setuptools} -Requires: %{python_module six} -Suggests: %{python_module typing} BuildArch: noarch # Fill in the actual package description to submit package to Fedora %global _description %{expand: -Generate distribution packages from PyPI} +Py2pack: Generate distribution packages from PyPI +================================================= + +.. image:: https://travis-ci.org/openSUSE/py2pack.png?branch=master + :target: https://travis-ci.org/openSUSE/py2pack + + +This script allows to generate RPM spec or DEB dsc files from Python modules. +It allows to list Python modules or search for them on the Python Package Index +(PyPI). Conveniently, it can fetch tarballs and changelogs making it an +universal tool to package Python modules. + + +Installation +------------ + +To install py2pack from the `Python Package Index`_, simply: + +.. code-block:: bash + + $ pip install py2pack + +Or, if you absolutely must: + +.. code-block:: bash + + $ easy_install py2pack + +But, you really shouldn't do that. Lastly, you can check your distro of choice +if they provide packages. For openSUSE, you can find packages in the `Open +Build Service`_ for all releases. If you happen to use openSUSE:Factory (the +rolling release / development version), simply: + +.. code-block:: bash + + $ sudo zypper install python-py2pack + + +Usage +----- + +Lets suppose you want to package zope.interface_ and you don't know how it is named +exactly. First of all, you can search for it and download the source tarball if +you found the correct module: + +.. code-block:: bash + + $ py2pack search zope.interface + searching for module zope.interface... + found zope.interface-3.6.1 + $ py2pack fetch zope.interface + downloading package zope.interface-3.6.1... + from http://pypi.python.org/packages/source/z/zope.interface/zope.interface-3.6.1.tar.gz + + +As a next step you may want to generate a package recipe for your distribution. +For RPM_-based distributions (let's use openSUSE_ as an example), you want to +generate a spec file (named 'python-zope.interface.spec'): + +.. code-block:: bash + + $ py2pack generate zope.interface -t opensuse.spec -f python-zope.interface.spec + +The source tarball and the package recipe is all you need to generate the RPM_ +(or DEB_) file. +This final step may depend on which distribution you use. Again, +for openSUSE_ (and by using the `Open Build Service`_), the complete recipe is: + +.. code-block:: bash + + $ osc mkpac python-zope.interface + $ cd python-zope.interface + $ py2pack fetch zope.interface + $ py2pack generate zope.interface -f python-zope.interface.spec + $ osc build + ... + +Depending on the module, you may have to adapt the resulting spec file slightly. +To get further help about py2pack usage, issue the following command: + +.. code-block:: bash + + $ py2pack help + + +Hacking and contributing +------------------------ + +You can test py2pack from your git checkout by executing the py2pack module: + +.. code-block:: bash + + $ python -m py2pack + +Fork `the repository`_ on Github to start making your changes to the **master** +branch (or branch off of it). Don't forget to write a test for fixed issues or +implemented features whenever appropriate. You can invoke the testsuite from +the repository root directory via `tox`_: + +.. code-block:: bash + + $ tox + +To run a single test class via `tox`_, use i.e.: + +.. code-block:: bash + + $ tox -epy27 test.test_py2pack:Py2packTestCase + + +You can also run `pytest`_ directly: + +.. code-block:: bash + + $ pytest + +It assumes you have the test dependencies installed (available on PYTHONPATH) +on your system. + +:copyright: (c) 2013 Sascha Peilicke. +:license: Apache-2.0, see LICENSE for more details. + + +.. _argparse: http://pypi.python.org/pypi/argparse +.. _Jinja2: http://pypi.python.org/pypi/Jinja2 +.. _zope.interface: http://pypi.python.org/pypi/zope.interface/ +.. _openSUSE: http://www.opensuse.org/en/ +.. _RPM: http://en.wikipedia.org/wiki/RPM_Package_Manager +.. _DEB: http://en.wikipedia.org/wiki/Deb_(file_format) +.. _`Python Package Index`: https://pypi.org/ +.. _`Open Build Service`: https://build.opensuse.org/package/show/devel:languages:python/python-py2pack +.. _`the repository`: https://github.com/openSUSE/py2pack +.. _`pytest`: https://github.com/pytest-dev/pytest +.. _`tox`: http://testrun.org/tox} %description %_description @@ -46,26 +161,24 @@ Summary: %{summary} %description -n %{python_name} %_description - %prep %autosetup -p1 -n %{pypi_name}-%{version} + +%generate_buildrequires +# Keep only those extras which you actually want to package or use during tests +%pyproject_buildrequires + + %build %pyproject_wheel %install %pyproject_install -# -#%python_clone -a %{buildroot}%{_bindir}/py2pack -# # For official Fedora packages, including files with '*' +auto is not allowed # Replace it with a list of relevant Python modules/globs and list extra files in %%files %pyproject_save_files '*' +auto -%if %{with test} -%check -%pytest -%endif %files -n %{python_name} -f %{pyproject_files} diff --git a/test/examples/py2pack-mageia.spec b/test/examples/py2pack-mageia.spec index 979972e..dbe4d7e 100644 --- a/test/examples/py2pack-mageia.spec +++ b/test/examples/py2pack-mageia.spec @@ -1,4 +1,4 @@ -%define mod_name py2pack +__USER__%define mod_name py2pack Name: python-%{mod_name} Version: 0.8.5 diff --git a/test/examples/py2pack-opensuse-augmented.spec b/test/examples/py2pack-opensuse-augmented.spec index e876c16..9bb42e4 100644 --- a/test/examples/py2pack-opensuse-augmented.spec +++ b/test/examples/py2pack-opensuse-augmented.spec @@ -1,4 +1,4 @@ -# +__USER__# # spec file for package python-py2pack # # Copyright (c) __YEAR__ SUSE LLC @@ -24,26 +24,146 @@ License: Apache-2.0 URL: http://github.com/openSUSE/py2pack Source: https://files.pythonhosted.org/packages/source/p/py2pack/py2pack-%{version}.tar.gz BuildRequires: python-rpm-macros -BuildRequires: %{python_module pbr >= 1.8} BuildRequires: %{python_module pip} -BuildRequires: %{python_module setuptools} -BuildRequires: %{python_module wheel} -# SECTION test requirements -BuildRequires: %{python_module Jinja2} -BuildRequires: %{python_module metaextract} -BuildRequires: %{python_module six} -# /SECTION BuildRequires: fdupes -Requires: python-Jinja2 -Requires: python-metaextract -Requires: python-setuptools -Requires: python-six -Suggests: python-typing BuildArch: noarch %python_subpackages %description -Generate distribution packages from PyPI +Py2pack: Generate distribution packages from PyPI +================================================= + +.. image:: https://travis-ci.org/openSUSE/py2pack.png?branch=master + :target: https://travis-ci.org/openSUSE/py2pack + + +This script allows to generate RPM spec or DEB dsc files from Python modules. +It allows to list Python modules or search for them on the Python Package Index +(PyPI). Conveniently, it can fetch tarballs and changelogs making it an +universal tool to package Python modules. + + +Installation +------------ + +To install py2pack from the `Python Package Index`_, simply: + +.. code-block:: bash + + $ pip install py2pack + +Or, if you absolutely must: + +.. code-block:: bash + + $ easy_install py2pack + +But, you really shouldn't do that. Lastly, you can check your distro of choice +if they provide packages. For openSUSE, you can find packages in the `Open +Build Service`_ for all releases. If you happen to use openSUSE:Factory (the +rolling release / development version), simply: + +.. code-block:: bash + + $ sudo zypper install python-py2pack + + +Usage +----- + +Lets suppose you want to package zope.interface_ and you don't know how it is named +exactly. First of all, you can search for it and download the source tarball if +you found the correct module: + +.. code-block:: bash + + $ py2pack search zope.interface + searching for module zope.interface... + found zope.interface-3.6.1 + $ py2pack fetch zope.interface + downloading package zope.interface-3.6.1... + from http://pypi.python.org/packages/source/z/zope.interface/zope.interface-3.6.1.tar.gz + + +As a next step you may want to generate a package recipe for your distribution. +For RPM_-based distributions (let's use openSUSE_ as an example), you want to +generate a spec file (named 'python-zope.interface.spec'): + +.. code-block:: bash + + $ py2pack generate zope.interface -t opensuse.spec -f python-zope.interface.spec + +The source tarball and the package recipe is all you need to generate the RPM_ +(or DEB_) file. +This final step may depend on which distribution you use. Again, +for openSUSE_ (and by using the `Open Build Service`_), the complete recipe is: + +.. code-block:: bash + + $ osc mkpac python-zope.interface + $ cd python-zope.interface + $ py2pack fetch zope.interface + $ py2pack generate zope.interface -f python-zope.interface.spec + $ osc build + ... + +Depending on the module, you may have to adapt the resulting spec file slightly. +To get further help about py2pack usage, issue the following command: + +.. code-block:: bash + + $ py2pack help + + +Hacking and contributing +------------------------ + +You can test py2pack from your git checkout by executing the py2pack module: + +.. code-block:: bash + + $ python -m py2pack + +Fork `the repository`_ on Github to start making your changes to the **master** +branch (or branch off of it). Don't forget to write a test for fixed issues or +implemented features whenever appropriate. You can invoke the testsuite from +the repository root directory via `tox`_: + +.. code-block:: bash + + $ tox + +To run a single test class via `tox`_, use i.e.: + +.. code-block:: bash + + $ tox -epy27 test.test_py2pack:Py2packTestCase + + +You can also run `pytest`_ directly: + +.. code-block:: bash + + $ pytest + +It assumes you have the test dependencies installed (available on PYTHONPATH) +on your system. + +:copyright: (c) 2013 Sascha Peilicke. +:license: Apache-2.0, see LICENSE for more details. + + +.. _argparse: http://pypi.python.org/pypi/argparse +.. _Jinja2: http://pypi.python.org/pypi/Jinja2 +.. _zope.interface: http://pypi.python.org/pypi/zope.interface/ +.. _openSUSE: http://www.opensuse.org/en/ +.. _RPM: http://en.wikipedia.org/wiki/RPM_Package_Manager +.. _DEB: http://en.wikipedia.org/wiki/Deb_(file_format) +.. _`Python Package Index`: https://pypi.org/ +.. _`Open Build Service`: https://build.opensuse.org/package/show/devel:languages:python/python-py2pack +.. _`the repository`: https://github.com/openSUSE/py2pack +.. _`pytest`: https://github.com/pytest-dev/pytest +.. _`tox`: http://testrun.org/tox %prep %autosetup -p1 -n py2pack-%{version} @@ -53,22 +173,9 @@ Generate distribution packages from PyPI %install %pyproject_install -%python_clone -a %{buildroot}%{_bindir}/py2pack %python_expand %fdupes %{buildroot}%{$python_sitelib} -%check -CHOOSE: %pytest OR %pyunittest -v OR CUSTOM - -%post -%python_install_alternative py2pack - -%postun -%python_uninstall_alternative py2pack - %files %{python_files} -%doc AUTHORS ChangeLog README.rst -%license LICENSE -%python_alternative %{_bindir}/py2pack %{python_sitelib}/py2pack %{python_sitelib}/py2pack-%{version}.dist-info diff --git a/test/examples/py2pack-opensuse-legacy.spec b/test/examples/py2pack-opensuse-legacy.spec index 1db7df9..0e9576d 100644 --- a/test/examples/py2pack-opensuse-legacy.spec +++ b/test/examples/py2pack-opensuse-legacy.spec @@ -1,4 +1,4 @@ -# +__USER__# # spec file for package python-py2pack # # Copyright (c) __YEAR__ SUSE LINUX GmbH, Nuernberg, Germany. diff --git a/test/examples/py2pack-opensuse.spec b/test/examples/py2pack-opensuse.spec index e40af6b..9bb42e4 100644 --- a/test/examples/py2pack-opensuse.spec +++ b/test/examples/py2pack-opensuse.spec @@ -1,4 +1,4 @@ -# +__USER__# # spec file for package python-py2pack # # Copyright (c) __YEAR__ SUSE LLC diff --git a/test/examples/sampleproject-fedora-augmented.spec b/test/examples/sampleproject-fedora-augmented.spec index 5ccce9d..6194580 100644 --- a/test/examples/sampleproject-fedora-augmented.spec +++ b/test/examples/sampleproject-fedora-augmented.spec @@ -1,57 +1,64 @@ -%define pypi_name sampleproject +__USER__%define pypi_name sampleproject %define python_name python3-%{pypi_name} Name: python-%{pypi_name} Version: 3.0.0 Release: %autorelease +# Fill in the actual package summary to submit package to Fedora Summary: A sample Python project # Check if the automatically generated License and its spelling is correct for Fedora # https://docs.fedoraproject.org/en-US/packaging-guidelines/LicensingGuidelines/ -License: Copyright (c) 2016 The Python Packaging Authority (PyPA) - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - (FIXME:No SPDX) -URL: https://github.com/pypa/sampleproject +License: Copyright (c) 2016 The Python Packaging Authority (PyPA) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (FIXME:No SPDX) +URL: Source: https://files.pythonhosted.org/packages/source/s/sampleproject/sampleproject-%{version}.tar.gz BuildRequires: pyproject-rpm-macros -BuildRequires: python-devel -%if %{undefined python_module} -%define python_module() python3dist(%1) -%endif -BuildRequires: %{python_module pip} -BuildRequires: %{python_module setuptools >= 43.0.0} -BuildRequires: %{python_module wheel} -# SECTION test requirements -%if %{with test} -BuildRequires: %{python_module peppercorn} -BuildRequires: %{python_module coverage} -%endif -# /SECTION +BuildRequires: python3-devel BuildRequires: fdupes -Requires: %{python_module peppercorn} -Suggests: %{python_module check-manifest} BuildArch: noarch # Fill in the actual package description to submit package to Fedora %global _description %{expand: -A sample Python project} +# A sample Python project + +![Python Logo](https://www.python.org/static/community_logos/python-logo.png "Sample inline image") + +A sample project that exists as an aid to the [Python Packaging User +Guide][packaging guide]'s [Tutorial on Packaging and Distributing +Projects][distribution tutorial]. + +This project does not aim to cover best practices for Python project +development as a whole. For example, it does not provide guidance or tool +recommendations for version control, documentation, or testing. + +[The source for this project is available here][src]. + +The metadata for a Python project is defined in the `pyproject.toml` file, +an example of which is included in this project. You should edit this file +accordingly to adapt this sample project to your needs. + +---- + +This is the README file for the project. + +The file should use UTF-8 encoding and can be written using +[reStructuredText][rst] or [markdown][md use] with the appropriate [key set][md +use]. It will be used to generate the project webpage on PyPI and will be +displayed as the project homepage on common code-hosting services, and should be +written for that purpose. + +Typical contents for this file would include an overview of the project, basic +usage examples, etc. Generally, including the project changelog in here is not a +good idea, although a simple “What's New” section for the most recent version +may be appropriate. + +[packaging guide]: https://packaging.python.org +[distribution tutorial]: https://packaging.python.org/tutorials/packaging-projects/ +[src]: https://github.com/pypa/sampleproject +[rst]: http://docutils.sourceforge.net/rst.html +[md]: https://tools.ietf.org/html/rfc7764#section-3.5 "CommonMark variant" +[md use]: https://packaging.python.org/specifications/core-metadata/#description-content-type-optional +} %description %_description @@ -59,20 +66,25 @@ A sample Python project} Summary: %{summary} %description -n %{python_name} %_description - +# For official Fedora packages, review which extras should be actually packaged +# See: https://docs.fedoraproject.org/en-US/packaging-guidelines/Python/#Extras +%pyproject_extras_subpkg -n %{python_name} dev,test %prep %autosetup -p1 -n %{pypi_name}-%{version} + +%generate_buildrequires +# Keep only those extras which you actually want to package or use during tests +%pyproject_buildrequires -x dev,test + + %build %pyproject_wheel %install %pyproject_install -# -#%python_clone -a %{buildroot}%{_bindir}/sample -# # For official Fedora packages, including files with '*' +auto is not allowed # Replace it with a list of relevant Python modules/globs and list extra files in %%files %pyproject_save_files '*' +auto diff --git a/test/examples/sampleproject-mageia.spec b/test/examples/sampleproject-mageia.spec index 4f0f033..3bea340 100644 --- a/test/examples/sampleproject-mageia.spec +++ b/test/examples/sampleproject-mageia.spec @@ -1,4 +1,4 @@ -%define mod_name sampleproject +__USER__%define mod_name sampleproject Name: python-%{mod_name} Version: 3.0.0 diff --git a/test/examples/sampleproject-opensuse-augmented.spec b/test/examples/sampleproject-opensuse-augmented.spec index 1b54385..83ac1a8 100644 --- a/test/examples/sampleproject-opensuse-augmented.spec +++ b/test/examples/sampleproject-opensuse-augmented.spec @@ -1,4 +1,4 @@ -# +__USER__# # spec file for package python-sampleproject # # Copyright (c) __YEAR__ SUSE LLC @@ -20,44 +20,56 @@ Name: python-sampleproject Version: 3.0.0 Release: 0 Summary: A sample Python project -License: Copyright (c) 2016 The Python Packaging Authority (PyPA) - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - (FIXME:No SPDX) -URL: https://github.com/pypa/sampleproject +License: Copyright (c) 2016 The Python Packaging Authority (PyPA) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (FIXME:No SPDX) +URL: Source: https://files.pythonhosted.org/packages/source/s/sampleproject/sampleproject-%{version}.tar.gz BuildRequires: python-rpm-macros BuildRequires: %{python_module pip} -BuildRequires: %{python_module setuptools >= 43.0.0} -BuildRequires: %{python_module wheel} -# SECTION test requirements -BuildRequires: %{python_module peppercorn} -BuildRequires: %{python_module coverage} -# /SECTION BuildRequires: fdupes -Requires: python-peppercorn -Suggests: python-check-manifest BuildArch: noarch %python_subpackages %description -A sample Python project +# A sample Python project + +![Python Logo](https://www.python.org/static/community_logos/python-logo.png "Sample inline image") + +A sample project that exists as an aid to the [Python Packaging User +Guide][packaging guide]'s [Tutorial on Packaging and Distributing +Projects][distribution tutorial]. + +This project does not aim to cover best practices for Python project +development as a whole. For example, it does not provide guidance or tool +recommendations for version control, documentation, or testing. + +[The source for this project is available here][src]. + +The metadata for a Python project is defined in the `pyproject.toml` file, +an example of which is included in this project. You should edit this file +accordingly to adapt this sample project to your needs. + +---- + +This is the README file for the project. + +The file should use UTF-8 encoding and can be written using +[reStructuredText][rst] or [markdown][md use] with the appropriate [key set][md +use]. It will be used to generate the project webpage on PyPI and will be +displayed as the project homepage on common code-hosting services, and should be +written for that purpose. + +Typical contents for this file would include an overview of the project, basic +usage examples, etc. Generally, including the project changelog in here is not a +good idea, although a simple “What's New” section for the most recent version +may be appropriate. + +[packaging guide]: https://packaging.python.org +[distribution tutorial]: https://packaging.python.org/tutorials/packaging-projects/ +[src]: https://github.com/pypa/sampleproject +[rst]: http://docutils.sourceforge.net/rst.html +[md]: https://tools.ietf.org/html/rfc7764#section-3.5 "CommonMark variant" +[md use]: https://packaging.python.org/specifications/core-metadata/#description-content-type-optional + %prep %autosetup -p1 -n sampleproject-%{version} @@ -67,19 +79,9 @@ A sample Python project %install %pyproject_install -%python_clone -a %{buildroot}%{_bindir}/sample %python_expand %fdupes %{buildroot}%{$python_sitelib} -%post -%python_install_alternative sample - -%postun -%python_uninstall_alternative sample - %files %{python_files} -%doc README.md -%license LICENSE.txt -%python_alternative %{_bindir}/sample %{python_sitelib}/sampleproject %{python_sitelib}/sampleproject-%{version}.dist-info diff --git a/test/examples/sampleproject-opensuse-legacy.spec b/test/examples/sampleproject-opensuse-legacy.spec index 407452b..b805991 100644 --- a/test/examples/sampleproject-opensuse-legacy.spec +++ b/test/examples/sampleproject-opensuse-legacy.spec @@ -1,4 +1,4 @@ -# +__USER__# # spec file for package python-sampleproject # # Copyright (c) __YEAR__ SUSE LINUX GmbH, Nuernberg, Germany. diff --git a/test/examples/sampleproject-opensuse.spec b/test/examples/sampleproject-opensuse.spec index 84fa2cb..83ac1a8 100644 --- a/test/examples/sampleproject-opensuse.spec +++ b/test/examples/sampleproject-opensuse.spec @@ -1,4 +1,4 @@ -# +__USER__# # spec file for package python-sampleproject # # Copyright (c) __YEAR__ SUSE LLC diff --git a/test/test_template.py b/test/test_template.py index 3d3d487..7dc9315 100644 --- a/test/test_template.py +++ b/test/test_template.py @@ -67,22 +67,38 @@ def test_template(tmpdir, template, fetch_tarball, project, version): args.name = project args.version = version reference = os.path.join(compare_dir, f'{args.name}-{filename}') + no_ref = False if project == 'poetry' and sys.version_info < (3, 11): pytest.xfail("Different requirements for python < 3.11") - if not os.path.exists(reference): - pytest.xfail("No reference template available") + no_ref = True with tmpdir.as_cwd(): if fetch_tarball: py2pack.fetch(args) + p1 = project[0] + source_glob = f'https://files.pythonhosted.org/packages/source/{p1}/{project}/{project}-{version}.tar.gz' + args.source_glob = source_glob py2pack.generate(args) + with open(filename) as filehandle: + written_spec = filehandle.read() + args.localfile = source_glob + py2pack.generate(args) + with open(filename) as filehandle: + assert filehandle.read() == written_spec else: with pytest.warns(UserWarning, match="No tarball"): py2pack.generate(args) - with open(filename) as filehandle: - written_spec = filehandle.read() - with open(reference) as filehandle: - required = filehandle.read() - required = required.replace('__USER__', username, 1) - required = required.replace('__YEAR__', str(datetime.date.today().year), 1) - assert written_spec == required + with open(filename) as filehandle: + written_spec = filehandle.read() + if no_ref: + required = written_spec.replace(username, '__USER__', 1) + required = required.replace(str(datetime.date.today().year), '__YEAR__', 1) + with open(reference, 'w') as filehandle: + filehandle.write(required) + pytest.xfail("No reference template available") + else: + with open(reference) as filehandle: + required = filehandle.read() + required = required.replace('__USER__', username, 1) + required = required.replace('__YEAR__', str(datetime.date.today().year), 1) + assert written_spec == required From ba1f745f86aca33013097fb93f4562d40390f07d Mon Sep 17 00:00:00 2001 From: u Date: Mon, 13 Jan 2025 10:16:31 +0300 Subject: [PATCH 2/3] Fix username empty string, add maintainer option --- py2pack/__init__.py | 21 ++++++++++++++----- test/examples/poetry-fedora-augmented.spec | 2 +- test/examples/poetry-mageia.spec | 2 +- test/examples/poetry-opensuse-augmented.spec | 2 +- test/examples/poetry-opensuse-legacy.spec | 2 +- test/examples/poetry-opensuse.spec | 2 +- test/examples/py2pack-fedora-augmented.spec | 2 +- test/examples/py2pack-mageia.spec | 2 +- test/examples/py2pack-opensuse-augmented.spec | 2 +- test/examples/py2pack-opensuse-legacy.spec | 2 +- test/examples/py2pack-opensuse.spec | 2 +- .../sampleproject-fedora-augmented.spec | 2 +- test/examples/sampleproject-mageia.spec | 2 +- .../sampleproject-opensuse-augmented.spec | 2 +- .../sampleproject-opensuse-legacy.spec | 2 +- test/examples/sampleproject-opensuse.spec | 2 +- test/test_template.py | 17 +++++++++++++-- 17 files changed, 46 insertions(+), 22 deletions(-) diff --git a/py2pack/__init__.py b/py2pack/__init__.py index 7eaec12..a24db0e 100755 --- a/py2pack/__init__.py +++ b/py2pack/__init__.py @@ -122,17 +122,17 @@ def pypi_json_stream(json_stream): def pypi_archive_file_tar(file_path): with tarfile.open(file_path, 'r') as archive: for member in archive.getmembers(): - if os.path.basename(member.name) == 'PKG-INFO': + if basename(member.name) == 'PKG-INFO': return pypi_text_stream(StringIO(archive.extractfile(member).read().decode())) - raise KeyError('PKG-INFO not found on archive '+file_path) + raise KeyError('PKG-INFO not found on archive ' + file_path) def pypi_archive_file_zip(file_path): with zipfile.ZipFile(file_path, 'r') as archive: for member in archive.namelist(): - if os.path.basename(member) == 'PKG-INFO': + if basename(member) == 'PKG-INFO': return pypi_text_stream(StringIO(archive.open(member).read().decode())) - raise KeyError('PKG-INFO not found on archive '+file_path) + raise KeyError('PKG-INFO not found on archive ' + file_path) def pypi_archive_file(file_path): @@ -421,6 +421,16 @@ def _get_source_url(pypi_name, filename): pypi_name[0], pypi_name, filename) +def get_user_name(args=None): + try: + maintainer = args.maintainer + if maintainer is not None: + return maintainer + except Exception: + pass + return pwd.getpwuid(os.getuid()).pw_name + + def generate(args): # TODO (toabctl): remove this is a later release if args.run: @@ -437,7 +447,7 @@ def generate(args): durl = newest_download_url(args) source_url = data['source_url'] = (args.source_url or (durl and durl['url'])) data['year'] = datetime.datetime.now().year # set current year - data['user_name'] = pwd.getpwuid(os.getuid())[4] # set system user (packager) + data['user_name'] = get_user_name(args) # set system user (packager) data['summary_no_ending_dot'] = re.sub(r'(.*)\.', r'\g<1>', data.get('summary')) if data.get('summary') else "" # If package name supplied on command line differs in case from PyPI's one @@ -574,6 +584,7 @@ def main(): parser_generate.add_argument('name', help='package name') parser_generate.add_argument('version', nargs='?', help='package version (optional)') parser_generate.add_argument('--source-url', default=None, help='source url') + parser_generate.add_argument('--maintainer', default=None, help='maintainer name') parser_generate.add_argument('--source-glob', help='source glob template') parser_generate.add_argument('--local', action='store_true', help='build from local package') parser_generate.add_argument('--localfile', default='', help='path to the local PKG-INFO or json metadata') diff --git a/test/examples/poetry-fedora-augmented.spec b/test/examples/poetry-fedora-augmented.spec index c38d98d..60fa12c 100644 --- a/test/examples/poetry-fedora-augmented.spec +++ b/test/examples/poetry-fedora-augmented.spec @@ -1,4 +1,4 @@ -__USER__%define pypi_name poetry +%define pypi_name poetry %define python_name python3-%{pypi_name} Name: python-%{pypi_name} Version: 1.5.1 diff --git a/test/examples/poetry-mageia.spec b/test/examples/poetry-mageia.spec index a952300..8307c0e 100644 --- a/test/examples/poetry-mageia.spec +++ b/test/examples/poetry-mageia.spec @@ -1,4 +1,4 @@ -__USER__%define mod_name poetry +%define mod_name poetry Name: python-%{mod_name} Version: 1.5.1 diff --git a/test/examples/poetry-opensuse-augmented.spec b/test/examples/poetry-opensuse-augmented.spec index 4586145..cd593d4 100644 --- a/test/examples/poetry-opensuse-augmented.spec +++ b/test/examples/poetry-opensuse-augmented.spec @@ -1,4 +1,4 @@ -__USER__# +# # spec file for package python-poetry # # Copyright (c) __YEAR__ SUSE LLC diff --git a/test/examples/poetry-opensuse-legacy.spec b/test/examples/poetry-opensuse-legacy.spec index 6e69341..a3e91d8 100644 --- a/test/examples/poetry-opensuse-legacy.spec +++ b/test/examples/poetry-opensuse-legacy.spec @@ -1,4 +1,4 @@ -__USER__# +# # spec file for package python-poetry # # Copyright (c) __YEAR__ SUSE LINUX GmbH, Nuernberg, Germany. diff --git a/test/examples/poetry-opensuse.spec b/test/examples/poetry-opensuse.spec index 4586145..cd593d4 100644 --- a/test/examples/poetry-opensuse.spec +++ b/test/examples/poetry-opensuse.spec @@ -1,4 +1,4 @@ -__USER__# +# # spec file for package python-poetry # # Copyright (c) __YEAR__ SUSE LLC diff --git a/test/examples/py2pack-fedora-augmented.spec b/test/examples/py2pack-fedora-augmented.spec index e493944..b0362e0 100644 --- a/test/examples/py2pack-fedora-augmented.spec +++ b/test/examples/py2pack-fedora-augmented.spec @@ -1,4 +1,4 @@ -__USER__%define pypi_name py2pack +%define pypi_name py2pack %define python_name python3-%{pypi_name} Name: python-%{pypi_name} Version: 0.8.5 diff --git a/test/examples/py2pack-mageia.spec b/test/examples/py2pack-mageia.spec index dbe4d7e..979972e 100644 --- a/test/examples/py2pack-mageia.spec +++ b/test/examples/py2pack-mageia.spec @@ -1,4 +1,4 @@ -__USER__%define mod_name py2pack +%define mod_name py2pack Name: python-%{mod_name} Version: 0.8.5 diff --git a/test/examples/py2pack-opensuse-augmented.spec b/test/examples/py2pack-opensuse-augmented.spec index 9bb42e4..e40af6b 100644 --- a/test/examples/py2pack-opensuse-augmented.spec +++ b/test/examples/py2pack-opensuse-augmented.spec @@ -1,4 +1,4 @@ -__USER__# +# # spec file for package python-py2pack # # Copyright (c) __YEAR__ SUSE LLC diff --git a/test/examples/py2pack-opensuse-legacy.spec b/test/examples/py2pack-opensuse-legacy.spec index 0e9576d..1db7df9 100644 --- a/test/examples/py2pack-opensuse-legacy.spec +++ b/test/examples/py2pack-opensuse-legacy.spec @@ -1,4 +1,4 @@ -__USER__# +# # spec file for package python-py2pack # # Copyright (c) __YEAR__ SUSE LINUX GmbH, Nuernberg, Germany. diff --git a/test/examples/py2pack-opensuse.spec b/test/examples/py2pack-opensuse.spec index 9bb42e4..e40af6b 100644 --- a/test/examples/py2pack-opensuse.spec +++ b/test/examples/py2pack-opensuse.spec @@ -1,4 +1,4 @@ -__USER__# +# # spec file for package python-py2pack # # Copyright (c) __YEAR__ SUSE LLC diff --git a/test/examples/sampleproject-fedora-augmented.spec b/test/examples/sampleproject-fedora-augmented.spec index 6194580..5b2726e 100644 --- a/test/examples/sampleproject-fedora-augmented.spec +++ b/test/examples/sampleproject-fedora-augmented.spec @@ -1,4 +1,4 @@ -__USER__%define pypi_name sampleproject +%define pypi_name sampleproject %define python_name python3-%{pypi_name} Name: python-%{pypi_name} Version: 3.0.0 diff --git a/test/examples/sampleproject-mageia.spec b/test/examples/sampleproject-mageia.spec index 3bea340..4f0f033 100644 --- a/test/examples/sampleproject-mageia.spec +++ b/test/examples/sampleproject-mageia.spec @@ -1,4 +1,4 @@ -__USER__%define mod_name sampleproject +%define mod_name sampleproject Name: python-%{mod_name} Version: 3.0.0 diff --git a/test/examples/sampleproject-opensuse-augmented.spec b/test/examples/sampleproject-opensuse-augmented.spec index 83ac1a8..84fa2cb 100644 --- a/test/examples/sampleproject-opensuse-augmented.spec +++ b/test/examples/sampleproject-opensuse-augmented.spec @@ -1,4 +1,4 @@ -__USER__# +# # spec file for package python-sampleproject # # Copyright (c) __YEAR__ SUSE LLC diff --git a/test/examples/sampleproject-opensuse-legacy.spec b/test/examples/sampleproject-opensuse-legacy.spec index b805991..407452b 100644 --- a/test/examples/sampleproject-opensuse-legacy.spec +++ b/test/examples/sampleproject-opensuse-legacy.spec @@ -1,4 +1,4 @@ -__USER__# +# # spec file for package python-sampleproject # # Copyright (c) __YEAR__ SUSE LINUX GmbH, Nuernberg, Germany. diff --git a/test/examples/sampleproject-opensuse.spec b/test/examples/sampleproject-opensuse.spec index 83ac1a8..84fa2cb 100644 --- a/test/examples/sampleproject-opensuse.spec +++ b/test/examples/sampleproject-opensuse.spec @@ -1,4 +1,4 @@ -__USER__# +# # spec file for package python-sampleproject # # Copyright (c) __YEAR__ SUSE LLC diff --git a/test/test_template.py b/test/test_template.py index 7dc9315..95304f1 100644 --- a/test/test_template.py +++ b/test/test_template.py @@ -19,7 +19,6 @@ import datetime import os import os.path -import pwd import sys import pytest @@ -41,7 +40,18 @@ class Args(object): compare_dir = os.path.join(os.path.dirname(__file__), 'examples') maxDiff = None -username = pwd.getpwuid(os.getuid())[4] + +import random +import string + + +def generate_random_string(length): + letters = string.ascii_letters + string.digits + string.punctuation + random_string = ''.join(random.choice(letters) for i in range(length)) + return random_string + + +username = generate_random_string(22) @pytest.mark.parametrize('template, fetch_tarball', @@ -60,6 +70,9 @@ def test_template(tmpdir, template, fetch_tarball, project, version): args = Args() args.template = template + args.maintainer = username + assert py2pack.get_user_name() + assert py2pack.get_user_name(args) == username base, ext = template.split(".") suffix = '-augmented' if fetch_tarball else '' filename = f"{base}{suffix}.{ext}" From e324c5712bc4a005a99779a52b0903403c11a80c Mon Sep 17 00:00:00 2001 From: u Date: Wed, 15 Jan 2025 10:29:56 +0300 Subject: [PATCH 3/3] Add --local and --localfile in show subcommand, make name attribute optional if --local or --localfile field is provided --- py2pack/__init__.py | 72 ++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/py2pack/__init__.py b/py2pack/__init__.py index a24db0e..e3482ea 100755 --- a/py2pack/__init__.py +++ b/py2pack/__init__.py @@ -206,7 +206,7 @@ def fetch(args): print('downloading package {0}-{1}...'.format(args.name, args.version)) print('from {0}'.format(url['url'])) - with requests.get(url['url']) as r: + with requests.get(url['download_url']) as r: with open(url['filename'], 'wb') as f: f.write(r.content) @@ -437,7 +437,7 @@ def generate(args): warnings.warn("the '--run' switch is deprecated and a noop", DeprecationWarning) - fetch_local_data(args) + fetch_data(args) if not args.template: args.template = file_template_list()[0] if not args.filename: @@ -445,7 +445,7 @@ def generate(args): print('generating spec file for {0}...'.format(args.name)) data = args.fetched_data['info'] durl = newest_download_url(args) - source_url = data['source_url'] = (args.source_url or (durl and durl['url'])) + source_url = data['source_url'] = (args.source_url or (durl and durl['download_url'])) data['year'] = datetime.datetime.now().year # set current year data['user_name'] = get_user_name(args) # set system user (packager) data['summary_no_ending_dot'] = re.sub(r'(.*)\.', r'\g<1>', data.get('summary')) if data.get('summary') else "" @@ -497,8 +497,8 @@ def generate(args): outfile.close() -def fetch_local_data(args): - localfile = args.localfile +def fetch_data(args): + localfile = args.localfile or '' local = args.local if not localfile and local: @@ -512,21 +512,19 @@ def fetch_local_data(args): except json.decoder.JSONDecodeError: data = pypi_text_file(localfile) args.fetched_data = data - args.version = args.fetched_data['info']['version'] + data_info = data['info'] + args.version = data_info['version'] + args.name = data_info['name'] fix_data(data) else: - fetch_data(args) - - -def fetch_data(args): - data = args.fetched_data = pypi_json(args.name, args.version) - urls = data.get('urls', []) - if len(urls) == 0: - print(f"unable to find a suitable release for {args.name}!") - sys.exit(1) - else: - args.version = args.fetched_data['info']['version'] # return current release number - fix_data(data) + data = args.fetched_data = pypi_json(args.name, args.version) + urls = data.get('urls', []) + if len(urls) == 0: + print(f"unable to find a suitable release for {args.name}!") + sys.exit(1) + else: + args.version = data['info']['version'] # return current release number + fix_data(data) def newest_download_url(args): @@ -537,14 +535,14 @@ def newest_download_url(args): if not hasattr(args, "fetched_data"): return {} for release in args.fetched_data['urls']: # Check download URLs in releases - if release['packagetype'] == 'sdist': # Found the source URL we care for - release['url'] = _get_source_url(args.name, release['filename']) + if release.get('packagetype') == 'sdist' and not release.get('download_url'): # Found the source URL we care for + release['download_url'] = _get_source_url(args.name, release['filename']) return release # No PyPI tarball release, let's see if an upstream download URL is provided: data = args.fetched_data['info'] - if 'download_url' in data and data['download_url']: - url = data['download_url'] - return {'url': url, + url = data.get('download_url') + if url: + return {'download_url': url, 'filename': os.path.basename(url)} return {} # We're all out of bubblegum @@ -556,6 +554,17 @@ def file_template_list(): return template_files +def Munch(args): + import collections + d = collections.defaultdict(lambda: None, args.__dict__) + return type('Munch', tuple(), { + "__getattr__": d.__getitem__, + "__setattr__": d.__setitem__, + "__getitem__": d.__getitem__, + "__setitem__": d.__setitem__, + "__contains__": d.__contains__})() + + def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--version', action='version', version='%(prog)s {0}'.format(py2pack_version.version)) @@ -570,8 +579,10 @@ def main(): parser_search.set_defaults(func=search) parser_show = subparsers.add_parser('show', help='show metadata for package') - parser_show.add_argument('name', help='package name') + parser_show.add_argument('name', nargs='?', help='package name') parser_show.add_argument('version', nargs='?', help='package version (optional)') + parser_show.add_argument('--local', action='store_true', help='get metadata from local package') + parser_show.add_argument('--localfile', default='', help='path to the local PKG-INFO or json metadata') parser_show.set_defaults(func=show) parser_fetch = subparsers.add_parser('fetch', help='download package source tarball from PyPI') @@ -581,12 +592,12 @@ def main(): parser_fetch.set_defaults(func=fetch) parser_generate = subparsers.add_parser('generate', help='generate RPM spec or DEB dsc file for a package') - parser_generate.add_argument('name', help='package name') + parser_generate.add_argument('name', nargs='?', help='package name') parser_generate.add_argument('version', nargs='?', help='package version (optional)') parser_generate.add_argument('--source-url', default=None, help='source url') parser_generate.add_argument('--maintainer', default=None, help='maintainer name') parser_generate.add_argument('--source-glob', help='source glob template') - parser_generate.add_argument('--local', action='store_true', help='build from local package') + parser_generate.add_argument('--local', action='store_true', help='get metadata from local package') parser_generate.add_argument('--localfile', default='', help='path to the local PKG-INFO or json metadata') parser_generate.add_argument('-t', '--template', choices=file_template_list(), default=DEFAULT_TEMPLATE, help='file template') parser_generate.add_argument('-f', '--filename', help='spec filename (optional)') @@ -599,7 +610,7 @@ def main(): parser_help = subparsers.add_parser('help', help='show this help') parser_help.set_defaults(func=lambda args: parser.print_help()) - args = parser.parse_args() + args = Munch(parser.parse_args()) # set HTTP proxy if one is provided if args.proxy: @@ -612,6 +623,13 @@ def main(): if 'func' not in args: sys.exit(parser.print_help()) + + namestr = args.func.__name__ + # Custom validation logic + if namestr in {'generate', 'show'}: + if args.localfile == '' and not args.local and not args.name: + subparsers.choices[namestr].error("The name argument is required if not --local or --localfile is provided.") + args.func(args)