Package: rsg
RSG - Random Struct GeneratorRsgis a tool that helps creating randomly generated nested data structures, from
simple built-in structures as lists, dictionaries and tuples, to custom composite class
hierarchies.In its current state, this library is merely a toy project with a few basic utilities,
created with the intent of experimenting with python decorators and language inspection.
It was originally a function that generated random yaml files for testing purposes,
that quickly grew to become a generic subpackage that had no dependencies and could be
exported as a separate python package.BasicsGenerators are subclasses ofRsgwith special "generator" methods:classRsgFooBar(Rsg):@generator("foo")defgenerate_foo(self):return"foo"@generator("bar")defgenerate_bar(self):return"bar"This class genertes either"foo"or"bar"with equal chance:rsg=RsgFooBar()[print(next(rsg))for_inrange(10)]# bar foo foo bar foo foo bar bar foo fooYou can set a custom chance for each generator method, by setting the keyword agument
named<METHOD_NAME>_chancein the object constructor. Probabilities are normalized
automatically, so you don't need to ensure they sum up to one.rsg=RsgFooBar(foo_chance=0.8,bar_chance=0.2)[print(next(rsg))for_inrange(10)]# foo foo bar foo foo foo foo foo foo barChances values all default to 1.0, you can set a custom default value when decorating
a generator method. Default values are used only when a chance value is not specified
in the object constructor.@generator("foo",default_chance=0.2)ParametersWhat if a generator method needs parameters? In this example we want to make a generator
of random powers of a number (a parameter), with the maximum exponent bounded by anohter
parameter.To do so, just add any amount of parameters to a generator method, you can use both
positional and keyword arguments, without the need to implement a custom constructor
for your class.classRsgPowers(Rsg):@generator("power")defgenerate_power(self,base:int,max_exp:int=5):exp=randint(0,max_exp)returnbase**expTo set the declared parameters, just pass them to the class constructor, as if you had
actually implemented it!rsg=RsgPowers(base=2,max_exp=10)[print(next(rsg))for_inrange(10)]# 512 1024 16 128 32 1024 64 8 8 1In the previous example, the parametermax_exphas a default value of5, this means
that you don't have to explicitly set it in the object constructor. On the other hand,
thebaseparameter doesn't have a default value, and so it must be set explicitly.Composite StructuresA generator method can be of two types:Leaf - Objects with no children, like the ones used so far.Composite - Objects with randomly generated children, which in turn can have children recursively.Let's make a generator of lists-of-lists-of-lists-of-l...classRsgList(Rsg):@generator("list")defgenerate_list(self,children:Iterable[Any])->list:returnlist(children)To make it recursive, you just need to add an argument namedchildrento the method.
This argument will contain an iterable of N randomly generated children objects.
The children objects are generated with the same generator used for the parent object.Recusion is governed by the following parameters:min_depth- The minimum number of recursions before a leaf object can be generated.
Ignored if no composite generators are available.max_depth- The maximum number of recursions. Must be >=min_depth.min_breadth- The minimum number of children per recursion step. If no leaf
generators are available, on the last recursion stepmin_breadthis forced to 0.max_breadth- The maximum number of children per recursion step.rsg=RsgList(min_depth=1,max_depth=3,min_breadth=2,max_breadth=4)print(next(rsg))# [[[[], []], [[], []], [[], []], [[], []]], [[[], [], [], []], [[], []], [[], []], [[], []]]]MixinYou can combine anyRsgby simply inheriting from them:classRsgMixed(RsgFooBar,RsgList,RsgPowers):passYou can still set all parameters of the base classes, remember to set the required ones,
or otherwise you will get anAttributeError!rsg=RsgMixed(base=3,foo_chance=0.1,bar_chance=0.1,power_chance=0.4,list_chance=0.4,min_depth=2,max_depth=3,min_breadth=2,max_breadth=4,)print(next(rsg))# [[[9, 243, 'foo', 'bar'], 27, ['foo', 3]], ['foo', [243, 81, 243]], ['foo', 243]]Built-in GeneratorsRsg provides generators for the following built-in types:int-rsg.core.RsgIntgenerates uniform integer values.float-rsg.core.RsgFloatgenerates uniform float values.str-rsg.core.RsgStrgenerates random strings containing a uniform random number
of letters, digits and punctuations.list-rsg.core.RsgListgenerates nested lists.tuple-rsg.core.RsgTuplegenerates nested tuples.dict-rsg.core.RsgDictgenerates nested dicts with valid python identifiers
as keys.In addition,rsg.core.RsgBasecombines all previous generators into one:fromrsg.coreimportRsgBasersg=RsgBase(min_depth=1,max_depth=4,min_breadth=1,max_breadth=3)print(next(rsg))# [#     ((0.73354,), (0.12835, '[^U`zq')),#     [643, ["QiX0@'", [0.95934, 0.27090, 0.66838]]],#     [{'N_dHLJUZA': 0.00379, 'D0N0uK1_eH': [667, 0.29715], 'SjWKcs_D': {'IMUUZH': 630}}]# ]