commit: d5e0e76bf311d5c2718d6db8d97c7e7e8dd589b5
Author: Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
AuthorDate: Fri Sep 2 06:48:33 2022 +0000
Commit: Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Fri Oct 14 10:35:47 2022 +0000
URL:
https://gitweb.gentoo.org/proj/pkgcore/pkgcore.git/commit/?id=d5e0e76b
patom: new command
Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org>
bin/patom | 1 +
doc/conf.py | 4 +-
src/pkgcore/scripts/patom.py | 105 +++++++++++++++++++++++++++++++++++++++++++
tests/scripts/test_patom.py | 58 ++++++++++++++++++++++++
4 files changed, 166 insertions(+), 2 deletions(-)
diff --git a/bin/patom b/bin/patom
new file mode 120000
index 000000000..6b4695d34
--- /dev/null
+++ b/bin/patom
@@ -0,0 +1 @@
+../src/pkgcore/scripts/__init__.py
\ No newline at end of file
diff --git a/doc/conf.py b/doc/conf.py
index 5a413c812..7166728fc 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -57,7 +57,7 @@ master_doc = 'index'
# General information about the project.
project = pkgdist.MODULE_NAME
authors = ''
-copyright = '2006-2019, pkgcore contributors'
+copyright = '2006-2022, pkgcore contributors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -111,7 +111,7 @@ if on_rtd:
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-html_theme = 'sphinxdoc'
+html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
diff --git a/src/pkgcore/scripts/patom.py b/src/pkgcore/scripts/patom.py
new file mode 100644
index 000000000..b1de47e18
--- /dev/null
+++ b/src/pkgcore/scripts/patom.py
@@ -0,0 +1,105 @@
+"""atom parsing utility"""
+
+import re
+from functools import partial
+
+from ..ebuild.atom import atom as atom_cls
+from ..ebuild.errors import MalformedAtom
+from ..util.commandline import ArgumentParser
+
+
+def atom(value: str) -> atom_cls:
+ try:
+ return atom_cls(value)
+ except MalformedAtom as exc:
+ # try to add an operator in case we got a version without op
+ try:
+ return atom_cls('=' + value)
+ except MalformedAtom:
+ raise exc
+
+argparser = ArgumentParser(description=__doc__, prog=__name__,
script=(__file__, __name__),
+ config=False, domain=False, )
+group = argparser.add_mutually_exclusive_group()
+group.add_argument("-F", "--format", nargs='+', metavar=("FORMAT", "ATOM"),
+ help="Custom output format",
+ docs="""
+ Specify a custom output format.
+
+ Conversion specifiers start with a ``%`` symbol and are followed by
+ either ``{`` or ``[``. Next is the name of the field to expand,
+ followed by a matching ``}`` or ``]``.
+
+ The difference between ``{`` and ``[`` is that the latter is only
+ printed if the field referred is set, while the former prints
+ ``<unset>`` in that case.
+
+ The following fields are supported:
+
+ CATEGORY
+ The category of the package.
+
+ PACKAGE
+ The package name.
+
+ VERSION
+ The package version without the ebuild revision.
+
+ FULLVER
+ The package name, version and revision when not zero. Thus, a zero
+ revision ``-r0`` is not printed.
+
+ REVISION
+ The ebuild revision.
+
+ SLOT
+ The package slot, if exists in atom, otherwise empty.
+
+ SUBSLOT
+ The package sub slot, if exists in atom, otherwise empty.
+
+ REPO_ID
+ The package repository.
+
+ OP
+ The package prefixes, that is version specifiers.
+ """
+)
+group.add_argument("-c", "--compare", nargs=2, metavar="ATOM", type=atom,
+ help="Compare two atoms")
+
+def _transform_format(atom: atom_cls, match: re.Match):
+ if res := getattr(atom, match.group(0)[2:-1].lower()):
+ return str(res)
+ return "<unset>" if match.group(0)[1] == "{" else ""
+
[email protected]_main_func
+def main(options, out, err):
+ if options.format:
+ fmt, *atoms = options.format
+ VAR_REGEX = re.compile(r"%\[.+?\]|%\{.+?\}")
+ for value in atoms:
+ try:
+ value = atom(value)
+ except MalformedAtom as exc:
+ err.write(f"malformed atom: {value!r}: {exc}")
+ continue
+ try:
+ out.write(VAR_REGEX.sub(partial(_transform_format, value),
fmt).strip())
+ except AttributeError:
+ err.write(f"bad format: {fmt!r}")
+ return 1
+ # TODO: check implementation and add tests
+ elif options.compare: # pragma: no cover
+ atom1, atom2 = options.compare
+ if atom1.unversioned_atom != atom2.unversioned_atom or atom1.slot !=
atom2.slot:
+ op = "!="
+ elif atom1 > atom2:
+ op = ">"
+ elif atom1 < atom2:
+ op = "<"
+ elif atom1 == atom2:
+ op = "=="
+ else:
+ op = "!="
+ out.write(f"{atom1} {op} {atom2}")
diff --git a/tests/scripts/test_patom.py b/tests/scripts/test_patom.py
new file mode 100644
index 000000000..423cfcaf4
--- /dev/null
+++ b/tests/scripts/test_patom.py
@@ -0,0 +1,58 @@
+import pytest
+
+from pkgcore.scripts import patom
+from pkgcore.test.scripts.helpers import ArgParseMixin
+
+class TestFormat(ArgParseMixin):
+
+ _argparser = patom.argparser
+
+ def test_empty(self):
+ self.assertOut([], '--format', '%{PACKAGE}')
+
+ def test_unversioned(self):
+ self.assertOut(['spork'], '--format', '%{PACKAGE}', 'dev-utils/spork')
+
+ def test_versioned(self):
+ self.assertOut(['spork'], '--format', '%{PACKAGE}',
'dev-utils/spork-1')
+
+ def test_versioned_op(self):
+ self.assertOut(['spork'], '--format', '%{PACKAGE}',
'=dev-utils/spork-1')
+
+ def test_unversioned_op(self):
+ self.assertErr(["malformed atom: '=dev-utils/spork': invalid package
atom: '=dev-utils/spork'"],
+ '--format', '%{PACKAGE}', '=dev-utils/spork')
+
+ def test_unknown_key(self):
+ self.assertErr(["bad format: '%{UNKNOWN}'"],
+ '--format', '%{UNKNOWN}', 'dev-utils/spork')
+
+ @pytest.mark.parametrize(('key', 'expected'), (
+ pytest.param('%{CATEGORY}', 'dev-utils', id='category'),
+ pytest.param('%{PACKAGE}', 'spork', id='package'),
+ pytest.param('%{VERSION}', '1.2.3_p20221014_p1', id='version'),
+ pytest.param('%{FULLVER}', '1.2.3_p20221014_p1-r12', id='fullver'),
+ pytest.param('%{REVISION}', '12', id='revision'),
+ pytest.param('%{SLOT}', '15', id='slot'),
+ pytest.param('%{SUBSLOT}', '2', id='subslot'),
+ pytest.param('%{REPO_ID}', 'gentoo', id='repo_id'),
+ pytest.param('%{OP}', '>=', id='op'),
+ ))
+ def test_atom_keys(self, key, expected):
+ self.assertOut([expected], '--format', key,
'!!>=dev-utils/spork-1.2.3_p20221014_p1-r12:15/2::gentoo[use]')
+
+ def test_unset(self):
+ self.assertOut(['<unset>'], '--format', '%{VERSION}',
'dev-utils/spork')
+ self.assertOut([''], '--format', '%[VERSION]', 'dev-utils/spork')
+
+ def test_other_text(self):
+ self.assertOut(['repo/dev-utils/spork.ebuild'], '--format',
'repo/%{CATEGORY}/%{PACKAGE}.ebuild', 'dev-utils/spork-2.5')
+
+ @pytest.mark.parametrize('format', (
+ '%{CATEGORY]',
+ '%[CATEGORY}',
+ '%{}',
+ '%[]',
+ ))
+ def test_ignore_format(self, format):
+ self.assertOut([format], '--format', format, 'dev-utils/spork-2.5')