diff --git a/py2pack/__init__.py b/py2pack/__init__.py index 37d39a8..5e14dfa 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"); @@ -38,8 +35,20 @@ from py2pack.utils import (_get_archive_filelist, get_pyproject_table, parse_pyproject, get_setuptools_scripts, get_metadata) - from email import parser +from packaging.requirements import Requirement +import tarfile +import zipfile + +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): @@ -51,6 +60,7 @@ def replace_string(output_string, replaces): warnings.simplefilter('always', DeprecationWarning) + SPDX_LICENSES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'spdx_license_map.json') with open(SPDX_LICENSES_FILE, 'r') as fp: SPDX_LICENSES = json.load(fp) @@ -69,7 +79,11 @@ 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) + return pypi_text_stream(pkg_info_file) + + +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('-', '_') @@ -86,7 +100,11 @@ 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) + return pypi_json_stream(json_file) + + +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 +112,22 @@ def pypi_json_file(file_path): return js +def pypi_archive_file(file_path): + try: + archive = tarfile.open(file_path) + member = archive.getmember('PKG-INFO') + if member.isfile(): + return pypi_text_stream(archive.extractfile(member)) + except tarfile.ReadError: + archive = zipfile.ZipFile(file_path) + member = archive.getinfo('PKG-INFO') + if not member.is_dir(): + return pypi_text_stream(archive.open(member)) + except Exception: + pass + return None + + 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!""" @@ -407,6 +441,9 @@ def generate(args): _normalize_license(data) + for i in ['license', 'source_url', 'home_page', 'summary_no_ending_dot', 'summary']: + data[i + '_singleline'] = str(data[i]).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 @@ -417,6 +454,22 @@ def generate(args): outfile.close() +def fix_data(data): + extra_from_req = re.compile(r'''\bextra\s+==\s+["']([^"']+)["']''') + extras = [] + data_info = data["info"] + requires_dist = data_info["requires_dist"] or [] + provides_extra = data_info["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["classifiers"] or []) + + def fetch_local_data(args): localfile = args.localfile local = args.local @@ -427,21 +480,25 @@ def fetch_local_data(args): try: data = pypi_json_file(localfile) except json.decoder.JSONDecodeError: - data = pypi_text_file(localfile) + data = pypi_archive_file(localfile) + if data is None: + 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 + args.version = data['info']['version'] # return current release number + fix_data(data) def newest_download_url(args): @@ -502,7 +559,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..121a3c6 100644 --- a/py2pack/templates/fedora.spec +++ b/py2pack/templates/fedora.spec @@ -3,57 +3,21 @@ Name: python-%{pypi_name} Version: {{ version }} Release: %autorelease -Summary: {{ summary }} +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_sinleline|replace(version, '%{version}') }} BuildRequires: pyproject-rpm-macros BuildRequires: python-devel -%if %{undefined python_module} -%define python_module() python3dist(%1) -%endif +BuildRequires: fdupes -{%- 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 %} @@ -67,24 +31,33 @@ BuildArch: noarch %package -n %{python_name} Summary: %{summary} + +{%- if provides_extra and provides_extra is not none %} +{%- set provides_extra_string = ','.join(provides_extra) %} +{%- set provides_extra_nonempty = 1 %} +{%- endif %} + + %description -n %{python_name} %_description +{%- if provides_extra_nonempty %} +%pyproject_extras_subpkg -n %{python_name} {{ provides_extra_string }} +{%- endif %} %prep %autosetup -p1 -n %{pypi_name}-%{version} + +%generate_buildrequires +%pyproject_buildrequires {% if provides_extra_nonempty %}-x {{ provides_extra_string }}{% 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 +66,7 @@ Summary: %{summary} {%- if testsuite or test_suite %} %if %{with test} %check +%pyproject_check_import {%- if has_ext_modules %} %pytest_arch {%- else %} @@ -101,8 +75,10 @@ Summary: %{summary} %endif {%- endif %} + %files -n %{python_name} -f %{pyproject_files} + %changelog %autochangelog 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..3f2bbd8 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_singleline, 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..635d1da 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_singleline, 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/pyproject.toml b/pyproject.toml index fe090ff..ed8010d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,7 @@ dependencies = [ "requests", "tomli; python_version < '3.11'", ] + requires-python = ">=3.6" dynamic = ['version'] @@ -45,6 +46,11 @@ dynamic = ['version'] [project.urls] homepage = "http://github.com/openSUSE/py2pack" +[project.optional-dependencies] +service = [ + "distro", +] + [project.scripts] py2pack = "py2pack:main" diff --git a/test/examples/poetry-fedora-augmented.spec b/test/examples/poetry-fedora-augmented.spec new file mode 100644 index 0000000..78fb80d --- /dev/null +++ b/test/examples/poetry-fedora-augmented.spec @@ -0,0 +1,175 @@ +%define pypi_name poetry +%define python_name python3-%{pypi_name} +Name: python-%{pypi_name} +Version: 1.5.1 +Release: %autorelease +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: None +Source: + +BuildRequires: pyproject-rpm-macros +BuildRequires: python-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 +%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 +%if %{with test} +%check +%pyproject_check_import +%pytest +%endif + + +%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..8307c0e --- /dev/null +++ b/test/examples/poetry-mageia.spec @@ -0,0 +1,32 @@ +%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..ff912cb 100644 --- a/test/examples/poetry-opensuse-augmented.spec +++ b/test/examples/poetry-opensuse-augmented.spec @@ -1,7 +1,7 @@ # # 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 @@ -21,7 +21,7 @@ Version: 1.5.1 Release: 0 Summary: Python dependency management and packaging made easy License: MIT -URL: https://python-poetry.org/ +URL: None Source: https://files.pythonhosted.org/packages/source/p/poetry/poetry-%{version}.tar.gz BuildRequires: python-rpm-macros BuildRequires: %{python_module pip} diff --git a/test/examples/poetry-opensuse-legacy.spec b/test/examples/poetry-opensuse-legacy.spec new file mode 100644 index 0000000..a3e91d8 --- /dev/null +++ b/test/examples/poetry-opensuse-legacy.spec @@ -0,0 +1,159 @@ +# +# 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..cd593d4 --- /dev/null +++ b/test/examples/poetry-opensuse.spec @@ -0,0 +1,163 @@ +# +# 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..7037923 100644 --- a/test/examples/py2pack-fedora-augmented.spec +++ b/test/examples/py2pack-fedora-augmented.spec @@ -9,30 +9,11 @@ Summary: Generate distribution packages from PyPI # https://docs.fedoraproject.org/en-US/packaging-guidelines/LicensingGuidelines/ License: Apache-2.0 URL: http://github.com/openSUSE/py2pack -Source: https://files.pythonhosted.org/packages/source/p/py2pack/py2pack-%{version}.tar.gz +Source: 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: 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 @@ -44,31 +25,36 @@ Generate distribution packages from PyPI} %package -n %{python_name} Summary: %{summary} -%description -n %{python_name} %_description +%description -n %{python_name} %_description %prep %autosetup -p1 -n %{pypi_name}-%{version} + +%generate_buildrequires +%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 +%pyproject_check_import %pytest %endif + %files -n %{python_name} -f %{pyproject_files} + %changelog %autochangelog diff --git a/test/examples/py2pack-opensuse-augmented.spec b/test/examples/py2pack-opensuse-augmented.spec index e876c16..0f28129 100644 --- a/test/examples/py2pack-opensuse-augmented.spec +++ b/test/examples/py2pack-opensuse-augmented.spec @@ -1,7 +1,7 @@ # # spec file for package python-py2pack # -# Copyright (c) __YEAR__ SUSE LLC +# Copyright (c) 2025 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed diff --git a/test/examples/sampleproject-fedora-augmented.spec b/test/examples/sampleproject-fedora-augmented.spec index 5ccce9d..f715ac4 100644 --- a/test/examples/sampleproject-fedora-augmented.spec +++ b/test/examples/sampleproject-fedora-augmented.spec @@ -7,46 +7,13 @@ 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) +License: Copyright (c) 2016 The Python Packaging Authority (PyPA)Permission is hereby granted, free of charge, to any person obtaining a copy ofthis software and associated documentation files (the "Software"), to deal inthe Software without restriction, including without limitation the rights touse, copy, modify, merge, publish, distribute, sublicense, and/or sell copiesof the Software, and to permit persons to whom the Software is furnished to doso, subject to the following conditions:The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, 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 THESOFTWARE. (FIXME:No SPDX) URL: https://github.com/pypa/sampleproject -Source: https://files.pythonhosted.org/packages/source/s/sampleproject/sampleproject-%{version}.tar.gz +Source: 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: fdupes -Requires: %{python_module peppercorn} -Suggests: %{python_module check-manifest} BuildArch: noarch # Fill in the actual package description to submit package to Fedora @@ -58,27 +25,32 @@ A sample Python project} %package -n %{python_name} Summary: %{summary} -%description -n %{python_name} %_description +%description -n %{python_name} %_description +%pyproject_extras_subpkg -n %{python_name} dev,test %prep %autosetup -p1 -n %{pypi_name}-%{version} + +%generate_buildrequires +%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 + %files -n %{python_name} -f %{pyproject_files} + %changelog %autochangelog diff --git a/test/examples/sampleproject-opensuse-augmented.spec b/test/examples/sampleproject-opensuse-augmented.spec index 1b54385..f8efaea 100644 --- a/test/examples/sampleproject-opensuse-augmented.spec +++ b/test/examples/sampleproject-opensuse-augmented.spec @@ -20,26 +20,7 @@ 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) +License: Copyright (c) 2016 The Python Packaging Authority (PyPA)Permission is hereby granted, free of charge, to any person obtaining a copy ofthis software and associated documentation files (the "Software"), to deal inthe Software without restriction, including without limitation the rights touse, copy, modify, merge, publish, distribute, sublicense, and/or sell copiesof the Software, and to permit persons to whom the Software is furnished to doso, subject to the following conditions:The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, 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 THESOFTWARE. (FIXME:No SPDX) URL: https://github.com/pypa/sampleproject Source: https://files.pythonhosted.org/packages/source/s/sampleproject/sampleproject-%{version}.tar.gz BuildRequires: python-rpm-macros diff --git a/test/test_template.py b/test/test_template.py index 3d3d487..e78c1d6 100644 --- a/test/test_template.py +++ b/test/test_template.py @@ -67,11 +67,11 @@ 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) @@ -81,8 +81,15 @@ def test_template(tmpdir, template, fetch_tarball, project, version): 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 + 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(written_spec) + 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