commit: e5e79c093c50bfaf7ec667fe0ded72efe972d1c3
Author: Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Tue Jan 10 09:42:00 2023 +0000
Commit: Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Tue Jan 17 20:46:03 2023 +0000
URL:
https://gitweb.gentoo.org/proj/pkgcore/pkgcore.git/commit/?id=e5e79c09
refactor(sync): hide FD passing as an internal thing.
Effectively, add '_spawn' and '_spawn_interactive' to the base class,
and expect consumers to invoke the appropriate one.
This addition doesn't fully fix the fundamental lack of an observer (or log)
based approach to getting info out of syncers, but it at least hides some of
the internals so the syncer classes can do things like knowing what the
correct encoding is for stdout, thus being able to write their own messages
to it.
TL;DR: This subsystem's interaction w/ CLI (and used as a library) needs
rewriting,
this just reduces that problem when we visit it.
Signed-off-by: Brian Harring <ferringb <AT> gmail.com>
Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org>
src/pkgcore/sync/base.py | 24 ++++++++++++++++--------
src/pkgcore/sync/http.py | 2 +-
src/pkgcore/sync/rsync.py | 11 +++++------
src/pkgcore/sync/svn.py | 10 ++++------
tests/scripts/test_pmaint.py | 7 ++++---
5 files changed, 30 insertions(+), 24 deletions(-)
diff --git a/src/pkgcore/sync/base.py b/src/pkgcore/sync/base.py
index 2a63545e3..1114532fb 100644
--- a/src/pkgcore/sync/base.py
+++ b/src/pkgcore/sync/base.py
@@ -14,6 +14,7 @@ __all__ = (
import os
import pwd
import stat
+import typing
from importlib import import_module
from snakeoil import process
@@ -123,7 +124,7 @@ class Syncer:
except KeyError as exc:
raise MissingLocalUser(raw_uri, str(exc))
- def sync(self, verbosity=None, force=False):
+ def sync(self, verbosity: typing.Optional[int] = None, force=False):
if self.disabled:
return False
kwds = {}
@@ -131,10 +132,9 @@ class Syncer:
kwds["force"] = True
if verbosity is None:
verbosity = self.verbosity
- # output_fd is harded coded as stdout atm.
- return self._sync(verbosity, 1, **kwds)
+ return self._sync(verbosity, **kwds)
- def _sync(self, verbosity, output_fd, **kwds):
+ def _sync(self, verbosity: int, **kwds):
raise NotImplementedError(self, "_sync")
def __str__(self):
@@ -191,11 +191,19 @@ class ExternalSyncer(Syncer):
disabled = cls._disabled = os.path.exists(path)
return disabled
- def _spawn(self, command, pipes, **kwargs):
+ def _spawn(self, command, **kwargs):
+ # Note: stderr is explicitly forced to stdout since that's how it was
originally done.
+ # This can be changed w/ a discussion.
+ kwargs.setdefault("fd_pipes", {1: 1, 2: 1})
return process.spawn.spawn(
- command, fd_pipes=pipes, uid=self.uid, gid=self.gid, env=self.env,
**kwargs
+ command, uid=self.uid, gid=self.gid, env=self.env, **kwargs
)
+ def _spawn_interactive(self, command, **kwargs):
+ # Note: stderr is explicitly forced to stdout since that's how it was
originally done.
+ # This can be changed w/ a discussion.
+ return self._spawn(command, fd_pipes={0: 0, 1: 1, 2: 1}, **kwargs)
+
@staticmethod
def _rewrite_uri_from_stat(path, uri):
chunks = uri.split("//", 1)
@@ -209,7 +217,7 @@ class ExternalSyncer(Syncer):
class VcsSyncer(ExternalSyncer):
- def _sync(self, verbosity, output_fd):
+ def _sync(self, verbosity):
try:
st = os.stat(self.basedir)
except FileNotFoundError:
@@ -229,7 +237,7 @@ class VcsSyncer(ExternalSyncer):
elif verbosity > 0:
command.append("-" + "v" * verbosity)
- ret = self._spawn(command, pipes={1: output_fd, 2: output_fd, 0: 0},
cwd=chdir)
+ ret = self._spawn_interactive(command, cwd=chdir)
return ret == 0
def _initial_pull(self):
diff --git a/src/pkgcore/sync/http.py b/src/pkgcore/sync/http.py
index b422d94ad..a3ec1898a 100644
--- a/src/pkgcore/sync/http.py
+++ b/src/pkgcore/sync/http.py
@@ -22,7 +22,7 @@ class http_syncer(base.Syncer):
self.basename = os.path.basename(uri)
super().__init__(basedir, uri, **kwargs)
- def _sync(self, verbosity, output_fd, force=False, **kwargs):
+ def _sync(self, verbosity, force=False, **kwargs):
dest = self._pre_download()
if self.uri.lower().startswith("https://"):
diff --git a/src/pkgcore/sync/rsync.py b/src/pkgcore/sync/rsync.py
index c6b95c70f..b0f032731 100644
--- a/src/pkgcore/sync/rsync.py
+++ b/src/pkgcore/sync/rsync.py
@@ -128,8 +128,7 @@ class rsync_syncer(base.ExternalSyncer):
f"DNS resolution failed for {self.hostname!r}: {e.strerror}"
)
- def _sync(self, verbosity, output_fd):
- fd_pipes = {1: output_fd, 2: output_fd}
+ def _sync(self, verbosity):
opts = list(self.opts)
if self.rsh:
opts.append("-e")
@@ -150,7 +149,7 @@ class rsync_syncer(base.ExternalSyncer):
self.basedir,
] + opts
- ret = self._spawn(cmd, fd_pipes)
+ ret = self._spawn(cmd)
if ret == 0:
return True
elif ret == 1:
@@ -203,7 +202,7 @@ class rsync_timestamp_syncer(rsync_syncer):
# malformed timestamp
return None
- def _sync(self, verbosity, output_fd, force=False):
+ def _sync(self, verbosity, force=False):
doit = force or self.last_timestamp is None
ret = None
try:
@@ -213,7 +212,7 @@ class rsync_timestamp_syncer(rsync_syncer):
timestamp_uri = pjoin(self.uri, "metadata",
"timestamp.chk")
timestamp_path = new_timestamp.name
timestamp_syncer = _RsyncFileSyncer(timestamp_path,
timestamp_uri)
- ret = timestamp_syncer._sync(verbosity, output_fd)
+ ret = timestamp_syncer._sync(verbosity)
if not ret:
doit = True
else:
@@ -226,7 +225,7 @@ class rsync_timestamp_syncer(rsync_syncer):
doit = delta > self.negative_sync_delay
if not doit:
return True
- ret = super()._sync(verbosity, output_fd)
+ ret = super()._sync(verbosity)
# force a reset of the timestamp
self.last_timestamp = self.current_timestamp()
finally:
diff --git a/src/pkgcore/sync/svn.py b/src/pkgcore/sync/svn.py
index e8cd60593..6b9e29a52 100644
--- a/src/pkgcore/sync/svn.py
+++ b/src/pkgcore/sync/svn.py
@@ -50,19 +50,17 @@ class svn_syncer(base.ExternalSyncer):
raise base.UriError(raw_uri, "protocol unknown")
return raw_uri
- def _sync(self, verbosity, output_fd):
+ def _sync(self, verbosity):
uri = self.uri
if uri.startswith("svn+http://"):
uri = uri.replace("svn+http://", "http://")
elif uri.startswith("svn+https://"):
uri = uri.replace("svn+https://", "https://")
if not os.path.exists(self.basedir):
- return 0 == self._spawn(
- [self.binary_path, "co", uri, self.basedir],
- {1: output_fd, 2: output_fd, 0: 0},
+ return 0 == self._spawn_interactive(
+ [self.binary_path, "co", uri, self.basedir]
)
- return 0 == self._spawn(
+ return 0 == self._spawn_interactive(
[self.binary_path, "update"],
- {1: output_fd, 2: output_fd, 0: 0},
cwd=self.basedir,
)
diff --git a/tests/scripts/test_pmaint.py b/tests/scripts/test_pmaint.py
index 63846456d..c9af122f2 100644
--- a/tests/scripts/test_pmaint.py
+++ b/tests/scripts/test_pmaint.py
@@ -1,6 +1,9 @@
from functools import partial
from io import BytesIO
+from snakeoil.formatters import PlainTextFormatter
+from snakeoil.mappings import AttrAccessible
+
from pkgcore.config import basics
from pkgcore.config.hint import ConfigHint
from pkgcore.ebuild.cpv import CPV
@@ -9,8 +12,6 @@ from pkgcore.repository import syncable, util
from pkgcore.scripts import pmaint
from pkgcore.sync import base
from pkgcore.test.scripts.helpers import ArgParseMixin
-from snakeoil.formatters import PlainTextFormatter
-from snakeoil.mappings import AttrAccessible
Options = AttrAccessible
@@ -96,7 +97,7 @@ class FakeSyncer(base.Syncer):
super().__init__(*args, **kwargs)
self.synced = False
- def _sync(self, verbosity, output_fd, **kwds):
+ def _sync(self, verbosity, **kwds):
self.synced = True
return self.succeed