diff options
author | Brian Harring <ferringb@gmail.com> | 2023-01-10 01:42:00 -0800 |
---|---|---|
committer | Arthur Zamarin <arthurzam@gentoo.org> | 2023-01-17 22:46:03 +0200 |
commit | e5e79c093c50bfaf7ec667fe0ded72efe972d1c3 (patch) | |
tree | 547b3e906afeed23782fbb0e19a20e1a7a7840d8 | |
parent | portage_conf: add support for make.profile as directory (diff) | |
download | pkgcore-e5e79c093c50bfaf7ec667fe0ded72efe972d1c3.tar.gz pkgcore-e5e79c093c50bfaf7ec667fe0ded72efe972d1c3.tar.bz2 pkgcore-e5e79c093c50bfaf7ec667fe0ded72efe972d1c3.zip |
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@gmail.com>
Signed-off-by: Arthur Zamarin <arthurzam@gentoo.org>
-rw-r--r-- | src/pkgcore/sync/base.py | 24 | ||||
-rw-r--r-- | src/pkgcore/sync/http.py | 2 | ||||
-rw-r--r-- | src/pkgcore/sync/rsync.py | 11 | ||||
-rw-r--r-- | src/pkgcore/sync/svn.py | 10 | ||||
-rw-r--r-- | 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 2a63545e..1114532f 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 b422d94a..a3ec1898 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 c6b95c70..b0f03273 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 e8cd6059..6b9e29a5 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 63846456..c9af122f 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 |