1
0
mirror of https://github.com/openSUSE/osc.git synced 2025-01-31 16:56:17 +01:00

Merge pull request #1474 from dmach/quiet-progressbar

Don't show progressbars when --quiet is specified
This commit is contained in:
Daniel Mach 2024-01-25 09:31:18 +01:00 committed by GitHub
commit 4ca4845f6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 129 additions and 20 deletions

View File

@ -344,13 +344,13 @@ class OscMainCommand(MainCommand):
"-v", "-v",
"--verbose", "--verbose",
action="store_true", action="store_true",
help="increase verbosity", help="increase verbosity (conflicts with --quiet)",
) )
self.add_argument( self.add_argument(
"-q", "-q",
"--quiet", "--quiet",
action="store_true", action="store_true",
help="be quiet, not verbose", help="be quiet, not verbose (conflicts with --verbose)",
) )
self.add_argument( self.add_argument(
"--debug", "--debug",
@ -434,6 +434,7 @@ class OscMainCommand(MainCommand):
override_http_full_debug=args.http_full_debug, override_http_full_debug=args.http_full_debug,
override_no_keyring=args.no_keyring, override_no_keyring=args.no_keyring,
override_post_mortem=args.post_mortem, override_post_mortem=args.post_mortem,
override_quiet=args.quiet,
override_traceback=args.traceback, override_traceback=args.traceback,
override_verbose=args.verbose, override_verbose=args.verbose,
overrides=overrides, overrides=overrides,

View File

@ -559,13 +559,25 @@ class Options(OscOptions):
section=True, section=True,
) # type: ignore[assignment] ) # type: ignore[assignment]
quiet: bool = Field(
default=False,
description=textwrap.dedent(
"""
Reduce amount of printed information to bare minimum.
If enabled, automatically sets ``verbose`` to ``False``.
"""
),
) # type: ignore[assignment]
verbose: bool = Field( verbose: bool = Field(
default=False, default=False,
description=textwrap.dedent( description=textwrap.dedent(
""" """
Increase amount of printed information to stdout. Increase amount of printed information to stdout.
Automatically set to ``False`` when ``quiet`` is enabled.
""" """
), ),
get_callback=lambda conf, value: False if conf.quiet else value,
) # type: ignore[assignment] ) # type: ignore[assignment]
debug: bool = Field( debug: bool = Field(
@ -582,8 +594,10 @@ class Options(OscOptions):
description=textwrap.dedent( description=textwrap.dedent(
""" """
Print HTTP traffic to stderr. Print HTTP traffic to stderr.
Automatically set to ``True`` when``http_full_debug`` is enabled.
""" """
), ),
get_callback=lambda conf, value: True if conf.http_full_debug else value,
) # type: ignore[assignment] ) # type: ignore[assignment]
http_full_debug: bool = Field( http_full_debug: bool = Field(
@ -591,6 +605,7 @@ class Options(OscOptions):
description=textwrap.dedent( description=textwrap.dedent(
""" """
[CAUTION!] Print HTTP traffic incl. authentication data to stderr. [CAUTION!] Print HTTP traffic incl. authentication data to stderr.
If enabled, automatically sets ``http_debug`` to ``True``.
""" """
), ),
) # type: ignore[assignment] ) # type: ignore[assignment]
@ -1782,6 +1797,7 @@ def get_config(override_conffile=None,
override_http_full_debug=None, override_http_full_debug=None,
override_traceback=None, override_traceback=None,
override_post_mortem=None, override_post_mortem=None,
override_quiet=None,
override_no_keyring=None, override_no_keyring=None,
override_verbose=None, override_verbose=None,
overrides=None overrides=None
@ -1810,7 +1826,6 @@ def get_config(override_conffile=None,
overrides["http_debug"] = override_http_debug overrides["http_debug"] = override_http_debug
if override_http_full_debug is not None: if override_http_full_debug is not None:
overrides["http_debug"] = override_http_full_debug or overrides["http_debug"]
overrides["http_full_debug"] = override_http_full_debug overrides["http_full_debug"] = override_http_full_debug
if override_traceback is not None: if override_traceback is not None:
@ -1822,6 +1837,9 @@ def get_config(override_conffile=None,
if override_no_keyring is not None: if override_no_keyring is not None:
overrides["use_keyring"] = not override_no_keyring overrides["use_keyring"] = not override_no_keyring
if override_quiet is not None:
overrides["quiet"] = override_quiet
if override_verbose is not None: if override_verbose is not None:
overrides["verbose"] = override_verbose overrides["verbose"] = override_verbose

View File

@ -5,6 +5,10 @@
import signal import signal
import sys
from abc import ABC
from abc import abstractmethod
from typing import Optional
try: try:
import progressbar as pb import progressbar as pb
@ -13,9 +17,25 @@ except ImportError:
have_pb_module = False have_pb_module = False
class PBTextMeter: class TextMeterBase(ABC):
@abstractmethod
def start(self, basename: str, size: Optional[int] = None):
pass
def start(self, basename, size=None): @abstractmethod
def update(self, amount_read: int):
pass
@abstractmethod
def end(self):
pass
class PBTextMeter(TextMeterBase):
def __init__(self):
self.bar: pb.ProgressBar
def start(self, basename: str, size: Optional[int] = None):
if size is None: if size is None:
widgets = [f"{basename}: ", pb.AnimatedMarker(), ' ', pb.Timer()] widgets = [f"{basename}: ", pb.AnimatedMarker(), ' ', pb.Timer()]
self.bar = pb.ProgressBar(widgets=widgets, maxval=pb.UnknownLength) self.bar = pb.ProgressBar(widgets=widgets, maxval=pb.UnknownLength)
@ -33,7 +53,7 @@ class PBTextMeter:
signal.siginterrupt(signal.SIGWINCH, False) signal.siginterrupt(signal.SIGWINCH, False)
self.bar.start() self.bar.start()
def update(self, amount_read): def update(self, amount_read: int):
self.bar.update(amount_read) self.bar.update(amount_read)
def end(self): def end(self):
@ -41,25 +61,27 @@ class PBTextMeter:
class NoPBTextMeter: class NoPBTextMeter:
def start(self, basename, size=None): def start(self, basename: str, size: Optional[int] = None):
pass pass
def update(self, *args, **kwargs): def update(self, amount_read: int):
pass pass
def end(self, *args, **kwargs): def end(self):
pass pass
def create_text_meter(*args, **kwargs): def create_text_meter(*args, **kwargs) -> TextMeterBase:
use_pb_fallback = kwargs.pop('use_pb_fallback', True) from .conf import config
if have_pb_module or use_pb_fallback:
return TextMeter(*args, **kwargs) # this option is no longer used
return None kwargs.pop("use_pb_fallback", True)
meter_class = PBTextMeter
if not have_pb_module or config.quiet or not config.show_download_progress or not sys.stdout.isatty():
meter_class = NoPBTextMeter
return meter_class(*args, **kwargs)
if have_pb_module:
TextMeter = PBTextMeter
else:
TextMeter = NoPBTextMeter
# vim: sw=4 et # vim: sw=4 et

View File

@ -10,6 +10,7 @@ import copy
import inspect import inspect
import sys import sys
import types import types
from typing import Callable
from typing import get_type_hints from typing import get_type_hints
# supported types # supported types
@ -76,6 +77,7 @@ class Field(property):
default: Any = NotSet, default: Any = NotSet,
description: Optional[str] = None, description: Optional[str] = None,
exclude: bool = False, exclude: bool = False,
get_callback: Optional[Callable] = None,
**extra, **extra,
): ):
# the default value; it can be a factory function that is lazily evaluated on the first use # the default value; it can be a factory function that is lazily evaluated on the first use
@ -106,6 +108,10 @@ class Field(property):
# whether to exclude this field from export # whether to exclude this field from export
self.exclude = exclude self.exclude = exclude
# optional callback to postprocess returned field value
# it takes (model_instance, value) and returns modified value
self.get_callback = get_callback
# extra fields # extra fields
self.extra = extra self.extra = extra
@ -235,12 +241,18 @@ class Field(property):
def get(self, obj): def get(self, obj):
try: try:
return obj._values[self.name] result = obj._values[self.name]
if self.get_callback is not None:
result = self.get_callback(obj, result)
return result
except KeyError: except KeyError:
pass pass
try: try:
return obj._defaults[self.name] result = obj._defaults[self.name]
if self.get_callback is not None:
result = self.get_callback(obj, result)
return result
except KeyError: except KeyError:
pass pass

View File

@ -43,6 +43,7 @@ debug = 0
http_debug = 0 http_debug = 0
http_full_debug = 0 http_full_debug = 0
http_retries = 3 http_retries = 3
quiet = 0
verbose = 0 verbose = 0
no_preinstallimage = 0 no_preinstallimage = 0
traceback = 0 traceback = 0
@ -218,6 +219,9 @@ class TestExampleConfig(unittest.TestCase):
def test_http_retries(self): def test_http_retries(self):
self.assertEqual(self.config["http_retries"], 3) self.assertEqual(self.config["http_retries"], 3)
def test_quiet(self):
self.assertEqual(self.config["quiet"], False)
def test_verbose(self): def test_verbose(self):
self.assertEqual(self.config["verbose"], False) self.assertEqual(self.config["verbose"], False)
@ -430,6 +434,33 @@ class TestExampleConfig(unittest.TestCase):
self.assertEqual(self.config["apiurl_aliases"], expected) self.assertEqual(self.config["apiurl_aliases"], expected)
class TestOverrides(unittest.TestCase):
def test_verbose(self):
self.options = osc.conf.Options()
self.assertEqual(self.options.quiet, False)
self.assertEqual(self.options.verbose, False)
self.options.quiet = True
self.options.verbose = True
self.assertEqual(self.options.quiet, True)
# ``verbose`` is forced to ``False`` by the ``quiet`` option
self.assertEqual(self.options.verbose, False)
self.options.quiet = False
self.assertEqual(self.options.quiet, False)
self.assertEqual(self.options.verbose, True)
def test_http_debug(self):
self.options = osc.conf.Options()
self.assertEqual(self.options.http_debug, False)
self.assertEqual(self.options.http_full_debug, False)
self.options.http_full_debug = True
# ``http_debug`` forced to ``True`` by the ``http_full_debug`` option
self.assertEqual(self.options.http_debug, True)
self.assertEqual(self.options.http_full_debug, True)
class TestFromParent(unittest.TestCase): class TestFromParent(unittest.TestCase):
def setUp(self): def setUp(self):
self.options = osc.conf.Options() self.options = osc.conf.Options()

View File

@ -291,6 +291,31 @@ class Test(unittest.TestCase):
self.assertEqual(c.field, "new-text") self.assertEqual(c.field, "new-text")
self.assertEqual(c.field2, "text") self.assertEqual(c.field2, "text")
def test_get_callback(self):
class Model(BaseModel):
quiet: bool = Field(
default=False,
)
verbose: bool = Field(
default=False,
# return False if ``quiet`` is True; return the actual value otherwise
get_callback=lambda obj, value: False if obj.quiet else value,
)
m = Model()
self.assertEqual(m.quiet, False)
self.assertEqual(m.verbose, False)
m.quiet = True
m.verbose = True
self.assertEqual(m.quiet, True)
self.assertEqual(m.verbose, False)
m.quiet = False
m.verbose = True
self.assertEqual(m.quiet, False)
self.assertEqual(m.verbose, True)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()