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')

Reply via email to