Merge pull request #1893 from jwhonce/bug/1869

Refactor CLI booleans to be consistent and defined behavior
This commit is contained in:
OpenShift Merge Robot
2018-12-07 08:31:55 -08:00
committed by GitHub
32 changed files with 208 additions and 264 deletions

View File

@ -1,6 +1,6 @@
PYTHON ?= $(shell command -v python3 2>/dev/null || command -v python)
DESTDIR ?= /
PODMAN_VERSION ?= '0.0.4'
PODMAN_VERSION ?= '0.11.1.1'
.PHONY: python-podman
python-podman:
@ -22,8 +22,8 @@ install:
.PHONY: upload
upload:
$(PODMAN_VERSION) $(PYTHON) setup.py sdist bdist_wheel
twine upload --repository-url https://test.pypi.org/legacy/ dist/*
PODMAN_VERSION=$(PODMAN_VERSION) $(PYTHON) setup.py sdist bdist_wheel
twine upload --verbose --repository-url https://test.pypi.org/legacy/ dist/*
.PHONY: clobber
clobber: uninstall clean

View File

@ -1,6 +1,6 @@
PYTHON ?= $(shell command -v python3 2>/dev/null || command -v python)
DESTDIR := /
PODMAN_VERSION ?= '0.0.4'
PODMAN_VERSION ?= '0.11.1.1'
.PHONY: python-pypodman
python-pypodman:
@ -22,7 +22,7 @@ install:
.PHONY: upload
upload:
$(PODMAN_VERSION) $(PYTHON) setup.py sdist bdist_wheel
PODMAN_VERSION=$(PODMAN_VERSION) $(PYTHON) setup.py sdist bdist_wheel
twine upload --repository-url https://test.pypi.org/legacy/ dist/*
.PHONY: clobber

View File

