Module refinery.units.crypto.keyderive
Implements key derivation routines. These are mostly meant to be used as
modifiers for multibin expressions that can be passed as key arguments to
modules in refinery.units.crypto.cipher
.
Expand source code Browse git
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Implements key derivation routines. These are mostly meant to be used as
modifiers for multibin expressions that can be passed as key arguments to
modules in `refinery.units.crypto.cipher`.
"""
from __future__ import annotations
import importlib
from refinery.units import Arg, Unit
from refinery.lib.argformats import number
from refinery.lib.types import ByteStr
from enum import Enum
from typing import Callable, TYPE_CHECKING
if TYPE_CHECKING:
from typing import Protocol
class _Hash(Protocol):
def update(self, data: ByteStr): ...
def digest(self) -> ByteStr: ...
def hexdigest(self) -> str: ...
class _HashModule(Protocol):
def new(self, data=None) -> _Hash: ...
__all__ = ['Arg', 'HASH', 'KeyDerivation']
class HASH(str, Enum):
MD2 = 'MD2'
MD4 = 'MD4'
MD5 = 'MD5'
SHA1 = 'SHA'
SHA256 = 'SHA256'
SHA512 = 'SHA512'
SHA224 = 'SHA224'
SHA384 = 'SHA384'
def multidecode(data: ByteStr, function: Callable[[str], ByteStr]) -> ByteStr:
for codec in ['utf8', 'latin1', 'cp1252']:
try:
return function(data.decode(codec))
except UnicodeError:
continue
else:
return function(''.join(chr(t) for t in data))
class KeyDerivation(Unit, abstract=True):
def __init__(
self,
size: Arg(help='The number of bytes to generate.', type=number),
salt: Arg(help='Salt for the derivation.'),
hash: Arg.Option(choices=HASH, metavar='hash',
help='Specify one of these algorithms (default is {default}): {choices}') = None,
iter: Arg.Number(metavar='iter', help='Number of iterations; default is {default}.') = None,
**kw
):
if hash is not None:
hash = Arg.AsOption(hash, HASH)
return super().__init__(salt=salt, size=size, iter=iter, hash=hash, **kw)
@property
def hash(self) -> _HashModule:
name = self.args.hash.value
hash = importlib.import_module(F'Cryptodome.Hash.{name}')
return hash
Sub-modules
refinery.units.crypto.keyderive.deskd
refinery.units.crypto.keyderive.hkdf
refinery.units.crypto.keyderive.hmac
refinery.units.crypto.keyderive.kblob
refinery.units.crypto.keyderive.mscdk
-
Reference: https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptderivekey
refinery.units.crypto.keyderive.mspdb
refinery.units.crypto.keyderive.pbkdf1
refinery.units.crypto.keyderive.pbkdf2
refinery.units.crypto.keyderive.unixcrypt
Classes
class Arg (*args, action=refinery.units.Arg.omit, choices=refinery.units.Arg.omit, const=refinery.units.Arg.omit, default=refinery.units.Arg.omit, dest=refinery.units.Arg.omit, help=refinery.units.Arg.omit, metavar=refinery.units.Arg.omit, nargs=refinery.units.Arg.omit, required=refinery.units.Arg.omit, type=refinery.units.Arg.omit, group=None, guessed=None)
-
This class is specifically an argument for the
add_argument
method of anArgumentParser
from theargparse
module. It can also be used as a decorator or annotation for the constructor of a refinery unit to better control the argument parser of that unit's command line interface. Example:class prefixer(Unit): def __init__( self, prefix: Arg.Binary(help='This data will be prepended to the input.') ): ... def process(self, data): return self.args.prefix + data
Note that when the init of a unit has a return annotation that is a base class of itself, then all its parameters will automatically be forwarded to that base class.
Expand source code Browse git
class Arg(Argument): """ This class is specifically an argument for the `add_argument` method of an `ArgumentParser` from the `argparse` module. It can also be used as a decorator or annotation for the constructor of a refinery unit to better control the argument parser of that unit's command line interface. Example: ``` class prefixer(Unit): def __init__( self, prefix: Arg.Binary(help='This data will be prepended to the input.') ): ... def process(self, data): return self.args.prefix + data ``` Note that when the init of a unit has a return annotation that is a base class of itself, then all its parameters will automatically be forwarded to that base class. """ class delete: pass class omit: pass args: List[str] def __init__( self, *args: str, action : Union[omit, str] = omit, # noqa choices : Union[omit, Iterable[Any]] = omit, # noqa const : Union[omit, Any] = omit, # noqa default : Union[omit, Any] = omit, # noqa dest : Union[omit, str] = omit, # noqa help : Union[omit, str] = omit, # noqa metavar : Union[omit, str] = omit, # noqa nargs : Union[omit, int, str] = omit, # noqa required : Union[omit, bool] = omit, # noqa type : Union[omit, type] = omit, # noqa group : Optional[str] = None, # noqa guessed : Optional[Set[str]] = None, # noqa ) -> None: kwargs = dict(action=action, choices=choices, const=const, default=default, dest=dest, help=help, metavar=metavar, nargs=nargs, required=required, type=type) kwargs = {key: value for key, value in kwargs.items() if value is not Arg.omit} self.group = group self.guessed = set(guessed or ()) super().__init__(*args, **kwargs) def update_help(self): """ This method is called to format the help text of the argument retroactively. The primary purpose is to fill in default arguments via the formatting symbol `{default}`. These default values are not necessarily part of the `refinery.units.Arg` object itself: They may be a default value in the `__init__` function of the `refinery.units.Unit` subclass. Therefore, it is necessary to format the help text after all information has been compiled. """ class formatting(dict): arg = self def __missing__(self, key): if key == 'choices': return ', '.join(self.arg.kwargs['choices']) if key == 'default': default: Union[bytes, int, str, slice] = self.arg.kwargs['default'] if isinstance(default, (list, tuple, set)): if not default: return 'empty' elif len(default) == 1: default = default[0] if isinstance(default, slice): parts = [default.start or '', default.stop or '', default.step] default = ':'.join(str(x) for x in parts if x is not None) if isinstance(default, int): return default if not isbuffer(default): return default if default.isalnum(): return default.decode('latin-1') return F'H:{default.hex()}' if key == 'varname': return self.arg.kwargs.get('metavar', self.arg.destination) try: help_string: str = self.kwargs['help'] self.kwargs.update( help=help_string.format_map(formatting())) except Exception: pass def __rmatmul__(self, method): self.update_help() return super().__rmatmul__(method) @staticmethod def AsOption(value: Optional[Any], cls: Enum) -> Enum: """ This method converts the input `value` to an instance of the enum `cls`. It is intended to be used on values that are passed as an argument marked with the `refinery.units.Arg.Option` decorator. If the input value is `None` or already an instance of `cls`, it is returned unchanged. Otherwise, the function attempts to find an element of the enumeration that matches the input, either by name or by value. """ if value is None or isinstance(value, cls): return value if isinstance(value, str): try: return cls[value] except KeyError: pass needle = normalize_to_identifier(value).casefold() for item in cls.__members__: if not isinstance(item, str): break if item.casefold() == needle: return cls[item] try: return cls(value) except Exception as E: choices = ', '.join(normalize_to_display(m) for m in cls.__members__) raise ValueError(F'Could not transform {value} into {cls.__name__}; the choices are: {choices}') from E @classmethod def Delete(cls): """ This should be specified when the argument is present for a (potentially abstract) parent unit but should be removed on the child. """ return cls(nargs=cls.delete) @classmethod def Counts( cls, *args : str, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, group : Optional[str] = None, ): """ A convenience method to add argparse arguments that introduce a counter. """ return cls(*args, group=group, help=help, dest=dest, action='count') @classmethod def Switch( cls, *args : str, off=False, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, group : Optional[str] = None, ): """ A convenience method to add argparse arguments that change a boolean value from True to False or vice versa. By default, a switch will have a False default and change it to True when specified. """ return cls(*args, group=group, help=help, dest=dest, action='store_false' if off else 'store_true') @classmethod def Binary( cls, *args : str, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, nargs : Union[omit, int, str] = omit, metavar : Optional[str] = None, group : Optional[str] = None, ): """ Used to add argparse arguments that contain binary data. """ if metavar is None and any('-' in a for a in args): metavar = 'B' return cls(*args, group=group, help=help, dest=dest, nargs=nargs, type=multibin, metavar=metavar) @classmethod def String( cls, *args : str, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, nargs : Union[omit, int, str] = omit, metavar : Optional[str] = None, group : Optional[str] = None, ): """ Used to add argparse arguments that contain string data. """ if metavar is None and any('-' in a for a in args): metavar = 'STR' return cls(*args, group=group, help=help, dest=dest, nargs=nargs, type=str, metavar=metavar) @classmethod def RegExp( cls, *args : str, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, nargs : Union[omit, int, str] = omit, metavar : Optional[str] = None, group : Optional[str] = None, ): """ Used to add argparse arguments that contain a regular expression. """ if metavar is None and any('-' in a for a in args): metavar = 'REGEX' return cls(*args, group=group, help=help, dest=dest, nargs=nargs, type=regexp, metavar=metavar) @classmethod def NumSeq( cls, *args : str, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, nargs : Union[omit, int, str] = omit, metavar : Optional[str] = None, group : Optional[str] = None, ): """ Used to add argparse arguments that contain a numeric sequence. """ return cls(*args, group=group, help=help, nargs=nargs, dest=dest, type=numseq, metavar=metavar) @classmethod def Bounds( cls, *args : str, help : Optional[Union[omit, str]] = None, dest : Union[omit, str] = omit, nargs : Union[omit, int, str] = omit, default : Union[omit, Any] = omit, range : bool = False, metavar : Optional[str] = 'start:end:step', group : Optional[str] = None, ): """ Used to add argparse arguments that contain a slice. """ if help is None: help = 'Specify start:end:step in Python slice syntax.' if default is not cls.omit: help = F'{help} The default is {{default}}.' parser = slicerange if range else sliceobj return cls(*args, group=group, help=help, default=default, nargs=nargs, dest=dest, type=parser, metavar=metavar) @classmethod def Number( cls, *args : str, bound : Union[omit, Tuple[int, int]] = omit, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, metavar : Optional[str] = None, group : Optional[str] = None, ): """ Used to add argparse arguments that contain a number. """ nt = number if bound is not cls.omit: lower, upper = bound nt = nt[lower:upper] return cls(*args, group=group, help=help, dest=dest, type=nt, metavar=metavar or 'N') @classmethod def Option( cls, *args : str, choices : Enum, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, metavar : Optional[str] = None, group : Optional[str] = None, ): """ Used to add argparse arguments with a fixed set of options, based on an enumeration. """ cnames = [normalize_to_display(c).casefold() for c in choices.__members__] metavar = metavar or choices.__name__ return cls(*args, group=group, help=help, metavar=metavar, dest=dest, choices=cnames, type=str.casefold) @classmethod def Choice( cls, *args : str, choices : List[str], help : Union[omit, str] = omit, metavar : Union[omit, str] = omit, dest : Union[omit, str] = omit, type : Type = str, nargs : Union[omit, int, str] = omit, group : Optional[str] = None, ): """ Used to add argparse arguments with a fixed set of options, based on a list of strings. """ return cls(*args, group=group, type=type, metavar=metavar, nargs=nargs, dest=dest, help=help, choices=choices) @property def positional(self) -> bool: """ Indicates whether the argument is positional. This is crudely determined by whether it has a specifier that does not start with a dash. """ return any(a[0] != '-' for a in self.args) @property def destination(self) -> str: """ The name of the variable where the contents of this parsed argument will be stored. """ for a in self.args: if a[0] != '-': return a try: return self.kwargs['dest'] except KeyError: for a in self.args: if a.startswith('--'): dest = normalize_to_identifier(a) if dest.isidentifier(): return dest raise AttributeError(F'The argument with these values has no destination: {self!r}') @classmethod def Infer(cls, pt: inspect.Parameter, module: Optional[str] = None): """ This class method can be used to infer the argparse argument for a Python function parameter. This guess is based on the annotation, name, and default value. """ def needs_type(item: Dict[str, str]): try: return item['action'] == 'store' except KeyError: return True def get_argp_type(annotation_type): if issubclass(annotation_type, (bytes, bytearray, memoryview)): return multibin if issubclass(annotation_type, int): return number if issubclass(annotation_type, slice): return sliceobj return annotation_type name = normalize_to_display(pt.name, False) default = pt.default guessed_pos_args = [] guessed_kwd_args = dict(dest=pt.name) guessed = set() annotation = pt.annotation def guess(key, value): try: return guessed_kwd_args[key] except KeyError: guessed_kwd_args[key] = value guessed.add(key) return value if isinstance(annotation, str): symbols = None while symbols is not False: try: annotation = eval(annotation, symbols) except NameError: if symbols is not None or module is None: break try: import importlib symbols = importlib.import_module(module).__dict__ except Exception: symbols = False except Exception: pass else: break if annotation is not pt.empty: if isinstance(annotation, Arg): if annotation.kwargs.get('dest', pt.name) != pt.name: raise ValueError( F'Incompatible argument destination specified; parameter {pt.name} ' F'was annotated with {annotation!r}.') guessed_pos_args = annotation.args guessed_kwd_args.update(annotation.kwargs) guessed_kwd_args.update(group=annotation.group) elif isinstance(annotation, type): guessed.add('type') if not issubclass(annotation, bool) and needs_type(guessed_kwd_args): guessed_kwd_args.update(type=get_argp_type(annotation)) elif not isinstance(default, bool): raise ValueError('Default value for boolean arguments must be provided.') if not guessed_pos_args: guessed_pos_args = guessed_pos_args or [F'--{name}' if pt.kind is pt.KEYWORD_ONLY else name] if pt.kind is pt.VAR_POSITIONAL: oldnargs = guess('nargs', ZERO_OR_MORE) if oldnargs not in (ONE_OR_MORE, ZERO_OR_MORE, REMAINDER): raise ValueError(F'Variadic positional arguments has nargs set to {oldnargs!r}') return cls(*guessed_pos_args, **guessed_kwd_args) if default is not pt.empty: if isinstance(default, Enum): default = default.name if isinstance(default, (list, tuple)): guess('nargs', ZERO_OR_MORE) if not pt.default: default = pt.empty else: guessed_kwd_args['default'] = pt.default default = default[0] else: guessed_kwd_args['default'] = default if pt.kind is pt.POSITIONAL_ONLY: guess('nargs', OPTIONAL) if default is not pt.empty: if isinstance(default, bool): action = 'store_false' if default else 'store_true' guessed_kwd_args['action'] = action elif needs_type(guessed_kwd_args): guess('type', get_argp_type(type(default))) return cls(*guessed_pos_args, **guessed_kwd_args, guessed=guessed) def merge_args(self, them: Argument) -> None: """ Merge the `args` component of another `refinery.units.Argument` into this one without overwriting or removing any of the `args` in this instance. """ def iterboth(): yield from them.args yield from self.args if not self.args: self.args = list(them.args) return sflag = None lflag = None for a in iterboth(): if a[:2] == '--': lflag = lflag or a elif a[0] == '-': sflag = sflag or a self.args = [] if sflag: self.args.append(sflag) if lflag: self.args.append(lflag) if not self.args: self.args = list(them.args) def merge_all(self, them: Arg) -> None: """ Merge another `refinery.units.Arg` into the current instance. This is an additive process where no data on the present instance is destroyed unless `refinery.units.Arg.Delete` was used on `them` to explicitly remove an option. """ for key, value in them.kwargs.items(): if value is Arg.delete: self.kwargs.pop(key, None) self.guessed.discard(key) continue if key in them.guessed: if key not in self.guessed: if key == 'type' and self.kwargs.get('action', None) != 'store': continue if key in self.kwargs: continue self.guessed.add(key) self.kwargs[key] = value self.merge_args(them) self.group = them.group or self.group def __copy__(self) -> Argument: cls = self.__class__ clone = cls.__new__(cls) clone.kwargs = dict(self.kwargs) clone.args = list(self.args) clone.group = self.group clone.guessed = set(self.guessed) return clone def __repr__(self) -> str: return F'{self.__class__.__name__}({super().__repr__()})' def __call__(self, init: Callable) -> Callable: parameters = inspect.signature(init).parameters try: inferred = Arg.Infer(parameters[self.destination]) inferred.merge_all(self) init.__annotations__[self.destination] = inferred except KeyError: raise ValueError(F'Unable to decorate because no parameter with name {self.destination} exists.') return init
Ancestors
Class variables
var delete
var omit
Static methods
def AsOption(value, cls)
-
This method converts the input
value
to an instance of the enumcls
. It is intended to be used on values that are passed as an argument marked with theArg.Option()
decorator. If the input value isNone
or already an instance ofcls
, it is returned unchanged. Otherwise, the function attempts to find an element of the enumeration that matches the input, either by name or by value.Expand source code Browse git
@staticmethod def AsOption(value: Optional[Any], cls: Enum) -> Enum: """ This method converts the input `value` to an instance of the enum `cls`. It is intended to be used on values that are passed as an argument marked with the `refinery.units.Arg.Option` decorator. If the input value is `None` or already an instance of `cls`, it is returned unchanged. Otherwise, the function attempts to find an element of the enumeration that matches the input, either by name or by value. """ if value is None or isinstance(value, cls): return value if isinstance(value, str): try: return cls[value] except KeyError: pass needle = normalize_to_identifier(value).casefold() for item in cls.__members__: if not isinstance(item, str): break if item.casefold() == needle: return cls[item] try: return cls(value) except Exception as E: choices = ', '.join(normalize_to_display(m) for m in cls.__members__) raise ValueError(F'Could not transform {value} into {cls.__name__}; the choices are: {choices}') from E
def Delete()
-
This should be specified when the argument is present for a (potentially abstract) parent unit but should be removed on the child.
Expand source code Browse git
@classmethod def Delete(cls): """ This should be specified when the argument is present for a (potentially abstract) parent unit but should be removed on the child. """ return cls(nargs=cls.delete)
def Counts(*args, help=refinery.units.Arg.omit, dest=refinery.units.Arg.omit, group=None)
-
A convenience method to add argparse arguments that introduce a counter.
Expand source code Browse git
@classmethod def Counts( cls, *args : str, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, group : Optional[str] = None, ): """ A convenience method to add argparse arguments that introduce a counter. """ return cls(*args, group=group, help=help, dest=dest, action='count')
def Switch(*args, off=False, help=refinery.units.Arg.omit, dest=refinery.units.Arg.omit, group=None)
-
A convenience method to add argparse arguments that change a boolean value from True to False or vice versa. By default, a switch will have a False default and change it to True when specified.
Expand source code Browse git
@classmethod def Switch( cls, *args : str, off=False, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, group : Optional[str] = None, ): """ A convenience method to add argparse arguments that change a boolean value from True to False or vice versa. By default, a switch will have a False default and change it to True when specified. """ return cls(*args, group=group, help=help, dest=dest, action='store_false' if off else 'store_true')
def Binary(*args, help=refinery.units.Arg.omit, dest=refinery.units.Arg.omit, nargs=refinery.units.Arg.omit, metavar=None, group=None)
-
Used to add argparse arguments that contain binary data.
Expand source code Browse git
@classmethod def Binary( cls, *args : str, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, nargs : Union[omit, int, str] = omit, metavar : Optional[str] = None, group : Optional[str] = None, ): """ Used to add argparse arguments that contain binary data. """ if metavar is None and any('-' in a for a in args): metavar = 'B' return cls(*args, group=group, help=help, dest=dest, nargs=nargs, type=multibin, metavar=metavar)
def String(*args, help=refinery.units.Arg.omit, dest=refinery.units.Arg.omit, nargs=refinery.units.Arg.omit, metavar=None, group=None)
-
Used to add argparse arguments that contain string data.
Expand source code Browse git
@classmethod def String( cls, *args : str, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, nargs : Union[omit, int, str] = omit, metavar : Optional[str] = None, group : Optional[str] = None, ): """ Used to add argparse arguments that contain string data. """ if metavar is None and any('-' in a for a in args): metavar = 'STR' return cls(*args, group=group, help=help, dest=dest, nargs=nargs, type=str, metavar=metavar)
def RegExp(*args, help=refinery.units.Arg.omit, dest=refinery.units.Arg.omit, nargs=refinery.units.Arg.omit, metavar=None, group=None)
-
Used to add argparse arguments that contain a regular expression.
Expand source code Browse git
@classmethod def RegExp( cls, *args : str, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, nargs : Union[omit, int, str] = omit, metavar : Optional[str] = None, group : Optional[str] = None, ): """ Used to add argparse arguments that contain a regular expression. """ if metavar is None and any('-' in a for a in args): metavar = 'REGEX' return cls(*args, group=group, help=help, dest=dest, nargs=nargs, type=regexp, metavar=metavar)
def NumSeq(*args, help=refinery.units.Arg.omit, dest=refinery.units.Arg.omit, nargs=refinery.units.Arg.omit, metavar=None, group=None)
-
Used to add argparse arguments that contain a numeric sequence.
Expand source code Browse git
@classmethod def NumSeq( cls, *args : str, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, nargs : Union[omit, int, str] = omit, metavar : Optional[str] = None, group : Optional[str] = None, ): """ Used to add argparse arguments that contain a numeric sequence. """ return cls(*args, group=group, help=help, nargs=nargs, dest=dest, type=numseq, metavar=metavar)
def Bounds(*args, help=None, dest=refinery.units.Arg.omit, nargs=refinery.units.Arg.omit, default=refinery.units.Arg.omit, range=False, metavar='start:end:step', group=None)
-
Used to add argparse arguments that contain a slice.
Expand source code Browse git
@classmethod def Bounds( cls, *args : str, help : Optional[Union[omit, str]] = None, dest : Union[omit, str] = omit, nargs : Union[omit, int, str] = omit, default : Union[omit, Any] = omit, range : bool = False, metavar : Optional[str] = 'start:end:step', group : Optional[str] = None, ): """ Used to add argparse arguments that contain a slice. """ if help is None: help = 'Specify start:end:step in Python slice syntax.' if default is not cls.omit: help = F'{help} The default is {{default}}.' parser = slicerange if range else sliceobj return cls(*args, group=group, help=help, default=default, nargs=nargs, dest=dest, type=parser, metavar=metavar)
def Number(*args, bound=refinery.units.Arg.omit, help=refinery.units.Arg.omit, dest=refinery.units.Arg.omit, metavar=None, group=None)
-
Used to add argparse arguments that contain a number.
Expand source code Browse git
@classmethod def Number( cls, *args : str, bound : Union[omit, Tuple[int, int]] = omit, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, metavar : Optional[str] = None, group : Optional[str] = None, ): """ Used to add argparse arguments that contain a number. """ nt = number if bound is not cls.omit: lower, upper = bound nt = nt[lower:upper] return cls(*args, group=group, help=help, dest=dest, type=nt, metavar=metavar or 'N')
def Option(*args, choices, help=refinery.units.Arg.omit, dest=refinery.units.Arg.omit, metavar=None, group=None)
-
Used to add argparse arguments with a fixed set of options, based on an enumeration.
Expand source code Browse git
@classmethod def Option( cls, *args : str, choices : Enum, help : Union[omit, str] = omit, dest : Union[omit, str] = omit, metavar : Optional[str] = None, group : Optional[str] = None, ): """ Used to add argparse arguments with a fixed set of options, based on an enumeration. """ cnames = [normalize_to_display(c).casefold() for c in choices.__members__] metavar = metavar or choices.__name__ return cls(*args, group=group, help=help, metavar=metavar, dest=dest, choices=cnames, type=str.casefold)
def Choice(*args, choices, help=refinery.units.Arg.omit, metavar=refinery.units.Arg.omit, dest=refinery.units.Arg.omit, type=builtins.str, nargs=refinery.units.Arg.omit, group=None)
-
Used to add argparse arguments with a fixed set of options, based on a list of strings.
Expand source code Browse git
@classmethod def Choice( cls, *args : str, choices : List[str], help : Union[omit, str] = omit, metavar : Union[omit, str] = omit, dest : Union[omit, str] = omit, type : Type = str, nargs : Union[omit, int, str] = omit, group : Optional[str] = None, ): """ Used to add argparse arguments with a fixed set of options, based on a list of strings. """ return cls(*args, group=group, type=type, metavar=metavar, nargs=nargs, dest=dest, help=help, choices=choices)
def Infer(pt, module=None)
-
This class method can be used to infer the argparse argument for a Python function parameter. This guess is based on the annotation, name, and default value.
Expand source code Browse git
@classmethod def Infer(cls, pt: inspect.Parameter, module: Optional[str] = None): """ This class method can be used to infer the argparse argument for a Python function parameter. This guess is based on the annotation, name, and default value. """ def needs_type(item: Dict[str, str]): try: return item['action'] == 'store' except KeyError: return True def get_argp_type(annotation_type): if issubclass(annotation_type, (bytes, bytearray, memoryview)): return multibin if issubclass(annotation_type, int): return number if issubclass(annotation_type, slice): return sliceobj return annotation_type name = normalize_to_display(pt.name, False) default = pt.default guessed_pos_args = [] guessed_kwd_args = dict(dest=pt.name) guessed = set() annotation = pt.annotation def guess(key, value): try: return guessed_kwd_args[key] except KeyError: guessed_kwd_args[key] = value guessed.add(key) return value if isinstance(annotation, str): symbols = None while symbols is not False: try: annotation = eval(annotation, symbols) except NameError: if symbols is not None or module is None: break try: import importlib symbols = importlib.import_module(module).__dict__ except Exception: symbols = False except Exception: pass else: break if annotation is not pt.empty: if isinstance(annotation, Arg): if annotation.kwargs.get('dest', pt.name) != pt.name: raise ValueError( F'Incompatible argument destination specified; parameter {pt.name} ' F'was annotated with {annotation!r}.') guessed_pos_args = annotation.args guessed_kwd_args.update(annotation.kwargs) guessed_kwd_args.update(group=annotation.group) elif isinstance(annotation, type): guessed.add('type') if not issubclass(annotation, bool) and needs_type(guessed_kwd_args): guessed_kwd_args.update(type=get_argp_type(annotation)) elif not isinstance(default, bool): raise ValueError('Default value for boolean arguments must be provided.') if not guessed_pos_args: guessed_pos_args = guessed_pos_args or [F'--{name}' if pt.kind is pt.KEYWORD_ONLY else name] if pt.kind is pt.VAR_POSITIONAL: oldnargs = guess('nargs', ZERO_OR_MORE) if oldnargs not in (ONE_OR_MORE, ZERO_OR_MORE, REMAINDER): raise ValueError(F'Variadic positional arguments has nargs set to {oldnargs!r}') return cls(*guessed_pos_args, **guessed_kwd_args) if default is not pt.empty: if isinstance(default, Enum): default = default.name if isinstance(default, (list, tuple)): guess('nargs', ZERO_OR_MORE) if not pt.default: default = pt.empty else: guessed_kwd_args['default'] = pt.default default = default[0] else: guessed_kwd_args['default'] = default if pt.kind is pt.POSITIONAL_ONLY: guess('nargs', OPTIONAL) if default is not pt.empty: if isinstance(default, bool): action = 'store_false' if default else 'store_true' guessed_kwd_args['action'] = action elif needs_type(guessed_kwd_args): guess('type', get_argp_type(type(default))) return cls(*guessed_pos_args, **guessed_kwd_args, guessed=guessed)
Instance variables
var positional
-
Indicates whether the argument is positional. This is crudely determined by whether it has a specifier that does not start with a dash.
Expand source code Browse git
@property def positional(self) -> bool: """ Indicates whether the argument is positional. This is crudely determined by whether it has a specifier that does not start with a dash. """ return any(a[0] != '-' for a in self.args)
var destination
-
The name of the variable where the contents of this parsed argument will be stored.
Expand source code Browse git
@property def destination(self) -> str: """ The name of the variable where the contents of this parsed argument will be stored. """ for a in self.args: if a[0] != '-': return a try: return self.kwargs['dest'] except KeyError: for a in self.args: if a.startswith('--'): dest = normalize_to_identifier(a) if dest.isidentifier(): return dest raise AttributeError(F'The argument with these values has no destination: {self!r}')
Methods
def update_help(self)
-
This method is called to format the help text of the argument retroactively. The primary purpose is to fill in default arguments via the formatting symbol
{default}
. These default values are not necessarily part of theArg
object itself: They may be a default value in the__init__
function of theUnit
subclass. Therefore, it is necessary to format the help text after all information has been compiled.Expand source code Browse git
def update_help(self): """ This method is called to format the help text of the argument retroactively. The primary purpose is to fill in default arguments via the formatting symbol `{default}`. These default values are not necessarily part of the `refinery.units.Arg` object itself: They may be a default value in the `__init__` function of the `refinery.units.Unit` subclass. Therefore, it is necessary to format the help text after all information has been compiled. """ class formatting(dict): arg = self def __missing__(self, key): if key == 'choices': return ', '.join(self.arg.kwargs['choices']) if key == 'default': default: Union[bytes, int, str, slice] = self.arg.kwargs['default'] if isinstance(default, (list, tuple, set)): if not default: return 'empty' elif len(default) == 1: default = default[0] if isinstance(default, slice): parts = [default.start or '', default.stop or '', default.step] default = ':'.join(str(x) for x in parts if x is not None) if isinstance(default, int): return default if not isbuffer(default): return default if default.isalnum(): return default.decode('latin-1') return F'H:{default.hex()}' if key == 'varname': return self.arg.kwargs.get('metavar', self.arg.destination) try: help_string: str = self.kwargs['help'] self.kwargs.update( help=help_string.format_map(formatting())) except Exception: pass
def merge_args(self, them)
-
Merge the
args
component of anotherArgument
into this one without overwriting or removing any of theargs
in this instance.Expand source code Browse git
def merge_args(self, them: Argument) -> None: """ Merge the `args` component of another `refinery.units.Argument` into this one without overwriting or removing any of the `args` in this instance. """ def iterboth(): yield from them.args yield from self.args if not self.args: self.args = list(them.args) return sflag = None lflag = None for a in iterboth(): if a[:2] == '--': lflag = lflag or a elif a[0] == '-': sflag = sflag or a self.args = [] if sflag: self.args.append(sflag) if lflag: self.args.append(lflag) if not self.args: self.args = list(them.args)
def merge_all(self, them)
-
Merge another
Arg
into the current instance. This is an additive process where no data on the present instance is destroyed unlessArg.Delete()
was used onthem
to explicitly remove an option.Expand source code Browse git
def merge_all(self, them: Arg) -> None: """ Merge another `refinery.units.Arg` into the current instance. This is an additive process where no data on the present instance is destroyed unless `refinery.units.Arg.Delete` was used on `them` to explicitly remove an option. """ for key, value in them.kwargs.items(): if value is Arg.delete: self.kwargs.pop(key, None) self.guessed.discard(key) continue if key in them.guessed: if key not in self.guessed: if key == 'type' and self.kwargs.get('action', None) != 'store': continue if key in self.kwargs: continue self.guessed.add(key) self.kwargs[key] = value self.merge_args(them) self.group = them.group or self.group
Inherited members
class HASH (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code Browse git
class HASH(str, Enum): MD2 = 'MD2' MD4 = 'MD4' MD5 = 'MD5' SHA1 = 'SHA' SHA256 = 'SHA256' SHA512 = 'SHA512' SHA224 = 'SHA224' SHA384 = 'SHA384'
Ancestors
- builtins.str
- enum.Enum
Class variables
var MD2
var MD4
var MD5
var SHA1
var SHA256
var SHA512
var SHA224
var SHA384
class KeyDerivation (size, salt, hash=None, iter=None, **kw)
-
Expand source code Browse git
class KeyDerivation(Unit, abstract=True): def __init__( self, size: Arg(help='The number of bytes to generate.', type=number), salt: Arg(help='Salt for the derivation.'), hash: Arg.Option(choices=HASH, metavar='hash', help='Specify one of these algorithms (default is {default}): {choices}') = None, iter: Arg.Number(metavar='iter', help='Number of iterations; default is {default}.') = None, **kw ): if hash is not None: hash = Arg.AsOption(hash, HASH) return super().__init__(salt=salt, size=size, iter=iter, hash=hash, **kw) @property def hash(self) -> _HashModule: name = self.args.hash.value hash = importlib.import_module(F'Cryptodome.Hash.{name}') return hash
Ancestors
Subclasses
Class variables
var required_dependencies
var optional_dependencies
Instance variables
var hash
-
Expand source code Browse git
@property def hash(self) -> _HashModule: name = self.args.hash.value hash = importlib.import_module(F'Cryptodome.Hash.{name}') return hash
Inherited members