Data structures

A data returned from successful API calls are transformed and presented to you as a [Munch](https://github.com/Infinidat/munch) (it is a subclass of a dict with all its features, with an additional support of accessing its attributes like object properties, etc). This page shows how to work with the results, how to access the original responses from frontend and what are the specifics for lists of results.

First, let’s just initialize an API client, and obtain some object (in this example a build) to be examined.

from copr.v3 import Client
from pprint import pprint
client = Client.create_from_config_file()
build = client.build_proxy.get(2545)
pprint(build)

As advertised, the data is represented as a [Munch](https://github.com/Infinidat/munch).

Munch({u'source_package': {u'url': u'http://backend/results//@copr/copr/srpm-builds/00002545/ed-1.14.2-3.fc26.src.rpm', u'version': u'1.14.2-3.fc26', u'name': u'ed'}, '__response__': <Response [200]>, u'projectname': u'copr', u'started_on': 1526406595, u'submitted_on': 1525635534, u'state': u'succeeded', u'ended_on': 1526408106, u'ownername': u'@copr', u'repo_url': u'http://backend/results/@copr/copr', u'submitter': u'frostyx', u'chroots': [u'fedora-27-x86_64', u'fedora-rawhide-x86_64'], u'id': 2545})

What exactly it is? It is a structure that extends dict and add more features to it.

print(type(build))
print(isinstance(build, dict))
<class 'munch.Munch'>
True

In the first example, it is quite hard to see, what attributes are available and what are their values. It is possible to print the structure in more human-readable format.

from pprint import pprint
pprint(dict(build))
{'__response__': <Response [200]>,
 u'chroots': [u'fedora-27-x86_64', u'fedora-rawhide-x86_64'],
 u'ended_on': 1526408106,
 u'id': 2545,
 u'ownername': u'@copr',
 u'projectname': u'copr',
 u'repo_url': u'http://backend/results/@copr/foo',
 u'source_package': {u'name': u'ed',
                     u'url': u'http://backend/results//@copr/copr/srpm-builds/00002545/ed-1.14.2-3.fc26.src.rpm',
                     u'version': u'1.14.2-3.fc26'},
 u'started_on': 1526406595,
 u'state': u'succeeded',
 u'submitted_on': 1525635534,
 u'submitter': u'frostyx'}

Attributes are accessible through standard dict bracket-style, but also through object like property-style.

assert build.ownername == build["ownername"]
print(build.ownername)
@copr

Every data munch also stores the original response from frontend.

print(build.__response__)
print(type(build.__response__))
print(build.__response__.status_code)
<Response [200]>
requests.models.Response
200

Lists of objects

Now, it should be clear how a single data object is represented. Let’s see how the situation looks like when multiple objects are returned.

builds = client.build_proxy.get_list("@copr", "copr")
print(builds)

At the first sight, it is just a list of munches.

[Munch({u'source_package': {u'url': u'http://backend/results//@copr/copr/srpm-builds/00002544/mksh-56c-3.fc26.src.rpm', u'version': u'56c-3.fc26', u'name': u'mksh'}, u'projectname': u'copr', u'started_on': 1519063348, u'submitted_on': 1519062565, u'state': u'succeeded', u'ended_on': 1519064069, u'ownername': u'frostyx', u'repo_url': u'http://backend/results/@copr/copr', u'submitter': u'frostyx', u'chroots': [u'fedora-rawhide-i386', u'fedora-rawhide-x86_64'], u'id': 2544}),
 Munch({u'source_package': {u'url': u'http://backend/results//@copr/copr/srpm-builds/00002545/ed-1.14.2-3.fc26.src.rpm', u'version': u'1.14.2-3.fc26', u'name': u'ed'}, u'projectname': u'copr', u'started_on': 1526406595, u'submitted_on': 1525635534, u'state': u'succeeded', u'ended_on': 1526408106, u'ownername': u'@copr', u'repo_url': u'http://backend/results/@copr/copr', u'submitter': u'frostyx', u'chroots': [u'fedora-27-x86_64', u'fedora-rawhide-x86_64'], u'id': 2545})]

Not exactly. It is a subclass of a list created in the copr package.

print(type(builds))
print(isinstance(builds, list))
<class 'copr.v3.helpers.List'>
True

Let’s answer the anticipated question, why do we need a modified implementation of a list. It can provide the frontend response in a __response__ attribute the same way that single munch does.

print(builds.__response__)
<Response [200]>

It also provides a meta attribute, that has information about ordering results in the list and possibly limiting their number. Please read more about the pagination.

print(builds.meta)
Munch({u'offset': 0, u'limit': None, u'order_type': u'ASC', u'order': u'id'})

Iterating through all objects in the response looks as expected.

for build in builds:
    print("Build {} {}".format(build.id, build.state))
Build 2544 succeeded
Build 2545 succeeded

Modifying data

Previous examples show the data structures when the object was explicitly queried (i.e. get or get_list method was used). It remains to be explained, how the responses look like when a user tries to add, modify, or delete some object. Simply enough, the operation is executed and the object is implicitly queried afterward.

build = client.build_proxy.delete(2545)
print(build)
Munch({u'source_package': {u'url': u'http://backend/results//@copr/copr/srpm-builds/00002545/ed-1.14.2-3.fc26.src.rpm', u'version': u'1.14.2-3.fc26', u'name': u'ed'}, u'projectname': u'copr', u'started_on': 1526406595, u'submitted_on': 1525635534, u'state': u'succeeded', u'ended_on': 1526408106, u'ownername': u'@copr', u'repo_url': u'http://backend/results/@copr/copr', u'submitter': u'frostyx', u'chroots': [u'fedora-27-x86_64', u'fedora-rawhide-x86_64'], u'id': 2545})

The object was deleted, so it obviously can’t be queried one more time

client.build_proxy.get(build.id)
CoprNoResultException: Build 2545 does not exist.