class StrRange(Range): _attrs = dict(lb=Attr(int, 0x20, doc='The lower bound'), ub=Attr(int, 0x7e, doc='The upper bound')) _opts = dict(coerce_args=False) @init_hook def _ensure_ints(self): if isinstance(self.lb, STR): self.lb = ord(self.lb) if isinstance(self.ub, STR): self.ub = ord(self.ub) def display(self, **kwargs): return '[{}, {}]'.format(unichr(self.lb), unichr(self.ub)) def hasmember(self, other): return super(StrRange, self).hasmember(ord(other)) def sample(self, **kwargs): ret = super(StrRange, self).sample() return unichr(ret) def enumerate(self, **kwargs): for item in super(StrRange, self).enumerate(**kwargs): yield unichr(item) def to_set(self, **kwargs): return super(Range, self).to_set(**kwargs)
class Problem(Base): _attrs = dict(constraints=Attr(List(Constraint)), domain=Attr(Domain)) _opts = dict(init_validate=True, args=('domain', 'constraints')) @init_hook def _init(self): self.domain = self.domain.copy() self.var_constraint = defaultdict(set) for con in self.constraints: con.preprocess(self.domain) for var in con.args: self.var_constraint[var].add(con) def check(self, theory): for con in self.constraints: if set(con.args) <= set(theory): if not con.check(**theory): return False return True def display(self, **kwargs): strs = [self.domain.display(**kwargs)] for con in self.constraints: s = con.display(**kwargs) if s: strs.append(s) return '\n'.join(strs) def validate(self): super(Problem, self).validate() if not set(self.var_constraint).issubset(set(self.domain)): raise ValueError('Some constraints defined in over ' 'undefined variables')
class Context(Base): context_name = None required_opts = () _attrs = dict(inside = Attr(STR, ''), envvars = Attr(Dict(STR), init=lambda self: dict()), opts = Attr(dict, init=lambda self: dict()), _skip_validation = Attr(bool, False, internal=True)) _opts = dict(init_validate = True) @classmethod @create_hook def _register(cls): if cls.context_name: CONTEXT_REGISTRY[cls.context_name] = cls @classmethod def from_yaml(cls, name, dct): kwargs = {} kwargs['envvars'] = dct.get('env', {}) kwargs['opts'] = dct.get('opts', {}) kwargs['inside'] = dct.get('inside', '') # TODO: if invalid context specified, raise ValidationError cls_ = CONTEXT_REGISTRY.get(dct.get('instanceof', 'null'), cls) return cls_(**kwargs) def resolve_macros(self, env, **kwargs): envvars = dict(self.envvars) for key , value in self.envvars.items(): envvars[key] = env.resolve(value) opts = dict(self.opts) for key, value in self.opts.items(): if isinstance(value, STR): opts[key] = env.resolve(value) return envvars, opts def run_command(self, command, env, **kwargs): if self.inside: ctx = env.contexts[self.inside] command = ctx.run_command(command, env, **kwargs) return command run = None def validate(self): super(Context, self).validate() if self._skip_validation: return for opt in self.required_opts: if opt not in self.opts: raise ValidationError('Required option "{}" not defined' .format(opt))
class SpecBase(Base): _attrs = dict( type=Attr(type, doc='The type of this argument'), name=Attr(STR, doc='The name of this argument'), required=Attr( bool, False, doc='If true, will throw an error if argument is not present')) _opts = dict(init_validate=True) def find_value(self, args, kwargs): raise NotImplementedError
class Operation(ListWrapper): '''Representation of a system operation.''' _attrs = dict(order=Attr(int, doc='An integer specifying the ' 'order in which to perform the operation ' '(smaller values are performed earlier)'), repetitions=Attr(int, 0, doc='Number of times to repeat ' 'the operation')) _opts = dict(init_validate=True) order_offset = 0 @init_hook def _adjust_order(self): self.order += self.order_offset @classmethod def optimize(cls, ops): ops.sort(key=attrgetter('order')) k = 0 while k < len(ops) - 1: ops[k].reduce(ListView(ops, k + 1, -1)) k += 1 def combine(self, other): '''Combine with another operation to increase execution efficiency.''' raise NotImplementedError def execute(self): '''Execute the operation on the system''' raise NotImplementedError def _reduce_single(self, op, ops): self.combine(op) def reduce(self, ops): '''Reduce a list of operations for optimizing total execution''' N = len(ops) while ops: op = ops[0] if not same_lineage(self, op): break if not self.order == op.order: break self._reduce_single(op, ops) if N == len(ops): break N = len(ops)
class MatchFailure(Base): _attrs = dict(message=Attr(STR, doc='Reason for failure'), seq=Attr(IterableList, doc='The sequence that failed to match'), fails=OAttr(list, doc='List of sub-failures')) _opts = dict(init_validate=True) # So that "if pattern.match(...):" can be used def __nonzero__(self): return False def __bool__(self): return False
class Logger(Base): _attrs = dict(logging_enabled = Attr(bool, True), decoration_enabled = Attr(bool, True), events = Attr(dict, internal=True) ) @init_hook def _init(self): if not hasattr(self, 'events'): self.events = defaultdict(list) def disable(self): self.logging_enabled = False def enable(self): self.logging_enabled = True def log_decorator(self, spec, before=True, after=False, name=None, log_return=True): def decorator(f): if not self.decoration_enabled: return f event_type_name = name if name is None: event_type_name = f.__name__ event_type = FunctionCall(spec) @wraps(f) def func(*args, **kwargs): if self.logging_enabled: event = event_type(args, kwargs) ret = f(*args, **kwargs) if self.logging_enabled: if log_return: event.return_value = ret self.record_event(event_type_name, event) return ret return func return decorator def log_event(self): pass def log(self, *args, **kwargs): # Determine what the context is from args and kwargs, then call log_decorator or log_event # But this also needs to be fast, so strike a balance between magic and speed return self.log_decorator(*args, **kwargs) def record_event(self, name, event): self.events[name].append(event)
class Command(Base): _attrs = dict(command = Attr(STR), context = Attr(STR, '')) # string used to look up context object in env _opts = dict(init_validate = True, args = ('command',)) def resolve_macros(self, env, **kwargs): command = env.resolve(self.command) context = env.resolve(kwargs.get('context', self.context)) return command, context def run(self, env, **kwargs): verbose = kwargs.get('verbose', False) preview = kwargs.get('preview', False) # Called from Task.run_preview() run_preview = kwargs.get('run_preview', False) silent = kwargs.get('silent', env.settings.get('silent', False)) pre = '' if preview and not run_preview: pre = kwargs.get('preview_pre', '') command, context_name = self.resolve_macros(env, **kwargs) if not context_name: context = env.default_context else: context = env.contexts[context_name] if callable(context.run): return context.run(command, env, **kwargs) cmd = context.run_command(command, env, **kwargs) if verbose: sys.stdout.write(pre + cmd + '\n') sys.stdout.flush() if not preview: args = shlex.split(cmd) if args[0] == 'yatr': try: from .main import _main _main(*args[1:]) return 0 except Exception as e: eprint(message(e)) return 1 return command_(cmd, silent)
class Pip(Dependency): '''Representation of a pip dependency''' key = 'pip' order = 30 _attrs = dict(order=Attr(int, order)) @classmethod @create_hook def _populate_pkgs(cls): if not cls._pkgs: try: pkgs = output('pip freeze') cls._pkgs = dict([ tuple(line.split('==')) for line in pkgs.split('\n') if line ]) except OSError: pass def satisfy(self): inst = [Install(self.name, order=self.order)] instver = [ Install('{}=={}'.format(self.name, self.version.rhs), order=self.order) ] if self.always_upgrade: return inst if not self.check(): if isinstance(self.version, (Eq, Le)): return instver return inst return []
class TypeWrapper(SetLeaf): '''The idea is that a type implicitly represents the set of all of its valid instances. ''' _attrs = dict(type=Attr(Type, doc='')) _opts = dict(args=('type', ), coerce_args=False) @init_hook def _convert_type(self): if not isinstance(self.type, Type): self.type = Type.dispatch(self.type) def display(self, **kwargs): return 'TypeWrapper({})'.format(self.type.display()) def size(self): return float('inf') def hasmember(self, item): return self.type.query(item) def sample(self, **kwargs): return self.type.generate(**kwargs) def enumerate(self, **kwargs): args = Args(**kwargs) maxenum = min(args.max_enumerate, args.type_enumerate) for k in range(maxenum): item = self.sample(**kwargs) yield item def to_set(self, **kwargs): return super(SetLeaf, self).to_set(**kwargs)
class Relation(Base): _attrs = dict( rhs=Attr(str, default='', doc='The value on the right hand side')) _opts = dict(init_validate=True, make_hashable=True, args=('rhs', )) func = None repr = None def __call__(self, value): if self.func is not None: return self.func(str(value), self.rhs) return True @classmethod @create_hook def _register(cls): if cls.repr is not None: RELATION_CACHE[cls.repr] = cls @classmethod def dispatch(cls, s): # Make sure the longer reprs get tested first for key, rel in sorted(RELATION_CACHE.items(), key=lambda x: -len(x[0])): if key in s: name, rhs = s.split(key) return rel(rhs.strip(), name=name.strip()) return cls('', name=s) def emit(self): repr = self.repr if self.repr else '' return repr + self.rhs
class Positional(Argument): _attrs = dict(repeatable=Attr(bool, False, ('If true, multiple values can' ' be supplied'))) def __init__(self, *args, **kwargs): super(Positional, self).__init__(*args, **kwargs) if not self.repeatable: self.quote = 'multiple'
class BinaryOption(Option): _attrs = dict(value=Attr(bool, False, 'Value of the option')) def render(self, value): out = '' if value: out = self.name return out
class SetWrapper(SetLeaf): _attrs = dict(set=Attr(set, doc='')) _opts = dict(args=('set', )) def display(self, **kwargs): return rstr(self.set) def size(self): return len(self.set) @_set_wrapper def union(self, *args): ret = reduce(set.union, (self.set, ) + args) return type(self)(ret) @_set_wrapper def intersection(self, *args): ret = reduce(set.intersection, (self.set, ) + args) return type(self)(ret) @_set_wrapper def difference(self, other): ret = self.set.difference(other) return type(self)(ret) @_set_wrapper def complement(self, universe): ret = universe.difference(self.set) return type(self)(ret) @_set_wrapper def issubset(self, other): return self.set.issubset(other) @_set_wrapper def issuperset(self, other): return self.set.issuperset(other) def hasmember(self, item): return item in self.set def sample(self, **kwargs): ret = choice(list(self.set)) return ret def enumerate(self, **kwargs): args = Args(**kwargs) maxenum = args.max_enumerate for k, item in enumerate(self.set): if k >= maxenum: break yield item def to_set(self, **kwargs): return set(self.set)
class Yatr(Dependency): key = 'yatr' order = 99999 _attrs = dict(order=Attr(int, order), yatrfile=Attr(STR, '')) def check(self): '''Tasks will always run.''' return False def installed(self): return False def satisfy(self): args = [self.name] # self.name is the task name + args (optional) if self.yatrfile: args.insert(0, '-f {}'.format(self.yatrfile)) task = Task(*args, order=self.order) return [task]
class Constraint(Base): _attrs = dict(args=Attr(Sequence, init=lambda self: tuple())) _opts = dict(init_validate=True, args=('args', ), make_hashable=True) def check(self, **kwargs): raise NotImplementedError def display(self, **kwargs): pass def preprocess(self, domain, **kwargs): pass
class Arg(SpecBase): '''Descriptor of a positional argument.''' _attrs = dict(index=Attr(int, doc='The index of this argument in *args')) _opts = dict(args=('name', 'index', 'type')) def find_value(self, args, kwargs): if len(args) <= self.index: if self.required: raise ValidationError('Positional argument (index={}) ' 'not present'.format(self.index)) return return args[self.index]
class ClassWrapper(SetLeaf): '''The idea is that a type implicitly represents the set of all of its subclasses, including itself. ''' _attrs = dict(type=Attr(type, doc=''), subclasses=Attr(List(type), doc='', init=lambda self: ([self.type] + subclasses(self.type)))) _opts = dict(args=('type', )) def __init__(self, *args, **kwargs): super(ClassWrapper, self).__init__(*args, **kwargs) self.subclasses = [self.type] + subclasses(self.type) def display(self, **kwargs): return 'ClassWrapper({})'.format(get_typename(self.type)) def size(self): return len(self.subclasses) def hasmember(self, item): return item in self.subclasses def sample(self, **kwargs): ret = choice(self.subclasses) return ret def enumerate(self, **kwargs): args = Args(**kwargs) maxenum = min(args.max_enumerate, len(self.subclasses)) for k, item in enumerate(self.subclasses): if k >= maxenum: break yield item def to_set(self, **kwargs): return super(SetLeaf, self).to_set(**kwargs)
class Option(Argument): _opts = dict(args=('name', 'aliases')) _attrs = dict( aliases=Attr(list, doc='Other strings denoting the option', optional=True), use_eq=Attr(bool, False, 'Use name=value syntax'), interleave=Attr(bool, True, ('Repeat the option for ' 'multiple values')), ) def __init__(self, *args, **kwargs): super(Option, self).__init__(*args, **kwargs) if self.aliases is None: self.aliases = [] else: self.aliases = list(self.aliases) def render(self, value): multiple = False if (isinstance(value, Iterable) and not self.single_value and not isinstance(value, STR)): multiple = True prefix = self.name if self.use_eq: prefix += '=' else: prefix += ' ' if multiple and self.interleave: strs = [super(Option, self).render(val) for val in value] else: strs = [super(Option, self).render(value)] out = ' '.join(prefix + s for s in strs) return out
class Domain(Base): _attrs = dict(vars=Attr(Mapping(SetNode), init=lambda self: dict())) _opts = dict(args=('vars', )) def __init__(self, *args, **kwargs): if not args and kwargs and not 'vars' in kwargs: kwargs = dict(vars=kwargs) if 'vars' in kwargs: for key in kwargs['vars']: value = kwargs['vars'][key] if not isinstance(value, SetNode): if isinstance(value, type): kwargs['vars'][key] = TypeWrapper(value) else: kwargs['vars'][key] = SetWrapper(value) super(Domain, self).__init__(*args, **kwargs) def __delitem__(self, key): del self.vars[key] def __getitem__(self, key): return self.vars[key] def __iter__(self): return iter(self.vars) def __len__(self): return len(self.vars) def __setitem__(self, key, value): if not isinstance(value, SetNode): if isinstance(value, type): self.vars[key] = TypeWrapper(value) else: self.vars[key] = SetWrapper(value) def copy(self, *args, **kwargs): return type(self)(self.vars.copy(*args, **kwargs)) def display(self, **kwargs): ret = 'Domain(' strs = [ '{} = {}'.format(var, vals.display(**kwargs)) for var, vals in sorted(self.vars.items(), key=itemgetter(0)) ] ret += ',\n '.join(strs) + ')' return ret
class Kwarg(SpecBase): '''Descriptor of a keyword-only argument.''' _attrs = dict(key=Attr(STR, doc='The key of this argument in **kwargs')) _opts = dict(args=('key', 'type')) @init_hook def _init(self): if not hasattr(self, 'name'): self.name = self.key def find_value(self, args, kwargs): if self.key not in kwargs: if self.required: raise ValidationError('Keyword argument (key={}) ' 'not present'.format(self.key)) return return kwargs[self.key]
class SchemaNode(Node): _aliases = dict(_list=['elems']) _attrs = dict(set=Attr(SetNode, optional=True, internal=True, doc='Internal set representation')) _opts = dict(optional_none=True) def __init__(self, *args, **kwargs): lst = [] for arg in args: if isinstance(arg, SchemaNode): lst.append(arg) elif isinstance(arg, (type, Type_)): lst.append(Type(arg)) elif isinstance(arg, SetNode): lst.append(Set(arg)) elif isinstance(arg, SET) or is_proper_sequence(arg): lst.append(Set(SetWrapper(arg))) else: lst.append(Set(SetWrapper([arg]))) # Create a singleton super(SchemaNode, self).__init__(*lst, **kwargs)
class Apt(Dependency): '''Representation of an apt dependency''' key = 'apt' order = 10 _attrs = dict(order=Attr(int, order)) @classmethod @create_hook def _populate_pkgs(cls): if not cls._pkgs: try: lines = output('dpkg -l').split('\n') partss = [l.split() for l in lines[5:] if l] pkgs = [(p[1], p[2]) for p in partss if fnmatch(p[0], '?i')] cls._pkgs = dict(pkgs) except OSError: pass def satisfy(self): up = [Update(order=self.order)] inst = up + [Install(self.name, order=self.order)] instver = up + [ Install('{}={}'.format(self.name, self.version.rhs), order=self.order) ] down = [Remove(self.name, order=self.order)] + instver if self.always_upgrade: return inst if not self.check(): if isinstance(self.version, (Eq, Le)): if self.installed(): return down return instver return inst return []
class Argument(Base): _arglist = None _opts = dict(args=('name', ), coerce_args=True, optional_none=True, init_validate=True) _attrs = dict(sep=Attr(STR, ' ', 'Character used to separate multiple values'), quote=Attr(['always', 'multiple', 'never'], 'multiple', ('Values quoting policy - either always quote, ' 'only quote if there are multiple values, or ' 'never quote')), quote_elements=Attr(bool, False, ('Quote only the individual' 'parts of a multi-value')), quote_char=Attr(["'", '"', '\"'], '"', 'Character to use for quoting values'), single_value=Attr(bool, False, ('Interpret value as ' 'a single value')), name=Attr(STR, doc='The name of the argument')) def __init__(self, *args, **kwargs): super(Argument, self).__init__(*args, **kwargs) arglist = type(self)._arglist if arglist is not None: arglist.append(self) def render(self, value): if (isinstance(value, Iterable) and not self.single_value and not isinstance(value, STR)): if self.quote_elements: out = self.sep.join( quote(strf(val), self.quote_char) for val in value) else: out = self.sep.join(strf(val) for val in value) multiple = True else: out = strf(value) multiple = False if self.quote == 'always' or (self.quote == 'multiple' and multiple and not self.quote_elements): out = quote(out, self.quote_char) return out
class Task(Base): _attrs = dict(commands = Attr(List(Command)), condition = Attr(This, optional=True), loop = Attr(For, optional=True), args = Attr(List((STR, int)), init=lambda self: list()), kwargs = Attr(Dict((STR, int)), init=lambda self: dict()), condition_type = Attr(bool, True)) _opts = dict(init_validate = True, optional_none = True) @classmethod def from_yaml(cls, name, dct): if isinstance(dct, STR): return cls(commands=[Command(dct)]) elif List(STR).query(dct): cmds = [Command(s) for s in dct] return cls(commands=cmds) elif isinstance(dct, dict): if set(dct.keys()).issuperset({'command'}): ret = Task.from_yaml(name, dct['command']) if 'context' in dct: for cmd in ret.commands: cmd.context = dct['context'] if 'args' in dct: ret.args = dct['args'] if 'kwargs' in dct: ret.kwargs = dct['kwargs'] if 'for' in dct: ret.loop = For.from_yaml(dct['for']) if 'if' in dct: ret.condition = Task.from_yaml(name + '-if', dct['if']) ret.condition_type = True elif 'ifnot' in dct: ret.condition = Task.from_yaml(name + '-ifnot', dct['ifnot']) ret.condition_type = False ret.validate() return ret raise ValidationError('Invalid data for task: {}'.format(name)) def run(self, env, **kwargs): codes = [] looping = kwargs.get('looping', False) exit_on_error = kwargs.get('exit_on_error', env.settings.get('exit_on_error', True)) preview_conditionals = kwargs.get('preview_conditionals', env.settings.get('preview_conditionals', True)) if self.condition and not looping: if preview_conditionals: kwargs['preview_pre'] = 'if: ' if self.condition_type else 'ifnot: ' codes_ = self.condition.run(env, **kwargs) code = max(codes_) if (((self.condition_type is True and code != 0) or (self.condition_type is False and code == 0)) and code is not None): return [] if preview_conditionals: kwargs['preview_pre'] = '\t' if (self.args or self.kwargs) and not looping: env = env.copy(**kwargs) if self.args: for k, arg in enumerate(self.args): env.env['_{}'.format(k + 1)] = arg if self.kwargs: for name, value in self.kwargs.items(): env.env[name] = value if self.loop and not looping: n = 0 kwargs['looping'] = True for env_ in self.loop.loop(env, **kwargs): env_.env[env_.settings['loop_count_macro']] = n codes_ = self.run(env_, **kwargs) codes.extend(codes_) n += 1 return codes for cmd in self.commands: if cmd.command in env.tasks: codes_ = env.tasks[cmd.command].run(env, **kwargs) codes.extend(codes_) else: code = cmd.run(env, **kwargs) codes.append(code) if exit_on_error and any(c != 0 for c in codes if c is not None): break return codes def run_preview(self, env, **kwargs): kwargs['preview'] = True kwargs['verbose'] = True kwargs['run_preview'] = True with assign(sys, 'stdout', cStringIO()): self.run(env, **kwargs) ret = sys.stdout.getvalue() return ret
class For(Base): _attrs = dict(var = Attr((STR, List(STR)), doc='The loop variable(s)'), in_ = Attr((STR, List((STR, int, list))), doc='Name(s) of list macro(s) to loop over')) _opts = dict(init_validate = True, args = ('var', 'in_')) @classmethod def from_yaml(cls, dct): if isinstance(dct, STR): m = re.match(FOREX, dct) if m: return cls(var=m.groups()[0], in_=m.groups()[1]) else: raise ValidationError('Invalid for loop specifier: {}' .format(dct)) kwargs = {} get_delete(dct, kwargs, 'var', None) get_delete(dct, kwargs, 'in', None, 'in_') if dct: raise ValidationError('Invalid for keys: {}' .format(','.join(dct.keys()))) return cls(**kwargs) def resolve_macros(self, env, **kwargs): var = self.var in_ = self.in_ if not isinstance(var, list): var = [self.var] in_ = [self.in_] outs = [] for k, v in enumerate(var): name = in_[k] if isinstance(name, list): val = name else: val = env.env[name] if not isinstance(val, list): raise ValidationError('For loop "in" specifier must be name of ' 'list macro: {}'.format(in_[k])) outs.append(val) return var, outs def loop(self, env, **kwargs): var, in_ = self.resolve_macros(env, **kwargs) for tup in product(*in_): yld = env.copy(**kwargs) for name, val in zip(var, tup): yld.env[name] = val yield yld def validate(self): super(For, self).validate() if isinstance(self.var, list): if len(self.var) != len(self.in_): raise ValidationError('"var" and "in" lists must be same ' 'length')
class IstrTest(Base): _attrs = dict(a=Attr(int))
class Range(SetLeaf): _attrs = dict(lb=Attr(int, doc='The lower bound'), ub=Attr(int, doc='The upper bound')) _opts = dict(args=('lb', 'ub')) def display(self, **kwargs): return '[{}, {}]'.format(self.lb, self.ub) def size(self): return self.ub - self.lb + 1 def validate(self): super(Range, self).validate() if self.lb > self.ub: raise ValueError("Invalid interval bounds") def overlaps(self, other): if isinstance(other, type(self)): if self.lb <= other.lb <= self.ub: return True if other.lb <= self.lb <= other.ub: return True return False @classmethod def _union(cls, a, b): if a is NULL and b is NULL: return NULL if a is NULL: return b if b is NULL: return a if a.lb <= b.lb and b.ub >= a.ub: return cls(a.lb, b.ub) elif a.lb <= b.lb: return a elif b.lb <= a.lb and a.ub <= b.ub: return b else: return cls(b.lb, a.ub) def union(self, *args): if not args: return self, [] test = lambda item, accum: item.overlaps(accum) or accum is NULL return defer_reduce(type(self)._union, (self, ) + args, test, NULL) @classmethod def _intersection(cls, a, b): if a is NULL or b is NULL: return NULL if not a.overlaps(b): return NULL if a.lb <= b.lb and b.ub >= a.ub: return cls(b.lb, a.ub) elif a.lb <= b.lb: return b elif b.lb <= a.lb and a.ub <= b.ub: return a else: return cls(a.lb, b.ub) def intersection(self, *args): if not args: return self ret = reduce(type(self)._intersection, (self, ) + args) return ret def difference(self, other): if other is NULL: return self, None if not self.overlaps(other): return self, None a = self b = other cls = type(self) if a.lb < b.lb and b.ub >= a.ub: return cls(a.lb, b.lb - 1), None elif a.lb < b.lb: return cls(a.lb, b.lb - 1), cls(b.ub + 1, a.ub) elif b.lb < a.lb and a.ub < b.ub: return NULL, None else: return cls(b.ub + 1, a.ub), None def complement(self, universe): ret = universe.difference(self) return ret def issubset(self, other): return self.lb >= other.lb and self.ub <= other.ub def issuperset(self, other): return self.lb <= other.lb and self.ub >= other.ub def hasmember(self, other): return self.lb <= other <= self.ub def sample(self, **kwargs): ret = randint(self.lb, self.ub) return ret def enumerate(self, **kwargs): args = Args(**kwargs) maxenum = args.max_enumerate for k, item in enumerate(xrange(self.lb, self.ub + 1)): if k >= maxenum: break yield item def to_set(self, **kwargs): args = Args(**kwargs) N = self.ub - self.lb ub = self.lb + min(N, args.max_enumerate - 1) ret = set(range(self.lb, ub + 1)) return ret
class B(Vars): _opts = dict(init_validate=True) _attrs = dict(foo=Attr(STR))
class A(Vars): _opts = dict(init_validate=True) _attrs = dict(a=Attr(int), b=Attr(float), c=Attr(STR), d=Attr(STR, optional=True))