Using generators¶
To provide test data for your properties, pyqcy has a set of generators for all common types and use cases, including Python’s scalar types and collections. It is also easy to combine several generators into one  up to creating complex data structures on the fly.
Still, if those are not enough, you can always define your own generator.
This is especially handy for custom classes, as it enables you to write
properties that should be true for their instances.
To create a custom generator, simply define a function that returns
an appropriate random object and decorate it with the arbitrary()
decorator:
from pyqcy import *
@arbitrary(MyClass)
def my_class():
obj = MyClass()
obj.some_field = next(int_(min=0, max=1024))
obj.other_field = next(str_(max_length=64))
return obj
Now you can write properties which use the new generator:
@qc
def forbs_correctly(obj=MyClass):
assert obj.forb() >= obj.some_field * len(obj.other_field)
Because we have passed a type argument to arbitrary()
,
we can use our class name (MyClass
) in place of generator name
(my_class
)  although the latter is of course still possible.

pyqcy.arbitraries.
arbitrary
(type_=None)¶ Decorator to be applied on functions in order to turn them into generators of arbitrary (“random”) values of given type.
Parameters: type – Type of values generated by the function The
type_
argument is optional. If provided, objects returned by the function will be checked against this type. It will be also possible to use the type directly when defining properties.Examples:
from pyqcy import * @arbitrary(MyClass) def my_class(): return MyClass() @qc def my_class_works(obj=MyClass): assert obj.is_valid()
Builtin types¶
Most Python types are conveniently supported by pyqcy and generators for them are readily available. They should cover a vast majority of typical use cases.
Numeric types¶
Numeric types have parametrized generators that allow for setting desired range of produces values. But if we are fine with the defaults, we can simply use the types directly, as seen in this example:
@qc
def vec2d_length_is_positive(x=float, y=float):
return vec2d_len(x, y) >= 0.0

pyqcy.arbitraries.numbers.
int_
(min, max)¶ Generator for arbitrary integers.
By default, it generates values from the whole integer range supported by operating system; this can be adjusted using parameters.
Parameters:  min – A minimum value of integer to generate
 max – A maximum value of integer to generate

pyqcy.arbitraries.numbers.
float_
(min, max)¶ Generator for arbitrary floats.
Parameters:  min – A minimum value of float to generate
 max – A maximum value of float to generate

pyqcy.arbitraries.numbers.
complex_
(min_real, max_real, min_imag, max_imag)¶ Generator for arbitrary complex numbers of the builtin Python complex type.
Parameters for this generator allow for adjusting the rectangle on the complex plane where the values will come from.
Parameters:  min_real – A minimum value for real part of generated numbers
 max_real – A maximum value for real part of generated numbers
 min_imag – A minimum value for the imaginary part of generated numbers
 max_imag – A maximum value for the imaginary part of generated numbers
Strings¶
For creating arbitrary texts, pyqcy has two generators for ANSI and Unicode strings. You can specify what characters the generators should draw from, as well the minimum and maximum length of strings to generate.

pyqcy.arbitraries.strings.
str_
(of, min_length, max_length)¶ Generator for arbitrary strings.
Parameters for this generator allow for adjusting the length of resulting strings and the set of characters they are composed of.
Parameters:  of – Characters used to construct the strings. This can be either an iterable of characters (e.g. a string) or a generator that produces them.
 min_length – A minimum length of string to generate
 max_length – A maximum length of string to generate

pyqcy.arbitraries.strings.
unicode_
(of, min_length, max_length)¶ Generator for arbitrary Unicode strings.
Parameters for this generator allow for adjusting the length of resulting strings and the set of characters they are composed of.
Parameters:  of – Characters used to construct the strings. This can be either an iterable of characters (e.g. a string) or a generator that produces them.
 min_length – A minimum length of string to generate
 max_length – A maximum length of string to generate
Quite often you would also want to deal only with strings of certain form that matches the expected input of the code you are testing. In those cases it’s useful to specify a regular expression that autogenerated strings should match.

pyqcy.arbitraries.strings.
regex
(pattern)¶ Generator for strings matching a regular expression.
Parameters: pattern – A regular expression  either a compiled one (through re.compile()
) or a string pattern
Note
Currently the regex
reverser supports only a limited subset
of syntactic features offered by Python regular expressions.
For example, it doesn’t support negative matches on character sets
([^...]
) or backreferences to capture groups (\\1
, \\2
, etc.).
Tuples¶
Tuples can be produced by combining several generators together
through tuple_()
function. There are also handy shortcuts
for pairs, triplers and quadruples that consists of values
from the same source.

pyqcy.arbitraries.collections.
tuple_
(*generators, of, n)¶ Generator for arbitrary tuples.
The tuples are always of the same length but their values may come from different generators. There two ways to specify those generators  either enumerate them all:
tuple_(int_(min=0, max=255), str_(max_length=64))
or use
n
argument with a single generator to get uniform tuples:ip_addresses = tuple_(int_(min=0, max=255), n=4) ip_addresses = tuple_(of=int_(min=0, max=255), n=4)
Those two styles are mutually exclusive  only one can be used at a time.
Parameters:  of – Generator used to generate tuple values
 n – Tuple length