@ -3,18 +3,17 @@ import sys
import podman
from pypodman.lib.action_base import AbstractActionBase
from pypodman.lib.parser_actions import (BooleanAction, BooleanValidate,
ChangeAction, PathAction,
PositiveIntAction, UnitAction)
from pypodman.lib.parser_actions import (ChangeAction, PathAction,
PositiveIntAction, SignalAction,
UnitAction)
from pypodman.lib.podman_parser import PodmanArgumentParser
from pypodman.lib.report import Report, ReportColumn
# Silence pylint overlording...
assert BooleanAction
assert BooleanValidate
assert ChangeAction
assert PathAction
assert PositiveIntAction
assert SignalAction
assert UnitAction
__all__ = [

View File

@ -17,29 +17,21 @@ class AbstractActionBase(abc.ABC):
Use set_defaults() to set attributes "class_" and "method". These will
be invoked as class_(parsed_args).method()
"""
parent.add_argument(
parent.add_flag(
'--all',
action='store_true',
help=('list all items.'
' (default: no-op, included for compatibility.)'))
parent.add_argument(
'--no-trunc',
'--notruncate',
action='store_false',
dest='truncate',
help='list all items.')
parent.add_flag(
'--truncate',
'--trunc',
default=True,
help='Display extended information. (default: False)')
parent.add_argument(
'--noheading',
action='store_false',
dest='heading',
help="Truncate id's and other long fields.")
parent.add_flag(
'--heading',
default=True,
help=('Omit the table headings from the output.'
' (default: False)'))
parent.add_argument(
help='Include table headings in the output.')
parent.add_flag(
'--quiet',
action='store_true',
help='List only the IDs. (default: %(default)s)')
help='List only the IDs.')
def __init__(self, args):
"""Construct class."""

View File

@ -1,6 +1,6 @@
"""Implement common create container arguments together."""
from pypodman.lib import BooleanAction, UnitAction
from pypodman.lib import SignalAction, UnitAction
class CreateArguments():
@ -108,11 +108,9 @@ class CreateArguments():
metavar='NODES',
help=('Memory nodes (MEMs) in which to allow execution (0-3, 0,1).'
' Only effective on NUMA systems'))
parser.add_argument(
parser.add_flag(
'--detach',
'-d',
action=BooleanAction,
default=False,
help='Detached mode: run the container in the background and'
' print the new container ID. (default: False)')
parser.add_argument(
@ -218,7 +216,7 @@ class CreateArguments():
# only way for argparse to handle these options.
vol_args = {
'choices': ['bind', 'tmpfs', 'ignore'],
'choices': ('bind', 'tmpfs', 'ignore'),
'metavar': 'MODE',
'type': str.lower,
'help': 'Tells podman how to handle the builtin image volumes',
@ -228,12 +226,10 @@ class CreateArguments():
volume_group.add_argument('--image-volume', **vol_args)
volume_group.add_argument('--builtin-volume', **vol_args)
parser.add_argument(
parser.add_flag(
'--interactive',
'-i',
action=BooleanAction,
default=False,
help='Keep STDIN open even if not attached. (default: False)')
help='Keep STDIN open even if not attached.')
parser.add_argument('--ipc', help='Create namespace')
parser.add_argument(
'--kernel-memory', action=UnitAction, help='Kernel memory limit')
@ -278,10 +274,9 @@ class CreateArguments():
metavar='BRIDGE',
help='Set the Network mode for the container.'
' (format: bridge, host, container:UUID, ns:PATH, none)')
parser.add_argument(
parser.add_flag(
'--oom-kill-disable',
action=BooleanAction,
help='Whether to disable OOM Killer for the container or not')
help='Whether to disable OOM Killer for the container or not.')
parser.add_argument(
'--oom-score-adj',
choices=range(-1000, 1000),
@ -298,41 +293,33 @@ class CreateArguments():
help=("Tune the container's pids limit."
" Set -1 to have unlimited pids for the container."))
parser.add_argument('--pod', help='Run container in an existing pod')
parser.add_argument(
parser.add_flag(
'--privileged',
action=BooleanAction,
help='Give extended privileges to this container.')
parser.add_argument(
'--publish',
'-p',
metavar='RANGE',
help="Publish a container's port, or range of ports, to the host")
parser.add_argument(
parser.add_flag(
'--publish-all',
'-P',
action=BooleanAction,
help='Publish all exposed ports to random'
' ports on the host interfaces'
'(default: False)')
parser.add_argument(
' ports on the host interfaces.')
parser.add_flag(
'--quiet',
'-q',
action='store_true',
help='Suppress output information when pulling images')
parser.add_argument(
parser.add_flag(
'--read-only',
action=BooleanAction,
help="Mount the container's root filesystem as read only.")
parser.add_argument(
parser.add_flag(
'--rm',
action=BooleanAction,
default=False,
help='Automatically remove the container when it exits.')
parser.add_argument(
'--rootfs',
action='store_true',
help=('If specified, the first argument refers to an'
' exploded container on the file system of remote host.'))
help='If specified, the first argument refers to an'
' exploded container on the file system of remote host.')
parser.add_argument(
'--security-opt',
action='append',
@ -340,15 +327,14 @@ class CreateArguments():
help='Set security options.')
parser.add_argument(
'--shm-size', action=UnitAction, help='Size of /dev/shm')
parser.add_argument(
parser.add_flag(
'--sig-proxy',
action=BooleanAction,
default=True,
help='Proxy signals sent to the podman run'
' command to the container process')
parser.add_argument(
'--stop-signal',
metavar='SIGTERM',
action=SignalAction,
default='TERM',
help='Signal to stop a container')
parser.add_argument(
'--stop-timeout',
@ -374,11 +360,9 @@ class CreateArguments():
metavar='MOUNT',
help='Create a tmpfs mount.'
' (default: rw,noexec,nosuid,nodev,size=65536k.)')
parser.add_argument(
parser.add_flag(
'--tty',
'-t',
action=BooleanAction,
default=False,
help='Allocate a pseudo-TTY for standard input of container.')
parser.add_argument(
'--uidmap',
@ -394,15 +378,16 @@ class CreateArguments():
parser.add_argument(
'--user',
'-u',
help=('Sets the username or UID used and optionally'
' the groupname or GID for the specified command.'))
help='Sets the username or UID used and optionally'
' the groupname or GID for the specified command.')
parser.add_argument(
'--userns',
metavar='NAMESPACE',
help='Set the user namespace mode for the container')
parser.add_argument(
'--uts',
choices=['host', 'ns'],
choices=('host', 'ns'),
type=str.lower,
help='Set the UTS mode for the container')
parser.add_argument('--volume', '-v', help='Create a bind mount.')
parser.add_argument(

View File

@ -2,7 +2,7 @@
import sys
import podman
from pypodman.lib import AbstractActionBase, BooleanAction, ChangeAction
from pypodman.lib import AbstractActionBase, ChangeAction
class Commit(AbstractActionBase):
@ -44,17 +44,14 @@ class Commit(AbstractActionBase):
help='Set commit message for committed image'
' (Only on docker images.)',
)
parser.add_argument(
parser.add_flag(
'--pause',
'-p',
action=BooleanAction,
default=True,
help='Pause the container when creating an image',
)
parser.add_argument(
parser.add_flag(
'--quiet',
'-q',
action='store_true',
help='Suppress output',
)
parser.add_argument(

View File

@ -5,8 +5,7 @@ from collections import OrderedDict
import humanize
import podman
from pypodman.lib import (AbstractActionBase, BooleanAction, Report,
ReportColumn)
from pypodman.lib import AbstractActionBase, Report, ReportColumn
class History(AbstractActionBase):
@ -17,13 +16,10 @@ class History(AbstractActionBase):
"""Add History command to parent parser."""
parser = parent.add_parser('history', help='report image history')
super().subparser(parser)
parser.add_argument(
parser.add_flag(
'--human',
'-H',
action=BooleanAction,
default='True',
help='Display sizes and dates in human readable format.'
' (default: %(default)s)')
help='Display sizes and dates in human readable format.')
parser.add_argument(
'--format',
choices=('json', 'table'),

View File

@ -24,11 +24,9 @@ class Images(AbstractActionBase):
help=('Change sort ordered of displayed images.'
' (default: %(default)s)'))
group = parser.add_mutually_exclusive_group()
group.add_argument(
parser.add_flag(
'--digests',
action='store_true',
help='Include digests with images. (default: %(default)s)')
help='Include digests with images.')
parser.set_defaults(class_=cls, method='list')
def __init__(self, args):

View File

@ -22,10 +22,6 @@ class Info(AbstractActionBase):
" (default: yaml)")
parser.set_defaults(class_=cls, method='info')
def __init__(self, args):
"""Construct Info class."""
super().__init__(args)
def info(self):
"""Report on Podman Service."""
try:

View File

@ -22,12 +22,9 @@ class Inspect(AbstractActionBase):
type=str.lower,
help='Type of object to inspect',
)
parser.add_argument(
parser.add_flag(
'--size',
action='store_true',
default=False,
help='Display the total file size if the type is a container.'
' Always True.')
help='Display the total file size if the type is a container.')
parser.add_argument(
'objects',
nargs='+',
@ -35,10 +32,6 @@ class Inspect(AbstractActionBase):
)
parser.set_defaults(class_=cls, method='inspect')
def __init__(self, args):
"""Construct Inspect class."""
super().__init__(args)
def _get_container(self, ident):
try:
logging.debug("Getting container %s", ident)

View File

@ -1,9 +1,8 @@
"""Remote client command for signaling podman containers."""
import signal
import sys
import podman
from pypodman.lib import AbstractActionBase
from pypodman.lib import AbstractActionBase, SignalAction
class Kill(AbstractActionBase):
@ -16,10 +15,9 @@ class Kill(AbstractActionBase):
parser.add_argument(
'--signal',
'-s',
choices=range(1, signal.NSIG),
metavar='[1,{}]'.format(signal.NSIG),
action=SignalAction,
default=9,
help='Signal to send to the container. (default: 9)')
help='Signal to send to the container. (default: %(default)s)')
parser.add_argument(
'containers',
nargs='+',
@ -27,10 +25,6 @@ class Kill(AbstractActionBase):
)
parser.set_defaults(class_=cls, method='kill')
def __init__(self, args):
"""Construct Kill class."""
super().__init__(args)
def kill(self):
"""Signal provided containers."""
try:

View File

@ -19,10 +19,6 @@ class Pause(AbstractActionBase):
)
parser.set_defaults(class_=cls, method='pause')
def __init__(self, args):
"""Construct Pause class."""
super().__init__(args)
def pause(self):
"""Pause provided containers."""
try:

View File

@ -2,7 +2,7 @@
import sys
import podman
from pypodman.lib import AbstractActionBase, BooleanAction
from pypodman.lib import AbstractActionBase
class CreatePod(AbstractActionBase):
@ -20,12 +20,9 @@ class CreatePod(AbstractActionBase):
type=str,
help='Path to cgroups under which the'
' cgroup for the pod will be created.')
parser.add_argument(
parser.add_flag(
'--infra',
action=BooleanAction,
default=True,
help='Create an infra container and associate it with the pod'
'(default: %(default)s)')
help='Create an infra container and associate it with the pod.')
parser.add_argument(
'-l',
'--label',

View File

@ -3,7 +3,7 @@ import signal
import sys
import podman
from pypodman.lib import AbstractActionBase
from pypodman.lib import AbstractActionBase, SignalAction
from pypodman.lib import query_model as query_pods
@ -15,18 +15,16 @@ class KillPod(AbstractActionBase):
"""Add Pod Kill command to parent parser."""
parser = parent.add_parser('kill', help='signal containers in pod')
parser.add_argument(
'-a',
parser.add_flag(
'--all',
action='store_true',
help='Sends signal to all pods')
'-a',
help='Sends signal to all pods.')
parser.add_argument(
'-s',
'--signal',
choices=range(1, signal.NSIG),
metavar='[1,{}]'.format(signal.NSIG),
action=SignalAction,
default=9,
help='Signal to send to the pod. (default: 9)')
help='Signal to send to the pod. (default: %(default)s)')
parser.add_argument('pod', nargs='*', help='pod(s) to signal')
parser.set_defaults(class_=cls, method='kill')

View File

@ -13,8 +13,10 @@ class PausePod(AbstractActionBase):
def subparser(cls, parent):
"""Add Pod Pause command to parent parser."""
parser = parent.add_parser('pause', help='pause containers in pod')
parser.add_argument(
'-a', '--all', action='store_true', help='Pause all pods')
parser.add_flag(
'--all',
'-a',
help='Pause all pods.')
parser.add_argument('pod', nargs='*', help='pod(s) to pause.')
parser.set_defaults(class_=cls, method='pause')

View File

@ -14,18 +14,15 @@ class ProcessesPod(AbstractActionBase):
parser = parent.add_parser('ps', help='list processes of pod')
super().subparser(parser)
parser.add_argument(
parser.add_flag(
'--ctr-names',
action='store_true',
help='Include container name in the info field')
parser.add_argument(
help='Include container name in the info field.')
parser.add_flag(
'--ctr-ids',
action='store_true',
help='Include container ID in the info field')
parser.add_argument(
help='Include container ID in the info field.')
parser.add_flag(
'--ctr-status',
action='store_true',
help='Include container status in the info field')
help='Include container status in the info field.')
parser.add_argument(
'--format',
choices=('json'),

View File

@ -13,13 +13,14 @@ class RemovePod(AbstractActionBase):
def subparser(cls, parent):
"""Add Pod Rm command to parent parser."""
parser = parent.add_parser('rm', help='Delete pod and container(s)')
parser.add_argument(
'-a', '--all', action='store_true', help='Remove all pods')
parser.add_argument(
'-f',
parser.add_flag(
'--all',
'-a',
help='Remove all pods.')
parser.add_flag(
'--force',
action='store_true',
help='Stop and remove container(s) then delete pod')
'-f',
help='Stop and remove container(s) then delete pod.')
parser.add_argument(
'pod', nargs='*', help='Pod to remove. Or, use --all')
parser.set_defaults(class_=cls, method='remove')

View File

@ -13,8 +13,10 @@ class RestartPod(AbstractActionBase):
def subparser(cls, parent):
"""Add Pod Restart command to parent parser."""
parser = parent.add_parser('restart', help='restart containers in pod')
parser.add_argument(
'-a', '--all', action='store_true', help='Restart all pods')
parser.add_flag(
'--all',
'-a',
help='Restart all pods.')
parser.add_argument(
'pod', nargs='*', help='Pod to restart. Or, use --all')
parser.set_defaults(class_=cls, method='restart')

View File

@ -14,8 +14,10 @@ class StartPod(AbstractActionBase):
def subparser(cls, parent):
"""Add Pod Start command to parent parser."""
parser = parent.add_parser('start', help='start pod')
parser.add_argument(
'-a', '--all', action='store_true', help='Start all pods')
parser.add_flag(
'--all',
'-a',
help='Start all pods.')
parser.add_argument(
'pod', nargs='*', help='Pod to start. Or, use --all')
parser.set_defaults(class_=cls, method='start')

View File

@ -13,8 +13,10 @@ class StopPod(AbstractActionBase):
def subparser(cls, parent):
"""Add Pod Stop command to parent parser."""
parser = parent.add_parser('stop', help='stop pod')
parser.add_argument(
'-a', '--all', action='store_true', help='Stop all pods')
parser.add_flag(
'--all',
'-a',
help='Stop all pods.')
parser.add_argument(
'pod', nargs='*', help='Pod to stop. Or, use --all')
parser.set_defaults(class_=cls, method='stop')

View File

@ -13,8 +13,10 @@ class UnpausePod(AbstractActionBase):
def subparser(cls, parent):
"""Add Pod Unpause command to parent parser."""
parser = parent.add_parser('unpause', help='unpause pod')
parser.add_argument(
'-a', '--all', action='store_true', help='Unpause all pods')
parser.add_flag(
'--all',
'-a',
help='Unpause all pods.')
parser.add_argument(
'pod', nargs='*', help='Pod to unpause. Or, use --all')
parser.set_defaults(class_=cls, method='unpause')

View File

@ -5,6 +5,8 @@ import sys
from pypodman.lib import AbstractActionBase
# pylint: disable=wildcard-import
# pylint: disable=unused-wildcard-import
from .pod import *

View File

@ -13,16 +13,13 @@ class Port(AbstractActionBase):
"""Add Port command to parent parser."""
parser = parent.add_parser(
'port', help='retrieve ports from containers')
parser.add_argument(
parser.add_flag(
'--all',
'-a',
action='store_true',
default=False,
help='List all known port mappings for running containers')
parser.add_argument(
'containers',
nargs='*',
default=None,
help='containers to list ports',
)
parser.set_defaults(class_=cls, method='port')
@ -61,3 +58,4 @@ class Port(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
return 0

View File

@ -15,12 +15,10 @@ class Push(AbstractActionBase):
'push',
help='push image elsewhere',
)
parser.add_argument(
parser.add_flag(
'--tlsverify',
action='store_true',
default=True,
help='Require HTTPS and verify certificates when'
' contacting registries (default: %(default)s)')
' contacting registries.')
parser.add_argument(
'image', nargs=1, help='name or id of image to push')
parser.add_argument(
@ -30,10 +28,6 @@ class Push(AbstractActionBase):
)
parser.set_defaults(class_=cls, method='push')
def __init__(self, args):
"""Construct Push class."""
super().__init__(args)
def pull(self):
"""Store image elsewhere."""
try:

View File

@ -23,10 +23,6 @@ class Restart(AbstractActionBase):
'targets', nargs='+', help='container id(s) to restart')
parser.set_defaults(class_=cls, method='restart')
def __init__(self, args):
"""Construct Restart class."""
super().__init__(args)
def restart(self):
"""Restart container(s)."""
try:

View File

@ -12,20 +12,14 @@ class Rm(AbstractActionBase):
def subparser(cls, parent):
"""Add Rm command to parent parser."""
parser = parent.add_parser('rm', help='delete container(s)')
parser.add_argument(
'-f',
parser.add_flag(
'--force',
action='store_true',
help=('force delete of running container(s).'
' (default: %(default)s)'))
'-f',
help='force delete of running container(s).')
parser.add_argument(
'targets', nargs='+', help='container id(s) to delete')
parser.set_defaults(class_=cls, method='remove')
def __init__(self, args):
"""Construct Rm class."""
super().__init__(args)
def remove(self):
"""Remove container(s)."""
for ident in self._args.targets:

View File

@ -12,19 +12,13 @@ class Rmi(AbstractActionBase):
def subparser(cls, parent):
"""Add Rmi command to parent parser."""
parser = parent.add_parser('rmi', help='delete image(s)')
parser.add_argument(
'-f',
parser.add_flag(
'--force',
action='store_true',
help=('force delete of image(s) and associated containers.'
' (default: %(default)s)'))
'-f',
help='force delete of image(s) and associated containers.')
parser.add_argument('targets', nargs='+', help='image id(s) to delete')
parser.set_defaults(class_=cls, method='remove')
def __init__(self, args):
"""Construct Rmi class."""
super().__init__(args)
def remove(self):
"""Remove image(s)."""
for ident in self._args.targets:

View File

@ -4,8 +4,8 @@ import sys
from collections import OrderedDict
import podman
from pypodman.lib import (AbstractActionBase, BooleanValidate,
PositiveIntAction, Report, ReportColumn)
from pypodman.lib import (AbstractActionBase, PositiveIntAction, Report,
ReportColumn)
class FilterAction(argparse.Action):
@ -58,16 +58,16 @@ class FilterAction(argparse.Action):
if val < 0:
parser.error(msg)
elif opt == 'is-automated':
try:
val = BooleanValidate()(val)
except ValueError:
if val.capitalize() in ('True', 'False'):
val = bool(val)
else:
msg = ('{} option "is-automated"'
' must be True or False.'.format(self.dest))
parser.error(msg)
elif opt == 'is-official':
try:
val = BooleanValidate()(val)
except ValueError:
if val.capitalize() in ('True', 'False'):
val = bool(val)
else:
msg = ('{} option "is-official"'
' must be True or False.'.format(self.dest))
parser.error(msg)

View File

@ -2,7 +2,7 @@
import sys
import podman
from pypodman.lib import AbstractActionBase, BooleanAction
from pypodman.lib import AbstractActionBase
class Start(AbstractActionBase):
@ -12,12 +12,10 @@ class Start(AbstractActionBase):
def subparser(cls, parent):
"""Add Start command to parent parser."""
parser = parent.add_parser('start', help='start container')
parser.add_argument(
parser.add_flag(
'--attach',
'-a',
action=BooleanAction,
default=False,
help="Attach container's STDOUT and STDERR (default: %(default)s)")
help="Attach container's STDOUT and STDERR.")
parser.add_argument(
'--detach-keys',
metavar='KEY(s)',
@ -25,18 +23,14 @@ class Start(AbstractActionBase):
help='Override the key sequence for detaching a container.'
' (format: a single character [a-Z] or ctrl-<value> where'
' <value> is one of: a-z, @, ^, [, , or _) (default: ^D)')
parser.add_argument(
parser.add_flag(
'--interactive',
'-i',
action=BooleanAction,
default=False,
help="Attach container's STDIN (default: %(default)s)")
help="Attach container's STDIN.")
# TODO: Implement sig-proxy
parser.add_argument(
parser.add_flag(
'--sig-proxy',
action=BooleanAction,
default=False,
help="Proxy received signals to the process (default: %(default)s)"
help="Proxy received signals to the process."
)
parser.add_argument(
'containers',
@ -74,3 +68,4 @@ class Start(AbstractActionBase):
file=sys.stderr,
flush=True)
return 1
return 0

View File

@ -1,9 +1,7 @@
"""Remote client command for reporting on Podman service."""
import json
import sys
import podman
import yaml
from pypodman.lib import AbstractActionBase
@ -17,10 +15,6 @@ class Version(AbstractActionBase):
'version', help='report version on podman service')
parser.set_defaults(class_=cls, method='version')
def __init__(self, args):
"""Construct Version class."""
super().__init__(args)
def version(self):
"""Report on Podman Service."""
try:

View File

@ -6,6 +6,7 @@ The constructors are very verbose but remain for IDE support.
import argparse
import copy
import os
import signal
# API defined by argparse.Action therefore shut up pylint
# pragma pylint: disable=redefined-builtin
@ -13,22 +14,8 @@ import os
# pragma pylint: disable=too-many-arguments
class BooleanValidate():
"""Validate value is boolean string."""
def __call__(self, value):
"""Return True, False or raise ValueError."""
val = value.capitalize()
if val == 'False':
return False
elif val == 'True':
return True
else:
raise ValueError('"{}" is not True or False'.format(value))
class BooleanAction(argparse.Action):
"""Convert and validate bool argument."""
class ChangeAction(argparse.Action):
"""Convert and validate change argument."""
def __init__(self,
option_strings,
@ -40,47 +27,12 @@ class BooleanAction(argparse.Action):
choices=None,
required=False,
help=None,
metavar='{True,False}'):
"""Create BooleanAction object."""
super().__init__(
option_strings=option_strings,
dest=dest,
nargs=nargs,
const=const,
default=default,
type=type,
choices=choices,
required=required,
help=help,
metavar=metavar)
def __call__(self, parser, namespace, values, option_string=None):
"""Convert and Validate input."""
try:
val = BooleanValidate()(values)
except ValueError:
parser.error('"{}" must be True or False.'.format(option_string))
else:
setattr(namespace, self.dest, val)
class ChangeAction(argparse.Action):
"""Convert and validate change argument."""
def __init__(self,
option_strings,
dest,
nargs=None,
const=None,
default=[],
type=None,
choices=None,
required=False,
help=None,
metavar='OPT=VALUE'):
"""Create ChangeAction object."""
help = (help or '') + ('Apply change(s) to the new image.'
' May be given multiple times.')
if default is None:
default = []
super().__init__(
option_strings=option_strings,
@ -102,7 +54,7 @@ class ChangeAction(argparse.Action):
choices = ('CMD', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'LABEL', 'ONBUILD',
'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR')
opt, val = values.split('=', 1)
opt, _ = values.split('=', 1)
if opt not in choices:
parser.error('Option "{}" is not supported by argument "{}",'
' valid options are: {}'.format(
@ -111,6 +63,70 @@ class ChangeAction(argparse.Action):
setattr(namespace, self.dest, items)
class SignalAction(argparse.Action):
"""Validate input as a signal."""
def __init__(self,
option_strings,
dest,
nargs=None,
const=None,
default=None,
type=str,
choices=None,
required=False,
help='The signal to send.'
' It may be given as a name or a number.',
metavar='SIGNAL'):
"""Create SignalAction object."""
super().__init__(
option_strings=option_strings,
dest=dest,
nargs=nargs,
const=const,
default=default,
type=type,
choices=choices,
required=required,
help=help,
metavar=metavar)
if hasattr(signal, "Signals"):
def _signal_number(signame):
cooked = 'SIG{}'.format(signame)
try:
return signal.Signals[cooked].value
except ValueError:
pass
else:
def _signal_number(signame):
cooked = 'SIG{}'.format(signame)
for n, v in sorted(signal.__dict__.items()):
if n != cooked:
continue
if n.startswith("SIG") and not n.startswith("SIG_"):
return v
self._signal_number = _signal_number
def __call__(self, parser, namespace, values, option_string=None):
"""Validate input is a signal for platform."""
if values.isdigit():
signum = int(values)
if signal.SIGRTMIN <= signum >= signal.SIGRTMAX:
raise ValueError('"{}" is not a valid signal. {}-{}'.format(
values, signal.SIGRTMIN, signal.SIGRTMAX))
else:
signum = self._signal_number(values)
if signum is None:
parser.error(
'"{}" is not a valid signal,'
' see your platform documentation.'.format(values))
setattr(namespace, self.dest, signum)
class UnitAction(argparse.Action):
"""Validate number given is positive integer, with optional suffix."""

View File

@ -48,6 +48,18 @@ class PodmanArgumentParser(argparse.ArgumentParser):
super().__init__(**kwargs)
def add_flag(self, *args, **kwargs):
"""Add flag to parser."""
flags = [a for a in args if a[0] in self.prefix_chars]
dest = flags[0].lstrip(self.prefix_chars)
no_flag = '{0}{0}no-{1}'.format(self.prefix_chars, dest)
group = self.add_mutually_exclusive_group(required=False)
group.add_argument(*flags, action='store_true', dest=dest, **kwargs)
group.add_argument(no_flag, action='store_false', dest=dest, **kwargs)
default = kwargs.get('default', False)
self.set_defaults(**{dest: default})
def initialize_parser(self):
"""Initialize parser without causing recursion meltdown."""
self.add_argument(