pyqcy.arbitraries.collections.
two
(of)¶ partial(func, *args, **keywords)  new function with partial application of the given arguments and keywords.
Collections¶
Lists and dictionaries can be generated by giving their minimum and maximum size, as well as a generator for their elements. For dictionaries, you can either specify a separate generators for keys and values, or a single generator that outputs 2element tuples.

pyqcy.arbitraries.collections.
list_
(of, min_length, max_length)¶ Generator for arbitrary lists.
Parameters for this generator allow for adjusting the length of resulting list and elements they contain.
Parameters:  of – Generator for list elements
 min_length – A minimum length of list to generate
 max_length – A maximum length of list to generate
Example of test property that uses
list_()
:@qc def calculating_average( l=list_(of=int_(min=0, max=1024), min_length=16, max_length=2048) ): average = sum(l) / len(l) assert min(l) <= average <= max(l)

pyqcy.arbitraries.collections.
dict_
(keys, values, items, min_length, max_length)¶ Generator for arbitrary dictionaries.
Dictionaries are specified using generators  either for
keys
andvalues
separately:dict_(keys=str_(max_length=64), values=str_(max_length=64))
or already combined into
items
(which should yield keyvalue pairs):dict_(items=two(str_(max_length=64)))
Those two styles are mutually exclusive  only one can be used at a time.
Parameters:  keys – Generator for dictionary keys
 values – Generator for dictionary values
 items – Generator for dictionary items (2element tuples).
 min_length – A minimum number of items the resulting dictionary will contain
 max_length – A maximum number of items the resulting dictionary will contain
Combinators¶
If you want to have a generator that produces values of more than one type,
use the simple one_of()
function or the more sophisticated
frequency()
combinator.
For a simpler task of always choosing a value from a predefined
set of objects, the elements()
function will come handy.

pyqcy.arbitraries.combinators.
one_of
(*generators)¶ Generator that yields values coming from given set of generators.
Generators can be passed either directly as arguments:
one_of(int, float)
or as a list:
one_of([int, float])
Every generator has equal probability of being chosen. If you need nonuniform probability distribution, use the
frequency()
function.

pyqcy.arbitraries.combinators.
frequency
(*distribution)¶ Generator that yields coming from given set of generators, according to their probability distribution.
The distribution is just a set of tuples:
(gen, freq)
which can be passed either directly as arguments:frequency((int, 1), (float, 2))
or a a list:
frequency([(int, 1), (float, 2)])
The second element of tuple (
freq
) is the relative frequency of values from particular generator, compared to those from other generators. In both examples above the resulting generator will yieldfloat
s twice as often asint
s.Typically, it’s convenient to use floatingpoint frequencies that sum to
1.0
or integer frequencies that sum to100
.

pyqcy.arbitraries.combinators.
elements
(*list)¶ Generator that returns random elements from given set.
Elements can be passed either directly as arguments:
elements(1, 2, 3)
or as a list:
elements([1, 2, 3])
Every element has equal probability of being chosen.
Parameters: count – Optional number of elements in every returned subset. If omitted, a single element will be yield every time. If provided, it should always be passed as keyword argument, e.g.
elements(range(10), count=3)
.This can be also a generator  such as
int_()
 if there’s a need to randomize the subset size, too.Note
There is difference between
elements(foo)
andelements(foo, count=1)
. The first form returns random element from the setfoo
, while the second returns random 1element subset offoo
x
vs[x]
, essentially.
Data structures¶
For testing higher level code, it is often required to prepare more complex
input data and not just simple, uniform collections of elements. Even then,
it can be possible to avoid writing a custom generator if we use the
data()
function.

pyqcy.arbitraries.combinators.
data
(schema)¶ Generator that outputs data structures conforming to given schema.
Parameters: schema – A list or dictionary that contains either immediate values or other generators. Note
schema
can be recursive and combine lists with dictionaries into complex structures. You can have nested dictionaries, lists containing lists, dictionaries with lists as values, and so on.A typical example of using
data()
:import string @qc def creating_user_works( request=data({ 'login': str_(of=string.ascii_letters  string.digits, min_length=3, max_length=32), 'password': str_(min_length=8, max_length=128), }) ): response = create_user(request['login'], request['password']) assert response['status'] == "OK"
Applying functions¶
Yet another way of combining generators is to use them as building blocks
for whole object pipelines. This is possible thanks to apply()
combinator.

pyqcy.arbitraries.combinators.
apply
(func, *args, **kwargs)¶ Generator that applies a specific function to objects returned by given generator(s).
Any number of generators can be passed as arguments, and they can be both positional (
args
) or keyword arguments (kwargs
). In either case, the same invocation style (i.e. positional or keyword) will be used when calling thefunc
with actual values obtained from given generators.As an example, the following call:
apply(json.dumps, dict_(items=two(str)))
will create a generator that yields results of
json.dumps(d)
, whered
is an arbitrary dictionary that maps strings to strings.Similarly, using
apply()
as shown below:apply(itertools.product, list_(of=int), repeat=4)
gets us a generator that produces results of
itertools.product(l, repeat=4)
, wherel
is an arbitrary list ofint
s.